668 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			668 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import { parser, configureNesting } from '@lezer/html';
 | |
| import { cssLanguage, css } from '@codemirror/lang-css';
 | |
| import { typescriptLanguage, jsxLanguage, tsxLanguage, javascriptLanguage, javascript } from '@codemirror/lang-javascript';
 | |
| import { EditorView } from '@codemirror/view';
 | |
| import { EditorSelection } from '@codemirror/state';
 | |
| import { LRLanguage, indentNodeProp, foldNodeProp, bracketMatchingHandle, LanguageSupport, syntaxTree } from '@codemirror/language';
 | |
| 
 | |
| const Targets = ["_blank", "_self", "_top", "_parent"];
 | |
| const Charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"];
 | |
| const Methods = ["get", "post", "put", "delete"];
 | |
| const Encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"];
 | |
| const Bool = ["true", "false"];
 | |
| const S = {}; // Empty tag spec
 | |
| const Tags = {
 | |
|     a: {
 | |
|         attrs: {
 | |
|             href: null, ping: null, type: null,
 | |
|             media: null,
 | |
|             target: Targets,
 | |
|             hreflang: null
 | |
|         }
 | |
|     },
 | |
|     abbr: S,
 | |
|     address: S,
 | |
|     area: {
 | |
|         attrs: {
 | |
|             alt: null, coords: null, href: null, target: null, ping: null,
 | |
|             media: null, hreflang: null, type: null,
 | |
|             shape: ["default", "rect", "circle", "poly"]
 | |
|         }
 | |
|     },
 | |
|     article: S,
 | |
|     aside: S,
 | |
|     audio: {
 | |
|         attrs: {
 | |
|             src: null, mediagroup: null,
 | |
|             crossorigin: ["anonymous", "use-credentials"],
 | |
|             preload: ["none", "metadata", "auto"],
 | |
|             autoplay: ["autoplay"],
 | |
|             loop: ["loop"],
 | |
|             controls: ["controls"]
 | |
|         }
 | |
|     },
 | |
|     b: S,
 | |
|     base: { attrs: { href: null, target: Targets } },
 | |
|     bdi: S,
 | |
|     bdo: S,
 | |
|     blockquote: { attrs: { cite: null } },
 | |
|     body: S,
 | |
|     br: S,
 | |
|     button: {
 | |
|         attrs: {
 | |
|             form: null, formaction: null, name: null, value: null,
 | |
|             autofocus: ["autofocus"],
 | |
|             disabled: ["autofocus"],
 | |
|             formenctype: Encs,
 | |
|             formmethod: Methods,
 | |
|             formnovalidate: ["novalidate"],
 | |
|             formtarget: Targets,
 | |
|             type: ["submit", "reset", "button"]
 | |
|         }
 | |
|     },
 | |
|     canvas: { attrs: { width: null, height: null } },
 | |
|     caption: S,
 | |
|     center: S,
 | |
|     cite: S,
 | |
|     code: S,
 | |
|     col: { attrs: { span: null } },
 | |
|     colgroup: { attrs: { span: null } },
 | |
|     command: {
 | |
|         attrs: {
 | |
|             type: ["command", "checkbox", "radio"],
 | |
|             label: null, icon: null, radiogroup: null, command: null, title: null,
 | |
|             disabled: ["disabled"],
 | |
|             checked: ["checked"]
 | |
|         }
 | |
|     },
 | |
|     data: { attrs: { value: null } },
 | |
|     datagrid: { attrs: { disabled: ["disabled"], multiple: ["multiple"] } },
 | |
|     datalist: { attrs: { data: null } },
 | |
|     dd: S,
 | |
|     del: { attrs: { cite: null, datetime: null } },
 | |
|     details: { attrs: { open: ["open"] } },
 | |
|     dfn: S,
 | |
|     div: S,
 | |
|     dl: S,
 | |
|     dt: S,
 | |
|     em: S,
 | |
|     embed: { attrs: { src: null, type: null, width: null, height: null } },
 | |
|     eventsource: { attrs: { src: null } },
 | |
|     fieldset: { attrs: { disabled: ["disabled"], form: null, name: null } },
 | |
|     figcaption: S,
 | |
|     figure: S,
 | |
|     footer: S,
 | |
|     form: {
 | |
|         attrs: {
 | |
|             action: null, name: null,
 | |
|             "accept-charset": Charsets,
 | |
|             autocomplete: ["on", "off"],
 | |
|             enctype: Encs,
 | |
|             method: Methods,
 | |
|             novalidate: ["novalidate"],
 | |
|             target: Targets
 | |
|         }
 | |
|     },
 | |
|     h1: S, h2: S, h3: S, h4: S, h5: S, h6: S,
 | |
|     head: {
 | |
|         children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"]
 | |
|     },
 | |
|     header: S,
 | |
|     hgroup: S,
 | |
|     hr: S,
 | |
|     html: {
 | |
|         attrs: { manifest: null }
 | |
|     },
 | |
|     i: S,
 | |
|     iframe: {
 | |
|         attrs: {
 | |
|             src: null, srcdoc: null, name: null, width: null, height: null,
 | |
|             sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"],
 | |
|             seamless: ["seamless"]
 | |
|         }
 | |
|     },
 | |
|     img: {
 | |
|         attrs: {
 | |
|             alt: null, src: null, ismap: null, usemap: null, width: null, height: null,
 | |
|             crossorigin: ["anonymous", "use-credentials"]
 | |
|         }
 | |
|     },
 | |
|     input: {
 | |
|         attrs: {
 | |
|             alt: null, dirname: null, form: null, formaction: null,
 | |
|             height: null, list: null, max: null, maxlength: null, min: null,
 | |
|             name: null, pattern: null, placeholder: null, size: null, src: null,
 | |
|             step: null, value: null, width: null,
 | |
|             accept: ["audio/*", "video/*", "image/*"],
 | |
|             autocomplete: ["on", "off"],
 | |
|             autofocus: ["autofocus"],
 | |
|             checked: ["checked"],
 | |
|             disabled: ["disabled"],
 | |
|             formenctype: Encs,
 | |
|             formmethod: Methods,
 | |
|             formnovalidate: ["novalidate"],
 | |
|             formtarget: Targets,
 | |
|             multiple: ["multiple"],
 | |
|             readonly: ["readonly"],
 | |
|             required: ["required"],
 | |
|             type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month",
 | |
|                 "week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio",
 | |
|                 "file", "submit", "image", "reset", "button"]
 | |
|         }
 | |
|     },
 | |
