Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol
diffdown.com
1import { NodeProp, IterMode, Tree, TreeFragment, Parser, NodeType, NodeSet } from '@lezer/common';
2import { StateEffect, StateField, Facet, EditorState, countColumn, combineConfig, RangeSet, RangeSetBuilder, Prec } from '@codemirror/state';
3import { ViewPlugin, logException, EditorView, Decoration, WidgetType, gutter, GutterMarker, Direction } from '@codemirror/view';
4import { tags, tagHighlighter, highlightTree, styleTags } from '@lezer/highlight';
5import { StyleModule } from 'style-mod';
6
7var _a;
8/**
9Node prop stored in a parser's top syntax node to provide the
10facet that stores language-specific data for that language.
11*/
12const languageDataProp = /*@__PURE__*/new NodeProp();
13/**
14Helper function to define a facet (to be added to the top syntax
15node(s) for a language via
16[`languageDataProp`](https://codemirror.net/6/docs/ref/#language.languageDataProp)), that will be
17used to associate language data with the language. You
18probably only need this when subclassing
19[`Language`](https://codemirror.net/6/docs/ref/#language.Language).
20*/
21function defineLanguageFacet(baseData) {
22 return Facet.define({
23 combine: baseData ? values => values.concat(baseData) : undefined
24 });
25}
26/**
27Syntax node prop used to register sublanguages. Should be added to
28the top level node type for the language.
29*/
30const sublanguageProp = /*@__PURE__*/new NodeProp();
31/**
32A language object manages parsing and per-language
33[metadata](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt). Parse data is
34managed as a [Lezer](https://lezer.codemirror.net) tree. The class
35can be used directly, via the [`LRLanguage`](https://codemirror.net/6/docs/ref/#language.LRLanguage)
36subclass for [Lezer](https://lezer.codemirror.net/) LR parsers, or
37via the [`StreamLanguage`](https://codemirror.net/6/docs/ref/#language.StreamLanguage) subclass
38for stream parsers.
39*/
40class Language {
41 /**
42 Construct a language object. If you need to invoke this
43 directly, first define a data facet with
44 [`defineLanguageFacet`](https://codemirror.net/6/docs/ref/#language.defineLanguageFacet), and then
45 configure your parser to [attach](https://codemirror.net/6/docs/ref/#language.languageDataProp) it
46 to the language's outer syntax node.
47 */
48 constructor(
49 /**
50 The [language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) facet
51 used for this language.
52 */
53 data, parser, extraExtensions = [],
54 /**
55 A language name.
56 */
57 name = "") {
58 this.data = data;
59 this.name = name;
60 // Kludge to define EditorState.tree as a debugging helper,
61 // without the EditorState package actually knowing about
62 // languages and lezer trees.
63 if (!EditorState.prototype.hasOwnProperty("tree"))
64 Object.defineProperty(EditorState.prototype, "tree", { get() { return syntaxTree(this); } });
65 this.parser = parser;
66 this.extension = [
67 language.of(this),
68 EditorState.languageData.of((state, pos, side) => {
69 let top = topNodeAt(state, pos, side), data = top.type.prop(languageDataProp);
70 if (!data)
71 return [];
72 let base = state.facet(data), sub = top.type.prop(sublanguageProp);
73 if (sub) {
74 let innerNode = top.resolve(pos - top.from, side);
75 for (let sublang of sub)
76 if (sublang.test(innerNode, state)) {
77 let data = state.facet(sublang.facet);
78 return sublang.type == "replace" ? data : data.concat(base);
79 }
80 }
81 return base;
82 })
83 ].concat(extraExtensions);
84 }
85 /**
86 Query whether this language is active at the given position.
87 */
88 isActiveAt(state, pos, side = -1) {
89 return topNodeAt(state, pos, side).type.prop(languageDataProp) == this.data;
90 }
91 /**
92 Find the document regions that were parsed using this language.
93 The returned regions will _include_ any nested languages rooted
94 in this language, when those exist.
95 */
96 findRegions(state) {
97 let lang = state.facet(language);
98 if ((lang === null || lang === void 0 ? void 0 : lang.data) == this.data)
99 return [{ from: 0, to: state.doc.length }];
100 if (!lang || !lang.allowsNesting)
101 return [];
102 let result = [];
103 let explore = (tree, from) => {
104 if (tree.prop(languageDataProp) == this.data) {
105 result.push({ from, to: from + tree.length });
106 return;
107 }
108 let mount = tree.prop(NodeProp.mounted);
109 if (mount) {
110 if (mount.tree.prop(languageDataProp) == this.data) {
111 if (mount.overlay)
112 for (let r of mount.overlay)
113 result.push({ from: r.from + from, to: r.to + from });
114 else
115 result.push({ from: from, to: from + tree.length });
116 return;
117 }
118 else if (mount.overlay) {
119 let size = result.length;
120 explore(mount.tree, mount.overlay[0].from + from);
121 if (result.length > size)
122 return;
123 }
124 }
125 for (let i = 0; i < tree.children.length; i++) {
126 let ch = tree.children[i];
127 if (ch instanceof Tree)
128 explore(ch, tree.positions[i] + from);
129 }
130 };
131 explore(syntaxTree(state), 0);
132 return result;
133 }
134 /**
135 Indicates whether this language allows nested languages. The
136 default implementation returns true.
137 */
138 get allowsNesting() { return true; }
139}
140/**
141@internal
142*/
143Language.setState = /*@__PURE__*/StateEffect.define();
144function topNodeAt(state, pos, side) {
145 let topLang = state.facet(language), tree = syntaxTree(state).topNode;
146 if (!topLang || topLang.allowsNesting) {
147 for (let node = tree; node; node = node.enter(pos, side, IterMode.ExcludeBuffers | IterMode.EnterBracketed))
148 if (node.type.isTop)
149 tree = node;
150 }
151 return tree;
152}
153/**
154A subclass of [`Language`](https://codemirror.net/6/docs/ref/#language.Language) for use with Lezer
155[LR parsers](https://lezer.codemirror.net/docs/ref#lr.LRParser)
156parsers.
157*/
158class LRLanguage extends Language {
159 constructor(data, parser, name) {
160 super(data, parser, [], name);
161 this.parser = parser;
162 }
163 /**
164 Define a language from a parser.
165 */
166 static define(spec) {
167 let data = defineLanguageFacet(spec.languageData);
168 return new LRLanguage(data, spec.parser.configure({
169 props: [languageDataProp.add(type => type.isTop ? data : undefined)]
170 }), spec.name);
171 }
172 /**
173 Create a new instance of this language with a reconfigured
174 version of its parser and optionally a new name.
175 */
176 configure(options, name) {
177 return new LRLanguage(this.data, this.parser.configure(options), name || this.name);
178 }
179 get allowsNesting() { return this.parser.hasWrappers(); }
180}
181/**
182Get the syntax tree for a state, which is the current (possibly
183incomplete) parse tree of the active
184[language](https://codemirror.net/6/docs/ref/#language.Language), or the empty tree if there is no
185language available.
186*/
187function syntaxTree(state) {
188 let field = state.field(Language.state, false);
189 return field ? field.tree : Tree.empty;
190}
191/**
192Try to get a parse tree that spans at least up to `upto`. The
193method will do at most `timeout` milliseconds of work to parse
194up to that point if the tree isn't already available.
195*/
196function ensureSyntaxTree(state, upto, timeout = 50) {
197 var _a;
198 let parse = (_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context;
199 if (!parse)
200 return null;
201 let oldVieport = parse.viewport;
202 parse.updateViewport({ from: 0, to: upto });
203 let result = parse.isDone(upto) || parse.work(timeout, upto) ? parse.tree : null;
204 parse.updateViewport(oldVieport);
205 return result;
206}
207/**
208Queries whether there is a full syntax tree available up to the
209given document position. If there isn't, the background parse
210process _might_ still be working and update the tree further, but
211there is no guarantee of that—the parser will [stop
212working](https://codemirror.net/6/docs/ref/#language.syntaxParserRunning) when it has spent a
213certain amount of time or has moved beyond the visible viewport.
214Always returns false if no language has been enabled.
215*/
216function syntaxTreeAvailable(state, upto = state.doc.length) {
217 var _a;
218 return ((_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context.isDone(upto)) || false;
219}
220/**
221Move parsing forward, and update the editor state afterwards to
222reflect the new tree. Will work for at most `timeout`
223milliseconds. Returns true if the parser managed get to the given
224position in that time.
225*/
226function forceParsing(view, upto = view.viewport.to, timeout = 100) {
227 let success = ensureSyntaxTree(view.state, upto, timeout);
228 if (success != syntaxTree(view.state))
229 view.dispatch({});
230 return !!success;
231}
232/**
233Tells you whether the language parser is planning to do more
234parsing work (in a `requestIdleCallback` pseudo-thread) or has
235stopped running, either because it parsed the entire document,
236because it spent too much time and was cut off, or because there
237is no language parser enabled.
238*/
239function syntaxParserRunning(view) {
240 var _a;
241 return ((_a = view.plugin(parseWorker)) === null || _a === void 0 ? void 0 : _a.isWorking()) || false;
242}
243/**
244Lezer-style
245[`Input`](https://lezer.codemirror.net/docs/ref#common.Input)
246object for a [`Text`](https://codemirror.net/6/docs/ref/#state.Text) object.
247*/
248class DocInput {
249 /**
250 Create an input object for the given document.
251 */
252 constructor(doc) {
253 this.doc = doc;
254 this.cursorPos = 0;
255 this.string = "";
256 this.cursor = doc.iter();
257 }
258 get length() { return this.doc.length; }
259 syncTo(pos) {
260 this.string = this.cursor.next(pos - this.cursorPos).value;
261 this.cursorPos = pos + this.string.length;
262 return this.cursorPos - this.string.length;
263 }
264 chunk(pos) {
265 this.syncTo(pos);
266 return this.string;
267 }
268 get lineChunks() { return true; }
269 read(from, to) {
270 let stringStart = this.cursorPos - this.string.length;
271 if (from < stringStart || to >= this.cursorPos)
272 return this.doc.sliceString(from, to);
273 else
274 return this.string.slice(from - stringStart, to - stringStart);
275 }
276}
277let currentContext = null;
278/**
279A parse context provided to parsers working on the editor content.
280*/
281class ParseContext {
282 constructor(parser,
283 /**
284 The current editor state.
285 */
286 state,
287 /**
288 Tree fragments that can be reused by incremental re-parses.
289 */
290 fragments = [],
291 /**
292 @internal
293 */
294 tree,
295 /**
296 @internal
297 */
298 treeLen,
299 /**
300 The current editor viewport (or some overapproximation
301 thereof). Intended to be used for opportunistically avoiding
302 work (in which case
303 [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.ParseContext.skipUntilInView)
304 should be called to make sure the parser is restarted when the
305 skipped region becomes visible).
306 */
307 viewport,
308 /**
309 @internal
310 */
311 skipped,
312 /**
313 This is where skipping parsers can register a promise that,
314 when resolved, will schedule a new parse. It is cleared when
315 the parse worker picks up the promise. @internal
316 */
317 scheduleOn) {
318 this.parser = parser;
319 this.state = state;
320 this.fragments = fragments;
321 this.tree = tree;
322 this.treeLen = treeLen;
323 this.viewport = viewport;
324 this.skipped = skipped;
325 this.scheduleOn = scheduleOn;
326 this.parse = null;
327 /**
328 @internal
329 */
330 this.tempSkipped = [];
331 }
332 /**
333 @internal
334 */
335 static create(parser, state, viewport) {
336 return new ParseContext(parser, state, [], Tree.empty, 0, viewport, [], null);
337 }
338 startParse() {
339 return this.parser.startParse(new DocInput(this.state.doc), this.fragments);
340 }
341 /**
342 @internal
343 */
344 work(until, upto) {
345 if (upto != null && upto >= this.state.doc.length)
346 upto = undefined;
347 if (this.tree != Tree.empty && this.isDone(upto !== null && upto !== void 0 ? upto : this.state.doc.length)) {
348 this.takeTree();
349 return true;
350 }
351 return this.withContext(() => {
352 var _a;
353 if (typeof until == "number") {
354 let endTime = Date.now() + until;
355 until = () => Date.now() > endTime;
356 }
357 if (!this.parse)
358 this.parse = this.startParse();
359 if (upto != null && (this.parse.stoppedAt == null || this.parse.stoppedAt > upto) &&
360 upto < this.state.doc.length)
361 this.parse.stopAt(upto);
362 for (;;) {
363 let done = this.parse.advance();
364 if (done) {
365 this.fragments = this.withoutTempSkipped(TreeFragment.addTree(done, this.fragments, this.parse.stoppedAt != null));
366 this.treeLen = (_a = this.parse.stoppedAt) !== null && _a !== void 0 ? _a : this.state.doc.length;
367 this.tree = done;
368 this.parse = null;
369 if (this.treeLen < (upto !== null && upto !== void 0 ? upto : this.state.doc.length))
370 this.parse = this.startParse();
371 else
372 return true;
373 }
374 if (until())
375 return false;
376 }
377 });
378 }
379 /**
380 @internal
381 */
382 takeTree() {
383 let pos, tree;
384 if (this.parse && (pos = this.parse.parsedPos) >= this.treeLen) {
385 if (this.parse.stoppedAt == null || this.parse.stoppedAt > pos)
386 this.parse.stopAt(pos);
387 this.withContext(() => { while (!(tree = this.parse.advance())) { } });
388 this.treeLen = pos;
389 this.tree = tree;
390 this.fragments = this.withoutTempSkipped(TreeFragment.addTree(this.tree, this.fragments, true));
391 this.parse = null;
392 }
393 }
394 withContext(f) {
395 let prev = currentContext;
396 currentContext = this;
397 try {
398 return f();
399 }
400 finally {
401 currentContext = prev;
402 }
403 }
404 withoutTempSkipped(fragments) {
405 for (let r; r = this.tempSkipped.pop();)
406 fragments = cutFragments(fragments, r.from, r.to);
407 return fragments;
408 }
409 /**
410 @internal
411 */
412 changes(changes, newState) {
413 let { fragments, tree, treeLen, viewport, skipped } = this;
414 this.takeTree();
415 if (!changes.empty) {
416 let ranges = [];
417 changes.iterChangedRanges((fromA, toA, fromB, toB) => ranges.push({ fromA, toA, fromB, toB }));
418 fragments = TreeFragment.applyChanges(fragments, ranges);
419 tree = Tree.empty;
420 treeLen = 0;
421 viewport = { from: changes.mapPos(viewport.from, -1), to: changes.mapPos(viewport.to, 1) };
422 if (this.skipped.length) {
423 skipped = [];
424 for (let r of this.skipped) {
425 let from = changes.mapPos(r.from, 1), to = changes.mapPos(r.to, -1);
426 if (from < to)
427 skipped.push({ from, to });
428 }
429 }
430 }
431 return new ParseContext(this.parser, newState, fragments, tree, treeLen, viewport, skipped, this.scheduleOn);
432 }
433 /**
434 @internal
435 */
436 updateViewport(viewport) {
437 if (this.viewport.from == viewport.from && this.viewport.to == viewport.to)
438 return false;
439 this.viewport = viewport;
440 let startLen = this.skipped.length;
441 for (let i = 0; i < this.skipped.length; i++) {
442 let { from, to } = this.skipped[i];
443 if (from < viewport.to && to > viewport.from) {
444 this.fragments = cutFragments(this.fragments, from, to);
445 this.skipped.splice(i--, 1);
446 }
447 }
448 if (this.skipped.length >= startLen)
449 return false;
450 this.reset();
451 return true;
452 }
453 /**
454 @internal
455 */
456 reset() {
457 if (this.parse) {
458 this.takeTree();
459 this.parse = null;
460 }
461 }
462 /**
463 Notify the parse scheduler that the given region was skipped
464 because it wasn't in view, and the parse should be restarted
465 when it comes into view.
466 */
467 skipUntilInView(from, to) {
468 this.skipped.push({ from, to });
469 }
470 /**
471 Returns a parser intended to be used as placeholder when
472 asynchronously loading a nested parser. It'll skip its input and
473 mark it as not-really-parsed, so that the next update will parse
474 it again.
475
476 When `until` is given, a reparse will be scheduled when that
477 promise resolves.
478 */
479 static getSkippingParser(until) {
480 return new class extends Parser {
481 createParse(input, fragments, ranges) {
482 let from = ranges[0].from, to = ranges[ranges.length - 1].to;
483 let parser = {
484 parsedPos: from,
485 advance() {
486 let cx = currentContext;
487 if (cx) {
488 for (let r of ranges)
489 cx.tempSkipped.push(r);
490 if (until)
491 cx.scheduleOn = cx.scheduleOn ? Promise.all([cx.scheduleOn, until]) : until;
492 }
493 this.parsedPos = to;
494 return new Tree(NodeType.none, [], [], to - from);
495 },
496 stoppedAt: null,
497 stopAt() { }
498 };
499 return parser;
500 }
501 };
502 }
503 /**
504 @internal
505 */
506 isDone(upto) {
507 upto = Math.min(upto, this.state.doc.length);
508 let frags = this.fragments;
509 return this.treeLen >= upto && frags.length && frags[0].from == 0 && frags[0].to >= upto;
510 }
511 /**
512 Get the context for the current parse, or `null` if no editor
513 parse is in progress.
514 */
515 static get() { return currentContext; }
516}
517function cutFragments(fragments, from, to) {
518 return TreeFragment.applyChanges(fragments, [{ fromA: from, toA: to, fromB: from, toB: to }]);
519}
520class LanguageState {
521 constructor(
522 // A mutable parse state that is used to preserve work done during
523 // the lifetime of a state when moving to the next state.
524 context) {
525 this.context = context;
526 this.tree = context.tree;
527 }
528 apply(tr) {
529 if (!tr.docChanged && this.tree == this.context.tree)
530 return this;
531 let newCx = this.context.changes(tr.changes, tr.state);
532 // If the previous parse wasn't done, go forward only up to its
533 // end position or the end of the viewport, to avoid slowing down
534 // state updates with parse work beyond the viewport.
535 let upto = this.context.treeLen == tr.startState.doc.length ? undefined
536 : Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);
537 if (!newCx.work(20 /* Work.Apply */, upto))
538 newCx.takeTree();
539 return new LanguageState(newCx);
540 }
541 static init(state) {
542 let vpTo = Math.min(3000 /* Work.InitViewport */, state.doc.length);
543 let parseState = ParseContext.create(state.facet(language).parser, state, { from: 0, to: vpTo });
544 if (!parseState.work(20 /* Work.Apply */, vpTo))
545 parseState.takeTree();
546 return new LanguageState(parseState);
547 }
548}
549Language.state = /*@__PURE__*/StateField.define({
550 create: LanguageState.init,
551 update(value, tr) {
552 for (let e of tr.effects)
553 if (e.is(Language.setState))
554 return e.value;
555 if (tr.startState.facet(language) != tr.state.facet(language))
556 return LanguageState.init(tr.state);
557 return value.apply(tr);
558 }
559});
560let requestIdle = (callback) => {
561 let timeout = setTimeout(() => callback(), 500 /* Work.MaxPause */);
562 return () => clearTimeout(timeout);
563};
564if (typeof requestIdleCallback != "undefined")
565 requestIdle = (callback) => {
566 let idle = -1, timeout = setTimeout(() => {
567 idle = requestIdleCallback(callback, { timeout: 500 /* Work.MaxPause */ - 100 /* Work.MinPause */ });
568 }, 100 /* Work.MinPause */);
569 return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);
570 };
571const isInputPending = typeof navigator != "undefined" && ((_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending)
572 ? () => navigator.scheduling.isInputPending() : null;
573const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
574 constructor(view) {
575 this.view = view;
576 this.working = null;
577 this.workScheduled = 0;
578 // End of the current time chunk
579 this.chunkEnd = -1;
580 // Milliseconds of budget left for this chunk
581 this.chunkBudget = -1;
582 this.work = this.work.bind(this);
583 this.scheduleWork();
584 }
585 update(update) {
586 let cx = this.view.state.field(Language.state).context;
587 if (cx.updateViewport(update.view.viewport) || this.view.viewport.to > cx.treeLen)
588 this.scheduleWork();
589 if (update.docChanged || update.selectionSet) {
590 if (this.view.hasFocus)
591 this.chunkBudget += 50 /* Work.ChangeBonus */;
592 this.scheduleWork();
593 }
594 this.checkAsyncSchedule(cx);
595 }
596 scheduleWork() {
597 if (this.working)
598 return;
599 let { state } = this.view, field = state.field(Language.state);
600 if (field.tree != field.context.tree || !field.context.isDone(state.doc.length))
601 this.working = requestIdle(this.work);
602 }
603 work(deadline) {
604 this.working = null;
605 let now = Date.now();
606 if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk
607 this.chunkEnd = now + 30000 /* Work.ChunkTime */;
608 this.chunkBudget = 3000 /* Work.ChunkBudget */;
609 }
610 if (this.chunkBudget <= 0)
611 return; // No more budget
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 /* Work.MaxParseAhead */))
614 return;
615 let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Work.Slice */, deadline && !isInputPending ? Math.max(25 /* Work.MinSlice */, deadline.timeRemaining() - 5) : 1e9);
616 let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1000;
617 let done = field.context.work(() => {
618 return isInputPending && isInputPending() || Date.now() > endTime;
619 }, vpTo + (viewportFirst ? 0 : 100000 /* Work.MaxParseAhead */));
620 this.chunkBudget -= Date.now() - now;
621 if (done || this.chunkBudget <= 0) {
622 field.context.takeTree();
623 this.view.dispatch({ effects: Language.setState.of(new LanguageState(field.context)) });
624 }
625 if (this.chunkBudget > 0 && !(done && !viewportFirst))
626 this.scheduleWork();
627 this.checkAsyncSchedule(field.context);
628 }
629 checkAsyncSchedule(cx) {
630 if (cx.scheduleOn) {
631 this.workScheduled++;
632 cx.scheduleOn
633 .then(() => this.scheduleWork())
634 .catch(err => logException(this.view.state, err))
635 .then(() => this.workScheduled--);
636 cx.scheduleOn = null;
637 }
638 }
639 destroy() {
640 if (this.working)
641 this.working();
642 }
643 isWorking() {
644 return !!(this.working || this.workScheduled > 0);
645 }
646}, {
647 eventHandlers: { focus() { this.scheduleWork(); } }
648});
649/**
650The facet used to associate a language with an editor state. Used
651by `Language` object's `extension` property (so you don't need to
652manually wrap your languages in this). Can be used to access the
653current language on a state.
654*/
655const language = /*@__PURE__*/Facet.define({
656 combine(languages) { return languages.length ? languages[0] : null; },
657 enables: language => [
658 Language.state,
659 parseWorker,
660 EditorView.contentAttributes.compute([language], state => {
661 let lang = state.facet(language);
662 return lang && lang.name ? { "data-language": lang.name } : {};
663 })
664 ]
665});
666/**
667This class bundles a [language](https://codemirror.net/6/docs/ref/#language.Language) with an
668optional set of supporting extensions. Language packages are
669encouraged to export a function that optionally takes a
670configuration object and returns a `LanguageSupport` instance, as
671the main way for client code to use the package.
672*/
673class LanguageSupport {
674 /**
675 Create a language support object.
676 */
677 constructor(
678 /**
679 The language object.
680 */
681 language,
682 /**
683 An optional set of supporting extensions. When nesting a
684 language in another language, the outer language is encouraged
685 to include the supporting extensions for its inner languages
686 in its own set of support extensions.
687 */
688 support = []) {
689 this.language = language;
690 this.support = support;
691 this.extension = [language, support];
692 }
693}
694/**
695Language descriptions are used to store metadata about languages
696and to dynamically load them. Their main role is finding the
697appropriate language for a filename or dynamically loading nested
698parsers.
699*/
700class LanguageDescription {
701 constructor(
702 /**
703 The name of this language.
704 */
705 name,
706 /**
707 Alternative names for the mode (lowercased, includes `this.name`).
708 */
709 alias,
710 /**
711 File extensions associated with this language.
712 */
713 extensions,
714 /**
715 Optional filename pattern that should be associated with this
716 language.
717 */
718 filename, loadFunc,
719 /**
720 If the language has been loaded, this will hold its value.
721 */
722 support = undefined) {
723 this.name = name;
724 this.alias = alias;
725 this.extensions = extensions;
726 this.filename = filename;
727 this.loadFunc = loadFunc;
728 this.support = support;
729 this.loading = null;
730 }
731 /**
732 Start loading the the language. Will return a promise that
733 resolves to a [`LanguageSupport`](https://codemirror.net/6/docs/ref/#language.LanguageSupport)
734 object when the language successfully loads.
735 */
736 load() {
737 return this.loading || (this.loading = this.loadFunc().then(support => this.support = support, err => { this.loading = null; throw err; }));
738 }
739 /**
740 Create a language description.
741 */
742 static of(spec) {
743 let { load, support } = spec;
744 if (!load) {
745 if (!support)
746 throw new RangeError("Must pass either 'load' or 'support' to LanguageDescription.of");
747 load = () => Promise.resolve(support);
748 }
749 return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, load, support);
750 }
751 /**
752 Look for a language in the given array of descriptions that
753 matches the filename. Will first match
754 [`filename`](https://codemirror.net/6/docs/ref/#language.LanguageDescription.filename) patterns,
755 and then [extensions](https://codemirror.net/6/docs/ref/#language.LanguageDescription.extensions),
756 and return the first language that matches.
757 */
758 static matchFilename(descs, filename) {
759 for (let d of descs)
760 if (d.filename && d.filename.test(filename))
761 return d;
762 let ext = /\.([^.]+)$/.exec(filename);
763 if (ext)
764 for (let d of descs)
765 if (d.extensions.indexOf(ext[1]) > -1)
766 return d;
767 return null;
768 }
769 /**
770 Look for a language whose name or alias matches the the given
771 name (case-insensitively). If `fuzzy` is true, and no direct
772 matchs is found, this'll also search for a language whose name
773 or alias occurs in the string (for names shorter than three
774 characters, only when surrounded by non-word characters).
775 */
776 static matchLanguageName(descs, name, fuzzy = true) {
777 name = name.toLowerCase();
778 for (let d of descs)
779 if (d.alias.some(a => a == name))
780 return d;
781 if (fuzzy)
782 for (let d of descs)
783 for (let a of d.alias) {
784 let found = name.indexOf(a);
785 if (found > -1 && (a.length > 2 || !/\w/.test(name[found - 1]) && !/\w/.test(name[found + a.length])))
786 return d;
787 }
788 return null;
789 }
790}
791
792/**
793Facet that defines a way to provide a function that computes the
794appropriate indentation depth, as a column number (see
795[`indentString`](https://codemirror.net/6/docs/ref/#language.indentString)), at the start of a given
796line. A return value of `null` indicates no indentation can be
797determined, and the line should inherit the indentation of the one
798above it. A return value of `undefined` defers to the next indent
799service.
800*/
801const indentService = /*@__PURE__*/Facet.define();
802/**
803Facet for overriding the unit by which indentation happens. Should
804be a string consisting entirely of the same whitespace character.
805When not set, this defaults to 2 spaces.
806*/
807const indentUnit = /*@__PURE__*/Facet.define({
808 combine: values => {
809 if (!values.length)
810 return " ";
811 let unit = values[0];
812 if (!unit || /\S/.test(unit) || Array.from(unit).some(e => e != unit[0]))
813 throw new Error("Invalid indent unit: " + JSON.stringify(values[0]));
814 return unit;
815 }
816});
817/**
818Return the _column width_ of an indent unit in the state.
819Determined by the [`indentUnit`](https://codemirror.net/6/docs/ref/#language.indentUnit)
820facet, and [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) when that
821contains tabs.
822*/
823function getIndentUnit(state) {
824 let unit = state.facet(indentUnit);
825 return unit.charCodeAt(0) == 9 ? state.tabSize * unit.length : unit.length;
826}
827/**
828Create an indentation string that covers columns 0 to `cols`.
829Will use tabs for as much of the columns as possible when the
830[`indentUnit`](https://codemirror.net/6/docs/ref/#language.indentUnit) facet contains
831tabs.
832*/
833function indentString(state, cols) {
834 let result = "", ts = state.tabSize, ch = state.facet(indentUnit)[0];
835 if (ch == "\t") {
836 while (cols >= ts) {
837 result += "\t";
838 cols -= ts;
839 }
840 ch = " ";
841 }
842 for (let i = 0; i < cols; i++)
843 result += ch;
844 return result;
845}
846/**
847Get the indentation, as a column number, at the given position.
848Will first consult any [indent services](https://codemirror.net/6/docs/ref/#language.indentService)
849that are registered, and if none of those return an indentation,
850this will check the syntax tree for the [indent node
851prop](https://codemirror.net/6/docs/ref/#language.indentNodeProp) and use that if found. Returns a
852number when an indentation could be determined, and null
853otherwise.
854*/
855function getIndentation(context, pos) {
856 if (context instanceof EditorState)
857 context = new IndentContext(context);
858 for (let service of context.state.facet(indentService)) {
859 let result = service(context, pos);
860 if (result !== undefined)
861 return result;
862 }
863 let tree = syntaxTree(context.state);
864 return tree.length >= pos ? syntaxIndentation(context, tree, pos) : null;
865}
866/**
867Create a change set that auto-indents all lines touched by the
868given document range.
869*/
870function indentRange(state, from, to) {
871 let updated = Object.create(null);
872 let context = new IndentContext(state, { overrideIndentation: start => { var _a; return (_a = updated[start]) !== null && _a !== void 0 ? _a : -1; } });
873 let changes = [];
874 for (let pos = from; pos <= to;) {
875 let line = state.doc.lineAt(pos);
876 pos = line.to + 1;
877 let indent = getIndentation(context, line.from);
878 if (indent == null)
879 continue;
880 if (!/\S/.test(line.text))
881 indent = 0;
882 let cur = /^\s*/.exec(line.text)[0];
883 let norm = indentString(state, indent);
884 if (cur != norm) {
885 updated[line.from] = indent;
886 changes.push({ from: line.from, to: line.from + cur.length, insert: norm });
887 }
888 }
889 return state.changes(changes);
890}
891/**
892Indentation contexts are used when calling [indentation
893services](https://codemirror.net/6/docs/ref/#language.indentService). They provide helper utilities
894useful in indentation logic, and can selectively override the
895indentation reported for some lines.
896*/
897class IndentContext {
898 /**
899 Create an indent context.
900 */
901 constructor(
902 /**
903 The editor state.
904 */
905 state,
906 /**
907 @internal
908 */
909 options = {}) {
910 this.state = state;
911 this.options = options;
912 this.unit = getIndentUnit(state);
913 }
914 /**
915 Get a description of the line at the given position, taking
916 [simulated line
917 breaks](https://codemirror.net/6/docs/ref/#language.IndentContext.constructor^options.simulateBreak)
918 into account. If there is such a break at `pos`, the `bias`
919 argument determines whether the part of the line line before or
920 after the break is used.
921 */
922 lineAt(pos, bias = 1) {
923 let line = this.state.doc.lineAt(pos);
924 let { simulateBreak, simulateDoubleBreak } = this.options;
925 if (simulateBreak != null && simulateBreak >= line.from && simulateBreak <= line.to) {
926 if (simulateDoubleBreak && simulateBreak == pos)
927 return { text: "", from: pos };
928 else if (bias < 0 ? simulateBreak < pos : simulateBreak <= pos)
929 return { text: line.text.slice(simulateBreak - line.from), from: simulateBreak };
930 else
931 return { text: line.text.slice(0, simulateBreak - line.from), from: line.from };
932 }
933 return line;
934 }
935 /**
936 Get the text directly after `pos`, either the entire line
937 or the next 100 characters, whichever is shorter.
938 */
939 textAfterPos(pos, bias = 1) {
940 if (this.options.simulateDoubleBreak && pos == this.options.simulateBreak)
941 return "";
942 let { text, from } = this.lineAt(pos, bias);
943 return text.slice(pos - from, Math.min(text.length, pos + 100 - from));
944 }
945 /**
946 Find the column for the given position.
947 */
948 column(pos, bias = 1) {
949 let { text, from } = this.lineAt(pos, bias);
950 let result = this.countColumn(text, pos - from);
951 let override = this.options.overrideIndentation ? this.options.overrideIndentation(from) : -1;
952 if (override > -1)
953 result += override - this.countColumn(text, text.search(/\S|$/));
954 return result;
955 }
956 /**
957 Find the column position (taking tabs into account) of the given
958 position in the given string.
959 */
960 countColumn(line, pos = line.length) {
961 return countColumn(line, this.state.tabSize, pos);
962 }
963 /**
964 Find the indentation column of the line at the given point.
965 */
966 lineIndent(pos, bias = 1) {
967 let { text, from } = this.lineAt(pos, bias);
968 let override = this.options.overrideIndentation;
969 if (override) {
970 let overriden = override(from);
971 if (overriden > -1)
972 return overriden;
973 }
974 return this.countColumn(text, text.search(/\S|$/));
975 }
976 /**
977 Returns the [simulated line
978 break](https://codemirror.net/6/docs/ref/#language.IndentContext.constructor^options.simulateBreak)
979 for this context, if any.
980 */
981 get simulatedBreak() {
982 return this.options.simulateBreak || null;
983 }
984}
985/**
986A syntax tree node prop used to associate indentation strategies
987with node types. Such a strategy is a function from an indentation
988context to a column number (see also
989[`indentString`](https://codemirror.net/6/docs/ref/#language.indentString)) or null, where null
990indicates that no definitive indentation can be determined.
991*/
992const indentNodeProp = /*@__PURE__*/new NodeProp();
993// Compute the indentation for a given position from the syntax tree.
994function syntaxIndentation(cx, ast, pos) {
995 let stack = ast.resolveStack(pos);
996 let inner = ast.resolveInner(pos, -1).resolve(pos, 0).enterUnfinishedNodesBefore(pos);
997 if (inner != stack.node) {
998 let add = [];
999 for (let cur = inner; cur && !(cur.from < stack.node.from || cur.to > stack.node.to ||
1000 cur.from == stack.node.from && cur.type == stack.node.type); cur = cur.parent)
1001 add.push(cur);
1002 for (let i = add.length - 1; i >= 0; i--)
1003 stack = { node: add[i], next: stack };
1004 }
1005 return indentFor(stack, cx, pos);
1006}
1007function indentFor(stack, cx, pos) {
1008 for (let cur = stack; cur; cur = cur.next) {
1009 let strategy = indentStrategy(cur.node);
1010 if (strategy)
1011 return strategy(TreeIndentContext.create(cx, pos, cur));
1012 }
1013 return 0;
1014}
1015function ignoreClosed(cx) {
1016 return cx.pos == cx.options.simulateBreak && cx.options.simulateDoubleBreak;
1017}
1018function indentStrategy(tree) {
1019 let strategy = tree.type.prop(indentNodeProp);
1020 if (strategy)
1021 return strategy;
1022 let first = tree.firstChild, close;
1023 if (first && (close = first.type.prop(NodeProp.closedBy))) {
1024 let last = tree.lastChild, closed = last && close.indexOf(last.name) > -1;
1025 return cx => delimitedStrategy(cx, true, 1, undefined, closed && !ignoreClosed(cx) ? last.from : undefined);
1026 }
1027 return tree.parent == null ? topIndent : null;
1028}
1029function topIndent() { return 0; }
1030/**
1031Objects of this type provide context information and helper
1032methods to indentation functions registered on syntax nodes.
1033*/
1034class TreeIndentContext extends IndentContext {
1035 constructor(base,
1036 /**
1037 The position at which indentation is being computed.
1038 */
1039 pos,
1040 /**
1041 @internal
1042 */
1043 context) {
1044 super(base.state, base.options);
1045 this.base = base;
1046 this.pos = pos;
1047 this.context = context;
1048 }
1049 /**
1050 The syntax tree node to which the indentation strategy
1051 applies.
1052 */
1053 get node() { return this.context.node; }
1054 /**
1055 @internal
1056 */
1057 static create(base, pos, context) {
1058 return new TreeIndentContext(base, pos, context);
1059 }
1060 /**
1061 Get the text directly after `this.pos`, either the entire line
1062 or the next 100 characters, whichever is shorter.
1063 */
1064 get textAfter() {
1065 return this.textAfterPos(this.pos);
1066 }
1067 /**
1068 Get the indentation at the reference line for `this.node`, which
1069 is the line on which it starts, unless there is a node that is
1070 _not_ a parent of this node covering the start of that line. If
1071 so, the line at the start of that node is tried, again skipping
1072 on if it is covered by another such node.
1073 */
1074 get baseIndent() {
1075 return this.baseIndentFor(this.node);
1076 }
1077 /**
1078 Get the indentation for the reference line of the given node
1079 (see [`baseIndent`](https://codemirror.net/6/docs/ref/#language.TreeIndentContext.baseIndent)).
1080 */
1081 baseIndentFor(node) {
1082 let line = this.state.doc.lineAt(node.from);
1083 // Skip line starts that are covered by a sibling (or cousin, etc)
1084 for (;;) {
1085 let atBreak = node.resolve(line.from);
1086 while (atBreak.parent && atBreak.parent.from == atBreak.from)
1087 atBreak = atBreak.parent;
1088 if (isParent(atBreak, node))
1089 break;
1090 line = this.state.doc.lineAt(atBreak.from);
1091 }
1092 return this.lineIndent(line.from);
1093 }
1094 /**
1095 Continue looking for indentations in the node's parent nodes,
1096 and return the result of that.
1097 */
1098 continue() {
1099 return indentFor(this.context.next, this.base, this.pos);
1100 }
1101}
1102function isParent(parent, of) {
1103 for (let cur = of; cur; cur = cur.parent)
1104 if (parent == cur)
1105 return true;
1106 return false;
1107}
1108// Check whether a delimited node is aligned (meaning there are
1109// non-skipped nodes on the same line as the opening delimiter). And
1110// if so, return the opening token.
1111function bracketedAligned(context) {
1112 let tree = context.node;
1113 let openToken = tree.childAfter(tree.from), last = tree.lastChild;
1114 if (!openToken)
1115 return null;
1116 let sim = context.options.simulateBreak;
1117 let openLine = context.state.doc.lineAt(openToken.from);
1118 let lineEnd = sim == null || sim <= openLine.from ? openLine.to : Math.min(openLine.to, sim);
1119 for (let pos = openToken.to;;) {
1120 let next = tree.childAfter(pos);
1121 if (!next || next == last)
1122 return null;
1123 if (!next.type.isSkipped) {
1124 if (next.from >= lineEnd)
1125 return null;
1126 let space = /^ */.exec(openLine.text.slice(openToken.to - openLine.from))[0].length;
1127 return { from: openToken.from, to: openToken.to + space };
1128 }
1129 pos = next.to;
1130 }
1131}
1132/**
1133An indentation strategy for delimited (usually bracketed) nodes.
1134Will, by default, indent one unit more than the parent's base
1135indent unless the line starts with a closing token. When `align`
1136is true and there are non-skipped nodes on the node's opening
1137line, the content of the node will be aligned with the end of the
1138opening node, like this:
1139
1140 foo(bar,
1141 baz)
1142*/
1143function delimitedIndent({ closing, align = true, units = 1 }) {
1144 return (context) => delimitedStrategy(context, align, units, closing);
1145}
1146function delimitedStrategy(context, align, units, closing, closedAt) {
1147 let after = context.textAfter, space = after.match(/^\s*/)[0].length;
1148 let closed = closing && after.slice(space, space + closing.length) == closing || closedAt == context.pos + space;
1149 let aligned = align ? bracketedAligned(context) : null;
1150 if (aligned)
1151 return closed ? context.column(aligned.from) : context.column(aligned.to);
1152 return context.baseIndent + (closed ? 0 : context.unit * units);
1153}
1154/**
1155An indentation strategy that aligns a node's content to its base
1156indentation.
1157*/
1158const flatIndent = (context) => context.baseIndent;
1159/**
1160Creates an indentation strategy that, by default, indents
1161continued lines one unit more than the node's base indentation.
1162You can provide `except` to prevent indentation of lines that
1163match a pattern (for example `/^else\b/` in `if`/`else`
1164constructs), and you can change the amount of units used with the
1165`units` option.
1166*/
1167function continuedIndent({ except, units = 1 } = {}) {
1168 return (context) => {
1169 let matchExcept = except && except.test(context.textAfter);
1170 return context.baseIndent + (matchExcept ? 0 : units * context.unit);
1171 };
1172}
1173const DontIndentBeyond = 200;
1174/**
1175Enables reindentation on input. When a language defines an
1176`indentOnInput` field in its [language
1177data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt), which must hold a regular
1178expression, the line at the cursor will be reindented whenever new
1179text is typed and the input from the start of the line up to the
1180cursor matches that regexp.
1181
1182To avoid unneccesary reindents, it is recommended to start the
1183regexp with `^` (usually followed by `\s*`), and end it with `$`.
1184For example, `/^\s*\}$/` will reindent when a closing brace is
1185added at the start of a line.
1186*/
1187function indentOnInput() {
1188 return EditorState.transactionFilter.of(tr => {
1189 if (!tr.docChanged || !tr.isUserEvent("input.type") && !tr.isUserEvent("input.complete"))
1190 return tr;
1191 let rules = tr.startState.languageDataAt("indentOnInput", tr.startState.selection.main.head);
1192 if (!rules.length)
1193 return tr;
1194 let doc = tr.newDoc, { head } = tr.newSelection.main, line = doc.lineAt(head);
1195 if (head > line.from + DontIndentBeyond)
1196 return tr;
1197 let lineStart = doc.sliceString(line.from, head);
1198 if (!rules.some(r => r.test(lineStart)))
1199 return tr;
1200 let { state } = tr, last = -1, changes = [];
1201 for (let { head } of state.selection.ranges) {
1202 let line = state.doc.lineAt(head);
1203 if (line.from == last)
1204 continue;
1205 last = line.from;
1206 let indent = getIndentation(state, line.from);
1207 if (indent == null)
1208 continue;
1209 let cur = /^\s*/.exec(line.text)[0];
1210 let norm = indentString(state, indent);
1211 if (cur != norm)
1212 changes.push({ from: line.from, to: line.from + cur.length, insert: norm });
1213 }
1214 return changes.length ? [tr, { changes, sequential: true }] : tr;
1215 });
1216}
1217
1218/**
1219A facet that registers a code folding service. When called with
1220the extent of a line, such a function should return a foldable
1221range that starts on that line (but continues beyond it), if one
1222can be found.
1223*/
1224const foldService = /*@__PURE__*/Facet.define();
1225/**
1226This node prop is used to associate folding information with
1227syntax node types. Given a syntax node, it should check whether
1228that tree is foldable and return the range that can be collapsed
1229when it is.
1230*/
1231const foldNodeProp = /*@__PURE__*/new NodeProp();
1232/**
1233[Fold](https://codemirror.net/6/docs/ref/#language.foldNodeProp) function that folds everything but
1234the first and the last child of a syntax node. Useful for nodes
1235that start and end with delimiters.
1236*/
1237function foldInside(node) {
1238 let first = node.firstChild, last = node.lastChild;
1239 return first && first.to < last.from ? { from: first.to, to: last.type.isError ? node.to : last.from } : null;
1240}
1241function syntaxFolding(state, start, end) {
1242 let tree = syntaxTree(state);
1243 if (tree.length < end)
1244 return null;
1245 let stack = tree.resolveStack(end, 1);
1246 let found = null;
1247 for (let iter = stack; iter; iter = iter.next) {
1248 let cur = iter.node;
1249 if (cur.to <= end || cur.from > end)
1250 continue;
1251 if (found && cur.from < start)
1252 break;
1253 let prop = cur.type.prop(foldNodeProp);
1254 if (prop && (cur.to < tree.length - 50 || tree.length == state.doc.length || !isUnfinished(cur))) {
1255 let value = prop(cur, state);
1256 if (value && value.from <= end && value.from >= start && value.to > end)
1257 found = value;
1258 }
1259 }
1260 return found;
1261}
1262function isUnfinished(node) {
1263 let ch = node.lastChild;
1264 return ch && ch.to == node.to && ch.type.isError;
1265}
1266/**
1267Check whether the given line is foldable. First asks any fold
1268services registered through
1269[`foldService`](https://codemirror.net/6/docs/ref/#language.foldService), and if none of them return
1270a result, tries to query the [fold node
1271prop](https://codemirror.net/6/docs/ref/#language.foldNodeProp) of syntax nodes that cover the end
1272of the line.
1273*/
1274function foldable(state, lineStart, lineEnd) {
1275 for (let service of state.facet(foldService)) {
1276 let result = service(state, lineStart, lineEnd);
1277 if (result)
1278 return result;
1279 }
1280 return syntaxFolding(state, lineStart, lineEnd);
1281}
1282function mapRange(range, mapping) {
1283 let from = mapping.mapPos(range.from, 1), to = mapping.mapPos(range.to, -1);
1284 return from >= to ? undefined : { from, to };
1285}
1286/**
1287State effect that can be attached to a transaction to fold the
1288given range. (You probably only need this in exceptional
1289circumstances—usually you'll just want to let
1290[`foldCode`](https://codemirror.net/6/docs/ref/#language.foldCode) and the [fold
1291gutter](https://codemirror.net/6/docs/ref/#language.foldGutter) create the transactions.)
1292*/
1293const foldEffect = /*@__PURE__*/StateEffect.define({ map: mapRange });
1294/**
1295State effect that unfolds the given range (if it was folded).
1296*/
1297const unfoldEffect = /*@__PURE__*/StateEffect.define({ map: mapRange });
1298function selectedLines(view) {
1299 let lines = [];
1300 for (let { head } of view.state.selection.ranges) {
1301 if (lines.some(l => l.from <= head && l.to >= head))
1302 continue;
1303 lines.push(view.lineBlockAt(head));
1304 }
1305 return lines;
1306}
1307/**
1308The state field that stores the folded ranges (as a [decoration
1309set](https://codemirror.net/6/docs/ref/#view.DecorationSet)). Can be passed to
1310[`EditorState.toJSON`](https://codemirror.net/6/docs/ref/#state.EditorState.toJSON) and
1311[`fromJSON`](https://codemirror.net/6/docs/ref/#state.EditorState^fromJSON) to serialize the fold
1312state.
1313*/
1314const foldState = /*@__PURE__*/StateField.define({
1315 create() {
1316 return Decoration.none;
1317 },
1318 update(folded, tr) {
1319 if (tr.isUserEvent("delete"))
1320 tr.changes.iterChangedRanges((fromA, toA) => folded = clearTouchedFolds(folded, fromA, toA));
1321 folded = folded.map(tr.changes);
1322 for (let e of tr.effects) {
1323 if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to)) {
1324 let { preparePlaceholder } = tr.state.facet(foldConfig);
1325 let widget = !preparePlaceholder ? foldWidget :
1326 Decoration.replace({ widget: new PreparedFoldWidget(preparePlaceholder(tr.state, e.value)) });
1327 folded = folded.update({ add: [widget.range(e.value.from, e.value.to)] });
1328 }
1329 else if (e.is(unfoldEffect)) {
1330 folded = folded.update({ filter: (from, to) => e.value.from != from || e.value.to != to,
1331 filterFrom: e.value.from, filterTo: e.value.to });
1332 }
1333 }
1334 // Clear folded ranges that cover the selection head
1335 if (tr.selection)
1336 folded = clearTouchedFolds(folded, tr.selection.main.head);
1337 return folded;
1338 },
1339 provide: f => EditorView.decorations.from(f),
1340 toJSON(folded, state) {
1341 let ranges = [];
1342 folded.between(0, state.doc.length, (from, to) => { ranges.push(from, to); });
1343 return ranges;
1344 },
1345 fromJSON(value) {
1346 if (!Array.isArray(value) || value.length % 2)
1347 throw new RangeError("Invalid JSON for fold state");
1348 let ranges = [];
1349 for (let i = 0; i < value.length;) {
1350 let from = value[i++], to = value[i++];
1351 if (typeof from != "number" || typeof to != "number")
1352 throw new RangeError("Invalid JSON for fold state");
1353 ranges.push(foldWidget.range(from, to));
1354 }
1355 return Decoration.set(ranges, true);
1356 }
1357});
1358function clearTouchedFolds(folded, from, to = from) {
1359 let touched = false;
1360 folded.between(from, to, (a, b) => { if (a < to && b > from)
1361 touched = true; });
1362 return !touched ? folded : folded.update({
1363 filterFrom: from,
1364 filterTo: to,
1365 filter: (a, b) => a >= to || b <= from
1366 });
1367}
1368/**
1369Get a [range set](https://codemirror.net/6/docs/ref/#state.RangeSet) containing the folded ranges
1370in the given state.
1371*/
1372function foldedRanges(state) {
1373 return state.field(foldState, false) || RangeSet.empty;
1374}
1375function findFold(state, from, to) {
1376 var _a;
1377 let found = null;
1378 (_a = state.field(foldState, false)) === null || _a === void 0 ? void 0 : _a.between(from, to, (from, to) => {
1379 if (!found || found.from > from)
1380 found = { from, to };
1381 });
1382 return found;
1383}
1384function foldExists(folded, from, to) {
1385 let found = false;
1386 folded.between(from, from, (a, b) => { if (a == from && b == to)
1387 found = true; });
1388 return found;
1389}
1390function maybeEnable(state, other) {
1391 return state.field(foldState, false) ? other : other.concat(StateEffect.appendConfig.of(codeFolding()));
1392}
1393/**
1394Fold the lines that are selected, if possible.
1395*/
1396const foldCode = view => {
1397 for (let line of selectedLines(view)) {
1398 let range = foldable(view.state, line.from, line.to);
1399 if (range) {
1400 view.dispatch({ effects: maybeEnable(view.state, [foldEffect.of(range), announceFold(view, range)]) });
1401 return true;
1402 }
1403 }
1404 return false;
1405};
1406/**
1407Unfold folded ranges on selected lines.
1408*/
1409const unfoldCode = view => {
1410 if (!view.state.field(foldState, false))
1411 return false;
1412 let effects = [];
1413 for (let line of selectedLines(view)) {
1414 let folded = findFold(view.state, line.from, line.to);
1415 if (folded)
1416 effects.push(unfoldEffect.of(folded), announceFold(view, folded, false));
1417 }
1418 if (effects.length)
1419 view.dispatch({ effects });
1420 return effects.length > 0;
1421};
1422function announceFold(view, range, fold = true) {
1423 let lineFrom = view.state.doc.lineAt(range.from).number, lineTo = view.state.doc.lineAt(range.to).number;
1424 return EditorView.announce.of(`${view.state.phrase(fold ? "Folded lines" : "Unfolded lines")} ${lineFrom} ${view.state.phrase("to")} ${lineTo}.`);
1425}
1426/**
1427Fold all top-level foldable ranges. Note that, in most cases,
1428folding information will depend on the [syntax
1429tree](https://codemirror.net/6/docs/ref/#language.syntaxTree), and folding everything may not work
1430reliably when the document hasn't been fully parsed (either
1431because the editor state was only just initialized, or because the
1432document is so big that the parser decided not to parse it
1433entirely).
1434*/
1435const foldAll = view => {
1436 let { state } = view, effects = [];
1437 for (let pos = 0; pos < state.doc.length;) {
1438 let line = view.lineBlockAt(pos), range = foldable(state, line.from, line.to);
1439 if (range)
1440 effects.push(foldEffect.of(range));
1441 pos = (range ? view.lineBlockAt(range.to) : line).to + 1;
1442 }
1443 if (effects.length)
1444 view.dispatch({ effects: maybeEnable(view.state, effects) });
1445 return !!effects.length;
1446};
1447/**
1448Unfold all folded code.
1449*/
1450const unfoldAll = view => {
1451 let field = view.state.field(foldState, false);
1452 if (!field || !field.size)
1453 return false;
1454 let effects = [];
1455 field.between(0, view.state.doc.length, (from, to) => { effects.push(unfoldEffect.of({ from, to })); });
1456 view.dispatch({ effects });
1457 return true;
1458};
1459// Find the foldable region containing the given line, if one exists
1460function foldableContainer(view, lineBlock) {
1461 // Look backwards through line blocks until we find a foldable region that
1462 // intersects with the line
1463 for (let line = lineBlock;;) {
1464 let foldableRegion = foldable(view.state, line.from, line.to);
1465 if (foldableRegion && foldableRegion.to > lineBlock.from)
1466 return foldableRegion;
1467 if (!line.from)
1468 return null;
1469 line = view.lineBlockAt(line.from - 1);
1470 }
1471}
1472/**
1473Toggle folding at cursors. Unfolds if there is an existing fold
1474starting in that line, tries to find a foldable range around it
1475otherwise.
1476*/
1477const toggleFold = (view) => {
1478 let effects = [];
1479 for (let line of selectedLines(view)) {
1480 let folded = findFold(view.state, line.from, line.to);
1481 if (folded) {
1482 effects.push(unfoldEffect.of(folded), announceFold(view, folded, false));
1483 }
1484 else {
1485 let foldRange = foldableContainer(view, line);
1486 if (foldRange)
1487 effects.push(foldEffect.of(foldRange), announceFold(view, foldRange));
1488 }
1489 }
1490 if (effects.length > 0)
1491 view.dispatch({ effects: maybeEnable(view.state, effects) });
1492 return !!effects.length;
1493};
1494/**
1495Default fold-related key bindings.
1496
1497 - Ctrl-Shift-[ (Cmd-Alt-[ on macOS): [`foldCode`](https://codemirror.net/6/docs/ref/#language.foldCode).
1498 - Ctrl-Shift-] (Cmd-Alt-] on macOS): [`unfoldCode`](https://codemirror.net/6/docs/ref/#language.unfoldCode).
1499 - Ctrl-Alt-[: [`foldAll`](https://codemirror.net/6/docs/ref/#language.foldAll).
1500 - Ctrl-Alt-]: [`unfoldAll`](https://codemirror.net/6/docs/ref/#language.unfoldAll).
1501*/
1502const foldKeymap = [
1503 { key: "Ctrl-Shift-[", mac: "Cmd-Alt-[", run: foldCode },
1504 { key: "Ctrl-Shift-]", mac: "Cmd-Alt-]", run: unfoldCode },
1505 { key: "Ctrl-Alt-[", run: foldAll },
1506 { key: "Ctrl-Alt-]", run: unfoldAll }
1507];
1508const defaultConfig = {
1509 placeholderDOM: null,
1510 preparePlaceholder: null,
1511 placeholderText: "…"
1512};
1513const foldConfig = /*@__PURE__*/Facet.define({
1514 combine(values) { return combineConfig(values, defaultConfig); }
1515});
1516/**
1517Create an extension that configures code folding.
1518*/
1519function codeFolding(config) {
1520 let result = [foldState, baseTheme$1];
1521 if (config)
1522 result.push(foldConfig.of(config));
1523 return result;
1524}
1525function widgetToDOM(view, prepared) {
1526 let { state } = view, conf = state.facet(foldConfig);
1527 let onclick = (event) => {
1528 let line = view.lineBlockAt(view.posAtDOM(event.target));
1529 let folded = findFold(view.state, line.from, line.to);
1530 if (folded)
1531 view.dispatch({ effects: unfoldEffect.of(folded) });
1532 event.preventDefault();
1533 };
1534 if (conf.placeholderDOM)
1535 return conf.placeholderDOM(view, onclick, prepared);
1536 let element = document.createElement("span");
1537 element.textContent = conf.placeholderText;
1538 element.setAttribute("aria-label", state.phrase("folded code"));
1539 element.title = state.phrase("unfold");
1540 element.className = "cm-foldPlaceholder";
1541 element.onclick = onclick;
1542 return element;
1543}
1544const foldWidget = /*@__PURE__*/Decoration.replace({ widget: /*@__PURE__*/new class extends WidgetType {
1545 toDOM(view) { return widgetToDOM(view, null); }
1546 } });
1547class PreparedFoldWidget extends WidgetType {
1548 constructor(value) {
1549 super();
1550 this.value = value;
1551 }
1552 eq(other) { return this.value == other.value; }
1553 toDOM(view) { return widgetToDOM(view, this.value); }
1554}
1555const foldGutterDefaults = {
1556 openText: "⌄",
1557 closedText: "›",
1558 markerDOM: null,
1559 domEventHandlers: {},
1560 foldingChanged: () => false
1561};
1562class FoldMarker extends GutterMarker {
1563 constructor(config, open) {
1564 super();
1565 this.config = config;
1566 this.open = open;
1567 }
1568 eq(other) { return this.config == other.config && this.open == other.open; }
1569 toDOM(view) {
1570 if (this.config.markerDOM)
1571 return this.config.markerDOM(this.open);
1572 let span = document.createElement("span");
1573 span.textContent = this.open ? this.config.openText : this.config.closedText;
1574 span.title = view.state.phrase(this.open ? "Fold line" : "Unfold line");
1575 return span;
1576 }
1577}
1578/**
1579Create an extension that registers a fold gutter, which shows a
1580fold status indicator before foldable lines (which can be clicked
1581to fold or unfold the line).
1582*/
1583function foldGutter(config = {}) {
1584 let fullConfig = { ...foldGutterDefaults, ...config };
1585 let canFold = new FoldMarker(fullConfig, true), canUnfold = new FoldMarker(fullConfig, false);
1586 let markers = ViewPlugin.fromClass(class {
1587 constructor(view) {
1588 this.from = view.viewport.from;
1589 this.markers = this.buildMarkers(view);
1590 }
1591 update(update) {
1592 if (update.docChanged || update.viewportChanged ||
1593 update.startState.facet(language) != update.state.facet(language) ||
1594 update.startState.field(foldState, false) != update.state.field(foldState, false) ||
1595 syntaxTree(update.startState) != syntaxTree(update.state) ||
1596 fullConfig.foldingChanged(update))
1597 this.markers = this.buildMarkers(update.view);
1598 }
1599 buildMarkers(view) {
1600 let builder = new RangeSetBuilder();
1601 for (let line of view.viewportLineBlocks) {
1602 let mark = findFold(view.state, line.from, line.to) ? canUnfold
1603 : foldable(view.state, line.from, line.to) ? canFold : null;
1604 if (mark)
1605 builder.add(line.from, line.from, mark);
1606 }
1607 return builder.finish();
1608 }
1609 });
1610 let { domEventHandlers } = fullConfig;
1611 return [
1612 markers,
1613 gutter({
1614 class: "cm-foldGutter",
1615 markers(view) { var _a; return ((_a = view.plugin(markers)) === null || _a === void 0 ? void 0 : _a.markers) || RangeSet.empty; },
1616 initialSpacer() {
1617 return new FoldMarker(fullConfig, false);
1618 },
1619 domEventHandlers: {
1620 ...domEventHandlers,
1621 click: (view, line, event) => {
1622 if (domEventHandlers.click && domEventHandlers.click(view, line, event))
1623 return true;
1624 let folded = findFold(view.state, line.from, line.to);
1625 if (folded) {
1626 view.dispatch({ effects: unfoldEffect.of(folded) });
1627 return true;
1628 }
1629 let range = foldable(view.state, line.from, line.to);
1630 if (range) {
1631 view.dispatch({ effects: foldEffect.of(range) });
1632 return true;
1633 }
1634 return false;
1635 }
1636 }
1637 }),
1638 codeFolding()
1639 ];
1640}
1641const baseTheme$1 = /*@__PURE__*/EditorView.baseTheme({
1642 ".cm-foldPlaceholder": {
1643 backgroundColor: "#eee",
1644 border: "1px solid #ddd",
1645 color: "#888",
1646 borderRadius: ".2em",
1647 margin: "0 1px",
1648 padding: "0 1px",
1649 cursor: "pointer"
1650 },
1651 ".cm-foldGutter span": {
1652 padding: "0 1px",
1653 cursor: "pointer"
1654 }
1655});
1656
1657/**
1658A highlight style associates CSS styles with highlighting
1659[tags](https://lezer.codemirror.net/docs/ref#highlight.Tag).
1660*/
1661class HighlightStyle {
1662 constructor(
1663 /**
1664 The tag styles used to create this highlight style.
1665 */
1666 specs, options) {
1667 this.specs = specs;
1668 let modSpec;
1669 function def(spec) {
1670 let cls = StyleModule.newName();
1671 (modSpec || (modSpec = Object.create(null)))["." + cls] = spec;
1672 return cls;
1673 }
1674 const all = typeof options.all == "string" ? options.all : options.all ? def(options.all) : undefined;
1675 const scopeOpt = options.scope;
1676 this.scope = scopeOpt instanceof Language ? (type) => type.prop(languageDataProp) == scopeOpt.data
1677 : scopeOpt ? (type) => type == scopeOpt : undefined;
1678 this.style = tagHighlighter(specs.map(style => ({
1679 tag: style.tag,
1680 class: style.class || def(Object.assign({}, style, { tag: null }))
1681 })), {
1682 all,
1683 }).style;
1684 this.module = modSpec ? new StyleModule(modSpec) : null;
1685 this.themeType = options.themeType;
1686 }
1687 /**
1688 Create a highlighter style that associates the given styles to
1689 the given tags. The specs must be objects that hold a style tag
1690 or array of tags in their `tag` property, and either a single
1691 `class` property providing a static CSS class (for highlighter
1692 that rely on external styling), or a
1693 [`style-mod`](https://github.com/marijnh/style-mod#documentation)-style
1694 set of CSS properties (which define the styling for those tags).
1695
1696 The CSS rules created for a highlighter will be emitted in the
1697 order of the spec's properties. That means that for elements that
1698 have multiple tags associated with them, styles defined further
1699 down in the list will have a higher CSS precedence than styles
1700 defined earlier.
1701 */
1702 static define(specs, options) {
1703 return new HighlightStyle(specs, options || {});
1704 }
1705}
1706const highlighterFacet = /*@__PURE__*/Facet.define();
1707const fallbackHighlighter = /*@__PURE__*/Facet.define({
1708 combine(values) { return values.length ? [values[0]] : null; }
1709});
1710function getHighlighters(state) {
1711 let main = state.facet(highlighterFacet);
1712 return main.length ? main : state.facet(fallbackHighlighter);
1713}
1714/**
1715Wrap a highlighter in an editor extension that uses it to apply
1716syntax highlighting to the editor content.
1717
1718When multiple (non-fallback) styles are provided, the styling
1719applied is the union of the classes they emit.
1720*/
1721function syntaxHighlighting(highlighter, options) {
1722 let ext = [treeHighlighter], themeType;
1723 if (highlighter instanceof HighlightStyle) {
1724 if (highlighter.module)
1725 ext.push(EditorView.styleModule.of(highlighter.module));
1726 themeType = highlighter.themeType;
1727 }
1728 if (options === null || options === void 0 ? void 0 : options.fallback)
1729 ext.push(fallbackHighlighter.of(highlighter));
1730 else if (themeType)
1731 ext.push(highlighterFacet.computeN([EditorView.darkTheme], state => {
1732 return state.facet(EditorView.darkTheme) == (themeType == "dark") ? [highlighter] : [];
1733 }));
1734 else
1735 ext.push(highlighterFacet.of(highlighter));
1736 return ext;
1737}
1738/**
1739Returns the CSS classes (if any) that the highlighters active in
1740the state would assign to the given style
1741[tags](https://lezer.codemirror.net/docs/ref#highlight.Tag) and
1742(optional) language
1743[scope](https://codemirror.net/6/docs/ref/#language.HighlightStyle^define^options.scope).
1744*/
1745function highlightingFor(state, tags, scope) {
1746 let highlighters = getHighlighters(state);
1747 let result = null;
1748 if (highlighters)
1749 for (let highlighter of highlighters) {
1750 if (!highlighter.scope || scope && highlighter.scope(scope)) {
1751 let cls = highlighter.style(tags);
1752 if (cls)
1753 result = result ? result + " " + cls : cls;
1754 }
1755 }
1756 return result;
1757}
1758class TreeHighlighter {
1759 constructor(view) {
1760 this.markCache = Object.create(null);
1761 this.tree = syntaxTree(view.state);
1762 this.decorations = this.buildDeco(view, getHighlighters(view.state));
1763 this.decoratedTo = view.viewport.to;
1764 }
1765 update(update) {
1766 let tree = syntaxTree(update.state), highlighters = getHighlighters(update.state);
1767 let styleChange = highlighters != getHighlighters(update.startState);
1768 let { viewport } = update.view, decoratedToMapped = update.changes.mapPos(this.decoratedTo, 1);
1769 if (tree.length < viewport.to && !styleChange && tree.type == this.tree.type && decoratedToMapped >= viewport.to) {
1770 this.decorations = this.decorations.map(update.changes);
1771 this.decoratedTo = decoratedToMapped;
1772 }
1773 else if (tree != this.tree || update.viewportChanged || styleChange) {
1774 this.tree = tree;
1775 this.decorations = this.buildDeco(update.view, highlighters);
1776 this.decoratedTo = viewport.to;
1777 }
1778 }
1779 buildDeco(view, highlighters) {
1780 if (!highlighters || !this.tree.length)
1781 return Decoration.none;
1782 let builder = new RangeSetBuilder();
1783 for (let { from, to } of view.visibleRanges) {
1784 highlightTree(this.tree, highlighters, (from, to, style) => {
1785 builder.add(from, to, this.markCache[style] || (this.markCache[style] = Decoration.mark({ class: style })));
1786 }, from, to);
1787 }
1788 return builder.finish();
1789 }
1790}
1791const treeHighlighter = /*@__PURE__*/Prec.high(/*@__PURE__*/ViewPlugin.fromClass(TreeHighlighter, {
1792 decorations: v => v.decorations
1793}));
1794/**
1795A default highlight style (works well with light themes).
1796*/
1797const defaultHighlightStyle = /*@__PURE__*/HighlightStyle.define([
1798 { tag: tags.meta,
1799 color: "#404740" },
1800 { tag: tags.link,
1801 textDecoration: "underline" },
1802 { tag: tags.heading,
1803 textDecoration: "underline",
1804 fontWeight: "bold" },
1805 { tag: tags.emphasis,
1806 fontStyle: "italic" },
1807 { tag: tags.strong,
1808 fontWeight: "bold" },
1809 { tag: tags.strikethrough,
1810 textDecoration: "line-through" },
1811 { tag: tags.keyword,
1812 color: "#708" },
1813 { tag: [tags.atom, tags.bool, tags.url, tags.contentSeparator, tags.labelName],
1814 color: "#219" },
1815 { tag: [tags.literal, tags.inserted],
1816 color: "#164" },
1817 { tag: [tags.string, tags.deleted],
1818 color: "#a11" },
1819 { tag: [tags.regexp, tags.escape, /*@__PURE__*/tags.special(tags.string)],
1820 color: "#e40" },
1821 { tag: /*@__PURE__*/tags.definition(tags.variableName),
1822 color: "#00f" },
1823 { tag: /*@__PURE__*/tags.local(tags.variableName),
1824 color: "#30a" },
1825 { tag: [tags.typeName, tags.namespace],
1826 color: "#085" },
1827 { tag: tags.className,
1828 color: "#167" },
1829 { tag: [/*@__PURE__*/tags.special(tags.variableName), tags.macroName],
1830 color: "#256" },
1831 { tag: /*@__PURE__*/tags.definition(tags.propertyName),
1832 color: "#00c" },
1833 { tag: tags.comment,
1834 color: "#940" },
1835 { tag: tags.invalid,
1836 color: "#f00" }
1837]);
1838
1839const baseTheme = /*@__PURE__*/EditorView.baseTheme({
1840 "&.cm-focused .cm-matchingBracket": { backgroundColor: "#328c8252" },
1841 "&.cm-focused .cm-nonmatchingBracket": { backgroundColor: "#bb555544" }
1842});
1843const DefaultScanDist = 10000, DefaultBrackets = "()[]{}";
1844const bracketMatchingConfig = /*@__PURE__*/Facet.define({
1845 combine(configs) {
1846 return combineConfig(configs, {
1847 afterCursor: true,
1848 brackets: DefaultBrackets,
1849 maxScanDistance: DefaultScanDist,
1850 renderMatch: defaultRenderMatch
1851 });
1852 }
1853});
1854const matchingMark = /*@__PURE__*/Decoration.mark({ class: "cm-matchingBracket" }), nonmatchingMark = /*@__PURE__*/Decoration.mark({ class: "cm-nonmatchingBracket" });
1855function defaultRenderMatch(match) {
1856 let decorations = [];
1857 let mark = match.matched ? matchingMark : nonmatchingMark;
1858 decorations.push(mark.range(match.start.from, match.start.to));
1859 if (match.end)
1860 decorations.push(mark.range(match.end.from, match.end.to));
1861 return decorations;
1862}
1863function bracketDeco(state) {
1864 let decorations = [];
1865 let config = state.facet(bracketMatchingConfig);
1866 for (let range of state.selection.ranges) {
1867 if (!range.empty)
1868 continue;
1869 let match = matchBrackets(state, range.head, -1, config)
1870 || (range.head > 0 && matchBrackets(state, range.head - 1, 1, config))
1871 || (config.afterCursor &&
1872 (matchBrackets(state, range.head, 1, config) ||
1873 (range.head < state.doc.length && matchBrackets(state, range.head + 1, -1, config))));
1874 if (match)
1875 decorations = decorations.concat(config.renderMatch(match, state));
1876 }
1877 return Decoration.set(decorations, true);
1878}
1879const bracketMatcher = /*@__PURE__*/ViewPlugin.fromClass(class {
1880 constructor(view) {
1881 this.paused = false;
1882 this.decorations = bracketDeco(view.state);
1883 }
1884 update(update) {
1885 if (update.docChanged || update.selectionSet || this.paused) {
1886 if (update.view.composing) {
1887 this.decorations = this.decorations.map(update.changes);
1888 this.paused = true;
1889 }
1890 else {
1891 this.decorations = bracketDeco(update.state);
1892 this.paused = false;
1893 }
1894 }
1895 }
1896}, {
1897 decorations: v => v.decorations
1898});
1899const bracketMatchingUnique = [
1900 bracketMatcher,
1901 baseTheme
1902];
1903/**
1904Create an extension that enables bracket matching. Whenever the
1905cursor is next to a bracket, that bracket and the one it matches
1906are highlighted. Or, when no matching bracket is found, another
1907highlighting style is used to indicate this.
1908*/
1909function bracketMatching(config = {}) {
1910 return [bracketMatchingConfig.of(config), bracketMatchingUnique];
1911}
1912/**
1913When larger syntax nodes, such as HTML tags, are marked as
1914opening/closing, it can be a bit messy to treat the whole node as
1915a matchable bracket. This node prop allows you to define, for such
1916a node, a ‘handle’—the part of the node that is highlighted, and
1917that the cursor must be on to activate highlighting in the first
1918place.
1919*/
1920const bracketMatchingHandle = /*@__PURE__*/new NodeProp();
1921function matchingNodes(node, dir, brackets) {
1922 let byProp = node.prop(dir < 0 ? NodeProp.openedBy : NodeProp.closedBy);
1923 if (byProp)
1924 return byProp;
1925 if (node.name.length == 1) {
1926 let index = brackets.indexOf(node.name);
1927 if (index > -1 && index % 2 == (dir < 0 ? 1 : 0))
1928 return [brackets[index + dir]];
1929 }
1930 return null;
1931}
1932function findHandle(node) {
1933 let hasHandle = node.type.prop(bracketMatchingHandle);
1934 return hasHandle ? hasHandle(node.node) : node;
1935}
1936/**
1937Find the matching bracket for the token at `pos`, scanning
1938direction `dir`. Only the `brackets` and `maxScanDistance`
1939properties are used from `config`, if given. Returns null if no
1940bracket was found at `pos`, or a match result otherwise.
1941*/
1942function matchBrackets(state, pos, dir, config = {}) {
1943 let maxScanDistance = config.maxScanDistance || DefaultScanDist, brackets = config.brackets || DefaultBrackets;
1944 let tree = syntaxTree(state), node = tree.resolveInner(pos, dir);
1945 for (let cur = node; cur; cur = cur.parent) {
1946 let matches = matchingNodes(cur.type, dir, brackets);
1947 if (matches && cur.from < cur.to) {
1948 let handle = findHandle(cur);
1949 if (handle && (dir > 0 ? pos >= handle.from && pos < handle.to : pos > handle.from && pos <= handle.to))
1950 return matchMarkedBrackets(state, pos, dir, cur, handle, matches, brackets);
1951 }
1952 }
1953 return matchPlainBrackets(state, pos, dir, tree, node.type, maxScanDistance, brackets);
1954}
1955function matchMarkedBrackets(_state, _pos, dir, token, handle, matching, brackets) {
1956 let parent = token.parent, firstToken = { from: handle.from, to: handle.to };
1957 let depth = 0, cursor = parent === null || parent === void 0 ? void 0 : parent.cursor();
1958 if (cursor && (dir < 0 ? cursor.childBefore(token.from) : cursor.childAfter(token.to)))
1959 do {
1960 if (dir < 0 ? cursor.to <= token.from : cursor.from >= token.to) {
1961 if (depth == 0 && matching.indexOf(cursor.type.name) > -1 && cursor.from < cursor.to) {
1962 let endHandle = findHandle(cursor);
1963 return { start: firstToken, end: endHandle ? { from: endHandle.from, to: endHandle.to } : undefined, matched: true };
1964 }
1965 else if (matchingNodes(cursor.type, dir, brackets)) {
1966 depth++;
1967 }
1968 else if (matchingNodes(cursor.type, -dir, brackets)) {
1969 if (depth == 0) {
1970 let endHandle = findHandle(cursor);
1971 return {
1972 start: firstToken,
1973 end: endHandle && endHandle.from < endHandle.to ? { from: endHandle.from, to: endHandle.to } : undefined,
1974 matched: false
1975 };
1976 }
1977 depth--;
1978 }
1979 }
1980 } while (dir < 0 ? cursor.prevSibling() : cursor.nextSibling());
1981 return { start: firstToken, matched: false };
1982}
1983function matchPlainBrackets(state, pos, dir, tree, tokenType, maxScanDistance, brackets) {
1984 let startCh = dir < 0 ? state.sliceDoc(pos - 1, pos) : state.sliceDoc(pos, pos + 1);
1985 let bracket = brackets.indexOf(startCh);
1986 if (bracket < 0 || (bracket % 2 == 0) != (dir > 0))
1987 return null;
1988 let startToken = { from: dir < 0 ? pos - 1 : pos, to: dir > 0 ? pos + 1 : pos };
1989 let iter = state.doc.iterRange(pos, dir > 0 ? state.doc.length : 0), depth = 0;
1990 for (let distance = 0; !(iter.next()).done && distance <= maxScanDistance;) {
1991 let text = iter.value;
1992 if (dir < 0)
1993 distance += text.length;
1994 let basePos = pos + distance * dir;
1995 for (let pos = dir > 0 ? 0 : text.length - 1, end = dir > 0 ? text.length : -1; pos != end; pos += dir) {
1996 let found = brackets.indexOf(text[pos]);
1997 if (found < 0 || tree.resolveInner(basePos + pos, 1).type != tokenType)
1998 continue;
1999 if ((found % 2 == 0) == (dir > 0)) {
2000 depth++;
2001 }
2002 else if (depth == 1) { // Closing
2003 return { start: startToken, end: { from: basePos + pos, to: basePos + pos + 1 }, matched: (found >> 1) == (bracket >> 1) };
2004 }
2005 else {
2006 depth--;
2007 }
2008 }
2009 if (dir > 0)
2010 distance += text.length;
2011 }
2012 return iter.done ? { start: startToken, matched: false } : null;
2013}
2014
2015// Counts the column offset in a string, taking tabs into account.
2016// Used mostly to find indentation.
2017function countCol(string, end, tabSize, startIndex = 0, startValue = 0) {
2018 if (end == null) {
2019 end = string.search(/[^\s\u00a0]/);
2020 if (end == -1)
2021 end = string.length;
2022 }
2023 let n = startValue;
2024 for (let i = startIndex; i < end; i++) {
2025 if (string.charCodeAt(i) == 9)
2026 n += tabSize - (n % tabSize);
2027 else
2028 n++;
2029 }
2030 return n;
2031}
2032/**
2033Encapsulates a single line of input. Given to stream syntax code,
2034which uses it to tokenize the content.
2035*/
2036class StringStream {
2037 /**
2038 Create a stream.
2039 */
2040 constructor(
2041 /**
2042 The line.
2043 */
2044 string, tabSize,
2045 /**
2046 The current indent unit size.
2047 */
2048 indentUnit, overrideIndent) {
2049 this.string = string;
2050 this.tabSize = tabSize;
2051 this.indentUnit = indentUnit;
2052 this.overrideIndent = overrideIndent;
2053 /**
2054 The current position on the line.
2055 */
2056 this.pos = 0;
2057 /**
2058 The start position of the current token.
2059 */
2060 this.start = 0;
2061 this.lastColumnPos = 0;
2062 this.lastColumnValue = 0;
2063 }
2064 /**
2065 True if we are at the end of the line.
2066 */
2067 eol() { return this.pos >= this.string.length; }
2068 /**
2069 True if we are at the start of the line.
2070 */
2071 sol() { return this.pos == 0; }
2072 /**
2073 Get the next code unit after the current position, or undefined
2074 if we're at the end of the line.
2075 */
2076 peek() { return this.string.charAt(this.pos) || undefined; }
2077 /**
2078 Read the next code unit and advance `this.pos`.
2079 */
2080 next() {
2081 if (this.pos < this.string.length)
2082 return this.string.charAt(this.pos++);
2083 }
2084 /**
2085 Match the next character against the given string, regular
2086 expression, or predicate. Consume and return it if it matches.
2087 */
2088 eat(match) {
2089 let ch = this.string.charAt(this.pos);
2090 let ok;
2091 if (typeof match == "string")
2092 ok = ch == match;
2093 else
2094 ok = ch && (match instanceof RegExp ? match.test(ch) : match(ch));
2095 if (ok) {
2096 ++this.pos;
2097 return ch;
2098 }
2099 }
2100 /**
2101 Continue matching characters that match the given string,
2102 regular expression, or predicate function. Return true if any
2103 characters were consumed.
2104 */
2105 eatWhile(match) {
2106 let start = this.pos;
2107 while (this.eat(match)) { }
2108 return this.pos > start;
2109 }
2110 /**
2111 Consume whitespace ahead of `this.pos`. Return true if any was
2112 found.
2113 */
2114 eatSpace() {
2115 let start = this.pos;
2116 while (/[\s\u00a0]/.test(this.string.charAt(this.pos)))
2117 ++this.pos;
2118 return this.pos > start;
2119 }
2120 /**
2121 Move to the end of the line.
2122 */
2123 skipToEnd() { this.pos = this.string.length; }
2124 /**
2125 Move to directly before the given character, if found on the
2126 current line.
2127 */
2128 skipTo(ch) {
2129 let found = this.string.indexOf(ch, this.pos);
2130 if (found > -1) {
2131 this.pos = found;
2132 return true;
2133 }
2134 }
2135 /**
2136 Move back `n` characters.
2137 */
2138 backUp(n) { this.pos -= n; }
2139 /**
2140 Get the column position at `this.pos`.
2141 */
2142 column() {
2143 if (this.lastColumnPos < this.start) {
2144 this.lastColumnValue = countCol(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
2145 this.lastColumnPos = this.start;
2146 }
2147 return this.lastColumnValue;
2148 }
2149 /**
2150 Get the indentation column of the current line.
2151 */
2152 indentation() {
2153 var _a;
2154 return (_a = this.overrideIndent) !== null && _a !== void 0 ? _a : countCol(this.string, null, this.tabSize);
2155 }
2156 /**
2157 Match the input against the given string or regular expression
2158 (which should start with a `^`). Return true or the regexp match
2159 if it matches.
2160
2161 Unless `consume` is set to `false`, this will move `this.pos`
2162 past the matched text.
2163
2164 When matching a string `caseInsensitive` can be set to true to
2165 make the match case-insensitive.
2166 */
2167 match(pattern, consume, caseInsensitive) {
2168 if (typeof pattern == "string") {
2169 let cased = (str) => caseInsensitive ? str.toLowerCase() : str;
2170 let substr = this.string.substr(this.pos, pattern.length);
2171 if (cased(substr) == cased(pattern)) {
2172 if (consume !== false)
2173 this.pos += pattern.length;
2174 return true;
2175 }
2176 else
2177 return null;
2178 }
2179 else {
2180 let match = this.string.slice(this.pos).match(pattern);
2181 if (match && match.index > 0)
2182 return null;
2183 if (match && consume !== false)
2184 this.pos += match[0].length;
2185 return match;
2186 }
2187 }
2188 /**
2189 Get the current token.
2190 */
2191 current() { return this.string.slice(this.start, this.pos); }
2192}
2193
2194function fullParser(spec) {
2195 return {
2196 name: spec.name || "",
2197 token: spec.token,
2198 blankLine: spec.blankLine || (() => { }),
2199 startState: spec.startState || (() => true),
2200 copyState: spec.copyState || defaultCopyState,
2201 indent: spec.indent || (() => null),
2202 languageData: spec.languageData || {},
2203 tokenTable: spec.tokenTable || noTokens,
2204 mergeTokens: spec.mergeTokens !== false
2205 };
2206}
2207function defaultCopyState(state) {
2208 if (typeof state != "object")
2209 return state;
2210 let newState = {};
2211 for (let prop in state) {
2212 let val = state[prop];
2213 newState[prop] = (val instanceof Array ? val.slice() : val);
2214 }
2215 return newState;
2216}
2217const IndentedFrom = /*@__PURE__*/new WeakMap();
2218/**
2219A [language](https://codemirror.net/6/docs/ref/#language.Language) class based on a CodeMirror
22205-style [streaming parser](https://codemirror.net/6/docs/ref/#language.StreamParser).
2221*/
2222class StreamLanguage extends Language {
2223 constructor(parser) {
2224 let data = defineLanguageFacet(parser.languageData);
2225 let p = fullParser(parser), self;
2226 let impl = new class extends Parser {
2227 createParse(input, fragments, ranges) {
2228 return new Parse(self, input, fragments, ranges);
2229 }
2230 };
2231 super(data, impl, [], parser.name);
2232 this.topNode = docID(data, this);
2233 self = this;
2234 this.streamParser = p;
2235 this.stateAfter = new NodeProp({ perNode: true });
2236 this.tokenTable = parser.tokenTable ? new TokenTable(p.tokenTable) : defaultTokenTable;
2237 }
2238 /**
2239 Define a stream language.
2240 */
2241 static define(spec) { return new StreamLanguage(spec); }
2242 /**
2243 @internal
2244 */
2245 getIndent(cx) {
2246 let from = undefined;
2247 let { overrideIndentation } = cx.options;
2248 if (overrideIndentation) {
2249 from = IndentedFrom.get(cx.state);
2250 if (from != null && from < cx.pos - 1e4)
2251 from = undefined;
2252 }
2253 let start = findState(this, cx.node.tree, cx.node.from, cx.node.from, from !== null && from !== void 0 ? from : cx.pos), statePos, state;
2254 if (start) {
2255 state = start.state;
2256 statePos = start.pos + 1;
2257 }
2258 else {
2259 state = this.streamParser.startState(cx.unit);
2260 statePos = cx.node.from;
2261 }
2262 if (cx.pos - statePos > 10000 /* C.MaxIndentScanDist */)
2263 return null;
2264 while (statePos < cx.pos) {
2265 let line = cx.state.doc.lineAt(statePos), end = Math.min(cx.pos, line.to);
2266 if (line.length) {
2267 let indentation = overrideIndentation ? overrideIndentation(line.from) : -1;
2268 let stream = new StringStream(line.text, cx.state.tabSize, cx.unit, indentation < 0 ? undefined : indentation);
2269 while (stream.pos < end - line.from)
2270 readToken(this.streamParser.token, stream, state);
2271 }
2272 else {
2273 this.streamParser.blankLine(state, cx.unit);
2274 }
2275 if (end == cx.pos)
2276 break;
2277 statePos = line.to + 1;
2278 }
2279 let line = cx.lineAt(cx.pos);
2280 if (overrideIndentation && from == null)
2281 IndentedFrom.set(cx.state, line.from);
2282 return this.streamParser.indent(state, /^\s*(.*)/.exec(line.text)[1], cx);
2283 }
2284 get allowsNesting() { return false; }
2285}
2286function findState(lang, tree, off, startPos, before) {
2287 let state = off >= startPos && off + tree.length <= before && tree.prop(lang.stateAfter);
2288 if (state)
2289 return { state: lang.streamParser.copyState(state), pos: off + tree.length };
2290 for (let i = tree.children.length - 1; i >= 0; i--) {
2291 let child = tree.children[i], pos = off + tree.positions[i];
2292 let found = child instanceof Tree && pos < before && findState(lang, child, pos, startPos, before);
2293 if (found)
2294 return found;
2295 }
2296 return null;
2297}
2298function cutTree(lang, tree, from, to, inside) {
2299 if (inside && from <= 0 && to >= tree.length)
2300 return tree;
2301 if (!inside && from == 0 && tree.type == lang.topNode)
2302 inside = true;
2303 for (let i = tree.children.length - 1; i >= 0; i--) {
2304 let pos = tree.positions[i], child = tree.children[i], inner;
2305 if (pos < to && child instanceof Tree) {
2306 if (!(inner = cutTree(lang, child, from - pos, to - pos, inside)))
2307 break;
2308 return !inside ? inner
2309 : new Tree(tree.type, tree.children.slice(0, i).concat(inner), tree.positions.slice(0, i + 1), pos + inner.length);
2310 }
2311 }
2312 return null;
2313}
2314function findStartInFragments(lang, fragments, startPos, endPos, editorState) {
2315 for (let f of fragments) {
2316 let from = f.from + (f.openStart ? 25 : 0), to = f.to - (f.openEnd ? 25 : 0);
2317 let found = from <= startPos && to > startPos && findState(lang, f.tree, 0 - f.offset, startPos, to), tree;
2318 if (found && found.pos <= endPos && (tree = cutTree(lang, f.tree, startPos + f.offset, found.pos + f.offset, false)))
2319 return { state: found.state, tree };
2320 }
2321 return { state: lang.streamParser.startState(editorState ? getIndentUnit(editorState) : 4), tree: Tree.empty };
2322}
2323class Parse {
2324 constructor(lang, input, fragments, ranges) {
2325 this.lang = lang;
2326 this.input = input;
2327 this.fragments = fragments;
2328 this.ranges = ranges;
2329 this.stoppedAt = null;
2330 this.chunks = [];
2331 this.chunkPos = [];
2332 this.chunk = [];
2333 this.chunkReused = undefined;
2334 this.rangeIndex = 0;
2335 this.to = ranges[ranges.length - 1].to;
2336 let context = ParseContext.get(), from = ranges[0].from;
2337 let { state, tree } = findStartInFragments(lang, fragments, from, this.to, context === null || context === void 0 ? void 0 : context.state);
2338 this.state = state;
2339 this.parsedPos = this.chunkStart = from + tree.length;
2340 for (let i = 0; i < tree.children.length; i++) {
2341 this.chunks.push(tree.children[i]);
2342 this.chunkPos.push(tree.positions[i]);
2343 }
2344 if (context && this.parsedPos < context.viewport.from - 100000 /* C.MaxDistanceBeforeViewport */ &&
2345 ranges.some(r => r.from <= context.viewport.from && r.to >= context.viewport.from)) {
2346 this.state = this.lang.streamParser.startState(getIndentUnit(context.state));
2347 context.skipUntilInView(this.parsedPos, context.viewport.from);
2348 this.parsedPos = context.viewport.from;
2349 }
2350 this.moveRangeIndex();
2351 }
2352 advance() {
2353 let context = ParseContext.get();
2354 let parseEnd = this.stoppedAt == null ? this.to : Math.min(this.to, this.stoppedAt);
2355 let end = Math.min(parseEnd, this.chunkStart + 512 /* C.ChunkSize */);
2356 if (context)
2357 end = Math.min(end, context.viewport.to);
2358 while (this.parsedPos < end)
2359 this.parseLine(context);
2360 if (this.chunkStart < this.parsedPos)
2361 this.finishChunk();
2362 if (this.parsedPos >= parseEnd)
2363 return this.finish();
2364 if (context && this.parsedPos >= context.viewport.to) {
2365 context.skipUntilInView(this.parsedPos, parseEnd);
2366 return this.finish();
2367 }
2368 return null;
2369 }
2370 stopAt(pos) {
2371 this.stoppedAt = pos;
2372 }
2373 lineAfter(pos) {
2374 let chunk = this.input.chunk(pos);
2375 if (!this.input.lineChunks) {
2376 let eol = chunk.indexOf("\n");
2377 if (eol > -1)
2378 chunk = chunk.slice(0, eol);
2379 }
2380 else if (chunk == "\n") {
2381 chunk = "";
2382 }
2383 return pos + chunk.length <= this.to ? chunk : chunk.slice(0, this.to - pos);
2384 }
2385 nextLine() {
2386 let from = this.parsedPos, line = this.lineAfter(from), end = from + line.length;
2387 for (let index = this.rangeIndex;;) {
2388 let rangeEnd = this.ranges[index].to;
2389 if (rangeEnd >= end)
2390 break;
2391 line = line.slice(0, rangeEnd - (end - line.length));
2392 index++;
2393 if (index == this.ranges.length)
2394 break;
2395 let rangeStart = this.ranges[index].from;
2396 let after = this.lineAfter(rangeStart);
2397 line += after;
2398 end = rangeStart + after.length;
2399 }
2400 return { line, end };
2401 }
2402 skipGapsTo(pos, offset, side) {
2403 for (;;) {
2404 let end = this.ranges[this.rangeIndex].to, offPos = pos + offset;
2405 if (side > 0 ? end > offPos : end >= offPos)
2406 break;
2407 let start = this.ranges[++this.rangeIndex].from;
2408 offset += start - end;
2409 }
2410 return offset;
2411 }
2412 moveRangeIndex() {
2413 while (this.ranges[this.rangeIndex].to < this.parsedPos)
2414 this.rangeIndex++;
2415 }
2416 emitToken(id, from, to, offset) {
2417 let size = 4;
2418 if (this.ranges.length > 1) {
2419 offset = this.skipGapsTo(from, offset, 1);
2420 from += offset;
2421 let len0 = this.chunk.length;
2422 offset = this.skipGapsTo(to, offset, -1);
2423 to += offset;
2424 size += this.chunk.length - len0;
2425 }
2426 let last = this.chunk.length - 4;
2427 if (this.lang.streamParser.mergeTokens && size == 4 && last >= 0 &&
2428 this.chunk[last] == id && this.chunk[last + 2] == from)
2429 this.chunk[last + 2] = to;
2430 else
2431 this.chunk.push(id, from, to, size);
2432 return offset;
2433 }
2434 parseLine(context) {
2435 let { line, end } = this.nextLine(), offset = 0, { streamParser } = this.lang;
2436 let stream = new StringStream(line, context ? context.state.tabSize : 4, context ? getIndentUnit(context.state) : 2);
2437 if (stream.eol()) {
2438 streamParser.blankLine(this.state, stream.indentUnit);
2439 }
2440 else {
2441 while (!stream.eol()) {
2442 let token = readToken(streamParser.token, stream, this.state);
2443 if (token)
2444 offset = this.emitToken(this.lang.tokenTable.resolve(token), this.parsedPos + stream.start, this.parsedPos + stream.pos, offset);
2445 if (stream.start > 10000 /* C.MaxLineLength */)
2446 break;
2447 }
2448 }
2449 this.parsedPos = end;
2450 this.moveRangeIndex();
2451 if (this.parsedPos < this.to)
2452 this.parsedPos++;
2453 }
2454 finishChunk() {
2455 let tree = Tree.build({
2456 buffer: this.chunk,
2457 start: this.chunkStart,
2458 length: this.parsedPos - this.chunkStart,
2459 nodeSet,
2460 topID: 0,
2461 maxBufferLength: 512 /* C.ChunkSize */,
2462 reused: this.chunkReused
2463 });
2464 tree = new Tree(tree.type, tree.children, tree.positions, tree.length, [[this.lang.stateAfter, this.lang.streamParser.copyState(this.state)]]);
2465 this.chunks.push(tree);
2466 this.chunkPos.push(this.chunkStart - this.ranges[0].from);
2467 this.chunk = [];
2468 this.chunkReused = undefined;
2469 this.chunkStart = this.parsedPos;
2470 }
2471 finish() {
2472 return new Tree(this.lang.topNode, this.chunks, this.chunkPos, this.parsedPos - this.ranges[0].from).balance();
2473 }
2474}
2475function readToken(token, stream, state) {
2476 stream.start = stream.pos;
2477 for (let i = 0; i < 10; i++) {
2478 let result = token(stream, state);
2479 if (stream.pos > stream.start)
2480 return result;
2481 }
2482 throw new Error("Stream parser failed to advance stream.");
2483}
2484const noTokens = /*@__PURE__*/Object.create(null);
2485const typeArray = [NodeType.none];
2486const nodeSet = /*@__PURE__*/new NodeSet(typeArray);
2487const warned = [];
2488// Cache of node types by name and tags
2489const byTag = /*@__PURE__*/Object.create(null);
2490const defaultTable = /*@__PURE__*/Object.create(null);
2491for (let [legacyName, name] of [
2492 ["variable", "variableName"],
2493 ["variable-2", "variableName.special"],
2494 ["string-2", "string.special"],
2495 ["def", "variableName.definition"],
2496 ["tag", "tagName"],
2497 ["attribute", "attributeName"],
2498 ["type", "typeName"],
2499 ["builtin", "variableName.standard"],
2500 ["qualifier", "modifier"],
2501 ["error", "invalid"],
2502 ["header", "heading"],
2503 ["property", "propertyName"]
2504])
2505 defaultTable[legacyName] = /*@__PURE__*/createTokenType(noTokens, name);
2506class TokenTable {
2507 constructor(extra) {
2508 this.extra = extra;
2509 this.table = Object.assign(Object.create(null), defaultTable);
2510 }
2511 resolve(tag) {
2512 return !tag ? 0 : this.table[tag] || (this.table[tag] = createTokenType(this.extra, tag));
2513 }
2514}
2515const defaultTokenTable = /*@__PURE__*/new TokenTable(noTokens);
2516function warnForPart(part, msg) {
2517 if (warned.indexOf(part) > -1)
2518 return;
2519 warned.push(part);
2520 console.warn(msg);
2521}
2522function createTokenType(extra, tagStr) {
2523 let tags$1 = [];
2524 for (let name of tagStr.split(" ")) {
2525 let found = [];
2526 for (let part of name.split(".")) {
2527 let value = (extra[part] || tags[part]);
2528 if (!value) {
2529 warnForPart(part, `Unknown highlighting tag ${part}`);
2530 }
2531 else if (typeof value == "function") {
2532 if (!found.length)
2533 warnForPart(part, `Modifier ${part} used at start of tag`);
2534 else
2535 found = found.map(value);
2536 }
2537 else {
2538 if (found.length)
2539 warnForPart(part, `Tag ${part} used as modifier`);
2540 else
2541 found = Array.isArray(value) ? value : [value];
2542 }
2543 }
2544 for (let tag of found)
2545 tags$1.push(tag);
2546 }
2547 if (!tags$1.length)
2548 return 0;
2549 let name = tagStr.replace(/ /g, "_"), key = name + " " + tags$1.map(t => t.id);
2550 let known = byTag[key];
2551 if (known)
2552 return known.id;
2553 let type = byTag[key] = NodeType.define({
2554 id: typeArray.length,
2555 name,
2556 props: [styleTags({ [name]: tags$1 })]
2557 });
2558 typeArray.push(type);
2559 return type.id;
2560}
2561function docID(data, lang) {
2562 let type = NodeType.define({ id: typeArray.length, name: "Document", props: [
2563 languageDataProp.add(() => data),
2564 indentNodeProp.add(() => cx => lang.getIndent(cx))
2565 ], top: true });
2566 typeArray.push(type);
2567 return type;
2568}
2569
2570function buildForLine(line) {
2571 return line.length <= 4096 && /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac\ufb50-\ufdff]/.test(line);
2572}
2573function textHasRTL(text) {
2574 for (let i = text.iter(); !i.next().done;)
2575 if (buildForLine(i.value))
2576 return true;
2577 return false;
2578}
2579function changeAddsRTL(change) {
2580 let added = false;
2581 change.iterChanges((fA, tA, fB, tB, ins) => {
2582 if (!added && textHasRTL(ins))
2583 added = true;
2584 });
2585 return added;
2586}
2587const alwaysIsolate = /*@__PURE__*/Facet.define({ combine: values => values.some(x => x) });
2588/**
2589Make sure nodes
2590[marked](https://lezer.codemirror.net/docs/ref/#common.NodeProp^isolate)
2591as isolating for bidirectional text are rendered in a way that
2592isolates them from the surrounding text.
2593*/
2594function bidiIsolates(options = {}) {
2595 let extensions = [isolateMarks];
2596 if (options.alwaysIsolate)
2597 extensions.push(alwaysIsolate.of(true));
2598 return extensions;
2599}
2600const isolateMarks = /*@__PURE__*/ViewPlugin.fromClass(class {
2601 constructor(view) {
2602 this.always = view.state.facet(alwaysIsolate) ||
2603 view.textDirection != Direction.LTR ||
2604 view.state.facet(EditorView.perLineTextDirection);
2605 this.hasRTL = !this.always && textHasRTL(view.state.doc);
2606 this.tree = syntaxTree(view.state);
2607 this.decorations = this.always || this.hasRTL ? buildDeco(view, this.tree, this.always) : Decoration.none;
2608 }
2609 update(update) {
2610 let always = update.state.facet(alwaysIsolate) ||
2611 update.view.textDirection != Direction.LTR ||
2612 update.state.facet(EditorView.perLineTextDirection);
2613 if (!always && !this.hasRTL && changeAddsRTL(update.changes))
2614 this.hasRTL = true;
2615 if (!always && !this.hasRTL)
2616 return;
2617 let tree = syntaxTree(update.state);
2618 if (always != this.always || tree != this.tree || update.docChanged || update.viewportChanged) {
2619 this.tree = tree;
2620 this.always = always;
2621 this.decorations = buildDeco(update.view, tree, always);
2622 }
2623 }
2624}, {
2625 provide: plugin => {
2626 function access(view) {
2627 var _a, _b;
2628 return (_b = (_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.decorations) !== null && _b !== void 0 ? _b : Decoration.none;
2629 }
2630 return [EditorView.outerDecorations.of(access),
2631 Prec.lowest(EditorView.bidiIsolatedRanges.of(access))];
2632 }
2633});
2634function buildDeco(view, tree, always) {
2635 let deco = new RangeSetBuilder();
2636 let ranges = view.visibleRanges;
2637 if (!always)
2638 ranges = clipRTLLines(ranges, view.state.doc);
2639 for (let { from, to } of ranges) {
2640 tree.iterate({
2641 enter: node => {
2642 let iso = node.type.prop(NodeProp.isolate);
2643 if (iso)
2644 deco.add(node.from, node.to, marks[iso]);
2645 },
2646 from, to
2647 });
2648 }
2649 return deco.finish();
2650}
2651function clipRTLLines(ranges, doc) {
2652 let cur = doc.iter(), pos = 0, result = [], last = null;
2653 for (let { from, to } of ranges) {
2654 if (last && last.to > from) {
2655 from = last.to;
2656 if (from >= to)
2657 continue;
2658 }
2659 if (pos + cur.value.length < from) {
2660 cur.next(from - (pos + cur.value.length));
2661 pos = from;
2662 }
2663 for (;;) {
2664 let start = pos, end = pos + cur.value.length;
2665 if (!cur.lineBreak && buildForLine(cur.value)) {
2666 if (last && last.to > start - 10)
2667 last.to = Math.min(to, end);
2668 else
2669 result.push(last = { from: start, to: Math.min(to, end) });
2670 }
2671 if (end >= to)
2672 break;
2673 pos = end;
2674 cur.next();
2675 }
2676 }
2677 return result;
2678}
2679const marks = {
2680 rtl: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "rtl" }, bidiIsolate: Direction.RTL }),
2681 ltr: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "ltr" }, bidiIsolate: Direction.LTR }),
2682 auto: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "auto" }, bidiIsolate: null })
2683};
2684
2685export { DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, StreamLanguage, StringStream, TreeIndentContext, bidiIsolates, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };