@codemirror/language 0.19.5 → 0.19.8

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,23 @@
1
+ ## 0.19.8 (2022-03-03)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue that could cause indentation logic to use the wrong line content when indenting multiple lines at once.
6
+
7
+ ## 0.19.7 (2021-12-02)
8
+
9
+ ### Bug fixes
10
+
11
+ Fix an issue where the parse worker could incorrectly stop working when the parse tree has skipped gaps in it.
12
+
13
+ ## 0.19.6 (2021-11-26)
14
+
15
+ ### Bug fixes
16
+
17
+ Fixes an issue where the background parse work would be scheduled too aggressively, degrading responsiveness on a newly-created editor with a large document.
18
+
19
+ Improve initial highlight for mixed-language editors and limit the amount of parsing done on state creation for faster startup.
20
+
1
21
  ## 0.19.5 (2021-11-17)
2
22
 
3
23
  ### New features
package/dist/index.cjs CHANGED
@@ -179,7 +179,7 @@ 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.treeLen >= upto || parse.work(timeout, upto) ? parse.tree : null;
182
+ return !parse ? null : parse.isDone(upto) || parse.work(timeout, upto) ? parse.tree : null;
183
183
  }
184
184
  /**
185
185
  Queries whether there is a full syntax tree available up to the
@@ -301,12 +301,12 @@ class ParseContext {
301
301
  }
302
302
  return this.withContext(() => {
303
303
  var _a;
304
+ let endTime = Date.now() + time;
304
305
  if (!this.parse)
305
306
  this.parse = this.startParse();
306
307
  if (upto != null && (this.parse.stoppedAt == null || this.parse.stoppedAt > upto) &&
307
308
  upto < this.state.doc.length)
308
309
  this.parse.stopAt(upto);
309
- let endTime = Date.now() + time;
310
310
  for (;;) {
311
311
  let done = this.parse.advance();
312
312
  if (done) {
@@ -329,10 +329,11 @@ class ParseContext {
329
329
  */
330
330
  takeTree() {
331
331
  let pos, tree;
332
- if (this.parse && (pos = this.parse.parsedPos) > this.treeLen) {
332
+ if (this.parse && (pos = this.parse.parsedPos) >= this.treeLen) {
333
333
  if (this.parse.stoppedAt == null || this.parse.stoppedAt > pos)
334
334
  this.parse.stopAt(pos);
335
335
  this.withContext(() => { while (!(tree = this.parse.advance())) { } });
336
+ this.treeLen = pos;
336
337
  this.tree = tree;
337
338
  this.fragments = this.withoutTempSkipped(common.TreeFragment.addTree(this.tree, this.fragments, true));
338
339
  this.parse = null;
@@ -450,13 +451,8 @@ class ParseContext {
450
451
  /**
451
452
  @internal
452
453
  */
453
- movedPast(pos) {
454
- return this.treeLen < pos && this.parse && this.parse.parsedPos >= pos;
455
- }
456
- /**
457
- @internal
458
- */
459
454
  isDone(upto) {
455
+ upto = Math.min(upto, this.state.doc.length);
460
456
  let frags = this.fragments;
461
457
  return this.treeLen >= upto && frags.length && frags[0].from == 0 && frags[0].to >= upto;
462
458
  }
@@ -486,13 +482,14 @@ class LanguageState {
486
482
  // state updates with parse work beyond the viewport.
487
483
  let upto = this.context.treeLen == tr.startState.doc.length ? undefined
488
484
  : Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);
489
- if (!newCx.work(25 /* Apply */, upto))
485
+ if (!newCx.work(20 /* Apply */, upto))
490
486
  newCx.takeTree();
491
487
  return new LanguageState(newCx);
492
488
  }
493
489
  static init(state) {
494
- let parseState = new ParseContext(state.facet(language).parser, state, [], common.Tree.empty, 0, { from: 0, to: state.doc.length }, [], null);
495
- if (!parseState.work(25 /* Apply */))
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))
496
493
  parseState.takeTree();
497
494
  return new LanguageState(parseState);
498
495
  }
@@ -508,13 +505,21 @@ Language.state = state.StateField.define({
508
505
  return value.apply(tr);
509
506
  }
510
507
  });
