@mirascript/monaco 0.1.80 → 0.1.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,475 +1,136 @@
1
+ import type { HighlighterCore, Grammar } from '@shikijs/core';
2
+ import type { StateStack } from '@shikijs/vscode-textmate';
1
3
  import { languages, type IDisposable } from '../monaco-api.js';
2
- import {
3
- REG_WHITESPACE,
4
- REG_ORDINAL,
5
- REG_OCT,
6
- REG_BIN,
7
- REG_HEX,
8
- REG_NUMBER,
9
- REG_IDENTIFIER,
10
- MAX_VERBATIM_LENGTH,
11
- CONSTANT_KEYWORDS,
12
- CONTROL_KEYWORDS,
13
- KEYWORDS,
14
- NUMERIC_KEYWORDS,
15
- } from '../constants.js';
16
- import { DefaultVmContext } from '@mirascript/mirascript/subtle';
17
- import { isVmModule } from '@mirascript/mirascript';
4
+ import { CONTRIBUTE_IDS } from '../contribute.js';
18
5
 
19
- const moduleNames = [...DefaultVmContext.keys()].filter(
20
- (name) => DefaultVmContext.has(name) && isVmModule(DefaultVmContext.get(name)),
21
- );
6
+ const TOKENIZE_MAX_LINE_LENGTH = 20000;
7
+ const TOKENIZE_TIME_LIMIT = 500;
22
8
 
23
- /** 匹配 identifier */
24
- function identifierCases(
25
- data?: Partial<languages.IExpandedMonarchLanguageAction>,
26
- defaultToken = 'variable',
27
- ): Record<string, languages.IExpandedMonarchLanguageAction> {
28
- return {
29
- '@numericKeywords': { ...data, token: `constant.numeric` },
30
- '@constantKeywords': { ...data, token: `constant.language` },
31
- '@controlKeywords': { ...data, token: `keyword.flow` },
32
- '@keywords': { ...data, token: `keyword` },
33
- '[@]+.*': { ...data, token: `variable.other.constant` },
34
- '~it': { ...data, token: `variable.other.constant.emphasis` },
35
- '@default': { ...data, token: defaultToken },
36
- };
9
+ /** Select the deepest scope that a native Monaco theme can style. */
10
+ function tokenScope(scopes: string[]): string {
11
+ const inInterpolation = scopes.some((scope) => scope.startsWith('meta.interpolation.'));
12
+ let fallback = '';
13
+ const styledScopePrefixes = [
14
+ 'invalid.',
15
+ 'comment.',
16
+ 'string.',
17
+ 'keyword.',
18
+ 'constant.',
19
+ 'variable.',
20
+ 'entity.',
21
+ 'storage.',
22
+ 'support.',
23
+ 'markup.',
24
+ ];
25
+ for (let index = scopes.length - 1; index >= 0; index -= 1) {
26
+ const scope = scopes[index]!;
27
+ if (scope === 'source.mira' || scope === 'source.mira.doc' || scope === 'text.miratpl') continue;
28
+ if (scope.startsWith('meta.')) continue;
29
+ fallback ||= scope;
30
+ if (inInterpolation || styledScopePrefixes.some((prefix) => scope.startsWith(prefix))) return scope;
31
+ }
32
+ return fallback;
37
33
  }
38
34
 
39
- /** 生成 TokensProvider */
40
- function getTokensProvider(mode: string): languages.IMonarchLanguage {
41
- return {
42
- ignoreCase: false,
43
- unicode: true,
44
- includeLF: false,
45
- brackets: [
46
- { open: '{', close: '}', token: 'delimiter.curly' },
47
- { open: '[', close: ']', token: 'delimiter.square' },
48
- { open: '(', close: ')', token: 'delimiter.parenthesis' },
49
- ],
50
- defaultToken: 'invalid',
51
-
52
- whitespace: REG_WHITESPACE,
53
- identifier: REG_IDENTIFIER,
54
- identifierNoAtOnly: /(?:(?:_+|\$+|\p{XID_Start})\p{XID_Continue}*|@+\p{XID_Continue}+)/u,
55
-
56
- keywords: KEYWORDS,
57
- controlKeywords: CONTROL_KEYWORDS,
58
- constantKeywords: CONSTANT_KEYWORDS,
59
- numericKeywords: NUMERIC_KEYWORDS,
60
-
61
- start: mode === 'template' ? 'root_template' : mode === 'doc' ? 'root_doc' : 'root',
62
- tokenPostfix: '.mirascript',
63
- tokenizer: {
64
- root: [
65
- [/[[\](){}]/, '@brackets'],
66
- // 用于修正关键字做为属性名时的高亮问题,由于与格式化字符串冲突,仅 root 规则启用,其余情况改由 semantic 高亮处理
67
- [/(0|[1-9]\d*|@identifier)(@whitespace*)(\??:)(?!:)/, ['variable.other.property', '', 'delimiter']],
68
- { include: '@common' },
69
- ],
70
- root_template: [
71
- [/[^$]+/, 'string'],
72
- [/(?=\$)/, '', '@string_interpolation.$S3'],
73
- [/[$]/, 'string'],
74
- ],
75
- common: [
76
- [
77
- /(mod)(@whitespace+)(@identifier)(?=$|@whitespace|[[({,;])/,
78
- ['keyword', '', { cases: identifierCases(undefined, 'entity.name.namespace') }],
79
- ],
80
- [
81
- /(fn)(@whitespace+)(@identifier)(?=$|@whitespace|[[({,;])/,
82
- ['keyword', '', { cases: identifierCases(undefined, 'entity.name.function') }],
83
- ],
84
- [
85
- /(for)(@whitespace+)(mut)(@whitespace+)(@identifier)(@whitespace+)(in)/,
86
- ['keyword.flow', '', 'keyword', '', { cases: identifierCases() }, '', 'keyword.flow'],
87
- ],
88
- [
89
- /(for)(@whitespace+)(@identifier)(@whitespace+)(in)/,
90
- ['keyword.flow', '', { cases: identifierCases() }, '', 'keyword.flow'],
91
- ],
92
- [
93
- /(\.)(@whitespace*)(\d+\b)/,
94
- [
95
- 'delimiter',
96
- '',
97
- {
98
- cases: {
99
- [REG_ORDINAL.source]: 'variable',
100
- '@default': 'number.float',
101
- },
102
- },
103
- ],
104
- ],
105
- [String.raw`\b(${moduleNames.join('|')})(@whitespace*(?=!?\.))`, ['type', '']],
106
- [
107
- /(\.)(@whitespace*)(@identifierNoAtOnly)(@whitespace*)(!?)(@whitespace*(?=\(|@*['"`]))/,
108
- ['delimiter', '', 'entity.name.function', '', 'delimiter', ''],
109
- ],
110
- [/(\.)(@whitespace*)(@identifier\b)/, ['delimiter', '', 'variable']],
111
- // 不可变通过 semantic token 处理,避免在无 semantic token 支持的环境下高亮不一致,此处仅用于将非保留关键字识别为 identifier。
112
- [/(let)(@whitespace+)(@identifier)(@whitespace+)(=)/, ['keyword', '', 'variable', '', 'delimiter']],
113
- [
114
- /(type)(@whitespace*)(!)(@whitespace*)([(])/,
115
- ['entity.name.function', '', 'delimiter', '', '@brackets'],
116
- ],
117
- [/(type)(@whitespace*)([-+=/~?:;,.!@$%^&|*<>])/, ['variable', '', 'delimiter']],
118
- [
119
- /(@identifierNoAtOnly)(@whitespace*)(!?)(@whitespace*(?=\(|@*['"`]))/,
120
- [
121
- {
122
- cases: identifierCases(undefined, `entity.name.function`),
123
- },
124
- '',
125
- 'delimiter',
126
- '',
127
- ],
128
- ],
129
- { include: '@whitespace' },
130
- { include: '@string' },
131
- [/(@identifier)/, { cases: identifierCases() }],
132
- [
133
- /0[xobXOB]\p{XID_Continue}*/u,
134
- {
135
- cases: {
136
- [REG_OCT.source]: 'number.octal',
137
- [REG_BIN.source]: 'number.binary',
138
- [REG_HEX.source]: 'number.hex',
139
- '@default': 'number.invalid',
140
- },
141
- },
142
- ],
143
- [
144
- REG_NUMBER,
145
- {
146
- cases: {
147
- [REG_ORDINAL.source]: 'number.ordinal',
148
- '@default': 'number.float',
149
- },
150
- },
151
- ],
152
- [/(\.\.|\?:|::|[-+=/~?:;,.!@$%^&|*<>])/, 'delimiter'],
153
- ],
154
- whitespace: [
155
- [/(@whitespace)+/, ''],
156
- [/\/\/.*$/, 'comment.line'],
157
- [/\/\*{2}(?!\/)/, 'comment.doc', '@doc_comment'],
158
- [/\/\*/, 'comment.block', '@block_comment'],
159
- ],
160
- format: [[/:(?!:)/, 'punctuation.format', '@format_string']],
161
- format_string: [
162
- [/\\./, 'string.escape.format'],
163
- [/\(/, { token: 'string.format', next: '@format_string_inner' }],
164
- [/\)/, { token: 'string.format', next: '@pop', goBack: 1 }],
165
- [/\[/, { token: 'string.format', next: '@format_string_class' }],
166
- [/[^()\\[]+/, 'string.format'],
167
- ],
168
- format_string_inner: [
169
- [/\\./, 'string.escape.format'],
170
- [/\(/, { token: 'string.format', next: '@push' }],
171
- [/\)/, { token: 'string.format', next: '@pop' }],
172
- [/\[/, { token: 'string.format', next: '@format_string_class' }],
173
- [/[^()\\[\]]+/, 'string.format'],
174
- ],
175
- format_string_class: [
176
- [/\\./, 'string.escape.format'],
177
- [/\]/, { token: 'string.format', next: '@pop' }],
178
- [/[^\\\]]+/, 'string.format'],
179
- ],
180
- string: [
181
- [/["'`]/, { token: 'string.quote.open', next: '@string_normal.$#', bracket: '@open' }],
182
- [
183
- /(@+)(["'`])/,
184
- { token: 'string.quote.open.$2$1.raw', next: '@string_verbatim.$2$1.$1', bracket: '@open' },
185
- ],
186
- ],
187
- string_normal: [
188
- [/[^'"`\\$]+/, 'string'],
189
- { include: '@string_escape' },
190
- [/(?=\$)/, '', '@string_interpolation.'],
191
- [
192
- /['"`]/,
193
- {
194
- cases: {
195
- '$S2==$#': { token: 'string.quote.close', next: '@pop', bracket: '@close' },
196
- '@default': 'string',
197
- },
198
- },
199
- ],
200
- ],
201
- string_verbatim: [
202
- [/[^'"`$]+/, 'string'],
203
- [/(?=\$)/, '', '@string_interpolation.$S3'],
204
- [
205
- /(['"`]@+)/,
206
- {
207
- cases: {
208
- '$S2==$#': { token: 'string.quote.close.raw.$#', next: '@pop', bracket: '@close' },
209
- '@default': 'string',
210
- },
211
- },
212
- ],
213
- [/['"`$]/, 'string'],
214
- ],
215
- string_escape: [
216
- [/\\([\\'"`$rntbfv0])/, 'string.escape'],
217
- [/\\u\{([0-9a-fA-F]+)\}/, 'string.escape.unicode'],
218
- [/\\x([0-9a-fA-F]{2})/, 'string.escape.ascii'],
219
- [/\\./, { token: 'string.escape.invalid' }],
220
- ],
221
- ...Object.fromEntries(
222
- Array.from({ length: MAX_VERBATIM_LENGTH }, (_, i) => {
223
- const dollarCount = i === 0 ? 1 : i;
224
- const dollarRegex = `\\\${${dollarCount}}`;
225
- return [
226
- `string_interpolation.${'@'.repeat(i)}`,
227
- [
228
- [
229
- `(${dollarRegex})(${REG_IDENTIFIER.source})`,
230
- ['punctuation.section.embedded', { cases: identifierCases({ next: '@pop' }) }],
231
- ],
232
- [
233
- String.raw`(${dollarRegex}\{)`,
234
- {
235
- token: 'punctuation.section.embedded',
236
- bracket: '@open',
237
- next: '@braced',
238
- },
239
- ],
240
- [
241
- String.raw`(${dollarRegex}\()`,
242
- {
243
- token: 'punctuation.section.embedded',
244
- bracket: '@open',
245
- next: '@parenthesized',
246
- },
247
- ],
248
- [`\\\${0,${dollarCount}}`, 'string', '@pop'],
249
- ['', '', '@pop'],
250
- ],
251
- ];
252
- }),
253
- ),
254
- string_interpolation: [[/\$*/, 'string', '@pop']],
255
-
256
- braced: [
257
- [/\{/, { token: '@brackets', next: '@braced_inner' }],
258
- [/\}/, { token: 'punctuation.section.embedded', bracket: '@close', next: '@pop' }],
259
- [/\(/, { token: '@brackets', next: '@parenthesized_inner' }],
260
- [/[[\])]/, '@brackets'],
261
- { include: '@common' },
262
- ],
263
- braced_inner: [
264
- [/\{/, { token: '@brackets', next: '@push' }],
265
- [/\}/, { token: '@brackets', next: '@pop' }],
266
- [/[[\]()]/, '@brackets'],
267
- { include: '@common' },
268
- ],
269
- parenthesized: [
270
- [/\(/, { token: '@brackets', next: '@parenthesized_inner' }],
271
- [/\)/, { token: 'punctuation.section.embedded', bracket: '@close', next: '@pop' }],
272
- [/\{/, { token: '@brackets', next: '@braced_inner' }],
273
- [/[[\]}]/, '@brackets'],
274
- { include: '@format' },
275
- { include: '@common' },
276
- ],
277
- parenthesized_inner: [
278
- [/\(/, { token: '@brackets', next: '@push' }],
279
- [/\)/, { token: '@brackets', next: '@pop' }],
280
- [/[[\]{}]/, '@brackets'],
281
- { include: '@common' },
282
- ],
35
+ /** Shared instance of highlighter. */
36
+ class HighlighterManager implements IDisposable {
37
+ private highlighterPromise: Promise<HighlighterCore> | null = null;
283
38
 
284
- block_comment: [
285
- [/\*\//, { token: 'comment.block', next: '@pop' }],
286
- [/[^*]+/, { token: 'comment.block' }],
287
- [/\*/, { token: 'comment.block' }],
288
- ],
39
+ /**
40
+ * Get the shared highlighter instance.
41
+ */
42
+ private async getHighlighter(): Promise<HighlighterCore> {
43
+ if (this.highlighterPromise) return this.highlighterPromise;
289
44
 
290
- doc_comment: [
291
- [/\*\//, { token: 'comment.doc', next: '@pop' }],
292
- [/^(\s*)\*(?!\/)/, { token: 'comment.doc' }],
293
- [/\\\*(?!\/)/, { token: 'comment.doc.escape' }],
294
- [/@(param|returns)/, { token: 'entity.name.tag.doc' }],
295
- [/\*{2}(\S|\S.*?\S)\*{2}(?!\/)/, { token: 'comment.strong' }],
296
- [/\*(\S|\S.*?\S)\*(?!\/)/, { token: 'comment.emphasis' }],
297
- [/[^*@\\]+/, { token: 'comment.doc' }],
298
- [/[*@\\]/, { token: 'comment.doc' }],
299
- ],
45
+ const [
46
+ { createHighlighterCore },
47
+ { createOnigurumaEngine },
48
+ wasm,
49
+ { mirascript, mirascriptDoc, mirascriptTemplate },
50
+ ] = await Promise.all([
51
+ import('@shikijs/core'),
52
+ import('@shikijs/engine-oniguruma'),
53
+ import('@shikijs/engine-oniguruma/wasm-inlined'),
54
+ import('@mirascript/textmate'),
55
+ ]);
56
+ this.highlighterPromise = createHighlighterCore({
57
+ langs: [mirascript, mirascriptDoc, mirascriptTemplate],
58
+ themes: [],
59
+ engine: createOnigurumaEngine(wasm),
60
+ });
61
+ return this.highlighterPromise;
62
+ }
300
63
 
301
- root_doc: [
302
- // inline doc, start with `\0`
303
- [/(?=^\0)/, { token: '', switchTo: '@inline_doc' }],
304
- [/(?=.)/, { token: '', switchTo: '@doc_mode' }],
305
- ],
306
-
307
- inline_doc: [
308
- [
309
- /(\0\(parameter(?: pattern)?\))(@whitespace+)(\.\.|)(mut)(@whitespace+)(@identifier)/,
310
- ['entity.name.label', '', 'delimiter', 'keyword.mut', '', 'variable.emphasis'],
311
- ],
312
- [
313
- /(\0\(parameter(?: pattern)?\))(@whitespace+)(\.\.|)(@identifier)/,
314
- ['entity.name.label', '', 'delimiter', 'variable.other.constant.emphasis'],
315
- ],
316
- [/(\0\([^)]+\))(@whitespace+)/, ['entity.name.label', '']],
317
- [/\b(@identifier)(?=\s*=\s*mod\s+)/, 'entity.name.namespace'],
318
- [/\b(@identifier)(?=\s*=)/, 'variable'],
319
- { include: '@doc_mode' },
320
- ],
321
-
322
- doc_mode: [
323
- [
324
- /(@identifier)(@whitespace*)(\??:)(@whitespace*)(\/\*@whitespace*<)(extern )((?:async )?function\*?)(>@whitespace*\*\/)/,
325
- [
326
- 'entity.name.function.doc',
327
- '',
328
- 'delimiter',
329
- '',
330
- 'comment.doc',
331
- 'type.doc',
332
- 'keyword.javascript',
333
- 'comment.doc',
334
- ],
335
- ],
336
- [
337
- /(@identifier)(@whitespace*)(\??:)(@whitespace*)(\/\*@whitespace*<)(extern )(class)(@whitespace*)([<>.\w]*)(>@whitespace*\*\/)/,
338
- [
339
- 'type.doc',
340
- '',
341
- 'delimiter',
342
- '',
343
- 'comment.doc',
344
- 'type.doc',
345
- 'keyword.javascript',
346
- '',
347
- 'type.javascript',
348
- 'comment.doc',
349
- ],
350
- ],
351
- [
352
- /(@identifier)(@whitespace*)(\??:)(@whitespace*)(\/\*@whitespace*<)(extern )([\w]*)(>@whitespace*\*\/)/,
353
- [
354
- 'variable.other.property.doc',
355
- '',
356
- 'delimiter',
357
- '',
358
- 'comment.doc',
359
- 'type.doc',
360
- 'type.javascript',
361
- 'comment.doc',
362
- ],
363
- ],
364
- [
365
- /(@identifier)(@whitespace*)(\??:)(@whitespace*)(\/\*@whitespace*<)(function )([.\w]*)(>@whitespace*\*\/)/,
366
- [
367
- 'entity.name.function.doc',
368
- '',
369
- 'delimiter',
370
- '',
371
- 'comment.doc',
372
- 'type.doc',
373
- 'entity.name.label',
374
- 'comment.doc',
375
- ],
376
- ],
377
- [/(@identifier)(@whitespace*)(\??:)(@whitespace+)/, ['variable.other.property', '', 'delimiter', '']],
64
+ /** Get tokens provider factory of language */
65
+ getTokensProviderFactory(languageId: string): languages.TokensProviderFactory {
66
+ return {
67
+ create: async () => {
68
+ const highlighter = await this.getHighlighter();
69
+ const { INITIAL } = await import('@shikijs/vscode-textmate');
70
+ const grammar = highlighter.getLanguage(languageId);
71
+ return new TokensProvider(grammar, INITIAL);
72
+ },
73
+ };
74
+ }
75
+ /** @inheritdoc */
76
+ dispose(): void {
77
+ const promise = this.highlighterPromise;
78
+ this.highlighterPromise = null;
79
+ if (promise) {
80
+ promise
81
+ .then((highlighter) => highlighter.dispose())
82
+ .catch(() => {
83
+ // eslint-disable-next-line no-console
84
+ console.error('Failed to dispose highlighter');
85
+ });
86
+ }
87
+ }
88
+ }
378
89
 
379
- [
380
- /(\/\*@whitespace*<)(extern )((?:async )?function\*?)(>@whitespace*\*\/)/,
381
- ['comment.doc', 'type.doc', 'keyword.javascript', 'comment.doc'],
382
- ],
383
- [
384
- /(\/\*@whitespace*<)(extern )(class)(@whitespace*)([<>.\w]*)(>@whitespace*\*\/)/,
385
- ['comment.doc', 'type.doc', 'keyword.javascript', '', 'type.javascript', 'comment.doc'],
386
- ],
387
- [
388
- /(\/\*@whitespace*<)(extern )([\w]*)(\()(\d+)(\))(>@whitespace*\*\/)/,
389
- [
390
- 'comment.doc',
391
- 'type.doc',
392
- 'type.javascript',
393
- 'delimiter',
394
- 'number.doc',
395
- 'delimiter',
396
- 'comment.doc',
397
- ],
398
- ],
399
- [
400
- /(\/\*@whitespace*<)(extern )([\w]*)([^>]*)(>@whitespace*\*\/)/,
401
- ['comment.doc', 'type.doc', 'type.javascript', '', 'comment.doc'],
402
- ],
403
- [
404
- /(\/\*@whitespace*<)(function )([.\w]*)(>@whitespace*\*\/)/,
405
- ['comment.doc', 'type.doc', 'entity.name.label', 'comment.doc'],
406
- ],
407
- [
408
- /(\/\*@whitespace*<)(\w+@whitespace*)([.\w]*)(>@whitespace*\*\/)/,
409
- ['comment.doc', 'type.doc', 'entity.name.label', 'comment.doc'],
410
- ],
90
+ /** A Monaco tokens provider that uses TextMate grammars. */
91
+ class TokensProvider implements languages.TokensProvider {
92
+ constructor(
93
+ private readonly grammar: Grammar,
94
+ private readonly initialState: StateStack,
95
+ ) {}
96
+ /** @inheritdoc */
97
+ getInitialState(): StateStack {
98
+ return this.initialState;
99
+ }
100
+ /** @inheritdoc */
101
+ tokenize(line: string, state: StateStack): languages.ILineTokens {
102
+ if (line.length >= TOKENIZE_MAX_LINE_LENGTH) {
103
+ // eslint-disable-next-line no-console
104
+ console.warn(
105
+ `MiraScript TextMate tokenization skipped for line exceeding ${TOKENIZE_MAX_LINE_LENGTH} characters: ${line.slice(0, 100)}`,
106
+ );
107
+ return {
108
+ endState: state,
109
+ tokens: [{ startIndex: 0, scopes: '' }],
110
+ };
111
+ }
411
112
 
412
- [
413
- /(let)(@whitespace+)(mut)(@whitespace+)(@identifier)/,
414
- [{ token: 'keyword.$1' }, '', 'keyword.mut', '', 'variable'],
415
- ],
416
- [/(let|const)(@whitespace+)(@identifier)/, [{ token: 'keyword.$1' }, '', 'variable.other.constant']],
417
- [/(fn)(@whitespace+)(@identifier)$/, ['keyword.fn.doc', '', 'entity.name.function.doc']],
418
- [
419
- /(fn)(@whitespace+)(@identifier)(\((?=(?:@identifier|@whitespace+|,|\.\.)+\)))/,
420
- [
421
- 'keyword.fn.doc',
422
- '',
423
- 'entity.name.function.doc',
424
- { token: '@brackets', next: '@type_doc_no_type' },
425
- ],
426
- ],
427
- [
428
- /(fn)(@whitespace+)(@identifier)(\()(\.\.)(\))/,
429
- ['keyword.fn.doc', '', 'entity.name.function.doc', '@brackets', 'delimiter', '@brackets'],
430
- ],
431
- [
432
- /(fn)(@whitespace+)(@identifier)/,
433
- ['keyword.fn.doc', '', { token: 'entity.name.function.doc', next: '@type_doc' }],
434
- ],
435
- [/[[\](){}]/, '@brackets'],
436
- { include: '@common' },
437
- ],
438
- type_doc: [
439
- [/;/, { token: 'delimiter', next: '@pop', goBack: 1 }],
440
- [/(fn)(\()/, ['type', '@brackets']],
441
- [/(type)(\()(@identifier)(\))/, ['type', '@brackets', 'variable.emphasis.doc', '@brackets']],
442
- [
443
- /(@identifier)(\??:)(@whitespace*)(fn)(\()/,
444
- ['entity.name.function.emphasis.doc', 'delimiter', '', 'type', '@brackets'],
445
- ],
446
- [/(@identifier)(\??:)/, ['variable.emphasis', 'delimiter']],
447
- [/@identifier/, 'type'],
448
- [/</, { token: 'delimiter', next: '@type_doc' }],
449
- [/>/, { token: 'delimiter', next: '@pop' }],
450
- [/[&|.,:?<>]/, 'delimiter'],
451
- [/->/, 'delimiter'],
452
- [/[[\]()]/, '@brackets'],
453
- { include: '@string' },
454
- { include: '@whitespace' },
455
- ],
456
- type_doc_no_type: [
457
- [/\)/, { token: '@brackets', next: '@pop' }],
458
- [/@identifier/, 'variable.emphasis'],
459
- [/[,.]/, 'delimiter'],
460
- [/[[\]()]/, '@brackets'],
461
- { include: '@string' },
462
- { include: '@whitespace' },
463
- ],
464
- },
465
- };
113
+ const result = this.grammar.tokenizeLine(line, state, TOKENIZE_TIME_LIMIT);
114
+ if (result.stoppedEarly) {
115
+ // eslint-disable-next-line no-console
116
+ console.warn(`MiraScript TextMate tokenization timed out: ${line.slice(0, 100)}`);
117
+ }
118
+ return {
119
+ endState: result.ruleStack,
120
+ tokens: result.tokens.map((token) => ({
121
+ startIndex: token.startIndex,
122
+ scopes: tokenScope(token.scopes),
123
+ })),
124
+ };
125
+ }
466
126
  }
467
127
 
468
- /** 注册 Mirascript TokensProvider */
128
+ /** Register TextMate-backed token providers without changing Monaco themes. */
469
129
  export function registerMiraScriptTokensProvider(): IDisposable[] {
470
- return [
471
- languages.setMonarchTokensProvider('mirascript', getTokensProvider('script')),
472
- languages.setMonarchTokensProvider('mirascript-template', getTokensProvider('template')),
473
- languages.setMonarchTokensProvider('mirascript-doc', getTokensProvider('doc')),
474
- ];
130
+ const manager = new HighlighterManager();
131
+ const disposables = CONTRIBUTE_IDS.map((id) =>
132
+ languages.registerTokensProviderFactory(id, manager.getTokensProviderFactory(id)),
133
+ );
134
+ disposables.push(manager);
135
+ return disposables;
475
136
  }
package/src/contribute.ts CHANGED
@@ -1,25 +1,25 @@
1
+ import { mirascriptLanguage, mirascriptDocLanguage, mirascriptTemplateLanguage } from '@mirascript/textmate/language';
1
2
  import { languages } from './monaco-api.js';
2
3
 
4
+ export const CONTRIBUTE_IDS = [mirascriptLanguage.name, mirascriptTemplateLanguage.name, mirascriptDocLanguage.name];
5
+
3
6
  /** 注册语言 */
4
7
  export function registerContribution(): void {
5
8
  languages.register({
6
- id: 'mirascript',
9
+ id: mirascriptLanguage.name,
7
10
  extensions: ['.mira'],
8
- aliases: ['MiraScript', 'mirascript', 'mira'],
11
+ aliases: mirascriptLanguage.aliases,
9
12
  mimetypes: ['text/x-mirascript'],
10
13
  });
11
14
 
12
15
  languages.register({
13
- id: 'mirascript-template',
16
+ id: mirascriptTemplateLanguage.name,
14
17
  extensions: ['.miratpl'],
15
- aliases: ['MiraScriptTemplate', 'mirascript-template', 'miratpl'],
18
+ aliases: mirascriptTemplateLanguage.aliases,
16
19
  mimetypes: ['text/x-mirascript-template'],
17
20
  });
18
21
 
19
22
  languages.register({
20
- id: 'mirascript-doc',
21
- extensions: [],
22
- aliases: [],
23
- mimetypes: ['text/x-mirascript-doc'],
23
+ id: mirascriptDocLanguage.name,
24
24
  });
25
25
  }
package/src/index.ts CHANGED
@@ -20,6 +20,8 @@ export class MiraScriptMonacoLoader implements IDisposable {
20
20
 
21
21
  languages.onLanguageEncountered('mirascript-template', _loadBasicFeatures);
22
22
  languages.onLanguage('mirascript-template', _loadFullFeatures);
23
+
24
+ languages.onLanguageEncountered('mirascript-doc', _loadBasicFeatures);
23
25
  }
24
26
  features: LspFeaturesConfig = {};
25
27
  private _basicFeaturesLoaded = false;