|     ins: { attrs: { cite: null, datetime: null } },
 | |
|     kbd: S,
 | |
|     keygen: {
 | |
|         attrs: {
 | |
|             challenge: null, form: null, name: null,
 | |
|             autofocus: ["autofocus"],
 | |
|             disabled: ["disabled"],
 | |
|             keytype: ["RSA"]
 | |
|         }
 | |
|     },
 | |
|     label: { attrs: { for: null, form: null } },
 | |
|     legend: S,
 | |
|     li: { attrs: { value: null } },
 | |
|     link: {
 | |
|         attrs: {
 | |
|             href: null, type: null,
 | |
|             hreflang: null,
 | |
|             media: null,
 | |
|             sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"]
 | |
|         }
 | |
|     },
 | |
|     map: { attrs: { name: null } },
 | |
|     mark: S,
 | |
|     menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } },
 | |
|     meta: {
 | |
|         attrs: {
 | |
|             content: null,
 | |
|             charset: Charsets,
 | |
|             name: ["viewport", "application-name", "author", "description", "generator", "keywords"],
 | |
|             "http-equiv": ["content-language", "content-type", "default-style", "refresh"]
 | |
|         }
 | |
|     },
 | |
|     meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } },
 | |
|     nav: S,
 | |
|     noscript: S,
 | |
|     object: {
 | |
|         attrs: {
 | |
|             data: null, type: null, name: null, usemap: null, form: null, width: null, height: null,
 | |
|             typemustmatch: ["typemustmatch"]
 | |
|         }
 | |
|     },
 | |
|     ol: { attrs: { reversed: ["reversed"], start: null, type: ["1", "a", "A", "i", "I"] },
 | |
|         children: ["li", "script", "template", "ul", "ol"] },
 | |
|     optgroup: { attrs: { disabled: ["disabled"], label: null } },
 | |
