@codemirror/language 0.19.8 → 0.20.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.js CHANGED
@@ -1,11 +1,13 @@
1
- import { NodeProp, Tree, TreeFragment, Parser, NodeType } from '@lezer/common';
2
- import { StateEffect, StateField, Facet, EditorState } from '@codemirror/state';
3
- import { ViewPlugin, logException } from '@codemirror/view';
4
- import { countColumn } from '@codemirror/text';
1
+ import { NodeProp, Tree, IterMode, TreeFragment, Parser, NodeType, NodeSet } from '@lezer/common';
2
+ import { StateEffect, StateField, Facet, EditorState, countColumn, combineConfig, RangeSet, RangeSetBuilder, Prec } from '@codemirror/state';
3
+ import { ViewPlugin, logException, Decoration, EditorView, WidgetType, gutter, GutterMarker } from '@codemirror/view';
4
+ import { tags, tagHighlighter, highlightTree, styleTags } from '@lezer/highlight';
5
+ import { StyleModule } from 'style-mod';
5
6
 
7
+ var _a;
6
8
  /**
7
- Node prop stored in a grammar's top syntax node to provide the
8
- facet that stores language data for that language.
9
+ Node prop stored in a parser's top syntax node to provide the
10
+ facet that stores language-specific data for that language.
9
11
  */
10
12
  const languageDataProp = /*@__PURE__*/new NodeProp();
11
13
  /**
@@ -24,31 +26,27 @@ function defineLanguageFacet(baseData) {
24
26
  /**
25
27
  A language object manages parsing and per-language
26
28
  [metadata](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt). Parse data is
27
- managed as a [Lezer](https://lezer.codemirror.net) tree. You'll
28
- want to subclass this class for custom parsers, or use the
29
- [`LRLanguage`](https://codemirror.net/6/docs/ref/#language.LRLanguage) or
30
- [`StreamLanguage`](https://codemirror.net/6/docs/ref/#stream-parser.StreamLanguage) abstractions for
31
- [Lezer](https://lezer.codemirror.net/) or stream parsers.
29
+ managed as a [Lezer](https://lezer.codemirror.net) tree. The class
30
+ can be used directly, via the [`LRLanguage`](https://codemirror.net/6/docs/ref/#language.LRLanguage)
31
+ subclass for [Lezer](https://lezer.codemirror.net/) LR parsers, or
32
+ via the [`StreamLanguage`](https://codemirror.net/6/docs/ref/#language.StreamLanguage) subclass
33
+ for stream parsers.
32
34
  */