511
- let requestIdle = typeof window != "undefined" && window.requestIdleCallback ||
512
- ((callback, { timeout }) => setTimeout(callback, timeout));
513
- let cancelIdle = typeof window != "undefined" && window.cancelIdleCallback || clearTimeout;
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
+ };
514
519
  const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
515
520
  constructor(view) {
516
521
  this.view = view;
517
- this.working = -1;
522
+ this.working = null;
518
523
  this.workScheduled = 0;
519
524
  // End of the current time chunk
520
525
  this.chunkEnd = -1;
@@ -535,14 +540,14 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
535
540
  this.checkAsyncSchedule(cx);
536
541
  }
537
542
  scheduleWork() {
538
- if (this.working > -1)
543
+ if (this.working)
539
544
  return;
540
545
  let { state } = this.view, field = state.field(Language.state);
541
546
  if (field.tree != field.context.tree || !field.context.isDone(state.doc.length))
542
- this.working = requestIdle(this.work, { timeout: 500 /* Pause */ });
547
+ this.working = requestIdle(this.work);
543
548
  }
544
549
  work(deadline) {
545
- this.working = -1;
550
+ this.working = null;
546
551
  let now = Date.now();
547
552
  if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk
548
553
  this.chunkEnd = now + 30000 /* ChunkTime */;
@@ -551,16 +556,17 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
551
556
  if (this.chunkBudget <= 0)
552
557
  return; // No more budget
553
558
  let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
554
- if (field.tree == field.context.tree && field.context.treeLen >= vpTo + 1000000 /* MaxParseAhead */)
559
+ if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* MaxParseAhead */))
555
560
  return;
556
- let time = Math.min(this.chunkBudget, deadline ? Math.max(25 /* MinSlice */, deadline.timeRemaining()) : 100 /* Slice */);
557
- let done = field.context.work(time, vpTo + 1000000 /* MaxParseAhead */);
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 */));
558
564
  this.chunkBudget -= Date.now() - now;
559
- if (done || this.chunkBudget <= 0 || field.context.movedPast(vpTo)) {
565
+ if (done || this.chunkBudget <= 0) {
560
566
  field.context.takeTree();
561
567
  this.view.dispatch({ effects: Language.setState.of(new LanguageState(field.context)) });
562
568
  }
563
- if (!done && this.chunkBudget > 0)
569
+ if (this.chunkBudget > 0 && !(done && !viewportFirst))
564
570
  this.scheduleWork();
565
571
  this.checkAsyncSchedule(field.context);
566
572
  }
@@ -575,11 +581,11 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
575
581
  }
576
582
  }
577
583
  destroy() {
578
- if (this.working >= 0)
579
- cancelIdle(this.working);
584
+ if (this.working)
585
+ this.working();
580
586
  }
581
587
  isWorking() {
582
- return this.working >= 0 || this.workScheduled > 0;
588
+ return this.working || this.workScheduled > 0;
583
589
  }