|     option: { attrs: { disabled: ["disabled"], label: null, selected: ["selected"], value: null } },
 | |
|     output: { attrs: { for: null, form: null, name: null } },
 | |
|     p: S,
 | |
|     param: { attrs: { name: null, value: null } },
 | |
|     pre: S,
 | |
|     progress: { attrs: { value: null, max: null } },
 | |
|     q: { attrs: { cite: null } },
 | |
|     rp: S,
 | |
|     rt: S,
 | |
|     ruby: S,
 | |
|     samp: S,
 | |
|     script: {
 | |
|         attrs: {
 | |
|             type: ["text/javascript"],
 | |
|             src: null,
 | |
|             async: ["async"],
 | |
|             defer: ["defer"],
 | |
|             charset: Charsets
 | |
|         }
 | |
|     },
 | |
|     section: S,
 | |
|     select: {
 | |
|         attrs: {
 | |
|             form: null, name: null, size: null,
 | |
|             autofocus: ["autofocus"],
 | |
|             disabled: ["disabled"],
 | |
|             multiple: ["multiple"]
 | |
|         }
 | |
|     },
 | |
|     slot: { attrs: { name: null } },
 | |
|     small: S,
 | |
|     source: { attrs: { src: null, type: null, media: null } },
 | |
|     span: S,
 | |
|     strong: S,
 | |
|     style: {
 | |
|         attrs: {
 | |
|             type: ["text/css"],
 | |
|             media: null,
 | |
|             scoped: null
 | |
|         }
 | |
|     },
 | |
|     sub: S,
 | |
|     summary: S,
 | |
|     sup: S,
 | |
|     table: S,
 | |
|     tbody: S,
 | |
|     td: { attrs: { colspan: null, rowspan: null, headers: null } },
 | |
|     template: S,
 | |
|     textarea: {
 | |
|         attrs: {
 | |
|             dirname: null, form: null, maxlength: null, name: null, placeholder: null,
 | |
|             rows: null, cols: null,
 | |
|             autofocus: ["autofocus"],
 | |
|             disabled: ["disabled"],
 | |
|             readonly: ["readonly"],
 | |
|             required: ["required"],
 | |
|             wrap: ["soft", "hard"]
 | |
|         }
 | |
|     },
 | |
|     tfoot: S,
 | |
|     th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } },
 | |
|     thead: S,
 | |
|     time: { attrs: { datetime: null } },
 | |
|     title: S,
 | |
|     tr: S,
 | |
|     track: {
 | |
|         attrs: {
 | |
|             src: null, label: null, default: null,
 | |
|             kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"],
 | |
|             srclang: null
 | |
|         }
 | |
|     },
 | |
|     ul: { children: ["li", "script", "template", "ul", "ol"] },
 | |
|     var: S,
 | |
|     video: {
 | |
|         attrs: {
 | |
|             src: null, poster: null, width: null, height: null,
 | |
|             crossorigin: ["anonymous", "use-credentials"],
 | |
|             preload: ["auto", "metadata", "none"],
 | |
|             autoplay: ["autoplay"],
 | |
|             mediagroup: ["movie"],
 | |
|             muted: ["muted"],
 | |
|             controls: ["controls"]
 | |
|         }
 | |
|     },
 | |
|     wbr: S
 | |
| };
 | |
