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