@codemirror/language 0.19.3 → 0.19.7
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/CHANGELOG.md +28 -0
- package/dist/index.cjs +80 -33
- package/dist/index.d.ts +28 -3
- package/dist/index.js +81 -36
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
## 0.19.7 (2021-12-02)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix an issue where the parse worker could incorrectly stop working when the parse tree has skipped gaps in it.
|
|
6
|
+
|
|
7
|
+
## 0.19.6 (2021-11-26)
|
|
8
|
+
|
|
9
|
+
### Bug fixes
|
|
10
|
+
|
|
11
|
+
Fixes an issue where the background parse work would be scheduled too aggressively, degrading responsiveness on a newly-created editor with a large document.
|
|
12
|
+
|
|
13
|
+
Improve initial highlight for mixed-language editors and limit the amount of parsing done on state creation for faster startup.
|
|
14
|
+
|
|
15
|
+
## 0.19.5 (2021-11-17)
|
|
16
|
+
|
|
17
|
+
### New features
|
|
18
|
+
|
|
19
|
+
The new function `syntaxTreeAvailable` can be used to check if a fully-parsed syntax tree is available up to a given document position.
|
|
20
|
+
|
|
21
|
+
The module now exports `syntaxParserRunning`, which tells you whether the background parser is still planning to do more work for a given editor view.
|
|
22
|
+
|
|
23
|
+
## 0.19.4 (2021-11-13)
|
|
24
|
+
|
|
25
|
+
### New features
|
|
26
|
+
|
|
27
|
+
`LanguageDescription.of` now takes an optional already-loaded extension.
|
|
28
|
+
|
|
1
29
|
## 0.19.3 (2021-09-13)
|
|
2
30
|
|
|
3
31
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -179,7 +179,31 @@ up to that point if the tree isn't already available.
|
|
|
179
179
|
function ensureSyntaxTree(state, upto, timeout = 50) {
|
|
180
180
|
var _a;
|
|
181
181
|
let parse = (_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context;
|
|
182
|
-
return !parse ? null : parse.
|
|
182
|
+
return !parse ? null : parse.isDone(upto) || parse.work(timeout, upto) ? parse.tree : null;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
Queries whether there is a full syntax tree available up to the
|
|
186
|
+
given document position. If there isn't, the background parse
|
|
187
|
+
process _might_ still be working and update the tree further, but
|
|
188
|
+
there is no guarantee of that—the parser will [stop
|
|
189
|
+
working](https://codemirror.net/6/docs/ref/#language.syntaxParserStopped) when it has spent a
|
|
190
|
+
certain amount of time or has moved beyond the visible viewport.
|
|
191
|
+
Always returns false if no language has been enabled.
|
|
192
|
+
*/
|
|
193
|
+
function syntaxTreeAvailable(state, upto = state.doc.length) {
|
|
194
|
+
var _a;
|
|
195
|
+
return ((_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context.isDone(upto)) || false;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
Tells you whether the language parser is planning to do more
|
|
199
|
+
parsing work (in a `requestIdleCallback` pseudo-thread) or has
|
|
200
|
+
stopped running, either because it parsed the entire document,
|
|
201
|
+
because it spent too much time and was cut off, or because there
|
|
202
|
+
is no language parser enabled.
|
|
203
|
+
*/
|
|
204
|
+
function syntaxParserRunning(view) {
|
|
205
|
+
var _a;
|
|
206
|
+
return ((_a = view.plugin(parseWorker)) === null || _a === void 0 ? void 0 : _a.isWorking()) || false;
|
|
183
207
|
}
|
|
184
208
|
// Lezer-style Input object for a Text document.
|
|
185
209
|
class DocInput {
|
|
@@ -277,12 +301,12 @@ class ParseContext {
|
|
|
277
301
|
}
|
|
278
302
|
return this.withContext(() => {
|
|
279
303
|
var _a;
|
|
304
|
+
let endTime = Date.now() + time;
|
|
280
305
|
if (!this.parse)
|
|
281
306
|
this.parse = this.startParse();
|
|
282
307
|
if (upto != null && (this.parse.stoppedAt == null || this.parse.stoppedAt > upto) &&
|
|
283
308
|
upto < this.state.doc.length)
|
|
284
309
|
this.parse.stopAt(upto);
|
|
285
|
-
let endTime = Date.now() + time;
|
|
286
310
|
for (;;) {
|
|
287
311
|
let done = this.parse.advance();
|
|
288
312
|
if (done) {
|
|
@@ -305,10 +329,11 @@ class ParseContext {
|
|
|
305
329
|
*/
|
|
306
330
|
takeTree() {
|
|
307
331
|
let pos, tree;
|
|
308
|
-
if (this.parse && (pos = this.parse.parsedPos)
|
|
332
|
+
if (this.parse && (pos = this.parse.parsedPos) >= this.treeLen) {
|
|
309
333
|
if (this.parse.stoppedAt == null || this.parse.stoppedAt > pos)
|
|
310
334
|
this.parse.stopAt(pos);
|
|
311
335
|
this.withContext(() => { while (!(tree = this.parse.advance())) { } });
|
|
336
|
+
this.treeLen = pos;
|
|
312
337
|
this.tree = tree;
|
|
313
338
|
this.fragments = this.withoutTempSkipped(common.TreeFragment.addTree(this.tree, this.fragments, true));
|
|
314
339
|
this.parse = null;
|
|
@@ -426,13 +451,8 @@ class ParseContext {
|
|
|
426
451
|
/**
|
|
427
452
|
@internal
|
|
428
453
|
*/
|
|
429
|
-
movedPast(pos) {
|
|
430
|
-
return this.treeLen < pos && this.parse && this.parse.parsedPos >= pos;
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
@internal
|
|
434
|
-
*/
|
|
435
454
|
isDone(upto) {
|
|
455
|
+
upto = Math.min(upto, this.state.doc.length);
|
|
436
456
|
let frags = this.fragments;
|
|
437
457
|
return this.treeLen >= upto && frags.length && frags[0].from == 0 && frags[0].to >= upto;
|
|
438
458
|
}
|
|
@@ -462,13 +482,14 @@ class LanguageState {
|
|
|
462
482
|
// state updates with parse work beyond the viewport.
|
|
463
483
|
let upto = this.context.treeLen == tr.startState.doc.length ? undefined
|
|
464
484
|
: Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);
|
|
465
|
-
if (!newCx.work(
|
|
485
|
+
if (!newCx.work(20 /* Apply */, upto))
|
|
466
486
|
newCx.takeTree();
|
|
467
487
|
return new LanguageState(newCx);
|
|
468
488
|
}
|
|
469
489
|
static init(state) {
|
|
470
|
-
let
|
|
471
|
-
|
|
490
|
+
let vpTo = Math.min(3000 /* InitViewport */, state.doc.length);
|
|
491
|
+
let parseState = new ParseContext(state.facet(language).parser, state, [], common.Tree.empty, 0, { from: 0, to: vpTo }, [], null);
|
|
492
|
+
if (!parseState.work(20 /* Apply */, vpTo))
|
|
472
493
|
parseState.takeTree();
|
|
473
494
|
return new LanguageState(parseState);
|
|
474
495
|
}
|
|
@@ -484,13 +505,22 @@ Language.state = state.StateField.define({
|
|
|
484
505
|
return value.apply(tr);
|
|
485
506
|
}
|
|
486
507
|
});
|
|
487
|
-
let requestIdle =
|
|
488
|
-
|
|
489
|
-
|
|
508
|
+
let requestIdle = (callback) => {
|
|
509
|
+
let timeout = setTimeout(() => callback(), 500 /* MaxPause */);
|
|
510
|
+
return () => clearTimeout(timeout);
|
|
511
|
+
};
|
|
512
|
+
if (typeof requestIdleCallback != "undefined")
|
|
513
|
+
requestIdle = (callback) => {
|
|
514
|
+
let idle = -1, timeout = setTimeout(() => {
|
|
515
|
+
idle = requestIdleCallback(callback, { timeout: 500 /* MaxPause */ - 100 /* MinPause */ });
|
|
516
|
+
}, 100 /* MinPause */);
|
|
517
|
+
return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);
|
|
518
|
+
};
|
|
490
519
|
const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
|
|
491
520
|
constructor(view) {
|
|
492
521
|
this.view = view;
|
|
493
|
-
this.working =
|
|
522
|
+
this.working = null;
|
|
523
|
+
this.workScheduled = 0;
|
|
494
524
|
// End of the current time chunk
|
|
495
525
|
this.chunkEnd = -1;
|
|
496
526
|
// Milliseconds of budget left for this chunk
|
|
@@ -510,14 +540,14 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
|
|
|
510
540
|
this.checkAsyncSchedule(cx);
|
|
511
541
|
}
|
|
512
542
|
scheduleWork() {
|
|
513
|
-
if (this.working
|
|
543
|
+
if (this.working)
|
|
514
544
|
return;
|
|
515
545
|
let { state } = this.view, field = state.field(Language.state);
|
|
516
546
|
if (field.tree != field.context.tree || !field.context.isDone(state.doc.length))
|
|
517
|
-
this.working = requestIdle(this.work
|
|
547
|
+
this.working = requestIdle(this.work);
|
|
518
548
|
}
|
|
519
549
|
work(deadline) {
|
|
520
|
-
this.working =
|
|
550
|
+
this.working = null;
|
|
521
551
|
let now = Date.now();
|
|
522
552
|
if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk
|
|
523
553
|
this.chunkEnd = now + 30000 /* ChunkTime */;
|
|
@@ -526,28 +556,36 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
|
|
|
526
556
|
if (this.chunkBudget <= 0)
|
|
527
557
|
return; // No more budget
|
|
528
558
|
let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
|
|
529
|
-
if (field.tree == field.context.tree && field.context.
|
|
559
|
+
if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* MaxParseAhead */))
|
|
530
560
|
return;
|
|
531
|
-
let time = Math.min(this.chunkBudget, deadline ? Math.max(25 /* MinSlice */, deadline.timeRemaining()) :
|
|
532
|
-
let
|
|
561
|
+
let time = Math.min(this.chunkBudget, 100 /* Slice */, deadline ? Math.max(25 /* MinSlice */, deadline.timeRemaining() - 5) : 1e9);
|
|
562
|
+
let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1000;
|
|
563
|
+
let done = field.context.work(time, vpTo + (viewportFirst ? 0 : 100000 /* MaxParseAhead */));
|
|
533
564
|
this.chunkBudget -= Date.now() - now;
|
|
534
|
-
if (done || this.chunkBudget <= 0
|
|
565
|
+
if (done || this.chunkBudget <= 0) {
|
|
535
566
|
field.context.takeTree();
|
|
536
567
|
this.view.dispatch({ effects: Language.setState.of(new LanguageState(field.context)) });
|
|
537
568
|
}
|
|
538
|
-
if (
|
|
569
|
+
if (this.chunkBudget > 0 && !(done && !viewportFirst))
|
|
539
570
|
this.scheduleWork();
|
|
540
571
|
this.checkAsyncSchedule(field.context);
|
|
541
572
|
}
|
|
542
573
|
checkAsyncSchedule(cx) {
|
|
543
574
|
if (cx.scheduleOn) {
|
|
544
|
-
|
|
575
|
+
this.workScheduled++;
|
|
576
|
+
cx.scheduleOn
|
|
577
|
+
.then(() => this.scheduleWork())
|
|
578
|
+
.catch(err => view.logException(this.view.state, err))
|
|
579
|
+
.then(() => this.workScheduled--);
|
|
545
580
|
cx.scheduleOn = null;
|
|
546
581
|
}
|
|
547
582
|
}
|
|
548
583
|
destroy() {
|
|
549
|
-
if (this.working
|
|
550
|
-
|
|
584
|
+
if (this.working)
|
|
585
|
+
this.working();
|
|
586
|
+
}
|
|
587
|
+
isWorking() {
|
|
588
|
+
return this.working || this.workScheduled > 0;
|
|
551
589
|
}
|
|
552
590
|
}, {
|
|
553
591
|
eventHandlers: { focus() { this.scheduleWork(); } }
|
|
@@ -611,16 +649,17 @@ class LanguageDescription {
|
|
|
611
649
|
Optional filename pattern that should be associated with this
|
|
612
650
|
language.
|
|
613
651
|
*/
|
|
614
|
-
filename, loadFunc
|
|
652
|
+
filename, loadFunc,
|
|
653
|
+
/**
|
|
654
|
+
If the language has been loaded, this will hold its value.
|
|
655
|
+
*/
|
|
656
|
+
support = undefined) {
|
|
615
657
|
this.name = name;
|
|
616
658
|
this.alias = alias;
|
|
617
659
|
this.extensions = extensions;
|
|
618
660
|
this.filename = filename;
|
|
619
661
|
this.loadFunc = loadFunc;
|
|
620
|
-
|
|
621
|
-
If the language has been loaded, this will hold its value.
|
|
622
|
-
*/
|
|
623
|
-
this.support = undefined;
|
|
662
|
+
this.support = support;
|
|
624
663
|
this.loading = null;
|
|
625
664
|
}
|
|
626
665
|
/**
|
|
@@ -635,7 +674,13 @@ class LanguageDescription {
|
|
|
635
674
|
Create a language description.
|
|
636
675
|
*/
|
|
637
676
|
static of(spec) {
|
|
638
|
-
|
|
677
|
+
let { load, support } = spec;
|
|
678
|
+
if (!load) {
|
|
679
|
+
if (!support)
|
|
680
|
+
throw new RangeError("Must pass either 'load' or 'support' to LanguageDescription.of");
|
|
681
|
+
load = () => Promise.resolve(support);
|
|
682
|
+
}
|
|
683
|
+
return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, load, support);
|
|
639
684
|
}
|
|
640
685
|
/**
|
|
641
686
|
Look for a language in the given array of descriptions that
|
|
@@ -1126,4 +1171,6 @@ exports.indentString = indentString;
|
|
|
1126
1171
|
exports.indentUnit = indentUnit;
|
|
1127
1172
|
exports.language = language;
|
|
1128
1173
|
exports.languageDataProp = languageDataProp;
|
|
1174
|
+
exports.syntaxParserRunning = syntaxParserRunning;
|
|
1129
1175
|
exports.syntaxTree = syntaxTree;
|
|
1176
|
+
exports.syntaxTreeAvailable = syntaxTreeAvailable;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NodeProp, NodeType, Parser, Tree, TreeFragment, Input, PartialParse, SyntaxNode } from '@lezer/common';
|
|
2
2
|
import { LRParser, ParserConfig } from '@lezer/lr';
|
|
3
3
|
import { Facet, Extension, EditorState } from '@codemirror/state';
|
|
4
|
+
import { EditorView } from '@codemirror/view';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
Node prop stored in a grammar's top syntax node to provide the
|
|
@@ -139,6 +140,24 @@ up to that point if the tree isn't already available.
|
|
|
139
140
|
*/
|
|
140
141
|
declare function ensureSyntaxTree(state: EditorState, upto: number, timeout?: number): Tree | null;
|
|
141
142
|
/**
|
|
143
|
+
Queries whether there is a full syntax tree available up to the
|
|
144
|
+
given document position. If there isn't, the background parse
|
|
145
|
+
process _might_ still be working and update the tree further, but
|
|
146
|
+
there is no guarantee of that—the parser will [stop
|
|
147
|
+
working](https://codemirror.net/6/docs/ref/#language.syntaxParserStopped) when it has spent a
|
|
148
|
+
certain amount of time or has moved beyond the visible viewport.
|
|
149
|
+
Always returns false if no language has been enabled.
|
|
150
|
+
*/
|
|
151
|
+
declare function syntaxTreeAvailable(state: EditorState, upto?: number): boolean;
|
|
152
|
+
/**
|
|
153
|
+
Tells you whether the language parser is planning to do more
|
|
154
|
+
parsing work (in a `requestIdleCallback` pseudo-thread) or has
|
|
155
|
+
stopped running, either because it parsed the entire document,
|
|
156
|
+
because it spent too much time and was cut off, or because there
|
|
157
|
+
is no language parser enabled.
|
|
158
|
+
*/
|
|
159
|
+
declare function syntaxParserRunning(view: EditorView): boolean | (() => void);
|
|
160
|
+
/**
|
|
142
161
|
A parse context provided to parsers working on the editor content.
|
|
143
162
|
*/
|
|
144
163
|
declare class ParseContext {
|
|
@@ -298,7 +317,8 @@ declare class LanguageDescription {
|
|
|
298
317
|
*/
|
|
299
318
|
alias?: readonly string[];
|
|
300
319
|
/**
|
|
301
|
-
An optional array of extensions associated with this
|
|
320
|
+
An optional array of filename extensions associated with this
|
|
321
|
+
language.
|
|
302
322
|
*/
|
|
303
323
|
extensions?: readonly string[];
|
|
304
324
|
/**
|
|
@@ -308,7 +328,12 @@ declare class LanguageDescription {
|
|
|
308
328
|
/**
|
|
309
329
|
A function that will asynchronously load the language.
|
|
310
330
|
*/
|
|
311
|
-
load
|
|
331
|
+
load?: () => Promise<LanguageSupport>;
|
|
332
|
+
/**
|
|
333
|
+
Alternatively to `load`, you can provide an already loaded
|
|
334
|
+
support object. Either this or `load` should be provided.
|
|
335
|
+
*/
|
|
336
|
+
support?: LanguageSupport;
|
|
312
337
|
}): LanguageDescription;
|
|
313
338
|
/**
|
|
314
339
|
Look for a language in the given array of descriptions that
|
|
@@ -584,4 +609,4 @@ declare function foldable(state: EditorState, lineStart: number, lineEnd: number
|
|
|
584
609
|
to: number;
|
|
585
610
|
} | null;
|
|
586
611
|
|
|
587
|
-
export { IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, TreeIndentContext, continuedIndent, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldInside, foldNodeProp, foldService, foldable, getIndentUnit, getIndentation, indentNodeProp, indentOnInput, indentService, indentString, indentUnit, language, languageDataProp, syntaxTree };
|
|
612
|
+
export { IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, TreeIndentContext, continuedIndent, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldInside, foldNodeProp, foldService, foldable, getIndentUnit, getIndentation, indentNodeProp, indentOnInput, indentService, indentString, indentUnit, language, languageDataProp, syntaxParserRunning, syntaxTree, syntaxTreeAvailable };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NodeProp, Tree, TreeFragment, Parser, NodeType } from '@lezer/common';
|
|
2
|
-
import {
|
|
3
|
-
import { ViewPlugin } from '@codemirror/view';
|
|
2
|
+
import { StateEffect, StateField, Facet, EditorState } from '@codemirror/state';
|
|
3
|
+
import { ViewPlugin, logException } from '@codemirror/view';
|
|
4
4
|
import { countColumn } from '@codemirror/text';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -175,7 +175,31 @@ up to that point if the tree isn't already available.
|
|
|
175
175
|
function ensureSyntaxTree(state, upto, timeout = 50) {
|
|
176
176
|
var _a;
|
|
177
177
|
let parse = (_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context;
|
|
178
|
-
return !parse ? null : parse.
|
|
178
|
+
return !parse ? null : parse.isDone(upto) || parse.work(timeout, upto) ? parse.tree : null;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
Queries whether there is a full syntax tree available up to the
|
|
182
|
+
given document position. If there isn't, the background parse
|
|
183
|
+
process _might_ still be working and update the tree further, but
|
|
184
|
+
there is no guarantee of that—the parser will [stop
|
|
185
|
+
working](https://codemirror.net/6/docs/ref/#language.syntaxParserStopped) when it has spent a
|
|
186
|
+
certain amount of time or has moved beyond the visible viewport.
|
|
187
|
+
Always returns false if no language has been enabled.
|
|
188
|
+
*/
|
|
189
|
+
function syntaxTreeAvailable(state, upto = state.doc.length) {
|
|
190
|
+
var _a;
|
|
191
|
+
return ((_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context.isDone(upto)) || false;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
Tells you whether the language parser is planning to do more
|
|
195
|
+
parsing work (in a `requestIdleCallback` pseudo-thread) or has
|
|
196
|
+
stopped running, either because it parsed the entire document,
|
|
197
|
+
because it spent too much time and was cut off, or because there
|
|
198
|
+
is no language parser enabled.
|
|
199
|
+
*/
|
|
200
|
+
function syntaxParserRunning(view) {
|
|
201
|
+
var _a;
|
|
202
|
+
return ((_a = view.plugin(parseWorker)) === null || _a === void 0 ? void 0 : _a.isWorking()) || false;
|
|
179
203
|
}
|
|
180
204
|
// Lezer-style Input object for a Text document.
|
|
181
205
|
class DocInput {
|
|
@@ -273,12 +297,12 @@ class ParseContext {
|
|
|
273
297
|
}
|
|
274
298
|
return this.withContext(() => {
|
|
275
299
|
var _a;
|
|
300
|
+
let endTime = Date.now() + time;
|
|
276
301
|
if (!this.parse)
|
|
277
302
|
this.parse = this.startParse();
|
|
278
303
|
if (upto != null && (this.parse.stoppedAt == null || this.parse.stoppedAt > upto) &&
|
|
279
304
|
upto < this.state.doc.length)
|
|
280
305
|
this.parse.stopAt(upto);
|
|
281
|
-
let endTime = Date.now() + time;
|
|
282
306
|
for (;;) {
|
|
283
307
|
let done = this.parse.advance();
|
|
284
308
|
if (done) {
|
|
@@ -301,10 +325,11 @@ class ParseContext {
|
|
|
301
325
|
*/
|
|
302
326
|
takeTree() {
|
|
303
327
|
let pos, tree;
|
|
304
|
-
if (this.parse && (pos = this.parse.parsedPos)
|
|
328
|
+
if (this.parse && (pos = this.parse.parsedPos) >= this.treeLen) {
|
|
305
329
|
if (this.parse.stoppedAt == null || this.parse.stoppedAt > pos)
|
|
306
330
|
this.parse.stopAt(pos);
|
|
307
331
|
this.withContext(() => { while (!(tree = this.parse.advance())) { } });
|
|
332
|
+
this.treeLen = pos;
|
|
308
333
|
this.tree = tree;
|
|
309
334
|
this.fragments = this.withoutTempSkipped(TreeFragment.addTree(this.tree, this.fragments, true));
|
|
310
335
|
this.parse = null;
|
|
@@ -422,13 +447,8 @@ class ParseContext {
|
|
|
422
447
|
/**
|
|
423
448
|
@internal
|
|
424
449
|
*/
|
|
425
|
-
movedPast(pos) {
|
|
426
|
-
return this.treeLen < pos && this.parse && this.parse.parsedPos >= pos;
|
|
427
|
-
}
|
|
428
|
-
/**
|
|
429
|
-
@internal
|
|
430
|
-
*/
|
|
431
450
|
isDone(upto) {
|
|
451
|
+
upto = Math.min(upto, this.state.doc.length);
|
|
432
452
|
let frags = this.fragments;
|
|
433
453
|
return this.treeLen >= upto && frags.length && frags[0].from == 0 && frags[0].to >= upto;
|
|
434
454
|
}
|
|
@@ -458,13 +478,14 @@ class LanguageState {
|
|
|
458
478
|
// state updates with parse work beyond the viewport.
|
|
459
479
|
let upto = this.context.treeLen == tr.startState.doc.length ? undefined
|
|
460
480
|
: Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);
|
|
461
|
-
if (!newCx.work(
|
|
481
|
+
if (!newCx.work(20 /* Apply */, upto))
|
|
462
482
|
newCx.takeTree();
|
|
463
483
|
return new LanguageState(newCx);
|
|
464
484
|
}
|
|
465
485
|
static init(state) {
|
|
466
|
-
let
|
|
467
|
-
|
|
486
|
+
let vpTo = Math.min(3000 /* InitViewport */, state.doc.length);
|
|
487
|
+
let parseState = new ParseContext(state.facet(language).parser, state, [], Tree.empty, 0, { from: 0, to: vpTo }, [], null);
|
|
488
|
+
if (!parseState.work(20 /* Apply */, vpTo))
|
|
468
489
|
parseState.takeTree();
|
|
469
490
|
return new LanguageState(parseState);
|
|
470
491
|
}
|
|
@@ -480,13 +501,22 @@ Language.state = /*@__PURE__*/StateField.define({
|
|
|
480
501
|
return value.apply(tr);
|
|
481
502
|
}
|
|
482
503
|
});
|
|
483
|
-
let requestIdle =
|
|
484
|
-
|
|
485
|
-
|
|
504
|
+
let requestIdle = (callback) => {
|
|
505
|
+
let timeout = setTimeout(() => callback(), 500 /* MaxPause */);
|
|
506
|
+
return () => clearTimeout(timeout);
|
|
507
|
+
};
|
|
508
|
+
if (typeof requestIdleCallback != "undefined")
|
|
509
|
+
requestIdle = (callback) => {
|
|
510
|
+
let idle = -1, timeout = setTimeout(() => {
|
|
511
|
+
idle = requestIdleCallback(callback, { timeout: 500 /* MaxPause */ - 100 /* MinPause */ });
|
|
512
|
+
}, 100 /* MinPause */);
|
|
513
|
+
return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);
|
|
514
|
+
};
|
|
486
515
|
const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
|
|
487
516
|
constructor(view) {
|
|
488
517
|
this.view = view;
|
|
489
|
-
this.working =
|
|
518
|
+
this.working = null;
|
|
519
|
+
this.workScheduled = 0;
|
|
490
520
|
// End of the current time chunk
|
|
491
521
|
this.chunkEnd = -1;
|
|
492
522
|
// Milliseconds of budget left for this chunk
|
|
@@ -506,14 +536,14 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
|
|
|
506
536
|
this.checkAsyncSchedule(cx);
|
|
507
537
|
}
|
|
508
538
|
scheduleWork() {
|
|
509
|
-
if (this.working
|
|
539
|
+
if (this.working)
|
|
510
540
|
return;
|
|
511
541
|
let { state } = this.view, field = state.field(Language.state);
|
|
512
542
|
if (field.tree != field.context.tree || !field.context.isDone(state.doc.length))
|
|
513
|
-
this.working = requestIdle(this.work
|
|
543
|
+
this.working = requestIdle(this.work);
|
|
514
544
|
}
|
|
515
545
|
work(deadline) {
|
|
516
|
-
this.working =
|
|
546
|
+
this.working = null;
|
|
517
547
|
let now = Date.now();
|
|
518
548
|
if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk
|
|
519
549
|
this.chunkEnd = now + 30000 /* ChunkTime */;
|
|
@@ -522,28 +552,36 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
|
|
|
522
552
|
if (this.chunkBudget <= 0)
|
|
523
553
|
return; // No more budget
|
|
524
554
|
let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
|
|
525
|
-
if (field.tree == field.context.tree && field.context.
|
|
555
|
+
if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* MaxParseAhead */))
|
|
526
556
|
return;
|
|
527
|
-
let time = Math.min(this.chunkBudget, deadline ? Math.max(25 /* MinSlice */, deadline.timeRemaining()) :
|
|
528
|
-
let
|
|
557
|
+
let time = Math.min(this.chunkBudget, 100 /* Slice */, deadline ? Math.max(25 /* MinSlice */, deadline.timeRemaining() - 5) : 1e9);
|
|
558
|
+
let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1000;
|
|
559
|
+
let done = field.context.work(time, vpTo + (viewportFirst ? 0 : 100000 /* MaxParseAhead */));
|
|
529
560
|
this.chunkBudget -= Date.now() - now;
|
|
530
|
-
if (done || this.chunkBudget <= 0
|
|
561
|
+
if (done || this.chunkBudget <= 0) {
|
|
531
562
|
field.context.takeTree();
|
|
532
563
|
this.view.dispatch({ effects: Language.setState.of(new LanguageState(field.context)) });
|
|
533
564
|
}
|
|
534
|
-
if (
|
|
565
|
+
if (this.chunkBudget > 0 && !(done && !viewportFirst))
|
|
535
566
|
this.scheduleWork();
|
|
536
567
|
this.checkAsyncSchedule(field.context);
|
|
537
568
|
}
|
|
538
569
|
checkAsyncSchedule(cx) {
|
|
539
570
|
if (cx.scheduleOn) {
|
|
540
|
-
|
|
571
|
+
this.workScheduled++;
|
|
572
|
+
cx.scheduleOn
|
|
573
|
+
.then(() => this.scheduleWork())
|
|
574
|
+
.catch(err => logException(this.view.state, err))
|
|
575
|
+
.then(() => this.workScheduled--);
|
|
541
576
|
cx.scheduleOn = null;
|
|
542
577
|
}
|
|
543
578
|
}
|
|
544
579
|
destroy() {
|
|
545
|
-
if (this.working
|
|
546
|
-
|
|
580
|
+
if (this.working)
|
|
581
|
+
this.working();
|
|
582
|
+
}
|
|
583
|
+
isWorking() {
|
|
584
|
+
return this.working || this.workScheduled > 0;
|
|
547
585
|
}
|
|
548
586
|
}, {
|
|
549
587
|
eventHandlers: { focus() { this.scheduleWork(); } }
|
|
@@ -607,16 +645,17 @@ class LanguageDescription {
|
|
|
607
645
|
Optional filename pattern that should be associated with this
|
|
608
646
|
language.
|
|
609
647
|
*/
|
|
610
|
-
filename, loadFunc
|
|
648
|
+
filename, loadFunc,
|
|
649
|
+
/**
|
|
650
|
+
If the language has been loaded, this will hold its value.
|
|
651
|
+
*/
|
|
652
|
+
support = undefined) {
|
|
611
653
|
this.name = name;
|
|
612
654
|
this.alias = alias;
|
|
613
655
|
this.extensions = extensions;
|
|
614
656
|
this.filename = filename;
|
|
615
657
|
this.loadFunc = loadFunc;
|
|
616
|
-
|
|
617
|
-
If the language has been loaded, this will hold its value.
|
|
618
|
-
*/
|
|
619
|
-
this.support = undefined;
|
|
658
|
+
this.support = support;
|
|
620
659
|
this.loading = null;
|
|
621
660
|
}
|
|
622
661
|
/**
|
|
@@ -631,7 +670,13 @@ class LanguageDescription {
|
|
|
631
670
|
Create a language description.
|
|
632
671
|
*/
|
|
633
672
|
static of(spec) {
|
|
634
|
-
|
|
673
|
+
let { load, support } = spec;
|
|
674
|
+
if (!load) {
|
|
675
|
+
if (!support)
|
|
676
|
+
throw new RangeError("Must pass either 'load' or 'support' to LanguageDescription.of");
|
|
677
|
+
load = () => Promise.resolve(support);
|
|
678
|
+
}
|
|
679
|
+
return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, load, support);
|
|
635
680
|
}
|
|
636
681
|
/**
|
|
637
682
|
Look for a language in the given array of descriptions that
|
|
@@ -1097,4 +1142,4 @@ function foldable(state, lineStart, lineEnd) {
|
|
|
1097
1142
|
return syntaxFolding(state, lineStart, lineEnd);
|
|
1098
1143
|
}
|
|
1099
1144
|
|
|
1100
|
-
export { IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, TreeIndentContext, continuedIndent, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldInside, foldNodeProp, foldService, foldable, getIndentUnit, getIndentation, indentNodeProp, indentOnInput, indentService, indentString, indentUnit, language, languageDataProp, syntaxTree };
|
|
1145
|
+
export { IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, TreeIndentContext, continuedIndent, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldInside, foldNodeProp, foldService, foldable, getIndentUnit, getIndentation, indentNodeProp, indentOnInput, indentService, indentString, indentUnit, language, languageDataProp, syntaxParserRunning, syntaxTree, syntaxTreeAvailable };
|