@codemirror/language 0.19.1 → 0.19.5

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 CHANGED
@@ -1,3 +1,31 @@
1
+ ## 0.19.5 (2021-11-17)
2
+
3
+ ### New features
4
+
5
+ The new function `syntaxTreeAvailable` can be used to check if a fully-parsed syntax tree is available up to a given document position.
6
+
7
+ The module now exports `syntaxParserRunning`, which tells you whether the background parser is still planning to do more work for a given editor view.
8
+
9
+ ## 0.19.4 (2021-11-13)
10
+
11
+ ### New features
12
+
13
+ `LanguageDescription.of` now takes an optional already-loaded extension.
14
+
15
+ ## 0.19.3 (2021-09-13)
16
+
17
+ ### Bug fixes
18
+
19
+ Fix an issue where a parse that skipped content with `skipUntilInView` would in some cases not be restarted when the range came into view.
20
+
21
+ ## 0.19.2 (2021-08-11)
22
+
23
+ ### Bug fixes
24
+
25
+ Fix a bug that caused `indentOnInput` to fire for the wrong kinds of transactions.
26
+
27
+ Fix a bug that could cause `indentOnInput` to apply its changes incorrectly.
28
+
1
29
  ## 0.19.1 (2021-08-11)
2
30
 
3
31
  ### Bug fixes
