@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/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## 6.9.0 (2023-08-16)
2
+
3
+ ### Bug fixes
4
+
5
+ Make `getIndentation` return null, rather than 0, when there is no syntax tree available.
6
+
7
+ ### New features
8
+
9
+ The new `preparePlaceholder` option to `codeFolding` makes it possible to display contextual information in a folded range placeholder widget.
10
+
11
+ ## 6.8.0 (2023-06-12)
12
+
13
+ ### New features
14
+
15
+ The new `baseIndentFor` method in `TreeIndentContext` can be used to find the base indentation for an arbitrary node.
16
+
1
17
  ## 6.7.0 (2023-05-19)
2
18
 
3
19
  ### New features
package/dist/index.cjs CHANGED
@@ -538,14 +538,14 @@ class LanguageState {
538
538
  // state updates with parse work beyond the viewport.
539
539
  let upto = this.context.treeLen == tr.startState.doc.length ? undefined
540
540
  : Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);
541
- if (!newCx.work(20 /* Work.Apply */, upto))
541
+ if (!newCx.work(20 /* Apply */, upto))
542
542
  newCx.takeTree();
543
543
  return new LanguageState(newCx);
544
544
  }
545
545
  static init(state) {
546
- let vpTo = Math.min(3000 /* Work.InitViewport */, state.doc.length);
546
+ let vpTo = Math.min(3000 /* InitViewport */, state.doc.length);
547
547
  let parseState = ParseContext.create(state.facet(language).parser, state, { from: 0, to: vpTo });
548
- if (!parseState.work(20 /* Work.Apply */, vpTo))
548
+ if (!parseState.work(20 /* Apply */, vpTo))
549
549
  parseState.takeTree();
550
550
  return new LanguageState(parseState);
551
551
  }
@@ -562,14 +562,14 @@ Language.state = state.StateField.define({
562
562
  }
563
563
  });
564
564
  let requestIdle = (callback) => {
565
- let timeout = setTimeout(() => callback(), 500 /* Work.MaxPause */);
565
+ let timeout = setTimeout(() => callback(), 500 /* MaxPause */);
566
566
  return () => clearTimeout(timeout);
567
567
  };
568
568
  if (typeof requestIdleCallback != "undefined")
569
569
  requestIdle = (callback) => {
570
570
  let idle = -1, timeout = setTimeout(() => {
571
- idle = requestIdleCallback(callback, { timeout: 500 /* Work.MaxPause */ - 100 /* Work.MinPause */ });
572
- }, 100 /* Work.MinPause */);
571
+ idle = requestIdleCallback(callback, { timeout: 500 /* MaxPause */ - 100 /* MinPause */ });
572
+ }, 100 /* MinPause */);
573
573
  return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);
574
574
  };
575
575
  const isInputPending = typeof navigator != "undefined" && ((_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending)
@@ -592,7 +592,7 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
592
592
  this.scheduleWork();
593
593
  if (update.docChanged) {
594
594
  if (this.view.hasFocus)
595
- this.chunkBudget += 50 /* Work.ChangeBonus */;
595
+ this.chunkBudget += 50 /* ChangeBonus */;
596
596
  this.scheduleWork();
597
597
  }
598
598
  this.checkAsyncSchedule(cx);
@@ -608,19 +608,19 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
608
608
  this.working = null;
609
609
  let now = Date.now();
610
610
  if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk
611
- this.chunkEnd = now + 30000 /* Work.ChunkTime */;
612
- this.chunkBudget = 3000 /* Work.ChunkBudget */;
611
+ this.chunkEnd = now + 30000 /* ChunkTime */;
612
+ this.chunkBudget = 3000 /* ChunkBudget */;
613
613
  }
614
614
  if (this.chunkBudget <= 0)
615
615
  return; // No more budget
616
616
  let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
617
- if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* Work.MaxParseAhead */))
617
+ if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* MaxParseAhead */))
618
618
  return;
619
- let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Work.Slice */, deadline && !isInputPending ? Math.max(25 /* Work.MinSlice */, deadline.timeRemaining() - 5) : 1e9);
619
+ let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Slice */, deadline && !isInputPending ? Math.max(25 /* MinSlice */, deadline.timeRemaining() - 5) : 1e9);
620
620
  let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1000;
621
621
  let done = field.context.work(() => {
622
622
  return isInputPending && isInputPending() || Date.now() > endTime;
623
- }, vpTo + (viewportFirst ? 0 : 100000 /* Work.MaxParseAhead */));
623
+ }, vpTo + (viewportFirst ? 0 : 100000 /* MaxParseAhead */));
624
624
  this.chunkBudget -= Date.now() - now;
