@codemirror/language 0.17.4 → 0.18.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/dist/index.cjs CHANGED
@@ -7,37 +7,52 @@ var text = require('@codemirror/text');
7
7
  var state = require('@codemirror/state');
8
8
  var view = require('@codemirror/view');
9
9
 
10
- /// Node prop stored in a grammar's top syntax node to provide the
11
- /// facet that stores language data for that language.
10
+ /**
11
+ Node prop stored in a grammar's top syntax node to provide the
12
+ facet that stores language data for that language.
13
+ */
12
14
  const languageDataProp = new lezerTree.NodeProp();
13
- /// Helper function to define a facet (to be added to the top syntax
14
- /// node(s) for a language via
15
- /// [`languageDataProp`](#language.languageDataProp)), that will be
16
- /// used to associate language data with the language. You
17
- /// probably only need this when subclassing
18
- /// [`Language`](#language.Language).
15
+ /**
16
+ Helper function to define a facet (to be added to the top syntax
17
+ node(s) for a language via
18
+ [`languageDataProp`](https://codemirror.net/6/docs/ref/#language.languageDataProp)), that will be
19
+ used to associate language data with the language. You
20
+ probably only need this when subclassing
21
+ [`Language`](https://codemirror.net/6/docs/ref/#language.Language).
22
+ */
19
23
  function defineLanguageFacet(baseData) {
20
24
  return state.Facet.define({
21
25
  combine: baseData ? values => values.concat(baseData) : undefined
22
26
  });
23
27
  }
24
- /// A language object manages parsing and per-language
25
- /// [metadata](#state.EditorState.languageDataAt). Parse data is
26
- /// managed as a [Lezer](https://lezer.codemirror.net) tree. You'll
27
- /// want to subclass this class for custom parsers, or use the
28
- /// [`LezerLanguage`](#language.LezerLanguage) or
29
- /// [`StreamLanguage`](#stream-parser.StreamLanguage) abstractions for
30
- /// [Lezer](https://lezer.codemirror.net/) or stream parsers.
28
+ /**
29
+ A language object manages parsing and per-language
30
+ [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
+ [`LezerLanguage`](https://codemirror.net/6/docs/ref/#language.LezerLanguage) or
34
+ [`StreamLanguage`](https://codemirror.net/6/docs/ref/#stream-parser.StreamLanguage) abstractions for
35
+ [Lezer](https://lezer.codemirror.net/) or stream parsers.
36
+ */
31
37
  class Language {
32
- /// Construct a language object. You usually don't need to invoke
33
- /// this directly. But when you do, make sure you use
34
- /// [`defineLanguageFacet`](#language.defineLanguageFacet) to create
35
- /// the first argument.
38
+ /**
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.
43
+ */
36
44
  constructor(
37
- /// The [language data](#state.EditorState.languageDataAt) data
38
- /// facet used for this language.
39
- data, parser, extraExtensions = []) {
45
+ /**
46
+ The [language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) data
47
+ facet used for this language.
48
+ */
49
+ data, parser,
50
+ /**
51
+ The node type of the top node of trees produced by this parser.
52
+ */
53
+ topNode, extraExtensions = []) {
40
54
  this.data = data;
55
+ this.topNode = topNode;
41
56
  // Kludge to define EditorState.tree as a debugging helper,
42
57
  // without the EditorState package actually knowing about
43
58
  // languages and lezer trees.
@@ -49,13 +64,17 @@ class Language {
49
64
  state.EditorState.languageData.of((state, pos) => state.facet(languageDataFacetAt(state, pos)))
50
65
  ].concat(extraExtensions);
51
66
  }
52
- /// Query whether this language is active at the given position.
67
+ /**
68
+ Query whether this language is active at the given position.
69
+ */
53
70
  isActiveAt(state, pos) {
54
71
  return languageDataFacetAt(state, pos) == this.data;
55
72
  }
56
- /// Find the document regions that were parsed using this language.
57
- /// The returned regions will _include_ any nested languages rooted
58
- /// in this language, when those exist.
73
+ /**
74
+ Find the document regions that were parsed using this language.
75
+ The returned regions will _include_ any nested languages rooted
76
+ in this language, when those exist.
77
+ */
59
78
  findRegions(state) {
60
79
  let lang = state.facet(language);
61
80
  if ((lang === null || lang === void 0 ? void 0 : lang.data) == this.data)
@@ -74,19 +93,25 @@ class Language {
74
93
  });
75
94
  return result;
76
95
  }
77
- /// Indicates whether this language allows nested languages. The
78
- /// default implementation returns true.
96
+ /**
97
+ Indicates whether this language allows nested languages. The
98
+ default implementation returns true.
99
+ */
79
100
  get allowsNesting() { return true; }
80
- /// Use this language to parse the given string into a tree.
101
+ /**
102
+ Use this language to parse the given string into a tree.
103
+ */
81
104
  parseString(code) {
82
105
  let doc = text.Text.of(code.split("\n"));
83
- let parse = this.parser.startParse(new DocInput(doc), 0, new EditorParseContext(this.parser, state.EditorState.create({ doc }), [], lezerTree.Tree.empty, { from: 0, to: code.length }, []));
106
+ let parse = this.parser.startParse(new DocInput(doc), 0, new EditorParseContext(this.parser, state.EditorState.create({ doc }), [], lezerTree.Tree.empty, { from: 0, to: code.length }, [], null));
84
107
  let tree;
85
108
  while (!(tree = parse.advance())) { }
86
109
  return tree;
87
110
  }
