@codemirror/language 6.7.0 → 6.9.0

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/dist/index.d.ts CHANGED
@@ -587,6 +587,11 @@ declare class TreeIndentContext extends IndentContext {
587
587
  */
588
588
  get baseIndent(): number;
589
589
  /**
590
+ Get the indentation for the reference line of the given node
591
+ (see [`baseIndent`](https://codemirror.net/6/docs/ref/#language.TreeIndentContext.baseIndent)).
592
+ */
593
+ baseIndentFor(node: SyntaxNode): number;
594
+ /**
590
595
  Continue looking for indentations in the node's parent nodes,
591
596
  and return the result of that.
592
597
  */
@@ -684,7 +689,7 @@ declare function foldable(state: EditorState, lineStart: number, lineEnd: number
684
689
  from: number;
685
690
  to: number;
686
691
  } | null;
687
- type DocRange = {
692
+ declare type DocRange = {
688
693
  from: number;
689
694
  to: number;
690
695
  };
@@ -756,23 +761,36 @@ interface FoldConfig {
756
761
  position of folded code. The `onclick` argument is the default
757
762
  click event handler, which toggles folding on the line that
758
763
  holds the element, and should probably be added as an event
759
- handler to the returned element.
764
+ handler to the returned element. If
765
+ [`preparePlaceholder`](https://codemirror.net/6/docs/ref/#language.FoldConfig.preparePlaceholder)
766
+ is given, its result will be passed as 3rd argument. Otherwise,
767
+ this will be null.
760
768
 
761
769
  When this option isn't given, the `placeholderText` option will
762
770
  be used to create the placeholder element.
763
771
  */
764
- placeholderDOM?: ((view: EditorView, onclick: (event: Event) => void) => HTMLElement) | null;
772
+ placeholderDOM?: ((view: EditorView, onclick: (event: Event) => void, prepared: any) => HTMLElement) | null;
765
773
  /**
766
774
  Text to use as placeholder for folded text. Defaults to `"…"`.
767
775
  Will be styled with the `"cm-foldPlaceholder"` class.
768
776
  */
769
777
  placeholderText?: string;
778
+ /**
779
+ Given a range that is being folded, create a value that
780
+ describes it, to be used by `placeholderDOM` to render a custom
781
+ widget that, for example, indicates something about the folded
782
+ range's size or type.
783
+ */
784
+ preparePlaceholder?: (state: EditorState, range: {
785
+ from: number;
786
+ to: number;
787
+ }) => any;
770
788
  }
771
789
  /**
772
790
  Create an extension that configures code folding.
773
791
  */
774
792
  declare function codeFolding(config?: FoldConfig): Extension;
775
- type Handlers = {
793
+ declare type Handlers = {
776
794
  [event: string]: (view: EditorView, line: BlockInfo, event: Event) => boolean;
777
795
  };
778
796
  interface FoldGutterConfig {
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { NodeProp, IterMode, Tree, TreeFragment, Parser, NodeType, NodeSet } from '@lezer/common';
1
+ import { NodeProp, Tree, IterMode, TreeFragment, Parser, NodeType, NodeSet } from '@lezer/common';
2
2
  import { StateEffect, StateField, Facet, EditorState, countColumn, combineConfig, RangeSet, RangeSetBuilder, Prec } from '@codemirror/state';
3
3
  import { ViewPlugin, logException, EditorView, Decoration, WidgetType, gutter, GutterMarker } from '@codemirror/view';
4
4
  import { tags, tagHighlighter, highlightTree, styleTags } from '@lezer/highlight';
@@ -534,14 +534,14 @@ class LanguageState {
534
534
  // state updates with parse work beyond the viewport.
535
535
  let upto = this.context.treeLen == tr.startState.doc.length ? undefined
536
536
  : Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);
537
- if (!newCx.work(20 /* Work.Apply */, upto))
537
+ if (!newCx.work(20 /* Apply */, upto))
538
538
  newCx.takeTree();
539
539
  return new LanguageState(newCx);
540
540
  }
541
541
  static init(state) {
542
- let vpTo = Math.min(3000 /* Work.InitViewport */, state.doc.length);
542
+ let vpTo = Math.min(3000 /* InitViewport */, state.doc.length);
543
543
  let parseState = ParseContext.create(state.facet(language).parser, state, { from: 0, to: vpTo });
544
- if (!parseState.work(20 /* Work.Apply */, vpTo))
544
+ if (!parseState.work(20 /* Apply */, vpTo))
545
545
  parseState.takeTree();
546
546
  return new LanguageState(parseState);
547
547
  }
@@ -558,14 +558,14 @@ Language.state = /*@__PURE__*/StateField.define({
558
558
  }
559
559
  });
560
560
  let requestIdle = (callback) => {
561
- let timeout = setTimeout(() => callback(), 500 /* Work.MaxPause */);
561
+ let timeout = setTimeout(() => callback(), 500 /* MaxPause */);
562
562
  return () => clearTimeout(timeout);
563
563
  };
564
564
  if (typeof requestIdleCallback != "undefined")
565
565
  requestIdle = (callback) => {
566
566
  let idle = -1, timeout = setTimeout(() => {
567
- idle = requestIdleCallback(callback, { timeout: 500 /* Work.MaxPause */ - 100 /* Work.MinPause */ });
568
- }, 100 /* Work.MinPause */);
567
+ idle = requestIdleCallback(callback, { timeout: 500 /* MaxPause */ - 100 /* MinPause */ });
568
+ }, 100 /* MinPause */);
569
569
  return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);
570
570
  };