@@ -16,6 +44,8 @@ CodeMirror now uses lezer 0.15, which means different package names (scoped with
16
44
 
17
45
  `LezerLanguage` was renamed to `LRLanguage` (because all languages must emit Lezer-style trees, the name was misleading).
18
46
 
47
+ `Language.parseString` no longer exists. You can just call `.parser.parse(...)` instead.
48
+
19
49
  ### New features
20
50
 
21
51
  New `IndentContext.lineAt` method to access lines in a way that is aware of simulated line breaks.
package/dist/index.cjs CHANGED
@@ -181,6 +181,30 @@ function ensureSyntaxTree(state, upto, timeout = 50) {
181
181
  let parse = (_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context;
182
182
  return !parse ? null : parse.treeLen >= upto || parse.work(timeout, upto) ? parse.tree : null;
183
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;
207
+ }
184
208
  // Lezer-style Input object for a Text document.
185
209
  class DocInput {
186
210
  constructor(doc, length = doc.length) {
@@ -233,7 +257,7 @@ class ParseContext {
233
257
  The current editor viewport (or some overapproximation
234
258
  thereof). Intended to be used for opportunistically avoiding
235
259
  work (in which case
236
- [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.EditorParseContext.skipUntilInView)
260
+ [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.ParseContext.skipUntilInView)
237
261
  should be called to make sure the parser is restarted when the
238
262
  skipped region becomes visible).
239
263
  */
@@ -271,7 +295,7 @@ class ParseContext {
271
295
  work(time, upto) {
272
296
  if (upto != null && upto >= this.state.doc.length)
273
297
  upto = undefined;
274
- if (this.tree != common.Tree.empty && (upto == null ? this.treeLen == this.state.doc.length : this.treeLen >= upto)) {
298
+ if (this.tree != common.Tree.empty && this.isDone(upto !== null && upto !== void 0 ? upto : this.state.doc.length)) {
275
299
  this.takeTree();
276
300
  return true;
277
301
  }
@@ -357,6 +381,8 @@ class ParseContext {
357
381
  @internal
358
382
  */
359
383
  updateViewport(viewport) {
384
+ if (this.viewport.from == viewport.from && this.viewport.to == viewport.to)
385
+ return false;
360
386
  this.viewport = viewport;
361
387
  let startLen = this.skipped.length;
362
388
  for (let i = 0; i < this.skipped.length; i++) {
@@ -366,7 +392,10 @@ class ParseContext {
366
392
  this.skipped.splice(i--, 1);
367
393
  }
368
394
  }
369
- return this.skipped.length < startLen;
395
+ if (this.skipped.length >= startLen)
396
+ return false;
397
+ this.reset();
398
+ return true;
370
399
  }
371
400
  /**
372
401
  @internal
@@ -425,6 +454,13 @@ class ParseContext {
425
454
  return this.treeLen < pos && this.parse && this.parse.parsedPos >= pos;
426
455
  }
427
456
  /**
457
+ @internal
458
+ */
459
+ isDone(upto) {
460
+ let frags = this.fragments;
461
+ return this.treeLen >= upto && frags.length && frags[0].from == 0 && frags[0].to >= upto;
462
+ }
463
+ /**
428
464
  Get the context for the current parse, or `null` if no editor
429
465
  parse is in progress.
430
466
  */
@@ -479,6 +515,7 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
479
515
  constructor(view) {
480
516
  this.view = view;
481
517
  this.working = -1;
518
+ this.workScheduled = 0;
482
519
  // End of the current time chunk
483
520
  this.chunkEnd = -1;
484
521
  // Milliseconds of budget left for this chunk
@@ -488,12 +525,8 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
488
525
  }
489
526
  update(update) {
490
527
  let cx = this.view.state.field(Language.state).context;
491
- if (update.viewportChanged) {
492
- if (cx.updateViewport(update.view.viewport))
493
- cx.reset();
494
- if (this.view.viewport.to > cx.treeLen)
495
- this.scheduleWork();
496
- }
528
+ if (cx.updateViewport(update.view.viewport) || this.view.viewport.to > cx.treeLen)
529
+ this.scheduleWork();
497
530
  if (update.docChanged) {
498
531
  if (this.view.hasFocus)
499
532
  this.chunkBudget += 50 /* ChangeBonus */;
@@ -504,11 +537,9 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
504
537
  scheduleWork() {
505
538
  if (this.working > -1)
506
539
  return;
507
- let { state } = this.view, field = state.field(Language.state), frags = field.context.fragments;
508
- if (field.tree == field.context.tree && field.context.treeLen >= state.doc.length &&
509
- frags.length && frags[0].from == 0 && frags[0].to >= state.doc.length)
510
- return;
511
- this.working = requestIdle(this.work, { timeout: 500 /* Pause */ });
540
+ let { state } = this.view, field = state.field(Language.state);
541
+ if (field.tree != field.context.tree || !field.context.isDone(state.doc.length))
542
+ this.working = requestIdle(this.work, { timeout: 500 /* Pause */ });
512
543
  }
513
544
  work(deadline) {
514
545
  this.working = -1;
@@ -535,7 +566,11 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
535
566
  }
536
567
  checkAsyncSchedule(cx) {
537
568
  if (cx.scheduleOn) {
538
- cx.scheduleOn.then(() => this.scheduleWork());
569
+ this.workScheduled++;
570
+ cx.scheduleOn
571
+ .then(() => this.scheduleWork())
572
+ .catch(err => view.logException(this.view.state, err))
573
+ .then(() => this.workScheduled--);
539
574
  cx.scheduleOn = null;
540
575
  }
541
576
  }
@@ -543,6 +578,9 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
543
578
  if (this.working >= 0)
544
579
  cancelIdle(this.working);
545
580
  }
581
+ isWorking() {
582
+ return this.working >= 0 || this.workScheduled > 0;
583
+ }
546
584
  }, {
547
585
  eventHandlers: { focus() { this.scheduleWork(); } }
548
586
  });
@@ -605,16 +643,17 @@ class LanguageDescription {
605
643
  Optional filename pattern that should be associated with this
606
644
  language.
607
645
  */
608
- filename, loadFunc) {
646
+ filename, loadFunc,
647
+ /**
648
+ If the language has been loaded, this will hold its value.
649
+ */
650
+ support = undefined) {
609
651
  this.name = name;
610
652
  this.alias = alias;
611
653
  this.extensions = extensions;
612
654
  this.filename = filename;
613
655
  this.loadFunc = loadFunc;
614
- /**
615
- If the language has been loaded, this will hold its value.
616
- */
617
- this.support = undefined;
656
+ this.support = support;
618
657
  this.loading = null;
619
658
  }
620
659
  /**
@@ -629,7 +668,13 @@ class LanguageDescription {
629
668
  Create a language description.
630
669
  */
631
670
  static of(spec) {
632
- return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, spec.load);
671
+ let { load, support } = spec;
672
+ if (!load) {
673
+ if (!support)
674
+ throw new RangeError("Must pass either 'load' or 'support' to LanguageDescription.of");
675
+ load = () => Promise.resolve(support);
676
+ }
677
+ return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, load, support);
633
678
  }
634
679
  /**
635
680
  Look for a language in the given array of descriptions that
@@ -839,24 +884,7 @@ definitive indentation can be determined.
839
884
  const indentNodeProp = new common.NodeProp();
840
885
  // Compute the indentation for a given position from the syntax tree.
841
886
  function syntaxIndentation(cx, ast, pos) {
842
- let tree = ast.resolveInner(pos);
843
- // Enter previous nodes that end in empty error terms, which means
844
- // they were broken off by error recovery, so that indentation
845
- // works even if the constructs haven't been finished.
846
- for (let scan = tree, scanPos = pos;;) {
847
- let last = scan.childBefore(scanPos);
848
- if (!last)
849
- break;
850
- if (last.type.isError && last.from == last.to) {
851
- tree = scan;
852
- scanPos = last.from;
853
- }
854
- else {
855
- scan = last;
856
- scanPos = scan.to + 1;
857
- }
858
- }
859
- return indentFrom(tree, pos, cx);
887
+ return indentFrom(ast.resolveInner(pos).enterUnfinishedNodesBefore(pos), pos, cx);
860
888
  }
861
889
  function ignoreClosed(cx) {
862
890
  return cx.pos == cx.options.simulateBreak && cx.options.simulateDoubleBreak;
@@ -1023,7 +1051,7 @@ added at the start of a line.
1023
1051
  */
1024
1052
  function indentOnInput() {
1025
1053
  return state.EditorState.transactionFilter.of(tr => {
1026
- if (!tr.docChanged || tr.isUserEvent("input.type"))
1054
+ if (!tr.docChanged || !tr.isUserEvent("input.type"))
1027
1055
  return tr;
1028
1056
  let rules = tr.startState.languageDataAt("indentOnInput", tr.startState.selection.main.head);
1029
1057
  if (!rules.length)
@@ -1048,7 +1076,7 @@ function indentOnInput() {
1048
1076
  if (cur != norm)
1049
1077
  changes.push({ from: line.from, to: line.from + cur.length, insert: norm });
1050
1078
  }
1051
- return changes.length ? [tr, { changes }] : tr;
1079
+ return changes.length ? [tr, { changes, sequential: true }] : tr;
1052
1080
  });
1053
1081
  }
1054
1082
 
@@ -1137,4 +1165,6 @@ exports.indentString = indentString;
1137
1165
  exports.indentUnit = indentUnit;
1138
1166
  exports.language = language;
1139
1167
  exports.languageDataProp = languageDataProp;
1168
+ exports.syntaxParserRunning = syntaxParserRunning;
1140
1169
  exports.syntaxTree = syntaxTree;
1170
+ 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;
160
+ /**
142
161
  A parse context provided to parsers working on the editor content.
143
162
  */
144
163
  declare class ParseContext {
@@ -156,7 +175,7 @@ declare class ParseContext {
156
175
  The current editor viewport (or some overapproximation
157
176
  thereof). Intended to be used for opportunistically avoiding
158
177
  work (in which case
159
- [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.EditorParseContext.skipUntilInView)
178
+ [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.ParseContext.skipUntilInView)
160
179
  should be called to make sure the parser is restarted when the
161
180
  skipped region becomes visible).
162
181
  */
@@ -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 language.
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: () => Promise<LanguageSupport>;
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 { Facet, EditorState, StateEffect, StateField } from '@codemirror/state';
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
  /**
@@ -177,6 +177,30 @@ function ensureSyntaxTree(state, upto, timeout = 50) {
177
177
  let parse = (_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context;
178
178
  return !parse ? null : parse.treeLen >= upto || parse.work(timeout, upto) ? parse.tree : null;
179
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;
203
+ }
180
204
  // Lezer-style Input object for a Text document.
181
205
  class DocInput {
182
206
  constructor(doc, length = doc.length) {
@@ -229,7 +253,7 @@ class ParseContext {
229
253
  The current editor viewport (or some overapproximation
230
254
  thereof). Intended to be used for opportunistically avoiding
231
255
  work (in which case
232
- [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.EditorParseContext.skipUntilInView)
256
+ [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.ParseContext.skipUntilInView)
233
257
  should be called to make sure the parser is restarted when the
234
258
  skipped region becomes visible).
235
259
  */
@@ -267,7 +291,7 @@ class ParseContext {
267
291
  work(time, upto) {
268
292
  if (upto != null && upto >= this.state.doc.length)
269
293
  upto = undefined;
270
- if (this.tree != Tree.empty && (upto == null ? this.treeLen == this.state.doc.length : this.treeLen >= upto)) {
294
+ if (this.tree != Tree.empty && this.isDone(upto !== null && upto !== void 0 ? upto : this.state.doc.length)) {
271
295
  this.takeTree();
272
296
  return true;
273
297
  }
@@ -353,6 +377,8 @@ class ParseContext {
353
377
  @internal
354
378
  */
355
379
  updateViewport(viewport) {
380
+ if (this.viewport.from == viewport.from && this.viewport.to == viewport.to)
381
+ return false;
356
382
  this.viewport = viewport;
357
383
  let startLen = this.skipped.length;
358
384
  for (let i = 0; i < this.skipped.length; i++) {
@@ -362,7 +388,10 @@ class ParseContext {
362
388
  this.skipped.splice(i--, 1);
363
389
  }
364
390
  }
365
- return this.skipped.length < startLen;
391
+ if (this.skipped.length >= startLen)
392
+ return false;
393
+ this.reset();
394
+ return true;
366
395
  }
367
396
  /**
368
397
  @internal
@@ -421,6 +450,13 @@ class ParseContext {
421
450
  return this.treeLen < pos && this.parse && this.parse.parsedPos >= pos;
422
451
  }
423
452
  /**
453
+ @internal
454
+ */
455
+ isDone(upto) {
456
+ let frags = this.fragments;
457
+ return this.treeLen >= upto && frags.length && frags[0].from == 0 && frags[0].to >= upto;
458
+ }
459
+ /**
424
460
  Get the context for the current parse, or `null` if no editor
425
461
  parse is in progress.
426
462
  */
@@ -475,6 +511,7 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
475
511
  constructor(view) {
476
512
  this.view = view;
477
513
  this.working = -1;
514
+ this.workScheduled = 0;
478
515
  // End of the current time chunk
479
516
  this.chunkEnd = -1;
480
517
  // Milliseconds of budget left for this chunk
@@ -484,12 +521,8 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
484
521
  }
485
522
  update(update) {
486
523
  let cx = this.view.state.field(Language.state).context;
487
- if (update.viewportChanged) {
488
- if (cx.updateViewport(update.view.viewport))
489
- cx.reset();
490
- if (this.view.viewport.to > cx.treeLen)
491
- this.scheduleWork();
492
- }
524
+ if (cx.updateViewport(update.view.viewport) || this.view.viewport.to > cx.treeLen)
525
+ this.scheduleWork();
493
526
  if (update.docChanged) {
494
527
  if (this.view.hasFocus)
495
528
  this.chunkBudget += 50 /* ChangeBonus */;
@@ -500,11 +533,9 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
500
533
  scheduleWork() {
501
534
  if (this.working > -1)
502
535
  return;
503
- let { state } = this.view, field = state.field(Language.state), frags = field.context.fragments;
504
- if (field.tree == field.context.tree && field.context.treeLen >= state.doc.length &&
505
- frags.length && frags[0].from == 0 && frags[0].to >= state.doc.length)
506
- return;
507
- this.working = requestIdle(this.work, { timeout: 500 /* Pause */ });
536
+ let { state } = this.view, field = state.field(Language.state);
537
+ if (field.tree != field.context.tree || !field.context.isDone(state.doc.length))
538
+ this.working = requestIdle(this.work, { timeout: 500 /* Pause */ });
508
539
  }
509
540
  work(deadline) {
510
541
  this.working = -1;
@@ -531,7 +562,11 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
531
562
  }
532
563
  checkAsyncSchedule(cx) {
533
564
  if (cx.scheduleOn) {
534
- cx.scheduleOn.then(() => this.scheduleWork());
565
+ this.workScheduled++;
566
+ cx.scheduleOn
567
+ .then(() => this.scheduleWork())
568
+ .catch(err => logException(this.view.state, err))
569
+ .then(() => this.workScheduled--);
535
570
  cx.scheduleOn = null;
536
571
  }
537
572
  }
@@ -539,6 +574,9 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
539
574
  if (this.working >= 0)
540
575
  cancelIdle(this.working);
541
576
  }
577
+ isWorking() {
578
+ return this.working >= 0 || this.workScheduled > 0;
579
+ }
542
580
  }, {
543
581
  eventHandlers: { focus() { this.scheduleWork(); } }
544
582
  });
@@ -601,16 +639,17 @@ class LanguageDescription {
601
639
  Optional filename pattern that should be associated with this
602
640
  language.
603
641
  */
604
- filename, loadFunc) {
642
+ filename, loadFunc,
643
+ /**
644
+ If the language has been loaded, this will hold its value.
645
+ */
646
+ support = undefined) {
605
647
  this.name = name;
606
648
  this.alias = alias;
607
649
  this.extensions = extensions;
608
650
  this.filename = filename;
609
651
  this.loadFunc = loadFunc;
610
- /**
611
- If the language has been loaded, this will hold its value.
612
- */
613
- this.support = undefined;
652
+ this.support = support;
614
653
  this.loading = null;
615
654
  }
616
655
  /**
@@ -625,7 +664,13 @@ class LanguageDescription {
625
664
  Create a language description.
626
665
  */
627
666
  static of(spec) {
628
- return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, spec.load);
667
+ let { load, support } = spec;
668
+ if (!load) {
669
+ if (!support)
670
+ throw new RangeError("Must pass either 'load' or 'support' to LanguageDescription.of");
671
+ load = () => Promise.resolve(support);
672
+ }
673
+ return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, load, support);
629
674
  }
630
675
  /**
631
676
  Look for a language in the given array of descriptions that
@@ -835,24 +880,7 @@ definitive indentation can be determined.
835
880
  const indentNodeProp = /*@__PURE__*/new NodeProp();
836
881
  // Compute the indentation for a given position from the syntax tree.
837
882
  function syntaxIndentation(cx, ast, pos) {
838
- let tree = ast.resolveInner(pos);
839
- // Enter previous nodes that end in empty error terms, which means
840
- // they were broken off by error recovery, so that indentation
841
- // works even if the constructs haven't been finished.
842
- for (let scan = tree, scanPos = pos;;) {
843
- let last = scan.childBefore(scanPos);
844
- if (!last)
845
- break;
846
- if (last.type.isError && last.from == last.to) {
847
- tree = scan;
848
- scanPos = last.from;
849
- }
850
- else {
851
- scan = last;
852
- scanPos = scan.to + 1;
853
- }
854
- }
855
- return indentFrom(tree, pos, cx);
883
+ return indentFrom(ast.resolveInner(pos).enterUnfinishedNodesBefore(pos), pos, cx);
856
884
  }
857
885
  function ignoreClosed(cx) {
858
886
  return cx.pos == cx.options.simulateBreak && cx.options.simulateDoubleBreak;
@@ -1019,7 +1047,7 @@ added at the start of a line.
1019
1047
  */
1020
1048
  function indentOnInput() {
1021
1049
  return EditorState.transactionFilter.of(tr => {
1022
- if (!tr.docChanged || tr.isUserEvent("input.type"))
1050
+ if (!tr.docChanged || !tr.isUserEvent("input.type"))
1023
1051
  return tr;
1024
1052
  let rules = tr.startState.languageDataAt("indentOnInput", tr.startState.selection.main.head);
1025
1053
  if (!rules.length)
@@ -1044,7 +1072,7 @@ function indentOnInput() {
1044
1072
  if (cur != norm)
1045
1073
  changes.push({ from: line.from, to: line.from + cur.length, insert: norm });
1046
1074
  }
1047
- return changes.length ? [tr, { changes }] : tr;
1075
+ return changes.length ? [tr, { changes, sequential: true }] : tr;
1048
1076
  });
1049
1077
  }
1050
1078
 
@@ -1108,4 +1136,4 @@ function foldable(state, lineStart, lineEnd) {
1108
1136
  return syntaxFolding(state, lineStart, lineEnd);
1109
1137
  }
1110
1138
 
1111
- 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 };
1139
+ 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/language",
3
- "version": "0.19.1",
3
+ "version": "0.19.5",
4
4
  "description": "Language support infrastructure for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -29,7 +29,7 @@
29
29
  "@codemirror/state": "^0.19.0",
30
30
  "@codemirror/text": "^0.19.0",
31
31
  "@codemirror/view": "^0.19.0",
32
- "@lezer/common": "^0.15.0",
32
+ "@lezer/common": "^0.15.5",
33
33
  "@lezer/lr": "^0.15.0"
34
34
  },
35
35
  "devDependencies": {