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