571
571
  const isInputPending = typeof navigator != "undefined" && ((_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending)
@@ -588,7 +588,7 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
588
588
  this.scheduleWork();
589
589
  if (update.docChanged) {
590
590
  if (this.view.hasFocus)
591
- this.chunkBudget += 50 /* Work.ChangeBonus */;
591
+ this.chunkBudget += 50 /* ChangeBonus */;
592
592
  this.scheduleWork();
593
593
  }
594
594
  this.checkAsyncSchedule(cx);
@@ -604,19 +604,19 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
604
604
  this.working = null;
605
605
  let now = Date.now();
606
606
  if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk
607
- this.chunkEnd = now + 30000 /* Work.ChunkTime */;
608
- this.chunkBudget = 3000 /* Work.ChunkBudget */;
607
+ this.chunkEnd = now + 30000 /* ChunkTime */;
608
+ this.chunkBudget = 3000 /* ChunkBudget */;
609
609
  }
610
610
  if (this.chunkBudget <= 0)
611
611
  return; // No more budget
612
612
  let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
613
- if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* Work.MaxParseAhead */))
613
+ if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* MaxParseAhead */))
614
614
  return;
615
- let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Work.Slice */, deadline && !isInputPending ? Math.max(25 /* Work.MinSlice */, deadline.timeRemaining() - 5) : 1e9);
615
+ let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Slice */, deadline && !isInputPending ? Math.max(25 /* MinSlice */, deadline.timeRemaining() - 5) : 1e9);
616
616
  let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1000;
617
617
  let done = field.context.work(() => {
618
618
  return isInputPending && isInputPending() || Date.now() > endTime;
619
- }, vpTo + (viewportFirst ? 0 : 100000 /* Work.MaxParseAhead */));
619
+ }, vpTo + (viewportFirst ? 0 : 100000 /* MaxParseAhead */));
620
620
  this.chunkBudget -= Date.now() - now;
621
621
  if (done || this.chunkBudget <= 0) {
622
622
  field.context.takeTree();
@@ -861,7 +861,7 @@ function getIndentation(context, pos) {
861
861
  return result;
862
862
  }
863
863
  let tree = syntaxTree(context.state);
864
- return tree ? syntaxIndentation(context, tree, pos) : null;
864
+ return tree.length >= pos ? syntaxIndentation(context, tree, pos) : null;
865
865
  }
866
866
  /**
867
867
  Create a change set that auto-indents all lines touched by the
@@ -1058,13 +1058,20 @@ class TreeIndentContext extends IndentContext {
1058
1058
  on if it is covered by another such node.
1059
1059
  */