| const GlobalAttrs = {
 | |
|     accesskey: null,
 | |
|     class: null,
 | |
|     contenteditable: Bool,
 | |
|     contextmenu: null,
 | |
|     dir: ["ltr", "rtl", "auto"],
 | |
|     draggable: ["true", "false", "auto"],
 | |
|     dropzone: ["copy", "move", "link", "string:", "file:"],
 | |
|     hidden: ["hidden"],
 | |
|     id: null,
 | |
|     inert: ["inert"],
 | |
|     itemid: null,
 | |
|     itemprop: null,
 | |
|     itemref: null,
 | |
|     itemscope: ["itemscope"],
 | |
|     itemtype: null,
 | |
|     lang: ["ar", "bn", "de", "en-GB", "en-US", "es", "fr", "hi", "id", "ja", "pa", "pt", "ru", "tr", "zh"],
 | |
|     spellcheck: Bool,
 | |
|     autocorrect: Bool,
 | |
|     autocapitalize: Bool,
 | |
|     style: null,
 | |
|     tabindex: null,
 | |
|     title: null,
 | |
|     translate: ["yes", "no"],
 | |
|     rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"],
 | |
|     role: /*@__PURE__*/"alert application article banner button cell checkbox complementary contentinfo dialog document feed figure form grid gridcell heading img list listbox listitem main navigation region row rowgroup search switch tab table tabpanel textbox timer".split(" "),
 | |
|     "aria-activedescendant": null,
 | |
|     "aria-atomic": Bool,
 | |
|     "aria-autocomplete": ["inline", "list", "both", "none"],
 | |
|     "aria-busy": Bool,
 | |
|     "aria-checked": ["true", "false", "mixed", "undefined"],
 | |
|     "aria-controls": null,
 | |
|     "aria-describedby": null,
 | |
|     "aria-disabled": Bool,
 | |
|     "aria-dropeffect": null,
 | |
|     "aria-expanded": ["true", "false", "undefined"],
 | |
|     "aria-flowto": null,
 | |
|     "aria-grabbed": ["true", "false", "undefined"],
 | |
|     "aria-haspopup": Bool,
 | |
|     "aria-hidden": Bool,
 | |
|     "aria-invalid": ["true", "false", "grammar", "spelling"],
 | |
|     "aria-label": null,
 | |
|     "aria-labelledby": null,
 | |
|     "aria-level": null,
 | |
|     "aria-live": ["off", "polite", "assertive"],
 | |
|     "aria-multiline": Bool,
 | |
|     "aria-multiselectable": Bool,
 | |
|     "aria-owns": null,
 | |
|     "aria-posinset": null,
 | |
|     "aria-pressed": ["true", "false", "mixed", "undefined"],
 | |
|     "aria-readonly": Bool,
 | |
|     "aria-relevant": null,
 | |
|     "aria-required": Bool,
 | |
|     "aria-selected": ["true", "false", "undefined"],
 | |
|     "aria-setsize": null,
 | |
|     "aria-sort": ["ascending", "descending", "none", "other"],
 | |
|     "aria-valuemax": null,
 | |
|     "aria-valuemin": null,
 | |
|     "aria-valuenow": null,
 | |
|     "aria-valuetext": null
 | |
| };
 | |
| const eventAttributes = /*@__PURE__*/("beforeunload copy cut dragstart dragover dragleave dragenter dragend " +
 | |
|     "drag paste focus blur change click load mousedown mouseenter mouseleave " +
 | |
|     "mouseup keydown keyup resize scroll unload").split(" ").map(n => "on" + n);
 | |
| for (let a of eventAttributes)
 | |
|     GlobalAttrs[a] = null;
 | |
| class Schema {
 | |
|     constructor(extraTags, extraAttrs) {
 | |
|         this.tags = Object.assign(Object.assign({}, Tags), extraTags);
 | |
|         this.globalAttrs = Object.assign(Object.assign({}, GlobalAttrs), extraAttrs);
 | |
|         this.allTags = Object.keys(this.tags);
 | |
|         this.globalAttrNames = Object.keys(this.globalAttrs);
 | |
|     }
 | |
| }
 | |
| Schema.default = /*@__PURE__*/new Schema;
 | |
| function elementName(doc, tree, max = doc.length) {
 | |
|     if (!tree)
 | |
|         return "";
 | |
|     let tag = tree.firstChild;
 | |
|     let name = tag && tag.getChild("TagName");
 | |
|     return name ? doc.sliceString(name.from, Math.min(name.to, max)) : "";
 | |
| }
 | |
| function findParentElement(tree, skip = false) {
 | |
|     for (; tree; tree = tree.parent)
 | |
|         if (tree.name == "Element") {
 | |
|             if (skip)
 | |
|                 skip = false;
 | |
|             else
 | |
|                 return tree;
 | |
|         }
 | |
|     return null;
 | |
| }
 | |
| function allowedChildren(doc, tree, schema) {
 | |
|     let parentInfo = schema.tags[elementName(doc, findParentElement(tree))];
 | |
|     return (parentInfo === null || parentInfo === void 0 ? void 0 : parentInfo.children) || schema.allTags;
 | |
| }
 | |
