@codemirror/language 0.19.0 → 0.19.4

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,29 @@
1
+ ## 0.19.4 (2021-11-13)
2
+
3
+ ### New features
4
+
5
+ `LanguageDescription.of` now takes an optional already-loaded extension.
6
+
7
+ ## 0.19.3 (2021-09-13)
8
+
9
+ ### Bug fixes
10
+
11
+ Fix an issue where a parse that skipped content with `skipUntilInView` would in some cases not be restarted when the range came into view.
12
+
13
+ ## 0.19.2 (2021-08-11)
14
+
15
+ ### Bug fixes
16
+
17
+ Fix a bug that caused `indentOnInput` to fire for the wrong kinds of transactions.
18
+
19
+ Fix a bug that could cause `indentOnInput` to apply its changes incorrectly.
20
+
21
+ ## 0.19.1 (2021-08-11)
22
+
23
+ ### Bug fixes
24
+
25
+ Fix incorrect versions for @lezer dependencies.
26
+
1
27
  ## 0.19.0 (2021-08-11)
2
28
 
3
29
  ### Breaking changes
@@ -10,6 +36,8 @@ CodeMirror now uses lezer 0.15, which means different package names (scoped with
10
36
 
11
37
  `LezerLanguage` was renamed to `LRLanguage` (because all languages must emit Lezer-style trees, the name was misleading).
12
38
 
39
+ `Language.parseString` no longer exists. You can just call `.parser.parse(...)` instead.
40
+
13
41
  ### New features
14
42
 
15
43
  New `IndentContext.lineAt` method to access lines in a way that is aware of simulated line breaks.
package/dist/index.cjs CHANGED
@@ -233,7 +233,7 @@ class ParseContext {
233
233
  The current editor viewport (or some overapproximation
234
234
  thereof). Intended to be used for opportunistically avoiding
235
235
  work (in which case
236
- [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.EditorParseContext.skipUntilInView)
236
+ [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.ParseContext.skipUntilInView)
237
237
  should be called to make sure the parser is restarted when the
238
238
  skipped region becomes visible).
239
239
  */
@@ -271,7 +271,7 @@ class ParseContext {
271
271
  work(time, upto) {
272
272
  if (upto != null && upto >= this.state.doc.length)
273
273
  upto = undefined;
274
- if (this.tree != common.Tree.empty && (upto == null ? this.treeLen == this.state.doc.length : this.treeLen >= upto)) {
274
+ if (this.tree != common.Tree.empty && this.isDone(upto !== null && upto !== void 0 ? upto : this.state.doc.length)) {
275
275
  this.takeTree();
276
276
  return true;
277
277
  }
@@ -357,6 +357,8 @@ class ParseContext {
357
357
  @internal
358
358
  */
359
359
  updateViewport(viewport) {
360
+ if (this.viewport.from == viewport.from && this.viewport.to == viewport.to)
361
+ return false;
360
362
  this.viewport = viewport;
361
363
  let startLen = this.skipped.length;
362
364
  for (let i = 0; i < this.skipped.length; i++) {
@@ -366,7 +368,10 @@ class ParseContext {
366
368
  this.skipped.splice(i--, 1);
367
369
  }
368
370
  }
369
- return this.skipped.length < startLen;
371
+ if (this.skipped.length >= startLen)
372
+ return false;
373
+ this.reset();
374
+ return true;
370
375
  }
371
376
  /**
372
377
  @internal
@@ -425,6 +430,13 @@ class ParseContext {
425
430
  return this.treeLen < pos && this.parse && this.parse.parsedPos >= pos;
426
431
  }
427
432
  /**
433
+ @internal
434
+ */
435
+ isDone(upto) {
436
+ let frags = this.fragments;
437
+ return this.treeLen >= upto && frags.length && frags[0].from == 0 && frags[0].to >= upto;
438
+ }
439
+ /**
428
440
  Get the context for the current parse, or `null` if no editor
429
441
  parse is in progress.
430
442
  */
@@ -488,12 +500,8 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
488
500
  }
489
501
  update(update) {
490
502
  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
- }
503
+ if (cx.updateViewport(update.view.viewport) || this.view.viewport.to > cx.treeLen)
504
+ this.scheduleWork();
497
505
  if (update.docChanged) {
498
506
  if (this.view.hasFocus)
499
507
  this.chunkBudget += 50 /* ChangeBonus */;
@@ -504,11 +512,9 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
504
512
  scheduleWork() {
505
513
  if (this.working > -1)
506
514
  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 */ });
515
+ let { state } = this.view, field = state.field(Language.state);
516
+ if (field.tree != field.context.tree || !field.context.isDone(state.doc.length))
517
+ this.working = requestIdle(this.work, { timeout: 500 /* Pause */ });
512
518
  }
513
519
  work(deadline) {
514
520
  this.working = -1;
@@ -605,16 +611,17 @@ class LanguageDescription {
605
611
  Optional filename pattern that should be associated with this
606
612
  language.
607
613
  */
608
- filename, loadFunc) {
614
+ filename, loadFunc,
615
+ /**
616
+ If the language has been loaded, this will hold its value.
617
+ */
618
+ support = undefined) {
609
619
  this.name = name;
610
620
  this.alias = alias;
611
621
  this.extensions = extensions;
612
622
  this.filename = filename;
613
623
  this.loadFunc = loadFunc;
614
- /**
615
- If the language has been loaded, this will hold its value.
616
- */
617
- this.support = undefined;
624
+ this.support = support;
618
625
  this.loading = null;
619
626
  }
620
627
  /**
@@ -629,7 +636,13 @@ class LanguageDescription {
629
636
  Create a language description.
630
637
  */
631
638
  static of(spec) {
632
- return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, spec.load);
639
+ let { load, support } = spec;
640
+ if (!load) {
641
+ if (!support)
642
+ throw new RangeError("Must pass either 'load' or 'support' to LanguageDescription.of");
643
+ load = () => Promise.resolve(support);
644
+ }
645
+ return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, load, support);
633
646
  }
634
647
  /**
635
648
  Look for a language in the given array of descriptions that
@@ -839,24 +852,7 @@ definitive indentation can be determined.
839
852
  const indentNodeProp = new common.NodeProp();
840
853
  // Compute the indentation for a given position from the syntax tree.
841
854
  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);
855
+ return indentFrom(ast.resolveInner(pos).enterUnfinishedNodesBefore(pos), pos, cx);
860
856
  }
861
857
  function ignoreClosed(cx) {
862
858
  return cx.pos == cx.options.simulateBreak && cx.options.simulateDoubleBreak;
@@ -1023,7 +1019,7 @@ added at the start of a line.
1023
1019
  */
1024
1020
  function indentOnInput() {
1025
1021
  return state.EditorState.transactionFilter.of(tr => {
1026
- if (!tr.docChanged || tr.isUserEvent("input.type"))
1022
+ if (!tr.docChanged || !tr.isUserEvent("input.type"))
1027
1023
  return tr;
1028
1024
  let rules = tr.startState.languageDataAt("indentOnInput", tr.startState.selection.main.head);
1029
1025
  if (!rules.length)
@@ -1048,7 +1044,7 @@ function indentOnInput() {
1048
1044
  if (cur != norm)
1049
1045
  changes.push({ from: line.from, to: line.from + cur.length, insert: norm });
1050
1046
  }
1051
- return changes.length ? [tr, { changes }] : tr;
1047
+ return changes.length ? [tr, { changes, sequential: true }] : tr;
1052
1048
  });
1053
1049
  }
1054
1050
 
package/dist/index.d.ts CHANGED
@@ -156,7 +156,7 @@ declare class ParseContext {
156
156
  The current editor viewport (or some overapproximation
157
157
  thereof). Intended to be used for opportunistically avoiding
158
158
  work (in which case
159
- [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.EditorParseContext.skipUntilInView)
159
+ [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.ParseContext.skipUntilInView)
160
160
  should be called to make sure the parser is restarted when the
161
161
  skipped region becomes visible).
162
162
  */
@@ -298,7 +298,8 @@ declare class LanguageDescription {
298
298
  */
299
299
  alias?: readonly string[];
300
300
  /**
301
- An optional array of extensions associated with this language.
301
+ An optional array of filename extensions associated with this
302
+ language.
302
303
  */
303
304
  extensions?: readonly string[];
304
305
  /**
@@ -308,7 +309,12 @@ declare class LanguageDescription {
308
309
  /**
309
310
  A function that will asynchronously load the language.
310
311
  */
311
- load: () => Promise<LanguageSupport>;
312
+ load?: () => Promise<LanguageSupport>;
313
+ /**
314
+ Alternatively to `load`, you can provide an already loaded
315
+ support object. Either this or `load` should be provided.
316
+ */
317
+ support?: LanguageSupport;
312
318
  }): LanguageDescription;
313
319
  /**
314
320
  Look for a language in the given array of descriptions that
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { NodeProp, Tree, TreeFragment, Parser, NodeType } from '@lezer/common';
2
- import { Facet, EditorState, StateEffect, StateField } from '@codemirror/state';
2
+ import { StateEffect, StateField, Facet, EditorState } from '@codemirror/state';
3
3
  import { ViewPlugin } from '@codemirror/view';
4
4
  import { countColumn } from '@codemirror/text';
5
5
 
@@ -229,7 +229,7 @@ class ParseContext {
229
229
  The current editor viewport (or some overapproximation
230
230
  thereof). Intended to be used for opportunistically avoiding
231
231
  work (in which case
232
- [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.EditorParseContext.skipUntilInView)
232
+ [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.ParseContext.skipUntilInView)
233
233
  should be called to make sure the parser is restarted when the
234
234
  skipped region becomes visible).
235
235
  */
@@ -267,7 +267,7 @@ class ParseContext {
267
267
  work(time, upto) {
268
268
  if (upto != null && upto >= this.state.doc.length)
269
269
  upto = undefined;
270
- if (this.tree != Tree.empty && (upto == null ? this.treeLen == this.state.doc.length : this.treeLen >= upto)) {
270
+ if (this.tree != Tree.empty && this.isDone(upto !== null && upto !== void 0 ? upto : this.state.doc.length)) {
271
271
  this.takeTree();
272
272
  return true;
273
273
  }
@@ -353,6 +353,8 @@ class ParseContext {
353
353
  @internal
354
354
  */
355
355
  updateViewport(viewport) {
356
+ if (this.viewport.from == viewport.from && this.viewport.to == viewport.to)
357
+ return false;
356
358
  this.viewport = viewport;
357
359
  let startLen = this.skipped.length;
358
360
  for (let i = 0; i < this.skipped.length; i++) {
@@ -362,7 +364,10 @@ class ParseContext {
362
364
  this.skipped.splice(i--, 1);
363
365
  }
364
366
  }
365
- return this.skipped.length < startLen;
367
+ if (this.skipped.length >= startLen)
368
+ return false;
369
+ this.reset();
370
+ return true;
366
371
  }
367
372
  /**
368
373
  @internal
@@ -421,6 +426,13 @@ class ParseContext {
421
426
  return this.treeLen < pos && this.parse && this.parse.parsedPos >= pos;
422
427
  }
423
428
  /**
429
+ @internal
430
+ */
431
+ isDone(upto) {
432
+ let frags = this.fragments;
433
+ return this.treeLen >= upto && frags.length && frags[0].from == 0 && frags[0].to >= upto;
434
+ }
435
+ /**
424
436
  Get the context for the current parse, or `null` if no editor
425
437
  parse is in progress.
426
438
  */
@@ -484,12 +496,8 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
484
496
  }
485
497
  update(update) {
486
498
  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
- }
499
+ if (cx.updateViewport(update.view.viewport) || this.view.viewport.to > cx.treeLen)
500
+ this.scheduleWork();
493
501
  if (update.docChanged) {
494
502
  if (this.view.hasFocus)
495
503
  this.chunkBudget += 50 /* ChangeBonus */;
@@ -500,11 +508,9 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
500
508
  scheduleWork() {
501
509
  if (this.working > -1)
502
510
  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 */ });
511
+ let { state } = this.view, field = state.field(Language.state);
512
+ if (field.tree != field.context.tree || !field.context.isDone(state.doc.length))
513
+ this.working = requestIdle(this.work, { timeout: 500 /* Pause */ });
508
514
  }
509
515
  work(deadline) {
510
516
  this.working = -1;
@@ -601,16 +607,17 @@ class LanguageDescription {
601
607
  Optional filename pattern that should be associated with this
602
608
  language.
603
609
  */
604
- filename, loadFunc) {
610
+ filename, loadFunc,
611
+ /**
612
+ If the language has been loaded, this will hold its value.
613
+ */
614
+ support = undefined) {
605
615
  this.name = name;
606
616
  this.alias = alias;
607
617
  this.extensions = extensions;
608
618
  this.filename = filename;
609
619
  this.loadFunc = loadFunc;
610
- /**
611
- If the language has been loaded, this will hold its value.
612
- */
613
- this.support = undefined;
620
+ this.support = support;
614
621
  this.loading = null;
615
622
  }
616
623
  /**
@@ -625,7 +632,13 @@ class LanguageDescription {
625
632
  Create a language description.
626
633
  */
627
634
  static of(spec) {
628
- return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, spec.load);
635
+ let { load, support } = spec;
636
+ if (!load) {
637
+ if (!support)
638
+ throw new RangeError("Must pass either 'load' or 'support' to LanguageDescription.of");
639
+ load = () => Promise.resolve(support);
640
+ }
641
+ return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, load, support);
629
642
  }
630
643
  /**
631
644
  Look for a language in the given array of descriptions that
@@ -835,24 +848,7 @@ definitive indentation can be determined.
835
848
  const indentNodeProp = /*@__PURE__*/new NodeProp();
836
849
  // Compute the indentation for a given position from the syntax tree.
837
850
  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);
851
+ return indentFrom(ast.resolveInner(pos).enterUnfinishedNodesBefore(pos), pos, cx);
856
852
  }
857
853
  function ignoreClosed(cx) {
858
854
  return cx.pos == cx.options.simulateBreak && cx.options.simulateDoubleBreak;
@@ -1019,7 +1015,7 @@ added at the start of a line.
1019
1015
  */
1020
1016
  function indentOnInput() {
1021
1017
  return EditorState.transactionFilter.of(tr => {
1022
- if (!tr.docChanged || tr.isUserEvent("input.type"))
1018
+ if (!tr.docChanged || !tr.isUserEvent("input.type"))
1023
1019
  return tr;
1024
1020
  let rules = tr.startState.languageDataAt("indentOnInput", tr.startState.selection.main.head);
1025
1021
  if (!rules.length)
@@ -1044,7 +1040,7 @@ function indentOnInput() {
1044
1040
  if (cur != norm)
1045
1041
  changes.push({ from: line.from, to: line.from + cur.length, insert: norm });
1046
1042
  }
1047
- return changes.length ? [tr, { changes }] : tr;
1043
+ return changes.length ? [tr, { changes, sequential: true }] : tr;
1048
1044
  });
1049
1045
  }
1050
1046
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/language",
3
- "version": "0.19.0",
3
+ "version": "0.19.4",
4
4
  "description": "Language support infrastructure for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -29,12 +29,12 @@
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.14.0",
33
- "@lezer/lr": "^0.14.0"
32
+ "@lezer/common": "^0.15.5",
33
+ "@lezer/lr": "^0.15.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@codemirror/buildhelper": "^0.1.5",
37
- "@lezer/javascript": "^0.14.0"
37
+ "@lezer/javascript": "^0.15.0"
38
38
  },
39
39
  "repository": {
40
40
  "type": "git",