625
625
  if (done || this.chunkBudget <= 0) {
626
626
  field.context.takeTree();
@@ -865,7 +865,7 @@ function getIndentation(context, pos) {
865
865
  return result;
866
866
  }
867
867
  let tree = syntaxTree(context.state);
868
- return tree ? syntaxIndentation(context, tree, pos) : null;
868
+ return tree.length >= pos ? syntaxIndentation(context, tree, pos) : null;
869
869
  }
870
870
  /**
871
871
  Create a change set that auto-indents all lines touched by the
@@ -1062,13 +1062,20 @@ class TreeIndentContext extends IndentContext {
1062
1062
  on if it is covered by another such node.
1063
1063
  */
1064
1064
  get baseIndent() {
1065
- let line = this.state.doc.lineAt(this.node.from);
1065
+ return this.baseIndentFor(this.node);
1066
+ }
1067
+ /**
1068
+ Get the indentation for the reference line of the given node
1069
+ (see [`baseIndent`](https://codemirror.net/6/docs/ref/#language.TreeIndentContext.baseIndent)).
1070
+ */
1071
+ baseIndentFor(node) {
1072
+ let line = this.state.doc.lineAt(node.from);
1066
1073
  // Skip line starts that are covered by a sibling (or cousin, etc)
1067
1074
  for (;;) {
1068
- let atBreak = this.node.resolve(line.from);
1075
+ let atBreak = node.resolve(line.from);
1069
1076
  while (atBreak.parent && atBreak.parent.from == atBreak.from)
1070
1077
  atBreak = atBreak.parent;
1071
- if (isParent(atBreak, this.node))
1078
+ if (isParent(atBreak, node))
1072
1079
  break;
1073
1080
  line = this.state.doc.lineAt(atBreak.from);
1074
1081
  }
@@ -1297,11 +1304,16 @@ const foldState = state.StateField.define({
1297
1304
  update(folded, tr) {
1298
1305
  folded = folded.map(tr.changes);
1299
1306
  for (let e of tr.effects) {
1300
- if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to))
1301
- folded = folded.update({ add: [foldWidget.range(e.value.from, e.value.to)] });
1302
- else if (e.is(unfoldEffect))
1307
+ if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to)) {
1308
+ let { preparePlaceholder } = tr.state.facet(foldConfig);
1309
+ let widget = !preparePlaceholder ? foldWidget :
1310
+ view.Decoration.replace({ widget: new PreparedFoldWidget(preparePlaceholder(tr.state, e.value)) });
1311
+ folded = folded.update({ add: [widget.range(e.value.from, e.value.to)] });
1312
+ }
1313
+ else if (e.is(unfoldEffect)) {
1303
1314
  folded = folded.update({ filter: (from, to) => e.value.from != from || e.value.to != to,
1304
1315
  filterFrom: e.value.from, filterTo: e.value.to });
1316
+ }
1305
1317
  }
1306
1318
  // Clear folded ranges that cover the selection head