88
111
  }
89
- /// @internal
112
+ /**
113
+ @internal
114
+ */
90
115
  Language.setState = state.StateEffect.define();
91
116
  function languageDataFacetAt(state, pos) {
92
117
  let topLang = state.facet(language);
@@ -104,38 +129,48 @@ function languageDataFacetAt(state, pos) {
104
129
  }
105
130
  return topLang.data;
106
131
  }
107
- /// A subclass of [`Language`](#language.Language) for use with
108
- /// [Lezer](https://lezer.codemirror.net/docs/ref#lezer.Parser)
109
- /// parsers.
132
+ /**
133
+ A subclass of [`Language`](https://codemirror.net/6/docs/ref/#language.Language) for use with
134
+ [Lezer](https://lezer.codemirror.net/docs/ref#lezer.Parser)
135
+ parsers.
136
+ */
110
137
  class LezerLanguage extends Language {
111
138
  constructor(data, parser) {
112
- super(data, parser);
139
+ super(data, parser, parser.topNode);
113
140
  this.parser = parser;
114
141
  }
115
- /// Define a language from a parser.
142
+ /**
143
+ Define a language from a parser.
144
+ */
116
145
  static define(spec) {
117
146
  let data = defineLanguageFacet(spec.languageData);
118
147
  return new LezerLanguage(data, spec.parser.configure({
119
148
  props: [languageDataProp.add(type => type.isTop ? data : undefined)]
120
149
  }));
121
150
  }
122
- /// Create a new instance of this language with a reconfigured
123
- /// version of its parser.
151
+ /**
152
+ Create a new instance of this language with a reconfigured
153
+ version of its parser.
154
+ */
124
155
  configure(options) {
125
156
  return new LezerLanguage(this.data, this.parser.configure(options));
126
157
  }
127
158
  get allowsNesting() { return this.parser.hasNested; }
128
159
  }
129
- /// Get the syntax tree for a state, which is the current (possibly
130
- /// incomplete) parse tree of active [language](#language.Language),
131
- /// or the empty tree if there is no language available.
160
+ /**
161
+ Get the syntax tree for a state, which is the current (possibly
162
+ incomplete) parse tree of active [language](https://codemirror.net/6/docs/ref/#language.Language),
163
+ or the empty tree if there is no language available.
164
+ */
132
165
  function syntaxTree(state) {
133
166
  let field = state.field(Language.state, false);
134
167
  return field ? field.tree : lezerTree.Tree.empty;
135
168
  }
