Skip to content

Commit 9c21b3f

Browse files
committed
Derive YAML plain-fold region literals from the indent config
The multi-line plain-scalar fold regions (gen-tm §2a′/§2a″) and the block-scalar value-prefix hardcoded YAML structural literals (`#`, `-`/`?`, `---`/`...`, the flow brackets, `:`) in their regexes. Derive them from the indent config instead so the YAML region code is data-driven, not hardcoded: comment from `indent.comment`, compact indicators from `indent.compactIndicators`, document markers from `blockScalar.documentMarkers`, the flow-bracket exclusion from `indent.flowOpen` / `indent.flowClose`, and the mapping key/value separator from a new `indent.keyValueSeparator` field (defaults to ':'). The one remaining inline literal — node-property `&`/`!` in the block-scalar prop prefix — is anchor/tag-specific (it derives from the Anchor/Tag tokens, not the indent config) and is left as is. These regions are gated on `grammar.indent.blockScalar`, so the other six grammars regenerate byte-identical (verified). The regenerated YAML grammar is tokenization-IDENTICAL to before: the only byte changes are two semantically-equivalent char-class normalizations from the derivation (`[-?]` → `[\-?]`, `[^\n{}[]]` → `[^\n[{]}]`), and a full-corpus tokenization diff over the yaml-test-suite (406 inputs) shows 0 differences. yaml-issue12-regressions stays 6 pass / 4 known-bug / 0 regression; scope-gap Monogram-wrong unchanged at 8; agnostic guard 9/9; sanity 15/15; scope-roles 47/47 + 9/9; tm-highlight-guards 22/0; RedCMD Onigmo diagnostics clean. Refs #12
1 parent e2b0a02 commit 9c21b3f

4 files changed

Lines changed: 29 additions & 9 deletions

File tree

