@codemirror/language 6.4.0 → 6.6.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,21 @@
1
+ ## 6.6.0 (2023-02-13)
2
+
3
+ ### New features
4
+
5
+ Syntax-driven language data queries now support sublanguages, which make it possible to return different data for specific parts of the tree produced by a single language.
6
+
7
+ ## 6.5.0 (2023-02-07)
8
+
9
+ ### Bug fixes
10
+
11
+ Make indentation for stream languages more reliable by having `StringStream.indentation` return overridden indentations from the indent context.
12
+
13
+ ### New features
14
+
15
+ The `toggleFold` command folds or unfolds depending on whether there's an existing folded range on the current line.
16
+
17
+ `indentUnit` now accepts any (repeated) whitespace character, not just spaces and tabs.
18
+
1
19
  ## 6.4.0 (2023-01-12)
2
20
 
3
21
  ### 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
@@ -28,6 +28,11 @@ function defineLanguageFacet(baseData) {
28
28
  });
29
29
  }
30
30
  /**
31
+ Syntax node prop used to register sublangauges. Should be added to
32
+ the top level node type for the language.
33
+ */
34
+ const sublanguageProp = new common.NodeProp();
35
+ /**
31
36
  A language object manages parsing and per-language
32
37
  [metadata](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt). Parse data is
33
38
  managed as a [Lezer](https://lezer.codemirror.net) tree. The class
@@ -64,14 +69,28 @@ class Language {
64
69
  this.parser = parser;
65
70
  this.extension = [
66
71
  language.of(this),
67
- state.EditorState.languageData.of((state, pos, side) => state.facet(languageDataFacetAt(state, pos, side)))
72
+ state.EditorState.languageData.of((state, pos, side) => {
73
+ let top = topNodeAt(state, pos, side), data = top.type.prop(languageDataProp);
74
+ if (!data)
75
+ return [];
76
+ let base = state.facet(data), sub = top.type.prop(sublanguageProp);
77
+ if (sub) {
78
+ let innerNode = top.resolve(pos - top.from, side);
79
+ for (let sublang of sub)
80
+ if (sublang.test(innerNode, state)) {
81
+ let data = state.facet(sublang.facet);
82
+ return sublang.type == "replace" ? data : data.concat(base);
83
+ }
84
+ }
85
+ return base;
86
+ })
68
87
  ].concat(extraExtensions);
69
88
  }
70
89
  /**
71
90
  Query whether this language is active at the given position.
72
91
  */
73
92
  isActiveAt(state, pos, side = -1) {
74
- return languageDataFacetAt(state, pos, side) == this.data;
93
+ return topNodeAt(state, pos, side).type.prop(languageDataProp) == this.data;
75
94
  }
76
95
  /**
77
96
  Find the document regions that were parsed using this language.
@@ -126,16 +145,14 @@ class Language {
126
145
  @internal
127
146
  */
128
147
  Language.setState = state.StateEffect.define();