1307
1319
  if (tr.selection) {
@@ -1478,6 +1490,7 @@ const foldKeymap = [
1478
1490
  ];
1479
1491
  const defaultConfig = {
1480
1492
  placeholderDOM: null,
1493
+ preparePlaceholder: null,
1481
1494
  placeholderText: "…"
1482
1495
  };
1483
1496
  const foldConfig = state.Facet.define({
@@ -1492,27 +1505,36 @@ function codeFolding(config) {
1492
1505
  result.push(foldConfig.of(config));
1493
1506
  return result;
1494
1507
  }
1508
+ function widgetToDOM(view, prepared) {
1509
+ let { state } = view, conf = state.facet(foldConfig);
1510
+ let onclick = (event) => {
1511
+ let line = view.lineBlockAt(view.posAtDOM(event.target));
1512
+ let folded = findFold(view.state, line.from, line.to);
1513
+ if (folded)
1514
+ view.dispatch({ effects: unfoldEffect.of(folded) });
1515
+ event.preventDefault();
1516
+ };
1517
+ if (conf.placeholderDOM)
1518
+ return conf.placeholderDOM(view, onclick, prepared);
1519
+ let element = document.createElement("span");
1520
+ element.textContent = conf.placeholderText;
1521
+ element.setAttribute("aria-label", state.phrase("folded code"));
1522
+ element.title = state.phrase("unfold");
1523
+ element.className = "cm-foldPlaceholder";
1524
+ element.onclick = onclick;
1525
+ return element;
1526
+ }
1495
1527
  const foldWidget = view.Decoration.replace({ widget: new class extends view.WidgetType {
1496
- toDOM(view) {
1497
- let { state } = view, conf = state.facet(foldConfig);
1498
- let onclick = (event) => {
1499
- let line = view.lineBlockAt(view.posAtDOM(event.target));
1500
- let folded = findFold(view.state, line.from, line.to);
1501
- if (folded)
1502
- view.dispatch({ effects: unfoldEffect.of(folded) });
1503
- event.preventDefault();
1504
- };
1505
- if (conf.placeholderDOM)
1506
- return conf.placeholderDOM(view, onclick);
1507
- let element = document.createElement("span");
1508
- element.textContent = conf.placeholderText;
1509
- element.setAttribute("aria-label", state.phrase("folded code"));
1510
- element.title = state.phrase("unfold");
1511
- element.className = "cm-foldPlaceholder";
1512
- element.onclick = onclick;
1513
- return element;
1514
- }
1528
+ toDOM(view) { return widgetToDOM(view, null); }
1515
1529
  } });
1530
+ class PreparedFoldWidget extends view.WidgetType {
1531
+ constructor(value) {
1532
+ super();
1533
+ this.value = value;
1534
+ }
1535
+ eq(other) { return this.value == other.value; }
1536
+ toDOM(view) { return widgetToDOM(view, this.value); }
1537
+ }
1516
1538
  const foldGutterDefaults = {
1517
1539
  openText: "⌄",
1518
1540
  closedText: "›",
@@ -2200,7 +2222,7 @@ class StreamLanguage extends Language {
2200
2222
  state = this.streamParser.startState(cx.unit);
2201
2223
  statePos = 0;
2202
2224
  }
2203
- if (pos - statePos > 10000 /* C.MaxIndentScanDist */)
2225
+ if (pos - statePos > 10000 /* MaxIndentScanDist */)
2204
2226
  return null;
2205
2227
  while (statePos < pos) {
2206
2228
  let line = cx.state.doc.lineAt(statePos), end = Math.min(pos, line.to);
@@ -2282,7 +2304,7 @@ class Parse {
2282
2304
  this.chunks.push(tree.children[i]);
2283
2305
  this.chunkPos.push(tree.positions[i]);
2284
2306
  }
2285
- if (context && this.parsedPos < context.viewport.from - 100000 /* C.MaxDistanceBeforeViewport */) {
2307
+ if (context && this.parsedPos < context.viewport.from - 100000 /* MaxDistanceBeforeViewport */) {
2286
2308
  this.state = this.lang.streamParser.startState(getIndentUnit(context.state));
2287
2309
  context.skipUntilInView(this.parsedPos, context.viewport.from);
2288
2310
  this.parsedPos = context.viewport.from;
@@ -2292,7 +2314,7 @@ class Parse {
2292
2314
  advance() {
2293
2315
  let context = ParseContext.get();
2294
2316
  let parseEnd = this.stoppedAt == null ? this.to : Math.min(this.to, this.stoppedAt);
2295
- let end = Math.min(parseEnd, this.chunkStart + 2048 /* C.ChunkSize */);
2317
+ let end = Math.min(parseEnd, this.chunkStart + 2048 /* ChunkSize */);
2296
2318
  if (context)
2297
2319
  end = Math.min(end, context.viewport.to);
2298
2320
  while (this.parsedPos < end)
@@ -2376,7 +2398,7 @@ class Parse {
2376
2398
  let token = readToken(streamParser.token, stream, this.state);
2377
2399
  if (token)
2378
2400
  offset = this.emitToken(this.lang.tokenTable.resolve(token), this.parsedPos + stream.start, this.parsedPos + stream.pos, 4, offset);
2379
- if (stream.start > 10000 /* C.MaxLineLength */)
2401
+ if (stream.start > 10000 /* MaxLineLength */)
2380
2402
  break;
2381
2403
  }
2382
2404
  }
@@ -2392,7 +2414,7 @@ class Parse {
2392
2414
  length: this.parsedPos - this.chunkStart,
2393
2415
  nodeSet,
2394
2416
  topID: 0,
2395
- maxBufferLength: 2048 /* C.ChunkSize */,
2417
+ maxBufferLength: 2048 /* ChunkSize */,
2396
2418
  reused: this.chunkReused
2397
2419
  });
2398
2420
  tree = new common.Tree(tree.type, tree.children, tree.positions, tree.length, [[this.lang.stateAfter, this.lang.streamParser.copyState(this.state)]]);
@@ -2482,7 +2504,7 @@ function createTokenType(extra, tagStr) {
2482
2504
  return type.id;
2483
2505
  }
2484
2506
  function docID(data) {
2485
- let type = common.NodeType.define({ id: typeArray.length, name: "Document", props: [languageDataProp.add(() => data)] });
2507
+ let type = common.NodeType.define({ id: typeArray.length, name: "Document", props: [languageDataProp.add(() => data)], top: true });
2486
2508
  typeArray.push(type);
2487
2509
  return type;
2488
2510
  }