136
- /// Try to get a parse tree that spans at least up to `upto`. The
137
- /// method will do at most `timeout` milliseconds of work to parse
138
- /// up to that point if the tree isn't already available.
169
+ /**
170
+ Try to get a parse tree that spans at least up to `upto`. The
171
+ method will do at most `timeout` milliseconds of work to parse
172
+ up to that point if the tree isn't already available.
173
+ */
139
174
  function ensureSyntaxTree(state, upto, timeout = 50) {
140
175
  var _a;
141
176
  let parse = (_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context;
@@ -178,7 +213,7 @@ class DocInput {
178
213
  let stringStart = this.cursorPos - this.string.length;
179
214
  if (pos < stringStart || pos >= this.cursorPos)
180
215
  stringStart = this.syncTo(pos);
181
- return this.cursor.lineBreak ? "" : this.string.slice(pos - stringStart);
216
+ return this.cursor.lineBreak ? "" : this.string.slice(pos - stringStart, Math.min(this.length - stringStart, this.string.length));
182
217
  }
183
218
  read(from, to) {
184
219
  let stringStart = this.cursorPos - this.string.length;
@@ -191,36 +226,61 @@ class DocInput {
191
226
  return new DocInput(this.doc, at);
192
227
  }
193
228
  }
194
- /// A parse context provided to parsers working on the editor content.
229
+ /**
230
+ A parse context provided to parsers working on the editor content.
231
+ */
195
232
  class EditorParseContext {
196
- /// @internal
233
+ /**
234
+ @internal
235
+ */
197
236
  constructor(parser,
198
- /// The current editor state.
237
+ /**
238
+ The current editor state.
239
+ */
199
240
  state,
200
- /// Tree fragments that can be reused by incremental re-parses.
241
+ /**
242
+ Tree fragments that can be reused by incremental re-parses.
243
+ */
201
244
  fragments = [],
202
- /// @internal
245
+ /**
246
+ @internal
247
+ */
203
248
  tree,
204
- /// The current editor viewport (or some overapproximation
205
- /// thereof). Intended to be used for opportunistically avoiding
206
- /// work (in which case
207
- /// [`skipUntilInView`](#language.EditorParseContext.skipUntilInView)
208
- /// should be called to make sure the parser is restarted when the
209
- /// skipped region becomes visible).
249
+ /**
250
+ The current editor viewport (or some overapproximation
251
+ thereof). Intended to be used for opportunistically avoiding
252
+ work (in which case
253
+ [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.EditorParseContext.skipUntilInView)
254
+ should be called to make sure the parser is restarted when the
255
+ skipped region becomes visible).
256
+ */
210
257
  viewport,
211
- /// @internal
212
- skipped) {
258
+ /**
259
+ @internal
260
+ */
261
+ skipped,
262
+ /**
263
+ This is where skipping parsers can register a promise that,
264
+ when resolved, will schedule a new parse. It is cleared when
265
+ the parse worker picks up the promise. @internal
266
+ */
267
+ scheduleOn) {
213
268
  this.parser = parser;
214
269
  this.state = state;
215
270
  this.fragments = fragments;
216
271
  this.tree = tree;
217
272
  this.viewport = viewport;
218
273
  this.skipped = skipped;
274
+ this.scheduleOn = scheduleOn;
219
275
  this.parse = null;
220
- /// @internal
276
+ /**
277
+ @internal
278
+ */
221
279
  this.tempSkipped = [];
222
280
  }
223
- /// @internal
281
+ /**
282
+ @internal
283
+ */
224
284
  work(time, upto) {
225
285
  if (this.tree != lezerTree.Tree.empty && (upto == null ? this.tree.length == this.state.doc.length : this.tree.length >= upto)) {
226
286
  this.takeTree();
@@ -245,7 +305,9 @@ class EditorParseContext {
245
305
  return false;
246
306
  }
247
307
  }
248
- /// @internal
308
+ /**
309
+ @internal
310
+ */
249
311
  takeTree() {
250
312
  if (this.parse && this.parse.pos > this.tree.length) {
251
313
  this.tree = this.parse.forceFinish();
@@ -257,7 +319,9 @@ class EditorParseContext {
257
319
  fragments = cutFragments(fragments, r.from, r.to);
258
320
  return fragments;
259
321
  }
260
- /// @internal
322
+ /**
323
+ @internal
324
+ */
261
325
  changes(changes, newState) {
262
326
  let { fragments, tree, viewport, skipped } = this;
263
327
  this.takeTree();
@@ -276,9 +340,11 @@ class EditorParseContext {
276
340
  }
277
341
  }
278
342
  }
279
- return new EditorParseContext(this.parser, newState, fragments, tree, viewport, skipped);
343
+ return new EditorParseContext(this.parser, newState, fragments, tree, viewport, skipped, this.scheduleOn);
280
344
  }
281
- /// @internal
345
+ /**
346
+ @internal
347
+ */
282
348
  updateViewport(viewport) {
283
349
  this.viewport = viewport;
284
350
  let startLen = this.skipped.length;
@@ -291,40 +357,61 @@ class EditorParseContext {
291
357
  }
292
358
  return this.skipped.length < startLen;
293
359
  }
294
- /// @internal
360
+ /**
361
+ @internal
362
+ */
295
363
  reset() {
296
364
  if (this.parse) {
297
365
  this.takeTree();
298
366
  this.parse = null;
299
367
  }
300
368
  }
301
- /// Notify the parse scheduler that the given region was skipped
302
- /// because it wasn't in view, and the parse should be restarted
303
- /// when it comes into view.
369
+ /**
370
+ Notify the parse scheduler that the given region was skipped
371
+ because it wasn't in view, and the parse should be restarted
372
+ when it comes into view.
373
+ */
304
374
  skipUntilInView(from, to) {
305
375
  this.skipped.push({ from, to });
306
376
  }
307
- /// @internal
377
+ /**
378
+ Returns a parser intended to be used as placeholder when
379
+ asynchronously loading a nested parser. It'll skip its input and
380
+ mark it as not-really-parsed, so that the next update will parse
381
+ it again.
382
+
383
+ When `until` is given, a reparse will be scheduled when that
384
+ promise resolves.
385
+ */
386
+ static getSkippingParser(until) {
387
+ return {
388
+ startParse(input, startPos, context) {
389
+ return {
390
+ pos: startPos,
391
+ advance() {
392
+ let ecx = context;
393
+ ecx.tempSkipped.push({ from: startPos, to: input.length });
394
+ if (until)
395
+ ecx.scheduleOn = ecx.scheduleOn ? Promise.all([ecx.scheduleOn, until]) : until;
396
+ this.pos = input.length;
397
+ return new lezerTree.Tree(lezerTree.NodeType.none, [], [], input.length - startPos);
398
+ },
399
+ forceFinish() { return this.advance(); }
400
+ };
401
+ }
402
+ };
403
+ }
404
+ /**
405
+ @internal
406
+ */
308
407
  movedPast(pos) {
309
408
  return this.tree.length < pos && this.parse && this.parse.pos >= pos;
310
409
  }
311
410
  }
312
- /// A parser intended to be used as placeholder when asynchronously
313
- /// loading a nested parser. It'll skip its input and mark it as
314
- /// not-really-parsed, so that the next update will parse it again.
315
- EditorParseContext.skippingParser = {
316
- startParse(input, startPos, context) {
317
- return {
318
- pos: startPos,
319
- advance() {
320
- context.tempSkipped.push({ from: startPos, to: input.length });
321
- this.pos = input.length;
322
- return new lezerTree.Tree(lezerTree.NodeType.none, [], [], input.length - startPos);
323
- },
324
- forceFinish() { return this.advance(); }
325
- };
326
- }
327
- };
411
+ /**
412
+ FIXME backwards compatible shim, remove on next major @internal
413
+ */
414
+ EditorParseContext.skippingParser = EditorParseContext.getSkippingParser();
328
415
  function cutFragments(fragments, from, to) {
329
416
  return lezerTree.TreeFragment.applyChanges(fragments, [{ fromA: from, toA: to, fromB: from, toB: to }]);
330
417
  }
@@ -350,7 +437,7 @@ class LanguageState {
350
437
  return new LanguageState(newCx);
351
438
  }
352
439
  static init(state) {
353
- let parseState = new EditorParseContext(state.facet(language).parser, state, [], lezerTree.Tree.empty, { from: 0, to: state.doc.length }, []);
440
+ let parseState = new EditorParseContext(state.facet(language).parser, state, [], lezerTree.Tree.empty, { from: 0, to: state.doc.length }, [], null);
354
441
  if (!parseState.work(25 /* Apply */))
355
442
  parseState.takeTree();
356
443
  return new LanguageState(parseState);
@@ -382,8 +469,8 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
382
469
  this.scheduleWork();
383
470
  }
384
471
  update(update) {
472
+ let cx = this.view.state.field(Language.state).context;
385
473
  if (update.viewportChanged) {
386
- let cx = this.view.state.field(Language.state).context;
387
474
  if (cx.updateViewport(update.view.viewport))
388
475
  cx.reset();
389
476
  if (this.view.viewport.to > cx.tree.length)
@@ -394,12 +481,13 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
394
481
  this.chunkBudget += 50 /* ChangeBonus */;
395
482
  this.scheduleWork();
396
483
  }
484
+ this.checkAsyncSchedule(cx);
397
485
  }
398
486
  scheduleWork() {
399
487
  if (this.working > -1)
400
488
  return;
401
- let { state } = this.view, field = state.field(Language.state);
402
- if (field.tree.length >= state.doc.length)
489
+ let { state } = this.view, field = state.field(Language.state), frags = field.context.fragments;
490
+ if (field.tree.length >= state.doc.length && frags.length && frags[0].from == 0 && frags[0].to >= state.doc.length)
403
491
  return;
404
492
  this.working = requestIdle(this.work, { timeout: 500 /* Pause */ });
405
493
  }
@@ -424,6 +512,13 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
424
512
  }
425
513
  if (!done && this.chunkBudget > 0)
426
514
  this.scheduleWork();
515
+ this.checkAsyncSchedule(field.context);
516
+ }
517
+ checkAsyncSchedule(cx) {
518
+ if (cx.scheduleOn) {
519
+ cx.scheduleOn.then(() => this.scheduleWork());
520
+ cx.scheduleOn = null;
521
+ }
427
522
  }
428
523
  destroy() {
429
524
  if (this.working >= 0)
@@ -432,70 +527,98 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
432
527
  }, {
433
528
  eventHandlers: { focus() { this.scheduleWork(); } }
434
529
  });
