@codemirror/language 6.9.0 → 6.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## 6.9.2 (2023-10-24)
2
+
3
+ ### Bug fixes
4
+
5
+ Allow `StreamParser` tokens get multiple highlighting tags.
6
+
7
+ ## 6.9.1 (2023-09-20)
8
+
9
+ ### Bug fixes
10
+
11
+ Indentation now works a lot better in mixed-language documents that interleave the languages in a complex way.
12
+
13
+ Code folding is now able to pick the right foldable syntax node when the line end falls in a mixed-parsing language that doesn't match the target node.
14
+
1
15
  ## 6.9.0 (2023-08-16)
2
16
 
3
17
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var common = require('@lezer/common');
6
4
  var state = require('@codemirror/state');
7
5
  var view = require('@codemirror/view');
@@ -538,14 +536,14 @@ class LanguageState {
538
536
  // state updates with parse work beyond the viewport.
539
537
  let upto = this.context.treeLen == tr.startState.doc.length ? undefined
540
538
  : Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);
541
- if (!newCx.work(20 /* Apply */, upto))
539
+ if (!newCx.work(20 /* Work.Apply */, upto))
542
540
  newCx.takeTree();
543
541
  return new LanguageState(newCx);
544
542
  }
545
543
  static init(state) {
546
- let vpTo = Math.min(3000 /* InitViewport */, state.doc.length);
544
+ let vpTo = Math.min(3000 /* Work.InitViewport */, state.doc.length);
547
545
  let parseState = ParseContext.create(state.facet(language).parser, state, { from: 0, to: vpTo });
548
- if (!parseState.work(20 /* Apply */, vpTo))
546
+ if (!parseState.work(20 /* Work.Apply */, vpTo))
549
547
  parseState.takeTree();
550
548
  return new LanguageState(parseState);
551
549
  }
@@ -562,14 +560,14 @@ Language.state = state.StateField.define({
562
560
  }
563
561
  });
564
562
  let requestIdle = (callback) => {
565
- let timeout = setTimeout(() => callback(), 500 /* MaxPause */);
563
+ let timeout = setTimeout(() => callback(), 500 /* Work.MaxPause */);
566
564
  return () => clearTimeout(timeout);
567
565
  };
568
566
  if (typeof requestIdleCallback != "undefined")
569
567
  requestIdle = (callback) => {
570
568
  let idle = -1, timeout = setTimeout(() => {
571
- idle = requestIdleCallback(callback, { timeout: 500 /* MaxPause */ - 100 /* MinPause */ });
572
- }, 100 /* MinPause */);
569
+ idle = requestIdleCallback(callback, { timeout: 500 /* Work.MaxPause */ - 100 /* Work.MinPause */ });
570
+ }, 100 /* Work.MinPause */);
573
571
  return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);
574
572
  };
575
573
  const isInputPending = typeof navigator != "undefined" && ((_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending)
@@ -590,9 +588,9 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
590
588
  let cx = this.view.state.field(Language.state).context;
591
589
  if (cx.updateViewport(update.view.viewport) || this.view.viewport.to > cx.treeLen)
592
590
  this.scheduleWork();
593
- if (update.docChanged) {
591
+ if (update.docChanged || update.selectionSet) {
594
592
  if (this.view.hasFocus)
595
- this.chunkBudget += 50 /* ChangeBonus */;
593
+ this.chunkBudget += 50 /* Work.ChangeBonus */;
596
594
  this.scheduleWork();
597
595
  }
598
596
  this.checkAsyncSchedule(cx);
@@ -608,19 +606,19 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
608
606
  this.working = null;
609
607
  let now = Date.now();
610
608
  if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk
611
- this.chunkEnd = now + 30000 /* ChunkTime */;
612
- this.chunkBudget = 3000 /* ChunkBudget */;
609
+ this.chunkEnd = now + 30000 /* Work.ChunkTime */;
610
+ this.chunkBudget = 3000 /* Work.ChunkBudget */;
613
611
  }
614
612
  if (this.chunkBudget <= 0)
615
613
  return; // No more budget
616
614
  let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
617
- if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* MaxParseAhead */))
615
+ if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* Work.MaxParseAhead */))
618
616
  return;
619
- let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Slice */, deadline && !isInputPending ? Math.max(25 /* MinSlice */, deadline.timeRemaining() - 5) : 1e9);
617
+ let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Work.Slice */, deadline && !isInputPending ? Math.max(25 /* Work.MinSlice */, deadline.timeRemaining() - 5) : 1e9);
620
618
  let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1000;
