@codemirror/language 6.4.0 → 6.5.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,15 @@
1
+ ## 6.5.0 (2023-02-07)
2
+
3
+ ### Bug fixes
4
+
5
+ Make indentation for stream languages more reliable by having `StringStream.indentation` return overridden indentations from the indent context.
6
+
7
+ ### New features
8
+
9
+ The `toggleFold` command folds or unfolds depending on whether there's an existing folded range on the current line.
10
+
11
+ `indentUnit` now accepts any (repeated) whitespace character, not just spaces and tabs.
12
+
1
13
  ## 6.4.0 (2023-01-12)
2
14
 
3
15
  ### New features
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (C) 2018-2021 by Marijn Haverbeke <marijnh@gmail.com> and others
3
+ Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.cjs CHANGED
@@ -229,13 +229,13 @@ function syntaxParserRunning(view) {
229
229
  }
230
230
  // Lezer-style Input object for a Text document.
231
231
  class DocInput {
232
- constructor(doc, length = doc.length) {
232
+ constructor(doc) {
233
233
  this.doc = doc;
234
- this.length = length;
235
234
  this.cursorPos = 0;
236
235
  this.string = "";
237
236
  this.cursor = doc.iter();
238
237
  }
238
+ get length() { return this.doc.length; }
239
239
  syncTo(pos) {
240
240
  this.string = this.cursor.next(pos - this.cursorPos).value;
241
241
  this.cursorPos = pos + this.string.length;
@@ -780,17 +780,18 @@ service.
780
780
  */
781
781
  const indentService = state.Facet.define();
782
782
  /**
783
- Facet for overriding the unit by which indentation happens.
784
- Should be a string consisting either entirely of spaces or
785
- entirely of tabs. When not set, this defaults to 2 spaces.
783
+ Facet for overriding the unit by which indentation happens. Should
784
+ be a string consisting either entirely of the same whitespace
785
+ character. When not set, this defaults to 2 spaces.
786
786
  */
787
787
  const indentUnit = state.Facet.define({
788
788
  combine: values => {
789
789
  if (!values.length)
790
790
  return " ";
791
- if (!/^(?: +|\t+)$/.test(values[0]))
791
+ let unit = values[0];
792
+ if (!unit || /\S/.test(unit) || Array.from(unit).some(e => e != unit[0]))
792
793
  throw new Error("Invalid indent unit: " + JSON.stringify(values[0]));
793
- return values[0];
794
+ return unit;
794
795
  }
795
796
  });
796
797
  /**
@@ -810,14 +811,16 @@ Will use tabs for as much of the columns as possible when the
810
811
  tabs.
811
812
  */
812
813
  function indentString(state, cols) {
813
- let result = "", ts = state.tabSize;
814
- if (state.facet(indentUnit).charCodeAt(0) == 9)
814
+ let result = "", ts = state.tabSize, ch = state.facet(indentUnit)[0];
815
+ if (ch == "\t") {
815
816
  while (cols >= ts) {
816
817
  result += "\t";
817
818
  cols -= ts;
818
819
  }
820
+ ch = " ";
821
+ }
819
822
  for (let i = 0; i < cols; i++)
820
- result += " ";
823
+ result += ch;
821
824
  return result;
822
825
  }
823
826
  /**
@@ -1400,6 +1403,41 @@ const unfoldAll = view => {
1400
1403
  view.dispatch({ effects });
1401
1404
  return true;
1402
1405
  };
1406
+ // Find the foldable region containing the given line, if one exists
1407
+ function foldableContainer(view, lineBlock) {
1408
+ // Look backwards through line blocks until we find a foldable region that
1409
+ // intersects with the line
1410
+ for (let line = lineBlock;;) {
1411
+ let foldableRegion = foldable(view.state, line.from, line.to);
1412
+ if (foldableRegion && foldableRegion.to > lineBlock.from)
1413
+ return foldableRegion;
1414
+ if (!line.from)
1415
+ return null;
1416
+ line = view.lineBlockAt(line.from - 1);
1417
+ }
1418
+ }
1419
+ /**
1420
+ Toggle folding at cursors. Unfolds if there is an existing fold
1421
+ starting in that line, tries to find a foldable range around it
1422
+ otherwise.
1423
+ */
1424
+ const toggleFold = (view) => {
1425
+ let effects = [];
1426
+ for (let line of selectedLines(view)) {
1427
+ let folded = findFold(view.state, line.from, line.to);
1428
+ if (folded) {
1429
+ effects.push(unfoldEffect.of(folded), announceFold(view, folded, false));
1430
+ }
1431
+ else {
1432
+ let foldRange = foldableContainer(view, line);
1433
+ if (foldRange)
1434
+ effects.push(foldEffect.of(foldRange), announceFold(view, foldRange));
1435
+ }
1436
+ }
1437
+ if (effects.length > 0)
1438
+ view.dispatch({ effects: maybeEnable(view.state, effects) });
1439
+ return !!effects.length;
1440
+ };
1403
1441
  /**
1404
1442
  Default fold-related key bindings.
1405
1443
 
@@ -1923,10 +1961,11 @@ class StringStream {
1923
1961
  /**
1924
1962
  The current indent unit size.
1925
1963
  */
1926
- indentUnit) {
1964
+ indentUnit, overrideIndent) {
1927
1965
  this.string = string;
1928
1966
  this.tabSize = tabSize;
1929
1967
  this.indentUnit = indentUnit;
1968
+ this.overrideIndent = overrideIndent;
1930
1969
  /**
1931
1970
  The current position on the line.
1932
1971
  */
@@ -2027,7 +2066,8 @@ class StringStream {
2027
2066
  Get the indentation column of the current line.
2028
2067
  */
2029
2068
  indentation() {
2030
- return countCol(this.string, null, this.tabSize);
2069
+ var _a;
2070
+ return (_a = this.overrideIndent) !== null && _a !== void 0 ? _a : countCol(this.string, null, this.tabSize);
2031
2071
  }
2032
2072
  /**
2033
2073
  Match the input against the given string or regular expression
@@ -2089,6 +2129,7 @@ function defaultCopyState(state) {
2089
2129
  }
2090
2130
  return newState;
2091
2131
  }
2132
+ const IndentedFrom = new WeakMap();
2092
2133
  /**
2093
2134
  A [language](https://codemirror.net/6/docs/ref/#language.Language) class based on a CodeMirror
2094
2135
  5-style [streaming parser](https://codemirror.net/6/docs/ref/#language.StreamParser).
@@ -2119,7 +2160,14 @@ class StreamLanguage extends Language {
2119
2160
  at = at.parent;
2120
2161
  if (!at)
2121
2162
  return null;
2122
- let start = findState(this, tree, 0, at.from, pos), statePos, state;
2163
+ let from = undefined;
2164
+ let { overrideIndentation } = cx.options;
2165
+ if (overrideIndentation) {
2166
+ from = IndentedFrom.get(cx.state);
2167
+ if (from != null && from < pos - 1e4)
2168
+ from = undefined;
2169
+ }
2170
+ let start = findState(this, tree, 0, at.from, from !== null && from !== void 0 ? from : pos), statePos, state;
2123
2171
  if (start) {
2124
2172
  state = start.state;
2125
2173
  statePos = start.pos + 1;
@@ -2133,7 +2181,8 @@ class StreamLanguage extends Language {
2133
2181
  while (statePos < pos) {
2134
2182
  let line = cx.state.doc.lineAt(statePos), end = Math.min(pos, line.to);
2135
2183
  if (line.length) {
2136
- let stream = new StringStream(line.text, cx.state.tabSize, cx.unit);
2184
+ let indentation = overrideIndentation ? overrideIndentation(line.from) : -1;
2185
+ let stream = new StringStream(line.text, cx.state.tabSize, cx.unit, indentation < 0 ? undefined : indentation);
2137
2186
  while (stream.pos < end - line.from)
2138
2187
  readToken(this.streamParser.token, stream, state);
2139
2188
  }
@@ -2144,8 +2193,10 @@ class StreamLanguage extends Language {
2144
2193
  break;
2145
2194
  statePos = line.to + 1;
2146
2195
  }
2147
- let { text } = cx.lineAt(pos);
2148
- return this.streamParser.indent(state, /^\s*(.*)/.exec(text)[1], cx);
2196
+ let line = cx.lineAt(pos);
2197
+ if (overrideIndentation && from == null)
2198
+ IndentedFrom.set(cx.state, line.from);
2199
+ return this.streamParser.indent(state, /^\s*(.*)/.exec(line.text)[1], cx);
2149
2200
  }
2150
2201
  get allowsNesting() { return false; }
2151
2202
  }
@@ -2459,6 +2510,7 @@ exports.syntaxHighlighting = syntaxHighlighting;
2459
2510
  exports.syntaxParserRunning = syntaxParserRunning;
2460
2511
  exports.syntaxTree = syntaxTree;
2461
2512
  exports.syntaxTreeAvailable = syntaxTreeAvailable;
2513
+ exports.toggleFold = toggleFold;
2462
2514
  exports.unfoldAll = unfoldAll;
2463
2515
  exports.unfoldCode = unfoldCode;
2464
2516
  exports.unfoldEffect = unfoldEffect;
package/dist/index.d.ts CHANGED
@@ -370,9 +370,9 @@ service.
370
370
  */
371
371
  declare const indentService: Facet<(context: IndentContext, pos: number) => number | null | undefined, readonly ((context: IndentContext, pos: number) => number | null | undefined)[]>;
372
372
  /**
373
- Facet for overriding the unit by which indentation happens.
374
- Should be a string consisting either entirely of spaces or
375
- entirely of tabs. When not set, this defaults to 2 spaces.
373
+ Facet for overriding the unit by which indentation happens. Should
374
+ be a string consisting either entirely of the same whitespace
375
+ character. When not set, this defaults to 2 spaces.
376
376
  */
377
377
  declare const indentUnit: Facet<string, string>;
378
378
  /**
@@ -450,7 +450,7 @@ declare class IndentContext {
450
450
  simulateBreak?: number;
451
451
  /**
452
452
  When `simulateBreak` is given, this can be used to make the
453
- simulate break behave like a double line break.
453
+ simulated break behave like a double line break.
454
454
  */
455
455
  simulateDoubleBreak?: boolean;
456
456
  });
@@ -678,6 +678,12 @@ Unfold all folded code.
678
678
  */
679
679
  declare const unfoldAll: Command;
680
680
  /**
681
+ Toggle folding at cursors. Unfolds if there is an existing fold
682
+ starting in that line, tries to find a foldable range around it
683
+ otherwise.
684
+ */
685
+ declare const toggleFold: Command;
686
+ /**
681
687
  Default fold-related key bindings.
682
688
 
683
689
  - Ctrl-Shift-[ (Cmd-Alt-[ on macOS): [`foldCode`](https://codemirror.net/6/docs/ref/#language.foldCode).
@@ -943,6 +949,7 @@ declare class StringStream {
943
949
  The current indent unit size.
944
950
  */
945
951
  indentUnit: number;
952
+ private overrideIndent?;
946
953
  /**
947
954
  The current position on the line.
948
955
  */
@@ -964,7 +971,7 @@ declare class StringStream {
964
971
  /**
965
972
  The current indent unit size.
966
973
  */
967
- indentUnit: number);
974
+ indentUnit: number, overrideIndent?: number | undefined);
968
975
  /**
969
976
  True if we are at the end of the line.
970
977
  */
@@ -1112,4 +1119,4 @@ declare class StreamLanguage<State> extends Language {
1112
1119
  get allowsNesting(): boolean;
1113
1120
  }
1114
1121
 
1115
- export { Config, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, MatchResult, ParseContext, StreamLanguage, StreamParser, StringStream, TagStyle, TreeIndentContext, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, unfoldAll, unfoldCode, unfoldEffect };
1122
+ export { Config, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, MatchResult, ParseContext, StreamLanguage, StreamParser, StringStream, TagStyle, TreeIndentContext, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };
package/dist/index.js CHANGED
@@ -225,13 +225,13 @@ function syntaxParserRunning(view) {
225
225
  }
226
226
  // Lezer-style Input object for a Text document.
227
227
  class DocInput {
228
- constructor(doc, length = doc.length) {
228
+ constructor(doc) {
229
229
  this.doc = doc;
230
- this.length = length;
231
230
  this.cursorPos = 0;
232
231
  this.string = "";
233
232
  this.cursor = doc.iter();
234
233
  }
234
+ get length() { return this.doc.length; }
235
235
  syncTo(pos) {
236
236
  this.string = this.cursor.next(pos - this.cursorPos).value;
237
237
  this.cursorPos = pos + this.string.length;
@@ -776,17 +776,18 @@ service.
776
776
  */
777
777
  const indentService = /*@__PURE__*/Facet.define();
778
778
  /**
779
- Facet for overriding the unit by which indentation happens.
780
- Should be a string consisting either entirely of spaces or
781
- entirely of tabs. When not set, this defaults to 2 spaces.
779
+ Facet for overriding the unit by which indentation happens. Should
780
+ be a string consisting either entirely of the same whitespace
781
+ character. When not set, this defaults to 2 spaces.
782
782
  */
783
783
  const indentUnit = /*@__PURE__*/Facet.define({
784
784
  combine: values => {
785
785
  if (!values.length)
786
786
  return " ";
787
- if (!/^(?: +|\t+)$/.test(values[0]))
787
+ let unit = values[0];
788
+ if (!unit || /\S/.test(unit) || Array.from(unit).some(e => e != unit[0]))
788
789
  throw new Error("Invalid indent unit: " + JSON.stringify(values[0]));
789
- return values[0];
790
+ return unit;
790
791
  }
791
792
  });
792
793
  /**
@@ -806,14 +807,16 @@ Will use tabs for as much of the columns as possible when the
806
807
  tabs.
807
808
  */
808
809
  function indentString(state, cols) {
809
- let result = "", ts = state.tabSize;
810
- if (state.facet(indentUnit).charCodeAt(0) == 9)
810
+ let result = "", ts = state.tabSize, ch = state.facet(indentUnit)[0];
811
+ if (ch == "\t") {
811
812
  while (cols >= ts) {
812
813
  result += "\t";
813
814
  cols -= ts;
814
815
  }
816
+ ch = " ";
817
+ }
815
818
  for (let i = 0; i < cols; i++)
816
- result += " ";
819
+ result += ch;
817
820
  return result;
818
821
  }
819
822
  /**
@@ -1396,6 +1399,41 @@ const unfoldAll = view => {
1396
1399
  view.dispatch({ effects });
1397
1400
  return true;
1398
1401
  };
1402
+ // Find the foldable region containing the given line, if one exists
1403
+ function foldableContainer(view, lineBlock) {
1404
+ // Look backwards through line blocks until we find a foldable region that
1405
+ // intersects with the line
1406
+ for (let line = lineBlock;;) {
1407
+ let foldableRegion = foldable(view.state, line.from, line.to);
1408
+ if (foldableRegion && foldableRegion.to > lineBlock.from)
1409
+ return foldableRegion;
1410
+ if (!line.from)
1411
+ return null;
1412
+ line = view.lineBlockAt(line.from - 1);
1413
+ }
1414
+ }
1415
+ /**
1416
+ Toggle folding at cursors. Unfolds if there is an existing fold
1417
+ starting in that line, tries to find a foldable range around it
1418
+ otherwise.
1419
+ */
1420
+ const toggleFold = (view) => {
1421
+ let effects = [];
1422
+ for (let line of selectedLines(view)) {
1423
+ let folded = findFold(view.state, line.from, line.to);
1424
+ if (folded) {
1425
+ effects.push(unfoldEffect.of(folded), announceFold(view, folded, false));
1426
+ }
1427
+ else {
1428
+ let foldRange = foldableContainer(view, line);
1429
+ if (foldRange)
1430
+ effects.push(foldEffect.of(foldRange), announceFold(view, foldRange));
1431
+ }
1432
+ }
1433
+ if (effects.length > 0)
1434
+ view.dispatch({ effects: maybeEnable(view.state, effects) });
1435
+ return !!effects.length;
1436
+ };
1399
1437
  /**
1400
1438
  Default fold-related key bindings.
1401
1439
 
@@ -1919,10 +1957,11 @@ class StringStream {
1919
1957
  /**
1920
1958
  The current indent unit size.
1921
1959
  */
1922
- indentUnit) {
1960
+ indentUnit, overrideIndent) {
1923
1961
  this.string = string;
1924
1962
  this.tabSize = tabSize;
1925
1963
  this.indentUnit = indentUnit;
1964
+ this.overrideIndent = overrideIndent;
1926
1965
  /**
1927
1966
  The current position on the line.
1928
1967
  */
@@ -2023,7 +2062,8 @@ class StringStream {
2023
2062
  Get the indentation column of the current line.
2024
2063
  */
2025
2064
  indentation() {
2026
- return countCol(this.string, null, this.tabSize);
2065
+ var _a;
2066
+ return (_a = this.overrideIndent) !== null && _a !== void 0 ? _a : countCol(this.string, null, this.tabSize);
2027
2067
  }
2028
2068
  /**
2029
2069
  Match the input against the given string or regular expression
@@ -2085,6 +2125,7 @@ function defaultCopyState(state) {
2085
2125
  }
2086
2126
  return newState;
2087
2127
  }
2128
+ const IndentedFrom = /*@__PURE__*/new WeakMap();
2088
2129
  /**
2089
2130
  A [language](https://codemirror.net/6/docs/ref/#language.Language) class based on a CodeMirror
2090
2131
  5-style [streaming parser](https://codemirror.net/6/docs/ref/#language.StreamParser).
@@ -2115,7 +2156,14 @@ class StreamLanguage extends Language {
2115
2156
  at = at.parent;
2116
2157
  if (!at)
2117
2158
  return null;
2118
- let start = findState(this, tree, 0, at.from, pos), statePos, state;
2159
+ let from = undefined;
2160
+ let { overrideIndentation } = cx.options;
2161
+ if (overrideIndentation) {
2162
+ from = IndentedFrom.get(cx.state);
2163
+ if (from != null && from < pos - 1e4)
2164
+ from = undefined;
2165
+ }
2166
+ let start = findState(this, tree, 0, at.from, from !== null && from !== void 0 ? from : pos), statePos, state;
2119
2167
  if (start) {
2120
2168
  state = start.state;
2121
2169
  statePos = start.pos + 1;
@@ -2129,7 +2177,8 @@ class StreamLanguage extends Language {
2129
2177
  while (statePos < pos) {
2130
2178
  let line = cx.state.doc.lineAt(statePos), end = Math.min(pos, line.to);
2131
2179
  if (line.length) {
2132
- let stream = new StringStream(line.text, cx.state.tabSize, cx.unit);
2180
+ let indentation = overrideIndentation ? overrideIndentation(line.from) : -1;
2181
+ let stream = new StringStream(line.text, cx.state.tabSize, cx.unit, indentation < 0 ? undefined : indentation);
2133
2182
  while (stream.pos < end - line.from)
2134
2183
  readToken(this.streamParser.token, stream, state);
2135
2184
  }
@@ -2140,8 +2189,10 @@ class StreamLanguage extends Language {
2140
2189
  break;
2141
2190
  statePos = line.to + 1;
2142
2191
  }
2143
- let { text } = cx.lineAt(pos);
2144
- return this.streamParser.indent(state, /^\s*(.*)/.exec(text)[1], cx);
2192
+ let line = cx.lineAt(pos);
2193
+ if (overrideIndentation && from == null)
2194
+ IndentedFrom.set(cx.state, line.from);
2195
+ return this.streamParser.indent(state, /^\s*(.*)/.exec(line.text)[1], cx);
2145
2196
  }
2146
2197
  get allowsNesting() { return false; }
2147
2198
  }
@@ -2408,4 +2459,4 @@ function docID(data) {
2408
2459
  return type;
2409
2460
  }
2410
2461
 
2411
- export { HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, StreamLanguage, StringStream, TreeIndentContext, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, unfoldAll, unfoldCode, unfoldEffect };
2462
+ export { HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, StreamLanguage, StringStream, TreeIndentContext, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/language",
3
- "version": "6.4.0",
3
+ "version": "6.5.0",
4
4
  "description": "Language support infrastructure for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "author": {
14
14
  "name": "Marijn Haverbeke",
15
- "email": "marijnh@gmail.com",
15
+ "email": "marijn@haverbeke.berlin",
16
16
  "url": "http://marijnhaverbeke.nl"
17
17
  },
18
18
  "type": "module",