129
- function languageDataFacetAt(state, pos, side) {
130
- let topLang = state.facet(language);
131
- if (!topLang)
132
- return null;
133
- let facet = topLang.data;
134
- if (topLang.allowsNesting) {
135
- for (let node = syntaxTree(state).topNode; node; node = node.enter(pos, side, common.IterMode.ExcludeBuffers))
136
- facet = node.type.prop(languageDataProp) || facet;
148
+ function topNodeAt(state, pos, side) {
149
+ let topLang = state.facet(language), tree = syntaxTree(state).topNode;
150
+ if (!topLang || topLang.allowsNesting) {
151
+ for (let node = tree; node; node = node.enter(pos, side, common.IterMode.ExcludeBuffers))
152
+ if (node.type.isTop)
153
+ tree = node;
137
154
  }
138
- return facet;
155
+ return tree;
139
156
  }
140
157
  /**
141
158
  A subclass of [`Language`](https://codemirror.net/6/docs/ref/#language.Language) for use with Lezer
@@ -229,13 +246,13 @@ function syntaxParserRunning(view) {
229
246
  }
230
247
  // Lezer-style Input object for a Text document.
231
248
  class DocInput {
232
- constructor(doc, length = doc.length) {
249
+ constructor(doc) {
233
250
  this.doc = doc;
234
- this.length = length;
235
251
  this.cursorPos = 0;
236
252
  this.string = "";
237
253
  this.cursor = doc.iter();
238
254
  }
255
+ get length() { return this.doc.length; }
239
256
  syncTo(pos) {
240
257
  this.string = this.cursor.next(pos - this.cursorPos).value;
241
258
  this.cursorPos = pos + this.string.length;
@@ -780,17 +797,18 @@ service.
780
797
  */
781
798
  const indentService = state.Facet.define();
782
799
  /**
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.
800
+ Facet for overriding the unit by which indentation happens. Should
801
+ be a string consisting either entirely of the same whitespace
802
+ character. When not set, this defaults to 2 spaces.
786
803
  */
787
804
  const indentUnit = state.Facet.define({
788
805
  combine: values => {
789
806
  if (!values.length)
790
807
  return " ";
791
- if (!/^(?: +|\t+)$/.test(values[0]))
808
+ let unit = values[0];
809
+ if (!unit || /\S/.test(unit) || Array.from(unit).some(e => e != unit[0]))
792
810
  throw new Error("Invalid indent unit: " + JSON.stringify(values[0]));
793
- return values[0];
811
+ return unit;
794
812
  }
795
813
  });
796
814
  /**
@@ -810,14 +828,16 @@ Will use tabs for as much of the columns as possible when the
810
828
  tabs.
811
829
  */
812
830
  function indentString(state, cols) {
813
- let result = "", ts = state.tabSize;
814
- if (state.facet(indentUnit).charCodeAt(0) == 9)
831
+ let result = "", ts = state.tabSize, ch = state.facet(indentUnit)[0];
832
+ if (ch == "\t") {
815
833
  while (cols >= ts) {
816
834
  result += "\t";
817
835
  cols -= ts;
818
836
  }
837
+ ch = " ";
838
+ }
819
839
  for (let i = 0; i < cols; i++)
820
- result += " ";
840
+ result += ch;
821
841
  return result;
822
842
  }
823
843
  /**
@@ -1400,6 +1420,41 @@ const unfoldAll = view => {
1400
1420
  view.dispatch({ effects });
1401
1421
  return true;
1402
1422
  };
1423
+ // Find the foldable region containing the given line, if one exists
1424
+ function foldableContainer(view, lineBlock) {
1425
+ // Look backwards through line blocks until we find a foldable region that
1426
+ // intersects with the line
1427
+ for (let line = lineBlock;;) {
1428
+ let foldableRegion = foldable(view.state, line.from, line.to);
1429
+ if (foldableRegion && foldableRegion.to > lineBlock.from)
1430
+ return foldableRegion;
1431
+ if (!line.from)
1432
+ return null;
1433
+ line = view.lineBlockAt(line.from - 1);
1434
+ }
1435
+ }
1436
+ /**
1437
+ Toggle folding at cursors. Unfolds if there is an existing fold
1438
+ starting in that line, tries to find a foldable range around it
1439
+ otherwise.
1440
+ */
1441
+ const toggleFold = (view) => {
1442
+ let effects = [];
1443
+ for (let line of selectedLines(view)) {
1444
+ let folded = findFold(view.state, line.from, line.to);
1445
+ if (folded) {
1446
+ effects.push(unfoldEffect.of(folded), announceFold(view, folded, false));
1447
+ }
1448
+ else {
1449
+ let foldRange = foldableContainer(view, line);
1450
+ if (foldRange)
1451
+ effects.push(foldEffect.of(foldRange), announceFold(view, foldRange));
1452
+ }
1453
+ }
1454
+ if (effects.length > 0)
1455
+ view.dispatch({ effects: maybeEnable(view.state, effects) });
1456
+ return !!effects.length;
1457
+ };
1403
1458
  /**
1404
1459
  Default fold-related key bindings.
1405
1460
 
@@ -1923,10 +1978,11 @@ class StringStream {
1923
1978
  /**
1924
1979
  The current indent unit size.
1925
1980
  */
1926
- indentUnit) {
1981
+ indentUnit, overrideIndent) {
1927
1982
  this.string = string;
1928
1983
  this.tabSize = tabSize;
1929
1984
  this.indentUnit = indentUnit;
1985
+ this.overrideIndent = overrideIndent;
1930
1986
  /**
1931
1987
  The current position on the line.
1932
1988
  */
@@ -2027,7 +2083,8 @@ class StringStream {
2027
2083
  Get the indentation column of the current line.
2028
2084
  */
2029
2085
  indentation() {
2030
- return countCol(this.string, null, this.tabSize);
2086
+ var _a;
2087
+ return (_a = this.overrideIndent) !== null && _a !== void 0 ? _a : countCol(this.string, null, this.tabSize);
2031
2088
  }
2032
2089
  /**
2033
2090
  Match the input against the given string or regular expression
@@ -2089,6 +2146,7 @@ function defaultCopyState(state) {
2089
2146
  }
2090
2147
  return newState;
2091
2148
  }
2149
+ const IndentedFrom = new WeakMap();
2092
2150
  /**
2093
2151
  A [language](https://codemirror.net/6/docs/ref/#language.Language) class based on a CodeMirror
2094
2152
  5-style [streaming parser](https://codemirror.net/6/docs/ref/#language.StreamParser).
@@ -2119,7 +2177,14 @@ class StreamLanguage extends Language {
2119
2177
  at = at.parent;
2120
2178
  if (!at)
2121
2179
  return null;
2122
- let start = findState(this, tree, 0, at.from, pos), statePos, state;
2180
+ let from = undefined;
2181
+ let { overrideIndentation } = cx.options;
2182
+ if (overrideIndentation) {
2183
+ from = IndentedFrom.get(cx.state);
2184
+ if (from != null && from < pos - 1e4)
2185
+ from = undefined;
2186
+ }
2187
+ let start = findState(this, tree, 0, at.from, from !== null && from !== void 0 ? from : pos), statePos, state;
2123
2188
  if (start) {
2124
2189
  state = start.state;
2125
2190
  statePos = start.pos + 1;
@@ -2133,7 +2198,8 @@ class StreamLanguage extends Language {
2133
2198
  while (statePos < pos) {
2134
2199
  let line = cx.state.doc.lineAt(statePos), end = Math.min(pos, line.to);
2135
2200
  if (line.length) {
2136
- let stream = new StringStream(line.text, cx.state.tabSize, cx.unit);
2201
+ let indentation = overrideIndentation ? overrideIndentation(line.from) : -1;
2202
+ let stream = new StringStream(line.text, cx.state.tabSize, cx.unit, indentation < 0 ? undefined : indentation);
2137
2203
  while (stream.pos < end - line.from)
2138
2204
  readToken(this.streamParser.token, stream, state);
2139
2205
  }
@@ -2144,8 +2210,10 @@ class StreamLanguage extends Language {
2144
2210
  break;
2145
2211
  statePos = line.to + 1;
2146
2212
  }
2147
- let { text } = cx.lineAt(pos);
2148
- return this.streamParser.indent(state, /^\s*(.*)/.exec(text)[1], cx);
2213
+ let line = cx.lineAt(pos);
2214
+ if (overrideIndentation && from == null)
2215
+ IndentedFrom.set(cx.state, line.from);
2216
+ return this.streamParser.indent(state, /^\s*(.*)/.exec(line.text)[1], cx);
2149
2217
  }
2150
2218
  get allowsNesting() { return false; }
2151
2219
  }
@@ -2455,10 +2523,12 @@ exports.indentUnit = indentUnit;
2455
2523
  exports.language = language;
2456
2524
  exports.languageDataProp = languageDataProp;
2457
2525
  exports.matchBrackets = matchBrackets;
2526
+ exports.sublanguageProp = sublanguageProp;
2458
2527
  exports.syntaxHighlighting = syntaxHighlighting;
2459
2528
  exports.syntaxParserRunning = syntaxParserRunning;
2460
2529
  exports.syntaxTree = syntaxTree;
2461
2530
  exports.syntaxTreeAvailable = syntaxTreeAvailable;
2531
+ exports.toggleFold = toggleFold;
2462
2532
  exports.unfoldAll = unfoldAll;
2463
2533
  exports.unfoldCode = unfoldCode;
2464
2534
  exports.unfoldEffect = unfoldEffect;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { NodeProp, Parser, Tree, TreeFragment, SyntaxNode, NodeType } from '@lezer/common';
1
+ import { NodeProp, SyntaxNode, Parser, Tree, TreeFragment, NodeType } from '@lezer/common';
2
2
  import { LRParser, ParserConfig } from '@lezer/lr';
3
3
  import * as _codemirror_state from '@codemirror/state';
4
- import { Facet, Extension, EditorState, StateField, Range } from '@codemirror/state';
4
+ import { Facet, EditorState, Extension, StateField, Range } from '@codemirror/state';
5
5
  import { EditorView, DecorationSet, Command, KeyBinding, ViewUpdate, BlockInfo, Decoration } from '@codemirror/view';
6
6
  import { Highlighter, Tag } from '@lezer/highlight';
7
7
  import { StyleModule, StyleSpec } from 'style-mod';
@@ -31,6 +31,44 @@ declare function defineLanguageFacet(baseData?: {
31
31
  [name: string]: any;
32
32
  }[]>;
33
33
  /**
34
+ Some languages need to return different [language
35
+ data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) for some parts of their
36
+ tree. Sublanguages, registered by adding a [node
37
+ prop](https://codemirror.net/6/docs/ref/#language.sublanguageProp) to the language's top syntax
38
+ node, provide a mechanism to do this.
39
+
40
+ (Note that when using nested parsing, where nested syntax is
41
+ parsed by a different parser and has its own top node type, you
42
+ don't need a sublanguage.)
43
+ */
44
+ interface Sublanguage {
45
+ /**
46
+ Determines whether the data provided by this sublanguage should
47
+ completely replace the regular data or be added to it (with
48
+ higher-precedence). The default is `"extend"`.
49
+ */
50
+ type?: "replace" | "extend";
51
+ /**
52
+ A predicate that returns whether the node at the queried
53
+ position is part of the sublanguage.
54
+ */
55
+ test: (node: SyntaxNode, state: EditorState) => boolean;
56
+ /**
57
+ The language data facet that holds the sublanguage's data.
58
+ You'll want to use
59
+ [`defineLanguageFacet`](https://codemirror.net/6/docs/ref/#language.defineLanguageFacet) to create
60
+ this.
61
+ */
62
+ facet: Facet<{
63
+ [name: string]: any;
64
+ }>;
65
+ }
66
+ /**
67
+ Syntax node prop used to register sublangauges. Should be added to
68
+ the top level node type for the language.
69
+ */
70
+ declare const sublanguageProp: NodeProp<Sublanguage[]>;
71
+ /**
34
72
  A language object manages parsing and per-language
35
73
  [metadata](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt). Parse data is
36
74
  managed as a [Lezer](https://lezer.codemirror.net) tree. The class
@@ -370,9 +408,9 @@ service.
370
408
  */
371
409
  declare const indentService: Facet<(context: IndentContext, pos: number) => number | null | undefined, readonly ((context: IndentContext, pos: number) => number | null | undefined)[]>;
372
410
  /**
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.
411
+ Facet for overriding the unit by which indentation happens. Should
412
+ be a string consisting either entirely of the same whitespace
413
+ character. When not set, this defaults to 2 spaces.
376
414
  */
377
415
  declare const indentUnit: Facet<string, string>;
378
416
  /**
@@ -450,7 +488,7 @@ declare class IndentContext {
450
488
  simulateBreak?: number;
451
489
  /**
452
490
  When `simulateBreak` is given, this can be used to make the
453
- simulate break behave like a double line break.
491
+ simulated break behave like a double line break.
454
492
  */
455
493
  simulateDoubleBreak?: boolean;
456
494
  });
@@ -678,6 +716,12 @@ Unfold all folded code.
678
716
  */
679
717
  declare const unfoldAll: Command;
680
718
  /**
719
+ Toggle folding at cursors. Unfolds if there is an existing fold
720
+ starting in that line, tries to find a foldable range around it
721
+ otherwise.
722
+ */
723
+ declare const toggleFold: Command;
724
+ /**
681
725
  Default fold-related key bindings.
682
726
 
683
727
  - Ctrl-Shift-[ (Cmd-Alt-[ on macOS): [`foldCode`](https://codemirror.net/6/docs/ref/#language.foldCode).
@@ -943,6 +987,7 @@ declare class StringStream {
943
987
  The current indent unit size.
944
988
  */
945
989
  indentUnit: number;
990
+ private overrideIndent?;
946
991
  /**
947
992
  The current position on the line.
948
993
  */
@@ -964,7 +1009,7 @@ declare class StringStream {
964
1009
  /**
965
1010
  The current indent unit size.
966
1011
  */
967
- indentUnit: number);
1012
+ indentUnit: number, overrideIndent?: number | undefined);
968
1013
  /**
969
1014
  True if we are at the end of the line.
970
1015
  */