| function openTags(doc, tree) {
 | |
|     let open = [];
 | |
|     for (let parent = findParentElement(tree); parent && !parent.type.isTop; parent = findParentElement(parent.parent)) {
 | |
|         let tagName = elementName(doc, parent);
 | |
|         if (tagName && parent.lastChild.name == "CloseTag")
 | |
|             break;
 | |
|         if (tagName && open.indexOf(tagName) < 0 && (tree.name == "EndTag" || tree.from >= parent.firstChild.to))
 | |
|             open.push(tagName);
 | |
|     }
 | |
|     return open;
 | |
| }
 | |
| const identifier = /^[:\-\.\w\u00b7-\uffff]*$/;
 | |
| function completeTag(state, schema, tree, from, to) {
 | |
|     let end = /\s*>/.test(state.sliceDoc(to, to + 5)) ? "" : ">";
 | |
|     let parent = findParentElement(tree, true);
 | |
|     return { from, to,
 | |
|         options: allowedChildren(state.doc, parent, schema).map(tagName => ({ label: tagName, type: "type" })).concat(openTags(state.doc, tree).map((tag, i) => ({ label: "/" + tag, apply: "/" + tag + end,
 | |
|             type: "type", boost: 99 - i }))),
 | |
|         validFor: /^\/?[:\-\.\w\u00b7-\uffff]*$/ };
 | |
| }
 | |
| function completeCloseTag(state, tree, from, to) {
 | |
|     let end = /\s*>/.test(state.sliceDoc(to, to + 5)) ? "" : ">";
 | |
|     return { from, to,
 | |
|         options: openTags(state.doc, tree).map((tag, i) => ({ label: tag, apply: tag + end, type: "type", boost: 99 - i })),
 | |
|         validFor: identifier };
 | |
| }
 | |
| function completeStartTag(state, schema, tree, pos) {
 | |
|     let options = [], level = 0;
 | |
|     for (let tagName of allowedChildren(state.doc, tree, schema))
 | |
|         options.push({ label: "<" + tagName, type: "type" });
 | |
|     for (let open of openTags(state.doc, tree))
 | |
|         options.push({ label: "</" + open + ">", type: "type", boost: 99 - level++ });
 | |
|     return { from: pos, to: pos, options, validFor: /^<\/?[:\-\.\w\u00b7-\uffff]*$/ };
 | |
| }
 | |
| function completeAttrName(state, schema, tree, from, to) {
 | |
|     let elt = findParentElement(tree), info = elt ? schema.tags[elementName(state.doc, elt)] : null;
 | |
|     let localAttrs = info && info.attrs ? Object.keys(info.attrs) : [];
 | |
|     let names = info && info.globalAttrs === false ? localAttrs
 | |
|         : localAttrs.length ? localAttrs.concat(schema.globalAttrNames) : schema.globalAttrNames;
 | |
|     return { from, to,
 | |
|         options: names.map(attrName => ({ label: attrName, type: "property" })),
 | |
|         validFor: identifier };
 | |
| }
 | |