435
- /// The facet used to associate a language with an editor state.
530
+ /**
531
+ The facet used to associate a language with an editor state.
532
+ */
436
533
  const language = state.Facet.define({
437
534
  combine(languages) { return languages.length ? languages[0] : null; },
438
535
  enables: [Language.state, parseWorker]
439
536
  });
440
- /// This class bundles a [language object](#language.Language) with an
441
- /// optional set of supporting extensions. Language packages are
442
- /// encouraged to export a function that optionally takes a
443
- /// configuration object and returns a `LanguageSupport` instance, as
444
- /// the main way for client code to use the package.
537
+ /**
538
+ This class bundles a [language object](https://codemirror.net/6/docs/ref/#language.Language) with an
539
+ optional set of supporting extensions. Language packages are
540
+ encouraged to export a function that optionally takes a
541
+ configuration object and returns a `LanguageSupport` instance, as
542
+ the main way for client code to use the package.
543
+ */
445
544
  class LanguageSupport {
446
- /// Create a support object.
545
+ /**
546
+ Create a support object.
547
+ */
447
548
  constructor(
448
- /// The language object.
549
+ /**
550
+ The language object.
551
+ */
449
552
  language,
450
- /// An optional set of supporting extensions. When nesting a
451
- /// language in another language, the outer language is encouraged
452
- /// to include the supporting extensions for its inner languages
453
- /// in its own set of support extensions.
553
+ /**
554
+ An optional set of supporting extensions. When nesting a
555
+ language in another language, the outer language is encouraged
556
+ to include the supporting extensions for its inner languages
557
+ in its own set of support extensions.
558
+ */
454
559
  support = []) {
455
560
  this.language = language;
456
561
  this.support = support;
457
562
  this.extension = [language, support];
458
563
  }
459
564
  }