1060
1060
  get baseIndent() {
1061
- let line = this.state.doc.lineAt(this.node.from);
1061
+ return this.baseIndentFor(this.node);
1062
+ }
1063
+ /**
1064
+ Get the indentation for the reference line of the given node
1065
+ (see [`baseIndent`](https://codemirror.net/6/docs/ref/#language.TreeIndentContext.baseIndent)).
1066
+ */
1067
+ baseIndentFor(node) {
1068
+ let line = this.state.doc.lineAt(node.from);
1062
1069
  // Skip line starts that are covered by a sibling (or cousin, etc)
1063
1070
  for (;;) {
1064
- let atBreak = this.node.resolve(line.from);
1071
+ let atBreak = node.resolve(line.from);
1065
1072
  while (atBreak.parent && atBreak.parent.from == atBreak.from)
1066
1073
  atBreak = atBreak.parent;
1067
- if (isParent(atBreak, this.node))
1074
+ if (isParent(atBreak, node))
1068
1075
  break;
1069
1076
  line = this.state.doc.lineAt(atBreak.from);
1070
1077
  }
@@ -1293,11 +1300,16 @@ const foldState = /*@__PURE__*/StateField.define({
1293
1300
  update(folded, tr) {
1294
1301
  folded = folded.map(tr.changes);
1295
1302
  for (let e of tr.effects) {
1296
- if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to))
1297
- folded = folded.update({ add: [foldWidget.range(e.value.from, e.value.to)] });
1298
- else if (e.is(unfoldEffect))
1303
+ if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to)) {
1304
+ let { preparePlaceholder } = tr.state.facet(foldConfig);
1305
+ let widget = !preparePlaceholder ? foldWidget :
1306
+ Decoration.replace({ widget: new PreparedFoldWidget(preparePlaceholder(tr.state, e.value)) });
1307
+ folded = folded.update({ add: [widget.range(e.value.from, e.value.to)] });
1308
+ }
1309
+ else if (e.is(unfoldEffect)) {
1299
1310
  folded = folded.update({ filter: (from, to) => e.value.from != from || e.value.to != to,
1300
1311
  filterFrom: e.value.from, filterTo: e.value.to });
1312
+ }
1301
1313
  }
1302
1314
  // Clear folded ranges that cover the selection head