| function completeAttrValue(state, schema, tree, from, to) {
 | |
|     var _a;
 | |
|     let nameNode = (_a = tree.parent) === null || _a === void 0 ? void 0 : _a.getChild("AttributeName");
 | |
|     let options = [], token = undefined;
 | |
|     if (nameNode) {
 | |
|         let attrName = state.sliceDoc(nameNode.from, nameNode.to);
 | |
|         let attrs = schema.globalAttrs[attrName];
 | |
|         if (!attrs) {
 | |
|             let elt = findParentElement(tree), info = elt ? schema.tags[elementName(state.doc, elt)] : null;
 | |
|             attrs = (info === null || info === void 0 ? void 0 : info.attrs) && info.attrs[attrName];
 | |
|         }
 | |
|         if (attrs) {
 | |
|             let base = state.sliceDoc(from, to).toLowerCase(), quoteStart = '"', quoteEnd = '"';
 | |
|             if (/^['"]/.test(base)) {
 | |
|                 token = base[0] == '"' ? /^[^"]*$/ : /^[^']*$/;
 | |
|                 quoteStart = "";
 | |
|                 quoteEnd = state.sliceDoc(to, to + 1) == base[0] ? "" : base[0];
 | |
|                 base = base.slice(1);
 | |
|                 from++;
 | |
|             }
 | |
|             else {
 | |
|                 token = /^[^\s<>='"]*$/;
 | |
|             }
 | |
|             for (let value of attrs)
 | |
|                 options.push({ label: value, apply: quoteStart + value + quoteEnd, type: "constant" });
 | |
|         }
 | |
|     }
 | |
|     return { from, to, options, validFor: token };
 | |
| }
 | |
| function htmlCompletionFor(schema, context) {
 | |
|     let { state, pos } = context, tree = syntaxTree(state).resolveInner(pos, -1), around = tree.resolve(pos);
 | |
|     for (let scan = pos, before; around == tree && (before = tree.childBefore(scan));) {
 | |
|         let last = before.lastChild;
 | |
|         if (!last || !last.type.isError || last.from < last.to)
 | |
|             break;
 | |
|         around = tree = before;
 | |
|         scan = last.from;
 | |
|     }
 | |
|     if (tree.name == "TagName") {
 | |
|         return tree.parent && /CloseTag$/.test(tree.parent.name) ? completeCloseTag(state, tree, tree.from, pos)
 | |
|             : completeTag(state, schema, tree, tree.from, pos);
 | |
|     }
 | |
|     else if (tree.name == "StartTag") {
 | |
|         return completeTag(state, schema, tree, pos, pos);
 | |
|     }
 | |
|     else if (tree.name == "StartCloseTag" || tree.name == "IncompleteCloseTag") {
 | |
|         return completeCloseTag(state, tree, pos, pos);
 | |
|     }
 | |
|     else if (tree.name == "OpenTag" || tree.name == "SelfClosingTag" || tree.name == "AttributeName") {
 | |
|         return completeAttrName(state, schema, tree, tree.name == "AttributeName" ? tree.from : pos, pos);
 | |
|     }
 | |
|     else if (tree.name == "Is" || tree.name == "AttributeValue" || tree.name == "UnquotedAttributeValue") {
 | |
|         return completeAttrValue(state, schema, tree, tree.name == "Is" ? pos : tree.from, pos);
 | |
|     }
 | |
|     else if (context.explicit && (around.name == "Element" || around.name == "Text" || around.name == "Document")) {
 | |
|         return completeStartTag(state, schema, tree, pos);
 | |
|     }
 | |
|     else {
 | |
|         return null;
 | |
|     }
 | |
| }
 | |
| /**
 | |
| HTML tag completion. Opens and closes tags and attributes in a
 | |
| context-aware way.
 | |
| */
 | |
| function htmlCompletionSource(context) {
 | |
|     return htmlCompletionFor(Schema.default, context);
 | |
| }
 | |
| /**
 | |
| Create a completion source for HTML extended with additional tags
 | |
| or attributes.
 | |
| */
 | |
| function htmlCompletionSourceWith(config) {
 | |
|     let { extraTags, extraGlobalAttributes: extraAttrs } = config;
 | |
|     let schema = extraAttrs || extraTags ? new Schema(extraTags, extraAttrs) : Schema.default;
 | |
|     return (context) => htmlCompletionFor(schema, context);
 | |
| }
 | |
| 
 | |
| const jsonParser = /*@__PURE__*/javascriptLanguage.parser.configure({ top: "SingleExpression" });
 | |
| const defaultNesting = [
 | |
|     { tag: "script",
 | |
|         attrs: attrs => attrs.type == "text/typescript" || attrs.lang == "ts",
 | |
|         parser: typescriptLanguage.parser },
 | |
|     { tag: "script",
 | |
|         attrs: attrs => attrs.type == "text/babel" || attrs.type == "text/jsx",
 | |
|         parser: jsxLanguage.parser },
 | |
|     { tag: "script",
 | |
|         attrs: attrs => attrs.type == "text/typescript-jsx",
 | |
|         parser: tsxLanguage.parser },
 | |
|     { tag: "script",
 | |
|         attrs(attrs) {
 | |
|             return /^(importmap|speculationrules|application\/(.+\+)?json)$/i.test(attrs.type);
 | |
|         },
 | |
|         parser: jsonParser },
 | |
|     { tag: "script",
 | |
|         attrs(attrs) {
 | |
|             return !attrs.type || /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i.test(attrs.type);
 | |
|         },
 | |
|         parser: javascriptLanguage.parser },
 | |
|     { tag: "style",
 | |
|         attrs(attrs) {
 | |
|             return (!attrs.lang || attrs.lang == "css") && (!attrs.type || /^(text\/)?(x-)?(stylesheet|css)$/i.test(attrs.type));
 | |
|         },
 | |
|         parser: cssLanguage.parser }
 | |
| ];
 | |
| const defaultAttrs = /*@__PURE__*/[
 | |
|     { name: "style",
 | |
|         parser: /*@__PURE__*/cssLanguage.parser.configure({ top: "Styles" }) }
 | |
| ].concat(/*@__PURE__*/eventAttributes.map(name => ({ name, parser: javascriptLanguage.parser })));
 | |
| /**
 | |
| A language provider based on the [Lezer HTML
 | |
| parser](https://github.com/lezer-parser/html), extended with the
 | |
| JavaScript and CSS parsers to parse the content of `<script>` and
 | |
| `<style>` tags.
 | |
| */
 | |
| const htmlPlain = /*@__PURE__*/LRLanguage.define({
 | |
|     name: "html",
 | |
|     parser: /*@__PURE__*/parser.configure({
 | |
|         props: [
 | |
|             /*@__PURE__*/indentNodeProp.add({
 | |
|                 Element(context) {
 | |
|                     let after = /^(\s*)(<\/)?/.exec(context.textAfter);
 | |
|                     if (context.node.to <= context.pos + after[0].length)
 | |
|                         return context.continue();
 | |
|                     return context.lineIndent(context.node.from) + (after[2] ? 0 : context.unit);
 | |
|                 },
 | |
|                 "OpenTag CloseTag SelfClosingTag"(context) {
 | |
|                     return context.column(context.node.from) + context.unit;
 | |
|                 },
 | |
|                 Document(context) {
 | |
|                     if (context.pos + /\s*/.exec(context.textAfter)[0].length < context.node.to)
 | |
|                         return context.continue();
 | |
|                     let endElt = null, close;
 | |
|                     for (let cur = context.node;;) {
 | |
|                         let last = cur.lastChild;
 | |
|                         if (!last || last.name != "Element" || last.to != cur.to)
 | |
|                             break;
 | |
|                         endElt = cur = last;
 | |
|                     }
 | |
|                     if (endElt && !((close = endElt.lastChild) && (close.name == "CloseTag" || close.name == "SelfClosingTag")))
 | |
|                         return context.lineIndent(endElt.from) + context.unit;
 | |
|                     return null;
 | |
|                 }
 | |
|             }),
 | |
|             /*@__PURE__*/foldNodeProp.add({
 | |
|                 Element(node) {
 | |
|                     let first = node.firstChild, last = node.lastChild;
 | |
|                     if (!first || first.name != "OpenTag")
 | |
|                         return null;
 | |
|                     return { from: first.to, to: last.name == "CloseTag" ? last.from : node.to };
 | |
|                 }
 | |
|             }),
 | |
|             /*@__PURE__*/bracketMatchingHandle.add({
 | |
|                 "OpenTag CloseTag": node => node.getChild("TagName")
 | |
|             })
 | |
|         ]
 | |
|     }),
 | |
|     languageData: {
 | |
|         commentTokens: { block: { open: "<!--", close: "-->" } },
 | |
|         indentOnInput: /^\s*<\/\w+\W$/,
 | |
|         wordChars: "-._"
 | |
|     }
 | |
| });
 | |
| /**
 | |
| A language provider based on the [Lezer HTML
 | |
| parser](https://github.com/lezer-parser/html), extended with the
 | |
| JavaScript and CSS parsers to parse the content of `<script>` and
 | |
| `<style>` tags.
 | |
| */
 | |
| const htmlLanguage = /*@__PURE__*/htmlPlain.configure({
 | |
|     wrap: /*@__PURE__*/configureNesting(defaultNesting, defaultAttrs)
 | |
| });
 | |
| /**
 | |
| Language support for HTML, including
 | |
| [`htmlCompletion`](https://codemirror.net/6/docs/ref/#lang-html.htmlCompletion) and JavaScript and
 | |
| CSS support extensions.
 | |
| */
 | |
| function html(config = {}) {
 | |
|     let dialect = "", wrap;
 | |
|     if (config.matchClosingTags === false)
 | |
|         dialect = "noMatch";
 | |
|     if (config.selfClosingTags === true)
 | |
|         dialect = (dialect ? dialect + " " : "") + "selfClosing";
 | |
|     if (config.nestedLanguages && config.nestedLanguages.length ||
 | |
|         config.nestedAttributes && config.nestedAttributes.length)
 | |
|         wrap = configureNesting((config.nestedLanguages || []).concat(defaultNesting), (config.nestedAttributes || []).concat(defaultAttrs));
 | |
|     let lang = wrap ? htmlPlain.configure({ wrap, dialect }) : dialect ? htmlLanguage.configure({ dialect }) : htmlLanguage;
 | |
|     return new LanguageSupport(lang, [
 | |
|         htmlLanguage.data.of({ autocomplete: htmlCompletionSourceWith(config) }),
 | |
|         config.autoCloseTags !== false ? autoCloseTags : [],
 | |
|         javascript().support,
 | |
|         css().support
 | |
|     ]);
 | |
| }
 | |
| const selfClosers = /*@__PURE__*/new Set(/*@__PURE__*/"area base br col command embed frame hr img input keygen link meta param source track wbr menuitem".split(" "));
 | |
| /**
 | |
| Extension that will automatically insert close tags when a `>` or
 | |
| `/` is typed.
 | |
| */
 | |
| const autoCloseTags = /*@__PURE__*/EditorView.inputHandler.of((view, from, to, text, insertTransaction) => {
 | |
|     if (view.composing || view.state.readOnly || from != to || (text != ">" && text != "/") ||
 | |
|         !htmlLanguage.isActiveAt(view.state, from, -1))
 | |
|         return false;
 | |
|     let base = insertTransaction(), { state } = base;
 | |
|     let closeTags = state.changeByRange(range => {
 | |
|         var _a, _b, _c;
 | |
|         let didType = state.doc.sliceString(range.from - 1, range.to) == text;
 | |
|         let { head } = range, after = syntaxTree(state).resolveInner(head, -1), name;
 | |
|         if (didType && text == ">" && after.name == "EndTag") {
 | |
|             let tag = after.parent;
 | |
|             if (((_b = (_a = tag.parent) === null || _a === void 0 ? void 0 : _a.lastChild) === null || _b === void 0 ? void 0 : _b.name) != "CloseTag" &&
 | |
|                 (name = elementName(state.doc, tag.parent, head)) &&
 | |
|                 !selfClosers.has(name)) {
 | |
|                 let to = head + (state.doc.sliceString(head, head + 1) === ">" ? 1 : 0);
 | |
|                 let insert = `</${name}>`;
 | |
|                 return { range, changes: { from: head, to, insert } };
 | |
|             }
 | |
|         }
 | |
|         else if (didType && text == "/" && after.name == "IncompleteCloseTag") {
 | |
|             let tag = after.parent;
 | |
|             if (after.from == head - 2 && ((_c = tag.lastChild) === null || _c === void 0 ? void 0 : _c.name) != "CloseTag" &&
 | |
|                 (name = elementName(state.doc, tag, head)) && !selfClosers.has(name)) {
 | |
|                 let to = head + (state.doc.sliceString(head, head + 1) === ">" ? 1 : 0);
 | |
|                 let insert = `${name}>`;
 | |
|                 return {
 | |
|                     range: EditorSelection.cursor(head + insert.length, -1),
 | |
|                     changes: { from: head, to, insert }
 | |
|                 };
 | |
|             }
 | |
|         }
 | |
|         return { range };
 | |
|     });
 | |
|     if (closeTags.changes.empty)
 | |
|         return false;
 | |
|     view.dispatch([
 | |
|         base,
 | |
|         state.update(closeTags, {
 | |
|             userEvent: "input.complete",
 | |
|             scrollIntoView: true
 | |
|         })
 | |
|     ]);
 | |
|     return true;
 | |
| });
 | |
| 
 | |
| export { autoCloseTags, html, htmlCompletionSource, htmlCompletionSourceWith, htmlLanguage, htmlPlain };
 |