@@ -3388,7 +3388,10 @@ function detectExplicitKey(grammar: CstGrammar): { indicator: string; keyScope:
33883388// • the KEY scope + plain-scalar shape come from the same Key/Plain token pair detectExplicitKey
33893389// uses (a scalar token whose pattern is `<plain>(?=…sep…)` over a broader plain scalar).
33903390// Returns null when the family has no flow collections (every non-YAML grammar).
3391- interface FlowColl { open : string ; close : string ; sep : string ; colon : string | null ; }
3391+ // `punct` (when present) are the SPECIFIC open/close/separator scopes the grammar declared for THIS
3392+ // collection via `indent.flowScopes.byOpen[open]` (null → fall back to the generic `punctuation.*`).
3393+ // Scope strings are bare (no `.${lang}` suffix); the region builder appends it.
3394+ interface FlowColl { open : string ; close : string ; sep : string ; colon : string | null ; punct : { begin : string ; end : string ; separator : string } | null ; }
33923395function detectFlowCollections ( grammar : CstGrammar ) : {
33933396 colls : FlowColl [ ] ;
33943397 plainStart : string ; // plain-scalar LEADING char class (no enclosing group)
@@ -3400,6 +3403,8 @@ function detectFlowCollections(grammar: CstGrammar): {
34003403 sqScope : string | null ; // single-quoted scalar scope
34013404 dqEscape : string | null ; // in-string escape sub-pattern for the double quote
34023405 sqEscape : string | null ; // in-string escape sub-pattern for the single quote
3406+ keyValueScope : string | null ; // declared `:` key/value separator scope (indent.flowScopes.keyValue), bare
3407+ explicitKeyScope : string | null ; // declared `?` explicit-key indicator scope (indent.flowScopes.explicitKey), bare
34033408} | null {
34043409 const indent = grammar . indent ;
34053410 if ( ! indent || ! indent . flowOpen ?. length || ! indent . flowClose ?. length ) return null ;
@@ -3447,7 +3452,10 @@ function detectFlowCollections(grammar: CstGrammar): {
34473452 const c = eLits . find ( l => l . length === 1 && l !== sep && l !== open && l !== close && ! / [ \w \s ] / . test ( l ) ) ;
34483453 if ( c ) { colon = c ; break ; }
34493454 }
3450- colls . push ( { open, close, sep, colon } ) ;
3455+ // The grammar may DECLARE specific open/close/separator scopes for this collection (keyed by the
3456+ // open bracket); absent → null → the region builder uses the generic `punctuation.${lang}`.
3457+ const punct = indent . flowScopes ?. byOpen ?. [ open ] ?? null ;
3458+ colls . push ( { open, close, sep, colon, punct } ) ;
34513459 }
34523460 if ( ! colls . length ) return null ;
34533461
@@ -3484,6 +3492,8 @@ function detectFlowCollections(grammar: CstGrammar): {
34843492 dq : dqTok ? tokenPatternSource ( dqTok ) : null , sq : sqTok ? tokenPatternSource ( sqTok ) : null ,
34853493 dqScope : dqTok ?. scope ?? null , sqScope : sqTok ?. scope ?? null ,
34863494 dqEscape : quoteEscape ( dqTok ) , sqEscape : quoteEscape ( sqTok ) ,
3495+ keyValueScope : indent . flowScopes ?. keyValue ?? null ,
3496+ explicitKeyScope : indent . flowScopes ?. explicitKey ?? null ,
34873497 } ;
34883498}
34893499
@@ -4861,6 +4871,12 @@ export function generateTmLanguage(grammar: CstGrammar, langName: string): TmGra
48614871 if ( flow ) {
48624872 const ln = langName ;
48634873 const P = `punctuation.${ ln } ` ;
4874+ // Resolve a DECLARED bare scope (from indent.flowScopes) to a full scope, or fall back to the
4875+ // generic `P`. The grammar supplies the language-flavoured names (mapping/sequence/key-value);
4876+ // the engine only appends the language suffix — so gen-tm stays agnostic (no hardcoded names).
4877+ const withLang = ( bare : string | null ) : string => bare ? `${ bare } .${ ln } ` : P ;
4878+ const kvScope = withLang ( flow . keyValueScope ) ; // the flow-mapping `:` key/value separator
4879+ const ekScope = withLang ( flow . explicitKeyScope ) ; // the flow `?` explicit-key indicator
48644880 // Comment includes (derived from the grammar's comment-scoped tokens — not a hardcoded key),
48654881 // spread into every flow sub-region so a `#…` comment is scoped even mid-entry / mid-value.
48664882 const commentIncs = commentIncludeKeys . map ( k => ( { include : `#${ k } ` } ) ) ;
@@ -4905,12 +4921,12 @@ export function generateTmLanguage(grammar: CstGrammar, langName: string): TmGra
49054921 // The VALUE colon (a flow separator `:`); and the JSON-style value colon glued after a closed
49064922 // key (`{"a":b}` — `:` preceded by a quote/closer/line-start, where no space-separator exists).
49074923 repository [ 'flow-map-value' ] = {
4908- begin : colonSep , beginCaptures : { '0' : { name : P } } , end : beforeClose ,
4924+ begin : colonSep , beginCaptures : { '0' : { name : kvScope } } , end : beforeClose ,
49094925 patterns : [ ...commentIncs , { include : '#flow-node' } ] ,
49104926 } ;
49114927 repository [ 'flow-map-value-json' ] = {
49124928 begin : `(?<=[${ quoteCls } ${ closeCls } ])[\\t ]*${ eColon } |(?<=^)[\\t ]*${ eColon } ` ,
4913- beginCaptures : { '0' : { name : P } } , end : beforeClose ,
4929+ beginCaptures : { '0' : { name : kvScope } } , end : beforeClose ,
49144930 patterns : [ ...commentIncs , { include : '#flow-node' } ] ,
49154931 } ;
49164932 // A plain scalar as a VALUE / as a KEY (entity.name.tag); both end at the flow boundary.
@@ -4931,24 +4947,29 @@ export function generateTmLanguage(grammar: CstGrammar, langName: string): TmGra
49314947 // boundary opens a key region. Derived from the explicit-key indicator when present.
49324948 const qmark = explicitKey ? escapeRegex ( explicitKey . indicator ) : null ;
49334949 const explicitKeyEntry : TmPattern [ ] = qmark ? [ {
4934- begin : `${ qmark } (?=[\\s${ closeCls } ]|$)` , beginCaptures : { '0' : { name : P } } , end : beforeClose ,
4950+ begin : `${ qmark } (?=[\\s${ closeCls } ]|$)` , beginCaptures : { '0' : { name : ekScope } } , end : beforeClose ,
49354951 patterns : [ ...commentIncs , { include : '#flow-mapping-map-key' } , { include : '#flow-map-value' } , { include : '#flow-node' } ] ,
49364952 } ] : [ ] ;
49374953
49384954 // Emit one region per collection. A MAPPING (`colon != null`): every entry-leading scalar is a
49394955 // key. A SEQUENCE: a scalar is a key only when a `:` separator follows (single-pair map element).
49404956 for ( const c of flow . colls ) {
49414957 const eOpen = escapeRegex ( c . open ) , eClose = escapeRegex ( c . close ) , eSep = escapeRegex ( c . sep ) ;
4958+ // The SPECIFIC open/close/separator scopes the grammar declared for THIS collection (mapping vs
4959+ // sequence — the names are the grammar's, not the engine's); null → the generic `P`.
4960+ const beginScope = c . punct ? `${ c . punct . begin } .${ ln } ` : P ;
4961+ const endScope = c . punct ? `${ c . punct . end } .${ ln } ` : P ;
4962+ const sepScope = c . punct ? `${ c . punct . separator } .${ ln } ` : P ;
49424963 if ( c . colon ) {
49434964 // A YAML flow mapping `{ … }` is a bracket region. CALLER predicate: detectFlowCollections
49444965 // found a collection rule (`OPEN … CLOSE` seq) whose entry rule carries a `:` key/value
49454966 // separator (`c.colon != null`). Recurse is via the body's `#flow-node` include (which
49464967 // re-includes #flow-mapping/#flow-sequence for nested collections).
49474968 repository [ 'flow-mapping' ] = emitBracketRegion ( {
49484969 name : `meta.flow.mapping.${ ln } ` ,
4949- openLit : eOpen , closeLit : eClose , beginCapName : P , endCapName : P ,
4970+ openLit : eOpen , closeLit : eClose , beginCapName : beginScope , endCapName : endScope ,
49504971 bodyPatterns : [
4951- ...commentIncs , { match : eSep , name : P } ,
4972+ ...commentIncs , { match : eSep , name : sepScope } ,
49524973 { include : '#flow-mapping-map-key' } , { include : '#flow-map-value-json' } , { include : '#flow-map-value' } , { include : '#flow-node' } ,
49534974 ] ,
49544975 } ) ;
@@ -4968,9 +4989,9 @@ export function generateTmLanguage(grammar: CstGrammar, langName: string): TmGra
49684989 // shape, but the entry rule has NO `:` separator (`c.colon == null`). Same #flow-node recurse.
49694990 repository [ 'flow-sequence' ] = emitBracketRegion ( {
49704991 name : `meta.flow.sequence.${ ln } ` ,
4971- openLit : eOpen , closeLit : eClose , beginCapName : P , endCapName : P ,
4992+ openLit : eOpen , closeLit : eClose , beginCapName : beginScope , endCapName : endScope ,
49724993 bodyPatterns : [
4973- ...commentIncs , { match : eSep , name : P } ,
4994+ ...commentIncs , { match : eSep , name : sepScope } ,
49744995 { include : '#flow-sequence-map-key' } , { include : '#flow-map-value-json' } , { include : '#flow-map-value' } , { include : '#flow-node' } ,
49754996 ] ,
49764997 } ) ;
0 commit comments