@codemirror/language 6.8.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,13 @@
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
+
1
11
  ## 6.8.0 (2023-06-12)
2
12
 
3
13
  ### New features
package/dist/index.cjs CHANGED
@@ -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
@@ -1304,11 +1304,16 @@ const foldState = state.StateField.define({
1304
1304
  update(folded, tr) {
1305
1305
  folded = folded.map(tr.changes);
1306
1306
  for (let e of tr.effects) {
1307
- if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to))
1308
- folded = folded.update({ add: [foldWidget.range(e.value.from, e.value.to)] });
1309
- 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)) {
1310
1314
  folded = folded.update({ filter: (from, to) => e.value.from != from || e.value.to != to,
1311
1315
  filterFrom: e.value.from, filterTo: e.value.to });
1316
+ }
1312
1317
  }
1313
1318
  // Clear folded ranges that cover the selection head
1314
1319
  if (tr.selection) {
@@ -1485,6 +1490,7 @@ const foldKeymap = [
1485
1490
  ];
1486
1491
  const defaultConfig = {
1487
1492
  placeholderDOM: null,
1493
+ preparePlaceholder: null,
1488
1494
  placeholderText: "…"
1489
1495
  };
1490
1496
  const foldConfig = state.Facet.define({
@@ -1499,27 +1505,36 @@ function codeFolding(config) {
1499
1505
  result.push(foldConfig.of(config));
1500
1506
  return result;
1501
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
+ }
1502
1527
  const foldWidget = view.Decoration.replace({ widget: new class extends view.WidgetType {
1503
- toDOM(view) {
1504
- let { state } = view, conf = state.facet(foldConfig);
1505
- let onclick = (event) => {
1506
- let line = view.lineBlockAt(view.posAtDOM(event.target));
1507
- let folded = findFold(view.state, line.from, line.to);
1508
- if (folded)
1509
- view.dispatch({ effects: unfoldEffect.of(folded) });
1510
- event.preventDefault();
1511
- };
1512
- if (conf.placeholderDOM)
1513
- return conf.placeholderDOM(view, onclick);
1514
- let element = document.createElement("span");
1515
- element.textContent = conf.placeholderText;
1516
- element.setAttribute("aria-label", state.phrase("folded code"));
1517
- element.title = state.phrase("unfold");
1518
- element.className = "cm-foldPlaceholder";
1519
- element.onclick = onclick;
1520
- return element;
1521
- }
1528
+ toDOM(view) { return widgetToDOM(view, null); }
1522
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
+ }
1523
1538
  const foldGutterDefaults = {
1524
1539
  openText: "⌄",
1525
1540
  closedText: "›",
@@ -2489,7 +2504,7 @@ function createTokenType(extra, tagStr) {
2489
2504
  return type.id;
2490
2505
  }
2491
2506
  function docID(data) {
2492
- 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 });
2493
2508
  typeArray.push(type);
2494
2509
  return type;
2495
2510
  }
package/dist/index.d.cts CHANGED
@@ -761,17 +761,30 @@ interface FoldConfig {
761
761
  position of folded code. The `onclick` argument is the default
762
762
  click event handler, which toggles folding on the line that
763
763
  holds the element, and should probably be added as an event
764
- 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.
765
768
 
766
769
  When this option isn't given, the `placeholderText` option will
767
770
  be used to create the placeholder element.
768
771
  */
769
- placeholderDOM?: ((view: EditorView, onclick: (event: Event) => void) => HTMLElement) | null;
772
+ placeholderDOM?: ((view: EditorView, onclick: (event: Event) => void, prepared: any) => HTMLElement) | null;
770
773
  /**
771
774
  Text to use as placeholder for folded text. Defaults to `"…"`.
772
775
  Will be styled with the `"cm-foldPlaceholder"` class.
773
776
  */
774
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;
775
788
  }
776
789
  /**
777
790
  Create an extension that configures code folding.
package/dist/index.d.ts CHANGED
@@ -761,17 +761,30 @@ interface FoldConfig {
761
761
  position of folded code. The `onclick` argument is the default
762
762
  click event handler, which toggles folding on the line that
763
763
  holds the element, and should probably be added as an event
764
- 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.
765
768
 
766
769
  When this option isn't given, the `placeholderText` option will
767
770
  be used to create the placeholder element.
768
771
  */
769
- placeholderDOM?: ((view: EditorView, onclick: (event: Event) => void) => HTMLElement) | null;
772
+ placeholderDOM?: ((view: EditorView, onclick: (event: Event) => void, prepared: any) => HTMLElement) | null;
770
773
  /**
771
774
  Text to use as placeholder for folded text. Defaults to `"…"`.
772
775
  Will be styled with the `"cm-foldPlaceholder"` class.
773
776
  */
774
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;
775
788
  }
776
789
  /**
777
790
  Create an extension that configures code folding.
package/dist/index.js CHANGED
@@ -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
@@ -1300,11 +1300,16 @@ const foldState = /*@__PURE__*/StateField.define({
1300
1300
  update(folded, tr) {
1301
1301
  folded = folded.map(tr.changes);
1302
1302
  for (let e of tr.effects) {
1303
- if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to))
1304
- folded = folded.update({ add: [foldWidget.range(e.value.from, e.value.to)] });
1305
- 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)) {
1306
1310
  folded = folded.update({ filter: (from, to) => e.value.from != from || e.value.to != to,
1307
1311
  filterFrom: e.value.from, filterTo: e.value.to });
1312
+ }
1308
1313
  }
1309
1314
  // Clear folded ranges that cover the selection head
1310
1315
  if (tr.selection) {
@@ -1481,6 +1486,7 @@ const foldKeymap = [
1481
1486
  ];
1482
1487
  const defaultConfig = {
1483
1488
  placeholderDOM: null,
1489
+ preparePlaceholder: null,
1484
1490
  placeholderText: "…"
1485
1491
  };
1486
1492
  const foldConfig = /*@__PURE__*/Facet.define({
@@ -1495,27 +1501,36 @@ function codeFolding(config) {
1495
1501
  result.push(foldConfig.of(config));
1496
1502
  return result;
1497
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
+ }
1498
1523
  const foldWidget = /*@__PURE__*/Decoration.replace({ widget: /*@__PURE__*/new class extends WidgetType {
1499
- toDOM(view) {
1500
- let { state } = view, conf = state.facet(foldConfig);
1501
- let onclick = (event) => {
1502
- let line = view.lineBlockAt(view.posAtDOM(event.target));
1503
- let folded = findFold(view.state, line.from, line.to);
1504
- if (folded)
1505
- view.dispatch({ effects: unfoldEffect.of(folded) });
1506
- event.preventDefault();
1507
- };
1508
- if (conf.placeholderDOM)
1509
- return conf.placeholderDOM(view, onclick);
1510
- let element = document.createElement("span");
1511
- element.textContent = conf.placeholderText;
1512
- element.setAttribute("aria-label", state.phrase("folded code"));
1513
- element.title = state.phrase("unfold");
1514
- element.className = "cm-foldPlaceholder";
1515
- element.onclick = onclick;
1516
- return element;
1517
- }
1524
+ toDOM(view) { return widgetToDOM(view, null); }
1518
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
+ }
1519
1534
  const foldGutterDefaults = {
1520
1535
  openText: "⌄",
1521
1536
  closedText: "›",
@@ -2485,7 +2500,7 @@ function createTokenType(extra, tagStr) {
2485
2500
  return type.id;
2486
2501
  }
2487
2502
  function docID(data) {
2488
- 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 });
2489
2504
  typeArray.push(type);
2490
2505
  return type;
2491
2506
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/language",
3
- "version": "6.8.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",