621
619
  let done = field.context.work(() => {
622
620
  return isInputPending && isInputPending() || Date.now() > endTime;
623
- }, vpTo + (viewportFirst ? 0 : 100000 /* MaxParseAhead */));
621
+ }, vpTo + (viewportFirst ? 0 : 100000 /* Work.MaxParseAhead */));
624
622
  this.chunkBudget -= Date.now() - now;
625
623
  if (done || this.chunkBudget <= 0) {
626
624
  field.context.takeTree();
@@ -996,7 +994,24 @@ indicates that no definitive indentation can be determined.
996
994
  const indentNodeProp = new common.NodeProp();
997
995
  // Compute the indentation for a given position from the syntax tree.
998
996
  function syntaxIndentation(cx, ast, pos) {
999
- return indentFrom(ast.resolveInner(pos).enterUnfinishedNodesBefore(pos), pos, cx);
997
+ let stack = ast.resolveStack(pos);
998
+ let inner = stack.node.enterUnfinishedNodesBefore(pos);
999
+ if (inner != stack.node) {
1000
+ let add = [];
1001
+ for (let cur = inner; cur != stack.node; cur = cur.parent)
1002
+ add.push(cur);
1003
+ for (let i = add.length - 1; i >= 0; i--)
1004
+ stack = { node: add[i], next: stack };
1005
+ }
1006
+ return indentFor(stack, cx, pos);
1007
+ }
1008
+ function indentFor(stack, cx, pos) {
1009
+ for (let cur = stack; cur; cur = cur.next) {
1010
+ let strategy = indentStrategy(cur.node);
1011
+ if (strategy)
1012
+ return strategy(TreeIndentContext.create(cx, pos, cur));
1013
+ }
1014
+ return 0;
1000
1015
  }
1001
1016
  function ignoreClosed(cx) {
1002
1017
  return cx.pos == cx.options.simulateBreak && cx.options.simulateDoubleBreak;
@@ -1012,14 +1027,6 @@ function indentStrategy(tree) {
1012
1027
  }
1013
1028
  return tree.parent == null ? topIndent : null;
1014
1029
  }
1015
- function indentFrom(node, pos, base) {
1016
- for (; node; node = node.parent) {
1017
- let strategy = indentStrategy(node);
1018
- if (strategy)
1019
- return strategy(TreeIndentContext.create(base, pos, node));
1020
- }
1021
- return null;
1022
- }
1023
1030
  function topIndent() { return 0; }
1024
1031
  /**
1025
1032
  Objects of this type provide context information and helper
@@ -1032,20 +1039,24 @@ class TreeIndentContext extends IndentContext {
1032
1039
  */
1033
1040
  pos,
1034
1041
  /**
1035
- The syntax tree node to which the indentation strategy
1036
- applies.
1042
+ @internal
1037
1043
  */
1038
- node) {
1044
+ context) {
1039
1045
  super(base.state, base.options);
1040
1046
  this.base = base;
1041
1047
  this.pos = pos;
1042
- this.node = node;
1048
+ this.context = context;
1043
1049
  }
1044
1050
  /**
1051
+ The syntax tree node to which the indentation strategy
1052
+ applies.
1053
+ */
1054
+ get node() { return this.context.node; }
1055
+ /**
1045
1056
  @internal
1046
1057
  */
1047
- static create(base, pos, node) {
1048
- return new TreeIndentContext(base, pos, node);
1058
+ static create(base, pos, context) {
1059
+ return new TreeIndentContext(base, pos, context);
1049
1060
  }
1050
1061
  /**
1051
1062
  Get the text directly after `this.pos`, either the entire line
@@ -1086,8 +1097,7 @@ class TreeIndentContext extends IndentContext {
1086
1097
  and return the result of that.
1087
1098
  */
1088
1099
  continue() {
1089
- let parent = this.node.parent;
1090
- return parent ? indentFrom(parent, this.pos, this.base) : 0;
1100
+ return indentFor(this.context.next, this.base, this.pos);
1091
1101
  }
1092
1102
  }
