2824 lines
		
	
	
		
			105 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			2824 lines
		
	
	
		
			105 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import { NodeType, NodeProp, IterMode, Tree, TreeFragment, Parser, NodeSet } from '@lezer/common';
 | ||
| import { Facet, EditorState, StateEffect, StateField, countColumn, RangeSet, combineConfig, RangeSetBuilder, Prec } from '@codemirror/state';
 | ||
| import { Decoration, Direction, ViewPlugin, logException, EditorView, WidgetType, gutter, GutterMarker } from '@codemirror/view';
 | ||
| import { tags, styleTags, tagHighlighter, highlightTree } from '@lezer/highlight';
 | ||
| 
 | ||
| const C = "\u037c";
 | ||
| const COUNT = typeof Symbol == "undefined" ? "__" + C : Symbol.for(C);
 | ||
| const SET = typeof Symbol == "undefined" ? "__styleSet" + Math.floor(Math.random() * 1e8) : Symbol("styleSet");
 | ||
| const top = typeof globalThis != "undefined" ? globalThis : typeof window != "undefined" ? window : {};
 | ||
| 
 | ||
| // :: - Style modules encapsulate a set of CSS rules defined from
 | ||
| // JavaScript. Their definitions are only available in a given DOM
 | ||
| // root after it has been _mounted_ there with `StyleModule.mount`.
 | ||
| //
 | ||
| // Style modules should be created once and stored somewhere, as
 | ||
| // opposed to re-creating them every time you need them. The amount of
 | ||
| // CSS rules generated for a given DOM root is bounded by the amount
 | ||
| // of style modules that were used. So to avoid leaking rules, don't
 | ||
| // create these dynamically, but treat them as one-time allocations.
 | ||
