import { syntaxTree, LRLanguage, indentNodeProp, delimitedIndent, foldNodeProp, LanguageSupport } from '@codemirror/language'; import { html } from '@codemirror/lang-html'; import { styleTags, tags } from '@lezer/highlight'; import { parseMixed } from '@lezer/common'; import { ExternalTokenizer, LRParser } from '@lezer/lr'; import { EditorSelection } from '@codemirror/state'; import { EditorView } from '@codemirror/view'; // This file was generated by lezer-generator. You probably shouldn't edit it. const interpolationStart = 1, tagStart = 2, endTagStart = 3, text = 180, endrawTagStart = 4, rawText = 181, endcommentTagStart = 5, commentText = 182, InlineComment = 6; function wordChar(code) { return code >= 65 && code <= 90 || code >= 97 && code <= 122; } const base = /*@__PURE__*/new ExternalTokenizer(input => { let start = input.pos; for (;;) { let { next } = input; if (next < 0) break; if (next == 123 /* Ch.BraceL */) { let after = input.peek(1); if (after == 123 /* Ch.BraceL */) { if (input.pos > start) break; input.acceptToken(interpolationStart, 2); return; } else if (after == 37 /* Ch.Percent */) { if (input.pos > start) break; let scan = 2, size = 2; for (;;) { let next = input.peek(scan); if (next == 32 /* Ch.Space */ || next == 10 /* Ch.Newline */) { ++scan; } else if (next == 35 /* Ch.Hash */) { ++scan; for (;;) { let comment = input.peek(scan); if (comment < 0 || comment == 10 /* Ch.Newline */) break; scan++; } } else if (next == 45 /* Ch.Dash */ && size == 2) { size = ++scan; } else { let end = next == 101 /* Ch.e */ && input.peek(scan + 1) == 110 /* Ch.n */ && input.peek(scan + 2) == 100 /* Ch.d */; input.acceptToken(end ? endTagStart : tagStart, size); return; } } } } input.advance(); if (next == 10 /* Ch.Newline */) break; } if (input.pos > start) input.acceptToken(text); }); function rawTokenizer(endTag, text, tagStart) { return new ExternalTokenizer(input => { let start = input.pos; for (;;) { let { next } = input; if (next == 123 /* Ch.BraceL */ && input.peek(1) == 37 /* Ch.Percent */) { let scan = 2; for (;; scan++) { let ch = input.peek(scan); if (ch != 32 /* Ch.Space */ && ch != 10 /* Ch.Newline */) break; } let word = ""; for (;; scan++) { let next = input.peek(scan); if (!wordChar(next)) break; word += String.fromCharCode(next); } if (word == endTag) { if (input.pos > start) break; input.acceptToken(tagStart, 2); break; } } else if (next < 0) { break; } input.advance(); if (next == 10 /* Ch.Newline */) break; } if (input.pos > start) input.acceptToken(text); }); } const comment = /*@__PURE__*/rawTokenizer("endcomment", commentText, endcommentTagStart); const raw = /*@__PURE__*/rawTokenizer("endraw", rawText, endrawTagStart); const inlineComment = /*@__PURE__*/new ExternalTokenizer(input => { if (input.next != 35 /* Ch.Hash */) return; input.advance(); for (;;) { if (input.next == 10 /* Ch.Newline */ || input.next < 0) break; if ((input.next == 37 /* Ch.Percent */ || input.next == 125 /* Ch.BraceR */) && input.peek(1) == 125 /* Ch.BraceR */) break; input.advance(); } input.acceptToken(InlineComment); }); // This file was generated by lezer-generator. You probably shouldn't edit it. const spec_identifier = {__proto__:null,contains:34, or:38, and:38, true:52, false:52, empty:54, forloop:56, tablerowloop:58, continue:60, in:130, with:196, for:198, as:200, if:236, endif:240, unless:246, endunless:250, elsif:254, else:258, case:264, endcase:268, when:272, endfor:280, tablerow:286, endtablerow:290, break:294, cycle:300, echo:304, render:308, include:312, assign:316, capture:322, endcapture:326, increment:330, decrement:334}; const spec_TagName = {__proto__:null,if:86, endif:90, elsif:94, else:98, unless:104, endunless:108, case:114, endcase:118, when:122, for:128, endfor:138, tablerow:144, endtablerow:148, break:152, continue:156, cycle:160, comment:166, endcomment:172, raw:178, endraw:184, echo:188, render:192, include:204, assign:208, capture:214, endcapture:218, increment:222, decrement:226, liquid:230}; const parser = /*@__PURE__*/LRParser.deserialize({ version: 14, states: "HOQYOPOOOOOP'#F{'#F{OeOaO'#CdOsQhO'#CfO!bQxO'#DSO#{OPO'#DVO$ZOPO'#D`O$iOPO'#DeO$wOPO'#DlO%VOPO'#DtO%eOSO'#EPO%jOQO'#EVO%oOPO'#EiOOOP'#Gb'#GbOOOP'#G]'#G]OOOP'#Fz'#FzQYOPOOOOOP-E9y-E9yOOQW'#Cg'#CgO&cQ!jO,59QO&jQ!jO'#G^OsQhO'#CtOOQW'#G^'#G^OOOP,59n,59nO)YQhO,59nOsQhO,59rOsQhO,59vO)dQhO,59xOsQhO,59{OsQhO,5:QOsQhO,5:UO!]QhO,5:XO!]QhO,5:aO)iQhO,5:eO)nQhO,5:gO)sQhO,5:iO)xQhO,5:lO)}QhO,5:rOsQhO,5:wOsQhO,5:yOsQhO,5;POsQhO,5;ROsQhO,5;UOsQhO,5;YOsQhO,5;[O+^QhO,5;^O+eOPO'#CdOOOP,59q,59qO#{OPO,59qO+sQxO'#DYOOOP,59z,59zO$ZOPO,59zO+xQxO'#DcOOOP,5:P,5:PO$iOPO,5:PO+}QxO'#DhOOOP,5:W,5:WO$wOPO,5:WO,SQxO'#DrOOOP,5:`,5:`O%VOPO,5:`O,XQxO'#DwOOOS'#GQ'#GQO,^OSO'#ESO,fOSO,5:kOOOQ'#GR'#GRO,kOQO'#EYO,sOQO,5:qOOOP,5;T,5;TO%oOPO,5;TO,xQxO'#ElOOOP-E9x-E9xO,}Q#|O,59SOsQhO,59VOsQhO,59WOsQhO,59WO-SQhO'#C}OOQW'#F|'#F|O-XQhO1G.lOOOP1G.l1G.lOsQhO,59WOsQhO,59[O-rQ!jO,59`O-yQ!jO1G/YO.QQhO1G/YOOOP1G/Y1G/YO.YQ!jO1G/^O.aQ!jO1G/bOOOP1G/d1G/dO.hQ!jO1G/gO.oQ!jO1G/lO.vQ!jO1G/pO/QQhO1G/sO/VQhO1G/{OOOP1G0P1G0POOOP1G0R1G0RO/[QhO1G0TOOOS1G0W1G0WOOOQ1G0^1G0^O/gQ!jO1G0cO/nQ!jO1G0eO0OQ!jO1G0kO0VQ!jO1G0mO0^Q!jO1G0pO0eQ!jO1G0tO0lQ!jO1G0vO0sQhO'#EtO0zQhO'#EyO1RQhO'#FSO1YQhO'#FZO1aQhO'#F_O1hQhO'#FqOOQW'#Gc'#GcOOQW'#GT'#GTO1oQhO1G0xOsQhO'#EuOsQhO'#EzOsQhO'#FOOOQW'#FQ'#FQOsQhO'#FTOsQhO'#FXO!]QhO'#F[O!]QhO'#F`OOQW'#Fd'#FdOOQW'#Ff'#FfO1vQhO'#FgOsQhO'#FiOsQhO'#FkOsQhO'#FmOsQhO'#FoOsQhO'#FrOsQhO'#FvOsQhO'#FxOOOP1G0x1G0xOOOP1G/]1G/]O1{QhO,59tOOOP1G/f1G/fO2QQhO,59}OOOP1G/k1G/kO2VQhO,5:SOOOP1G/r1G/rO2[QhO,5:^OOOP1G/z1G/zO2aQhO,5:cOOOS-E:O-E:OOOOP1G0V1G0VO2fQxO'#ETOOOQ-E:P-E:POOOP1G0]1G0]O2kQxO'#EZOOOP1G0o1G0oO2pQhO,5;WOOQW1G.n1G.nO2uQ!jO1G.qO5fQ!jO1G.rO5mQ!jO1G.rOOQW'#DP'#DPO7{QhO,59iOOQW-E9z-E9zOOOP7+$W7+$WO9uQ!jO1G.rO9|Q!jO1G.vOsQhO1G.zO<[QhO7+$tOOOP7+$t7+$tOOOP7+$x7+$xOOOP7+$|7+$|OOOP7+%R7+%ROOOP7+%W7+%WOsQhO'#F}O}Q!jO,5;fO@^Q!jO,5;jOBPQ!jO,5;oOC`Q!jO,5;sOEUQhO,5;vOEZQhO,5;zOE`QhO,5eOOOPAN>eAN>eO!6_QhOAN>mOOOPAN>mAN>mO!6gQhOAN>uOOOPAN>uAN>uOsQhO1G0gO!]QhO1G0gO!6oQ!jO7+&|O!8RQ!jO7+'QO!9eQhO7+'XOOQW-E:S-E:SO!;XQhO<zQhO< spec_identifier[value] || -1},{term: 39, get: (value) => spec_TagName[value] || -1}], tokenPrec: 0 }); function completions(words, type) { return words.split(" ").map(label => ({ label, type })); } const Filters = /*@__PURE__*/completions("abs append at_least at_most capitalize ceil compact concat date default " + "divided_by downcase escape escape_once first floor join last lstrip map minus modulo " + "newline_to_br plus prepend remove remove_first replace replace_first reverse round rstrip " + "size slice sort sort_natural split strip strip_html strip_newlines sum times truncate " + "truncatewords uniq upcase url_decode url_encode where", "function"); const Tags = /*@__PURE__*/completions("cycle comment endcomment raw endraw echo increment decrement liquid if elsif " + "else endif unless endunless case endcase for endfor tablerow endtablerow break continue " + "assign capture endcapture render include", "keyword"); const Expressions = /*@__PURE__*/completions("empty forloop tablerowloop in with as", "keyword"); const forloop = /*@__PURE__*/completions("first index index0 last length rindex", "property"); const tablerowloop = /*@__PURE__*/completions("col col0 col_first col_last first index index0 last length rindex rindex0 row", "property"); function findContext(context) { var _a; let { state, pos } = context; let node = syntaxTree(state).resolveInner(pos, -1).enterUnfinishedNodesBefore(pos); let before = ((_a = node.childBefore(pos)) === null || _a === void 0 ? void 0 : _a.name) || node.name; if (node.name == "FilterName") return { type: "filter", node }; if (context.explicit && before == "|") return { type: "filter" }; if (node.name == "TagName") return { type: "tag", node }; if (context.explicit && before == "{%") return { type: "tag" }; if (node.name == "PropertyName" && node.parent.name == "MemberExpression") return { type: "property", node, target: node.parent }; if (node.name == "." && node.parent.name == "MemberExpression") return { type: "property", target: node.parent }; if (node.name == "MemberExpression" && before == ".") return { type: "property", target: node }; if (node.name == "VariableName") return { type: "expression", from: node.from }; let word = context.matchBefore(/[\w\u00c0-\uffff]+$/); if (word) return { type: "expression", from: word.from }; if (context.explicit && node.name != "CommentText" && node.name != "StringLiteral" && node.name != "NumberLiteral" && node.name != "InlineComment") return { type: "expression" }; return null; } function resolveProperties(state, node, context, properties) { let path = []; for (;;) { let obj = node.getChild("Expression"); if (!obj) return []; if (obj.name == "forloop") { return path.length ? [] : forloop; } else if (obj.name == "tablerowloop") { return path.length ? [] : tablerowloop; } else if (obj.name == "VariableName") { path.unshift(state.sliceDoc(obj.from, obj.to)); break; } else if (obj.name == "MemberExpression") { let name = obj.getChild("PropertyName"); if (name) path.unshift(state.sliceDoc(name.from, name.to)); node = obj; } else { return []; } } return properties ? properties(path, state, context) : []; } /** Returns a completion source for liquid templates. Optionally takes a configuration that adds additional custom completions. */ function liquidCompletionSource(config = {}) { let filters = config.filters ? config.filters.concat(Filters) : Filters; let tags = config.tags ? config.tags.concat(Tags) : Tags; let exprs = config.variables ? config.variables.concat(Expressions) : Expressions; let { properties } = config; return (context) => { var _a; let cx = findContext(context); if (!cx) return null; let from = (_a = cx.from) !== null && _a !== void 0 ? _a : (cx.node ? cx.node.from : context.pos); let options; if (cx.type == "filter") options = filters; else if (cx.type == "tag") options = tags; else if (cx.type == "expression") options = exprs; else /* property */ options = resolveProperties(context.state, cx.target, context, properties); return options.length ? { options, from, validFor: /^[\w\u00c0-\uffff]*$/ } : null; }; } /** This extension will, when the user types a `%` between two matching braces, insert two percent signs instead and put the cursor between them. */ const closePercentBrace = /*@__PURE__*/EditorView.inputHandler.of((view, from, to, text) => { if (text != "%" || from != to || view.state.doc.sliceString(from - 1, to + 1) != "{}") return false; view.dispatch(view.state.changeByRange(range => ({ changes: { from: range.from, to: range.to, insert: "%%" }, range: EditorSelection.cursor(range.from + 1) })), { scrollIntoView: true, userEvent: "input.type" }); return true; }); function directiveIndent(except) { return (context) => { let back = except.test(context.textAfter); return context.lineIndent(context.node.from) + (back ? 0 : context.unit); }; } const tagLanguage = /*@__PURE__*/LRLanguage.define({ name: "liquid", parser: /*@__PURE__*/parser.configure({ props: [ /*@__PURE__*/styleTags({ "cycle comment endcomment raw endraw echo increment decrement liquid in with as": tags.keyword, "empty forloop tablerowloop": tags.atom, "if elsif else endif unless endunless case endcase for endfor tablerow endtablerow break continue": tags.controlKeyword, "assign capture endcapture": tags.definitionKeyword, "contains": tags.operatorKeyword, "render include": tags.moduleKeyword, VariableName: tags.variableName, TagName: tags.tagName, FilterName: /*@__PURE__*/tags.function(tags.variableName), PropertyName: tags.propertyName, CompareOp: tags.compareOperator, AssignOp: tags.definitionOperator, LogicOp: tags.logicOperator, NumberLiteral: tags.number, StringLiteral: tags.string, BooleanLiteral: tags.bool, InlineComment: tags.lineComment, CommentText: tags.blockComment, "{% %} {{ }}": tags.brace, "[ ]": tags.bracket, "( )": tags.paren, ".": tags.derefOperator, ", .. : |": tags.punctuation }), /*@__PURE__*/indentNodeProp.add({ Tag: /*@__PURE__*/delimitedIndent({ closing: "%}" }), "UnlessDirective ForDirective TablerowDirective CaptureDirective": /*@__PURE__*/directiveIndent(/^\s*(\{%-?\s*)?end\w/), IfDirective: /*@__PURE__*/directiveIndent(/^\s*(\{%-?\s*)?(endif|else|elsif)\b/), CaseDirective: /*@__PURE__*/directiveIndent(/^\s*(\{%-?\s*)?(endcase|when)\b/), }), /*@__PURE__*/foldNodeProp.add({ "UnlessDirective ForDirective TablerowDirective CaptureDirective IfDirective CaseDirective RawDirective Comment"(tree) { let first = tree.firstChild, last = tree.lastChild; if (!first || first.name != "Tag") return null; return { from: first.to, to: last.name == "EndTag" ? last.from : tree.to }; } }) ] }), languageData: { commentTokens: { line: "#" }, indentOnInput: /^\s*{%-?\s*(?:end|elsif|else|when|)$/ } }); const baseHTML = /*@__PURE__*/html(); function makeLiquid(base) { return tagLanguage.configure({ wrap: parseMixed(node => node.type.isTop ? { parser: base.parser, overlay: n => n.name == "Text" || n.name == "RawText" } : null) }, "liquid"); } /** A language provider for Liquid templates. */ const liquidLanguage = /*@__PURE__*/makeLiquid(baseHTML.language); /** Liquid template support. */ function liquid(config = {}) { let base = config.base || baseHTML; let lang = base.language == baseHTML.language ? liquidLanguage : makeLiquid(base.language); return new LanguageSupport(lang, [ base.support, lang.data.of({ autocomplete: liquidCompletionSource(config) }), base.language.data.of({ closeBrackets: { brackets: ["{"] } }), closePercentBrace ]); } export { closePercentBrace, liquid, liquidCompletionSource, liquidLanguage };