460
- /// Language descriptions are used to store metadata about languages
461
- /// and to dynamically load them. Their main role is finding the
462
- /// appropriate language for a filename or dynamically loading nested
463
- /// parsers.
565
+ /**
566
+ Language descriptions are used to store metadata about languages
567
+ and to dynamically load them. Their main role is finding the
568
+ appropriate language for a filename or dynamically loading nested
569
+ parsers.
570
+ */
464
571
  class LanguageDescription {
465
572
  constructor(
466
- /// The name of this language.
573
+ /**
574
+ The name of this language.
575
+ */
467
576
  name,
468
- /// Alternative names for the mode (lowercased, includes `this.name`).
577
+ /**
578
+ Alternative names for the mode (lowercased, includes `this.name`).
579
+ */
469
580
  alias,
470
- /// File extensions associated with this language.
581
+ /**
582
+ File extensions associated with this language.
583
+ */
471
584
  extensions,
472
- /// Optional filename pattern that should be associated with this
473
- /// language.
585
+ /**
586
+ Optional filename pattern that should be associated with this
587
+ language.
588
+ */
474
589
  filename, loadFunc) {
475
590
  this.name = name;
476
591
  this.alias = alias;
477
592
  this.extensions = extensions;
478
593
  this.filename = filename;
479
594
  this.loadFunc = loadFunc;
480
- /// If the language has been loaded, this will hold its value.
595
+ /**
596
+ If the language has been loaded, this will hold its value.
597
+ */
481
598
  this.support = undefined;
482
599
  this.loading = null;
483
600
  }
484
- /// Start loading the the language. Will return a promise that
485
- /// resolves to a [`LanguageSupport`](#language.LanguageSupport)
486
- /// object when the language successfully loads.
601
+ /**
602
+ Start loading the the language. Will return a promise that
603
+ resolves to a [`LanguageSupport`](https://codemirror.net/6/docs/ref/#language.LanguageSupport)
604
+ object when the language successfully loads.
605
+ */
487
606
  load() {
488
607
  return this.loading || (this.loading = this.loadFunc().then(support => this.support = support, err => { this.loading = null; throw err; }));
489
608
  }
490
- /// Create a language description.
609
+ /**
610
+ Create a language description.
611
+ */
491
612
  static of(spec) {
492
613
  return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, spec.load);
493
614
  }
