11/* ---------------------------------------------------------
22 * Copyright (C) Microsoft Corporation. All rights reserved.
33 *-------------------------------------------------------- */
4- import type { IGrammar } from './textmate'
4+ import type { IGrammar , IRawThemeSetting } from './textmate'
55import { INITIAL } from './textmate'
66import type { CodeToTokensBaseOptions , FontStyle , ShikiInternal , ThemeRegistrationResolved , ThemedToken , ThemedTokenScopeExplanation , TokenizeWithThemeOptions } from './types'
77import { StackElementMetadata } from './stack-element-metadata'
@@ -33,6 +33,12 @@ export function codeToTokensBase(
3333 return tokenizeWithTheme ( code , _grammar , theme , colorMap , options )
3434}
3535
36+ /** for explanations */
37+ interface ThemeSettingsSelectors {
38+ settings : IRawThemeSetting
39+ selectors : string [ ] [ ]
40+ }
41+
3642export function tokenizeWithTheme (
3743 code : string ,
3844 grammar : IGrammar ,
@@ -53,6 +59,28 @@ export function tokenizeWithTheme(
5359 let actual : ThemedToken [ ] = [ ]
5460 const final : ThemedToken [ ] [ ] = [ ]
5561
62+ const themeSettingsSelectors : ThemeSettingsSelectors [ ] = [ ]
63+ if ( options . includeExplanation ) {
64+ for ( const setting of theme . settings ) {
65+ let selectors : string [ ]
66+ switch ( typeof setting . scope ) {
67+ case 'string' :
68+ selectors = setting . scope . split ( / , / ) . map ( scope => scope . trim ( ) )
69+ break
70+ case 'object' :
71+ selectors = setting . scope
72+ break
73+ default :
74+ continue
75+ }
76+
77+ themeSettingsSelectors . push ( {
78+ settings : setting ,
79+ selectors : selectors . map ( selector => selector . split ( / / ) ) ,
80+ } )
81+ }
82+ }
83+
5684 for ( let i = 0 , len = lines . length ; i < len ; i ++ ) {
5785 const [ line , lineOffset ] = lines [ i ]
5886 if ( line === '' ) {
@@ -119,7 +147,7 @@ export function tokenizeWithTheme(
119147 offset += tokenWithScopesText . length
120148 token . explanation . push ( {
121149 content : tokenWithScopesText ,
122- scopes : explainThemeScopes ( theme , tokenWithScopes . scopes ) ,
150+ scopes : explainThemeScopes ( themeSettingsSelectors , tokenWithScopes . scopes ) ,
123151 } )
124152
125153 tokensWithScopesIndex ! += 1
@@ -137,7 +165,7 @@ export function tokenizeWithTheme(
137165}
138166
139167function explainThemeScopes (
140- theme : ThemeRegistrationResolved ,
168+ themeSelectors : ThemeSettingsSelectors [ ] ,
141169 scopes : string [ ] ,
142170) : ThemedTokenScopeExplanation [ ] {
143171 const result : ThemedTokenScopeExplanation [ ] = [ ]
@@ -146,33 +174,29 @@ function explainThemeScopes(
146174 const scope = scopes [ i ]
147175 result [ i ] = {
148176 scopeName : scope ,
149- themeMatches : explainThemeScope ( theme , scope , parentScopes ) ,
177+ themeMatches : explainThemeScope ( themeSelectors , scope , parentScopes ) ,
150178 }
151179 }
152180 return result
153181}
154182
155183function matchesOne ( selector : string , scope : string ) : boolean {
156- const selectorPrefix = `${ selector } .`
157- if ( selector === scope || scope . substring ( 0 , selectorPrefix . length ) === selectorPrefix )
158- return true
159-
160- return false
184+ return selector === scope
185+ || ( scope . substring ( 0 , selector . length ) === selector && scope [ selector . length ] === '.' )
161186}
162187
163188function matches (
164- selector : string ,
165- selectorParentScopes : string [ ] ,
189+ selectors : string [ ] ,
166190 scope : string ,
167191 parentScopes : string [ ] ,
168192) : boolean {
169- if ( ! matchesOne ( selector , scope ) )
193+ if ( ! matchesOne ( selectors [ selectors . length - 1 ] , scope ) )
170194 return false
171195
172- let selectorParentIndex = selectorParentScopes . length - 1
196+ let selectorParentIndex = selectors . length - 2
173197 let parentIndex = parentScopes . length - 1
174198 while ( selectorParentIndex >= 0 && parentIndex >= 0 ) {
175- if ( matchesOne ( selectorParentScopes [ selectorParentIndex ] , parentScopes [ parentIndex ] ) )
199+ if ( matchesOne ( selectors [ selectorParentIndex ] , parentScopes [ parentIndex ] ) )
176200 selectorParentIndex -= 1
177201 parentIndex -= 1
178202 }
@@ -184,34 +208,16 @@ function matches(
184208}
185209
186210function explainThemeScope (
187- theme : ThemeRegistrationResolved ,
211+ themeSettingsSelectors : ThemeSettingsSelectors [ ] ,
188212 scope : string ,
189213 parentScopes : string [ ] ,
190- ) : any [ ] {
191- const result : any [ ] = [ ]
192- let resultLen = 0
193- for ( let i = 0 , len = theme . settings . length ; i < len ; i ++ ) {
194- const setting = theme . settings [ i ]
195- let selectors : string [ ]
196- if ( typeof setting . scope === 'string' )
197- selectors = setting . scope . split ( / , / ) . map ( scope => scope . trim ( ) )
198- else if ( Array . isArray ( setting . scope ) )
199- selectors = setting . scope
200- else
201- continue
202-
203- for ( let j = 0 , lenJ = selectors . length ; j < lenJ ; j ++ ) {
204- const rawSelector = selectors [ j ]
205- const rawSelectorPieces = rawSelector . split ( / / )
206-
207- const selector = rawSelectorPieces [ rawSelectorPieces . length - 1 ]
208- const selectorParentScopes = rawSelectorPieces . slice ( 0 , rawSelectorPieces . length - 1 )
209-
210- if ( matches ( selector , selectorParentScopes , scope , parentScopes ) ) {
211- // match!
212- result [ resultLen ++ ] = setting
213- // break the loop
214- j = lenJ
214+ ) : IRawThemeSetting [ ] {
215+ const result : IRawThemeSetting [ ] = [ ]
216+ for ( const { selectors, settings } of themeSettingsSelectors ) {
217+ for ( const selectorPieces of selectors ) {
218+ if ( matches ( selectorPieces , scope , parentScopes ) ) {
219+ result . push ( settings )
220+ break // continue to the next theme settings
215221 }
216222 }
217223 }
0 commit comments