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