@theia/monaco 1.76.0-next.4 → 1.76.0-next.42
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.
- package/lib/browser/textmate/textmate-tokenizer.d.ts +2 -1
- package/lib/browser/textmate/textmate-tokenizer.d.ts.map +1 -1
- package/lib/browser/textmate/textmate-tokenizer.js +6 -4
- package/lib/browser/textmate/textmate-tokenizer.js.map +1 -1
- package/lib/browser/textmate/textmate-tokenizer.spec.d.ts +2 -0
- package/lib/browser/textmate/textmate-tokenizer.spec.d.ts.map +1 -0
- package/lib/browser/textmate/textmate-tokenizer.spec.js +57 -0
- package/lib/browser/textmate/textmate-tokenizer.spec.js.map +1 -0
- package/package.json +8 -8
- package/src/browser/textmate/textmate-tokenizer.spec.ts +63 -0
- package/src/browser/textmate/textmate-tokenizer.ts +8 -5
|
@@ -12,7 +12,8 @@ export declare class TokenizerState implements monaco.languages.IState {
|
|
|
12
12
|
export interface TokenizerOption {
|
|
13
13
|
/**
|
|
14
14
|
* Maximum line length that will be handled by the TextMate tokenizer. If the length of the actual line exceeds this
|
|
15
|
-
* limit, the
|
|
15
|
+
* limit, the line is skipped and tokenization continues from the preceding state. Constructs opened on a skipped
|
|
16
|
+
* line (e.g. a block comment) are therefore not accounted for.
|
|
16
17
|
*
|
|
17
18
|
* If the `lineLimit` is not defined, it means, there are no line length limits. Otherwise, it must be a positive
|
|
18
19
|
* integer or an error will be thrown.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textmate-tokenizer.d.ts","sourceRoot":"","sources":["../../../src/browser/textmate/textmate-tokenizer.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAW,QAAQ,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,MAAM,MAAM,2BAA2B,CAAC;AAEpD,qBAAa,cAAe,YAAW,MAAM,CAAC,SAAS,CAAC,MAAM;aAGtC,UAAU,EAAE,UAAU;gBAAtB,UAAU,EAAE,UAAU;IAG1C,KAAK,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM;IAIhC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,OAAO;CAIlD;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAE5B
|
|
1
|
+
{"version":3,"file":"textmate-tokenizer.d.ts","sourceRoot":"","sources":["../../../src/browser/textmate/textmate-tokenizer.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAW,QAAQ,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,MAAM,MAAM,2BAA2B,CAAC;AAEpD,qBAAa,cAAe,YAAW,MAAM,CAAC,SAAS,CAAC,MAAM;aAGtC,UAAU,EAAE,UAAU;gBAAtB,UAAU,EAAE,UAAU;IAG1C,KAAK,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM;IAIhC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,OAAO;CAIlD;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAE5B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CAEtB;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,GAAG,MAAM,CAAC,SAAS,CAAC,qBAAqB,GAAG,MAAM,CAAC,SAAS,CAAC,cAAc,CAkC7J"}
|
|
@@ -38,8 +38,10 @@ function createTextmateTokenizer(grammar, options) {
|
|
|
38
38
|
getInitialState: () => new TokenizerState(vscode_textmate_1.INITIAL),
|
|
39
39
|
tokenizeEncoded(line, state) {
|
|
40
40
|
if (options.lineLimit !== undefined && line.length > options.lineLimit) {
|
|
41
|
-
// Skip tokenizing the line if it exceeds the line limit.
|
|
42
|
-
|
|
41
|
+
// Skip tokenizing the line if it exceeds the line limit. The state must be
|
|
42
|
+
// handed back as-is: `state.stateStack` is the raw `vscode-textmate` stack, and
|
|
43
|
+
// Monaco passes whatever is returned here straight into the next line.
|
|
44
|
+
return { endState: state, tokens: new Uint32Array() };
|
|
43
45
|
}
|
|
44
46
|
const result = grammar.tokenizeLine2(line, state.stateStack, 500);
|
|
45
47
|
return {
|
|
@@ -49,8 +51,8 @@ function createTextmateTokenizer(grammar, options) {
|
|
|
49
51
|
},
|
|
50
52
|
tokenize(line, state) {
|
|
51
53
|
if (options.lineLimit !== undefined && line.length > options.lineLimit) {
|
|
52
|
-
// Skip tokenizing the line if it exceeds the line limit.
|
|
53
|
-
return { endState: state
|
|
54
|
+
// Skip tokenizing the line if it exceeds the line limit. See `tokenizeEncoded`.
|
|
55
|
+
return { endState: state, tokens: [] };
|
|
54
56
|
}
|
|
55
57
|
const result = grammar.tokenizeLine(line, state.stateStack, 500);
|
|
56
58
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textmate-tokenizer.js","sourceRoot":"","sources":["../../../src/browser/textmate/textmate-tokenizer.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,0CAA0C;AAC1C,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;;
|
|
1
|
+
{"version":3,"file":"textmate-tokenizer.js","sourceRoot":"","sources":["../../../src/browser/textmate/textmate-tokenizer.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,0CAA0C;AAC1C,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;;AAsChF,0DAkCC;AAtED,qDAAgE;AAGhE,MAAa,cAAc;IAEvB,YACoB,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IACtC,CAAC;IAEL,KAAK;QACD,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,CAAC,KAA8B;QACjC,OAAO,KAAK,YAAY,cAAc,IAAI,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC;IACvG,CAAC;CAEJ;AAdD,wCAcC;AAmBD,SAAgB,uBAAuB,CAAC,OAAiB,EAAE,OAAwB;IAC/E,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;QACtG,MAAM,IAAI,KAAK,CAAC,sDAAsD,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;IAChG,CAAC;IACD,OAAO;QACH,eAAe,EAAE,GAAG,EAAE,CAAC,IAAI,cAAc,CAAC,yBAAO,CAAC;QAClD,eAAe,CAAC,IAAY,EAAE,KAAqB;YAC/C,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;gBACrE,2EAA2E;gBAC3E,gFAAgF;gBAChF,uEAAuE;gBACvE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,WAAW,EAAE,EAAE,CAAC;YAC1D,CAAC;YACD,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;YAClE,OAAO;gBACH,QAAQ,EAAE,IAAI,cAAc,CAAC,MAAM,CAAC,SAAS,CAAC;gBAC9C,MAAM,EAAE,MAAM,CAAC,MAAM;aACxB,CAAC;QACN,CAAC;QACD,QAAQ,CAAC,IAAY,EAAE,KAAqB;YACxC,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;gBACrE,gFAAgF;gBAChF,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;YAC3C,CAAC;YACD,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;YACjE,OAAO;gBACH,QAAQ,EAAE,IAAI,cAAc,CAAC,MAAM,CAAC,SAAS,CAAC;gBAC9C,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBAC5B,UAAU,EAAE,CAAC,CAAC,UAAU;oBACxB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;iBACvC,CAAC,CAAC;aACN,CAAC;QACN,CAAC;KACJ,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textmate-tokenizer.spec.d.ts","sourceRoot":"","sources":["../../../src/browser/textmate/textmate-tokenizer.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// *****************************************************************************
|
|
3
|
+
// Copyright (C) 2026 Matthew Farrow and others.
|
|
4
|
+
//
|
|
5
|
+
// This program and the accompanying materials are made available under the
|
|
6
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
+
//
|
|
9
|
+
// This Source Code may also be made available under the following Secondary
|
|
10
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
+
// with the GNU Classpath Exception which is available at
|
|
13
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
14
|
+
//
|
|
15
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
|
+
// *****************************************************************************
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
const chai_1 = require("chai");
|
|
19
|
+
const textmate_tokenizer_1 = require("./textmate-tokenizer");
|
|
20
|
+
describe('createTextmateTokenizer', () => {
|
|
21
|
+
// A grammar that hands back the rule stack it was given, so the tests only observe
|
|
22
|
+
// what the tokenizer itself does with the state.
|
|
23
|
+
const grammar = {
|
|
24
|
+
tokenizeLine: (line, ruleStack) => ({ tokens: [], ruleStack }),
|
|
25
|
+
tokenizeLine2: (line, ruleStack) => ({ tokens: new Uint32Array(), ruleStack })
|
|
26
|
+
};
|
|
27
|
+
const tokenizer = (0, textmate_tokenizer_1.createTextmateTokenizer)(grammar, { lineLimit: 8 });
|
|
28
|
+
const longLine = 'x'.repeat(16);
|
|
29
|
+
// Monaco stores the end state and passes it into the next line. Returning anything
|
|
30
|
+
// other than a `TokenizerState` breaks the line after the skipped one: a short line
|
|
31
|
+
// silently restarts the grammar, and a second skipped line returns `undefined`,
|
|
32
|
+
// which Monaco rejects with 'Cannot set null/undefined state', stopping tokenization
|
|
33
|
+
// for the whole model.
|
|
34
|
+
it('should keep the state when tokenizeEncoded skips a line over the limit', () => {
|
|
35
|
+
const state = tokenizer.getInitialState();
|
|
36
|
+
const { endState } = tokenizer.tokenizeEncoded(longLine, state);
|
|
37
|
+
(0, chai_1.expect)(endState).to.be.an.instanceOf(textmate_tokenizer_1.TokenizerState);
|
|
38
|
+
(0, chai_1.expect)(endState).to.equal(state);
|
|
39
|
+
});
|
|
40
|
+
it('should keep the state when tokenize skips a line over the limit', () => {
|
|
41
|
+
const state = tokenizer.getInitialState();
|
|
42
|
+
const { endState } = tokenizer.tokenize(longLine, state);
|
|
43
|
+
(0, chai_1.expect)(endState).to.be.an.instanceOf(textmate_tokenizer_1.TokenizerState);
|
|
44
|
+
(0, chai_1.expect)(endState).to.equal(state);
|
|
45
|
+
});
|
|
46
|
+
it('should tokenize the line after a skipped one from the same state', () => {
|
|
47
|
+
const initial = tokenizer.getInitialState();
|
|
48
|
+
const afterSkipped = tokenizer.tokenizeEncoded(longLine, initial).endState;
|
|
49
|
+
// Two skipped lines in a row is where the broken state surfaced as an exception.
|
|
50
|
+
const afterSecondSkipped = tokenizer.tokenizeEncoded(longLine, afterSkipped).endState;
|
|
51
|
+
(0, chai_1.expect)(afterSecondSkipped).to.be.an.instanceOf(textmate_tokenizer_1.TokenizerState);
|
|
52
|
+
const { endState } = tokenizer.tokenizeEncoded('short', afterSecondSkipped);
|
|
53
|
+
(0, chai_1.expect)(endState).to.be.an.instanceOf(textmate_tokenizer_1.TokenizerState);
|
|
54
|
+
(0, chai_1.expect)(endState.stateStack).to.equal(initial.stateStack);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=textmate-tokenizer.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textmate-tokenizer.spec.js","sourceRoot":"","sources":["../../../src/browser/textmate/textmate-tokenizer.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theia/monaco",
|
|
3
|
-
"version": "1.76.0-next.
|
|
3
|
+
"version": "1.76.0-next.42+b0f9e63a6",
|
|
4
4
|
"description": "Theia - Monaco Extension",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@theia/core": "1.76.0-next.
|
|
7
|
-
"@theia/editor": "1.76.0-next.
|
|
8
|
-
"@theia/filesystem": "1.76.0-next.
|
|
9
|
-
"@theia/markers": "1.76.0-next.
|
|
6
|
+
"@theia/core": "1.76.0-next.42+b0f9e63a6",
|
|
7
|
+
"@theia/editor": "1.76.0-next.42+b0f9e63a6",
|
|
8
|
+
"@theia/filesystem": "1.76.0-next.42+b0f9e63a6",
|
|
9
|
+
"@theia/markers": "1.76.0-next.42+b0f9e63a6",
|
|
10
10
|
"@theia/monaco-editor-core": "1.108.201",
|
|
11
|
-
"@theia/outline-view": "1.76.0-next.
|
|
12
|
-
"@theia/workspace": "1.76.0-next.
|
|
11
|
+
"@theia/outline-view": "1.76.0-next.42+b0f9e63a6",
|
|
12
|
+
"@theia/workspace": "1.76.0-next.42+b0f9e63a6",
|
|
13
13
|
"fast-plist": "^0.1.3",
|
|
14
14
|
"idb": "^4.0.5",
|
|
15
15
|
"jsonc-parser": "^3.3.1",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"nyc": {
|
|
58
58
|
"extends": "../../configs/nyc.json"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "b0f9e63a6d331135265869a341dce7c7f1eef158"
|
|
61
61
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 Matthew Farrow and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { expect } from 'chai';
|
|
18
|
+
import { IGrammar, StateStack } from 'vscode-textmate';
|
|
19
|
+
import { createTextmateTokenizer, TokenizerState } from './textmate-tokenizer';
|
|
20
|
+
|
|
21
|
+
describe('createTextmateTokenizer', () => {
|
|
22
|
+
|
|
23
|
+
// A grammar that hands back the rule stack it was given, so the tests only observe
|
|
24
|
+
// what the tokenizer itself does with the state.
|
|
25
|
+
const grammar = <IGrammar><unknown>{
|
|
26
|
+
tokenizeLine: (line: string, ruleStack: StateStack) => ({ tokens: [], ruleStack }),
|
|
27
|
+
tokenizeLine2: (line: string, ruleStack: StateStack) => ({ tokens: new Uint32Array(), ruleStack })
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const tokenizer = createTextmateTokenizer(grammar, { lineLimit: 8 });
|
|
31
|
+
const longLine = 'x'.repeat(16);
|
|
32
|
+
|
|
33
|
+
// Monaco stores the end state and passes it into the next line. Returning anything
|
|
34
|
+
// other than a `TokenizerState` breaks the line after the skipped one: a short line
|
|
35
|
+
// silently restarts the grammar, and a second skipped line returns `undefined`,
|
|
36
|
+
// which Monaco rejects with 'Cannot set null/undefined state', stopping tokenization
|
|
37
|
+
// for the whole model.
|
|
38
|
+
it('should keep the state when tokenizeEncoded skips a line over the limit', () => {
|
|
39
|
+
const state = tokenizer.getInitialState();
|
|
40
|
+
const { endState } = tokenizer.tokenizeEncoded(longLine, state);
|
|
41
|
+
expect(endState).to.be.an.instanceOf(TokenizerState);
|
|
42
|
+
expect(endState).to.equal(state);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should keep the state when tokenize skips a line over the limit', () => {
|
|
46
|
+
const state = tokenizer.getInitialState();
|
|
47
|
+
const { endState } = tokenizer.tokenize(longLine, state);
|
|
48
|
+
expect(endState).to.be.an.instanceOf(TokenizerState);
|
|
49
|
+
expect(endState).to.equal(state);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should tokenize the line after a skipped one from the same state', () => {
|
|
53
|
+
const initial = tokenizer.getInitialState();
|
|
54
|
+
const afterSkipped = tokenizer.tokenizeEncoded(longLine, initial).endState;
|
|
55
|
+
// Two skipped lines in a row is where the broken state surfaced as an exception.
|
|
56
|
+
const afterSecondSkipped = tokenizer.tokenizeEncoded(longLine, afterSkipped).endState;
|
|
57
|
+
expect(afterSecondSkipped).to.be.an.instanceOf(TokenizerState);
|
|
58
|
+
const { endState } = tokenizer.tokenizeEncoded('short', afterSecondSkipped);
|
|
59
|
+
expect(endState).to.be.an.instanceOf(TokenizerState);
|
|
60
|
+
expect((<TokenizerState>endState).stateStack).to.equal((<TokenizerState>initial).stateStack);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
});
|
|
@@ -40,7 +40,8 @@ export interface TokenizerOption {
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* Maximum line length that will be handled by the TextMate tokenizer. If the length of the actual line exceeds this
|
|
43
|
-
* limit, the
|
|
43
|
+
* limit, the line is skipped and tokenization continues from the preceding state. Constructs opened on a skipped
|
|
44
|
+
* line (e.g. a block comment) are therefore not accounted for.
|
|
44
45
|
*
|
|
45
46
|
* If the `lineLimit` is not defined, it means, there are no line length limits. Otherwise, it must be a positive
|
|
46
47
|
* integer or an error will be thrown.
|
|
@@ -57,8 +58,10 @@ export function createTextmateTokenizer(grammar: IGrammar, options: TokenizerOpt
|
|
|
57
58
|
getInitialState: () => new TokenizerState(INITIAL),
|
|
58
59
|
tokenizeEncoded(line: string, state: TokenizerState): monaco.languages.IEncodedLineTokens {
|
|
59
60
|
if (options.lineLimit !== undefined && line.length > options.lineLimit) {
|
|
60
|
-
// Skip tokenizing the line if it exceeds the line limit.
|
|
61
|
-
|
|
61
|
+
// Skip tokenizing the line if it exceeds the line limit. The state must be
|
|
62
|
+
// handed back as-is: `state.stateStack` is the raw `vscode-textmate` stack, and
|
|
63
|
+
// Monaco passes whatever is returned here straight into the next line.
|
|
64
|
+
return { endState: state, tokens: new Uint32Array() };
|
|
62
65
|
}
|
|
63
66
|
const result = grammar.tokenizeLine2(line, state.stateStack, 500);
|
|
64
67
|
return {
|
|
@@ -68,8 +71,8 @@ export function createTextmateTokenizer(grammar: IGrammar, options: TokenizerOpt
|
|
|
68
71
|
},
|
|
69
72
|
tokenize(line: string, state: TokenizerState): monaco.languages.ILineTokens {
|
|
70
73
|
if (options.lineLimit !== undefined && line.length > options.lineLimit) {
|
|
71
|
-
// Skip tokenizing the line if it exceeds the line limit.
|
|
72
|
-
return { endState: state
|
|
74
|
+
// Skip tokenizing the line if it exceeds the line limit. See `tokenizeEncoded`.
|
|
75
|
+
return { endState: state, tokens: [] };
|
|
73
76
|
}
|
|
74
77
|
const result = grammar.tokenizeLine(line, state.stateStack, 500);
|
|
75
78
|
return {
|