1303
1315
  if (tr.selection) {
@@ -1474,6 +1486,7 @@ const foldKeymap = [
1474
1486
  ];
1475
1487
  const defaultConfig = {
1476
1488
  placeholderDOM: null,
1489
+ preparePlaceholder: null,
1477
1490
  placeholderText: "…"
1478
1491
  };
1479
1492
  const foldConfig = /*@__PURE__*/Facet.define({
@@ -1488,27 +1501,36 @@ function codeFolding(config) {
1488
1501
  result.push(foldConfig.of(config));
1489
1502
  return result;
1490
1503
  }
1504
+ function widgetToDOM(view, prepared) {
1505
+ let { state } = view, conf = state.facet(foldConfig);
1506
+ let onclick = (event) => {
1507
+ let line = view.lineBlockAt(view.posAtDOM(event.target));
1508
+ let folded = findFold(view.state, line.from, line.to);
1509
+ if (folded)
1510
+ view.dispatch({ effects: unfoldEffect.of(folded) });
1511
+ event.preventDefault();
1512
+ };
1513
+ if (conf.placeholderDOM)
1514
+ return conf.placeholderDOM(view, onclick, prepared);
1515
+ let element = document.createElement("span");
1516
+ element.textContent = conf.placeholderText;
1517
+ element.setAttribute("aria-label", state.phrase("folded code"));
1518
+ element.title = state.phrase("unfold");
1519
+ element.className = "cm-foldPlaceholder";
1520
+ element.onclick = onclick;
1521
+ return element;
1522
+ }
1491
1523
  const foldWidget = /*@__PURE__*/Decoration.replace({ widget: /*@__PURE__*/new class extends WidgetType {
1492
- toDOM(view) {
1493
- let { state } = view, conf = state.facet(foldConfig);
1494
- let onclick = (event) => {
1495
- let line = view.lineBlockAt(view.posAtDOM(event.target));
1496
- let folded = findFold(view.state, line.from, line.to);
1497
- if (folded)
1498
- view.dispatch({ effects: unfoldEffect.of(folded) });
1499
- event.preventDefault();
1500
- };
1501
- if (conf.placeholderDOM)
1502
- return conf.placeholderDOM(view, onclick);
1503
- let element = document.createElement("span");
1504
- element.textContent = conf.placeholderText;
1505
- element.setAttribute("aria-label", state.phrase("folded code"));
1506
- element.title = state.phrase("unfold");
1507
- element.className = "cm-foldPlaceholder";
1508
- element.onclick = onclick;
1509
- return element;
1510
- }
1524
+ toDOM(view) { return widgetToDOM(view, null); }
1511
1525
  } });
1526
+ class PreparedFoldWidget extends WidgetType {
1527
+ constructor(value) {
1528
+ super();
1529
+ this.value = value;
1530
+ }
1531
+ eq(other) { return this.value == other.value; }
1532
+ toDOM(view) { return widgetToDOM(view, this.value); }
1533
+ }
1512
1534
  const foldGutterDefaults = {
1513
1535
  openText: "⌄",
1514
1536
  closedText: "›",
@@ -2196,7 +2218,7 @@ class StreamLanguage extends Language {
2196
2218
  state = this.streamParser.startState(cx.unit);
2197
2219
  statePos = 0;
2198
2220
  }
2199
- if (pos - statePos > 10000 /* C.MaxIndentScanDist */)
2221
+ if (pos - statePos > 10000 /* MaxIndentScanDist */)
2200
2222
  return null;
2201
2223
  while (statePos < pos) {
2202
2224
  let line = cx.state.doc.lineAt(statePos), end = Math.min(pos, line.to);
@@ -2278,7 +2300,7 @@ class Parse {
2278
2300
  this.chunks.push(tree.children[i]);
2279
2301
  this.chunkPos.push(tree.positions[i]);
2280
2302
  }
2281
- if (context && this.parsedPos < context.viewport.from - 100000 /* C.MaxDistanceBeforeViewport */) {
2303
+ if (context && this.parsedPos < context.viewport.from - 100000 /* MaxDistanceBeforeViewport */) {
2282
2304
  this.state = this.lang.streamParser.startState(getIndentUnit(context.state));
2283
2305
  context.skipUntilInView(this.parsedPos, context.viewport.from);
2284
2306
  this.parsedPos = context.viewport.from;
@@ -2288,7 +2310,7 @@ class Parse {
2288
2310
  advance() {
2289
2311
  let context = ParseContext.get();
2290
2312
  let parseEnd = this.stoppedAt == null ? this.to : Math.min(this.to, this.stoppedAt);
2291
- let end = Math.min(parseEnd, this.chunkStart + 2048 /* C.ChunkSize */);
2313
+ let end = Math.min(parseEnd, this.chunkStart + 2048 /* ChunkSize */);
2292
2314
  if (context)
2293
2315
  end = Math.min(end, context.viewport.to);
2294
2316
  while (this.parsedPos < end)
@@ -2372,7 +2394,7 @@ class Parse {
2372
2394
  let token = readToken(streamParser.token, stream, this.state);
2373
2395
  if (token)
2374
2396
  offset = this.emitToken(this.lang.tokenTable.resolve(token), this.parsedPos + stream.start, this.parsedPos + stream.pos, 4, offset);
2375
- if (stream.start > 10000 /* C.MaxLineLength */)
2397
+ if (stream.start > 10000 /* MaxLineLength */)
2376
2398
  break;
2377
2399
  }
2378
2400
  }
@@ -2388,7 +2410,7 @@ class Parse {
2388
2410
  length: this.parsedPos - this.chunkStart,
2389
2411
  nodeSet,
2390
2412
  topID: 0,
2391
- maxBufferLength: 2048 /* C.ChunkSize */,
2413
+ maxBufferLength: 2048 /* ChunkSize */,
2392
2414
  reused: this.chunkReused
2393
2415
  });
2394
2416
  tree = new Tree(tree.type, tree.children, tree.positions, tree.length, [[this.lang.stateAfter, this.lang.streamParser.copyState(this.state)]]);
@@ -2478,7 +2500,7 @@ function createTokenType(extra, tagStr) {
2478
2500
  return type.id;
2479
2501
  }
2480
2502
  function docID(data) {
2481
- let type = NodeType.define({ id: typeArray.length, name: "Document", props: [languageDataProp.add(() => data)] });
2503
+ let type = NodeType.define({ id: typeArray.length, name: "Document", props: [languageDataProp.add(() => data)], top: true });
2482
2504
  typeArray.push(type);
2483
2505
  return type;
2484
2506
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/language",
3
- "version": "6.7.0",
3
+ "version": "6.9.0",
4
4
  "description": "Language support infrastructure for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -34,7 +34,7 @@
34
34
  "style-mod": "^4.0.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@codemirror/buildhelper": "^0.1.5",
37
+ "@codemirror/buildhelper": "^1.0.0",
38
38
  "@lezer/javascript": "^1.0.0"
39
39
  },
40
40
  "repository": {