| class StyleModule {
 | ||
|   // :: (Object<Style>, ?{finish: ?(string) → string})
 | ||
|   // Create a style module from the given spec.
 | ||
|   //
 | ||
|   // When `finish` is given, it is called on regular (non-`@`)
 | ||
|   // selectors (after `&` expansion) to compute the final selector.
 | ||
|   constructor(spec, options) {
 | ||
|     this.rules = [];
 | ||
|     let {finish} = options || {};
 | ||
| 
 | ||
|     function splitSelector(selector) {
 | ||
|       return /^@/.test(selector) ? [selector] : selector.split(/,\s*/)
 | ||
|     }
 | ||
| 
 | ||
|     function render(selectors, spec, target, isKeyframes) {
 | ||
|       let local = [], isAt = /^@(\w+)\b/.exec(selectors[0]), keyframes = isAt && isAt[1] == "keyframes";
 | ||
|       if (isAt && spec == null) return target.push(selectors[0] + ";")
 | ||
|       for (let prop in spec) {
 | ||
|         let value = spec[prop];
 | ||
|         if (/&/.test(prop)) {
 | ||
|           render(prop.split(/,\s*/).map(part => selectors.map(sel => part.replace(/&/, sel))).reduce((a, b) => a.concat(b)),
 | ||
|                  value, target);
 | ||
|         } else if (value && typeof value == "object") {
 | ||
|           if (!isAt) throw new RangeError("The value of a property (" + prop + ") should be a primitive value.")
 | ||
|           render(splitSelector(prop), value, local, keyframes);
 | ||
|         } else if (value != null) {
 | ||
|           local.push(prop.replace(/_.*/, "").replace(/[A-Z]/g, l => "-" + l.toLowerCase()) + ": " + value + ";");
 | ||
|         }
 | ||
|       }
 | ||
|       if (local.length || keyframes) {
 | ||
|         target.push((finish && !isAt && !isKeyframes ? selectors.map(finish) : selectors).join(", ") +
 | ||
|                     " {" + local.join(" ") + "}");
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     for (let prop in spec) render(splitSelector(prop), spec[prop], this.rules);
 | ||
|   }
 | ||
| 
 | ||
|   // :: () → string
 | ||
|   // Returns a string containing the module's CSS rules.
 | ||
|   getRules() { return this.rules.join("\n") }
 | ||
| 
 | ||
|   // :: () → string
 | ||
|   // Generate a new unique CSS class name.
 | ||
|   static newName() {
 | ||
|     let id = top[COUNT] || 1;
 | ||
|     top[COUNT] = id + 1;
 | ||
|     return C + id.toString(36)
 | ||
|   }
 | ||
| 
 | ||
|   // :: (union<Document, ShadowRoot>, union<[StyleModule], StyleModule>, ?{nonce: ?string})
 | ||
|   //
 | ||
|   // Mount the given set of modules in the given DOM root, which ensures
 | ||
|   // that the CSS rules defined by the module are available in that
 | ||
|   // context.
 | ||
|   //
 | ||
|   // Rules are only added to the document once per root.
 | ||
|   //
 | ||
|   // Rule order will follow the order of the modules, so that rules from
 | ||
|   // modules later in the array take precedence of those from earlier
 | ||
|   // modules. If you call this function multiple times for the same root
 | ||
|   // in a way that changes the order of already mounted modules, the old
 | ||
|   // order will be changed.
 | ||
|   //
 | ||
|   // If a Content Security Policy nonce is provided, it is added to
 | ||
|   // the `<style>` tag generated by the library.
 | ||
|   static mount(root, modules, options) {
 | ||
|     let set = root[SET], nonce = options && options.nonce;
 | ||
|     if (!set) set = new StyleSet(root, nonce);
 | ||
|     else if (nonce) set.setNonce(nonce);
 | ||
|     set.mount(Array.isArray(modules) ? modules : [modules], root);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| let adoptedSet = new Map; //<Document, StyleSet>
 | ||
| 
 | ||
| class StyleSet {
 | ||
|   constructor(root, nonce) {
 | ||
|     let doc = root.ownerDocument || root, win = doc.defaultView;
 | ||
|     if (!root.head && root.adoptedStyleSheets && win.CSSStyleSheet) {
 | ||
|       let adopted = adoptedSet.get(doc);
 | ||
|       if (adopted) return root[SET] = adopted
 | ||
|       this.sheet = new win.CSSStyleSheet;
 | ||
|       adoptedSet.set(doc, this);
 | ||
|     } else {
 | ||
|       this.styleTag = doc.createElement("style");
 | ||
|       if (nonce) this.styleTag.setAttribute("nonce", nonce);
 | ||
|     }
 | ||
|     this.modules = [];
 | ||
|     root[SET] = this;
 | ||
|   }
 | ||
| 
 | ||
|   mount(modules, root) {
 | ||
|     let sheet = this.sheet;
 | ||
|     let pos = 0 /* Current rule offset */, j = 0; /* Index into this.modules */
 | ||
|     for (let i = 0; i < modules.length; i++) {
 | ||
|       let mod = modules[i], index = this.modules.indexOf(mod);
 | ||
|       if (index < j && index > -1) { // Ordering conflict
 | ||
|         this.modules.splice(index, 1);
 | ||
|         j--;
 | ||
|         index = -1;
 | ||
|       }
 | ||
|       if (index == -1) {
 | ||
|         this.modules.splice(j++, 0, mod);
 | ||
|         if (sheet) for (let k = 0; k < mod.rules.length; k++)
 | ||
|           sheet.insertRule(mod.rules[k], pos++);
 | ||
|       } else {
 | ||
|         while (j < index) pos += this.modules[j++].rules.length;
 | ||
|         pos += mod.rules.length;
 | ||
|         j++;
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     if (sheet) {
 | ||
|       if (root.adoptedStyleSheets.indexOf(this.sheet) < 0)
 | ||
|         root.adoptedStyleSheets = [this.sheet, ...root.adoptedStyleSheets];
 | ||
|     } else {
 | ||
|       let text = "";
 | ||
|       for (let i = 0; i < this.modules.length; i++)
 | ||
|         text += this.modules[i].getRules() + "\n";
 | ||
|       this.styleTag.textContent = text;
 | ||
|       let target = root.head || root;
 | ||
|       if (this.styleTag.parentNode != target)
 | ||
|         target.insertBefore(this.styleTag, target.firstChild);
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   setNonce(nonce) {
 | ||
|     if (this.styleTag && this.styleTag.getAttribute("nonce") != nonce)
 | ||
|       this.styleTag.setAttribute("nonce", nonce);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| // Style::Object<union<Style,string>>
 | ||
| //
 | ||
| // A style is an object that, in the simple case, maps CSS property
 | ||
| // names to strings holding their values, as in `{color: "red",
 | ||
| // fontWeight: "bold"}`. The property names can be given in
 | ||
| // camel-case—the library will insert a dash before capital letters
 | ||
| // when converting them to CSS.
 | ||
| //
 | ||
| // If you include an underscore in a property name, it and everything
 | ||
| // after it will be removed from the output, which can be useful when
 | ||
| // providing a property multiple times, for browser compatibility
 | ||
| // reasons.
 | ||
| //
 | ||
| // A property in a style object can also be a sub-selector, which
 | ||
| // extends the current context to add a pseudo-selector or a child
 | ||
| // selector. Such a property should contain a `&` character, which
 | ||
| // will be replaced by the current selector. For example `{"&:before":
 | ||
| // {content: '"hi"'}}`. Sub-selectors and regular properties can
 | ||
| // freely be mixed in a given object. Any property containing a `&` is
 | ||
| // assumed to be a sub-selector.
 | ||
| //
 | ||
| // Finally, a property can specify an @-block to be wrapped around the
 | ||
| // styles defined inside the object that's the property's value. For
 | ||
| // example to create a media query you can do `{"@media screen and
 | ||
| // (min-width: 400px)": {...}}`.
 | ||
| 
 | ||
| var _a;
 | ||
| /**
 | ||
| Node prop stored in a parser's top syntax node to provide the
 | ||
| facet that stores language-specific data for that language.
 | ||
| */
 | ||
| const languageDataProp = /*@__PURE__*/new NodeProp();
 | ||
| /**
 | ||
| Helper function to define a facet (to be added to the top syntax
 | ||
| node(s) for a language via
 | ||
| [`languageDataProp`](https://codemirror.net/6/docs/ref/#language.languageDataProp)), that will be
 | ||
| used to associate language data with the language. You
 | ||
| probably only need this when subclassing
 | ||
| [`Language`](https://codemirror.net/6/docs/ref/#language.Language).
 | ||
| */
 | ||
| function defineLanguageFacet(baseData) {
 | ||
|     return Facet.define({
 | ||
|         combine: baseData ? values => values.concat(baseData) : undefined
 | ||
|     });
 | ||
| }
 | ||
| /**
 | ||
| Syntax node prop used to register sublanguages. Should be added to
 | ||
| the top level node type for the language.
 | ||
| */
 | ||
| const sublanguageProp = /*@__PURE__*/new NodeProp();
 | ||
| /**
 | ||
| A language object manages parsing and per-language
 | ||
| [metadata](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt). Parse data is
 | ||
| managed as a [Lezer](https://lezer.codemirror.net) tree. The class
 | ||
| can be used directly, via the [`LRLanguage`](https://codemirror.net/6/docs/ref/#language.LRLanguage)
 | ||
| subclass for [Lezer](https://lezer.codemirror.net/) LR parsers, or
 | ||
| via the [`StreamLanguage`](https://codemirror.net/6/docs/ref/#language.StreamLanguage) subclass
 | ||
| for stream parsers.
 | ||
| */
 | ||
| class Language {
 | ||
|     /**
 | ||
|     Construct a language object. If you need to invoke this
 | ||
|     directly, first define a data facet with
 | ||
|     [`defineLanguageFacet`](https://codemirror.net/6/docs/ref/#language.defineLanguageFacet), and then
 | ||
|     configure your parser to [attach](https://codemirror.net/6/docs/ref/#language.languageDataProp) it
 | ||
|     to the language's outer syntax node.
 | ||
|     */
 | ||
|     constructor(
 | ||
|     /**
 | ||
|     The [language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) facet
 | ||
|     used for this language.
 | ||
|     */
 | ||
|     data, parser, extraExtensions = [], 
 | ||
|     /**
 | ||
|     A language name.
 | ||
|     */
 | ||
|     name = "") {
 | ||
|         this.data = data;
 | ||
|         this.name = name;
 | ||
|         // Kludge to define EditorState.tree as a debugging helper,
 | ||
|         // without the EditorState package actually knowing about
 | ||
|         // languages and lezer trees.
 | ||
|         if (!EditorState.prototype.hasOwnProperty("tree"))
 | ||
|             Object.defineProperty(EditorState.prototype, "tree", { get() { return syntaxTree(this); } });
 | ||
|         this.parser = parser;
 | ||
|         this.extension = [
 | ||
|             language.of(this),
 | ||
|             EditorState.languageData.of((state, pos, side) => {
 | ||
|                 let top = topNodeAt(state, pos, side), data = top.type.prop(languageDataProp);
 | ||
|                 if (!data)
 | ||
|                     return [];
 | ||
|                 let base = state.facet(data), sub = top.type.prop(sublanguageProp);
 | ||
|                 if (sub) {
 | ||
|                     let innerNode = top.resolve(pos - top.from, side);
 | ||
|                     for (let sublang of sub)
 | ||
|                         if (sublang.test(innerNode, state)) {
 | ||
|                             let data = state.facet(sublang.facet);
 | ||
|                             return sublang.type == "replace" ? data : data.concat(base);
 | ||
|                         }
 | ||
|                 }
 | ||
|                 return base;
 | ||
|             })
 | ||
|         ].concat(extraExtensions);
 | ||
|     }
 | ||
|     /**
 | ||
|     Query whether this language is active at the given position.
 | ||
|     */
 | ||
|     isActiveAt(state, pos, side = -1) {
 | ||
|         return topNodeAt(state, pos, side).type.prop(languageDataProp) == this.data;
 | ||
|     }
 | ||
|     /**
 | ||
|     Find the document regions that were parsed using this language.
 | ||
|     The returned regions will _include_ any nested languages rooted
 | ||
|     in this language, when those exist.
 | ||
|     */
 | ||
|     findRegions(state) {
 | ||
|         let lang = state.facet(language);
 | ||
|         if ((lang === null || lang === void 0 ? void 0 : lang.data) == this.data)
 | ||
|             return [{ from: 0, to: state.doc.length }];
 | ||
|         if (!lang || !lang.allowsNesting)
 | ||
|             return [];
 | ||
|         let result = [];
 | ||
|         let explore = (tree, from) => {
 | ||
|             if (tree.prop(languageDataProp) == this.data) {
 | ||
|                 result.push({ from, to: from + tree.length });
 | ||
|                 return;
 | ||
|             }
 | ||
|             let mount = tree.prop(NodeProp.mounted);
 | ||
|             if (mount) {
 | ||
|                 if (mount.tree.prop(languageDataProp) == this.data) {
 | ||
|                     if (mount.overlay)
 | ||
|                         for (let r of mount.overlay)
 | ||
|                             result.push({ from: r.from + from, to: r.to + from });
 | ||
|                     else
 | ||
|                         result.push({ from: from, to: from + tree.length });
 | ||
|                     return;
 | ||
|                 }
 | ||
|                 else if (mount.overlay) {
 | ||
|                     let size = result.length;
 | ||
|                     explore(mount.tree, mount.overlay[0].from + from);
 | ||
|                     if (result.length > size)
 | ||
|                         return;
 | ||
|                 }
 | ||
|             }
 | ||
|             for (let i = 0; i < tree.children.length; i++) {
 | ||
|                 let ch = tree.children[i];
 | ||
|                 if (ch instanceof Tree)
 | ||
|                     explore(ch, tree.positions[i] + from);
 | ||
|             }
 | ||
|         };
 | ||
|         explore(syntaxTree(state), 0);
 | ||
|         return result;
 | ||
|     }
 | ||
|     /**
 | ||
|     Indicates whether this language allows nested languages. The
 | ||
|     default implementation returns true.
 | ||
|     */
 | ||
|     get allowsNesting() { return true; }
 | ||
| }
 | ||
| /**
 | ||
| @internal
 | ||
| */
 | ||
| Language.setState = /*@__PURE__*/StateEffect.define();
 | ||
| function topNodeAt(state, pos, side) {
 | ||
|     let topLang = state.facet(language), tree = syntaxTree(state).topNode;
 | ||
|     if (!topLang || topLang.allowsNesting) {
 | ||
|         for (let node = tree; node; node = node.enter(pos, side, IterMode.ExcludeBuffers))
 | ||
|             if (node.type.isTop)
 | ||
|                 tree = node;
 | ||
|     }
 | ||
|     return tree;
 | ||
| }
 | ||
| /**
 | ||
| A subclass of [`Language`](https://codemirror.net/6/docs/ref/#language.Language) for use with Lezer
 | ||
| [LR parsers](https://lezer.codemirror.net/docs/ref#lr.LRParser)
 | ||
| parsers.
 | ||
| */
 | ||
| class LRLanguage extends Language {
 | ||
|     constructor(data, parser, name) {
 | ||
|         super(data, parser, [], name);
 | ||
|         this.parser = parser;
 | ||
|     }
 | ||
|     /**
 | ||
|     Define a language from a parser.
 | ||
|     */
 | ||
|     static define(spec) {
 | ||
|         let data = defineLanguageFacet(spec.languageData);
 | ||
|         return new LRLanguage(data, spec.parser.configure({
 | ||
|             props: [languageDataProp.add(type => type.isTop ? data : undefined)]
 | ||
|         }), spec.name);
 | ||
|     }
 | ||
|     /**
 | ||
|     Create a new instance of this language with a reconfigured
 | ||
|     version of its parser and optionally a new name.
 | ||
|     */
 | ||
|     configure(options, name) {
 | ||
|         return new LRLanguage(this.data, this.parser.configure(options), name || this.name);
 | ||
|     }
 | ||
|     get allowsNesting() { return this.parser.hasWrappers(); }
 | ||
| }
 | ||
| /**
 | ||
| Get the syntax tree for a state, which is the current (possibly
 | ||
| incomplete) parse tree of the active
 | ||
| [language](https://codemirror.net/6/docs/ref/#language.Language), or the empty tree if there is no
 | ||
| language available.
 | ||
| */
 | ||
| function syntaxTree(state) {
 | ||
|     let field = state.field(Language.state, false);
 | ||
|     return field ? field.tree : Tree.empty;
 | ||
| }
 | ||
| /**
 | ||
| Try to get a parse tree that spans at least up to `upto`. The
 | ||
| method will do at most `timeout` milliseconds of work to parse
 | ||
| up to that point if the tree isn't already available.
 | ||
| */
 | ||
| function ensureSyntaxTree(state, upto, timeout = 50) {
 | ||
|     var _a;
 | ||
|     let parse = (_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context;
 | ||
|     if (!parse)
 | ||
|         return null;
 | ||
|     let oldVieport = parse.viewport;
 | ||
|     parse.updateViewport({ from: 0, to: upto });
 | ||
|     let result = parse.isDone(upto) || parse.work(timeout, upto) ? parse.tree : null;
 | ||
|     parse.updateViewport(oldVieport);
 | ||
|     return result;
 | ||
| }
 | ||
| /**
 | ||
| Queries whether there is a full syntax tree available up to the
 | ||
| given document position. If there isn't, the background parse
 | ||
| process _might_ still be working and update the tree further, but
 | ||
| there is no guarantee of that—the parser will [stop
 | ||
| working](https://codemirror.net/6/docs/ref/#language.syntaxParserRunning) when it has spent a
 | ||
| certain amount of time or has moved beyond the visible viewport.
 | ||
| Always returns false if no language has been enabled.
 | ||
| */
 | ||
| function syntaxTreeAvailable(state, upto = state.doc.length) {
 | ||
|     var _a;
 | ||
|     return ((_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context.isDone(upto)) || false;
 | ||
| }
 | ||
| /**
 | ||
| Move parsing forward, and update the editor state afterwards to
 | ||
| reflect the new tree. Will work for at most `timeout`
 | ||
| milliseconds. Returns true if the parser managed get to the given
 | ||
| position in that time.
 | ||
| */
 | ||
| function forceParsing(view, upto = view.viewport.to, timeout = 100) {
 | ||
|     let success = ensureSyntaxTree(view.state, upto, timeout);
 | ||
|     if (success != syntaxTree(view.state))
 | ||
|         view.dispatch({});
 | ||
|     return !!success;
 | ||
| }
 | ||
| /**
 | ||
| Tells you whether the language parser is planning to do more
 | ||
| parsing work (in a `requestIdleCallback` pseudo-thread) or has
 | ||
| stopped running, either because it parsed the entire document,
 | ||
| because it spent too much time and was cut off, or because there
 | ||
| is no language parser enabled.
 | ||
| */
 | ||
| function syntaxParserRunning(view) {
 | ||
|     var _a;
 | ||
|     return ((_a = view.plugin(parseWorker)) === null || _a === void 0 ? void 0 : _a.isWorking()) || false;
 | ||
| }
 | ||
| /**
 | ||
| Lezer-style
 | ||
| [`Input`](https://lezer.codemirror.net/docs/ref#common.Input)
 | ||
| object for a [`Text`](https://codemirror.net/6/docs/ref/#state.Text) object.
 | ||
| */
 | ||
| class DocInput {
 | ||
|     /**
 | ||
|     Create an input object for the given document.
 | ||
|     */
 | ||
|     constructor(doc) {
 | ||
|         this.doc = doc;
 | ||
|         this.cursorPos = 0;
 | ||
|         this.string = "";
 | ||
|         this.cursor = doc.iter();
 | ||
|     }
 | ||
|     get length() { return this.doc.length; }
 | ||
|     syncTo(pos) {
 | ||
|         this.string = this.cursor.next(pos - this.cursorPos).value;
 | ||
|         this.cursorPos = pos + this.string.length;
 | ||
|         return this.cursorPos - this.string.length;
 | ||
|     }
 | ||
|     chunk(pos) {
 | ||
|         this.syncTo(pos);
 | ||
|         return this.string;
 | ||
|     }
 | ||
|     get lineChunks() { return true; }
 | ||
|     read(from, to) {
 | ||
|         let stringStart = this.cursorPos - this.string.length;
 | ||
|         if (from < stringStart || to >= this.cursorPos)
 | ||
|             return this.doc.sliceString(from, to);
 | ||
|         else
 | ||
|             return this.string.slice(from - stringStart, to - stringStart);
 | ||
|     }
 | ||
| }
 | ||
| let currentContext = null;
 | ||
| /**
 | ||
| A parse context provided to parsers working on the editor content.
 | ||
| */
 | ||
| class ParseContext {
 | ||
|     constructor(parser, 
 | ||
|     /**
 | ||
|     The current editor state.
 | ||
|     */
 | ||
|     state, 
 | ||
|     /**
 | ||
|     Tree fragments that can be reused by incremental re-parses.
 | ||
|     */
 | ||
|     fragments = [], 
 | ||
|     /**
 | ||
|     @internal
 | ||
|     */
 | ||
|     tree, 
 | ||
|     /**
 | ||
|     @internal
 | ||
|     */
 | ||
|     treeLen, 
 | ||
|     /**
 | ||
|     The current editor viewport (or some overapproximation
 | ||
|     thereof). Intended to be used for opportunistically avoiding
 | ||
|     work (in which case
 | ||
|     [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.ParseContext.skipUntilInView)
 | ||
|     should be called to make sure the parser is restarted when the
 | ||
|     skipped region becomes visible).
 | ||
|     */
 | ||
|     viewport, 
 | ||
|     /**
 | ||
|     @internal
 | ||
|     */
 | ||
|     skipped, 
 | ||
|     /**
 | ||
|     This is where skipping parsers can register a promise that,
 | ||
|     when resolved, will schedule a new parse. It is cleared when
 | ||
|     the parse worker picks up the promise. @internal
 | ||
|     */
 | ||
|     scheduleOn) {
 | ||
|         this.parser = parser;
 | ||
|         this.state = state;
 | ||
|         this.fragments = fragments;
 | ||
|         this.tree = tree;
 | ||
|         this.treeLen = treeLen;
 | ||
|         this.viewport = viewport;
 | ||
|         this.skipped = skipped;
 | ||
|         this.scheduleOn = scheduleOn;
 | ||
|         this.parse = null;
 | ||
|         /**
 | ||
|         @internal
 | ||
|         */
 | ||
|         this.tempSkipped = [];
 | ||
|     }
 | ||
|     /**
 | ||
|     @internal
 | ||
|     */
 | ||
|     static create(parser, state, viewport) {
 | ||
|         return new ParseContext(parser, state, [], Tree.empty, 0, viewport, [], null);
 | ||
|     }
 | ||
|     startParse() {
 | ||
|         return this.parser.startParse(new DocInput(this.state.doc), this.fragments);
 | ||
|     }
 | ||
|     /**
 | ||
|     @internal
 | ||
|     */
 | ||
|     work(until, upto) {
 | ||
|         if (upto != null && upto >= this.state.doc.length)
 | ||
|             upto = undefined;
 | ||
|         if (this.tree != Tree.empty && this.isDone(upto !== null && upto !== void 0 ? upto : this.state.doc.length)) {
 | ||
|             this.takeTree();
 | ||
|             return true;
 | ||
|         }
 | ||
|         return this.withContext(() => {
 | ||
|             var _a;
 | ||
|             if (typeof until == "number") {
 | ||
|                 let endTime = Date.now() + until;
 | ||
|                 until = () => Date.now() > endTime;
 | ||
|             }
 | ||
|             if (!this.parse)
 | ||
|                 this.parse = this.startParse();
 | ||
|             if (upto != null && (this.parse.stoppedAt == null || this.parse.stoppedAt > upto) &&
 | ||
|                 upto < this.state.doc.length)
 | ||
|                 this.parse.stopAt(upto);
 | ||
|             for (;;) {
 | ||
|                 let done = this.parse.advance();
 | ||
|                 if (done) {
 | ||
|                     this.fragments = this.withoutTempSkipped(TreeFragment.addTree(done, this.fragments, this.parse.stoppedAt != null));
 | ||
|                     this.treeLen = (_a = this.parse.stoppedAt) !== null && _a !== void 0 ? _a : this.state.doc.length;
 | ||
|                     this.tree = done;
 | ||
|                     this.parse = null;
 | ||
|                     if (this.treeLen < (upto !== null && upto !== void 0 ? upto : this.state.doc.length))
 | ||
|                         this.parse = this.startParse();
 | ||
|                     else
 | ||
|                         return true;
 | ||
|                 }
 | ||
|                 if (until())
 | ||
|                     return false;
 | ||
|             }
 | ||
|         });
 | ||
|     }
 | ||
|     /**
 | ||
|     @internal
 | ||
|     */
 | ||
|     takeTree() {
 | ||
|         let pos, tree;
 | ||
|         if (this.parse && (pos = this.parse.parsedPos) >= this.treeLen) {
 | ||
|             if (this.parse.stoppedAt == null || this.parse.stoppedAt > pos)
 | ||
|                 this.parse.stopAt(pos);
 | ||
|             this.withContext(() => { while (!(tree = this.parse.advance())) { } });
 | ||
|             this.treeLen = pos;
 | ||
|             this.tree = tree;
 | ||
|             this.fragments = this.withoutTempSkipped(TreeFragment.addTree(this.tree, this.fragments, true));
 | ||
|             this.parse = null;
 | ||
|         }
 | ||
|     }
 | ||
|     withContext(f) {
 | ||
|         let prev = currentContext;
 | ||
|         currentContext = this;
 | ||
|         try {
 | ||
|             return f();
 | ||
|         }
 | ||
|         finally {
 | ||
|             currentContext = prev;
 | ||
|         }
 | ||
|     }
 | ||
|     withoutTempSkipped(fragments) {
 | ||
|         for (let r; r = this.tempSkipped.pop();)
 | ||
|             fragments = cutFragments(fragments, r.from, r.to);
 | ||
|         return fragments;
 | ||
|     }
 | ||
|     /**
 | ||
|     @internal
 | ||
|     */
 | ||
|     changes(changes, newState) {
 | ||
|         let { fragments, tree, treeLen, viewport, skipped } = this;
 | ||
|         this.takeTree();
 | ||
|         if (!changes.empty) {
 | ||
|             let ranges = [];
 | ||
|             changes.iterChangedRanges((fromA, toA, fromB, toB) => ranges.push({ fromA, toA, fromB, toB }));
 | ||
|             fragments = TreeFragment.applyChanges(fragments, ranges);
 | ||
|             tree = Tree.empty;
 | ||
|             treeLen = 0;
 | ||
|             viewport = { from: changes.mapPos(viewport.from, -1), to: changes.mapPos(viewport.to, 1) };
 | ||
|             if (this.skipped.length) {
 | ||
|                 skipped = [];
 | ||
|                 for (let r of this.skipped) {
 | ||
|                     let from = changes.mapPos(r.from, 1), to = changes.mapPos(r.to, -1);
 | ||
|                     if (from < to)
 | ||
|                         skipped.push({ from, to });
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
|         return new ParseContext(this.parser, newState, fragments, tree, treeLen, viewport, skipped, this.scheduleOn);
 | ||
|     }
 | ||
|     /**
 | ||
|     @internal
 | ||
|     */
 | ||
|     updateViewport(viewport) {
 | ||
|         if (this.viewport.from == viewport.from && this.viewport.to == viewport.to)
 | ||
|             return false;
 | ||
|         this.viewport = viewport;
 | ||
|         let startLen = this.skipped.length;
 | ||
|         for (let i = 0; i < this.skipped.length; i++) {
 | ||
|             let { from, to } = this.skipped[i];
 | ||
|             if (from < viewport.to && to > viewport.from) {
 | ||
|                 this.fragments = cutFragments(this.fragments, from, to);
 | ||
|                 this.skipped.splice(i--, 1);
 | ||
|             }
 | ||
|         }
 | ||
|         if (this.skipped.length >= startLen)
 | ||
|             return false;
 | ||
|         this.reset();
 | ||
|         return true;
 | ||
|     }
 | ||
|     /**
 | ||
|     @internal
 | ||
|     */
 | ||
|     reset() {
 | ||
|         if (this.parse) {
 | ||
|             this.takeTree();
 | ||
|             this.parse = null;
 | ||
|         }
 | ||
|     }
 | ||
|     /**
 | ||
|     Notify the parse scheduler that the given region was skipped
 | ||
|     because it wasn't in view, and the parse should be restarted
 | ||
|     when it comes into view.
 | ||
|     */
 | ||
|     skipUntilInView(from, to) {
 | ||
|         this.skipped.push({ from, to });
 | ||
|     }
 | ||
|     /**
 | ||
|     Returns a parser intended to be used as placeholder when
 | ||
|     asynchronously loading a nested parser. It'll skip its input and
 | ||
|     mark it as not-really-parsed, so that the next update will parse
 | ||
|     it again.
 | ||
|     
 | ||
|     When `until` is given, a reparse will be scheduled when that
 | ||
|     promise resolves.
 | ||
|     */
 | ||
|     static getSkippingParser(until) {
 | ||
|         return new class extends Parser {
 | ||
|             createParse(input, fragments, ranges) {
 | ||
|                 let from = ranges[0].from, to = ranges[ranges.length - 1].to;
 | ||
|                 let parser = {
 | ||
|                     parsedPos: from,
 | ||
|                     advance() {
 | ||
|                         let cx = currentContext;
 | ||
|                         if (cx) {
 | ||
|                             for (let r of ranges)
 | ||
|                                 cx.tempSkipped.push(r);
 | ||
|                             if (until)
 | ||
|                                 cx.scheduleOn = cx.scheduleOn ? Promise.all([cx.scheduleOn, until]) : until;
 | ||
|                         }
 | ||
|                         this.parsedPos = to;
 | ||
|                         return new Tree(NodeType.none, [], [], to - from);
 | ||
|                     },
 | ||
|                     stoppedAt: null,
 | ||
|                     stopAt() { }
 | ||
|                 };
 | ||
|                 return parser;
 | ||
|             }
 | ||
|         };
 | ||
|     }
 | ||
|     /**
 | ||
|     @internal
 | ||
|     */
 | ||
|     isDone(upto) {
 | ||
|         upto = Math.min(upto, this.state.doc.length);
 | ||
|         let frags = this.fragments;
 | ||
|         return this.treeLen >= upto && frags.length && frags[0].from == 0 && frags[0].to >= upto;
 | ||
|     }
 | ||
|     /**
 | ||
|     Get the context for the current parse, or `null` if no editor
 | ||
|     parse is in progress.
 | ||
|     */
 | ||
|     static get() { return currentContext; }
 | ||
| }
 | ||
| function cutFragments(fragments, from, to) {
 | ||
|     return TreeFragment.applyChanges(fragments, [{ fromA: from, toA: to, fromB: from, toB: to }]);
 | ||
| }
 | ||
| class LanguageState {
 | ||
|     constructor(
 | ||
|     // A mutable parse state that is used to preserve work done during
 | ||
|     // the lifetime of a state when moving to the next state.
 | ||
|     context) {
 | ||
|         this.context = context;
 | ||
|         this.tree = context.tree;
 | ||
|     }
 | ||
|     apply(tr) {
 | ||
|         if (!tr.docChanged && this.tree == this.context.tree)
 | ||
|             return this;
 | ||
|         let newCx = this.context.changes(tr.changes, tr.state);
 | ||
|         // If the previous parse wasn't done, go forward only up to its
 | ||
|         // end position or the end of the viewport, to avoid slowing down
 | ||
|         // state updates with parse work beyond the viewport.
 | ||
|         let upto = this.context.treeLen == tr.startState.doc.length ? undefined
 | ||
|             : Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);
 | ||
|         if (!newCx.work(20 /* Work.Apply */, upto))
 | ||
|             newCx.takeTree();
 | ||
|         return new LanguageState(newCx);
 | ||
|     }
 | ||
|     static init(state) {
 | ||
|         let vpTo = Math.min(3000 /* Work.InitViewport */, state.doc.length);
 | ||
|         let parseState = ParseContext.create(state.facet(language).parser, state, { from: 0, to: vpTo });
 | ||
|         if (!parseState.work(20 /* Work.Apply */, vpTo))
 | ||
|             parseState.takeTree();
 | ||
|         return new LanguageState(parseState);
 | ||
|     }
 | ||
| }
 | ||
| Language.state = /*@__PURE__*/StateField.define({
 | ||
|     create: LanguageState.init,
 | ||
|     update(value, tr) {
 | ||
|         for (let e of tr.effects)
 | ||
|             if (e.is(Language.setState))
 | ||
|                 return e.value;
 | ||
|         if (tr.startState.facet(language) != tr.state.facet(language))
 | ||
|             return LanguageState.init(tr.state);
 | ||
|         return value.apply(tr);
 | ||
|     }
 | ||
| });
 | ||
| let requestIdle = (callback) => {
 | ||
|     let timeout = setTimeout(() => callback(), 500 /* Work.MaxPause */);
 | ||
|     return () => clearTimeout(timeout);
 | ||
| };
 | ||
| if (typeof requestIdleCallback != "undefined")
 | ||
|     requestIdle = (callback) => {
 | ||
|         let idle = -1, timeout = setTimeout(() => {
 | ||
|             idle = requestIdleCallback(callback, { timeout: 500 /* Work.MaxPause */ - 100 /* Work.MinPause */ });
 | ||
|         }, 100 /* Work.MinPause */);
 | ||
|         return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);
 | ||
|     };
 | ||
| const isInputPending = typeof navigator != "undefined" && ((_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending)
 | ||
|     ? () => navigator.scheduling.isInputPending() : null;
 | ||
| const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
 | ||
|     constructor(view) {
 | ||
|         this.view = view;
 | ||
|         this.working = null;
 | ||
|         this.workScheduled = 0;
 | ||
|         // End of the current time chunk
 | ||
|         this.chunkEnd = -1;
 | ||
|         // Milliseconds of budget left for this chunk
 | ||
|         this.chunkBudget = -1;
 | ||
|         this.work = this.work.bind(this);
 | ||
|         this.scheduleWork();
 | ||
|     }
 | ||
|     update(update) {
 | ||
|         let cx = this.view.state.field(Language.state).context;
 | ||
|         if (cx.updateViewport(update.view.viewport) || this.view.viewport.to > cx.treeLen)
 | ||
|             this.scheduleWork();
 | ||
|         if (update.docChanged || update.selectionSet) {
 | ||
|             if (this.view.hasFocus)
 | ||
|                 this.chunkBudget += 50 /* Work.ChangeBonus */;
 | ||
|             this.scheduleWork();
 | ||
|         }
 | ||
|         this.checkAsyncSchedule(cx);
 | ||
|     }
 | ||
|     scheduleWork() {
 | ||
|         if (this.working)
 | ||
|             return;
 | ||
|         let { state } = this.view, field = state.field(Language.state);
 | ||
|         if (field.tree != field.context.tree || !field.context.isDone(state.doc.length))
 | ||
|             this.working = requestIdle(this.work);
 | ||
|     }
 | ||
|     work(deadline) {
 | ||
|         this.working = null;
 | ||
|         let now = Date.now();
 | ||
|         if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk
 | ||
|             this.chunkEnd = now + 30000 /* Work.ChunkTime */;
 | ||
|             this.chunkBudget = 3000 /* Work.ChunkBudget */;
 | ||
|         }
 | ||
|         if (this.chunkBudget <= 0)
 | ||
|             return; // No more budget
 | ||
|         let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
 | ||
|         if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* Work.MaxParseAhead */))
 | ||
|             return;
 | ||
|         let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Work.Slice */, deadline && !isInputPending ? Math.max(25 /* Work.MinSlice */, deadline.timeRemaining() - 5) : 1e9);
 | ||
|         let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1000;
 | ||
|         let done = field.context.work(() => {
 | ||
|             return isInputPending && isInputPending() || Date.now() > endTime;
 | ||
|         }, vpTo + (viewportFirst ? 0 : 100000 /* Work.MaxParseAhead */));
 | ||
|         this.chunkBudget -= Date.now() - now;
 | ||
|         if (done || this.chunkBudget <= 0) {
 | ||
|             field.context.takeTree();
 | ||
|             this.view.dispatch({ effects: Language.setState.of(new LanguageState(field.context)) });
 | ||
|         }
 | ||
|         if (this.chunkBudget > 0 && !(done && !viewportFirst))
 | ||
|             this.scheduleWork();
 | ||
|         this.checkAsyncSchedule(field.context);
 | ||
|     }
 | ||
|     checkAsyncSchedule(cx) {
 | ||
|         if (cx.scheduleOn) {
 | ||
|             this.workScheduled++;
 | ||
|             cx.scheduleOn
 | ||
|                 .then(() => this.scheduleWork())
 | ||
|                 .catch(err => logException(this.view.state, err))
 | ||
|                 .then(() => this.workScheduled--);
 | ||
|             cx.scheduleOn = null;
 | ||
|         }
 | ||
|     }
 | ||
|     destroy() {
 | ||
|         if (this.working)
 | ||
|             this.working();
 | ||
|     }
 | ||
|     isWorking() {
 | ||
|         return !!(this.working || this.workScheduled > 0);
 | ||
|     }
 | ||
| }, {
 | ||
|     eventHandlers: { focus() { this.scheduleWork(); } }
 | ||
| });
 | ||
| /**
 | ||
| The facet used to associate a language with an editor state. Used
 | ||
| by `Language` object's `extension` property (so you don't need to
 | ||
| manually wrap your languages in this). Can be used to access the
 | ||
| current language on a state.
 | ||
| */
 | ||
| const language = /*@__PURE__*/Facet.define({
 | ||
|     combine(languages) { return languages.length ? languages[0] : null; },
 | ||
|     enables: language => [
 | ||
|         Language.state,
 | ||
|         parseWorker,
 | ||
|         EditorView.contentAttributes.compute([language], state => {
 | ||
|             let lang = state.facet(language);
 | ||
|             return lang && lang.name ? { "data-language": lang.name } : {};
 | ||
|         })
 | ||
|     ]
 | ||
| });
 | ||
| /**
 | ||
| This class bundles a [language](https://codemirror.net/6/docs/ref/#language.Language) with an
 | ||
| optional set of supporting extensions. Language packages are
 | ||
| encouraged to export a function that optionally takes a
 | ||
| configuration object and returns a `LanguageSupport` instance, as
 | ||
| the main way for client code to use the package.
 | ||
| */
 | ||
| class LanguageSupport {
 | ||
|     /**
 | ||
|     Create a language support object.
 | ||
|     */
 | ||
|     constructor(
 | ||
|     /**
 | ||
|     The language object.
 | ||
|     */
 | ||
|     language, 
 | ||
|     /**
 | ||
|     An optional set of supporting extensions. When nesting a
 | ||
|     language in another language, the outer language is encouraged
 | ||
|     to include the supporting extensions for its inner languages
 | ||
|     in its own set of support extensions.
 | ||
|     */
 | ||
|     support = []) {
 | ||
|         this.language = language;
 | ||
|         this.support = support;
 | ||
|         this.extension = [language, support];
 | ||
|     }
 | ||
| }
 | ||
| /**
 | ||
| Language descriptions are used to store metadata about languages
 | ||
| and to dynamically load them. Their main role is finding the
 | ||
| appropriate language for a filename or dynamically loading nested
 | ||
| parsers.
 | ||
| */
 | ||
| class LanguageDescription {
 | ||
|     constructor(
 | ||
|     /**
 | ||
|     The name of this language.
 | ||
|     */
 | ||
|     name, 
 | ||
|     /**
 | ||
|     Alternative names for the mode (lowercased, includes `this.name`).
 | ||
|     */
 | ||
|     alias, 
 | ||
|     /**
 | ||
|     File extensions associated with this language.
 | ||
|     */
 | ||
|     extensions, 
 | ||
|     /**
 | ||
|     Optional filename pattern that should be associated with this
 | ||
|     language.
 | ||
|     */
 | ||
|     filename, loadFunc, 
 | ||
|     /**
 | ||
|     If the language has been loaded, this will hold its value.
 | ||
|     */
 | ||
|     support = undefined) {
 | ||
|         this.name = name;
 | ||
|         this.alias = alias;
 | ||
|         this.extensions = extensions;
 | ||
|         this.filename = filename;
 | ||
|         this.loadFunc = loadFunc;
 | ||
|         this.support = support;
 | ||
|         this.loading = null;
 | ||
|     }
 | ||
|     /**
 | ||
|     Start loading the the language. Will return a promise that
 | ||
|     resolves to a [`LanguageSupport`](https://codemirror.net/6/docs/ref/#language.LanguageSupport)
 | ||
|     object when the language successfully loads.
 | ||
|     */
 | ||
|     load() {
 | ||
|         return this.loading || (this.loading = this.loadFunc().then(support => this.support = support, err => { this.loading = null; throw err; }));
 | ||
|     }
 | ||
|     /**
 | ||
|     Create a language description.
 | ||
|     */
 | ||
|     static of(spec) {
 | ||
|         let { load, support } = spec;
 | ||
|         if (!load) {
 | ||
|             if (!support)
 | ||
|                 throw new RangeError("Must pass either 'load' or 'support' to LanguageDescription.of");
 | ||
|             load = () => Promise.resolve(support);
 | ||
|         }
 | ||
|         return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, load, support);
 | ||
|     }
 | ||
|     /**
 | ||
|     Look for a language in the given array of descriptions that
 | ||
|     matches the filename. Will first match
 | ||
|     [`filename`](https://codemirror.net/6/docs/ref/#language.LanguageDescription.filename) patterns,
 | ||
|     and then [extensions](https://codemirror.net/6/docs/ref/#language.LanguageDescription.extensions),
 | ||
|     and return the first language that matches.
 | ||
|     */
 | ||
|     static matchFilename(descs, filename) {
 | ||
|         for (let d of descs)
 | ||
|             if (d.filename && d.filename.test(filename))
 | ||
|                 return d;
 | ||
|         let ext = /\.([^.]+)$/.exec(filename);
 | ||
|         if (ext)
 | ||
|             for (let d of descs)
 | ||
|                 if (d.extensions.indexOf(ext[1]) > -1)
 | ||
|                     return d;
 | ||
|         return null;
 | ||
|     }
 | ||
|     /**
 | ||
|     Look for a language whose name or alias matches the the given
 | ||
|     name (case-insensitively). If `fuzzy` is true, and no direct
 | ||
|     matchs is found, this'll also search for a language whose name
 | ||
|     or alias occurs in the string (for names shorter than three
 | ||
|     characters, only when surrounded by non-word characters).
 | ||
|     */
 | ||
|     static matchLanguageName(descs, name, fuzzy = true) {
 | ||
|         name = name.toLowerCase();
 | ||
|         for (let d of descs)
 | ||
|             if (d.alias.some(a => a == name))
 | ||
|                 return d;
 | ||
|         if (fuzzy)
 | ||
|             for (let d of descs)
 | ||
|                 for (let a of d.alias) {
 | ||
|                     let found = name.indexOf(a);
 | ||
|                     if (found > -1 && (a.length > 2 || !/\w/.test(name[found - 1]) && !/\w/.test(name[found + a.length])))
 | ||
|                         return d;
 | ||
|                 }
 | ||
|         return null;
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
| Facet that defines a way to provide a function that computes the
 | ||
| appropriate indentation depth, as a column number (see
 | ||
| [`indentString`](https://codemirror.net/6/docs/ref/#language.indentString)), at the start of a given
 | ||
| line. A return value of `null` indicates no indentation can be
 | ||
| determined, and the line should inherit the indentation of the one
 | ||
| above it. A return value of `undefined` defers to the next indent
 | ||
| service.
 | ||
| */
 | ||
| const indentService = /*@__PURE__*/Facet.define();
 | ||
| /**
 | ||
| Facet for overriding the unit by which indentation happens. Should
 | ||
| be a string consisting either entirely of the same whitespace
 | ||
| character. When not set, this defaults to 2 spaces.
 | ||
| */
 | ||
| const indentUnit = /*@__PURE__*/Facet.define({
 | ||
|     combine: values => {
 | ||
|         if (!values.length)
 | ||
|             return "  ";
 | ||
|         let unit = values[0];
 | ||
|         if (!unit || /\S/.test(unit) || Array.from(unit).some(e => e != unit[0]))
 | ||
|             throw new Error("Invalid indent unit: " + JSON.stringify(values[0]));
 | ||
|         return unit;
 | ||
|     }
 | ||
| });
 | ||
| /**
 | ||
| Return the _column width_ of an indent unit in the state.
 | ||
| Determined by the [`indentUnit`](https://codemirror.net/6/docs/ref/#language.indentUnit)
 | ||
| facet, and [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) when that
 | ||
| contains tabs.
 | ||
| */
 | ||
| function getIndentUnit(state) {
 | ||
|     let unit = state.facet(indentUnit);
 | ||
|     return unit.charCodeAt(0) == 9 ? state.tabSize * unit.length : unit.length;
 | ||
| }
 | ||
| /**
 | ||
| Create an indentation string that covers columns 0 to `cols`.
 | ||
| Will use tabs for as much of the columns as possible when the
 | ||
| [`indentUnit`](https://codemirror.net/6/docs/ref/#language.indentUnit) facet contains
 | ||
| tabs.
 | ||
| */
 | ||
| function indentString(state, cols) {
 | ||
|     let result = "", ts = state.tabSize, ch = state.facet(indentUnit)[0];
 | ||
|     if (ch == "\t") {
 | ||
|         while (cols >= ts) {
 | ||
|             result += "\t";
 | ||
|             cols -= ts;
 | ||
|         }
 | ||
|         ch = " ";
 | ||
|     }
 | ||
|     for (let i = 0; i < cols; i++)
 | ||
|         result += ch;
 | ||
|     return result;
 | ||
| }
 | ||
| /**
 | ||
| Get the indentation, as a column number, at the given position.
 | ||
| Will first consult any [indent services](https://codemirror.net/6/docs/ref/#language.indentService)
 | ||
| that are registered, and if none of those return an indentation,
 | ||
| this will check the syntax tree for the [indent node
 | ||
| prop](https://codemirror.net/6/docs/ref/#language.indentNodeProp) and use that if found. Returns a
 | ||
| number when an indentation could be determined, and null
 | ||
| otherwise.
 | ||
| */
 | ||
| function getIndentation(context, pos) {
 | ||
|     if (context instanceof EditorState)
 | ||
|         context = new IndentContext(context);
 | ||
|     for (let service of context.state.facet(indentService)) {
 | ||
|         let result = service(context, pos);
 | ||
|         if (result !== undefined)
 | ||
|             return result;
 | ||
|     }
 | ||
|     let tree = syntaxTree(context.state);
 | ||
|     return tree.length >= pos ? syntaxIndentation(context, tree, pos) : null;
 | ||
| }
 | ||
| /**
 | ||
| Create a change set that auto-indents all lines touched by the
 | ||
| given document range.
 | ||
| */
 | ||
| function indentRange(state, from, to) {
 | ||
|     let updated = Object.create(null);
 | ||
|     let context = new IndentContext(state, { overrideIndentation: start => { var _a; return (_a = updated[start]) !== null && _a !== void 0 ? _a : -1; } });
 | ||
|     let changes = [];
 | ||
|     for (let pos = from; pos <= to;) {
 | ||
|         let line = state.doc.lineAt(pos);
 | ||
|         pos = line.to + 1;
 | ||
|         let indent = getIndentation(context, line.from);
 | ||
|         if (indent == null)
 | ||
|             continue;
 | ||
|         if (!/\S/.test(line.text))
 | ||
|             indent = 0;
 | ||
|         let cur = /^\s*/.exec(line.text)[0];
 | ||
|         let norm = indentString(state, indent);
 | ||
|         if (cur != norm) {
 | ||
|             updated[line.from] = indent;
 | ||
|             changes.push({ from: line.from, to: line.from + cur.length, insert: norm });
 | ||
|         }
 | ||
|     }
 | ||
|     return state.changes(changes);
 | ||
| }
 | ||
| /**
 | ||
| Indentation contexts are used when calling [indentation
 | ||
| services](https://codemirror.net/6/docs/ref/#language.indentService). They provide helper utilities
 | ||
| useful in indentation logic, and can selectively override the
 | ||
| indentation reported for some lines.
 | ||
| */
 | ||
| class IndentContext {
 | ||
|     /**
 | ||
|     Create an indent context.
 | ||
|     */
 | ||
|     constructor(
 | ||
|     /**
 | ||
|     The editor state.
 | ||
|     */
 | ||
|     state, 
 | ||
|     /**
 | ||
|     @internal
 | ||
|     */
 | ||
|     options = {}) {
 | ||
|         this.state = state;
 | ||
|         this.options = options;
 | ||
|         this.unit = getIndentUnit(state);
 | ||
|     }
 | ||
|     /**
 | ||
|     Get a description of the line at the given position, taking
 | ||
|     [simulated line
 | ||
|     breaks](https://codemirror.net/6/docs/ref/#language.IndentContext.constructor^options.simulateBreak)
 | ||
|     into account. If there is such a break at `pos`, the `bias`
 | ||
|     argument determines whether the part of the line line before or
 | ||
|     after the break is used.
 | ||
|     */
 | ||
|     lineAt(pos, bias = 1) {
 | ||
|         let line = this.state.doc.lineAt(pos);
 | ||
|         let { simulateBreak, simulateDoubleBreak } = this.options;
 | ||
|         if (simulateBreak != null && simulateBreak >= line.from && simulateBreak <= line.to) {
 | ||
|             if (simulateDoubleBreak && simulateBreak == pos)
 | ||
|                 return { text: "", from: pos };
 | ||
|             else if (bias < 0 ? simulateBreak < pos : simulateBreak <= pos)
 | ||
|                 return { text: line.text.slice(simulateBreak - line.from), from: simulateBreak };
 | ||
|             else
 | ||
|                 return { text: line.text.slice(0, simulateBreak - line.from), from: line.from };
 | ||
|         }
 | ||
|         return line;
 | ||
|     }
 | ||
|     /**
 | ||
|     Get the text directly after `pos`, either the entire line
 | ||
|     or the next 100 characters, whichever is shorter.
 | ||
|     */
 | ||
|     textAfterPos(pos, bias = 1) {
 | ||
|         if (this.options.simulateDoubleBreak && pos == this.options.simulateBreak)
 | ||
|             return "";
 | ||
|         let { text, from } = this.lineAt(pos, bias);
 | ||
|         return text.slice(pos - from, Math.min(text.length, pos + 100 - from));
 | ||
|     }
 | ||
|     /**
 | ||
|     Find the column for the given position.
 | ||
|     */
 | ||
|     column(pos, bias = 1) {
 | ||
|         let { text, from } = this.lineAt(pos, bias);
 | ||
|         let result = this.countColumn(text, pos - from);
 | ||
|         let override = this.options.overrideIndentation ? this.options.overrideIndentation(from) : -1;
 | ||
|         if (override > -1)
 | ||
|             result += override - this.countColumn(text, text.search(/\S|$/));
 | ||
|         return result;
 | ||
|     }
 | ||
|     /**
 | ||
|     Find the column position (taking tabs into account) of the given
 | ||
|     position in the given string.
 | ||
|     */
 | ||
|     countColumn(line, pos = line.length) {
 | ||
|         return countColumn(line, this.state.tabSize, pos);
 | ||
|     }
 | ||
|     /**
 | ||
|     Find the indentation column of the line at the given point.
 | ||
|     */
 | ||
|     lineIndent(pos, bias = 1) {
 | ||
|         let { text, from } = this.lineAt(pos, bias);
 | ||
|         let override = this.options.overrideIndentation;
 | ||
|         if (override) {
 | ||
|             let overriden = override(from);
 | ||
|             if (overriden > -1)
 | ||
|                 return overriden;
 | ||
|         }
 | ||
|         return this.countColumn(text, text.search(/\S|$/));
 | ||
|     }
 | ||
|     /**
 | ||
|     Returns the [simulated line
 | ||
|     break](https://codemirror.net/6/docs/ref/#language.IndentContext.constructor^options.simulateBreak)
 | ||
|     for this context, if any.
 | ||
|     */
 | ||
|     get simulatedBreak() {
 | ||
|         return this.options.simulateBreak || null;
 | ||
|     }
 | ||
| }
 | ||
| /**
 | ||
| A syntax tree node prop used to associate indentation strategies
 | ||
| with node types. Such a strategy is a function from an indentation
 | ||
| context to a column number (see also
 | ||
| [`indentString`](https://codemirror.net/6/docs/ref/#language.indentString)) or null, where null
 | ||
| indicates that no definitive indentation can be determined.
 | ||
| */
 | ||
| const indentNodeProp = /*@__PURE__*/new NodeProp();
 | ||
| // Compute the indentation for a given position from the syntax tree.
 | ||
| function syntaxIndentation(cx, ast, pos) {
 | ||
|     let stack = ast.resolveStack(pos);
 | ||
|     let inner = stack.node.enterUnfinishedNodesBefore(pos);
 | ||
|     if (inner != stack.node) {
 | ||
|         let add = [];
 | ||
|         for (let cur = inner; cur != stack.node; cur = cur.parent)
 | ||
|             add.push(cur);
 | ||
|         for (let i = add.length - 1; i >= 0; i--)
 | ||
|             stack = { node: add[i], next: stack };
 | ||
|     }
 | ||
|     return indentFor(stack, cx, pos);
 | ||
| }
 | ||
| function indentFor(stack, cx, pos) {
 | ||
|     for (let cur = stack; cur; cur = cur.next) {
 | ||
|         let strategy = indentStrategy(cur.node);
 | ||
|         if (strategy)
 | ||
|             return strategy(TreeIndentContext.create(cx, pos, cur));
 | ||
|     }
 | ||
|     return 0;
 | ||
| }
 | ||
| function ignoreClosed(cx) {
 | ||
|     return cx.pos == cx.options.simulateBreak && cx.options.simulateDoubleBreak;
 | ||
| }
 | ||
| function indentStrategy(tree) {
 | ||
|     let strategy = tree.type.prop(indentNodeProp);
 | ||
|     if (strategy)
 | ||
|         return strategy;
 | ||
|     let first = tree.firstChild, close;
 | ||
|     if (first && (close = first.type.prop(NodeProp.closedBy))) {
 | ||
|         let last = tree.lastChild, closed = last && close.indexOf(last.name) > -1;
 | ||
|         return cx => delimitedStrategy(cx, true, 1, undefined, closed && !ignoreClosed(cx) ? last.from : undefined);
 | ||
|     }
 | ||
|     return tree.parent == null ? topIndent : null;
 | ||
| }
 | ||
| function topIndent() { return 0; }
 | ||
| /**
 | ||
| Objects of this type provide context information and helper
 | ||
| methods to indentation functions registered on syntax nodes.
 | ||
| */
 | ||
| class TreeIndentContext extends IndentContext {
 | ||
|     constructor(base, 
 | ||
|     /**
 | ||
|     The position at which indentation is being computed.
 | ||
|     */
 | ||
|     pos, 
 | ||
|     /**
 | ||
|     @internal
 | ||
|     */
 | ||
|     context) {
 | ||
|         super(base.state, base.options);
 | ||
|         this.base = base;
 | ||
|         this.pos = pos;
 | ||
|         this.context = context;
 | ||
|     }
 | ||
|     /**
 | ||
|     The syntax tree node to which the indentation strategy
 | ||
|     applies.
 | ||
|     */
 | ||
|     get node() { return this.context.node; }
 | ||
|     /**
 | ||
|     @internal
 | ||
|     */
 | ||
|     static create(base, pos, context) {
 | ||
|         return new TreeIndentContext(base, pos, context);
 | ||
|     }
 | ||
|     /**
 | ||
|     Get the text directly after `this.pos`, either the entire line
 | ||
|     or the next 100 characters, whichever is shorter.
 | ||
|     */
 | ||
|     get textAfter() {
 | ||
|         return this.textAfterPos(this.pos);
 | ||
|     }
 | ||
|     /**
 | ||
|     Get the indentation at the reference line for `this.node`, which
 | ||
|     is the line on which it starts, unless there is a node that is
 | ||
|     _not_ a parent of this node covering the start of that line. If
 | ||
|     so, the line at the start of that node is tried, again skipping
 | ||
|     on if it is covered by another such node.
 | ||
|     */
 | ||
|     get baseIndent() {
 | ||
|         return this.baseIndentFor(this.node);
 | ||
|     }
 | ||
|     /**
 | ||
|     Get the indentation for the reference line of the given node
 | ||
|     (see [`baseIndent`](https://codemirror.net/6/docs/ref/#language.TreeIndentContext.baseIndent)).
 | ||
|     */
 | ||
|     baseIndentFor(node) {
 | ||
|         let line = this.state.doc.lineAt(node.from);
 | ||
|         // Skip line starts that are covered by a sibling (or cousin, etc)
 | ||
|         for (;;) {
 | ||
|             let atBreak = node.resolve(line.from);
 | ||
|             while (atBreak.parent && atBreak.parent.from == atBreak.from)
 | ||
|                 atBreak = atBreak.parent;
 | ||
|             if (isParent(atBreak, node))
 | ||
|                 break;
 | ||
|             line = this.state.doc.lineAt(atBreak.from);
 | ||
|         }
 | ||
|         return this.lineIndent(line.from);
 | ||
|     }
 | ||
|     /**
 | ||
|     Continue looking for indentations in the node's parent nodes,
 | ||
|     and return the result of that.
 | ||
|     */
 | ||
|     continue() {
 | ||
|         return indentFor(this.context.next, this.base, this.pos);
 | ||
|     }
 | ||
| }
 | ||
| function isParent(parent, of) {
 | ||
|     for (let cur = of; cur; cur = cur.parent)
 | ||
|         if (parent == cur)
 | ||
|             return true;
 | ||
|     return false;
 | ||
| }
 | ||
| // Check whether a delimited node is aligned (meaning there are
 | ||
| // non-skipped nodes on the same line as the opening delimiter). And
 | ||
| // if so, return the opening token.
 | ||
| function bracketedAligned(context) {
 | ||
|     let tree = context.node;
 | ||
|     let openToken = tree.childAfter(tree.from), last = tree.lastChild;
 | ||
|     if (!openToken)
 | ||
|         return null;
 | ||
|     let sim = context.options.simulateBreak;
 | ||
|     let openLine = context.state.doc.lineAt(openToken.from);
 | ||
|     let lineEnd = sim == null || sim <= openLine.from ? openLine.to : Math.min(openLine.to, sim);
 | ||
|     for (let pos = openToken.to;;) {
 | ||
|         let next = tree.childAfter(pos);
 | ||
|         if (!next || next == last)
 | ||
|             return null;
 | ||
|         if (!next.type.isSkipped)
 | ||
|             return next.from < lineEnd ? openToken : null;
 | ||
|         pos = next.to;
 | ||
|     }
 | ||
| }
 | ||
| /**
 | ||
| An indentation strategy for delimited (usually bracketed) nodes.
 | ||
| Will, by default, indent one unit more than the parent's base
 | ||
| indent unless the line starts with a closing token. When `align`
 | ||
| is true and there are non-skipped nodes on the node's opening
 | ||
| line, the content of the node will be aligned with the end of the
 | ||
| opening node, like this:
 | ||
| 
 | ||
|     foo(bar,
 | ||
|         baz)
 | ||
| */
 | ||
| function delimitedIndent({ closing, align = true, units = 1 }) {
 | ||
|     return (context) => delimitedStrategy(context, align, units, closing);
 | ||
| }
 | ||
| function delimitedStrategy(context, align, units, closing, closedAt) {
 | ||
|     let after = context.textAfter, space = after.match(/^\s*/)[0].length;
 | ||
|     let closed = closing && after.slice(space, space + closing.length) == closing || closedAt == context.pos + space;
 | ||
|     let aligned = align ? bracketedAligned(context) : null;
 | ||
|     if (aligned)
 | ||
|         return closed ? context.column(aligned.from) : context.column(aligned.to);
 | ||
|     return context.baseIndent + (closed ? 0 : context.unit * units);
 | ||
| }
 | ||
| /**
 | ||
| An indentation strategy that aligns a node's content to its base
 | ||
| indentation.
 | ||
| */
 | ||
| const flatIndent = (context) => context.baseIndent;
 | ||
| /**
 | ||
| Creates an indentation strategy that, by default, indents
 | ||
| continued lines one unit more than the node's base indentation.
 | ||
| You can provide `except` to prevent indentation of lines that
 | ||
| match a pattern (for example `/^else\b/` in `if`/`else`
 | ||
| constructs), and you can change the amount of units used with the
 | ||
| `units` option.
 | ||
| */
 | ||
| function continuedIndent({ except, units = 1 } = {}) {
 | ||
|     return (context) => {
 | ||
|         let matchExcept = except && except.test(context.textAfter);
 | ||
|         return context.baseIndent + (matchExcept ? 0 : units * context.unit);
 | ||
|     };
 | ||
| }
 | ||
| const DontIndentBeyond = 200;
 | ||
| /**
 | ||
| Enables reindentation on input. When a language defines an
 | ||
| `indentOnInput` field in its [language
 | ||
| data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt), which must hold a regular
 | ||
| expression, the line at the cursor will be reindented whenever new
 | ||
| text is typed and the input from the start of the line up to the
 | ||
| cursor matches that regexp.
 | ||
| 
 | ||
| To avoid unneccesary reindents, it is recommended to start the
 | ||
| regexp with `^` (usually followed by `\s*`), and end it with `$`.
 | ||
| For example, `/^\s*\}$/` will reindent when a closing brace is
 | ||
| added at the start of a line.
 | ||
| */
 | ||
| function indentOnInput() {
 | ||
|     return EditorState.transactionFilter.of(tr => {
 | ||
|         if (!tr.docChanged || !tr.isUserEvent("input.type") && !tr.isUserEvent("input.complete"))
 | ||
|             return tr;
 | ||
|         let rules = tr.startState.languageDataAt("indentOnInput", tr.startState.selection.main.head);
 | ||
|         if (!rules.length)
 | ||
|             return tr;
 | ||
|         let doc = tr.newDoc, { head } = tr.newSelection.main, line = doc.lineAt(head);
 | ||
|         if (head > line.from + DontIndentBeyond)
 | ||
|             return tr;
 | ||
|         let lineStart = doc.sliceString(line.from, head);
 | ||
|         if (!rules.some(r => r.test(lineStart)))
 | ||
|             return tr;
 | ||
|         let { state } = tr, last = -1, changes = [];
 | ||
|         for (let { head } of state.selection.ranges) {
 | ||
|             let line = state.doc.lineAt(head);
 | ||
|             if (line.from == last)
 | ||
|                 continue;
 | ||
|             last = line.from;
 | ||
|             let indent = getIndentation(state, line.from);
 | ||
|             if (indent == null)
 | ||
|                 continue;
 | ||
|             let cur = /^\s*/.exec(line.text)[0];
 | ||
|             let norm = indentString(state, indent);
 | ||
|             if (cur != norm)
 | ||
|                 changes.push({ from: line.from, to: line.from + cur.length, insert: norm });
 | ||
|         }
 | ||
|         return changes.length ? [tr, { changes, sequential: true }] : tr;
 | ||
|     });
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
| A facet that registers a code folding service. When called with
 | ||
| the extent of a line, such a function should return a foldable
 | ||
| range that starts on that line (but continues beyond it), if one
 | ||
| can be found.
 | ||
| */
 | ||
| const foldService = /*@__PURE__*/Facet.define();
 | ||
| /**
 | ||
| This node prop is used to associate folding information with
 | ||
| syntax node types. Given a syntax node, it should check whether
 | ||
| that tree is foldable and return the range that can be collapsed
 | ||
| when it is.
 | ||
| */
 | ||
| const foldNodeProp = /*@__PURE__*/new NodeProp();
 | ||
| /**
 | ||
| [Fold](https://codemirror.net/6/docs/ref/#language.foldNodeProp) function that folds everything but
 | ||
| the first and the last child of a syntax node. Useful for nodes
 | ||
| that start and end with delimiters.
 | ||
| */
 | ||
| function foldInside(node) {
 | ||
|     let first = node.firstChild, last = node.lastChild;
 | ||
|     return first && first.to < last.from ? { from: first.to, to: last.type.isError ? node.to : last.from } : null;
 | ||
| }
 | ||
| function syntaxFolding(state, start, end) {
 | ||
|     let tree = syntaxTree(state);
 | ||
|     if (tree.length < end)
 | ||
|         return null;
 | ||
|     let stack = tree.resolveStack(end, 1);
 | ||
|     let found = null;
 | ||
|     for (let iter = stack; iter; iter = iter.next) {
 | ||
|         let cur = iter.node;
 | ||
|         if (cur.to <= end || cur.from > end)
 | ||
|             continue;
 | ||
|         if (found && cur.from < start)
 | ||
|             break;
 | ||
|         let prop = cur.type.prop(foldNodeProp);
 | ||
|         if (prop && (cur.to < tree.length - 50 || tree.length == state.doc.length || !isUnfinished(cur))) {
 | ||
|             let value = prop(cur, state);
 | ||
|             if (value && value.from <= end && value.from >= start && value.to > end)
 | ||
|                 found = value;
 | ||
|         }
 | ||
|     }
 | ||
|     return found;
 | ||
| }
 | ||
| function isUnfinished(node) {
 | ||
|     let ch = node.lastChild;
 | ||
|     return ch && ch.to == node.to && ch.type.isError;
 | ||
| }
 | ||
| /**
 | ||
| Check whether the given line is foldable. First asks any fold
 | ||
| services registered through
 | ||
| [`foldService`](https://codemirror.net/6/docs/ref/#language.foldService), and if none of them return
 | ||
| a result, tries to query the [fold node
 | ||
| prop](https://codemirror.net/6/docs/ref/#language.foldNodeProp) of syntax nodes that cover the end
 | ||
| of the line.
 | ||
| */
 | ||
| function foldable(state, lineStart, lineEnd) {
 | ||
|     for (let service of state.facet(foldService)) {
 | ||
|         let result = service(state, lineStart, lineEnd);
 | ||
|         if (result)
 | ||
|             return result;
 | ||
|     }
 | ||
|     return syntaxFolding(state, lineStart, lineEnd);
 | ||
| }
 | ||
| function mapRange(range, mapping) {
 | ||
|     let from = mapping.mapPos(range.from, 1), to = mapping.mapPos(range.to, -1);
 | ||
|     return from >= to ? undefined : { from, to };
 | ||
| }
 | ||
| /**
 | ||
| State effect that can be attached to a transaction to fold the
 | ||
| given range. (You probably only need this in exceptional
 | ||
| circumstances—usually you'll just want to let
 | ||
| [`foldCode`](https://codemirror.net/6/docs/ref/#language.foldCode) and the [fold
 | ||
| gutter](https://codemirror.net/6/docs/ref/#language.foldGutter) create the transactions.)
 | ||
| */
 | ||
| const foldEffect = /*@__PURE__*/StateEffect.define({ map: mapRange });
 | ||
| /**
 | ||
| State effect that unfolds the given range (if it was folded).
 | ||
| */
 | ||
| const unfoldEffect = /*@__PURE__*/StateEffect.define({ map: mapRange });
 | ||
| function selectedLines(view) {
 | ||
|     let lines = [];
 | ||
|     for (let { head } of view.state.selection.ranges) {
 | ||
|         if (lines.some(l => l.from <= head && l.to >= head))
 | ||
|             continue;
 | ||
|         lines.push(view.lineBlockAt(head));
 | ||
|     }
 | ||
|     return lines;
 | ||
| }
 | ||
| /**
 | ||
| The state field that stores the folded ranges (as a [decoration
 | ||
| set](https://codemirror.net/6/docs/ref/#view.DecorationSet)). Can be passed to
 | ||
| [`EditorState.toJSON`](https://codemirror.net/6/docs/ref/#state.EditorState.toJSON) and
 | ||
| [`fromJSON`](https://codemirror.net/6/docs/ref/#state.EditorState^fromJSON) to serialize the fold
 | ||
| state.
 | ||
| */
 | ||
| const foldState = /*@__PURE__*/StateField.define({
 | ||
|     create() {
 | ||
|         return Decoration.none;
 | ||
|     },
 | ||
|     update(folded, tr) {
 | ||
|         folded = folded.map(tr.changes);
 | ||
|         for (let e of tr.effects) {
 | ||
|             if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to)) {
 | ||
|                 let { preparePlaceholder } = tr.state.facet(foldConfig);
 | ||
|                 let widget = !preparePlaceholder ? foldWidget :
 | ||
|                     Decoration.replace({ widget: new PreparedFoldWidget(preparePlaceholder(tr.state, e.value)) });
 | ||
|                 folded = folded.update({ add: [widget.range(e.value.from, e.value.to)] });
 | ||
|             }
 | ||
|             else if (e.is(unfoldEffect)) {
 | ||
|                 folded = folded.update({ filter: (from, to) => e.value.from != from || e.value.to != to,
 | ||
|                     filterFrom: e.value.from, filterTo: e.value.to });
 | ||
|             }
 | ||
|         }
 | ||
|         // Clear folded ranges that cover the selection head
 | ||
|         if (tr.selection) {
 | ||
|             let onSelection = false, { head } = tr.selection.main;
 | ||
|             folded.between(head, head, (a, b) => { if (a < head && b > head)
 | ||
|                 onSelection = true; });
 | ||
|             if (onSelection)
 | ||
|                 folded = folded.update({
 | ||
|                     filterFrom: head,
 | ||
|                     filterTo: head,
 | ||
|                     filter: (a, b) => b <= head || a >= head
 | ||
|                 });
 | ||
|         }
 | ||
|         return folded;
 | ||
|     },
 | ||
|     provide: f => EditorView.decorations.from(f),
 | ||
|     toJSON(folded, state) {
 | ||
|         let ranges = [];
 | ||
|         folded.between(0, state.doc.length, (from, to) => { ranges.push(from, to); });
 | ||
|         return ranges;
 | ||
|     },
 | ||
|     fromJSON(value) {
 | ||
|         if (!Array.isArray(value) || value.length % 2)
 | ||
|             throw new RangeError("Invalid JSON for fold state");
 | ||
|         let ranges = [];
 | ||
|         for (let i = 0; i < value.length;) {
 | ||
|             let from = value[i++], to = value[i++];
 | ||
|             if (typeof from != "number" || typeof to != "number")
 | ||
|                 throw new RangeError("Invalid JSON for fold state");
 | ||
|             ranges.push(foldWidget.range(from, to));
 | ||
|         }
 | ||
|         return Decoration.set(ranges, true);
 | ||
|     }
 | ||
| });
 | ||
| /**
 | ||
| Get a [range set](https://codemirror.net/6/docs/ref/#state.RangeSet) containing the folded ranges
 | ||
| in the given state.
 | ||
| */
 | ||
| function foldedRanges(state) {
 | ||
|     return state.field(foldState, false) || RangeSet.empty;
 | ||
| }
 | ||
| function findFold(state, from, to) {
 | ||
|     var _a;
 | ||
|     let found = null;
 | ||
|     (_a = state.field(foldState, false)) === null || _a === void 0 ? void 0 : _a.between(from, to, (from, to) => {
 | ||
|         if (!found || found.from > from)
 | ||
|             found = { from, to };
 | ||
|     });
 | ||
|     return found;
 | ||
| }
 | ||
| function foldExists(folded, from, to) {
 | ||
|     let found = false;
 | ||
|     folded.between(from, from, (a, b) => { if (a == from && b == to)
 | ||
|         found = true; });
 | ||
|     return found;
 | ||
| }
 | ||
| function maybeEnable(state, other) {
 | ||
|     return state.field(foldState, false) ? other : other.concat(StateEffect.appendConfig.of(codeFolding()));
 | ||
| }
 | ||
| /**
 | ||
| Fold the lines that are selected, if possible.
 | ||
| */
 | ||
| const foldCode = view => {
 | ||
|     for (let line of selectedLines(view)) {
 | ||
|         let range = foldable(view.state, line.from, line.to);
 | ||
|         if (range) {
 | ||
|             view.dispatch({ effects: maybeEnable(view.state, [foldEffect.of(range), announceFold(view, range)]) });
 | ||
|             return true;
 | ||
|         }
 | ||
|     }
 | ||
|     return false;
 | ||
| };
 | ||
| /**
 | ||
| Unfold folded ranges on selected lines.
 | ||
| */
 | ||
| const unfoldCode = view => {
 | ||
|     if (!view.state.field(foldState, false))
 | ||
|         return false;
 | ||
|     let effects = [];
 | ||
|     for (let line of selectedLines(view)) {
 | ||
|         let folded = findFold(view.state, line.from, line.to);
 | ||
|         if (folded)
 | ||
|             effects.push(unfoldEffect.of(folded), announceFold(view, folded, false));
 | ||
|     }
 | ||
|     if (effects.length)
 | ||
|         view.dispatch({ effects });
 | ||
|     return effects.length > 0;
 | ||
| };
 | ||
| function announceFold(view, range, fold = true) {
 | ||
|     let lineFrom = view.state.doc.lineAt(range.from).number, lineTo = view.state.doc.lineAt(range.to).number;
 | ||
|     return EditorView.announce.of(`${view.state.phrase(fold ? "Folded lines" : "Unfolded lines")} ${lineFrom} ${view.state.phrase("to")} ${lineTo}.`);
 | ||
| }
 | ||
| /**
 | ||
| Fold all top-level foldable ranges. Note that, in most cases,
 | ||
| folding information will depend on the [syntax
 | ||
| tree](https://codemirror.net/6/docs/ref/#language.syntaxTree), and folding everything may not work
 | ||
| reliably when the document hasn't been fully parsed (either
 | ||
| because the editor state was only just initialized, or because the
 | ||
| document is so big that the parser decided not to parse it
 | ||
| entirely).
 | ||
| */
 | ||
| const foldAll = view => {
 | ||
|     let { state } = view, effects = [];
 | ||
|     for (let pos = 0; pos < state.doc.length;) {
 | ||
|         let line = view.lineBlockAt(pos), range = foldable(state, line.from, line.to);
 | ||
|         if (range)
 | ||
|             effects.push(foldEffect.of(range));
 | ||
|         pos = (range ? view.lineBlockAt(range.to) : line).to + 1;
 | ||
|     }
 | ||
|     if (effects.length)
 | ||
|         view.dispatch({ effects: maybeEnable(view.state, effects) });
 | ||
|     return !!effects.length;
 | ||
| };
 | ||
| /**
 | ||
| Unfold all folded code.
 | ||
| */
 | ||
| const unfoldAll = view => {
 | ||
|     let field = view.state.field(foldState, false);
 | ||
|     if (!field || !field.size)
 | ||
|         return false;
 | ||
|     let effects = [];
 | ||
|     field.between(0, view.state.doc.length, (from, to) => { effects.push(unfoldEffect.of({ from, to })); });
 | ||
|     view.dispatch({ effects });
 | ||
|     return true;
 | ||
| };
 | ||
| // Find the foldable region containing the given line, if one exists
 | ||
| function foldableContainer(view, lineBlock) {
 | ||
|     // Look backwards through line blocks until we find a foldable region that
 | ||
|     // intersects with the line
 | ||
|     for (let line = lineBlock;;) {
 | ||
|         let foldableRegion = foldable(view.state, line.from, line.to);
 | ||
|         if (foldableRegion && foldableRegion.to > lineBlock.from)
 | ||
|             return foldableRegion;
 | ||
|         if (!line.from)
 | ||
|             return null;
 | ||
|         line = view.lineBlockAt(line.from - 1);
 | ||
|     }
 | ||
| }
 | ||
| /**
 | ||
| Toggle folding at cursors. Unfolds if there is an existing fold
 | ||
| starting in that line, tries to find a foldable range around it
 | ||
| otherwise.
 | ||
| */
 | ||
| const toggleFold = (view) => {
 | ||
|     let effects = [];
 | ||
|     for (let line of selectedLines(view)) {
 | ||
|         let folded = findFold(view.state, line.from, line.to);
 | ||
|         if (folded) {
 | ||
|             effects.push(unfoldEffect.of(folded), announceFold(view, folded, false));
 | ||
|         }
 | ||
|         else {
 | ||
|             let foldRange = foldableContainer(view, line);
 | ||
|             if (foldRange)
 | ||
|                 effects.push(foldEffect.of(foldRange), announceFold(view, foldRange));
 | ||
|         }
 | ||
|     }
 | ||
|     if (effects.length > 0)
 | ||
|         view.dispatch({ effects: maybeEnable(view.state, effects) });
 | ||
|     return !!effects.length;
 | ||
| };
 | ||
| /**
 | ||
| Default fold-related key bindings.
 | ||
| 
 | ||
|  - Ctrl-Shift-[ (Cmd-Alt-[ on macOS): [`foldCode`](https://codemirror.net/6/docs/ref/#language.foldCode).
 | ||
|  - Ctrl-Shift-] (Cmd-Alt-] on macOS): [`unfoldCode`](https://codemirror.net/6/docs/ref/#language.unfoldCode).
 | ||
|  - Ctrl-Alt-[: [`foldAll`](https://codemirror.net/6/docs/ref/#language.foldAll).
 | ||
|  - Ctrl-Alt-]: [`unfoldAll`](https://codemirror.net/6/docs/ref/#language.unfoldAll).
 | ||
| */
 | ||
| const foldKeymap = [
 | ||
|     { key: "Ctrl-Shift-[", mac: "Cmd-Alt-[", run: foldCode },
 | ||
|     { key: "Ctrl-Shift-]", mac: "Cmd-Alt-]", run: unfoldCode },
 | ||
|     { key: "Ctrl-Alt-[", run: foldAll },
 | ||
|     { key: "Ctrl-Alt-]", run: unfoldAll }
 | ||
| ];
 | ||
| const defaultConfig = {
 | ||
|     placeholderDOM: null,
 | ||
|     preparePlaceholder: null,
 | ||
|     placeholderText: "…"
 | ||
| };
 | ||
| const foldConfig = /*@__PURE__*/Facet.define({
 | ||
|     combine(values) { return combineConfig(values, defaultConfig); }
 | ||
| });
 | ||
| /**
 | ||
| Create an extension that configures code folding.
 | ||
| */
 | ||
| function codeFolding(config) {
 | ||
|     let result = [foldState, baseTheme$1];
 | ||
|     if (config)
 | ||
|         result.push(foldConfig.of(config));
 | ||
|     return result;
 | ||
| }
 | ||
| function widgetToDOM(view, prepared) {
 | ||
|     let { state } = view, conf = state.facet(foldConfig);
 | ||
|     let onclick = (event) => {
 | ||
|         let line = view.lineBlockAt(view.posAtDOM(event.target));
 | ||
|         let folded = findFold(view.state, line.from, line.to);
 | ||
|         if (folded)
 | ||
|             view.dispatch({ effects: unfoldEffect.of(folded) });
 | ||
|         event.preventDefault();
 | ||
|     };
 | ||
|     if (conf.placeholderDOM)
 | ||
|         return conf.placeholderDOM(view, onclick, prepared);
 | ||
|     let element = document.createElement("span");
 | ||
|     element.textContent = conf.placeholderText;
 | ||
|     element.setAttribute("aria-label", state.phrase("folded code"));
 | ||
|     element.title = state.phrase("unfold");
 | ||
|     element.className = "cm-foldPlaceholder";
 | ||
|     element.onclick = onclick;
 | ||
|     return element;
 | ||
| }
 | ||
| const foldWidget = /*@__PURE__*/Decoration.replace({ widget: /*@__PURE__*/new class extends WidgetType {
 | ||
|         toDOM(view) { return widgetToDOM(view, null); }
 | ||
|     } });
 | ||
| class PreparedFoldWidget extends WidgetType {
 | ||
|     constructor(value) {
 | ||
|         super();
 | ||
|         this.value = value;
 | ||
|     }
 | ||
|     eq(other) { return this.value == other.value; }
 | ||
|     toDOM(view) { return widgetToDOM(view, this.value); }
 | ||
| }
 | ||
| const foldGutterDefaults = {
 | ||
|     openText: "⌄",
 | ||
|     closedText: "›",
 | ||
|     markerDOM: null,
 | ||
|     domEventHandlers: {},
 | ||
|     foldingChanged: () => false
 | ||
| };
 | ||
| class FoldMarker extends GutterMarker {
 | ||
|     constructor(config, open) {
 | ||
|         super();
 | ||
|         this.config = config;
 | ||
|         this.open = open;
 | ||
|     }
 | ||
|     eq(other) { return this.config == other.config && this.open == other.open; }
 | ||
|     toDOM(view) {
 | ||
|         if (this.config.markerDOM)
 | ||
|             return this.config.markerDOM(this.open);
 | ||
|         let span = document.createElement("span");
 | ||
|         span.textContent = this.open ? this.config.openText : this.config.closedText;
 | ||
|         span.title = view.state.phrase(this.open ? "Fold line" : "Unfold line");
 | ||
|         return span;
 | ||
|     }
 | ||
| }
 | ||
| /**
 | ||
| Create an extension that registers a fold gutter, which shows a
 | ||
| fold status indicator before foldable lines (which can be clicked
 | ||
| to fold or unfold the line).
 | ||
| */
 | ||
| function foldGutter(config = {}) {
 | ||
|     let fullConfig = Object.assign(Object.assign({}, foldGutterDefaults), config);
 | ||
|     let canFold = new FoldMarker(fullConfig, true), canUnfold = new FoldMarker(fullConfig, false);
 | ||
|     let markers = ViewPlugin.fromClass(class {
 | ||
|         constructor(view) {
 | ||
|             this.from = view.viewport.from;
 | ||
|             this.markers = this.buildMarkers(view);
 | ||
|         }
 | ||
|         update(update) {
 | ||
|             if (update.docChanged || update.viewportChanged ||
 | ||
|                 update.startState.facet(language) != update.state.facet(language) ||
 | ||
|                 update.startState.field(foldState, false) != update.state.field(foldState, false) ||
 | ||
|                 syntaxTree(update.startState) != syntaxTree(update.state) ||
 | ||
|                 fullConfig.foldingChanged(update))
 | ||
|                 this.markers = this.buildMarkers(update.view);
 | ||
|         }
 | ||
|         buildMarkers(view) {
 | ||
|             let builder = new RangeSetBuilder();
 | ||
|             for (let line of view.viewportLineBlocks) {
 | ||
|                 let mark = findFold(view.state, line.from, line.to) ? canUnfold
 | ||
|                     : foldable(view.state, line.from, line.to) ? canFold : null;
 | ||
|                 if (mark)
 | ||
|                     builder.add(line.from, line.from, mark);
 | ||
|             }
 | ||
|             return builder.finish();
 | ||
|         }
 | ||
|     });
 | ||
|     let { domEventHandlers } = fullConfig;
 | ||
|     return [
 | ||
|         markers,
 | ||
|         gutter({
 | ||
|             class: "cm-foldGutter",
 | ||
|             markers(view) { var _a; return ((_a = view.plugin(markers)) === null || _a === void 0 ? void 0 : _a.markers) || RangeSet.empty; },
 | ||
|             initialSpacer() {
 | ||
|                 return new FoldMarker(fullConfig, false);
 | ||
|             },
 | ||
|             domEventHandlers: Object.assign(Object.assign({}, domEventHandlers), { click: (view, line, event) => {
 | ||
|                     if (domEventHandlers.click && domEventHandlers.click(view, line, event))
 | ||
|                         return true;
 | ||
|                     let folded = findFold(view.state, line.from, line.to);
 | ||
|                     if (folded) {
 | ||
|                         view.dispatch({ effects: unfoldEffect.of(folded) });
 | ||
|                         return true;
 | ||
|                     }
 | ||
|                     let range = foldable(view.state, line.from, line.to);
 | ||
|                     if (range) {
 | ||
|                         view.dispatch({ effects: foldEffect.of(range) });
 | ||
|                         return true;
 | ||
|                     }
 | ||
|                     return false;
 | ||
|                 } })
 | ||
|         }),
 | ||
|         codeFolding()
 | ||
|     ];
 | ||
| }
 | ||
| const baseTheme$1 = /*@__PURE__*/EditorView.baseTheme({
 | ||
|     ".cm-foldPlaceholder": {
 | ||
|         backgroundColor: "#eee",
 | ||
|         border: "1px solid #ddd",
 | ||
|         color: "#888",
 | ||
|         borderRadius: ".2em",
 | ||
|         margin: "0 1px",
 | ||
|         padding: "0 1px",
 | ||
|         cursor: "pointer"
 | ||
|     },
 | ||
|     ".cm-foldGutter span": {
 | ||
|         padding: "0 1px",
 | ||
|         cursor: "pointer"
 | ||
|     }
 | ||
| });
 | ||
| 
 | ||
| /**
 | ||
| A highlight style associates CSS styles with higlighting
 | ||
| [tags](https://lezer.codemirror.net/docs/ref#highlight.Tag).
 | ||
| */
 | ||
| class HighlightStyle {
 | ||
|     constructor(
 | ||
|     /**
 | ||
|     The tag styles used to create this highlight style.
 | ||
|     */
 | ||
|     specs, options) {
 | ||
|         this.specs = specs;
 | ||
|         let modSpec;
 | ||
|         function def(spec) {
 | ||
|             let cls = StyleModule.newName();
 | ||
|             (modSpec || (modSpec = Object.create(null)))["." + cls] = spec;
 | ||
|             return cls;
 | ||
|         }
 | ||
|         const all = typeof options.all == "string" ? options.all : options.all ? def(options.all) : undefined;
 | ||
|         const scopeOpt = options.scope;
 | ||
|         this.scope = scopeOpt instanceof Language ? (type) => type.prop(languageDataProp) == scopeOpt.data
 | ||
|             : scopeOpt ? (type) => type == scopeOpt : undefined;
 | ||
|         this.style = tagHighlighter(specs.map(style => ({
 | ||
|             tag: style.tag,
 | ||
|             class: style.class || def(Object.assign({}, style, { tag: null }))
 | ||
|         })), {
 | ||
|             all,
 | ||
|         }).style;
 | ||
|         this.module = modSpec ? new StyleModule(modSpec) : null;
 | ||
|         this.themeType = options.themeType;
 | ||
|     }
 | ||
|     /**
 | ||
|     Create a highlighter style that associates the given styles to
 | ||
|     the given tags. The specs must be objects that hold a style tag
 | ||
|     or array of tags in their `tag` property, and either a single
 | ||
|     `class` property providing a static CSS class (for highlighter
 | ||
|     that rely on external styling), or a
 | ||
|     [`style-mod`](https://github.com/marijnh/style-mod#documentation)-style
 | ||
|     set of CSS properties (which define the styling for those tags).
 | ||
|     
 | ||
|     The CSS rules created for a highlighter will be emitted in the
 | ||
|     order of the spec's properties. That means that for elements that
 | ||
|     have multiple tags associated with them, styles defined further
 | ||
|     down in the list will have a higher CSS precedence than styles
 | ||
|     defined earlier.
 | ||
|     */
 | ||
|     static define(specs, options) {
 | ||
|         return new HighlightStyle(specs, options || {});
 | ||
|     }
 | ||
| }
 | ||
| const highlighterFacet = /*@__PURE__*/Facet.define();
 | ||
| const fallbackHighlighter = /*@__PURE__*/Facet.define({
 | ||
|     combine(values) { return values.length ? [values[0]] : null; }
 | ||
| });
 | ||
| function getHighlighters(state) {
 | ||
|     let main = state.facet(highlighterFacet);
 | ||
|     return main.length ? main : state.facet(fallbackHighlighter);
 | ||
| }
 | ||
| /**
 | ||
| Wrap a highlighter in an editor extension that uses it to apply
 | ||
| syntax highlighting to the editor content.
 | ||
| 
 | ||
| When multiple (non-fallback) styles are provided, the styling
 | ||
| applied is the union of the classes they emit.
 | ||
| */
 | ||
| function syntaxHighlighting(highlighter, options) {
 | ||
|     let ext = [treeHighlighter], themeType;
 | ||
|     if (highlighter instanceof HighlightStyle) {
 | ||
|         if (highlighter.module)
 | ||
|             ext.push(EditorView.styleModule.of(highlighter.module));
 | ||
|         themeType = highlighter.themeType;
 | ||
|     }
 | ||
|     if (options === null || options === void 0 ? void 0 : options.fallback)
 | ||
|         ext.push(fallbackHighlighter.of(highlighter));
 | ||
|     else if (themeType)
 | ||
|         ext.push(highlighterFacet.computeN([EditorView.darkTheme], state => {
 | ||
|             return state.facet(EditorView.darkTheme) == (themeType == "dark") ? [highlighter] : [];
 | ||
|         }));
 | ||
|     else
 | ||
|         ext.push(highlighterFacet.of(highlighter));
 | ||
|     return ext;
 | ||
| }
 | ||
| /**
 | ||
| Returns the CSS classes (if any) that the highlighters active in
 | ||
| the state would assign to the given style
 | ||
| [tags](https://lezer.codemirror.net/docs/ref#highlight.Tag) and
 | ||
| (optional) language
 | ||
| [scope](https://codemirror.net/6/docs/ref/#language.HighlightStyle^define^options.scope).
 | ||
| */
 | ||
| function highlightingFor(state, tags, scope) {
 | ||
|     let highlighters = getHighlighters(state);
 | ||
|     let result = null;
 | ||
|     if (highlighters)
 | ||
|         for (let highlighter of highlighters) {
 | ||
|             if (!highlighter.scope || scope && highlighter.scope(scope)) {
 | ||
|                 let cls = highlighter.style(tags);
 | ||
|                 if (cls)
 | ||
|                     result = result ? result + " " + cls : cls;
 | ||
|             }
 | ||
|         }
 | ||
|     return result;
 | ||
| }
 | ||
| class TreeHighlighter {
 | ||
|     constructor(view) {
 | ||
|         this.markCache = Object.create(null);
 | ||
|         this.tree = syntaxTree(view.state);
 | ||
|         this.decorations = this.buildDeco(view, getHighlighters(view.state));
 | ||
|         this.decoratedTo = view.viewport.to;
 | ||
|     }
 | ||
|     update(update) {
 | ||
|         let tree = syntaxTree(update.state), highlighters = getHighlighters(update.state);
 | ||
|         let styleChange = highlighters != getHighlighters(update.startState);
 | ||
|         let { viewport } = update.view, decoratedToMapped = update.changes.mapPos(this.decoratedTo, 1);
 | ||
|         if (tree.length < viewport.to && !styleChange && tree.type == this.tree.type && decoratedToMapped >= viewport.to) {
 | ||
|             this.decorations = this.decorations.map(update.changes);
 | ||
|             this.decoratedTo = decoratedToMapped;
 | ||
|         }
 | ||
|         else if (tree != this.tree || update.viewportChanged || styleChange) {
 | ||
|             this.tree = tree;
 | ||
|             this.decorations = this.buildDeco(update.view, highlighters);
 | ||
|             this.decoratedTo = viewport.to;
 | ||
|         }
 | ||
|     }
 | ||
|     buildDeco(view, highlighters) {
 | ||
|         if (!highlighters || !this.tree.length)
 | ||
|             return Decoration.none;
 | ||
|         let builder = new RangeSetBuilder();
 | ||
|         for (let { from, to } of view.visibleRanges) {
 | ||
|             highlightTree(this.tree, highlighters, (from, to, style) => {
 | ||
|                 builder.add(from, to, this.markCache[style] || (this.markCache[style] = Decoration.mark({ class: style })));
 | ||
|             }, from, to);
 | ||
|         }
 | ||
|         return builder.finish();
 | ||
|     }
 | ||
| }
 | ||
| const treeHighlighter = /*@__PURE__*/Prec.high(/*@__PURE__*/ViewPlugin.fromClass(TreeHighlighter, {
 | ||
|     decorations: v => v.decorations
 | ||
| }));
 | ||
| /**
 | ||
| A default highlight style (works well with light themes).
 | ||
| */
 | ||
| const defaultHighlightStyle = /*@__PURE__*/HighlightStyle.define([
 | ||
|     { tag: tags.meta,
 | ||
|         color: "#404740" },
 | ||
|     { tag: tags.link,
 | ||
|         textDecoration: "underline" },
 | ||
|     { tag: tags.heading,
 | ||
|         textDecoration: "underline",
 | ||
|         fontWeight: "bold" },
 | ||
|     { tag: tags.emphasis,
 | ||
|         fontStyle: "italic" },
 | ||
|     { tag: tags.strong,
 | ||
|         fontWeight: "bold" },
 | ||
|     { tag: tags.strikethrough,
 | ||
|         textDecoration: "line-through" },
 | ||
|     { tag: tags.keyword,
 | ||
|         color: "#708" },
 | ||
|     { tag: [tags.atom, tags.bool, tags.url, tags.contentSeparator, tags.labelName],
 | ||
|         color: "#219" },
 | ||
|     { tag: [tags.literal, tags.inserted],
 | ||
|         color: "#164" },
 | ||
|     { tag: [tags.string, tags.deleted],
 | ||
|         color: "#a11" },
 | ||
|     { tag: [tags.regexp, tags.escape, /*@__PURE__*/tags.special(tags.string)],
 | ||
|         color: "#e40" },
 | ||
|     { tag: /*@__PURE__*/tags.definition(tags.variableName),
 | ||
|         color: "#00f" },
 | ||
|     { tag: /*@__PURE__*/tags.local(tags.variableName),
 | ||
|         color: "#30a" },
 | ||
|     { tag: [tags.typeName, tags.namespace],
 | ||
|         color: "#085" },
 | ||
|     { tag: tags.className,
 | ||
|         color: "#167" },
 | ||
|     { tag: [/*@__PURE__*/tags.special(tags.variableName), tags.macroName],
 | ||
|         color: "#256" },
 | ||
|     { tag: /*@__PURE__*/tags.definition(tags.propertyName),
 | ||
|         color: "#00c" },
 | ||
|     { tag: tags.comment,
 | ||
|         color: "#940" },
 | ||
|     { tag: tags.invalid,
 | ||
|         color: "#f00" }
 | ||
| ]);
 | ||
| 
 | ||
| const baseTheme = /*@__PURE__*/EditorView.baseTheme({
 | ||
|     "&.cm-focused .cm-matchingBracket": { backgroundColor: "#328c8252" },
 | ||
|     "&.cm-focused .cm-nonmatchingBracket": { backgroundColor: "#bb555544" }
 | ||
| });
 | ||
| const DefaultScanDist = 10000, DefaultBrackets = "()[]{}";
 | ||
| const bracketMatchingConfig = /*@__PURE__*/Facet.define({
 | ||
|     combine(configs) {
 | ||
|         return combineConfig(configs, {
 | ||
|             afterCursor: true,
 | ||
|             brackets: DefaultBrackets,
 | ||
|             maxScanDistance: DefaultScanDist,
 | ||
|             renderMatch: defaultRenderMatch
 | ||
|         });
 | ||
|     }
 | ||
| });
 | ||
| const matchingMark = /*@__PURE__*/Decoration.mark({ class: "cm-matchingBracket" }), nonmatchingMark = /*@__PURE__*/Decoration.mark({ class: "cm-nonmatchingBracket" });
 | ||
| function defaultRenderMatch(match) {
 | ||
|     let decorations = [];
 | ||
|     let mark = match.matched ? matchingMark : nonmatchingMark;
 | ||
|     decorations.push(mark.range(match.start.from, match.start.to));
 | ||
|     if (match.end)
 | ||
|         decorations.push(mark.range(match.end.from, match.end.to));
 | ||
|     return decorations;
 | ||
| }
 | ||
| const bracketMatchingState = /*@__PURE__*/StateField.define({
 | ||
|     create() { return Decoration.none; },
 | ||
|     update(deco, tr) {
 | ||
|         if (!tr.docChanged && !tr.selection)
 | ||
|             return deco;
 | ||
|         let decorations = [];
 | ||
|         let config = tr.state.facet(bracketMatchingConfig);
 | ||
|         for (let range of tr.state.selection.ranges) {
 | ||
|             if (!range.empty)
 | ||
|                 continue;
 | ||
|             let match = matchBrackets(tr.state, range.head, -1, config)
 | ||
|                 || (range.head > 0 && matchBrackets(tr.state, range.head - 1, 1, config))
 | ||
|                 || (config.afterCursor &&
 | ||
|                     (matchBrackets(tr.state, range.head, 1, config) ||
 | ||
|                         (range.head < tr.state.doc.length && matchBrackets(tr.state, range.head + 1, -1, config))));
 | ||
|             if (match)
 | ||
|                 decorations = decorations.concat(config.renderMatch(match, tr.state));
 | ||
|         }
 | ||
|         return Decoration.set(decorations, true);
 | ||
|     },
 | ||
|     provide: f => EditorView.decorations.from(f)
 | ||
| });
 | ||
| const bracketMatchingUnique = [
 | ||
|     bracketMatchingState,
 | ||
|     baseTheme
 | ||
| ];
 | ||
| /**
 | ||
| Create an extension that enables bracket matching. Whenever the
 | ||
| cursor is next to a bracket, that bracket and the one it matches
 | ||
| are highlighted. Or, when no matching bracket is found, another
 | ||
| highlighting style is used to indicate this.
 | ||
| */
 | ||
| function bracketMatching(config = {}) {
 | ||
|     return [bracketMatchingConfig.of(config), bracketMatchingUnique];
 | ||
| }
 | ||
| /**
 | ||
| When larger syntax nodes, such as HTML tags, are marked as
 | ||
| opening/closing, it can be a bit messy to treat the whole node as
 | ||
| a matchable bracket. This node prop allows you to define, for such
 | ||
| a node, a ‘handle’—the part of the node that is highlighted, and
 | ||
| that the cursor must be on to activate highlighting in the first
 | ||
| place.
 | ||
| */
 | ||
| const bracketMatchingHandle = /*@__PURE__*/new NodeProp();
 | ||
| function matchingNodes(node, dir, brackets) {
 | ||
|     let byProp = node.prop(dir < 0 ? NodeProp.openedBy : NodeProp.closedBy);
 | ||
|     if (byProp)
 | ||
|         return byProp;
 | ||
|     if (node.name.length == 1) {
 | ||
|         let index = brackets.indexOf(node.name);
 | ||
|         if (index > -1 && index % 2 == (dir < 0 ? 1 : 0))
 | ||
|             return [brackets[index + dir]];
 | ||
|     }
 | ||
|     return null;
 | ||
| }
 | ||
| function findHandle(node) {
 | ||
|     let hasHandle = node.type.prop(bracketMatchingHandle);
 | ||
|     return hasHandle ? hasHandle(node.node) : node;
 | ||
| }
 | ||
| /**
 | ||
| Find the matching bracket for the token at `pos`, scanning
 | ||
| direction `dir`. Only the `brackets` and `maxScanDistance`
 | ||
| properties are used from `config`, if given. Returns null if no
 | ||
| bracket was found at `pos`, or a match result otherwise.
 | ||
| */
 | ||
| function matchBrackets(state, pos, dir, config = {}) {
 | ||
|     let maxScanDistance = config.maxScanDistance || DefaultScanDist, brackets = config.brackets || DefaultBrackets;
 | ||
|     let tree = syntaxTree(state), node = tree.resolveInner(pos, dir);
 | ||
|     for (let cur = node; cur; cur = cur.parent) {
 | ||
|         let matches = matchingNodes(cur.type, dir, brackets);
 | ||
|         if (matches && cur.from < cur.to) {
 | ||
|             let handle = findHandle(cur);
 | ||
|             if (handle && (dir > 0 ? pos >= handle.from && pos < handle.to : pos > handle.from && pos <= handle.to))
 | ||
|                 return matchMarkedBrackets(state, pos, dir, cur, handle, matches, brackets);
 | ||
|         }
 | ||
|     }
 | ||
|     return matchPlainBrackets(state, pos, dir, tree, node.type, maxScanDistance, brackets);
 | ||
| }
 | ||
| function matchMarkedBrackets(_state, _pos, dir, token, handle, matching, brackets) {
 | ||
|     let parent = token.parent, firstToken = { from: handle.from, to: handle.to };
 | ||
|     let depth = 0, cursor = parent === null || parent === void 0 ? void 0 : parent.cursor();
 | ||
|     if (cursor && (dir < 0 ? cursor.childBefore(token.from) : cursor.childAfter(token.to)))
 | ||
|         do {
 | ||
|             if (dir < 0 ? cursor.to <= token.from : cursor.from >= token.to) {
 | ||
|                 if (depth == 0 && matching.indexOf(cursor.type.name) > -1 && cursor.from < cursor.to) {
 | ||
|                     let endHandle = findHandle(cursor);
 | ||
|                     return { start: firstToken, end: endHandle ? { from: endHandle.from, to: endHandle.to } : undefined, matched: true };
 | ||
|                 }
 | ||
|                 else if (matchingNodes(cursor.type, dir, brackets)) {
 | ||
|                     depth++;
 | ||
|                 }
 | ||
|                 else if (matchingNodes(cursor.type, -dir, brackets)) {
 | ||
|                     if (depth == 0) {
 | ||
|                         let endHandle = findHandle(cursor);
 | ||
|                         return {
 | ||
|                             start: firstToken,
 | ||
|                             end: endHandle && endHandle.from < endHandle.to ? { from: endHandle.from, to: endHandle.to } : undefined,
 | ||
|                             matched: false
 | ||
|                         };
 | ||
|                     }
 | ||
|                     depth--;
 | ||
|                 }
 | ||
|             }
 | ||
|         } while (dir < 0 ? cursor.prevSibling() : cursor.nextSibling());
 | ||
|     return { start: firstToken, matched: false };
 | ||
| }
 | ||
| function matchPlainBrackets(state, pos, dir, tree, tokenType, maxScanDistance, brackets) {
 | ||
|     let startCh = dir < 0 ? state.sliceDoc(pos - 1, pos) : state.sliceDoc(pos, pos + 1);
 | ||
|     let bracket = brackets.indexOf(startCh);
 | ||
|     if (bracket < 0 || (bracket % 2 == 0) != (dir > 0))
 | ||
|         return null;
 | ||
|     let startToken = { from: dir < 0 ? pos - 1 : pos, to: dir > 0 ? pos + 1 : pos };
 | ||
|     let iter = state.doc.iterRange(pos, dir > 0 ? state.doc.length : 0), depth = 0;
 | ||
|     for (let distance = 0; !(iter.next()).done && distance <= maxScanDistance;) {
 | ||
|         let text = iter.value;
 | ||
|         if (dir < 0)
 | ||
|             distance += text.length;
 | ||
|         let basePos = pos + distance * dir;
 | ||
|         for (let pos = dir > 0 ? 0 : text.length - 1, end = dir > 0 ? text.length : -1; pos != end; pos += dir) {
 | ||
|             let found = brackets.indexOf(text[pos]);
 | ||
|             if (found < 0 || tree.resolveInner(basePos + pos, 1).type != tokenType)
 | ||
|                 continue;
 | ||
|             if ((found % 2 == 0) == (dir > 0)) {
 | ||
|                 depth++;
 | ||
|             }
 | ||
|             else if (depth == 1) { // Closing
 | ||
|                 return { start: startToken, end: { from: basePos + pos, to: basePos + pos + 1 }, matched: (found >> 1) == (bracket >> 1) };
 | ||
|             }
 | ||
|             else {
 | ||
|                 depth--;
 | ||
|             }
 | ||
|         }
 | ||
|         if (dir > 0)
 | ||
|             distance += text.length;
 | ||
|     }
 | ||
|     return iter.done ? { start: startToken, matched: false } : null;
 | ||
| }
 | ||
| 
 | ||
| // Counts the column offset in a string, taking tabs into account.
 | ||
| // Used mostly to find indentation.
 | ||
| function countCol(string, end, tabSize, startIndex = 0, startValue = 0) {
 | ||
|     if (end == null) {
 | ||
|         end = string.search(/[^\s\u00a0]/);
 | ||
|         if (end == -1)
 | ||
|             end = string.length;
 | ||
|     }
 | ||
|     let n = startValue;
 | ||
|     for (let i = startIndex; i < end; i++) {
 | ||
|         if (string.charCodeAt(i) == 9)
 | ||
|             n += tabSize - (n % tabSize);
 | ||
|         else
 | ||
|             n++;
 | ||
|     }
 | ||
|     return n;
 | ||
| }
 | ||
| /**
 | ||
| Encapsulates a single line of input. Given to stream syntax code,
 | ||
| which uses it to tokenize the content.
 | ||
| */
 | ||
| class StringStream {
 | ||
|     /**
 | ||
|     Create a stream.
 | ||
|     */
 | ||
|     constructor(
 | ||
|     /**
 | ||
|     The line.
 | ||
|     */
 | ||
|     string, tabSize, 
 | ||
|     /**
 | ||
|     The current indent unit size.
 | ||
|     */
 | ||
|     indentUnit, overrideIndent) {
 | ||
|         this.string = string;
 | ||
|         this.tabSize = tabSize;
 | ||
|         this.indentUnit = indentUnit;
 | ||
|         this.overrideIndent = overrideIndent;
 | ||
|         /**
 | ||
|         The current position on the line.
 | ||
|         */
 | ||
|         this.pos = 0;
 | ||
|         /**
 | ||
|         The start position of the current token.
 | ||
|         */
 | ||
|         this.start = 0;
 | ||
|         this.lastColumnPos = 0;
 | ||
|         this.lastColumnValue = 0;
 | ||
|     }
 | ||
|     /**
 | ||
|     True if we are at the end of the line.
 | ||
|     */
 | ||
|     eol() { return this.pos >= this.string.length; }
 | ||
|     /**
 | ||
|     True if we are at the start of the line.
 | ||
|     */
 | ||
|     sol() { return this.pos == 0; }
 | ||
|     /**
 | ||
|     Get the next code unit after the current position, or undefined
 | ||
|     if we're at the end of the line.
 | ||
|     */
 | ||
|     peek() { return this.string.charAt(this.pos) || undefined; }
 | ||
|     /**
 | ||
|     Read the next code unit and advance `this.pos`.
 | ||
|     */
 | ||
|     next() {
 | ||
|         if (this.pos < this.string.length)
 | ||
|             return this.string.charAt(this.pos++);
 | ||
|     }
 | ||
|     /**
 | ||
|     Match the next character against the given string, regular
 | ||
|     expression, or predicate. Consume and return it if it matches.
 | ||
|     */
 | ||
|     eat(match) {
 | ||
|         let ch = this.string.charAt(this.pos);
 | ||
|         let ok;
 | ||
|         if (typeof match == "string")
 | ||
|             ok = ch == match;
 | ||
|         else
 | ||
|             ok = ch && (match instanceof RegExp ? match.test(ch) : match(ch));
 | ||
|         if (ok) {
 | ||
|             ++this.pos;
 | ||
|             return ch;
 | ||
|         }
 | ||
|     }
 | ||
|     /**
 | ||
|     Continue matching characters that match the given string,
 | ||
|     regular expression, or predicate function. Return true if any
 | ||
|     characters were consumed.
 | ||
|     */
 | ||
|     eatWhile(match) {
 | ||
|         let start = this.pos;
 | ||
|         while (this.eat(match)) { }
 | ||
|         return this.pos > start;
 | ||
|     }
 | ||
|     /**
 | ||
|     Consume whitespace ahead of `this.pos`. Return true if any was
 | ||
|     found.
 | ||
|     */
 | ||
|     eatSpace() {
 | ||
|         let start = this.pos;
 | ||
|         while (/[\s\u00a0]/.test(this.string.charAt(this.pos)))
 | ||
|             ++this.pos;
 | ||
|         return this.pos > start;
 | ||
|     }
 | ||
|     /**
 | ||
|     Move to the end of the line.
 | ||
|     */
 | ||
|     skipToEnd() { this.pos = this.string.length; }
 | ||
|     /**
 | ||
|     Move to directly before the given character, if found on the
 | ||
|     current line.
 | ||
|     */
 | ||
|     skipTo(ch) {
 | ||
|         let found = this.string.indexOf(ch, this.pos);
 | ||
|         if (found > -1) {
 | ||
|             this.pos = found;
 | ||
|             return true;
 | ||
|         }
 | ||
|     }
 | ||
|     /**
 | ||
|     Move back `n` characters.
 | ||
|     */
 | ||
|     backUp(n) { this.pos -= n; }
 | ||
|     /**
 | ||
|     Get the column position at `this.pos`.
 | ||
|     */
 | ||
|     column() {
 | ||
|         if (this.lastColumnPos < this.start) {
 | ||
|             this.lastColumnValue = countCol(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
 | ||
|             this.lastColumnPos = this.start;
 | ||
|         }
 | ||
|         return this.lastColumnValue;
 | ||
|     }
 | ||
|     /**
 | ||
|     Get the indentation column of the current line.
 | ||
|     */
 | ||
|     indentation() {
 | ||
|         var _a;
 | ||
|         return (_a = this.overrideIndent) !== null && _a !== void 0 ? _a : countCol(this.string, null, this.tabSize);
 | ||
|     }
 | ||
|     /**
 | ||
|     Match the input against the given string or regular expression
 | ||
|     (which should start with a `^`). Return true or the regexp match
 | ||
|     if it matches.
 | ||
|     
 | ||
|     Unless `consume` is set to `false`, this will move `this.pos`
 | ||
|     past the matched text.
 | ||
|     
 | ||
|     When matching a string `caseInsensitive` can be set to true to
 | ||
|     make the match case-insensitive.
 | ||
|     */
 | ||
|     match(pattern, consume, caseInsensitive) {
 | ||
|         if (typeof pattern == "string") {
 | ||
|             let cased = (str) => caseInsensitive ? str.toLowerCase() : str;
 | ||
|             let substr = this.string.substr(this.pos, pattern.length);
 | ||
|             if (cased(substr) == cased(pattern)) {
 | ||
|                 if (consume !== false)
 | ||
|                     this.pos += pattern.length;
 | ||
|                 return true;
 | ||
|             }
 | ||
|             else
 | ||
|                 return null;
 | ||
|         }
 | ||
|         else {
 | ||
|             let match = this.string.slice(this.pos).match(pattern);
 | ||
|             if (match && match.index > 0)
 | ||
|                 return null;
 | ||
|             if (match && consume !== false)
 | ||
|                 this.pos += match[0].length;
 | ||
|             return match;
 | ||
|         }
 | ||
|     }
 | ||
|     /**
 | ||
|     Get the current token.
 | ||
|     */
 | ||
|     current() { return this.string.slice(this.start, this.pos); }
 | ||
| }
 | ||
| 
 | ||
| function fullParser(spec) {
 | ||
|     return {
 | ||
|         name: spec.name || "",
 | ||
|         token: spec.token,
 | ||
|         blankLine: spec.blankLine || (() => { }),
 | ||
|         startState: spec.startState || (() => true),
 | ||
|         copyState: spec.copyState || defaultCopyState,
 | ||
|         indent: spec.indent || (() => null),
 | ||
|         languageData: spec.languageData || {},
 | ||
|         tokenTable: spec.tokenTable || noTokens
 | ||
|     };
 | ||
| }
 | ||
| function defaultCopyState(state) {
 | ||
|     if (typeof state != "object")
 | ||
|         return state;
 | ||
|     let newState = {};
 | ||
|     for (let prop in state) {
 | ||
|         let val = state[prop];
 | ||
|         newState[prop] = (val instanceof Array ? val.slice() : val);
 | ||
|     }
 | ||
|     return newState;
 | ||
| }
 | ||
| const IndentedFrom = /*@__PURE__*/new WeakMap();
 | ||
| /**
 | ||
| A [language](https://codemirror.net/6/docs/ref/#language.Language) class based on a CodeMirror
 | ||
| 5-style [streaming parser](https://codemirror.net/6/docs/ref/#language.StreamParser).
 | ||
| */
 | ||
| class StreamLanguage extends Language {
 | ||
|     constructor(parser) {
 | ||
|         let data = defineLanguageFacet(parser.languageData);
 | ||
|         let p = fullParser(parser), self;
 | ||
|         let impl = new class extends Parser {
 | ||
|             createParse(input, fragments, ranges) {
 | ||
|                 return new Parse(self, input, fragments, ranges);
 | ||
|             }
 | ||
|         };
 | ||
|         super(data, impl, [indentService.of((cx, pos) => this.getIndent(cx, pos))], parser.name);
 | ||
|         this.topNode = docID(data);
 | ||
|         self = this;
 | ||
|         this.streamParser = p;
 | ||
|         this.stateAfter = new NodeProp({ perNode: true });
 | ||
|         this.tokenTable = parser.tokenTable ? new TokenTable(p.tokenTable) : defaultTokenTable;
 | ||
|     }
 | ||
|     /**
 | ||
|     Define a stream language.
 | ||
|     */
 | ||
|     static define(spec) { return new StreamLanguage(spec); }
 | ||
|     getIndent(cx, pos) {
 | ||
|         let tree = syntaxTree(cx.state), at = tree.resolve(pos);
 | ||
|         while (at && at.type != this.topNode)
 | ||
|             at = at.parent;
 | ||
|         if (!at)
 | ||
|             return null;
 | ||
|         let from = undefined;
 | ||
|         let { overrideIndentation } = cx.options;
 | ||
|         if (overrideIndentation) {
 | ||
|             from = IndentedFrom.get(cx.state);
 | ||
|             if (from != null && from < pos - 1e4)
 | ||
|                 from = undefined;
 | ||
|         }
 | ||
|         let start = findState(this, tree, 0, at.from, from !== null && from !== void 0 ? from : pos), statePos, state;
 | ||
|         if (start) {
 | ||
|             state = start.state;
 | ||
|             statePos = start.pos + 1;
 | ||
|         }
 | ||
|         else {
 | ||
|             state = this.streamParser.startState(cx.unit);
 | ||
|             statePos = 0;
 | ||
|         }
 | ||
|         if (pos - statePos > 10000 /* C.MaxIndentScanDist */)
 | ||
|             return null;
 | ||
|         while (statePos < pos) {
 | ||
|             let line = cx.state.doc.lineAt(statePos), end = Math.min(pos, line.to);
 | ||
|             if (line.length) {
 | ||
|                 let indentation = overrideIndentation ? overrideIndentation(line.from) : -1;
 | ||
|                 let stream = new StringStream(line.text, cx.state.tabSize, cx.unit, indentation < 0 ? undefined : indentation);
 | ||
|                 while (stream.pos < end - line.from)
 | ||
|                     readToken(this.streamParser.token, stream, state);
 | ||
|             }
 | ||
|             else {
 | ||
|                 this.streamParser.blankLine(state, cx.unit);
 | ||
|             }
 | ||
|             if (end == pos)
 | ||
|                 break;
 | ||
|             statePos = line.to + 1;
 | ||
|         }
 | ||
|         let line = cx.lineAt(pos);
 | ||
|         if (overrideIndentation && from == null)
 | ||
|             IndentedFrom.set(cx.state, line.from);
 | ||
|         return this.streamParser.indent(state, /^\s*(.*)/.exec(line.text)[1], cx);
 | ||
|     }
 | ||
|     get allowsNesting() { return false; }
 | ||
| }
 | ||
| function findState(lang, tree, off, startPos, before) {
 | ||
|     let state = off >= startPos && off + tree.length <= before && tree.prop(lang.stateAfter);
 | ||
|     if (state)
 | ||
|         return { state: lang.streamParser.copyState(state), pos: off + tree.length };
 | ||
|     for (let i = tree.children.length - 1; i >= 0; i--) {
 | ||
|         let child = tree.children[i], pos = off + tree.positions[i];
 | ||
|         let found = child instanceof Tree && pos < before && findState(lang, child, pos, startPos, before);
 | ||
|         if (found)
 | ||
|             return found;
 | ||
|     }
 | ||
|     return null;
 | ||
| }
 | ||
| function cutTree(lang, tree, from, to, inside) {
 | ||
|     if (inside && from <= 0 && to >= tree.length)
 | ||
|         return tree;
 | ||
|     if (!inside && tree.type == lang.topNode)
 | ||
|         inside = true;
 | ||
|     for (let i = tree.children.length - 1; i >= 0; i--) {
 | ||
|         let pos = tree.positions[i], child = tree.children[i], inner;
 | ||
|         if (pos < to && child instanceof Tree) {
 | ||
|             if (!(inner = cutTree(lang, child, from - pos, to - pos, inside)))
 | ||
|                 break;
 | ||
|             return !inside ? inner
 | ||
|                 : new Tree(tree.type, tree.children.slice(0, i).concat(inner), tree.positions.slice(0, i + 1), pos + inner.length);
 | ||
|         }
 | ||
|     }
 | ||
|     return null;
 | ||
| }
 | ||
| function findStartInFragments(lang, fragments, startPos, editorState) {
 | ||
|     for (let f of fragments) {
 | ||
|         let from = f.from + (f.openStart ? 25 : 0), to = f.to - (f.openEnd ? 25 : 0);
 | ||
|         let found = from <= startPos && to > startPos && findState(lang, f.tree, 0 - f.offset, startPos, to), tree;
 | ||
|         if (found && (tree = cutTree(lang, f.tree, startPos + f.offset, found.pos + f.offset, false)))
 | ||
|             return { state: found.state, tree };
 | ||
|     }
 | ||
|     return { state: lang.streamParser.startState(editorState ? getIndentUnit(editorState) : 4), tree: Tree.empty };
 | ||
| }
 | ||
| class Parse {
 | ||
|     constructor(lang, input, fragments, ranges) {
 | ||
|         this.lang = lang;
 | ||
|         this.input = input;
 | ||
|         this.fragments = fragments;
 | ||
|         this.ranges = ranges;
 | ||
|         this.stoppedAt = null;
 | ||
|         this.chunks = [];
 | ||
|         this.chunkPos = [];
 | ||
|         this.chunk = [];
 | ||
|         this.chunkReused = undefined;
 | ||
|         this.rangeIndex = 0;
 | ||
|         this.to = ranges[ranges.length - 1].to;
 | ||
|         let context = ParseContext.get(), from = ranges[0].from;
 | ||
|         let { state, tree } = findStartInFragments(lang, fragments, from, context === null || context === void 0 ? void 0 : context.state);
 | ||
|         this.state = state;
 | ||
|         this.parsedPos = this.chunkStart = from + tree.length;
 | ||
|         for (let i = 0; i < tree.children.length; i++) {
 | ||
|             this.chunks.push(tree.children[i]);
 | ||
|             this.chunkPos.push(tree.positions[i]);
 | ||
|         }
 | ||
|         if (context && this.parsedPos < context.viewport.from - 100000 /* C.MaxDistanceBeforeViewport */) {
 | ||
|             this.state = this.lang.streamParser.startState(getIndentUnit(context.state));
 | ||
|             context.skipUntilInView(this.parsedPos, context.viewport.from);
 | ||
|             this.parsedPos = context.viewport.from;
 | ||
|         }
 | ||
|         this.moveRangeIndex();
 | ||
|     }
 | ||
|     advance() {
 | ||
|         let context = ParseContext.get();
 | ||
|         let parseEnd = this.stoppedAt == null ? this.to : Math.min(this.to, this.stoppedAt);
 | ||
|         let end = Math.min(parseEnd, this.chunkStart + 2048 /* C.ChunkSize */);
 | ||
|         if (context)
 | ||
|             end = Math.min(end, context.viewport.to);
 | ||
|         while (this.parsedPos < end)
 | ||
|             this.parseLine(context);
 | ||
|         if (this.chunkStart < this.parsedPos)
 | ||
|             this.finishChunk();
 | ||
|         if (this.parsedPos >= parseEnd)
 | ||
|             return this.finish();
 | ||
|         if (context && this.parsedPos >= context.viewport.to) {
 | ||
|             context.skipUntilInView(this.parsedPos, parseEnd);
 | ||
|             return this.finish();
 | ||
|         }
 | ||
|         return null;
 | ||
|     }
 | ||
|     stopAt(pos) {
 | ||
|         this.stoppedAt = pos;
 | ||
|     }
 | ||
|     lineAfter(pos) {
 | ||
|         let chunk = this.input.chunk(pos);
 | ||
|         if (!this.input.lineChunks) {
 | ||
|             let eol = chunk.indexOf("\n");
 | ||
|             if (eol > -1)
 | ||
|                 chunk = chunk.slice(0, eol);
 | ||
|         }
 | ||
|         else if (chunk == "\n") {
 | ||
|             chunk = "";
 | ||
|         }
 | ||
|         return pos + chunk.length <= this.to ? chunk : chunk.slice(0, this.to - pos);
 | ||
|     }
 | ||
|     nextLine() {
 | ||
|         let from = this.parsedPos, line = this.lineAfter(from), end = from + line.length;
 | ||
|         for (let index = this.rangeIndex;;) {
 | ||
|             let rangeEnd = this.ranges[index].to;
 | ||
|             if (rangeEnd >= end)
 | ||
|                 break;
 | ||
|             line = line.slice(0, rangeEnd - (end - line.length));
 | ||
|             index++;
 | ||
|             if (index == this.ranges.length)
 | ||
|                 break;
 | ||
|             let rangeStart = this.ranges[index].from;
 | ||
|             let after = this.lineAfter(rangeStart);
 | ||
|             line += after;
 | ||
|             end = rangeStart + after.length;
 | ||
|         }
 | ||
|         return { line, end };
 | ||
|     }
 | ||
|     skipGapsTo(pos, offset, side) {
 | ||
|         for (;;) {
 | ||
|             let end = this.ranges[this.rangeIndex].to, offPos = pos + offset;
 | ||
|             if (side > 0 ? end > offPos : end >= offPos)
 | ||
|                 break;
 | ||
|             let start = this.ranges[++this.rangeIndex].from;
 | ||
|             offset += start - end;
 | ||
|         }
 | ||
|         return offset;
 | ||
|     }
 | ||
|     moveRangeIndex() {
 | ||
|         while (this.ranges[this.rangeIndex].to < this.parsedPos)
 | ||
|             this.rangeIndex++;
 | ||
|     }
 | ||
|     emitToken(id, from, to, size, offset) {
 | ||
|         if (this.ranges.length > 1) {
 | ||
|             offset = this.skipGapsTo(from, offset, 1);
 | ||
|             from += offset;
 | ||
|             let len0 = this.chunk.length;
 | ||
|             offset = this.skipGapsTo(to, offset, -1);
 | ||
|             to += offset;
 | ||
|             size += this.chunk.length - len0;
 | ||
|         }
 | ||
|         this.chunk.push(id, from, to, size);
 | ||
|         return offset;
 | ||
|     }
 | ||
|     parseLine(context) {
 | ||
|         let { line, end } = this.nextLine(), offset = 0, { streamParser } = this.lang;
 | ||
|         let stream = new StringStream(line, context ? context.state.tabSize : 4, context ? getIndentUnit(context.state) : 2);
 | ||
|         if (stream.eol()) {
 | ||
|             streamParser.blankLine(this.state, stream.indentUnit);
 | ||
|         }
 | ||
|         else {
 | ||
|             while (!stream.eol()) {
 | ||
|                 let token = readToken(streamParser.token, stream, this.state);
 | ||
|                 if (token)
 | ||
|                     offset = this.emitToken(this.lang.tokenTable.resolve(token), this.parsedPos + stream.start, this.parsedPos + stream.pos, 4, offset);
 | ||
|                 if (stream.start > 10000 /* C.MaxLineLength */)
 | ||
|                     break;
 | ||
|             }
 | ||
|         }
 | ||
|         this.parsedPos = end;
 | ||
|         this.moveRangeIndex();
 | ||
|         if (this.parsedPos < this.to)
 | ||
|             this.parsedPos++;
 | ||
|     }
 | ||
|     finishChunk() {
 | ||
|         let tree = Tree.build({
 | ||
|             buffer: this.chunk,
 | ||
|             start: this.chunkStart,
 | ||
|             length: this.parsedPos - this.chunkStart,
 | ||
|             nodeSet,
 | ||
|             topID: 0,
 | ||
|             maxBufferLength: 2048 /* C.ChunkSize */,
 | ||
|             reused: this.chunkReused
 | ||
|         });
 | ||
|         tree = new Tree(tree.type, tree.children, tree.positions, tree.length, [[this.lang.stateAfter, this.lang.streamParser.copyState(this.state)]]);
 | ||
|         this.chunks.push(tree);
 | ||
|         this.chunkPos.push(this.chunkStart - this.ranges[0].from);
 | ||
|         this.chunk = [];
 | ||
|         this.chunkReused = undefined;
 | ||
|         this.chunkStart = this.parsedPos;
 | ||
|     }
 | ||
|     finish() {
 | ||
|         return new Tree(this.lang.topNode, this.chunks, this.chunkPos, this.parsedPos - this.ranges[0].from).balance();
 | ||
|     }
 | ||
| }
 | ||
| function readToken(token, stream, state) {
 | ||
|     stream.start = stream.pos;
 | ||
|     for (let i = 0; i < 10; i++) {
 | ||
|         let result = token(stream, state);
 | ||
|         if (stream.pos > stream.start)
 | ||
|             return result;
 | ||
|     }
 | ||
|     throw new Error("Stream parser failed to advance stream.");
 | ||
| }
 | ||
| const noTokens = /*@__PURE__*/Object.create(null);
 | ||
| const typeArray = [NodeType.none];
 | ||
| const nodeSet = /*@__PURE__*/new NodeSet(typeArray);
 | ||
| const warned = [];
 | ||
| // Cache of node types by name and tags
 | ||
| const byTag = /*@__PURE__*/Object.create(null);
 | ||
| const defaultTable = /*@__PURE__*/Object.create(null);
 | ||
| for (let [legacyName, name] of [
 | ||
|     ["variable", "variableName"],
 | ||
|     ["variable-2", "variableName.special"],
 | ||
|     ["string-2", "string.special"],
 | ||
|     ["def", "variableName.definition"],
 | ||
|     ["tag", "tagName"],
 | ||
|     ["attribute", "attributeName"],
 | ||
|     ["type", "typeName"],
 | ||
|     ["builtin", "variableName.standard"],
 | ||
|     ["qualifier", "modifier"],
 | ||
|     ["error", "invalid"],
 | ||
|     ["header", "heading"],
 | ||
|     ["property", "propertyName"]
 | ||
| ])
 | ||
|     defaultTable[legacyName] = /*@__PURE__*/createTokenType(noTokens, name);
 | ||
| class TokenTable {
 | ||
|     constructor(extra) {
 | ||
|         this.extra = extra;
 | ||
|         this.table = Object.assign(Object.create(null), defaultTable);
 | ||
|     }
 | ||
|     resolve(tag) {
 | ||
|         return !tag ? 0 : this.table[tag] || (this.table[tag] = createTokenType(this.extra, tag));
 | ||
|     }
 | ||
| }
 | ||
| const defaultTokenTable = /*@__PURE__*/new TokenTable(noTokens);
 | ||
| function warnForPart(part, msg) {
 | ||
|     if (warned.indexOf(part) > -1)
 | ||
|         return;
 | ||
|     warned.push(part);
 | ||
|     console.warn(msg);
 | ||
| }
 | ||
| function createTokenType(extra, tagStr) {
 | ||
|     let tags$1 = [];
 | ||
|     for (let name of tagStr.split(" ")) {
 | ||
|         let found = [];
 | ||
|         for (let part of name.split(".")) {
 | ||
|             let value = (extra[part] || tags[part]);
 | ||
|             if (!value) {
 | ||
|                 warnForPart(part, `Unknown highlighting tag ${part}`);
 | ||
|             }
 | ||
|             else if (typeof value == "function") {
 | ||
|                 if (!found.length)
 | ||
|                     warnForPart(part, `Modifier ${part} used at start of tag`);
 | ||
|                 else
 | ||
|                     found = found.map(value);
 | ||
|             }
 | ||
|             else {
 | ||
|                 if (found.length)
 | ||
|                     warnForPart(part, `Tag ${part} used as modifier`);
 | ||
|                 else
 | ||
|                     found = Array.isArray(value) ? value : [value];
 | ||
|             }
 | ||
|         }
 | ||
|         for (let tag of found)
 | ||
|             tags$1.push(tag);
 | ||
|     }
 | ||
|     if (!tags$1.length)
 | ||
|         return 0;
 | ||
|     let name = tagStr.replace(/ /g, "_"), key = name + " " + tags$1.map(t => t.id);
 | ||
|     let known = byTag[key];
 | ||
|     if (known)
 | ||
|         return known.id;
 | ||
|     let type = byTag[key] = NodeType.define({
 | ||
|         id: typeArray.length,
 | ||
|         name,
 | ||
|         props: [styleTags({ [name]: tags$1 })]
 | ||
|     });
 | ||
|     typeArray.push(type);
 | ||
|     return type.id;
 | ||
| }
 | ||
| function docID(data) {
 | ||
|     let type = NodeType.define({ id: typeArray.length, name: "Document", props: [languageDataProp.add(() => data)], top: true });
 | ||
|     typeArray.push(type);
 | ||
|     return type;
 | ||
| }
 | ||
| 
 | ||
| function buildForLine(line) {
 | ||
|     return line.length <= 4096 && /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac\ufb50-\ufdff]/.test(line);
 | ||
| }
 | ||
| function textHasRTL(text) {
 | ||
|     for (let i = text.iter(); !i.next().done;)
 | ||
|         if (buildForLine(i.value))
 | ||
|             return true;
 | ||
|     return false;
 | ||
| }
 | ||
| function changeAddsRTL(change) {
 | ||
|     let added = false;
 | ||
|     change.iterChanges((fA, tA, fB, tB, ins) => {
 | ||
|         if (!added && textHasRTL(ins))
 | ||
|             added = true;
 | ||
|     });
 | ||
|     return added;
 | ||
| }
 | ||
| const alwaysIsolate = /*@__PURE__*/Facet.define({ combine: values => values.some(x => x) });
 | ||
| /**
 | ||
| Make sure nodes
 | ||
| [marked](https://lezer.codemirror.net/docs/ref/#common.NodeProp^isolate)
 | ||
| as isolating for bidirectional text are rendered in a way that
 | ||
| isolates them from the surrounding text.
 | ||
| */
 | ||
| function bidiIsolates(options = {}) {
 | ||
|     let extensions = [isolateMarks];
 | ||
|     if (options.alwaysIsolate)
 | ||
|         extensions.push(alwaysIsolate.of(true));
 | ||
|     return extensions;
 | ||
| }
 | ||
| const isolateMarks = /*@__PURE__*/ViewPlugin.fromClass(class {
 | ||
|     constructor(view) {
 | ||
|         this.always = view.state.facet(alwaysIsolate) ||
 | ||
|             view.textDirection != Direction.LTR ||
 | ||
|             view.state.facet(EditorView.perLineTextDirection);
 | ||
|         this.hasRTL = !this.always && textHasRTL(view.state.doc);
 | ||
|         this.tree = syntaxTree(view.state);
 | ||
|         this.decorations = this.always || this.hasRTL ? buildDeco(view, this.tree, this.always) : Decoration.none;
 | ||
|     }
 | ||
|     update(update) {
 | ||
|         let always = update.state.facet(alwaysIsolate) ||
 | ||
|             update.view.textDirection != Direction.LTR ||
 | ||
|             update.state.facet(EditorView.perLineTextDirection);
 | ||
|         if (!always && !this.hasRTL && changeAddsRTL(update.changes))
 | ||
|             this.hasRTL = true;
 | ||
|         if (!always && !this.hasRTL)
 | ||
|             return;
 | ||
|         let tree = syntaxTree(update.state);
 | ||
|         if (always != this.always || tree != this.tree || update.docChanged || update.viewportChanged) {
 | ||
|             this.tree = tree;
 | ||
|             this.always = always;
 | ||
|             this.decorations = buildDeco(update.view, tree, always);
 | ||
|         }
 | ||
|     }
 | ||
| }, {
 | ||
|     provide: plugin => {
 | ||
|         function access(view) {
 | ||
|             var _a, _b;
 | ||
|             return (_b = (_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.decorations) !== null && _b !== void 0 ? _b : Decoration.none;
 | ||
|         }
 | ||
|         return [EditorView.outerDecorations.of(access),
 | ||
|             Prec.lowest(EditorView.bidiIsolatedRanges.of(access))];
 | ||
|     }
 | ||
| });
 | ||
| function buildDeco(view, tree, always) {
 | ||
|     let deco = new RangeSetBuilder();
 | ||
|     let ranges = view.visibleRanges;
 | ||
|     if (!always)
 | ||
|         ranges = clipRTLLines(ranges, view.state.doc);
 | ||
|     for (let { from, to } of ranges) {
 | ||
|         tree.iterate({
 | ||
|             enter: node => {
 | ||
|                 let iso = node.type.prop(NodeProp.isolate);
 | ||
|                 if (iso)
 | ||
|                     deco.add(node.from, node.to, marks[iso]);
 | ||
|             },
 | ||
|             from, to
 | ||
|         });
 | ||
|     }
 | ||
|     return deco.finish();
 | ||
| }
 | ||
| function clipRTLLines(ranges, doc) {
 | ||
|     let cur = doc.iter(), pos = 0, result = [], last = null;
 | ||
|     for (let { from, to } of ranges) {
 | ||
|         if (last && last.to > from) {
 | ||
|             from = last.to;
 | ||
|             if (from >= to)
 | ||
|                 continue;
 | ||
|         }
 | ||
|         if (pos + cur.value.length < from) {
 | ||
|             cur.next(from - (pos + cur.value.length));
 | ||
|             pos = from;
 | ||
|         }
 | ||
|         for (;;) {
 | ||
|             let start = pos, end = pos + cur.value.length;
 | ||
|             if (!cur.lineBreak && buildForLine(cur.value)) {
 | ||
|                 if (last && last.to > start - 10)
 | ||
|                     last.to = Math.min(to, end);
 | ||
|                 else
 | ||
|                     result.push(last = { from: start, to: Math.min(to, end) });
 | ||
|             }
 | ||
|             if (end >= to)
 | ||
|                 break;
 | ||
|             pos = end;
 | ||
|             cur.next();
 | ||
|         }
 | ||
|     }
 | ||
|     return result;
 | ||
| }
 | ||
| const marks = {
 | ||
|     rtl: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "rtl" }, bidiIsolate: Direction.RTL }),
 | ||
|     ltr: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "ltr" }, bidiIsolate: Direction.LTR }),
 | ||
|     auto: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "auto" }, bidiIsolate: null })
 | ||
| };
 | ||
| 
 | ||
| export { DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, StreamLanguage, StringStream, TreeIndentContext, bidiIsolates, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };
 |