@@ -1112,4 +1157,4 @@ declare class StreamLanguage<State> extends Language {
1112
1157
  get allowsNesting(): boolean;
1113
1158
  }
1114
1159
 
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 };
1160
+ export { Config, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, MatchResult, ParseContext, StreamLanguage, StreamParser, StringStream, Sublanguage, 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, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };
package/dist/index.js CHANGED
@@ -24,6 +24,11 @@ function defineLanguageFacet(baseData) {
24
24
  });
25
25
  }
26
26
  /**
27
+ Syntax node prop used to register sublangauges. Should be added to
28
+ the top level node type for the language.
29
+ */
30
+ const sublanguageProp = /*@__PURE__*/new NodeProp();
31
+ /**
27
32
  A language object manages parsing and per-language
28
33
  [metadata](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt). Parse data is
29
34
  managed as a [Lezer](https://lezer.codemirror.net) tree. The class
@@ -60,14 +65,28 @@ class Language {
60
65
  this.parser = parser;
61
66
  this.extension = [
62
67
  language.of(this),
63
- EditorState.languageData.of((state, pos, side) => state.facet(languageDataFacetAt(state, pos, side)))
68
+ EditorState.languageData.of((state, pos, side) => {
69
+ let top = topNodeAt(state, pos, side), data = top.type.prop(languageDataProp);
70
+ if (!data)
71
+ return [];
72
+ let base = state.facet(data), sub = top.type.prop(sublanguageProp);
73
+ if (sub) {
74
+ let innerNode = top.resolve(pos - top.from, side);
75
+ for (let sublang of sub)
76
+ if (sublang.test(innerNode, state)) {
77
+ let data = state.facet(sublang.facet);
78
+ return sublang.type == "replace" ? data : data.concat(base);
79
+ }
80
+ }
81
+ return base;
82
+ })
64
83
  ].concat(extraExtensions);
65
84
  }
66
85
  /**
67
86
  Query whether this language is active at the given position.
68
87
  */