584
590
  }, {
585
591
  eventHandlers: { focus() { this.scheduleWork(); } }
@@ -816,9 +822,11 @@ class IndentContext {
816
822
  */
817
823
  lineAt(pos, bias = 1) {
818
824
  let line = this.state.doc.lineAt(pos);
819
- let { simulateBreak } = this.options;
825
+ let { simulateBreak, simulateDoubleBreak } = this.options;
820
826
  if (simulateBreak != null && simulateBreak >= line.from && simulateBreak <= line.to) {
821
- if (bias < 0 ? simulateBreak < pos : simulateBreak <= pos)
827
+ if (simulateDoubleBreak && simulateBreak == pos)
828
+ return { text: "", from: pos };
829
+ else if (bias < 0 ? simulateBreak < pos : simulateBreak <= pos)
822
830
  return { text: line.text.slice(simulateBreak - line.from), from: simulateBreak };
823
831
  else
824
832
  return { text: line.text.slice(0, simulateBreak - line.from), from: line.from };
package/dist/index.d.ts CHANGED
@@ -156,7 +156,7 @@ stopped running, either because it parsed the entire document,
156
156
  because it spent too much time and was cut off, or because there
157
157
  is no language parser enabled.
158
158
  */
159
- declare function syntaxParserRunning(view: EditorView): boolean;
159
+ declare function syntaxParserRunning(view: EditorView): boolean | (() => void);
160
160
  /**
161
161
  A parse context provided to parsers working on the editor content.
162
162
  */
package/dist/index.js CHANGED
@@ -175,7 +175,7 @@ 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.treeLen >= upto || parse.work(timeout, upto) ? parse.tree : null;
178
+ return !parse ? null : parse.isDone(upto) || parse.work(timeout, upto) ? parse.tree : null;
179
179
  }
180
180
  /**
181
181
  Queries whether there is a full syntax tree available up to the
@@ -297,12 +297,12 @@ class ParseContext {
297
297
  }
298
298
  return this.withContext(() => {
299
299
  var _a;
300
+ let endTime = Date.now() + time;
300
301
  if (!this.parse)
301
302
  this.parse = this.startParse();
302
303
  if (upto != null && (this.parse.stoppedAt == null || this.parse.stoppedAt > upto) &&
303
304
  upto < this.state.doc.length)
304
305
  this.parse.stopAt(upto);
305
- let endTime = Date.now() + time;
306
306
  for (;;) {
307
307
  let done = this.parse.advance();
308
308
  if (done) {
@@ -325,10 +325,11 @@ class ParseContext {
325
325
  */
326
326
  takeTree() {
327
327
  let pos, tree;
328
- if (this.parse && (pos = this.parse.parsedPos) > this.treeLen) {
328
+ if (this.parse && (pos = this.parse.parsedPos) >= this.treeLen) {
329
329
  if (this.parse.stoppedAt == null || this.parse.stoppedAt > pos)
330
330
  this.parse.stopAt(pos);
331
331
  this.withContext(() => { while (!(tree = this.parse.advance())) { } });
332
+ this.treeLen = pos;
332
333
  this.tree = tree;
333
334
  this.fragments = this.withoutTempSkipped(TreeFragment.addTree(this.tree, this.fragments, true));
334
335
  this.parse = null;
@@ -446,13 +447,8 @@ class ParseContext {
446
447
  /**
447
448
  @internal
448
449
  */
449
- movedPast(pos) {
450
- return this.treeLen < pos && this.parse && this.parse.parsedPos >= pos;
451
- }
452
- /**
453
- @internal
454
- */
455
450
  isDone(upto) {
451
+ upto = Math.min(upto, this.state.doc.length);
456
452
  let frags = this.fragments;
457
453
  return this.treeLen >= upto && frags.length && frags[0].from == 0 && frags[0].to >= upto;
458
454
  }
@@ -482,13 +478,14 @@ class LanguageState {
482
478
  // state updates with parse work beyond the viewport.
483
479
  let upto = this.context.treeLen == tr.startState.doc.length ? undefined
484
480
  : Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);
485
- if (!newCx.work(25 /* Apply */, upto))
481
+ if (!newCx.work(20 /* Apply */, upto))
486
482
  newCx.takeTree();
487
483
  return new LanguageState(newCx);
488
484
  }
489
485
  static init(state) {
490
- let parseState = new ParseContext(state.facet(language).parser, state, [], Tree.empty, 0, { from: 0, to: state.doc.length }, [], null);
491
- if (!parseState.work(25 /* Apply */))
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))
492
489
  parseState.takeTree();
493
490
  return new LanguageState(parseState);
494
491
  }
@@ -504,13 +501,21 @@ Language.state = /*@__PURE__*/StateField.define({
504
501
  return value.apply(tr);
505
502
  }
506
503
  });
507
- let requestIdle = typeof window != "undefined" && window.requestIdleCallback ||
508
- ((callback, { timeout }) => setTimeout(callback, timeout));
509
- let cancelIdle = typeof window != "undefined" && window.cancelIdleCallback || clearTimeout;
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
+ };
510
515
  const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
511
516
  constructor(view) {
512
517
  this.view = view;
513
- this.working = -1;
518
+ this.working = null;
514
519
  this.workScheduled = 0;
515
520
  // End of the current time chunk
516
521
  this.chunkEnd = -1;
@@ -531,14 +536,14 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
531
536
  this.checkAsyncSchedule(cx);
532
537
  }
533
538
  scheduleWork() {
534
- if (this.working > -1)
539
+ if (this.working)
535
540
  return;
536
541
  let { state } = this.view, field = state.field(Language.state);
537
542
  if (field.tree != field.context.tree || !field.context.isDone(state.doc.length))
538
- this.working = requestIdle(this.work, { timeout: 500 /* Pause */ });
543
+ this.working = requestIdle(this.work);
539
544
  }
540
545
  work(deadline) {
541
- this.working = -1;
546
+ this.working = null;
542
547
  let now = Date.now();
543
548
  if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk
544
549
  this.chunkEnd = now + 30000 /* ChunkTime */;
@@ -547,16 +552,17 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
547
552
  if (this.chunkBudget <= 0)
548
553
  return; // No more budget
549
554
  let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
550
- if (field.tree == field.context.tree && field.context.treeLen >= vpTo + 1000000 /* MaxParseAhead */)
555
+ if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* MaxParseAhead */))
551
556
  return;
552
- let time = Math.min(this.chunkBudget, deadline ? Math.max(25 /* MinSlice */, deadline.timeRemaining()) : 100 /* Slice */);
553
- let done = field.context.work(time, vpTo + 1000000 /* MaxParseAhead */);
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 */));
554
560
  this.chunkBudget -= Date.now() - now;