1093
1103
  function isParent(parent, of) {
@@ -1229,9 +1239,10 @@ function syntaxFolding(state, start, end) {
1229
1239
  let tree = syntaxTree(state);
1230
1240
  if (tree.length < end)
1231
1241
  return null;
1232
- let inner = tree.resolveInner(end, 1);
1242
+ let stack = tree.resolveStack(end, 1);
1233
1243
  let found = null;
1234
- for (let cur = inner; cur; cur = cur.parent) {
1244
+ for (let iter = stack; iter; iter = iter.next) {
1245
+ let cur = iter.node;
1235
1246
  if (cur.to <= end || cur.from > end)
1236
1247
  continue;
1237
1248
  if (found && cur.from < start)
@@ -2222,7 +2233,7 @@ class StreamLanguage extends Language {
2222
2233
  state = this.streamParser.startState(cx.unit);
2223
2234
  statePos = 0;
2224
2235
  }
2225
- if (pos - statePos > 10000 /* MaxIndentScanDist */)
2236
+ if (pos - statePos > 10000 /* C.MaxIndentScanDist */)
2226
2237
  return null;
2227
2238
  while (statePos < pos) {
2228
2239
  let line = cx.state.doc.lineAt(statePos), end = Math.min(pos, line.to);
@@ -2304,7 +2315,7 @@ class Parse {
2304
2315
  this.chunks.push(tree.children[i]);
2305
2316
  this.chunkPos.push(tree.positions[i]);
2306
2317
  }
2307
- if (context && this.parsedPos < context.viewport.from - 100000 /* MaxDistanceBeforeViewport */) {
2318
+ if (context && this.parsedPos < context.viewport.from - 100000 /* C.MaxDistanceBeforeViewport */) {
2308
2319
  this.state = this.lang.streamParser.startState(getIndentUnit(context.state));
2309
2320
  context.skipUntilInView(this.parsedPos, context.viewport.from);
2310
2321
  this.parsedPos = context.viewport.from;
@@ -2314,7 +2325,7 @@ class Parse {
2314
2325
  advance() {
2315
2326
  let context = ParseContext.get();
2316
2327
  let parseEnd = this.stoppedAt == null ? this.to : Math.min(this.to, this.stoppedAt);
2317
- let end = Math.min(parseEnd, this.chunkStart + 2048 /* ChunkSize */);
2328
+ let end = Math.min(parseEnd, this.chunkStart + 2048 /* C.ChunkSize */);
2318
2329
  if (context)
2319
2330
  end = Math.min(end, context.viewport.to);
2320
2331
  while (this.parsedPos < end)
@@ -2398,7 +2409,7 @@ class Parse {
2398
2409
  let token = readToken(streamParser.token, stream, this.state);
2399
2410
  if (token)
2400
2411
  offset = this.emitToken(this.lang.tokenTable.resolve(token), this.parsedPos + stream.start, this.parsedPos + stream.pos, 4, offset);
2401
- if (stream.start > 10000 /* MaxLineLength */)
2412
+ if (stream.start > 10000 /* C.MaxLineLength */)
2402
2413
  break;
2403
2414
  }
2404
2415
  }
@@ -2414,7 +2425,7 @@ class Parse {
2414
2425
  length: this.parsedPos - this.chunkStart,
2415
2426
  nodeSet,
2416
2427
  topID: 0,
2417
- maxBufferLength: 2048 /* ChunkSize */,
2428
+ maxBufferLength: 2048 /* C.ChunkSize */,
2418
2429
  reused: this.chunkReused
2419
2430
  });
2420
2431
  tree = new common.Tree(tree.type, tree.children, tree.positions, tree.length, [[this.lang.stateAfter, this.lang.streamParser.copyState(this.state)]]);
@@ -2474,31 +2485,36 @@ function warnForPart(part, msg) {
2474
2485
  console.warn(msg);
2475
2486
  }
2476
2487
  function createTokenType(extra, tagStr) {
2477
- let tag = null;
2478
- for (let part of tagStr.split(".")) {
2479
- let value = (extra[part] || highlight.tags[part]);
2480
- if (!value) {
2481
- warnForPart(part, `Unknown highlighting tag ${part}`);
2482
- }
2483
- else if (typeof value == "function") {
2484
- if (!tag)
2485
- warnForPart(part, `Modifier ${part} used at start of tag`);
2486
- else
2487
- tag = value(tag);
2488
- }
2489
- else {
2490
- if (tag)
2491
- warnForPart(part, `Tag ${part} used as modifier`);
2492
- else
2493
- tag = value;
2488
+ let tags = [];
2489
+ for (let name of tagStr.split(" ")) {
2490
+ let found = [];
2491
+ for (let part of name.split(".")) {
2492
+ let value = (extra[part] || highlight.tags[part]);
2493
+ if (!value) {
2494
+ warnForPart(part, `Unknown highlighting tag ${part}`);
2495
+ }
2496
+ else if (typeof value == "function") {
2497
+ if (!found.length)
2498
+ warnForPart(part, `Modifier ${part} used at start of tag`);
2499
+ else
2500
+ found = found.map(value);
2501
+ }
2502
+ else {
2503
+ if (found.length)
2504
+ warnForPart(part, `Tag ${part} used as modifier`);
2505
+ else
2506
+ found = Array.isArray(value) ? value : [value];
2507
+ }
2494
2508
  }
2509
+ for (let tag of found)
2510
+ tags.push(tag);
2495
2511
  }
2496
- if (!tag)
2512
+ if (!tags.length)
2497
2513
  return 0;
2498
2514
  let name = tagStr.replace(/ /g, "_"), type = common.NodeType.define({
2499
2515
  id: typeArray.length,
2500
2516
  name,
2501
- props: [highlight.styleTags({ [name]: tag })]
2517
+ props: [highlight.styleTags({ [name]: tags })]
2502
2518
  });
2503
2519
  typeArray.push(type);
2504
2520
  return type.id;
package/dist/index.d.cts CHANGED
@@ -567,12 +567,12 @@ declare class TreeIndentContext extends IndentContext {
567
567
  The position at which indentation is being computed.
568
568
  */
569
569
  readonly pos: number;
570
+ private constructor();
570
571
  /**
571
572
  The syntax tree node to which the indentation strategy
572
573
  applies.
573
574
  */
574
- readonly node: SyntaxNode;
575
- private constructor();
575
+ get node(): SyntaxNode;
576
576
  /**
577
577
  Get the text directly after `this.pos`, either the entire line
578
578
  or the next 100 characters, whichever is shorter.
@@ -689,7 +689,7 @@ declare function foldable(state: EditorState, lineStart: number, lineEnd: number
689
689
  from: number;
690
690
  to: number;
691
691
  } | null;
692
- declare type DocRange = {
692
+ type DocRange = {
693
693
  from: number;
694
694
  to: number;
695
695
  };
@@ -790,7 +790,7 @@ interface FoldConfig {
790
790
  Create an extension that configures code folding.
791
791
  */
792
792
  declare function codeFolding(config?: FoldConfig): Extension;
793
- declare type Handlers = {
793
+ type Handlers = {
794
794
  [event: string]: (view: EditorView, line: BlockInfo, event: Event) => boolean;
795
795
  };
796
796
  interface FoldGutterConfig {
@@ -1139,11 +1139,13 @@ interface StreamParser<State> {
1139
1139
  Read one token, advancing the stream past it, and returning a
1140
1140
  string indicating the token's style tag—either the name of one
1141
1141
  of the tags in
1142
- [`tags`](https://lezer.codemirror.net/docs/ref#highlight.tags),
1143
- or such a name suffixed by one or more tag
1142
+ [`tags`](https://lezer.codemirror.net/docs/ref#highlight.tags)
1143
+ or [`tokenTable`](https://codemirror.net/6/docs/ref/#language.StreamParser.tokenTable), or such a
1144
+ name suffixed by one or more tag
1144
1145
  [modifier](https://lezer.codemirror.net/docs/ref#highlight.Tag^defineModifier)
1145
1146
  names, separated by periods. For example `"keyword"` or
1146
- "`variableName.constant"`.
1147
+ "`variableName.constant"`, or a space-separated set of such
1148
+ token types.
1147
1149
 
1148
1150
  It is okay to return a zero-length token, but only if that
1149
1151
  updates the state so that the next call will return a non-empty
@@ -1175,10 +1177,10 @@ interface StreamParser<State> {
1175
1177
  /**
1176
1178
  Extra tokens to use in this parser. When the tokenizer returns a
1177
1179
  token name that exists as a property in this object, the
1178
- corresponding tag will be assigned to the token.
1180
+ corresponding tags will be assigned to the token.
1179
1181
  */
1180
1182
  tokenTable?: {
1181
- [name: string]: Tag;
1183
+ [name: string]: Tag | readonly Tag[];
1182
1184
  };
1183
1185
  }
1184
1186
  /**
package/dist/index.d.ts CHANGED
@@ -567,12 +567,12 @@ declare class TreeIndentContext extends IndentContext {
567
567
  The position at which indentation is being computed.
568
568
  */
569
569
  readonly pos: number;
570
+ private constructor();
570
571
  /**
571
572
  The syntax tree node to which the indentation strategy
572
573
  applies.
573
574
  */
574
- readonly node: SyntaxNode;
575
- private constructor();
575
+ get node(): SyntaxNode;
576
576
  /**
577
577
  Get the text directly after `this.pos`, either the entire line
578
578
  or the next 100 characters, whichever is shorter.
@@ -689,7 +689,7 @@ declare function foldable(state: EditorState, lineStart: number, lineEnd: number
689
689
  from: number;
690
690
  to: number;
691
691
  } | null;
692
- declare type DocRange = {
692
+ type DocRange = {
693
693
  from: number;
694
694
  to: number;
695
695
  };
@@ -790,7 +790,7 @@ interface FoldConfig {
790
790
  Create an extension that configures code folding.
791
791
  */
792
792
  declare function codeFolding(config?: FoldConfig): Extension;
793
- declare type Handlers = {
793
+ type Handlers = {
794
794
  [event: string]: (view: EditorView, line: BlockInfo, event: Event) => boolean;
795
795
  };
796
796
  interface FoldGutterConfig {
@@ -1139,11 +1139,13 @@ interface StreamParser<State> {
1139
1139
  Read one token, advancing the stream past it, and returning a
1140
1140
  string indicating the token's style tag—either the name of one
1141
1141
  of the tags in
1142
- [`tags`](https://lezer.codemirror.net/docs/ref#highlight.tags),
1143
- or such a name suffixed by one or more tag
1142
+ [`tags`](https://lezer.codemirror.net/docs/ref#highlight.tags)
1143
+ or [`tokenTable`](https://codemirror.net/6/docs/ref/#language.StreamParser.tokenTable), or such a
1144
+ name suffixed by one or more tag
1144
1145
  [modifier](https://lezer.codemirror.net/docs/ref#highlight.Tag^defineModifier)
1145
1146
  names, separated by periods. For example `"keyword"` or
1146
- "`variableName.constant"`.
1147
+ "`variableName.constant"`, or a space-separated set of such
1148
+ token types.
1147
1149
 
1148
1150
  It is okay to return a zero-length token, but only if that
1149
1151
  updates the state so that the next call will return a non-empty
@@ -1175,10 +1177,10 @@ interface StreamParser<State> {
1175
1177
  /**
1176
1178
  Extra tokens to use in this parser. When the tokenizer returns a
1177
1179
  token name that exists as a property in this object, the
1178
- corresponding tag will be assigned to the token.
1180
+ corresponding tags will be assigned to the token.
1179
1181
  */
1180
1182
  tokenTable?: {
1181
- [name: string]: Tag;
1183
+ [name: string]: Tag | readonly Tag[];
1182
1184
  };
1183
1185
  }
1184
1186
  /**
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { NodeProp, Tree, IterMode, TreeFragment, Parser, NodeType, NodeSet } from '@lezer/common';
1
+ import { NodeProp, IterMode, Tree, TreeFragment, Parser, NodeType, NodeSet } from '@lezer/common';
2
2
  import { StateEffect, StateField, Facet, EditorState, countColumn, combineConfig, RangeSet, RangeSetBuilder, Prec } from '@codemirror/state';
3
3
  import { ViewPlugin, logException, EditorView, Decoration, WidgetType, gutter, GutterMarker } from '@codemirror/view';
4
4
  import { tags, tagHighlighter, highlightTree, styleTags } from '@lezer/highlight';
@@ -534,14 +534,14 @@ class LanguageState {
534
534
  // state updates with parse work beyond the viewport.
535
535
  let upto = this.context.treeLen == tr.startState.doc.length ? undefined
536
536
  : Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);
537
- if (!newCx.work(20 /* Apply */, upto))
537
+ if (!newCx.work(20 /* Work.Apply */, upto))
538
538
  newCx.takeTree();
539
539
  return new LanguageState(newCx);
540
540
  }
541
541
  static init(state) {
542
- let vpTo = Math.min(3000 /* InitViewport */, state.doc.length);
542
+ let vpTo = Math.min(3000 /* Work.InitViewport */, state.doc.length);
543
543
  let parseState = ParseContext.create(state.facet(language).parser, state, { from: 0, to: vpTo });
544
- if (!parseState.work(20 /* Apply */, vpTo))
544
+ if (!parseState.work(20 /* Work.Apply */, vpTo))
545
545
  parseState.takeTree();
546
546
  return new LanguageState(parseState);
547
547
  }
@@ -558,14 +558,14 @@ Language.state = /*@__PURE__*/StateField.define({
558
558
  }
559
559
  });
560
560
  let requestIdle = (callback) => {
561
- let timeout = setTimeout(() => callback(), 500 /* MaxPause */);
561
+ let timeout = setTimeout(() => callback(), 500 /* Work.MaxPause */);
562
562
  return () => clearTimeout(timeout);
563
563
  };
564
564
  if (typeof requestIdleCallback != "undefined")
565
565
  requestIdle = (callback) => {
566
566
  let idle = -1, timeout = setTimeout(() => {
567
- idle = requestIdleCallback(callback, { timeout: 500 /* MaxPause */ - 100 /* MinPause */ });
568
- }, 100 /* MinPause */);
567
+ idle = requestIdleCallback(callback, { timeout: 500 /* Work.MaxPause */ - 100 /* Work.MinPause */ });
568
+ }, 100 /* Work.MinPause */);
569
569
  return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);
570
570
  };
571
571
  const isInputPending = typeof navigator != "undefined" && ((_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending)
@@ -586,9 +586,9 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
586
586
  let cx = this.view.state.field(Language.state).context;
587
587
  if (cx.updateViewport(update.view.viewport) || this.view.viewport.to > cx.treeLen)
588
588
  this.scheduleWork();
589
- if (update.docChanged) {
589
+ if (update.docChanged || update.selectionSet) {
590
590
  if (this.view.hasFocus)
591
- this.chunkBudget += 50 /* ChangeBonus */;
591
+ this.chunkBudget += 50 /* Work.ChangeBonus */;
592
592
  this.scheduleWork();
593
593
  }
594
594
  this.checkAsyncSchedule(cx);
@@ -604,19 +604,19 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
604
604
  this.working = null;
605
605
  let now = Date.now();
606
606
  if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk
607
- this.chunkEnd = now + 30000 /* ChunkTime */;
608
- this.chunkBudget = 3000 /* ChunkBudget */;
607
+ this.chunkEnd = now + 30000 /* Work.ChunkTime */;
608
+ this.chunkBudget = 3000 /* Work.ChunkBudget */;
609
609
  }
610
610
  if (this.chunkBudget <= 0)
611
611
  return; // No more budget
612
612
  let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
613
- if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* MaxParseAhead */))
613
+ if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* Work.MaxParseAhead */))
614
614
  return;
615
- let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Slice */, deadline && !isInputPending ? Math.max(25 /* MinSlice */, deadline.timeRemaining() - 5) : 1e9);
615
+ let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Work.Slice */, deadline && !isInputPending ? Math.max(25 /* Work.MinSlice */, deadline.timeRemaining() - 5) : 1e9);
616
616
  let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1000;
617
617
  let done = field.context.work(() => {
618
618
  return isInputPending && isInputPending() || Date.now() > endTime;
619
- }, vpTo + (viewportFirst ? 0 : 100000 /* MaxParseAhead */));
619
+ }, vpTo + (viewportFirst ? 0 : 100000 /* Work.MaxParseAhead */));
620
620
  this.chunkBudget -= Date.now() - now;
621
621
  if (done || this.chunkBudget <= 0) {
622
622
  field.context.takeTree();
@@ -992,7 +992,24 @@ indicates that no definitive indentation can be determined.
992
992
  const indentNodeProp = /*@__PURE__*/new NodeProp();
993
993
  // Compute the indentation for a given position from the syntax tree.
994
994
  function syntaxIndentation(cx, ast, pos) {
995
- return indentFrom(ast.resolveInner(pos).enterUnfinishedNodesBefore(pos), pos, cx);
995
+ let stack = ast.resolveStack(pos);
996
+ let inner = stack.node.enterUnfinishedNodesBefore(pos);
997
+ if (inner != stack.node) {
998
+ let add = [];
999
+ for (let cur = inner; cur != stack.node; cur = cur.parent)
1000
+ add.push(cur);
1001
+ for (let i = add.length - 1; i >= 0; i--)
1002
+ stack = { node: add[i], next: stack };
1003
+ }
1004
+ return indentFor(stack, cx, pos);
1005
+ }
1006
+ function indentFor(stack, cx, pos) {
1007
+ for (let cur = stack; cur; cur = cur.next) {
1008
+ let strategy = indentStrategy(cur.node);
1009
+ if (strategy)
1010
+ return strategy(TreeIndentContext.create(cx, pos, cur));
1011
+ }
1012
+ return 0;
996
1013
  }
997
1014
  function ignoreClosed(cx) {
998
1015
  return cx.pos == cx.options.simulateBreak && cx.options.simulateDoubleBreak;
@@ -1008,14 +1025,6 @@ function indentStrategy(tree) {
1008
1025
  }
1009
1026
  return tree.parent == null ? topIndent : null;
1010
1027
  }
1011
- function indentFrom(node, pos, base) {
1012
- for (; node; node = node.parent) {
1013
- let strategy = indentStrategy(node);
1014
- if (strategy)
1015
- return strategy(TreeIndentContext.create(base, pos, node));
1016
- }
1017
- return null;
1018
- }
1019
1028
  function topIndent() { return 0; }
1020
1029
  /**
1021
1030
  Objects of this type provide context information and helper
@@ -1028,20 +1037,24 @@ class TreeIndentContext extends IndentContext {
1028
1037
  */
1029
1038
  pos,
1030
1039
  /**
1031
- The syntax tree node to which the indentation strategy
1032
- applies.
1040
+ @internal
1033
1041
  */
1034
- node) {
1042
+ context) {
1035
1043
  super(base.state, base.options);
1036
1044
  this.base = base;
1037
1045
  this.pos = pos;
1038
- this.node = node;
1046
+ this.context = context;
1039
1047
  }
1040
1048
  /**
1049
+ The syntax tree node to which the indentation strategy
1050
+ applies.
1051
+ */
1052
+ get node() { return this.context.node; }
1053
+ /**
1041
1054
  @internal
1042
1055
  */
1043
- static create(base, pos, node) {
1044
- return new TreeIndentContext(base, pos, node);
1056
+ static create(base, pos, context) {
1057
+ return new TreeIndentContext(base, pos, context);
1045
1058
  }
1046
1059
  /**
1047
1060
  Get the text directly after `this.pos`, either the entire line
@@ -1082,8 +1095,7 @@ class TreeIndentContext extends IndentContext {
1082
1095
  and return the result of that.
1083
1096
  */
1084
1097
  continue() {
1085
- let parent = this.node.parent;
1086
- return parent ? indentFrom(parent, this.pos, this.base) : 0;
1098
+ return indentFor(this.context.next, this.base, this.pos);
1087
1099
  }
1088
1100
  }
1089
1101
  function isParent(parent, of) {
@@ -1225,9 +1237,10 @@ function syntaxFolding(state, start, end) {
1225
1237
  let tree = syntaxTree(state);
1226
1238
  if (tree.length < end)
1227
1239
  return null;
1228
- let inner = tree.resolveInner(end, 1);
1240
+ let stack = tree.resolveStack(end, 1);
1229
1241
  let found = null;
1230
- for (let cur = inner; cur; cur = cur.parent) {
1242
+ for (let iter = stack; iter; iter = iter.next) {
1243
+ let cur = iter.node;
1231
1244
  if (cur.to <= end || cur.from > end)
1232
1245
  continue;
1233
1246
  if (found && cur.from < start)
@@ -2218,7 +2231,7 @@ class StreamLanguage extends Language {
2218
2231
  state = this.streamParser.startState(cx.unit);
2219
2232
  statePos = 0;
2220
2233
  }
2221
- if (pos - statePos > 10000 /* MaxIndentScanDist */)
2234
+ if (pos - statePos > 10000 /* C.MaxIndentScanDist */)
2222
2235
  return null;
2223
2236
  while (statePos < pos) {
2224
2237
  let line = cx.state.doc.lineAt(statePos), end = Math.min(pos, line.to);
@@ -2300,7 +2313,7 @@ class Parse {
2300
2313
  this.chunks.push(tree.children[i]);
2301
2314
  this.chunkPos.push(tree.positions[i]);
2302
2315
  }
2303
- if (context && this.parsedPos < context.viewport.from - 100000 /* MaxDistanceBeforeViewport */) {
2316
+ if (context && this.parsedPos < context.viewport.from - 100000 /* C.MaxDistanceBeforeViewport */) {
2304
2317
  this.state = this.lang.streamParser.startState(getIndentUnit(context.state));
2305
2318
  context.skipUntilInView(this.parsedPos, context.viewport.from);
2306
2319
  this.parsedPos = context.viewport.from;
@@ -2310,7 +2323,7 @@ class Parse {
2310
2323
  advance() {
2311
2324
  let context = ParseContext.get();
2312
2325
  let parseEnd = this.stoppedAt == null ? this.to : Math.min(this.to, this.stoppedAt);
2313
- let end = Math.min(parseEnd, this.chunkStart + 2048 /* ChunkSize */);
2326
+ let end = Math.min(parseEnd, this.chunkStart + 2048 /* C.ChunkSize */);
2314
2327
  if (context)
2315
2328
  end = Math.min(end, context.viewport.to);
2316
2329
  while (this.parsedPos < end)
@@ -2394,7 +2407,7 @@ class Parse {
2394
2407
  let token = readToken(streamParser.token, stream, this.state);
2395
2408
  if (token)
2396
2409
  offset = this.emitToken(this.lang.tokenTable.resolve(token), this.parsedPos + stream.start, this.parsedPos + stream.pos, 4, offset);
2397
- if (stream.start > 10000 /* MaxLineLength */)
2410
+ if (stream.start > 10000 /* C.MaxLineLength */)
2398
2411
  break;
2399
2412
  }
2400
2413
  }
@@ -2410,7 +2423,7 @@ class Parse {
2410
2423
  length: this.parsedPos - this.chunkStart,
2411
2424
  nodeSet,
2412
2425
  topID: 0,
2413
- maxBufferLength: 2048 /* ChunkSize */,
2426
+ maxBufferLength: 2048 /* C.ChunkSize */,
2414
2427
  reused: this.chunkReused
2415
2428
  });
2416
2429
  tree = new Tree(tree.type, tree.children, tree.positions, tree.length, [[this.lang.stateAfter, this.lang.streamParser.copyState(this.state)]]);
@@ -2470,31 +2483,36 @@ function warnForPart(part, msg) {
2470
2483
  console.warn(msg);
2471
2484
  }
2472
2485
  function createTokenType(extra, tagStr) {
2473
- let tag = null;
2474
- for (let part of tagStr.split(".")) {
2475
- let value = (extra[part] || tags[part]);
2476
- if (!value) {
2477
- warnForPart(part, `Unknown highlighting tag ${part}`);
2478
- }
2479
- else if (typeof value == "function") {
2480
- if (!tag)
2481
- warnForPart(part, `Modifier ${part} used at start of tag`);
2482
- else
2483
- tag = value(tag);
2484
- }
2485
- else {
2486
- if (tag)
2487
- warnForPart(part, `Tag ${part} used as modifier`);
2488
- else
2489
- tag = value;
2486
+ let tags$1 = [];
2487
+ for (let name of tagStr.split(" ")) {
2488
+ let found = [];
2489
+ for (let part of name.split(".")) {
2490
+ let value = (extra[part] || tags[part]);
2491
+ if (!value) {
2492
+ warnForPart(part, `Unknown highlighting tag ${part}`);
2493
+ }
2494
+ else if (typeof value == "function") {
2495
+ if (!found.length)
2496
+ warnForPart(part, `Modifier ${part} used at start of tag`);
2497
+ else
2498
+ found = found.map(value);
2499
+ }
2500
+ else {
2501
+ if (found.length)
2502
+ warnForPart(part, `Tag ${part} used as modifier`);
2503
+ else
2504
+ found = Array.isArray(value) ? value : [value];
2505
+ }
2490
2506
  }
2507
+ for (let tag of found)
2508
+ tags$1.push(tag);
2491
2509
  }
2492
- if (!tag)
2510
+ if (!tags$1.length)
2493
2511
  return 0;
2494
2512
  let name = tagStr.replace(/ /g, "_"), type = NodeType.define({
2495
2513
  id: typeArray.length,
2496
2514
  name,
2497
- props: [styleTags({ [name]: tag })]
2515
+ props: [styleTags({ [name]: tags$1 })]
2498
2516
  });
2499
2517
  typeArray.push(type);
2500
2518
  return type.id;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/language",
3
- "version": "6.9.0",
3
+ "version": "6.9.2",
4
4
  "description": "Language support infrastructure for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -28,7 +28,7 @@
28
28
  "dependencies": {
29
29
  "@codemirror/state": "^6.0.0",
30
30
  "@codemirror/view": "^6.0.0",
31
- "@lezer/common": "^1.0.0",
31
+ "@lezer/common": "^1.1.0",
32
32
  "@lezer/highlight": "^1.0.0",
33
33
  "@lezer/lr": "^1.0.0",
34
34
  "style-mod": "^4.0.0"