33
35
  class Language {
34
36
  /**
35
- Construct a language object. You usually don't need to invoke
36
- this directly. But when you do, make sure you use
37
- [`defineLanguageFacet`](https://codemirror.net/6/docs/ref/#language.defineLanguageFacet) to create
38
- the first argument.
37
+ Construct a language object. If you need to invoke this
38
+ directly, first define a data facet with
39
+ [`defineLanguageFacet`](https://codemirror.net/6/docs/ref/#language.defineLanguageFacet), and then
40
+ configure your parser to [attach](https://codemirror.net/6/docs/ref/#language.languageDataProp) it
41
+ to the language's outer syntax node.
39
42
  */
40
43
  constructor(
41
44
  /**
42
- The [language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) data
43
- facet used for this language.
45
+ The [language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) facet
46
+ used for this language.
44
47
  */
45
- data, parser,
46
- /**
47
- The node type of the top node of trees produced by this parser.
48
- */
49
- topNode, extraExtensions = []) {
48
+ data, parser, extraExtensions = []) {
50
49
  this.data = data;
51
- this.topNode = topNode;
52
50
  // Kludge to define EditorState.tree as a debugging helper,
53
51
  // without the EditorState package actually knowing about
54
52
  // languages and lezer trees.
@@ -125,7 +123,7 @@ function languageDataFacetAt(state, pos, side) {
125
123
  return null;
126
124
  let facet = topLang.data;
127
125
  if (topLang.allowsNesting) {
128
- for (let node = syntaxTree(state).topNode; node; node = node.enter(pos, side, true, false))
126
+ for (let node = syntaxTree(state).topNode; node; node = node.enter(pos, side, IterMode.ExcludeBuffers))
129
127
  facet = node.type.prop(languageDataProp) || facet;
130
128
  }
131
129
  return facet;
@@ -137,7 +135,7 @@ parsers.
137
135
  */
138
136
  class LRLanguage extends Language {
139
137
  constructor(data, parser) {
140
- super(data, parser, parser.topNode);
138
+ super(data, parser);
141
139
  this.parser = parser;
142
140
  }
143
141
  /**
@@ -156,12 +154,13 @@ class LRLanguage extends Language {
156
154
  configure(options) {
157
155
  return new LRLanguage(this.data, this.parser.configure(options));
158
156
  }
159
- get allowsNesting() { return this.parser.wrappers.length > 0; } // FIXME
157
+ get allowsNesting() { return this.parser.hasWrappers(); }
160
158
  }
161
159
  /**
162
160
  Get the syntax tree for a state, which is the current (possibly
163
- incomplete) parse tree of active [language](https://codemirror.net/6/docs/ref/#language.Language),
164
- or the empty tree if there is no language available.
161
+ incomplete) parse tree of the active
162
+ [language](https://codemirror.net/6/docs/ref/#language.Language), or the empty tree if there is no
163
+ language available.
165
164
  */
166
165
  function syntaxTree(state) {
167
166
  let field = state.field(Language.state, false);
@@ -182,7 +181,7 @@ Queries whether there is a full syntax tree available up to the
182
181
  given document position. If there isn't, the background parse
183
182
  process _might_ still be working and update the tree further, but
184
183
  there is no guarantee of that—the parser will [stop
185
- working](https://codemirror.net/6/docs/ref/#language.syntaxParserStopped) when it has spent a
184
+ working](https://codemirror.net/6/docs/ref/#language.syntaxParserRunning) when it has spent a
186
185
  certain amount of time or has moved beyond the visible viewport.
187
186
  Always returns false if no language has been enabled.
188
187
  */
@@ -191,6 +190,18 @@ function syntaxTreeAvailable(state, upto = state.doc.length) {
191
190
  return ((_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context.isDone(upto)) || false;
192
191
  }
193
192
  /**
193
+ Move parsing forward, and update the editor state afterwards to
194
+ reflect the new tree. Will work for at most `timeout`
195
+ milliseconds. Returns true if the parser managed get to the given
196
+ position in that time.
197
+ */
198
+ function forceParsing(view, upto = view.viewport.to, timeout = 100) {
199
+ let success = ensureSyntaxTree(view.state, upto, timeout);
200
+ if (success != syntaxTree(view.state))
201
+ view.dispatch({});
202
+ return !!success;
203
+ }
204
+ /**
194
205
  Tells you whether the language parser is planning to do more
195
206
  parsing work (in a `requestIdleCallback` pseudo-thread) or has
196
207
  stopped running, either because it parsed the entire document,
@@ -248,7 +259,11 @@ class ParseContext {
248
259
  /**
249
260
  @internal
250
261
  */
251
- tree, treeLen,
262
+ tree,
263
+ /**
264
+ @internal
265
+ */
266
+ treeLen,
252
267
  /**
253
268
  The current editor viewport (or some overapproximation
254
269
  thereof). Intended to be used for opportunistically avoiding
@@ -288,7 +303,7 @@ class ParseContext {
288
303
  /**
289
304
  @internal
290
305
  */
291
- work(time, upto) {
306
+ work(until, upto) {
292
307
  if (upto != null && upto >= this.state.doc.length)
293
308
  upto = undefined;
294
309
  if (this.tree != Tree.empty && this.isDone(upto !== null && upto !== void 0 ? upto : this.state.doc.length)) {
@@ -297,7 +312,10 @@ class ParseContext {
297
312
  }
298
313
  return this.withContext(() => {
299
314
  var _a;
300
- let endTime = Date.now() + time;
315
+ if (typeof until == "number") {
316
+ let endTime = Date.now() + until;
317
+ until = () => Date.now() > endTime;
318
+ }
301
319
  if (!this.parse)
302
320
  this.parse = this.startParse();
303
321
  if (upto != null && (this.parse.stoppedAt == null || this.parse.stoppedAt > upto) &&
@@ -315,7 +333,7 @@ class ParseContext {
315
333
  else
316
334
  return true;
317
335
  }
318
- if (Date.now() > endTime)
336
+ if (until())
319
337
  return false;
320
338
  }
321
339
  });
@@ -470,7 +488,7 @@ class LanguageState {
470
488
  this.tree = context.tree;
471
489
  }
472
490
  apply(tr) {
473
- if (!tr.docChanged)
491
+ if (!tr.docChanged && this.tree == this.context.tree)
474
492
  return this;
475
493
  let newCx = this.context.changes(tr.changes, tr.state);
476
494
  // If the previous parse wasn't done, go forward only up to its
@@ -512,6 +530,8 @@ if (typeof requestIdleCallback != "undefined")
512
530
  }, 100 /* MinPause */);
513
531
  return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);
514
532
  };
533
+ const isInputPending = typeof navigator != "undefined" && ((_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending)
534
+ ? () => navigator.scheduling.isInputPending() : null;
515
535
  const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
516
536
  constructor(view) {
517
537
  this.view = view;
@@ -554,9 +574,11 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
554
574
  let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
555
575
  if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* MaxParseAhead */))
556
576
  return;
557
- let time = Math.min(this.chunkBudget, 100 /* Slice */, deadline ? Math.max(25 /* MinSlice */, deadline.timeRemaining() - 5) : 1e9);
577
+ let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Slice */, deadline && !isInputPending ? Math.max(25 /* MinSlice */, deadline.timeRemaining() - 5) : 1e9);
558
578
  let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1000;
559
- let done = field.context.work(time, vpTo + (viewportFirst ? 0 : 100000 /* MaxParseAhead */));
579
+ let done = field.context.work(() => {
580
+ return isInputPending && isInputPending() || Date.now() > endTime;
581
+ }, vpTo + (viewportFirst ? 0 : 100000 /* MaxParseAhead */));
560
582
  this.chunkBudget -= Date.now() - now;
561
583
  if (done || this.chunkBudget <= 0) {
562
584
  field.context.takeTree();
@@ -581,7 +603,7 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
581
603
  this.working();
582
604
  }
583
605
  isWorking() {
584
- return this.working || this.workScheduled > 0;
606
+ return !!(this.working || this.workScheduled > 0);
585
607
  }
586
608
  }, {
587
609
  eventHandlers: { focus() { this.scheduleWork(); } }
@@ -594,7 +616,7 @@ const language = /*@__PURE__*/Facet.define({
594
616
  enables: [Language.state, parseWorker]
595
617
  });
596
618
  /**
597
- This class bundles a [language object](https://codemirror.net/6/docs/ref/#language.Language) with an
619
+ This class bundles a [language](https://codemirror.net/6/docs/ref/#language.Language) with an
598
620
  optional set of supporting extensions. Language packages are
599
621
  encouraged to export a function that optionally takes a
600
622
  configuration object and returns a `LanguageSupport` instance, as
@@ -602,7 +624,7 @@ the main way for client code to use the package.
602
624
  */
603
625
  class LanguageSupport {
604
626
  /**
605
- Create a support object.
627
+ Create a language support object.
606
628
  */
607
629
  constructor(
608
630
  /**
@@ -915,7 +937,7 @@ function indentFrom(node, pos, base) {
915
937
  function topIndent() { return 0; }
916
938
  /**
917
939
  Objects of this type provide context information and helper
918
- methods to indentation functions.
940
+ methods to indentation functions registered on syntax nodes.
919
941
  */
920
942
  class TreeIndentContext extends IndentContext {
921
943
  /**
@@ -1055,7 +1077,7 @@ added at the start of a line.
1055
1077
  */
1056
1078
  function indentOnInput() {
1057
1079
  return EditorState.transactionFilter.of(tr => {
1058
- if (!tr.docChanged || !tr.isUserEvent("input.type"))
1080
+ if (!tr.docChanged || !tr.isUserEvent("input.type") && !tr.isUserEvent("input.complete"))
1059
1081
  return tr;
1060
1082
  let rules = tr.startState.languageDataAt("indentOnInput", tr.startState.selection.main.head);
1061
1083
  if (!rules.length)
@@ -1109,7 +1131,7 @@ function foldInside(node) {
1109
1131
  }
1110
1132
  function syntaxFolding(state, start, end) {
1111
1133
  let tree = syntaxTree(state);
1112
- if (tree.length == 0)
1134
+ if (tree.length < end)
1113
1135
  return null;
1114
1136
  let inner = tree.resolveInner(end);
1115
1137
  let found = null;
@@ -1119,7 +1141,7 @@ function syntaxFolding(state, start, end) {
1119
1141
  if (found && cur.from < start)
1120
1142
  break;
1121
1143
  let prop = cur.type.prop(foldNodeProp);
1122
- if (prop) {
1144
+ if (prop && (cur.to < tree.length - 50 || tree.length == state.doc.length || !isUnfinished(cur))) {
1123
1145
  let value = prop(cur, state);
1124
1146
  if (value && value.from <= end && value.from >= start && value.to > end)
1125
1147
  found = value;
@@ -1127,6 +1149,10 @@ function syntaxFolding(state, start, end) {
1127
1149
  }
1128
1150
  return found;
1129
1151
  }
1152
+ function isUnfinished(node) {
1153
+ let ch = node.lastChild;
1154
+ return ch && ch.to == node.to && ch.type.isError;
1155
+ }
1130
1156
  /**
1131
1157
  Check whether the given line is foldable. First asks any fold
1132
1158
  services registered through
@@ -1143,5 +1169,1133 @@ function foldable(state, lineStart, lineEnd) {
1143
1169
  }
1144
1170
  return syntaxFolding(state, lineStart, lineEnd);
1145
1171
  }
1172
+ function mapRange(range, mapping) {
1173
+ let from = mapping.mapPos(range.from, 1), to = mapping.mapPos(range.to, -1);
1174
+ return from >= to ? undefined : { from, to };
1175
+ }
1176
+ /**
1177
+ State effect that can be attached to a transaction to fold the
1178
+ given range. (You probably only need this in exceptional
1179
+ circumstances—usually you'll just want to let
1180
+ [`foldCode`](https://codemirror.net/6/docs/ref/#language.foldCode) and the [fold
1181
+ gutter](https://codemirror.net/6/docs/ref/#language.foldGutter) create the transactions.)
1182
+ */
1183
+ const foldEffect = /*@__PURE__*/StateEffect.define({ map: mapRange });
1184
+ /**
1185
+ State effect that unfolds the given range (if it was folded).
1186
+ */
1187
+ const unfoldEffect = /*@__PURE__*/StateEffect.define({ map: mapRange });
1188
+ function selectedLines(view) {
1189
+ let lines = [];
1190
+ for (let { head } of view.state.selection.ranges) {
1191
+ if (lines.some(l => l.from <= head && l.to >= head))
1192
+ continue;
1193
+ lines.push(view.lineBlockAt(head));
1194
+ }
1195
+ return lines;
1196
+ }
1197
+ const foldState = /*@__PURE__*/StateField.define({
1198
+ create() {
1199
+ return Decoration.none;
1200
+ },
1201
+ update(folded, tr) {
1202
+ folded = folded.map(tr.changes);
1203
+ for (let e of tr.effects) {
1204
+ if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to))
1205
+ folded = folded.update({ add: [foldWidget.range(e.value.from, e.value.to)] });
1206
+ else if (e.is(unfoldEffect))
1207
+ folded = folded.update({ filter: (from, to) => e.value.from != from || e.value.to != to,
1208
+ filterFrom: e.value.from, filterTo: e.value.to });
1209
+ }
1210
+ // Clear folded ranges that cover the selection head
1211
+ if (tr.selection) {
1212
+ let onSelection = false, { head } = tr.selection.main;
1213
+ folded.between(head, head, (a, b) => { if (a < head && b > head)
1214
+ onSelection = true; });
1215
+ if (onSelection)
1216
+ folded = folded.update({
1217
+ filterFrom: head,
1218
+ filterTo: head,
1219
+ filter: (a, b) => b <= head || a >= head
1220
+ });
1221
+ }
1222
+ return folded;
1223
+ },
1224
+ provide: f => EditorView.decorations.from(f)
1225
+ });
1226
+ /**
1227
+ Get a [range set](https://codemirror.net/6/docs/ref/#state.RangeSet) containing the folded ranges
1228
+ in the given state.
1229
+ */
1230
+ function foldedRanges(state) {
1231
+ return state.field(foldState, false) || RangeSet.empty;
1232
+ }
1233
+ function findFold(state, from, to) {
1234
+ var _a;
1235
+ let found = null;
1236
+ (_a = state.field(foldState, false)) === null || _a === void 0 ? void 0 : _a.between(from, to, (from, to) => {
1237
+ if (!found || found.from > from)
1238
+ found = { from, to };
1239
+ });
1240
+ return found;
1241
+ }
1242
+ function foldExists(folded, from, to) {
1243
+ let found = false;
1244
+ folded.between(from, from, (a, b) => { if (a == from && b == to)
1245
+ found = true; });
1246
+ return found;
1247
+ }
1248
+ function maybeEnable(state, other) {
1249
+ return state.field(foldState, false) ? other : other.concat(StateEffect.appendConfig.of(codeFolding()));
1250
+ }
1251
+ /**
1252
+ Fold the lines that are selected, if possible.
1253
+ */
1254
+ const foldCode = view => {
1255
+ for (let line of selectedLines(view)) {
1256
+ let range = foldable(view.state, line.from, line.to);
1257
+ if (range) {
1258
+ view.dispatch({ effects: maybeEnable(view.state, [foldEffect.of(range), announceFold(view, range)]) });
1259
+ return true;
1260
+ }
1261
+ }
1262
+ return false;
1263
+ };
1264
+ /**
1265
+ Unfold folded ranges on selected lines.
1266
+ */
1267
+ const unfoldCode = view => {
1268
+ if (!view.state.field(foldState, false))
1269
+ return false;
1270
+ let effects = [];
1271
+ for (let line of selectedLines(view)) {
1272
+ let folded = findFold(view.state, line.from, line.to);
1273
+ if (folded)
1274
+ effects.push(unfoldEffect.of(folded), announceFold(view, folded, false));
1275
+ }
1276
+ if (effects.length)
1277
+ view.dispatch({ effects });
1278
+ return effects.length > 0;
1279
+ };
1280
+ function announceFold(view, range, fold = true) {
1281
+ let lineFrom = view.state.doc.lineAt(range.from).number, lineTo = view.state.doc.lineAt(range.to).number;
1282
+ return EditorView.announce.of(`${view.state.phrase(fold ? "Folded lines" : "Unfolded lines")} ${lineFrom} ${view.state.phrase("to")} ${lineTo}.`);
1283
+ }
1284
+ /**
1285
+ Fold all top-level foldable ranges. Note that, in most cases,
1286
+ folding information will depend on the [syntax
1287
+ tree](https://codemirror.net/6/docs/ref/#language.syntaxTree), and folding everything may not work
1288
+ reliably when the document hasn't been fully parsed (either
1289
+ because the editor state was only just initialized, or because the
1290
+ document is so big that the parser decided not to parse it
1291
+ entirely).
1292
+ */
1293
+ const foldAll = view => {
1294
+ let { state } = view, effects = [];
1295
+ for (let pos = 0; pos < state.doc.length;) {
1296
+ let line = view.lineBlockAt(pos), range = foldable(state, line.from, line.to);
1297
+ if (range)
1298
+ effects.push(foldEffect.of(range));
1299
+ pos = (range ? view.lineBlockAt(range.to) : line).to + 1;
1300
+ }
1301
+ if (effects.length)
1302
+ view.dispatch({ effects: maybeEnable(view.state, effects) });
1303
+ return !!effects.length;
1304
+ };
1305
+ /**
1306
+ Unfold all folded code.
1307
+ */
1308
+ const unfoldAll = view => {
1309
+ let field = view.state.field(foldState, false);
1310
+ if (!field || !field.size)
1311
+ return false;
1312
+ let effects = [];
1313
+ field.between(0, view.state.doc.length, (from, to) => { effects.push(unfoldEffect.of({ from, to })); });
1314
+ view.dispatch({ effects });
1315
+ return true;
1316
+ };
1317
+ /**
1318
+ Default fold-related key bindings.
1319
+
1320
+ - Ctrl-Shift-[ (Cmd-Alt-[ on macOS): [`foldCode`](https://codemirror.net/6/docs/ref/#language.foldCode).
1321
+ - Ctrl-Shift-] (Cmd-Alt-] on macOS): [`unfoldCode`](https://codemirror.net/6/docs/ref/#language.unfoldCode).
1322
+ - Ctrl-Alt-[: [`foldAll`](https://codemirror.net/6/docs/ref/#language.foldAll).
1323
+ - Ctrl-Alt-]: [`unfoldAll`](https://codemirror.net/6/docs/ref/#language.unfoldAll).
1324
+ */
1325
+ const foldKeymap = [
1326
+ { key: "Ctrl-Shift-[", mac: "Cmd-Alt-[", run: foldCode },
1327
+ { key: "Ctrl-Shift-]", mac: "Cmd-Alt-]", run: unfoldCode },
1328
+ { key: "Ctrl-Alt-[", run: foldAll },
1329
+ { key: "Ctrl-Alt-]", run: unfoldAll }
1330
+ ];
1331
+ const defaultConfig = {
1332
+ placeholderDOM: null,
1333
+ placeholderText: "…"
1334
+ };
1335
+ const foldConfig = /*@__PURE__*/Facet.define({
1336
+ combine(values) { return combineConfig(values, defaultConfig); }
1337
+ });
1338
+ /**
1339
+ Create an extension that configures code folding.
1340
+ */
1341
+ function codeFolding(config) {
1342
+ let result = [foldState, baseTheme$1];
1343
+ if (config)
1344
+ result.push(foldConfig.of(config));
1345
+ return result;
1346
+ }
1347
+ const foldWidget = /*@__PURE__*/Decoration.replace({ widget: /*@__PURE__*/new class extends WidgetType {
1348
+ toDOM(view) {
1349
+ let { state } = view, conf = state.facet(foldConfig);
1350
+ let onclick = (event) => {
1351
+ let line = view.lineBlockAt(view.posAtDOM(event.target));
1352
+ let folded = findFold(view.state, line.from, line.to);
1353
+ if (folded)
1354
+ view.dispatch({ effects: unfoldEffect.of(folded) });
1355
+ event.preventDefault();
1356
+ };
1357
+ if (conf.placeholderDOM)
1358
+ return conf.placeholderDOM(view, onclick);
1359
+ let element = document.createElement("span");
1360
+ element.textContent = conf.placeholderText;
1361
+ element.setAttribute("aria-label", state.phrase("folded code"));
1362
+ element.title = state.phrase("unfold");
1363
+ element.className = "cm-foldPlaceholder";
1364
+ element.onclick = onclick;
1365
+ return element;
1366
+ }
1367
+ } });
1368
+ const foldGutterDefaults = {
1369
+ openText: "⌄",
1370
+ closedText: "›",
1371
+ markerDOM: null,
1372
+ domEventHandlers: {},
1373
+ };
1374
+ class FoldMarker extends GutterMarker {
1375
+ constructor(config, open) {
1376
+ super();
1377
+ this.config = config;
1378
+ this.open = open;
1379
+ }
1380
+ eq(other) { return this.config == other.config && this.open == other.open; }
1381
+ toDOM(view) {
1382
+ if (this.config.markerDOM)
1383
+ return this.config.markerDOM(this.open);
1384
+ let span = document.createElement("span");
1385
+ span.textContent = this.open ? this.config.openText : this.config.closedText;
1386
+ span.title = view.state.phrase(this.open ? "Fold line" : "Unfold line");
1387
+ return span;
1388
+ }
1389
+ }
1390
+ /**
1391
+ Create an extension that registers a fold gutter, which shows a
1392
+ fold status indicator before foldable lines (which can be clicked
1393
+ to fold or unfold the line).
1394
+ */
1395
+ function foldGutter(config = {}) {
1396
+ let fullConfig = Object.assign(Object.assign({}, foldGutterDefaults), config);
1397
+ let canFold = new FoldMarker(fullConfig, true), canUnfold = new FoldMarker(fullConfig, false);
1398
+ let markers = ViewPlugin.fromClass(class {
1399
+ constructor(view) {
1400
+ this.from = view.viewport.from;
1401
+ this.markers = this.buildMarkers(view);
1402
+ }
1403
+ update(update) {
1404
+ if (update.docChanged || update.viewportChanged ||
1405
+ update.startState.facet(language) != update.state.facet(language) ||
1406
+ update.startState.field(foldState, false) != update.state.field(foldState, false) ||
1407
+ syntaxTree(update.startState) != syntaxTree(update.state))
1408
+ this.markers = this.buildMarkers(update.view);
1409
+ }
1410
+ buildMarkers(view) {
1411
+ let builder = new RangeSetBuilder();
1412
+ for (let line of view.viewportLineBlocks) {
1413
+ let mark = findFold(view.state, line.from, line.to) ? canUnfold
1414
+ : foldable(view.state, line.from, line.to) ? canFold : null;
1415
+ if (mark)
1416
+ builder.add(line.from, line.from, mark);
1417
+ }
1418
+ return builder.finish();
1419
+ }
1420
+ });
1421
+ let { domEventHandlers } = fullConfig;
1422
+ return [
1423
+ markers,
1424
+ gutter({
1425
+ class: "cm-foldGutter",
1426
+ markers(view) { var _a; return ((_a = view.plugin(markers)) === null || _a === void 0 ? void 0 : _a.markers) || RangeSet.empty; },
1427
+ initialSpacer() {
1428
+ return new FoldMarker(fullConfig, false);
1429
+ },
1430
+ domEventHandlers: Object.assign(Object.assign({}, domEventHandlers), { click: (view, line, event) => {
1431
+ if (domEventHandlers.click && domEventHandlers.click(view, line, event))
1432
+ return true;
1433
+ let folded = findFold(view.state, line.from, line.to);
1434
+ if (folded) {
1435
+ view.dispatch({ effects: unfoldEffect.of(folded) });
1436
+ return true;
1437
+ }
1438
+ let range = foldable(view.state, line.from, line.to);
1439
+ if (range) {
1440
+ view.dispatch({ effects: foldEffect.of(range) });
1441
+ return true;
1442
+ }
1443
+ return false;
1444
+ } })
1445
+ }),
1446
+ codeFolding()
1447
+ ];
1448
+ }
1449
+ const baseTheme$1 = /*@__PURE__*/EditorView.baseTheme({
1450
+ ".cm-foldPlaceholder": {
1451
+ backgroundColor: "#eee",
1452
+ border: "1px solid #ddd",
1453
+ color: "#888",
1454
+ borderRadius: ".2em",
1455
+ margin: "0 1px",
1456
+ padding: "0 1px",
1457
+ cursor: "pointer"
1458
+ },
1459
+ ".cm-foldGutter span": {
1460
+ padding: "0 1px",
1461
+ cursor: "pointer"
1462
+ }
1463
+ });
1464
+
1465
+ /**
1466
+ A highlight style associates CSS styles with higlighting
1467
+ [tags](https://lezer.codemirror.net/docs/ref#highlight.Tag).
1468
+ */
1469
+ class HighlightStyle {
1470
+ constructor(spec, options) {
1471
+ let modSpec;
1472
+ function def(spec) {
1473
+ let cls = StyleModule.newName();
1474
+ (modSpec || (modSpec = Object.create(null)))["." + cls] = spec;
1475
+ return cls;
1476
+ }
1477
+ const scopeOpt = options.scope;
1478
+ this.scope = scopeOpt instanceof Language ? (type) => type.prop(languageDataProp) == scopeOpt.data
1479
+ : scopeOpt ? (type) => type == scopeOpt : undefined;
1480
+ this.style = tagHighlighter(spec.map(style => ({
1481
+ tag: style.tag,
1482
+ class: style.class || def(Object.assign({}, style, { tag: null }))
1483
+ })), {
1484
+ all: typeof options.all == "string" ? options.all : options.all ? def(options.all) : undefined,
1485
+ }).style;
1486
+ this.module = modSpec ? new StyleModule(modSpec) : null;
1487
+ this.themeType = options.themeType;
1488
+ }
1489
+ /**
1490
+ Create a highlighter style that associates the given styles to
1491
+ the given tags. The specs must be objects that hold a style tag
1492
+ or array of tags in their `tag` property, and either a single
1493
+ `class` property providing a static CSS class (for highlighter
1494
+ that rely on external styling), or a
1495
+ [`style-mod`](https://github.com/marijnh/style-mod#documentation)-style
1496
+ set of CSS properties (which define the styling for those tags).
1497
+
1498
+ The CSS rules created for a highlighter will be emitted in the
1499
+ order of the spec's properties. That means that for elements that
1500
+ have multiple tags associated with them, styles defined further
1501
+ down in the list will have a higher CSS precedence than styles
1502
+ defined earlier.
1503
+ */
1504
+ static define(specs, options) {
1505
+ return new HighlightStyle(specs, options || {});
1506
+ }
1507
+ }
1508
+ const highlighterFacet = /*@__PURE__*/Facet.define();
1509
+ const fallbackHighlighter = /*@__PURE__*/Facet.define({
1510
+ combine(values) { return values.length ? [values[0]] : null; }
1511
+ });
1512
+ function getHighlighters(state) {
1513
+ let main = state.facet(highlighterFacet);
1514
+ return main.length ? main : state.facet(fallbackHighlighter);
1515
+ }
1516
+ /**
1517
+ Wrap a highlighter in an editor extension that uses it to apply
1518
+ syntax highlighting to the editor content.
1519
+
1520
+ When multiple (non-fallback) styles are provided, the styling
1521
+ applied is the union of the classes they emit.
1522
+ */
1523
+ function syntaxHighlighting(highlighter, options) {
1524
+ let ext = [treeHighlighter], themeType;
1525
+ if (highlighter instanceof HighlightStyle) {
1526
+ if (highlighter.module)
1527
+ ext.push(EditorView.styleModule.of(highlighter.module));
1528
+ themeType = highlighter.themeType;
1529
+ }
1530
+ if (options === null || options === void 0 ? void 0 : options.fallback)
1531
+ ext.push(fallbackHighlighter.of(highlighter));
1532
+ else if (themeType)
1533
+ ext.push(highlighterFacet.computeN([EditorView.darkTheme], state => {
1534
+ return state.facet(EditorView.darkTheme) == (themeType == "dark") ? [highlighter] : [];
1535
+ }));
1536
+ else
1537
+ ext.push(highlighterFacet.of(highlighter));
1538
+ return ext;
1539
+ }
1540
+ /**
1541
+ Returns the CSS classes (if any) that the highlighters active in
1542
+ the state would assign to the given style
1543
+ [tags](https://lezer.codemirror.net/docs/ref#highlight.Tag) and
1544
+ (optional) language
1545
+ [scope](https://codemirror.net/6/docs/ref/#language.HighlightStyle^define^options.scope).
1546
+ */
1547
+ function highlightingFor(state, tags, scope) {
1548
+ let highlighters = getHighlighters(state);
1549
+ let result = null;
1550
+ if (highlighters)
1551
+ for (let highlighter of highlighters) {
1552
+ if (!highlighter.scope || scope && highlighter.scope(scope)) {
1553
+ let cls = highlighter.style(tags);
1554
+ if (cls)
1555
+ result = result ? result + " " + cls : cls;
1556
+ }
1557
+ }
1558
+ return result;
1559
+ }
1560
+ class TreeHighlighter {
1561
+ constructor(view) {
1562
+ this.markCache = Object.create(null);
1563
+ this.tree = syntaxTree(view.state);
1564
+ this.decorations = this.buildDeco(view, getHighlighters(view.state));
1565
+ }
1566
+ update(update) {
1567
+ let tree = syntaxTree(update.state), highlighters = getHighlighters(update.state);
1568
+ let styleChange = highlighters != getHighlighters(update.startState);
1569
+ if (tree.length < update.view.viewport.to && !styleChange && tree.type == this.tree.type) {
1570
+ this.decorations = this.decorations.map(update.changes);
1571
+ }
1572
+ else if (tree != this.tree || update.viewportChanged || styleChange) {
1573
+ this.tree = tree;
1574
+ this.decorations = this.buildDeco(update.view, highlighters);
1575
+ }
1576
+ }
1577
+ buildDeco(view, highlighters) {
1578
+ if (!highlighters || !this.tree.length)
1579
+ return Decoration.none;
1580
+ let builder = new RangeSetBuilder();
1581
+ for (let { from, to } of view.visibleRanges) {
1582
+ highlightTree(this.tree, highlighters, (from, to, style) => {
1583
+ builder.add(from, to, this.markCache[style] || (this.markCache[style] = Decoration.mark({ class: style })));
1584
+ }, from, to);
1585
+ }
1586
+ return builder.finish();
1587
+ }
1588
+ }
1589
+ const treeHighlighter = /*@__PURE__*/Prec.high(/*@__PURE__*/ViewPlugin.fromClass(TreeHighlighter, {
1590
+ decorations: v => v.decorations
1591
+ }));
1592
+ /**
1593
+ A default highlight style (works well with light themes).
1594
+ */
1595
+ const defaultHighlightStyle = /*@__PURE__*/HighlightStyle.define([
1596
+ { tag: tags.meta,
1597
+ color: "#7a757a" },
1598
+ { tag: tags.link,
1599
+ textDecoration: "underline" },
1600
+ { tag: tags.heading,
1601
+ textDecoration: "underline",
1602
+ fontWeight: "bold" },
1603
+ { tag: tags.emphasis,
1604
+ fontStyle: "italic" },
1605
+ { tag: tags.strong,
1606
+ fontWeight: "bold" },
1607
+ { tag: tags.strikethrough,
1608
+ textDecoration: "line-through" },
1609
+ { tag: tags.keyword,
1610
+ color: "#708" },
1611
+ { tag: [tags.atom, tags.bool, tags.url, tags.contentSeparator, tags.labelName],
1612
+ color: "#219" },
1613
+ { tag: [tags.literal, tags.inserted],
1614
+ color: "#164" },
1615
+ { tag: [tags.string, tags.deleted],
1616
+ color: "#a11" },
1617
+ { tag: [tags.regexp, tags.escape, /*@__PURE__*/tags.special(tags.string)],
1618
+ color: "#e40" },
1619
+ { tag: /*@__PURE__*/tags.definition(tags.variableName),
1620
+ color: "#00f" },
1621
+ { tag: /*@__PURE__*/tags.local(tags.variableName),
1622
+ color: "#30a" },
1623
+ { tag: [tags.typeName, tags.namespace],
1624
+ color: "#085" },
1625
+ { tag: tags.className,
1626
+ color: "#167" },
1627
+ { tag: [/*@__PURE__*/tags.special(tags.variableName), tags.macroName],
1628
+ color: "#256" },
1629
+ { tag: /*@__PURE__*/tags.definition(tags.propertyName),
1630
+ color: "#00c" },
1631
+ { tag: tags.comment,
1632
+ color: "#940" },
1633
+ { tag: tags.invalid,
1634
+ color: "#f00" }
1635
+ ]);
1636
+
1637
+ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
1638
+ "&.cm-focused .cm-matchingBracket": { backgroundColor: "#328c8252" },
1639
+ "&.cm-focused .cm-nonmatchingBracket": { backgroundColor: "#bb555544" }
1640
+ });
1641
+ const DefaultScanDist = 10000, DefaultBrackets = "()[]{}";
1642
+ const bracketMatchingConfig = /*@__PURE__*/Facet.define({
1643
+ combine(configs) {
1644
+ return combineConfig(configs, {
1645
+ afterCursor: true,
1646
+ brackets: DefaultBrackets,
1647
+ maxScanDistance: DefaultScanDist,
1648
+ renderMatch: defaultRenderMatch
1649
+ });
1650
+ }
1651
+ });
1652
+ const matchingMark = /*@__PURE__*/Decoration.mark({ class: "cm-matchingBracket" }), nonmatchingMark = /*@__PURE__*/Decoration.mark({ class: "cm-nonmatchingBracket" });
1653
+ function defaultRenderMatch(match) {
1654
+ let decorations = [];
1655
+ let mark = match.matched ? matchingMark : nonmatchingMark;
1656
+ decorations.push(mark.range(match.start.from, match.start.to));
1657
+ if (match.end)
1658
+ decorations.push(mark.range(match.end.from, match.end.to));
1659
+ return decorations;
1660
+ }
1661
+ const bracketMatchingState = /*@__PURE__*/StateField.define({
1662
+ create() { return Decoration.none; },
1663
+ update(deco, tr) {
1664
+ if (!tr.docChanged && !tr.selection)
1665
+ return deco;
1666
+ let decorations = [];
1667
+ let config = tr.state.facet(bracketMatchingConfig);
1668
+ for (let range of tr.state.selection.ranges) {
1669
+ if (!range.empty)
1670
+ continue;
1671
+ let match = matchBrackets(tr.state, range.head, -1, config)
1672
+ || (range.head > 0 && matchBrackets(tr.state, range.head - 1, 1, config))
1673
+ || (config.afterCursor &&
1674
+ (matchBrackets(tr.state, range.head, 1, config) ||
1675
+ (range.head < tr.state.doc.length && matchBrackets(tr.state, range.head + 1, -1, config))));
1676
+ if (match)
1677
+ decorations = decorations.concat(config.renderMatch(match, tr.state));
1678
+ }
1679
+ return Decoration.set(decorations, true);
1680
+ },
1681
+ provide: f => EditorView.decorations.from(f)
1682
+ });
1683
+ const bracketMatchingUnique = [
1684
+ bracketMatchingState,
1685
+ baseTheme
1686
+ ];
1687
+ /**
1688
+ Create an extension that enables bracket matching. Whenever the
1689
+ cursor is next to a bracket, that bracket and the one it matches
1690
+ are highlighted. Or, when no matching bracket is found, another
1691
+ highlighting style is used to indicate this.
1692
+ */
1693
+ function bracketMatching(config = {}) {
1694
+ return [bracketMatchingConfig.of(config), bracketMatchingUnique];
1695
+ }
1696
+ function matchingNodes(node, dir, brackets) {
1697
+ let byProp = node.prop(dir < 0 ? NodeProp.openedBy : NodeProp.closedBy);
1698
+ if (byProp)
1699
+ return byProp;
1700
+ if (node.name.length == 1) {
1701
+ let index = brackets.indexOf(node.name);
1702
+ if (index > -1 && index % 2 == (dir < 0 ? 1 : 0))
1703
+ return [brackets[index + dir]];
1704
+ }
1705
+ return null;
1706
+ }
1707
+ /**
1708
+ Find the matching bracket for the token at `pos`, scanning
1709
+ direction `dir`. Only the `brackets` and `maxScanDistance`
1710
+ properties are used from `config`, if given. Returns null if no
1711
+ bracket was found at `pos`, or a match result otherwise.
1712
+ */
1713
+ function matchBrackets(state, pos, dir, config = {}) {
1714
+ let maxScanDistance = config.maxScanDistance || DefaultScanDist, brackets = config.brackets || DefaultBrackets;
1715
+ let tree = syntaxTree(state), node = tree.resolveInner(pos, dir);
1716
+ for (let cur = node; cur; cur = cur.parent) {
1717
+ let matches = matchingNodes(cur.type, dir, brackets);
1718
+ if (matches && cur.from < cur.to)
1719
+ return matchMarkedBrackets(state, pos, dir, cur, matches, brackets);
1720
+ }
1721
+ return matchPlainBrackets(state, pos, dir, tree, node.type, maxScanDistance, brackets);
1722
+ }
1723
+ function matchMarkedBrackets(_state, _pos, dir, token, matching, brackets) {
1724
+ let parent = token.parent, firstToken = { from: token.from, to: token.to };
1725
+ let depth = 0, cursor = parent === null || parent === void 0 ? void 0 : parent.cursor();
1726
+ if (cursor && (dir < 0 ? cursor.childBefore(token.from) : cursor.childAfter(token.to)))
1727
+ do {
1728
+ if (dir < 0 ? cursor.to <= token.from : cursor.from >= token.to) {
1729
+ if (depth == 0 && matching.indexOf(cursor.type.name) > -1 && cursor.from < cursor.to) {
1730
+ return { start: firstToken, end: { from: cursor.from, to: cursor.to }, matched: true };
1731
+ }
1732
+ else if (matchingNodes(cursor.type, dir, brackets)) {
1733
+ depth++;
1734
+ }
1735
+ else if (matchingNodes(cursor.type, -dir, brackets)) {
1736
+ depth--;
1737
+ if (depth == 0)
1738
+ return {
1739
+ start: firstToken,
1740
+ end: cursor.from == cursor.to ? undefined : { from: cursor.from, to: cursor.to },
1741
+ matched: false
1742
+ };
1743
+ }
1744
+ }
1745
+ } while (dir < 0 ? cursor.prevSibling() : cursor.nextSibling());
1746
+ return { start: firstToken, matched: false };
1747
+ }
1748
+ function matchPlainBrackets(state, pos, dir, tree, tokenType, maxScanDistance, brackets) {
1749
+ let startCh = dir < 0 ? state.sliceDoc(pos - 1, pos) : state.sliceDoc(pos, pos + 1);
1750
+ let bracket = brackets.indexOf(startCh);
1751
+ if (bracket < 0 || (bracket % 2 == 0) != (dir > 0))
1752
+ return null;
1753
+ let startToken = { from: dir < 0 ? pos - 1 : pos, to: dir > 0 ? pos + 1 : pos };
1754
+ let iter = state.doc.iterRange(pos, dir > 0 ? state.doc.length : 0), depth = 0;
1755
+ for (let distance = 0; !(iter.next()).done && distance <= maxScanDistance;) {
1756
+ let text = iter.value;
1757
+ if (dir < 0)
1758
+ distance += text.length;
1759
+ let basePos = pos + distance * dir;
1760
+ for (let pos = dir > 0 ? 0 : text.length - 1, end = dir > 0 ? text.length : -1; pos != end; pos += dir) {
1761
+ let found = brackets.indexOf(text[pos]);
1762
+ if (found < 0 || tree.resolve(basePos + pos, 1).type != tokenType)
1763
+ continue;
1764
+ if ((found % 2 == 0) == (dir > 0)) {
1765
+ depth++;
1766
+ }
1767
+ else if (depth == 1) { // Closing
1768
+ return { start: startToken, end: { from: basePos + pos, to: basePos + pos + 1 }, matched: (found >> 1) == (bracket >> 1) };
1769
+ }
1770
+ else {
1771
+ depth--;
1772
+ }
1773
+ }
1774
+ if (dir > 0)
1775
+ distance += text.length;
1776
+ }
1777
+ return iter.done ? { start: startToken, matched: false } : null;
1778
+ }
1779
+
1780
+ // Counts the column offset in a string, taking tabs into account.
1781
+ // Used mostly to find indentation.
1782
+ function countCol(string, end, tabSize, startIndex = 0, startValue = 0) {
1783
+ if (end == null) {
1784
+ end = string.search(/[^\s\u00a0]/);
1785
+ if (end == -1)
1786
+ end = string.length;
1787
+ }
1788
+ let n = startValue;
1789
+ for (let i = startIndex; i < end; i++) {
1790
+ if (string.charCodeAt(i) == 9)
1791
+ n += tabSize - (n % tabSize);
1792
+ else
1793
+ n++;
1794
+ }
1795
+ return n;
1796
+ }
1797
+ /**
1798
+ Encapsulates a single line of input. Given to stream syntax code,
1799
+ which uses it to tokenize the content.
1800
+ */
1801
+ class StringStream {
1802
+ /**
1803
+ @internal
1804
+ */
1805
+ constructor(
1806
+ /**
1807
+ The line.
1808
+ */
1809
+ string, tabSize,
1810
+ /**
1811
+ The current indent unit size.
1812
+ */
1813
+ indentUnit) {
1814
+ this.string = string;
1815
+ this.tabSize = tabSize;
1816
+ this.indentUnit = indentUnit;
1817
+ /**
1818
+ The current position on the line.
1819
+ */
1820
+ this.pos = 0;
1821
+ /**
1822
+ The start position of the current token.
1823
+ */
1824
+ this.start = 0;
1825
+ this.lastColumnPos = 0;
1826
+ this.lastColumnValue = 0;
1827
+ }
1828
+ /**
1829
+ True if we are at the end of the line.
1830
+ */
1831
+ eol() { return this.pos >= this.string.length; }
1832
+ /**
1833
+ True if we are at the start of the line.
1834
+ */
1835
+ sol() { return this.pos == 0; }
1836
+ /**
1837
+ Get the next code unit after the current position, or undefined
1838
+ if we're at the end of the line.
1839
+ */
1840
+ peek() { return this.string.charAt(this.pos) || undefined; }
1841
+ /**
1842
+ Read the next code unit and advance `this.pos`.
1843
+ */
1844
+ next() {
1845
+ if (this.pos < this.string.length)
1846
+ return this.string.charAt(this.pos++);
1847
+ }
1848
+ /**
1849
+ Match the next character against the given string, regular
1850
+ expression, or predicate. Consume and return it if it matches.
1851
+ */
1852
+ eat(match) {
1853
+ let ch = this.string.charAt(this.pos);
1854
+ let ok;
1855
+ if (typeof match == "string")
1856
+ ok = ch == match;
1857
+ else
1858
+ ok = ch && (match instanceof RegExp ? match.test(ch) : match(ch));
1859
+ if (ok) {
1860
+ ++this.pos;
1861
+ return ch;
1862
+ }
1863
+ }
1864
+ /**
1865
+ Continue matching characters that match the given string,
1866
+ regular expression, or predicate function. Return true if any
1867
+ characters were consumed.
1868
+ */
1869
+ eatWhile(match) {
1870
+ let start = this.pos;
1871
+ while (this.eat(match)) { }
1872
+ return this.pos > start;
1873
+ }
1874
+ /**
1875
+ Consume whitespace ahead of `this.pos`. Return true if any was
1876
+ found.
1877
+ */
1878
+ eatSpace() {
1879
+ let start = this.pos;
1880
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos)))
1881
+ ++this.pos;
1882
+ return this.pos > start;
1883
+ }
1884
+ /**
1885
+ Move to the end of the line.
1886
+ */
1887
+ skipToEnd() { this.pos = this.string.length; }
1888
+ /**
1889
+ Move to directly before the given character, if found on the
1890
+ current line.
1891
+ */
1892
+ skipTo(ch) {
1893
+ let found = this.string.indexOf(ch, this.pos);
1894
+ if (found > -1) {
1895
+ this.pos = found;
1896
+ return true;
1897
+ }
1898
+ }
1899
+ /**
1900
+ Move back `n` characters.
1901
+ */
1902
+ backUp(n) { this.pos -= n; }
1903
+ /**
1904
+ Get the column position at `this.pos`.
1905
+ */
1906
+ column() {
1907
+ if (this.lastColumnPos < this.start) {
1908
+ this.lastColumnValue = countCol(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
1909
+ this.lastColumnPos = this.start;
1910
+ }
1911
+ return this.lastColumnValue;
1912
+ }
1913
+ /**
1914
+ Get the indentation column of the current line.
1915
+ */
1916
+ indentation() {
1917
+ return countCol(this.string, null, this.tabSize);
1918
+ }
1919
+ /**
1920
+ Match the input against the given string or regular expression
1921
+ (which should start with a `^`). Return true or the regexp match
1922
+ if it matches.
1923
+
1924
+ Unless `consume` is set to `false`, this will move `this.pos`
1925
+ past the matched text.
1926
+
1927
+ When matching a string `caseInsensitive` can be set to true to
1928
+ make the match case-insensitive.
1929
+ */
1930
+ match(pattern, consume, caseInsensitive) {
1931
+ if (typeof pattern == "string") {
1932
+ let cased = (str) => caseInsensitive ? str.toLowerCase() : str;
1933
+ let substr = this.string.substr(this.pos, pattern.length);
1934
+ if (cased(substr) == cased(pattern)) {
1935
+ if (consume !== false)
1936
+ this.pos += pattern.length;
1937
+ return true;
1938
+ }
1939
+ else
1940
+ return null;
1941
+ }
1942
+ else {
1943
+ let match = this.string.slice(this.pos).match(pattern);
1944
+ if (match && match.index > 0)
1945
+ return null;
1946
+ if (match && consume !== false)
1947
+ this.pos += match[0].length;
1948
+ return match;
1949
+ }
1950
+ }
1951
+ /**
1952
+ Get the current token.
1953
+ */
1954
+ current() { return this.string.slice(this.start, this.pos); }
1955
+ }
1956
+
1957
+ function fullParser(spec) {
1958
+ return {
1959
+ token: spec.token,
1960
+ blankLine: spec.blankLine || (() => { }),
1961
+ startState: spec.startState || (() => true),
1962
+ copyState: spec.copyState || defaultCopyState,
1963
+ indent: spec.indent || (() => null),
1964
+ languageData: spec.languageData || {},
1965
+ tokenTable: spec.tokenTable || noTokens
1966
+ };
1967
+ }
1968
+ function defaultCopyState(state) {
1969
+ if (typeof state != "object")
1970
+ return state;
1971
+ let newState = {};
1972
+ for (let prop in state) {
1973
+ let val = state[prop];
1974
+ newState[prop] = (val instanceof Array ? val.slice() : val);
1975
+ }
1976
+ return newState;
1977
+ }
1978
+ /**
1979
+ A [language](https://codemirror.net/6/docs/ref/#language.Language) class based on a CodeMirror
1980
+ 5-style [streaming parser](https://codemirror.net/6/docs/ref/#language.StreamParser).
1981
+ */
1982
+ class StreamLanguage extends Language {
1983
+ constructor(parser) {
1984
+ let data = defineLanguageFacet(parser.languageData);
1985
+ let p = fullParser(parser), self;
1986
+ let impl = new class extends Parser {
1987
+ createParse(input, fragments, ranges) {
1988
+ return new Parse(self, input, fragments, ranges);
1989
+ }
1990
+ };
1991
+ super(data, impl, [indentService.of((cx, pos) => this.getIndent(cx, pos))]);
1992
+ this.topNode = docID(data);
1993
+ self = this;
1994
+ this.streamParser = p;
1995
+ this.stateAfter = new NodeProp({ perNode: true });
1996
+ this.tokenTable = parser.tokenTable ? new TokenTable(p.tokenTable) : defaultTokenTable;
1997
+ }
1998
+ /**
1999
+ Define a stream language.
2000
+ */
2001
+ static define(spec) { return new StreamLanguage(spec); }
2002
+ getIndent(cx, pos) {
2003
+ let tree = syntaxTree(cx.state), at = tree.resolve(pos);
2004
+ while (at && at.type != this.topNode)
2005
+ at = at.parent;
2006
+ if (!at)
2007
+ return null;
2008
+ let start = findState(this, tree, 0, at.from, pos), statePos, state;
2009
+ if (start) {
2010
+ state = start.state;
2011
+ statePos = start.pos + 1;
2012
+ }
2013
+ else {
2014
+ state = this.streamParser.startState(cx.unit);
2015
+ statePos = 0;
2016
+ }
2017
+ if (pos - statePos > 10000 /* MaxIndentScanDist */)
2018
+ return null;
2019
+ while (statePos < pos) {
2020
+ let line = cx.state.doc.lineAt(statePos), end = Math.min(pos, line.to);
2021
+ if (line.length) {
2022
+ let stream = new StringStream(line.text, cx.state.tabSize, cx.unit);
2023
+ while (stream.pos < end - line.from)
2024
+ readToken(this.streamParser.token, stream, state);
2025
+ }
2026
+ else {
2027
+ this.streamParser.blankLine(state, cx.unit);
2028
+ }
2029
+ if (end == pos)
2030
+ break;
2031
+ statePos = line.to + 1;
2032
+ }
2033
+ let { text } = cx.lineAt(pos);
2034
+ return this.streamParser.indent(state, /^\s*(.*)/.exec(text)[1], cx);
2035
+ }
2036
+ get allowsNesting() { return false; }
2037
+ }
2038
+ function findState(lang, tree, off, startPos, before) {
2039
+ let state = off >= startPos && off + tree.length <= before && tree.prop(lang.stateAfter);
2040
+ if (state)
2041
+ return { state: lang.streamParser.copyState(state), pos: off + tree.length };
2042
+ for (let i = tree.children.length - 1; i >= 0; i--) {
2043
+ let child = tree.children[i], pos = off + tree.positions[i];
2044
+ let found = child instanceof Tree && pos < before && findState(lang, child, pos, startPos, before);
2045
+ if (found)
2046
+ return found;
2047
+ }
2048
+ return null;
2049
+ }
2050
+ function cutTree(lang, tree, from, to, inside) {
2051
+ if (inside && from <= 0 && to >= tree.length)
2052
+ return tree;
2053
+ if (!inside && tree.type == lang.topNode)
2054
+ inside = true;
2055
+ for (let i = tree.children.length - 1; i >= 0; i--) {
2056
+ let pos = tree.positions[i], child = tree.children[i], inner;
2057
+ if (pos < to && child instanceof Tree) {
2058
+ if (!(inner = cutTree(lang, child, from - pos, to - pos, inside)))
2059
+ break;
2060
+ return !inside ? inner
2061
+ : new Tree(tree.type, tree.children.slice(0, i).concat(inner), tree.positions.slice(0, i + 1), pos + inner.length);
2062
+ }
2063
+ }
2064
+ return null;
2065
+ }
2066
+ function findStartInFragments(lang, fragments, startPos, editorState) {
2067
+ for (let f of fragments) {
2068
+ let from = f.from + (f.openStart ? 25 : 0), to = f.to - (f.openEnd ? 25 : 0);
2069
+ let found = from <= startPos && to > startPos && findState(lang, f.tree, 0 - f.offset, startPos, to), tree;
2070
+ if (found && (tree = cutTree(lang, f.tree, startPos + f.offset, found.pos + f.offset, false)))
2071
+ return { state: found.state, tree };
2072
+ }
2073
+ return { state: lang.streamParser.startState(editorState ? getIndentUnit(editorState) : 4), tree: Tree.empty };
2074
+ }
2075
+ class Parse {
2076
+ constructor(lang, input, fragments, ranges) {
2077
+ this.lang = lang;
2078
+ this.input = input;
2079
+ this.fragments = fragments;
2080
+ this.ranges = ranges;
2081
+ this.stoppedAt = null;
2082
+ this.chunks = [];
2083
+ this.chunkPos = [];
2084
+ this.chunk = [];
2085
+ this.chunkReused = undefined;
2086
+ this.rangeIndex = 0;
2087
+ this.to = ranges[ranges.length - 1].to;
2088
+ let context = ParseContext.get(), from = ranges[0].from;
2089
+ let { state, tree } = findStartInFragments(lang, fragments, from, context === null || context === void 0 ? void 0 : context.state);
2090
+ this.state = state;
2091
+ this.parsedPos = this.chunkStart = from + tree.length;
2092
+ for (let i = 0; i < tree.children.length; i++) {
2093
+ this.chunks.push(tree.children[i]);
2094
+ this.chunkPos.push(tree.positions[i]);
2095
+ }
2096
+ if (context && this.parsedPos < context.viewport.from - 100000 /* MaxDistanceBeforeViewport */) {
2097
+ this.state = this.lang.streamParser.startState(getIndentUnit(context.state));
2098
+ context.skipUntilInView(this.parsedPos, context.viewport.from);
2099
+ this.parsedPos = context.viewport.from;
2100
+ }
2101
+ this.moveRangeIndex();
2102
+ }
2103
+ advance() {
2104
+ let context = ParseContext.get();
2105
+ let parseEnd = this.stoppedAt == null ? this.to : Math.min(this.to, this.stoppedAt);
2106
+ let end = Math.min(parseEnd, this.chunkStart + 2048 /* ChunkSize */);
2107
+ if (context)
2108
+ end = Math.min(end, context.viewport.to);
2109
+ while (this.parsedPos < end)
2110
+ this.parseLine(context);
2111
+ if (this.chunkStart < this.parsedPos)
2112
+ this.finishChunk();
2113
+ if (this.parsedPos >= parseEnd)
2114
+ return this.finish();
2115
+ if (context && this.parsedPos >= context.viewport.to) {
2116
+ context.skipUntilInView(this.parsedPos, parseEnd);
2117
+ return this.finish();
2118
+ }
2119
+ return null;
2120
+ }
2121
+ stopAt(pos) {
2122
+ this.stoppedAt = pos;
2123
+ }
2124
+ lineAfter(pos) {
2125
+ let chunk = this.input.chunk(pos);
2126
+ if (!this.input.lineChunks) {
2127
+ let eol = chunk.indexOf("\n");
2128
+ if (eol > -1)
2129
+ chunk = chunk.slice(0, eol);
2130
+ }
2131
+ else if (chunk == "\n") {
2132
+ chunk = "";
2133
+ }
2134
+ return pos + chunk.length <= this.to ? chunk : chunk.slice(0, this.to - pos);
2135
+ }
2136
+ nextLine() {
2137
+ let from = this.parsedPos, line = this.lineAfter(from), end = from + line.length;
2138
+ for (let index = this.rangeIndex;;) {
2139
+ let rangeEnd = this.ranges[index].to;
2140
+ if (rangeEnd >= end)
2141
+ break;
2142
+ line = line.slice(0, rangeEnd - (end - line.length));
2143
+ index++;
2144
+ if (index == this.ranges.length)
2145
+ break;
2146
+ let rangeStart = this.ranges[index].from;
2147
+ let after = this.lineAfter(rangeStart);
2148
+ line += after;
2149
+ end = rangeStart + after.length;
2150
+ }
2151
+ return { line, end };
2152
+ }
2153
+ skipGapsTo(pos, offset, side) {
2154
+ for (;;) {
2155
+ let end = this.ranges[this.rangeIndex].to, offPos = pos + offset;
2156
+ if (side > 0 ? end > offPos : end >= offPos)
2157
+ break;
2158
+ let start = this.ranges[++this.rangeIndex].from;
2159
+ offset += start - end;
2160
+ }
2161
+ return offset;
2162
+ }
2163
+ moveRangeIndex() {
2164
+ while (this.ranges[this.rangeIndex].to < this.parsedPos)
2165
+ this.rangeIndex++;
2166
+ }
2167
+ emitToken(id, from, to, size, offset) {
2168
+ if (this.ranges.length > 1) {
2169
+ offset = this.skipGapsTo(from, offset, 1);
2170
+ from += offset;
2171
+ let len0 = this.chunk.length;
2172
+ offset = this.skipGapsTo(to, offset, -1);
2173
+ to += offset;
2174
+ size += this.chunk.length - len0;
2175
+ }
2176
+ this.chunk.push(id, from, to, size);
2177
+ return offset;
2178
+ }
2179
+ parseLine(context) {
2180
+ let { line, end } = this.nextLine(), offset = 0, { streamParser } = this.lang;
2181
+ let stream = new StringStream(line, context ? context.state.tabSize : 4, context ? getIndentUnit(context.state) : 2);
2182
+ if (stream.eol()) {
2183
+ streamParser.blankLine(this.state, stream.indentUnit);
2184
+ }
2185
+ else {
2186
+ while (!stream.eol()) {
2187
+ let token = readToken(streamParser.token, stream, this.state);
2188
+ if (token)
2189
+ offset = this.emitToken(this.lang.tokenTable.resolve(token), this.parsedPos + stream.start, this.parsedPos + stream.pos, 4, offset);
2190
+ if (stream.start > 10000 /* MaxLineLength */)
2191
+ break;
2192
+ }
2193
+ }
2194
+ this.parsedPos = end;
2195
+ this.moveRangeIndex();
2196
+ if (this.parsedPos < this.to)
2197
+ this.parsedPos++;
2198
+ }
2199
+ finishChunk() {
2200
+ let tree = Tree.build({
2201
+ buffer: this.chunk,
2202
+ start: this.chunkStart,
2203
+ length: this.parsedPos - this.chunkStart,
2204
+ nodeSet,
2205
+ topID: 0,
2206
+ maxBufferLength: 2048 /* ChunkSize */,
2207
+ reused: this.chunkReused
2208
+ });
2209
+ tree = new Tree(tree.type, tree.children, tree.positions, tree.length, [[this.lang.stateAfter, this.lang.streamParser.copyState(this.state)]]);
2210
+ this.chunks.push(tree);
2211
+ this.chunkPos.push(this.chunkStart - this.ranges[0].from);
2212
+ this.chunk = [];
2213
+ this.chunkReused = undefined;
2214
+ this.chunkStart = this.parsedPos;
2215
+ }
2216
+ finish() {
2217
+ return new Tree(this.lang.topNode, this.chunks, this.chunkPos, this.parsedPos - this.ranges[0].from).balance();
2218
+ }
2219
+ }
2220
+ function readToken(token, stream, state) {
2221
+ stream.start = stream.pos;
2222
+ for (let i = 0; i < 10; i++) {
2223
+ let result = token(stream, state);
2224
+ if (stream.pos > stream.start)
2225
+ return result;
2226
+ }
2227
+ throw new Error("Stream parser failed to advance stream.");
2228
+ }
2229
+ const noTokens = /*@__PURE__*/Object.create(null);
2230
+ const typeArray = [NodeType.none];
2231
+ const nodeSet = /*@__PURE__*/new NodeSet(typeArray);
2232
+ const warned = [];
2233
+ const defaultTable = /*@__PURE__*/Object.create(null);
2234
+ for (let [legacyName, name] of [
2235
+ ["variable", "variableName"],
2236
+ ["variable-2", "variableName.special"],
2237
+ ["string-2", "string.special"],
2238
+ ["def", "variableName.definition"],
2239
+ ["tag", "typeName"],
2240
+ ["attribute", "propertyName"],
2241
+ ["type", "typeName"],
2242
+ ["builtin", "variableName.standard"],
2243
+ ["qualifier", "modifier"],
2244
+ ["error", "invalid"],
2245
+ ["header", "heading"],
2246
+ ["property", "propertyName"]
2247
+ ])
2248
+ defaultTable[legacyName] = /*@__PURE__*/createTokenType(noTokens, name);
2249
+ class TokenTable {
2250
+ constructor(extra) {
2251
+ this.extra = extra;
2252
+ this.table = Object.assign(Object.create(null), defaultTable);
2253
+ }
2254
+ resolve(tag) {
2255
+ return !tag ? 0 : this.table[tag] || (this.table[tag] = createTokenType(this.extra, tag));
2256
+ }
2257
+ }
2258
+ const defaultTokenTable = /*@__PURE__*/new TokenTable(noTokens);
2259
+ function warnForPart(part, msg) {
2260
+ if (warned.indexOf(part) > -1)
2261
+ return;
2262
+ warned.push(part);
2263
+ console.warn(msg);
2264
+ }
2265
+ function createTokenType(extra, tagStr) {
2266
+ let tag = null;
2267
+ for (let part of tagStr.split(".")) {
2268
+ let value = (extra[part] || tags[part]);
2269
+ if (!value) {
2270
+ warnForPart(part, `Unknown highlighting tag ${part}`);
2271
+ }
2272
+ else if (typeof value == "function") {
2273
+ if (!tag)
2274
+ warnForPart(part, `Modifier ${part} used at start of tag`);
2275
+ else
2276
+ tag = value(tag);
2277
+ }
2278
+ else {
2279
+ if (tag)
2280
+ warnForPart(part, `Tag ${part} used as modifier`);
2281
+ else
2282
+ tag = value;
2283
+ }
2284
+ }
2285
+ if (!tag)
2286
+ return 0;
2287
+ let name = tagStr.replace(/ /g, "_"), type = NodeType.define({
2288
+ id: typeArray.length,
2289
+ name,
2290
+ props: [styleTags({ [name]: tag })]
2291
+ });
2292
+ typeArray.push(type);
2293
+ return type.id;
2294
+ }
2295
+ function docID(data) {
2296
+ let type = NodeType.define({ id: typeArray.length, name: "Document", props: [languageDataProp.add(() => data)] });
2297
+ typeArray.push(type);
2298
+ return type;
2299
+ }
1146
2300
 
1147
- export { IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, TreeIndentContext, continuedIndent, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldInside, foldNodeProp, foldService, foldable, getIndentUnit, getIndentation, indentNodeProp, indentOnInput, indentService, indentString, indentUnit, language, languageDataProp, syntaxParserRunning, syntaxTree, syntaxTreeAvailable };
2301
+ export { HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, StreamLanguage, StringStream, TreeIndentContext, bracketMatching, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, unfoldAll, unfoldCode, unfoldEffect };