1
1
/* ---------------------------------------------------------
2
2
* Copyright (C) Microsoft Corporation. All rights reserved.
3
3
*-------------------------------------------------------- */
4
- import type { IGrammar } from './textmate'
4
+ import type { IGrammar , IRawThemeSetting } from './textmate'
5
5
import { INITIAL } from './textmate'
6
6
import type { CodeToTokensBaseOptions , FontStyle , ShikiInternal , ThemeRegistrationResolved , ThemedToken , ThemedTokenScopeExplanation , TokenizeWithThemeOptions } from './types'
7
7
import { StackElementMetadata } from './stack-element-metadata'
@@ -33,6 +33,12 @@ export function codeToTokensBase(
33
33
return tokenizeWithTheme ( code , _grammar , theme , colorMap , options )
34
34
}
35
35
36
+ /** for explanations */
37
+ interface ThemeSettingsSelectors {
38
+ settings : IRawThemeSetting
39
+ selectors : string [ ] [ ]
40
+ }
41
+
36
42
export function tokenizeWithTheme (
37
43
code : string ,
38
44
grammar : IGrammar ,
@@ -53,6 +59,28 @@ export function tokenizeWithTheme(
53
59
let actual : ThemedToken [ ] = [ ]
54
60
const final : ThemedToken [ ] [ ] = [ ]
55
61
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
+
56
84
for ( let i = 0 , len = lines . length ; i < len ; i ++ ) {
57
85
const [ line , lineOffset ] = lines [ i ]
58
86
if ( line === '' ) {
@@ -119,7 +147,7 @@ export function tokenizeWithTheme(
119
147
offset += tokenWithScopesText . length
120
148
token . explanation . push ( {
121
149
content : tokenWithScopesText ,
122
- scopes : explainThemeScopes ( theme , tokenWithScopes . scopes ) ,
150
+ scopes : explainThemeScopes ( themeSettingsSelectors , tokenWithScopes . scopes ) ,
123
151
} )
124
152
125
153
tokensWithScopesIndex ! += 1
@@ -137,7 +165,7 @@ export function tokenizeWithTheme(
137
165
}
138
166
139
167
function explainThemeScopes (
140
- theme : ThemeRegistrationResolved ,
168
+ themeSelectors : ThemeSettingsSelectors [ ] ,
141
169
scopes : string [ ] ,
142
170
) : ThemedTokenScopeExplanation [ ] {
143
171
const result : ThemedTokenScopeExplanation [ ] = [ ]
@@ -146,33 +174,29 @@ function explainThemeScopes(
146
174
const scope = scopes [ i ]
147
175
result [ i ] = {
148
176
scopeName : scope ,
149
- themeMatches : explainThemeScope ( theme , scope , parentScopes ) ,
177
+ themeMatches : explainThemeScope ( themeSelectors , scope , parentScopes ) ,
150
178
}
151
179
}
152
180
return result
153
181
}
154
182
155
183
function 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 ] === '.' )
161
186
}
162
187
163
188
function matches (
164
- selector : string ,
165
- selectorParentScopes : string [ ] ,
189
+ selectors : string [ ] ,
166
190
scope : string ,
167
191
parentScopes : string [ ] ,
168
192
) : boolean {
169
- if ( ! matchesOne ( selector , scope ) )
193
+ if ( ! matchesOne ( selectors [ selectors . length - 1 ] , scope ) )
170
194
return false
171
195
172
- let selectorParentIndex = selectorParentScopes . length - 1
196
+ let selectorParentIndex = selectors . length - 2
173
197
let parentIndex = parentScopes . length - 1
174
198
while ( selectorParentIndex >= 0 && parentIndex >= 0 ) {
175
- if ( matchesOne ( selectorParentScopes [ selectorParentIndex ] , parentScopes [ parentIndex ] ) )
199
+ if ( matchesOne ( selectors [ selectorParentIndex ] , parentScopes [ parentIndex ] ) )
176
200
selectorParentIndex -= 1
177
201
parentIndex -= 1
178
202
}
@@ -184,34 +208,16 @@ function matches(
184
208
}
185
209
186
210
function explainThemeScope (
187
- theme : ThemeRegistrationResolved ,
211
+ themeSettingsSelectors : ThemeSettingsSelectors [ ] ,
188
212
scope : string ,
189
213
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
215
221
}
216
222
}
217
223
}
0 commit comments