69
88
  isActiveAt(state, pos, side = -1) {
70
- return languageDataFacetAt(state, pos, side) == this.data;
89
+ return topNodeAt(state, pos, side).type.prop(languageDataProp) == this.data;
71
90
  }
72
91
  /**
73
92
  Find the document regions that were parsed using this language.
@@ -122,16 +141,14 @@ class Language {
122
141
  @internal
123
142
  */
124
143
  Language.setState = /*@__PURE__*/StateEffect.define();
125
- function languageDataFacetAt(state, pos, side) {
126
- let topLang = state.facet(language);
127
- if (!topLang)
128
- return null;
129
- let facet = topLang.data;
130
- if (topLang.allowsNesting) {
131
- for (let node = syntaxTree(state).topNode; node; node = node.enter(pos, side, IterMode.ExcludeBuffers))
132
- facet = node.type.prop(languageDataProp) || facet;
144
+ function topNodeAt(state, pos, side) {
145
+ let topLang = state.facet(language), tree = syntaxTree(state).topNode;
146
+ if (!topLang || topLang.allowsNesting) {
147
+ for (let node = tree; node; node = node.enter(pos, side, IterMode.ExcludeBuffers))
148
+ if (node.type.isTop)
149
+ tree = node;
133
150
  }
134
- return facet;
151
+ return tree;
135
152
  }
136
153
  /**
137
154
  A subclass of [`Language`](https://codemirror.net/6/docs/ref/#language.Language) for use with Lezer
@@ -225,13 +242,13 @@ function syntaxParserRunning(view) {
225
242
  }
226
243
  // Lezer-style Input object for a Text document.
227
244
  class DocInput {
228
- constructor(doc, length = doc.length) {
245
+ constructor(doc) {
229
246
  this.doc = doc;
230
- this.length = length;
231
247
  this.cursorPos = 0;
232
248
  this.string = "";
233
249
  this.cursor = doc.iter();
234
250
  }
251
+ get length() { return this.doc.length; }
235
252
  syncTo(pos) {
236
253
  this.string = this.cursor.next(pos - this.cursorPos).value;
237
254
  this.cursorPos = pos + this.string.length;
@@ -776,17 +793,18 @@ service.
776
793
  */
777
794
  const indentService = /*@__PURE__*/Facet.define();
778
795
  /**
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.
796
+ Facet for overriding the unit by which indentation happens. Should
797
+ be a string consisting either entirely of the same whitespace
798
+ character. When not set, this defaults to 2 spaces.
782
799
  */
783
800
  const indentUnit = /*@__PURE__*/Facet.define({
784
801
  combine: values => {
785
802
  if (!values.length)
786
803
  return " ";
787
- if (!/^(?: +|\t+)$/.test(values[0]))
804
+ let unit = values[0];
805
+ if (!unit || /\S/.test(unit) || Array.from(unit).some(e => e != unit[0]))
788
806
  throw new Error("Invalid indent unit: " + JSON.stringify(values[0]));
789
- return values[0];
807
+ return unit;
790
808
  }
791
809
  });
792
810
  /**
@@ -806,14 +824,16 @@ Will use tabs for as much of the columns as possible when the
806
824
  tabs.
807
825
  */
808
826
  function indentString(state, cols) {
809
- let result = "", ts = state.tabSize;
810
- if (state.facet(indentUnit).charCodeAt(0) == 9)
827
+ let result = "", ts = state.tabSize, ch = state.facet(indentUnit)[0];
828
+ if (ch == "\t") {
811
829
  while (cols >= ts) {
812
830
  result += "\t";
813
831
  cols -= ts;
814
832
  }
833
+ ch = " ";
834
+ }
815
835
  for (let i = 0; i < cols; i++)
816
- result += " ";
836
+ result += ch;
817
837
  return result;
818
838
  }
819
839
  /**
@@ -1396,6 +1416,41 @@ const unfoldAll = view => {
1396
1416
  view.dispatch({ effects });
1397
1417
  return true;
1398
1418
  };
1419
+ // Find the foldable region containing the given line, if one exists
1420
+ function foldableContainer(view, lineBlock) {
1421
+ // Look backwards through line blocks until we find a foldable region that
1422
+ // intersects with the line
1423
+ for (let line = lineBlock;;) {
1424
+ let foldableRegion = foldable(view.state, line.from, line.to);
1425
+ if (foldableRegion && foldableRegion.to > lineBlock.from)
1426
+ return foldableRegion;
1427
+ if (!line.from)
1428
+ return null;
1429
+ line = view.lineBlockAt(line.from - 1);
1430
+ }
1431
+ }
1432
+ /**
1433
+ Toggle folding at cursors. Unfolds if there is an existing fold
1434
+ starting in that line, tries to find a foldable range around it
1435
+ otherwise.
1436
+ */
1437
+ const toggleFold = (view) => {
1438
+ let effects = [];
1439
+ for (let line of selectedLines(view)) {
1440
+ let folded = findFold(view.state, line.from, line.to);
1441
+ if (folded) {
1442
+ effects.push(unfoldEffect.of(folded), announceFold(view, folded, false));
1443
+ }
1444
+ else {
1445
+ let foldRange = foldableContainer(view, line);
1446
+ if (foldRange)
1447
+ effects.push(foldEffect.of(foldRange), announceFold(view, foldRange));
1448
+ }
1449
+ }
1450
+ if (effects.length > 0)
1451
+ view.dispatch({ effects: maybeEnable(view.state, effects) });
1452
+ return !!effects.length;
1453
+ };
1399
1454
  /**
1400
1455
  Default fold-related key bindings.
1401
1456
 
@@ -1919,10 +1974,11 @@ class StringStream {
1919
1974
  /**
1920
1975
  The current indent unit size.
1921
1976
  */
1922
- indentUnit) {
1977
+ indentUnit, overrideIndent) {
1923
1978
  this.string = string;
1924
1979
  this.tabSize = tabSize;
1925
1980
  this.indentUnit = indentUnit;
1981
+ this.overrideIndent = overrideIndent;
1926
1982
  /**
1927
1983
  The current position on the line.
1928
1984
  */
@@ -2023,7 +2079,8 @@ class StringStream {
2023
2079
  Get the indentation column of the current line.
2024
2080
  */
2025
2081
  indentation() {
2026
- return countCol(this.string, null, this.tabSize);
2082
+ var _a;
2083
+ return (_a = this.overrideIndent) !== null && _a !== void 0 ? _a : countCol(this.string, null, this.tabSize);
2027
2084
  }
2028
2085
  /**
2029
2086
  Match the input against the given string or regular expression
@@ -2085,6 +2142,7 @@ function defaultCopyState(state) {
2085
2142
  }
2086
2143
  return newState;
2087
2144
  }
2145
+ const IndentedFrom = /*@__PURE__*/new WeakMap();
2088
2146
  /**
2089
2147
  A [language](https://codemirror.net/6/docs/ref/#language.Language) class based on a CodeMirror
2090
2148
  5-style [streaming parser](https://codemirror.net/6/docs/ref/#language.StreamParser).
@@ -2115,7 +2173,14 @@ class StreamLanguage extends Language {
2115
2173
  at = at.parent;
2116
2174
  if (!at)
2117
2175
  return null;
2118
- let start = findState(this, tree, 0, at.from, pos), statePos, state;
2176
+ let from = undefined;
2177
+ let { overrideIndentation } = cx.options;
2178
+ if (overrideIndentation) {
2179
+ from = IndentedFrom.get(cx.state);
2180
+ if (from != null && from < pos - 1e4)
2181
+ from = undefined;
2182
+ }
2183
+ let start = findState(this, tree, 0, at.from, from !== null && from !== void 0 ? from : pos), statePos, state;
2119
2184
  if (start) {
2120
2185
  state = start.state;
2121
2186
  statePos = start.pos + 1;
@@ -2129,7 +2194,8 @@ class StreamLanguage extends Language {
2129
2194
  while (statePos < pos) {
2130
2195
  let line = cx.state.doc.lineAt(statePos), end = Math.min(pos, line.to);
2131
2196
  if (line.length) {
2132
- let stream = new StringStream(line.text, cx.state.tabSize, cx.unit);
2197
+ let indentation = overrideIndentation ? overrideIndentation(line.from) : -1;
2198
+ let stream = new StringStream(line.text, cx.state.tabSize, cx.unit, indentation < 0 ? undefined : indentation);
2133
2199
  while (stream.pos < end - line.from)
2134
2200
  readToken(this.streamParser.token, stream, state);
2135
2201
  }
@@ -2140,8 +2206,10 @@ class StreamLanguage extends Language {
2140
2206
  break;
2141
2207
  statePos = line.to + 1;
2142
2208
  }
2143
- let { text } = cx.lineAt(pos);
2144
- return this.streamParser.indent(state, /^\s*(.*)/.exec(text)[1], cx);
2209
+ let line = cx.lineAt(pos);
2210
+ if (overrideIndentation && from == null)
2211
+ IndentedFrom.set(cx.state, line.from);
2212
+ return this.streamParser.indent(state, /^\s*(.*)/.exec(line.text)[1], cx);
2145
2213
  }
2146
2214
  get allowsNesting() { return false; }
2147
2215
  }
@@ -2408,4 +2476,4 @@ function docID(data) {
2408
2476
  return type;
2409
2477
  }
2410
2478
 
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 };
2479
+ 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, sublanguageProp, 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.6.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",