src/gen-tm.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4645,6 +4645,20 @@ export function generateTmLanguage(grammar: CstGrammar, langName: string): TmGra
46454645
let bsIndicatorScope = '';
46464646
let bsContentScope = '';
46474647
if (blockScalar) {
4648+
// YAML structural literals DERIVED from the indent config (kept data-driven, NOT hardcoded in the
4649+
// generator) — used by the block-scalar value-prefix (`bsVp`) and the multi-line plain-scalar fold
4650+
// regions (§2a′/§2a″): the comment introducer, the compact indicators (`-`/`?`) as an alternation
4651+
// and a char class, the document markers (`---`/`...`), the flow brackets (for the key-scan
4652+
// exclusion class), and the mapping key/value separator (`:`). The one structural literal WITHOUT a
4653+
// dedicated field — node-property `&`/`!` in `bsProp` below — is anchor/tag-specific (it comes from
4654+
// the Anchor/Tag tokens, not the indent config) and stays inline.
4655+
const ind = grammar.indent!;
4656+
const cmtLit = escapeRegex(ind.comment ?? '#');
4657+
const compactAlt = (ind.compactIndicators ?? []).map((c) => `${escapeRegex(c)}[\\t ]`).join('|');
4658+
const compactCls = `[${(ind.compactIndicators ?? []).map(escapeForCharClass).join('')}]`;
4659+
const docAlt = (blockScalar.documentMarkers ?? []).map(escapeRegex).join('|');
4660+
const flowEx = `[^\\n${[...(ind.flowOpen ?? []), ...(ind.flowClose ?? [])].map(escapeForCharClass).join('')}]`;
4661+
const kvSep = escapeRegex(ind.keyValueSeparator ?? ':');
46484662
const bsTok = grammar.tokens.find(t => t.name === blockScalar.token);
46494663
const bsKey = blockScalar.token.toLowerCase();
46504664
const bsScope = bsTok?.scope ?? 'string.unquoted.block';
@@ -4739,7 +4753,7 @@ export function generateTmLanguage(grammar: CstGrammar, langName: string): TmGra
47394753
// END in a pipe) from opening a region: after `a: ` the next token is `foo`, not a separator/
47404754
// key-colon/property, so the lookahead fails. The key arm matches up to the FIRST `: ` separator.
47414755
const bsProp = '(?:[&!][^\\t\\n\\f\\r \\[\\]{},]*[\\t ]+)*';
4742-
const bsVp = `(?:(?:---|\\.\\.\\.)[\\t ]+)?(?:[-?][\\t ]+)*(?:[^\\n]*?:[\\t ]+)?${bsProp}`;
4756+
const bsVp = `(?:(?:${docAlt})[\\t ]+)?(?:${compactCls}[\\t ]+)*(?:[^\\n]*?${kvSep}[\\t ]+)?${bsProp}`;
47434757
// Expose the introducer / inner-rule / header-includes to §2b (the `? |` explicit-key variant).
47444758
bsIntro = intro;
47454759
bsFunkyIntroRule = bsIntroRule;
@@ -4832,7 +4846,7 @@ export function generateTmLanguage(grammar: CstGrammar, langName: string): TmGra
48324846
// flow bracket (a `{`/`[` is flow, handled elsewhere), and `:(?:[\t ]|$)` requires the colon to be
48334847
// a real key separator (`http://x` keeps its glued `:` → still plain content). Used as a NEGATIVE
48344848
// lookahead to bound the fold at the first sibling/comment line, matching the parser's foldedPlain.
4835-
const structAhead = `(?:#|-[\\t ]|\\?[\\t ]|[^\\n{}\\[\\]]*?:(?:[\\t ]|$))`;
4849+
const structAhead = `(?:${cmtLit}|${compactAlt}|${flowEx}*?${kvSep}(?:[\\t ]|$))`;
48364850
// Value-position lookahead: after the node indent is stripped, the line must carry an INLINE
48374851
// BLOCK plain value — either `<key>: <plain>` (mapping value) or a `-`/`?` indicator + `<plain>`
48384852
// (sequence entry / explicit key). The plain value is confirmed by `(?=plainSrc)`, which only
@@ -4844,7 +4858,7 @@ export function generateTmLanguage(grammar: CstGrammar, langName: string): TmGra
48444858
// a flow collection (`{ a: b,\n c }` / `a: { b: c,\n d }`) is a multi-line begin/end region of
48454859
// its own — a `{`/`[` before the `:` means the `:` is a FLOW separator, not a block one, so the
48464860
// region must NOT open and steal those lines from #flow-mapping/#flow-sequence.
4847-
const plainVp = `(?:(?:---|\\.\\.\\.)[\\t ]+)?(?:(?:[-?][\\t ]+)+(?:[^\\n{}\\[\\]]*?:[\\t ]+)?|[^\\n{}\\[\\]]*?:[\\t ]+)(?=${plainSrc})`;
4861+
const plainVp = `(?:(?:${docAlt})[\\t ]+)?(?:(?:${compactCls}[\\t ]+)+(?:${flowEx}*?${kvSep}[\\t ]+)?|${flowEx}*?${kvSep}[\\t ]+)(?=${plainSrc})`;
48484862
// Header-line token includes: the same shape any plain `key: value` line gets, so the header is
48494863
// scoped identically to the top level (only the CONTINUATION changes). Includes the typed-value
48504864
// tokens (`#num`/`#boolnull`) so a SINGLE-line `a: 1` keeps `constant.numeric`, and the full
@@ -4897,7 +4911,7 @@ export function generateTmLanguage(grammar: CstGrammar, langName: string): TmGra
48974911
// an inner `begin:$`/`while:\G` body swallows each following line as `string.unquoted` (stopping
48984912
// before an inline ` #` comment, which then falls to `#comment`). structRelease extends §2a′'s
48994913
// `structAhead` with the doc markers (a `---`/`...` ends a doc-body fold).
4900-
const structRelease = `(?:#|-[\\t ]|\\?[\\t ]|(?:---|\\.\\.\\.)(?:[\\t ]|$)|[^\\n{}\\[\\]]*?:(?:[\\t ]|$))`;
4914+
const structRelease = `(?:${cmtLit}|${compactAlt}|(?:${docAlt})(?:[\\t ]|$)|${flowEx}*?${kvSep}(?:[\\t ]|$))`;
49014915
// The HEADER line is scoped by the normal token includes (so a standalone bare `42`/`true` keeps
49024916
// its `#num`/`#boolnull` typing). A CONTINUATION line that opens with a non-token char (`%`, which
49034917
// no header include matches) would leave that char unscoped and let a LATER `#plain` claim only the

src/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,11 @@ export interface IndentConfig {
311311
explicitKey?: string; // the flow `?` explicit-key indicator (e.g. punctuation.definition.key-value)
312312
};
313313
comment?: string; // line-comment introducer ignored for indentation (e.g. '#')
314+
// The mapping KEY/VALUE separator literal (YAML `:`). Used by the derived highlighter's multi-line
315+
// plain-scalar fold regions (gen-tm §2a′/§2a″) to recognise a `key:`-led line as STRUCTURAL (a
316+
// sibling that ends a fold) vs a bare plain-scalar continuation. Declared here (not hardcoded in the
317+
// generator) so the YAML region code stays data-driven. Absent → defaults to ':'.
318+
keyValueSeparator?: string;
314319
// Block scalars (YAML `|` / `>`): when the rest of a line is an introducer + indicators, the
315320
// following more-indented lines are verbatim content emitted as ONE token (like raw-text, but
316321
// bounded by indentation rather than a close tag). `introducers` are the leading chars (['|','>']).

yaml.tmLanguage.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@
318318
"match": "!(?:<[^>]*>|[^\\t\\n\\f\\r \\[\\]{},]*)"
319319
},
320320
"blockscalar": {
321-
"begin": "^([ \\t]*)(?=(?:(?:---|\\.\\.\\.)[\\t ]+)?(?:[-?][\\t ]+)*(?:[^\\n]*?:[\\t ]+)?(?:[&!][^\\t\\n\\f\\r \\[\\]{},]*[\\t ]+)*[|>](?:[1-9][-+]?|[-+][1-9]?|[-+])?[\\t ]*(?:#|$))",
321+
"begin": "^([ \\t]*)(?=(?:(?:---|\\.\\.\\.)[\\t ]+)?(?:[\\-?][\\t ]+)*(?:[^\\n]*?:[\\t ]+)?(?:[&!][^\\t\\n\\f\\r \\[\\]{},]*[\\t ]+)*[|>](?:[1-9][-+]?|[-+][1-9]?|[-+])?[\\t ]*(?:#|$))",
322322
"while": "\\G(?=\\1[ \\t]|[ \\t]*$)",
323323
"patterns": [
324324
{
@@ -606,8 +606,8 @@
606606
]
607607
},
608608
"plain-continuation": {
609-
"begin": "^([ \\t]*)(?=(?:(?:---|\\.\\.\\.)[\\t ]+)?(?:(?:[-?][\\t ]+)+(?:[^\\n{}\\[\\]]*?:[\\t ]+)?|[^\\n{}\\[\\]]*?:[\\t ]+)(?=(?:[^\\t\\n\\f\\r \\-?:,\\[\\]{}#&*!|>'\"%@`]|[\\-?:](?=[^\\t\\n\\f\\r ,\\[\\]{}]))(?:[^:#\\n,\\[\\]{}]|:(?=[^\\t\\n\\f\\r ,\\]}])|#(?<=[^\\t\\n\\f\\r ]#))*))",
610-
"while": "\\G(?=[ \\t]*$|\\1[ \\t]+(?!(?:#|-[\\t ]|\\?[\\t ]|[^\\n{}\\[\\]]*?:(?:[\\t ]|$))))",
609+
"begin": "^([ \\t]*)(?=(?:(?:---|\\.\\.\\.)[\\t ]+)?(?:(?:[\\-?][\\t ]+)+(?:[^\\n\\[{\\]}]*?:[\\t ]+)?|[^\\n\\[{\\]}]*?:[\\t ]+)(?=(?:[^\\t\\n\\f\\r \\-?:,\\[\\]{}#&*!|>'\"%@`]|[\\-?:](?=[^\\t\\n\\f\\r ,\\[\\]{}]))(?:[^:#\\n,\\[\\]{}]|:(?=[^\\t\\n\\f\\r ,\\]}])|#(?<=[^\\t\\n\\f\\r ]#))*))",
610+
"while": "\\G(?=[ \\t]*$|\\1[ \\t]+(?!(?:#|-[\\t ]|\\?[\\t ]|[^\\n\\[{\\]}]*?:(?:[\\t ]|$))))",
611611
"patterns": [
612612
{
613613
"match": "\\G[\\t ]+(?:[^#\\n]|#(?<=[^\\t\\n\\f\\r ]#))*",
@@ -661,8 +661,8 @@
661661
]
662662
},
663663
"plain-bare-fold": {
664-
"begin": "^([ \\t]*)(?=(?:[^\\t\\n\\f\\r \\-?:,\\[\\]{}#&*!|>'\"%@`]|[\\-?:](?=[^\\t\\n\\f\\r ,\\[\\]{}]))(?:[^:#\\n,\\[\\]{}]|:(?=[^\\t\\n\\f\\r ,\\]}])|#(?<=[^\\t\\n\\f\\r ]#))*)(?!(?:#|-[\\t ]|\\?[\\t ]|(?:---|\\.\\.\\.)(?:[\\t ]|$)|[^\\n{}\\[\\]]*?:(?:[\\t ]|$)))",
665-
"while": "\\G(?=[ \\t]*$|\\1(?=[ \\t]*\\S)(?![ \\t]*(?:#|-[\\t ]|\\?[\\t ]|(?:---|\\.\\.\\.)(?:[\\t ]|$)|[^\\n{}\\[\\]]*?:(?:[\\t ]|$))))",
664+
"begin": "^([ \\t]*)(?=(?:[^\\t\\n\\f\\r \\-?:,\\[\\]{}#&*!|>'\"%@`]|[\\-?:](?=[^\\t\\n\\f\\r ,\\[\\]{}]))(?:[^:#\\n,\\[\\]{}]|:(?=[^\\t\\n\\f\\r ,\\]}])|#(?<=[^\\t\\n\\f\\r ]#))*)(?!(?:#|-[\\t ]|\\?[\\t ]|(?:---|\\.\\.\\.)(?:[\\t ]|$)|[^\\n\\[{\\]}]*?:(?:[\\t ]|$)))",
665+
"while": "\\G(?=[ \\t]*$|\\1(?=[ \\t]*\\S)(?![ \\t]*(?:#|-[\\t ]|\\?[\\t ]|(?:---|\\.\\.\\.)(?:[\\t ]|$)|[^\\n\\[{\\]}]*?:(?:[\\t ]|$))))",
666666
"patterns": [
667667
{
668668
"include": "#docstart"

yaml.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,7 @@ const indent: IndentConfig = {
614614
explicitKey: 'punctuation.definition.key-value',
615615
},
616616
comment: '#',
617+
keyValueSeparator: ':',
617618
blockScalar: { introducers: ['|', '>'], token: 'BlockScalar', documentMarkers: ['---', '...'], indicatorScope: 'keyword.control.flow.block-scalar' },
618619
compactIndicators: ['-', '?'],
619620
// Tag-handle per-document membership (§6.8.2 / §6.9.1): a named handle `!h!` used by a Tag must

0 commit comments

Comments
 (0)