494
- /// Look for a language in the given array of descriptions that
495
- /// matches the filename. Will first match
496
- /// [`filename`](#language.LanguageDescription.filename) patterns,
497
- /// and then [extensions](#language.LanguageDescription.extensions),
498
- /// and return the first language that matches.
615
+ /**
616
+ Look for a language in the given array of descriptions that
617
+ matches the filename. Will first match
618
+ [`filename`](https://codemirror.net/6/docs/ref/#language.LanguageDescription.filename) patterns,
619
+ and then [extensions](https://codemirror.net/6/docs/ref/#language.LanguageDescription.extensions),
620
+ and return the first language that matches.
621
+ */
499
622
  static matchFilename(descs, filename) {
500
623
  for (let d of descs)
501
624
  if (d.filename && d.filename.test(filename))
@@ -507,11 +630,13 @@ class LanguageDescription {
507
630
  return d;
508
631
  return null;
509
632
  }
510
- /// Look for a language whose name or alias matches the the given
511
- /// name (case-insensitively). If `fuzzy` is true, and no direct
512
- /// matchs is found, this'll also search for a language whose name
513
- /// or alias occurs in the string (for names shorter than three
514
- /// characters, only when surrounded by non-word characters).
633
+ /**
634
+ Look for a language whose name or alias matches the the given
635
+ name (case-insensitively). If `fuzzy` is true, and no direct
636
+ matchs is found, this'll also search for a language whose name
637
+ or alias occurs in the string (for names shorter than three
638
+ characters, only when surrounded by non-word characters).
639
+ */
515
640
  static matchLanguageName(descs, name, fuzzy = true) {
516
641
  name = name.toLowerCase();
517
642
  for (let d of descs)
@@ -528,13 +653,17 @@ class LanguageDescription {
528
653
  }
529
654
  }
530
655
 
531
- /// Facet that defines a way to provide a function that computes the
532
- /// appropriate indentation depth at the start of a given line, or
533
- /// `null` to indicate no appropriate indentation could be determined.
656
+ /**
657
+ Facet that defines a way to provide a function that computes the
658
+ appropriate indentation depth at the start of a given line, or
659
+ `null` to indicate no appropriate indentation could be determined.
660
+ */
534
661
  const indentService = state.Facet.define();
535
- /// Facet for overriding the unit by which indentation happens.
536
- /// Should be a string consisting either entirely of spaces or
537
- /// entirely of tabs. When not set, this defaults to 2 spaces.
662
+ /**
663
+ Facet for overriding the unit by which indentation happens.
664
+ Should be a string consisting either entirely of spaces or
665
+ entirely of tabs. When not set, this defaults to 2 spaces.
666
+ */
538
667
  const indentUnit = state.Facet.define({
539
668
  combine: values => {
540
669
  if (!values.length)
@@ -544,18 +673,22 @@ const indentUnit = state.Facet.define({
544
673
  return values[0];
545
674
  }
546
675
  });
547
- /// Return the _column width_ of an indent unit in the state.
548
- /// Determined by the [`indentUnit`](#language.indentUnit)
549
- /// facet, and [`tabSize`](#state.EditorState^tabSize) when that
550
- /// contains tabs.
676
+ /**
677
+ Return the _column width_ of an indent unit in the state.
678
+ Determined by the [`indentUnit`](https://codemirror.net/6/docs/ref/#language.indentUnit)
679
+ facet, and [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) when that
680
+ contains tabs.
681
+ */
551
682
  function getIndentUnit(state) {
552
683
  let unit = state.facet(indentUnit);
553
684
  return unit.charCodeAt(0) == 9 ? state.tabSize * unit.length : unit.length;
554
685
  }
555
- /// Create an indentation string that covers columns 0 to `cols`.
556
- /// Will use tabs for as much of the columns as possible when the
557
- /// [`indentUnit`](#language.indentUnit) facet contains
558
- /// tabs.
686
+ /**
687
+ Create an indentation string that covers columns 0 to `cols`.
688
+ Will use tabs for as much of the columns as possible when the
689
+ [`indentUnit`](https://codemirror.net/6/docs/ref/#language.indentUnit) facet contains
690
+ tabs.
691
+ */
559
692
  function indentString(state, cols) {
560
693
  let result = "", ts = state.tabSize;
561
694
  if (state.facet(indentUnit).charCodeAt(0) == 9)
@@ -567,12 +700,14 @@ function indentString(state, cols) {
567
700
  result += " ";
568
701
  return result;
569
702
  }
570
- /// Get the indentation at the given position. Will first consult any
571
- /// [indent services](#language.indentService) that are registered,
572
- /// and if none of those return an indentation, this will check the
573
- /// syntax tree for the [indent node prop](#language.indentNodeProp)
574
- /// and use that if found. Returns a number when an indentation could
575
- /// be determined, and null otherwise.
703
+ /**
704
+ Get the indentation at the given position. Will first consult any
705
+ [indent services](https://codemirror.net/6/docs/ref/#language.indentService) that are registered,
706
+ and if none of those return an indentation, this will check the
707
+ syntax tree for the [indent node prop](https://codemirror.net/6/docs/ref/#language.indentNodeProp)
708
+ and use that if found. Returns a number when an indentation could
709
+ be determined, and null otherwise.
710
+ */
576
711
  function getIndentation(context, pos) {
577
712
  if (context instanceof state.EditorState)
578
713
  context = new IndentContext(context);
@@ -584,23 +719,33 @@ function getIndentation(context, pos) {
584
719
  let tree = syntaxTree(context.state);
585
720
  return tree ? syntaxIndentation(context, tree, pos) : null;
586
721
  }
587
- /// Indentation contexts are used when calling [indentation
588
- /// services](#language.indentService). They provide helper utilities
589
- /// useful in indentation logic, and can selectively override the
590
- /// indentation reported for some lines.
722
+ /**
723
+ Indentation contexts are used when calling [indentation
724
+ services](https://codemirror.net/6/docs/ref/#language.indentService). They provide helper utilities
725
+ useful in indentation logic, and can selectively override the
726
+ indentation reported for some lines.
727
+ */
591
728
  class IndentContext {
592
- /// Create an indent context.
729
+ /**
730
+ Create an indent context.
731
+ */
593
732
  constructor(
594
- /// The editor state.
733
+ /**
734
+ The editor state.
735
+ */
595
736
  state,
596
- /// @internal
737
+ /**
738
+ @internal
739
+ */
597
740
  options = {}) {
598
741
  this.state = state;
599
742
  this.options = options;
600
743
  this.unit = getIndentUnit(state);
601
744
  }
602
- /// Get the text directly after `pos`, either the entire line
603
- /// or the next 100 characters, whichever is shorter.
745
+ /**
746
+ Get the text directly after `pos`, either the entire line
747
+ or the next 100 characters, whichever is shorter.
748
+ */
604
749
  textAfterPos(pos) {
605
750
  var _a, _b;
606
751
  let sim = (_a = this.options) === null || _a === void 0 ? void 0 : _a.simulateBreak;
@@ -608,7 +753,9 @@ class IndentContext {
608
753
  return "";
609
754
  return this.state.sliceDoc(pos, Math.min(pos + 100, sim != null && sim > pos ? sim : 1e9, this.state.doc.lineAt(pos).to));
610
755
  }
611
- /// Find the column for the given position.
756
+ /**
757
+ Find the column for the given position.
758
+ */
612
759
  column(pos) {
613
760
  var _a;
614
761
  let line = this.state.doc.lineAt(pos), text = line.text.slice(0, pos - line.from);
@@ -618,12 +765,16 @@ class IndentContext {
618
765
  result += override - this.countColumn(text, text.search(/\S/));
619
766
  return result;
620
767
  }
621
- /// find the column position (taking tabs into account) of the given
622
- /// position in the given string.
768
+ /**
769
+ find the column position (taking tabs into account) of the given
770
+ position in the given string.
771
+ */
623
772
  countColumn(line, pos) {
624
773
  return text.countColumn(pos < 0 ? line : line.slice(0, pos), 0, this.state.tabSize);
625
774
  }
626
- /// Find the indentation column of the given document line.
775
+ /**
776
+ Find the indentation column of the given document line.
777
+ */
627
778
  lineIndent(line) {
628
779
  var _a;
629
780
  let override = (_a = this.options) === null || _a === void 0 ? void 0 : _a.overrideIndentation;
@@ -635,10 +786,12 @@ class IndentContext {
635
786
  return this.countColumn(line.text, line.text.search(/\S/));
636
787
  }
637
788
  }
638
- /// A syntax tree node prop used to associate indentation strategies
639
- /// with node types. Such a strategy is a function from an indentation
640
- /// context to a column number or null, where null indicates that no
641
- /// definitive indentation can be determined.
789
+ /**
790
+ A syntax tree node prop used to associate indentation strategies
791
+ with node types. Such a strategy is a function from an indentation
792
+ context to a column number or null, where null indicates that no
793
+ definitive indentation can be determined.
794
+ */
642
795
  const indentNodeProp = new lezerTree.NodeProp();
643
796
  // Compute the indentation for a given position from the syntax tree.
644
797
  function syntaxIndentation(cx, ast, pos) {
@@ -659,12 +812,7 @@ function syntaxIndentation(cx, ast, pos) {
659
812
  scanPos = scan.to + 1;
660
813
  }
661
814
  }
662
- for (; tree; tree = tree.parent) {
663
- let strategy = indentStrategy(tree);
664
- if (strategy)
665
- return strategy(new TreeIndentContext(cx, pos, tree));
666
- }
667
- return null;
815
+ return indentFrom(tree, pos, cx);
668
816
  }
669
817
  function ignoreClosed(cx) {
670
818
  var _a, _b;
@@ -681,31 +829,52 @@ function indentStrategy(tree) {
681
829
  }
682
830
  return tree.parent == null ? topIndent : null;
683
831
  }
832
+ function indentFrom(node, pos, base) {
833
+ for (; node; node = node.parent) {
834
+ let strategy = indentStrategy(node);
835
+ if (strategy)
836
+ return strategy(new TreeIndentContext(base, pos, node));
837
+ }
838
+ return null;
839
+ }
684
840
  function topIndent() { return 0; }
685
- /// Objects of this type provide context information and helper
686
- /// methods to indentation functions.
841
+ /**
842
+ Objects of this type provide context information and helper
843
+ methods to indentation functions.
844
+ */
687
845
  class TreeIndentContext extends IndentContext {
688
- /// @internal
846
+ /**
847
+ @internal
848
+ */
689
849
  constructor(base,
690
- /// The position at which indentation is being computed.
850
+ /**
851
+ The position at which indentation is being computed.
852
+ */
691
853
  pos,
692
- /// The syntax tree node to which the indentation strategy
693
- /// applies.
854
+ /**
855
+ The syntax tree node to which the indentation strategy
856
+ applies.
857
+ */
694
858
  node) {
695
859
  super(base.state, base.options);
860
+ this.base = base;
696
861
  this.pos = pos;
697
862
  this.node = node;
698
863
  }
699
- /// Get the text directly after `this.pos`, either the entire line
700
- /// or the next 100 characters, whichever is shorter.
864
+ /**
865
+ Get the text directly after `this.pos`, either the entire line
866
+ or the next 100 characters, whichever is shorter.
867
+ */
701
868
  get textAfter() {
702
869
  return this.textAfterPos(this.pos);
703
870
  }
704
- /// Get the indentation at the reference line for `this.node`, which
705
- /// is the line on which it starts, unless there is a node that is
706
- /// _not_ a parent of this node covering the start of that line. If
707
- /// so, the line at the start of that node is tried, again skipping
708
- /// on if it is covered by another such node.
871
+ /**
872
+ Get the indentation at the reference line for `this.node`, which
873
+ is the line on which it starts, unless there is a node that is
874
+ _not_ a parent of this node covering the start of that line. If
875
+ so, the line at the start of that node is tried, again skipping
876
+ on if it is covered by another such node.
877
+ */
709
878
  get baseIndent() {
710
879
  let line = this.state.doc.lineAt(this.node.from);
711
880
  // Skip line starts that are covered by a sibling (or cousin, etc)
@@ -719,6 +888,14 @@ class TreeIndentContext extends IndentContext {
719
888
  }
720
889
  return this.lineIndent(line);
721
890
  }
891
+ /**
892
+ Continue looking for indentations in the node's parent nodes,
893
+ and return the result of that.
894
+ */
895
+ continue() {
896
+ let parent = this.node.parent;
897
+ return parent ? indentFrom(parent, this.pos, this.base) : 0;
898
+ }
722
899
  }
723
900
  function isParent(parent, of) {
724
901
  for (let cur = of; cur; cur = cur.parent)
@@ -747,15 +924,17 @@ function bracketedAligned(context) {
747
924
  pos = next.to;
748
925
  }
749
926
  }
750
- /// An indentation strategy for delimited (usually bracketed) nodes.
751
- /// Will, by default, indent one unit more than the parent's base
752
- /// indent unless the line starts with a closing token. When `align`
753
- /// is true and there are non-skipped nodes on the node's opening
754
- /// line, the content of the node will be aligned with the end of the
755
- /// opening node, like this:
756
- ///
757
- /// foo(bar,
758
- /// baz)
927
+ /**
928
+ An indentation strategy for delimited (usually bracketed) nodes.
929
+ Will, by default, indent one unit more than the parent's base
930
+ indent unless the line starts with a closing token. When `align`
931
+ is true and there are non-skipped nodes on the node's opening
932
+ line, the content of the node will be aligned with the end of the
933
+ opening node, like this:
934
+
935
+ foo(bar,
936
+ baz)
937
+ */
759
938
  function delimitedIndent({ closing, align = true, units = 1 }) {
760
939
  return (context) => delimitedStrategy(context, align, units, closing);
761
940
  }
@@ -767,15 +946,19 @@ function delimitedStrategy(context, align, units, closing, closedAt) {
767
946
  return closed ? context.column(aligned.from) : context.column(aligned.to);
768
947
  return context.baseIndent + (closed ? 0 : context.unit * units);
769
948
  }
770
- /// An indentation strategy that aligns a node's content to its base
771
- /// indentation.
949
+ /**
950
+ An indentation strategy that aligns a node's content to its base
951
+ indentation.
952
+ */
772
953
  const flatIndent = (context) => context.baseIndent;
773
- /// Creates an indentation strategy that, by default, indents
774
- /// continued lines one unit more than the node's base indentation.
775
- /// You can provide `except` to prevent indentation of lines that
776
- /// match a pattern (for example `/^else\b/` in `if`/`else`
777
- /// constructs), and you can change the amount of units used with the
778
- /// `units` option.
954
+ /**
955
+ Creates an indentation strategy that, by default, indents
956
+ continued lines one unit more than the node's base indentation.
957
+ You can provide `except` to prevent indentation of lines that
958
+ match a pattern (for example `/^else\b/` in `if`/`else`
959
+ constructs), and you can change the amount of units used with the
960
+ `units` option.
961
+ */
779
962
  function continuedIndent({ except, units = 1 } = {}) {
780
963
  return (context) => {
781
964
  let matchExcept = except && except.test(context.textAfter);
@@ -783,17 +966,19 @@ function continuedIndent({ except, units = 1 } = {}) {
783
966
  };
784
967
  }
785
968
  const DontIndentBeyond = 200;
786
- /// Enables reindentation on input. When a language defines an
787
- /// `indentOnInput` field in its [language
788
- /// data](#state.EditorState.languageDataAt), which must hold a regular
789
- /// expression, the line at the cursor will be reindented whenever new
790
- /// text is typed and the input from the start of the line up to the
791
- /// cursor matches that regexp.
792
- ///
793
- /// To avoid unneccesary reindents, it is recommended to start the
794
- /// regexp with `^` (usually followed by `\s*`), and end it with `$`.
795
- /// For example, `/^\s*\}$/` will reindent when a closing brace is
796
- /// added at the start of a line.
969
+ /**
970
+ Enables reindentation on input. When a language defines an
971
+ `indentOnInput` field in its [language
972
+ data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt), which must hold a regular
973
+ expression, the line at the cursor will be reindented whenever new
974
+ text is typed and the input from the start of the line up to the
975
+ cursor matches that regexp.
976
+
977
+ To avoid unneccesary reindents, it is recommended to start the
978
+ regexp with `^` (usually followed by `\s*`), and end it with `$`.
979
+ For example, `/^\s*\}$/` will reindent when a closing brace is
980
+ added at the start of a line.
981
+ */
797
982
  function indentOnInput() {
798
983
  return state.EditorState.transactionFilter.of(tr => {
799
984
  if (!tr.docChanged || tr.annotation(state.Transaction.userEvent) != "input")
@@ -825,16 +1010,29 @@ function indentOnInput() {
825
1010
  });
826
1011
  }
827
1012
 
828
- /// A facet that registers a code folding service. When called with
829
- /// the extent of a line, such a function should return a foldable
830
- /// range that starts on that line (but continues beyond it), if one
831
- /// can be found.
1013
+ /**
1014
+ A facet that registers a code folding service. When called with
1015
+ the extent of a line, such a function should return a foldable
1016
+ range that starts on that line (but continues beyond it), if one
1017
+ can be found.
1018
+ */
832
1019
  const foldService = state.Facet.define();
833
- /// This node prop is used to associate folding information with
834
- /// syntax node types. Given a syntax node, it should check whether
835
- /// that tree is foldable and return the range that can be collapsed
836
- /// when it is.
1020
+ /**
1021
+ This node prop is used to associate folding information with
1022
+ syntax node types. Given a syntax node, it should check whether
1023
+ that tree is foldable and return the range that can be collapsed
1024
+ when it is.
1025
+ */
837
1026
  const foldNodeProp = new lezerTree.NodeProp();
1027
+ /**
1028
+ [Fold](https://codemirror.net/6/docs/ref/#language.foldNodeProp) function that folds everything but
1029
+ the first and the last child of a syntax node. Useful for nodes
1030
+ that start and end with delimiters.
1031
+ */
1032
+ function foldInside(node) {
1033
+ let first = node.firstChild, last = node.lastChild;
1034
+ return first && first.to < last.from ? { from: first.to, to: last.type.isError ? node.to : last.from } : null;
1035
+ }
838
1036
  function syntaxFolding(state, start, end) {
839
1037
  let tree = syntaxTree(state);
840
1038
  if (tree.length == 0)
@@ -855,12 +1053,14 @@ function syntaxFolding(state, start, end) {
855
1053
  }
856
1054
  return found;
857
1055
  }
858
- /// Check whether the given line is foldable. First asks any fold
859
- /// services registered through
860
- /// [`foldService`](#language.foldService), and if none of them return
861
- /// a result, tries to query the [fold node
862
- /// prop](#language.foldNodeProp) of syntax nodes that cover the end
863
- /// of the line.
1056
+ /**
1057
+ Check whether the given line is foldable. First asks any fold
1058
+ services registered through
1059
+ [`foldService`](https://codemirror.net/6/docs/ref/#language.foldService), and if none of them return
1060
+ a result, tries to query the [fold node
1061
+ prop](https://codemirror.net/6/docs/ref/#language.foldNodeProp) of syntax nodes that cover the end
1062
+ of the line.
1063
+ */
864
1064
  function foldable(state, lineStart, lineEnd) {
865
1065
  for (let service of state.facet(foldService)) {
866
1066
  let result = service(state, lineStart, lineEnd);
@@ -882,6 +1082,7 @@ exports.defineLanguageFacet = defineLanguageFacet;
882
1082
  exports.delimitedIndent = delimitedIndent;
883
1083
  exports.ensureSyntaxTree = ensureSyntaxTree;
884
1084
  exports.flatIndent = flatIndent;
1085
+ exports.foldInside = foldInside;
885
1086
  exports.foldNodeProp = foldNodeProp;
886
1087
  exports.foldService = foldService;
887
1088
  exports.foldable = foldable;