555
- if (done || this.chunkBudget <= 0 || field.context.movedPast(vpTo)) {
561
+ if (done || this.chunkBudget <= 0) {
556
562
  field.context.takeTree();
557
563
  this.view.dispatch({ effects: Language.setState.of(new LanguageState(field.context)) });
558
564
  }
559
- if (!done && this.chunkBudget > 0)
565
+ if (this.chunkBudget > 0 && !(done && !viewportFirst))
560
566
  this.scheduleWork();
561
567
  this.checkAsyncSchedule(field.context);
562
568
  }
@@ -571,11 +577,11 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
571
577
  }
572
578
  }
573
579
  destroy() {
574
- if (this.working >= 0)
575
- cancelIdle(this.working);
580
+ if (this.working)
581
+ this.working();
576
582
  }
577
583
  isWorking() {
578
- return this.working >= 0 || this.workScheduled > 0;
584
+ return this.working || this.workScheduled > 0;
579
585
  }
580
586
  }, {
581
587
  eventHandlers: { focus() { this.scheduleWork(); } }
@@ -812,9 +818,11 @@ class IndentContext {
812
818
  */
813
819
  lineAt(pos, bias = 1) {
814
820
  let line = this.state.doc.lineAt(pos);
815
- let { simulateBreak } = this.options;
821
+ let { simulateBreak, simulateDoubleBreak } = this.options;
816
822
  if (simulateBreak != null && simulateBreak >= line.from && simulateBreak <= line.to) {
817
- if (bias < 0 ? simulateBreak < pos : simulateBreak <= pos)
823
+ if (simulateDoubleBreak && simulateBreak == pos)
824
+ return { text: "", from: pos };
825
+ else if (bias < 0 ? simulateBreak < pos : simulateBreak <= pos)
818
826
  return { text: line.text.slice(simulateBreak - line.from), from: simulateBreak };
819
827
  else
820
828
  return { text: line.text.slice(0, simulateBreak - line.from), from: line.from };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/language",
3
- "version": "0.19.5",
3
+ "version": "0.19.8",
4
4
  "description": "Language support infrastructure for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",