@lucidkiln/language-server 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","names":["docs"],"sources":["../src/builtins.ts","../src/context.ts","../src/docs.json","../src/docs.ts","../src/html.ts","../src/project.ts","../src/features.ts","../src/server.ts","../src/main.ts"],"sourcesContent":["/** A built-in filter, function or test: how to call it, for completions and hover. What it does comes from `docs.json`. */\nexport type BuiltinDoc = { signature: string };\n\n/** A tag: the snippet that completes it after `{%`. `$0` marks where the cursor ends. What it does comes from `docs.json`. */\nexport type TagDoc = { snippet: string };\n\nconst block = (name: string, header: string) =>\n\t`${name}${header} %}\\n\\t$0\\n{% end${name} %}`;\n\nexport const TAGS: Record<string, TagDoc> = {\n\tif: {\n\t\tsnippet: block(\"if\", \" ${1:condition}\"),\n\t},\n\telseif: {\n\t\tsnippet: \"elseif ${1:condition} %}\",\n\t},\n\telse: {\n\t\tsnippet: \"else %}\",\n\t},\n\tfor: {\n\t\tsnippet: block(\"for\", \" ${1:item} in ${2:items}\"),\n\t},\n\tset: {\n\t\tsnippet: \"set ${1:name} = ${2:value} %}\",\n\t},\n\tprops: {\n\t\tsnippet: \"props ${1:name} %}\",\n\t},\n\textends: {\n\t\tsnippet: 'extends \"${1:layouts/base}\" %}',\n\t},\n\tblock: {\n\t\tsnippet: block(\"block\", \" ${1:name}\"),\n\t},\n\tinclude: {\n\t\tsnippet: 'include \"${1}\" %}',\n\t},\n\tcomponent: {\n\t\tsnippet: 'component \"${1}\" with { $2 } %}\\n\\t$0\\n{% endcomponent %}',\n\t},\n\tisland: {\n\t\tsnippet: 'island \"${1}\" with { $2 } %}\\n\\t$0\\n{% endisland %}',\n\t},\n\tfill: {\n\t\tsnippet: block(\"fill\", ' \"${1:name}\"'),\n\t},\n\tslot: {\n\t\tsnippet: \"slot %}$0{% endslot %}\",\n\t},\n\tmacro: {\n\t\tsnippet: block(\"macro\", \" ${1:name}(${2})\"),\n\t},\n\timport: {\n\t\tsnippet: 'import \"${1}\" as ${2:name} %}',\n\t},\n\tfrom: {\n\t\tsnippet: 'from \"${1}\" import ${2:name} %}',\n\t},\n\twith: {\n\t\tsnippet: block(\"with\", \" { ${1} }\"),\n\t},\n\tcache: {\n\t\tsnippet: block(\"cache\", ' \"${1:key}\" for ${2:600}'),\n\t},\n\toutlet: {\n\t\tsnippet: 'outlet \"${1:head}\" %}',\n\t},\n\tadd: {\n\t\tsnippet: block(\"add\", ' to \"${1:head}\"'),\n\t},\n\tflush: {\n\t\tsnippet: \"flush %}\",\n\t},\n\tapply: {\n\t\tsnippet: block(\"apply\", \" ${1:filter}\"),\n\t},\n\tautoescape: {\n\t\tsnippet: block(\"autoescape\", ' \"${1:html}\"'),\n\t},\n\tverbatim: {\n\t\tsnippet: \"verbatim %}\\n$0\\n{% endverbatim %}\",\n\t},\n};\n\n/** Tags that have a body and an end tag. */\nexport const BLOCK_TAGS = new Set([\n\t\"if\",\n\t\"for\",\n\t\"set\",\n\t\"block\",\n\t\"component\",\n\t\"island\",\n\t\"fill\",\n\t\"slot\",\n\t\"macro\",\n\t\"with\",\n\t\"cache\",\n\t\"add\",\n\t\"apply\",\n\t\"autoescape\",\n\t\"verbatim\",\n]);\n\nexport const FILTERS: Record<string, BuiltinDoc> = {\n\tupper: { signature: \"upper\" },\n\tlower: { signature: \"lower\" },\n\tcapitalize: {\n\t\tsignature: \"capitalize\",\n\t},\n\ttitle: {\n\t\tsignature: \"title\",\n\t},\n\ttrim: {\n\t\tsignature: \"trim(characters?, side?)\",\n\t},\n\treplace: {\n\t\tsignature: \"replace(search, replacement)\",\n\t},\n\tsplit: {\n\t\tsignature: \"split(separator, limit?)\",\n\t},\n\ttruncate: {\n\t\tsignature: \"truncate(length, ellipsis?)\",\n\t},\n\tstripTags: {\n\t\tsignature: \"stripTags\",\n\t},\n\tnl2br: {\n\t\tsignature: \"nl2br\",\n\t},\n\turlEncode: {\n\t\tsignature: \"urlEncode\",\n\t},\n\tjson: {\n\t\tsignature: \"json(indent?)\",\n\t},\n\tescape: {\n\t\tsignature: \"escape(strategy?)\",\n\t},\n\traw: {\n\t\tsignature: \"raw\",\n\t},\n\tdefault: {\n\t\tsignature: \"default(fallback)\",\n\t},\n\tlength: {\n\t\tsignature: \"length\",\n\t},\n\tfirst: {\n\t\tsignature: \"first\",\n\t},\n\tlast: {\n\t\tsignature: \"last\",\n\t},\n\tslice: {\n\t\tsignature: \"slice(start, end?)\",\n\t},\n\tjoin: {\n\t\tsignature: \"join(glue?, lastGlue?)\",\n\t},\n\tkeys: {\n\t\tsignature: \"keys\",\n\t},\n\tmerge: {\n\t\tsignature: \"merge(...values)\",\n\t},\n\treverse: {\n\t\tsignature: \"reverse\",\n\t},\n\tsort: {\n\t\tsignature: \"sort(by?)\",\n\t},\n\tmap: {\n\t\tsignature: \"map((value, key) => ...)\",\n\t},\n\tfilter: {\n\t\tsignature: \"filter((value, key) => ...)\",\n\t},\n\treduce: {\n\t\tsignature: \"reduce((total, value, key) => ..., initial)\",\n\t},\n\tfind: {\n\t\tsignature: \"find((value, key) => ...)\",\n\t},\n\tcolumn: {\n\t\tsignature: \"column(name)\",\n\t},\n\tbatch: {\n\t\tsignature: \"batch(size, fill?)\",\n\t},\n\tabs: {\n\t\tsignature: \"abs\",\n\t},\n\tround: {\n\t\tsignature: \"round(precision?, method?)\",\n\t},\n\tformatNumber: {\n\t\tsignature: \"formatNumber(...options)\",\n\t},\n\tplural: {\n\t\tsignature: \"plural(one, other, ...forms)\",\n\t},\n\tformatDate: {\n\t\tsignature: \"formatDate(...options)\",\n\t},\n};\n\nexport const FUNCTIONS: Record<string, BuiltinDoc> = {\n\trange: {\n\t\tsignature: \"range(start, end, step?)\",\n\t},\n\tmax: {\n\t\tsignature: \"max(...values)\",\n\t},\n\tmin: {\n\t\tsignature: \"min(...values)\",\n\t},\n\tcycle: {\n\t\tsignature: \"cycle(values, position)\",\n\t},\n\tdate: {\n\t\tsignature: \"date(value?)\",\n\t},\n\tattrs: {\n\t\tsignature: \"attrs(attributes)\",\n\t},\n\tdump: {\n\t\tsignature: \"dump(...values)\",\n\t},\n\tparent: {\n\t\tsignature: \"parent()\",\n\t},\n\tblock: {\n\t\tsignature: \"block(name)\",\n\t},\n};\n\nexport const TESTS: Record<string, BuiltinDoc> = {\n\tdefined: {\n\t\tsignature: \"defined\",\n\t},\n\tnull: {\n\t\tsignature: \"null\",\n\t},\n\tempty: {\n\t\tsignature: \"empty\",\n\t},\n\teven: { signature: \"even\" },\n\todd: { signature: \"odd\" },\n\titerable: {\n\t\tsignature: \"iterable\",\n\t},\n\t\"divisible by\": {\n\t\tsignature: \"divisible by(divisor)\",\n\t},\n};\n\nexport const LOOP_PROPERTIES: Record<string, string> = {\n\tindex: \"Position, starting at 1.\",\n\tindex0: \"Position, starting at 0.\",\n\trevIndex: \"Items left, ending at 1.\",\n\trevIndex0: \"Items left, ending at 0.\",\n\tfirst: \"`true` on the first item.\",\n\tlast: \"`true` on the last item.\",\n\tlength: \"Total number of items.\",\n};\n\nexport const KEYWORDS = [\n\t\"true\",\n\t\"false\",\n\t\"null\",\n\t\"and\",\n\t\"or\",\n\t\"not\",\n\t\"in\",\n\t\"is\",\n];\n","import { BLOCK_TAGS } from \"./builtins.js\";\n\n/**\n * What the text before the cursor is in the middle of, for completions. `start` is where the word being typed starts.\n *\n * - `tag`: a tag's name, just after `{%`. `end` is where an existing `%}` on the line ends, so a snippet can replace it.\n * - `template`: a template name in the string after `extends`, `include`, `component`, `island`, `import` or `from`.\n * - `filter`, `test`: a name after `|`, or after `is` or `is not`.\n * - `loop`: a property after `loop.`.\n * - `prop`: a key in the `with { }` hash of an `include`, `component` or `island` of `template`.\n * - `expression`: a variable or function name.\n */\nexport type CursorContext =\n\t| { kind: \"tag\"; start: number; end: number | null; open: string | null }\n\t| { kind: \"template\"; start: number; prefix: string }\n\t| { kind: \"filter\" | \"test\" | \"loop\" | \"expression\"; start: number }\n\t| { kind: \"prop\"; start: number; template: string };\n\n/** Where the output or tag around `offset` opens, and its kind. Null outside one, or inside a comment. */\nexport const enclosingDelimiter = (\n\ttext: string,\n\toffset: number,\n): { start: number; kind: \"output\" | \"tag\" } | null => {\n\tconst before = text.slice(0, offset);\n\tconst output = before.lastIndexOf(\"{{\");\n\tconst tag = before.lastIndexOf(\"{%\");\n\tconst comment = before.lastIndexOf(\"{#\");\n\tif (comment !== -1 && before.indexOf(\"#}\", comment) === -1) return null;\n\tconst start = Math.max(output, tag);\n\tif (start === -1 || start < comment) return null;\n\tconst kind = start === tag ? \"tag\" : \"output\";\n\tif (before.indexOf(kind === \"tag\" ? \"%}\" : \"}}\", start) !== -1) return null;\n\treturn { start, kind };\n};\n\n/** The block tags open at `offset`, innermost last, from the tags before it. */\nexport const openBlocks = (text: string, offset: number): string[] => {\n\tconst stack: string[] = [];\n\tconst tags = /\\{%-?\\s*(\\w+)([^%]*)%\\}/g;\n\tfor (const match of text.slice(0, offset).matchAll(tags)) {\n\t\tconst name = match[1] as string;\n\t\tconst rest = match[2] as string;\n\t\tif (BLOCK_TAGS.has(name)) {\n\t\t\tif (name === \"set\" && rest.includes(\"=\")) continue;\n\t\t\tif (name === \"block\" && rest.trim().includes(\" \")) continue;\n\t\t\tstack.push(name);\n\t\t} else if (name.startsWith(\"end\")) {\n\t\t\tconst index = stack.lastIndexOf(name.slice(3));\n\t\t\tif (index !== -1) stack.length = index;\n\t\t}\n\t}\n\treturn stack;\n};\n\nconst WORD_END = /[A-Za-z_][A-Za-z0-9_]*$/;\n\nexport const cursorContext = (\n\ttext: string,\n\toffset: number,\n): CursorContext | null => {\n\tconst delimiter = enclosingDelimiter(text, offset);\n\tif (!delimiter) return null;\n\tconst inner = text.slice(delimiter.start + 2, offset).replace(/^-/, \"\");\n\tconst word = WORD_END.exec(inner)?.[0] ?? \"\";\n\tconst start = offset - word.length;\n\tconst head = inner.slice(0, inner.length - word.length);\n\n\tif (delimiter.kind === \"tag\" && /^\\s*$/.test(head)) {\n\t\tconst after = /^\\w*(\\s*-?%\\})/.exec(\n\t\t\ttext.slice(offset).split(\"\\n\")[0] ?? \"\",\n\t\t);\n\t\tconst blocks = openBlocks(text, delimiter.start);\n\t\treturn {\n\t\t\tkind: \"tag\",\n\t\t\tstart,\n\t\t\tend: after ? offset + (after[0] as string).length : null,\n\t\t\topen: blocks.at(-1) ?? null,\n\t\t};\n\t}\n\n\tconst template =\n\t\t/\\b(?:extends|include|component|island|import|from)\\s+([\"'])([^\"']*)$/.exec(\n\t\t\tinner,\n\t\t);\n\tif (template) {\n\t\tconst prefix = template[2] as string;\n\t\treturn { kind: \"template\", start: offset - prefix.length, prefix };\n\t}\n\tif (/\\bloop\\.$/.test(head)) return { kind: \"loop\", start };\n\tif (/(?:^|[^|])\\|\\s*$/.test(head)) return { kind: \"filter\", start };\n\tif (/\\bis\\s+(?:not\\s+)?$/.test(head)) return { kind: \"test\", start };\n\n\tconst props =\n\t\t/\\b(?:include|component|island)\\s+([\"'])([^\"']+)\\1\\s+with\\s+\\{([^{}]*)$/.exec(\n\t\t\thead,\n\t\t);\n\tif (props && /(?:^|,)\\s*$/.test(props[3] as string)) {\n\t\treturn { kind: \"prop\", start, template: props[2] as string };\n\t}\n\tif (/[.\\w]$/.test(head) || /[\"']$/.test(head)) return null;\n\treturn { kind: \"expression\", start };\n};\n\n/** The name under the cursor, and what it names, for hover and go to definition. */\nexport type CursorSymbol = {\n\tkind:\n\t\t| \"tag\"\n\t\t| \"filter\"\n\t\t| \"test\"\n\t\t| \"function\"\n\t\t| \"loop\"\n\t\t| \"variable\"\n\t\t| \"template\";\n\tname: string;\n\tstart: number;\n\tend: number;\n};\n\n/** Every template name written as a string after `extends`, `include`, `component`, `island`, `import` or `from`, with the name's offsets. */\nexport const templateStrings = (\n\ttext: string,\n): { name: string; start: number; end: number }[] => {\n\tconst found: { name: string; start: number; end: number }[] = [];\n\tconst pattern =\n\t\t/\\{%-?\\s*(?:extends|include|component|island|import|from)\\s+([\"'])([^\"'\\n]*)\\1/g;\n\tfor (const match of text.matchAll(pattern)) {\n\t\tconst name = match[2] as string;\n\t\tconst end = (match.index as number) + match[0].length - 1;\n\t\tfound.push({ name, start: end - name.length, end });\n\t}\n\treturn found;\n};\n\nexport const symbolAt = (text: string, offset: number): CursorSymbol | null => {\n\tconst string = templateStrings(text).find(\n\t\t(found) => offset >= found.start && offset <= found.end,\n\t);\n\tif (string) return { kind: \"template\", ...string };\n\n\tconst delimiter = enclosingDelimiter(text, offset);\n\tif (!delimiter) return null;\n\tlet start = offset;\n\tlet end = offset;\n\twhile (start > 0 && /\\w/.test(text[start - 1] as string)) start--;\n\twhile (end < text.length && /\\w/.test(text[end] as string)) end++;\n\tif (start === end || !/^[A-Za-z_]/.test(text.slice(start))) return null;\n\tlet name = text.slice(start, end);\n\tconst head = text.slice(delimiter.start + 2, start);\n\tconst tail = text.slice(end, end + 40);\n\n\tif (delimiter.kind === \"tag\" && /^-?\\s*$/.test(head)) {\n\t\treturn { kind: \"tag\", name, start, end };\n\t}\n\tif (/\\|\\s*$/.test(head)) return { kind: \"filter\", name, start, end };\n\tif (/\\bis\\s+(?:not\\s+)?$/.test(head)) {\n\t\tif (name === \"divisible\" && /^\\s+by\\b/.test(tail)) name = \"divisible by\";\n\t\treturn { kind: \"test\", name, start, end };\n\t}\n\tif (name === \"by\" && /\\bis\\s+(?:not\\s+)?divisible\\s+$/.test(head)) {\n\t\treturn { kind: \"test\", name: \"divisible by\", start, end };\n\t}\n\tif (/\\bloop\\.$/.test(head)) return { kind: \"loop\", name, start, end };\n\tif (/[.\\w]$/.test(head)) return null;\n\tif (/^\\s*\\(/.test(tail)) return { kind: \"function\", name, start, end };\n\treturn { kind: \"variable\", name, start, end };\n};\n","","import docs from \"./docs.json\" with { type: \"json\" };\n\n/** The kinds of built-in that have docs. */\nexport type DocKind = \"tags\" | \"filters\" | \"functions\" | \"tests\";\n\n/**\n * What each built-in does, from its section of the docs site, up to the end of its first example. The docs build writes\n * `docs.json`; see `apps/documentation/src/editor-docs.ts`.\n */\nconst DOCS: Record<\n\tDocKind,\n\tRecord<string, { markdown: string; url: string }>\n> = docs;\n\n/** A built-in's docs as Markdown, ending with a link to its section on the docs site. Null for names without docs. */\nexport const docsFor = (kind: DocKind, name: string): string | null => {\n\tconst doc = DOCS[kind][name];\n\treturn doc ? `${doc.markdown}\\n\\n---\\n\\n[Kiln docs](${doc.url})` : null;\n};\n","import { templateText } from \"@lucidkiln/core\";\nimport { getLanguageService } from \"vscode-html-languageservice\";\nimport type { CompletionItem, Hover } from \"vscode-languageserver\";\nimport { TextDocument } from \"vscode-languageserver-textdocument\";\n\nconst service = getLanguageService();\n\n/** The template as an HTML document, with Kiln syntax blanked out so the HTML service only sees the markup. */\nconst htmlOf = (document: TextDocument) => {\n\tconst html = TextDocument.create(\n\t\tdocument.uri,\n\t\t\"html\",\n\t\tdocument.version,\n\t\ttemplateText(document.getText()),\n\t);\n\treturn { html, parsed: service.parseHTMLDocument(html) };\n};\n\n/** HTML elements, attributes and attribute values at `offset`, outside Kiln syntax. */\nexport const htmlCompletions = (\n\tdocument: TextDocument,\n\toffset: number,\n): CompletionItem[] => {\n\tconst { html, parsed } = htmlOf(document);\n\treturn service.doComplete(html, document.positionAt(offset), parsed).items;\n};\n\n/** MDN's description of the HTML element or attribute at `offset`, outside Kiln syntax. */\nexport const htmlHover = (\n\tdocument: TextDocument,\n\toffset: number,\n): Hover | null => {\n\tconst { html, parsed } = htmlOf(document);\n\treturn service.doHover(html, document.positionAt(offset), parsed);\n};\n","import { readdir, readFile } from \"node:fs/promises\";\nimport { dirname, join, relative, sep } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport * as bundledKiln from \"@lucidkiln/core\";\nimport type { Diagnostic, Kiln, KilnManifest } from \"@lucidkiln/core\";\nimport * as bundledFormat from \"@lucidkiln/core/format\";\n\ntype KilnModule = Pick<typeof bundledKiln, \"createKiln\">;\ntype FormatModule = Pick<\n\ttypeof bundledFormat,\n\t\"formatEdits\" | \"KilnFormatError\"\n>;\n\n/** Diagnostics that need to know the project's names and templates, so they are dropped without a `kiln.json`. */\nconst NEEDS_MANIFEST = new Set([\n\t\"unknown-function\",\n\t\"unknown-filter\",\n\t\"unknown-test\",\n\t\"unknown-type\",\n\t\"missing-template\",\n]);\n\n/**\n * The templates under one `kiln.json`, or a workspace folder without one, and a Kiln environment that checks them.\n *\n * Template names are paths relative to `root`, such as `pages/home.kiln`, as Kiln resolves them.\n */\nexport type Project = {\n\t/** The `kiln.json` file, or null without one. */\n\tmanifestPath: string | null;\n\tmanifest: KilnManifest | null;\n\troot: string;\n\tkiln: Kiln;\n\tformat: FormatModule;\n\t/** Unsaved text of open templates, by name. The loader reads these before the disk. */\n\toverlay: Map<string, string>;\n};\n\n/**\n * Resolves a template name as Kiln does: relative to `from` when it starts with `./` or `../`, with `.kiln` added when the last part has no extension. Null when the name is invalid.\n */\nexport const resolveName = (name: string, from?: string): string | null => {\n\tconst relativeName = name.startsWith(\"./\") || name.startsWith(\"../\");\n\tconst segments =\n\t\trelativeName && from ? from.split(\"/\").slice(0, -1) : ([] as string[]);\n\tfor (const segment of name.split(\"/\")) {\n\t\tif (segment === \"\" || segment === \".\") continue;\n\t\tif (segment === \"..\") {\n\t\t\tif (segments.length === 0) return null;\n\t\t\tsegments.pop();\n\t\t} else segments.push(segment);\n\t}\n\tconst last = segments.at(-1);\n\tif (last === undefined || name.startsWith(\"/\")) return null;\n\tif (!last.includes(\".\")) segments[segments.length - 1] = `${last}.kiln`;\n\treturn segments.join(\"/\");\n};\n\n/** The template name for a file, or null when it is outside the project's root. */\nexport const nameOf = (project: Project, path: string): string | null => {\n\tconst name = relative(project.root, path);\n\tif (name.startsWith(\"..\") || name.startsWith(sep) || name === \"\") return null;\n\treturn name.split(sep).join(\"/\");\n};\n\nexport const pathOf = (project: Project, name: string): string =>\n\tjoin(project.root, ...name.split(\"/\"));\n\n/** Every template under the root, skipping dot folders and node_modules. */\nexport const listTemplates = async (project: Project): Promise<string[]> => {\n\tconst names: string[] = [];\n\tconst visit = async (folder: string, from: string): Promise<void> => {\n\t\t// An unreadable folder should not hide templates found elsewhere in the project.\n\t\tconst entries = await readdir(folder, { withFileTypes: true }).catch(\n\t\t\t() => [],\n\t\t);\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.name.startsWith(\".\") || entry.name === \"node_modules\") continue;\n\t\t\tconst name = from + entry.name;\n\t\t\tif (entry.isDirectory())\n\t\t\t\tawait visit(join(folder, entry.name), name + \"/\");\n\t\t\telse if (entry.isFile() && name.endsWith(\".kiln\")) names.push(name);\n\t\t}\n\t};\n\tawait visit(project.root, \"\");\n\treturn names.sort();\n};\n\n/** The nearest `kiln.json` in `folder` or above it, stopping at `stop` when given. */\nexport const findManifest = async (\n\tfolder: string,\n\tstop: string | null,\n): Promise<string | null> => {\n\tfor (let current = folder; ; current = dirname(current)) {\n\t\tconst candidate = join(current, \"kiln.json\");\n\t\ttry {\n\t\t\tawait readFile(candidate, \"utf8\");\n\t\t\treturn candidate;\n\t\t} catch {\n\t\t\t// Keep looking.\n\t\t}\n\t\tif (current === stop || dirname(current) === current) return null;\n\t}\n};\n\n/** Reads `kiln.json`, filling in defaults. Throws when it isn't valid. */\nexport const readManifest = async (path: string): Promise<KilnManifest> => {\n\tconst data: unknown = JSON.parse(await readFile(path, \"utf8\"));\n\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\tthrow new Error(\"kiln.json must hold an object\");\n\t}\n\tconst field = (key: keyof KilnManifest): string[] => {\n\t\tconst value = (data as Record<string, unknown>)[key];\n\t\tif (value === undefined) return [];\n\t\tif (\n\t\t\t!Array.isArray(value) ||\n\t\t\tvalue.some((item) => typeof item !== \"string\")\n\t\t) {\n\t\t\tthrow new Error(`\"${key}\" in kiln.json must be a list of names`);\n\t\t}\n\t\treturn value;\n\t};\n\tconst {\n\t\troot = \".\",\n\t\tstrict = true,\n\t\thtmlChecks = true,\n\t} = data as Partial<KilnManifest>;\n\tif (typeof root !== \"string\")\n\t\tthrow new Error('\"root\" in kiln.json must be a string');\n\tif (typeof strict !== \"boolean\") {\n\t\tthrow new Error('\"strict\" in kiln.json must be true or false');\n\t}\n\t// createKiln checks the levels inside.\n\tif (\n\t\ttypeof htmlChecks !== \"boolean\" &&\n\t\t(typeof htmlChecks !== \"object\" ||\n\t\t\thtmlChecks === null ||\n\t\t\tArray.isArray(htmlChecks))\n\t) {\n\t\tthrow new Error(\n\t\t\t'\"htmlChecks\" in kiln.json must be true, false or an object',\n\t\t);\n\t}\n\treturn {\n\t\troot,\n\t\tstrict,\n\t\thtmlChecks,\n\t\tfunctions: field(\"functions\"),\n\t\tfilters: field(\"filters\"),\n\t\ttests: field(\"tests\"),\n\t\tglobals: field(\"globals\"),\n\t\ttypes: field(\"types\"),\n\t};\n};\n\n/** Imports an entry point of the copy of Kiln installed in a project, or returns null. */\nconst importInstalled = async <T>(\n\tfrom: string,\n\tentry: \".\" | \"./format\",\n): Promise<T | null> => {\n\tfor (let current = from; ; current = dirname(current)) {\n\t\tconst folder = join(current, \"node_modules\", \"@lucidkiln\", \"core\");\n\t\ttry {\n\t\t\tconst pkg = JSON.parse(\n\t\t\t\tawait readFile(join(folder, \"package.json\"), \"utf8\"),\n\t\t\t) as { exports?: Record<string, { import?: string } | undefined> };\n\t\t\tconst file = pkg.exports?.[entry]?.import;\n\t\t\treturn file\n\t\t\t\t? ((await import(pathToFileURL(join(folder, file)).href)) as T)\n\t\t\t\t: null;\n\t\t} catch {\n\t\t\t// Not installed here, or too old for this entry point.\n\t\t}\n\t\tif (dirname(current) === current) return null;\n\t}\n};\n\nconst stubs = <T>(names: readonly string[], value: T) =>\n\tObject.fromEntries(names.map((name) => [name, value]));\n\n/**\n * Opens the project for a template folder: the nearest `kiln.json`, or `fallbackRoot` without one. Prefers the project's own copy of Kiln, so errors match how it renders.\n *\n * A broken `kiln.json` is reported through `warn`, and the project is opened as if it had none.\n */\nexport const openProject = async (\n\tmanifestPath: string | null,\n\tfallbackRoot: string,\n\twarn: (message: string) => void,\n): Promise<Project> => {\n\tlet manifest: KilnManifest | null = null;\n\tif (manifestPath) {\n\t\ttry {\n\t\t\tmanifest = await readManifest(manifestPath);\n\t\t} catch (error) {\n\t\t\twarn(`Kiln: ignoring ${manifestPath}. ${(error as Error).message}`);\n\t\t}\n\t}\n\tconst root =\n\t\tmanifestPath && manifest\n\t\t\t? join(dirname(manifestPath), manifest.root)\n\t\t\t: fallbackRoot;\n\tconst base = manifestPath ? dirname(manifestPath) : fallbackRoot;\n\tconst { createKiln } =\n\t\t(await importInstalled<KilnModule>(base, \".\")) ?? bundledKiln;\n\tconst format =\n\t\t(await importInstalled<FormatModule>(base, \"./format\")) ?? bundledFormat;\n\n\tconst overlay = new Map<string, string>();\n\tconst options = {\n\t\tloader: {\n\t\t\tload: async (name: string) => {\n\t\t\t\tconst open = overlay.get(name);\n\t\t\t\tif (open !== undefined) return open;\n\t\t\t\tif (!manifest) return null;\n\t\t\t\ttry {\n\t\t\t\t\treturn await readFile(join(root, ...name.split(\"/\")), \"utf8\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t};\n\tlet kiln: Kiln;\n\ttry {\n\t\tkiln = manifest\n\t\t\t? createKiln({\n\t\t\t\t\t...options,\n\t\t\t\t\tfunctions: stubs(manifest.functions, () => null),\n\t\t\t\t\tfilters: stubs(manifest.filters, () => null),\n\t\t\t\t\ttests: stubs(manifest.tests, () => true),\n\t\t\t\t\tglobals: stubs(manifest.globals, null),\n\t\t\t\t\ttypes: stubs(manifest.types, () => true),\n\t\t\t\t\tstrict: manifest.strict,\n\t\t\t\t\thtmlChecks: manifest.htmlChecks,\n\t\t\t\t})\n\t\t\t: createKiln(options);\n\t} catch (error) {\n\t\twarn(`Kiln: ignoring ${manifestPath}. ${(error as Error).message}`);\n\t\tmanifest = null;\n\t\tkiln = createKiln(options);\n\t}\n\treturn { manifestPath, manifest, root, kiln, format, overlay };\n};\n\n/** Checks a template's text and returns its own diagnostics. */\nexport const diagnose = async (\n\tproject: Project,\n\tname: string,\n\ttext: string,\n): Promise<Diagnostic[]> => {\n\tconst diagnostics = await project.kiln.check(name, text);\n\treturn diagnostics.filter(\n\t\t(diagnostic) =>\n\t\t\tdiagnostic.template === name &&\n\t\t\t(project.manifest !== null || !NEEDS_MANIFEST.has(diagnostic.code)),\n\t);\n};\n","import { readFile } from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport type { AnalysedProp, Analysis, PropType } from \"@lucidkiln/core\";\nimport {\n\ttype CompletionItem,\n\tCompletionItemKind,\n\ttype DocumentLink,\n\ttype DocumentSymbol,\n\ttype Hover,\n\tInsertTextFormat,\n\tInsertTextMode,\n\ttype Location,\n\tMarkupKind,\n\tSymbolKind,\n} from \"vscode-languageserver\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\nimport {\n\ttype BuiltinDoc,\n\tFILTERS,\n\tFUNCTIONS,\n\tKEYWORDS,\n\tLOOP_PROPERTIES,\n\tTAGS,\n\tTESTS,\n} from \"./builtins.js\";\nimport {\n\tcursorContext,\n\tenclosingDelimiter,\n\tsymbolAt,\n\ttemplateStrings,\n} from \"./context.js\";\nimport { type DocKind, docsFor } from \"./docs.js\";\nimport { htmlCompletions, htmlHover } from \"./html.js\";\nimport { listTemplates, pathOf, type Project, resolveName } from \"./project.js\";\n\n/** A template open in the editor, with its project and template name. */\nexport type OpenTemplate = {\n\tdocument: TextDocument;\n\tproject: Project;\n\t/** Null when the file is outside the project's root. */\n\tname: string | null;\n};\n\nconst analyse = async (\n\tproject: Project,\n\tname: string,\n\ttext?: string,\n): Promise<Analysis | null> => {\n\ttry {\n\t\treturn await project.kiln.analyze(name, text);\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n/** Writes a prop type as it appears in `props`, such as `\"a\" | \"b\"` or `{ url?: string }[]`. */\nexport const describeType = (type: readonly PropType[] | null): string => {\n\tif (type === null) return \"any\";\n\treturn type\n\t\t.map((member): string => {\n\t\t\tif (typeof member === \"string\") return member;\n\t\t\tif (\"literal\" in member) return JSON.stringify(member.literal);\n\t\t\tif (\"named\" in member) return member.named;\n\t\t\tif (\"list\" in member) {\n\t\t\t\tconst inner = describeType(member.list);\n\t\t\t\treturn member.list.length > 1 ? `(${inner})[]` : `${inner}[]`;\n\t\t\t}\n\t\t\tconst keys = member.shape.map(\n\t\t\t\t(key) =>\n\t\t\t\t\t`${key.name}${key.optional ? \"?\" : \"\"}: ${describeType(key.type)}`,\n\t\t\t);\n\t\t\treturn `{ ${keys.join(\", \")} }`;\n\t\t})\n\t\t.join(\" | \");\n};\n\nconst describeProp = (prop: AnalysedProp) =>\n\t`${prop.name}${prop.required ? \"\" : \"?\"}: ${describeType(prop.type)}`;\n\nconst markdown = (value: string) => ({ kind: MarkupKind.Markdown, value });\n\n/** Markdown for an item's docs, or nothing when it has none. */\nconst documentation = (kind: DocKind, name: string) => {\n\tconst docs = docsFor(kind, name);\n\treturn docs ? markdown(docs) : undefined;\n};\n\nconst builtinItems = (\n\tbuiltins: Record<string, BuiltinDoc>,\n\tdocKind: DocKind,\n\tkind: CompletionItemKind,\n\textra: readonly string[],\n): CompletionItem[] => [\n\t...Object.entries(builtins).map(([label, doc]) => ({\n\t\tlabel,\n\t\tkind,\n\t\tdetail: doc.signature,\n\t\tdocumentation: documentation(docKind, label),\n\t})),\n\t...extra\n\t\t.filter((name) => !(name in builtins))\n\t\t.map((label) => ({ label, kind, detail: \"From your project\" })),\n];\n\n/** Names a template sets or brings into scope before `offset`: loop variables, sets, macros and imports. */\nconst localNames = (text: string, offset: number): string[] => {\n\tconst before = text.slice(0, offset);\n\tconst names = new Set<string>();\n\tfor (const match of before.matchAll(\n\t\t/\\{%-?\\s*for\\s+(\\w+)(?:\\s*,\\s*(\\w+))?\\s+in\\b/g,\n\t)) {\n\t\tnames.add(match[1] as string);\n\t\tif (match[2]) names.add(match[2]);\n\t}\n\tfor (const match of before.matchAll(/\\{%-?\\s*(?:set|macro)\\s+(\\w+)/g)) {\n\t\tnames.add(match[1] as string);\n\t}\n\tfor (const match of before.matchAll(\n\t\t/\\{%-?\\s*import\\s+[\"'][^\"']*[\"']\\s+as\\s+(\\w+)/g,\n\t)) {\n\t\tnames.add(match[1] as string);\n\t}\n\tfor (const match of before.matchAll(\n\t\t/\\{%-?\\s*from\\s+[\"'][^\"']*[\"']\\s+import\\s+([^%]*)%\\}/g,\n\t)) {\n\t\tfor (const part of (match[1] as string).split(\",\")) {\n\t\t\tconst alias = /(\\w+)\\s*$/.exec(part.trim())?.[1];\n\t\t\tif (alias) names.add(alias);\n\t\t}\n\t}\n\treturn [...names];\n};\n\nexport const complete = async (\n\topen: OpenTemplate,\n\toffset: number,\n): Promise<CompletionItem[]> => {\n\tconst { document, project, name } = open;\n\tconst text = document.getText();\n\tconst context = cursorContext(text, offset);\n\tif (!context) {\n\t\treturn enclosingDelimiter(text, offset)\n\t\t\t? []\n\t\t\t: htmlCompletions(document, offset);\n\t}\n\tconst replace = (end = offset) => ({\n\t\tstart: document.positionAt(context.start),\n\t\tend: document.positionAt(end),\n\t});\n\tconst manifest = project.manifest;\n\n\tswitch (context.kind) {\n\t\tcase \"tag\": {\n\t\t\tconst items: CompletionItem[] = Object.entries(TAGS).map(\n\t\t\t\t([label, tag]) => ({\n\t\t\t\t\tlabel,\n\t\t\t\t\tkind: CompletionItemKind.Keyword,\n\t\t\t\t\tdocumentation: documentation(\"tags\", label),\n\t\t\t\t\tinsertTextFormat: InsertTextFormat.Snippet,\n\t\t\t\t\tinsertTextMode: InsertTextMode.adjustIndentation,\n\t\t\t\t\t// Replaces a `%}` already on the line, since the snippet brings its own.\n\t\t\t\t\ttextEdit: {\n\t\t\t\t\t\trange: replace(context.end ?? offset),\n\t\t\t\t\t\tnewText: tag.snippet,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (context.open) {\n\t\t\t\tconst label = `end${context.open}`;\n\t\t\t\titems.unshift({\n\t\t\t\t\tlabel,\n\t\t\t\t\tkind: CompletionItemKind.Keyword,\n\t\t\t\t\tdetail: `Closes the open ${context.open}`,\n\t\t\t\t\tsortText: \"0\",\n\t\t\t\t\ttextEdit: {\n\t\t\t\t\t\trange: replace(context.end ?? offset),\n\t\t\t\t\t\tnewText: `${label} %}`,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn items;\n\t\t}\n\t\tcase \"template\": {\n\t\t\tconst templates = await listTemplates(project);\n\t\t\treturn templates.map((template) => {\n\t\t\t\tconst label = template.replace(/\\.kiln$/, \"\");\n\t\t\t\treturn {\n\t\t\t\t\tlabel,\n\t\t\t\t\tkind: CompletionItemKind.File,\n\t\t\t\t\ttextEdit: { range: replace(), newText: label },\n\t\t\t\t};\n\t\t\t});\n\t\t}\n\t\tcase \"filter\":\n\t\t\treturn builtinItems(\n\t\t\t\tFILTERS,\n\t\t\t\t\"filters\",\n\t\t\t\tCompletionItemKind.Function,\n\t\t\t\tmanifest?.filters ?? [],\n\t\t\t);\n\t\tcase \"test\":\n\t\t\treturn builtinItems(\n\t\t\t\tTESTS,\n\t\t\t\t\"tests\",\n\t\t\t\tCompletionItemKind.Function,\n\t\t\t\tmanifest?.tests ?? [],\n\t\t\t);\n\t\tcase \"loop\":\n\t\t\treturn Object.entries(LOOP_PROPERTIES).map(([label, description]) => ({\n\t\t\t\tlabel,\n\t\t\t\tkind: CompletionItemKind.Property,\n\t\t\t\tdocumentation: markdown(description),\n\t\t\t}));\n\t\tcase \"prop\": {\n\t\t\tconst target = resolveName(context.template, name ?? undefined);\n\t\t\tconst analysis = target ? await analyse(project, target) : null;\n\t\t\treturn (analysis?.props ?? []).map((prop) => ({\n\t\t\t\tlabel: prop.name,\n\t\t\t\tkind: CompletionItemKind.Field,\n\t\t\t\tdetail: describeProp(prop),\n\t\t\t\tinsertText: `${prop.name}: `,\n\t\t\t}));\n\t\t}\n\t\tcase \"expression\": {\n\t\t\tconst analysis = name ? await analyse(project, name, text) : null;\n\t\t\tconst variables = new Set([\n\t\t\t\t...(analysis?.props.map((prop) => prop.name) ?? []),\n\t\t\t\t...(analysis?.rest ? [analysis.rest] : []),\n\t\t\t\t...localNames(text, offset),\n\t\t\t\t...(manifest?.globals ?? []),\n\t\t\t]);\n\t\t\tconst props = new Map(analysis?.props.map((prop) => [prop.name, prop]));\n\t\t\treturn [\n\t\t\t\t...[...variables].map((label) => {\n\t\t\t\t\tconst prop = props.get(label);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tlabel,\n\t\t\t\t\t\tkind: CompletionItemKind.Variable,\n\t\t\t\t\t\tdetail: prop ? describeProp(prop) : undefined,\n\t\t\t\t\t};\n\t\t\t\t}),\n\t\t\t\t...builtinItems(\n\t\t\t\t\tFUNCTIONS,\n\t\t\t\t\t\"functions\",\n\t\t\t\t\tCompletionItemKind.Function,\n\t\t\t\t\tmanifest?.functions ?? [],\n\t\t\t\t),\n\t\t\t\t...KEYWORDS.map((label) => ({\n\t\t\t\t\tlabel,\n\t\t\t\t\tkind: CompletionItemKind.Keyword,\n\t\t\t\t})),\n\t\t\t];\n\t\t}\n\t}\n};\n\n/** The `{% macro name(params) %}` definitions in a template, by name. */\nconst macrosIn = (text: string) =>\n\tnew Map(\n\t\t[...text.matchAll(/\\{%-?\\s*macro\\s+(\\w+)\\s*\\(([^)]*)\\)/g)].map((match) => [\n\t\t\tmatch[1] as string,\n\t\t\t{ params: (match[2] as string).trim(), start: match.index as number },\n\t\t]),\n\t);\n\nconst codeBlock = (code: string) => `\\`\\`\\`kiln\\n${code}\\n\\`\\`\\``;\n\n/** A hover's parts, such as a signature and its docs, with a rule between each. Missing parts are left out. */\nconst sections = (...parts: (string | null | undefined)[]) =>\n\tparts.filter(Boolean).join(\"\\n\\n---\\n\\n\");\n\n/** A tag's syntax, from its completion snippet: named placeholders become their names, and the rest become `…`. */\nconst tagSyntax = (snippet: string) =>\n\t`{% ${snippet.replace(/\\$\\{\\d+:([^}]*)\\}/g, \"$1\").replace(/\\$\\{?\\d+\\}?/g, \"…\")}`;\n\nexport const hover = async (\n\topen: OpenTemplate,\n\toffset: number,\n): Promise<Hover | null> => {\n\tconst { document, project, name } = open;\n\tconst text = document.getText();\n\tconst symbol = symbolAt(text, offset);\n\tif (!symbol) {\n\t\treturn enclosingDelimiter(text, offset)\n\t\t\t? null\n\t\t\t: htmlHover(document, offset);\n\t}\n\tconst range = {\n\t\tstart: document.positionAt(symbol.start),\n\t\tend: document.positionAt(symbol.end),\n\t};\n\tconst fromProject = (names: readonly string[] | undefined, what: string) =>\n\t\tnames?.includes(symbol.name) ? `A ${what} your project registers.` : null;\n\tlet value: string | null = null;\n\n\t/** A built-in's signature, then its docs. */\n\tconst builtin = (signature: string, kind: DocKind) =>\n\t\tsections(codeBlock(signature), docsFor(kind, symbol.name));\n\n\tswitch (symbol.kind) {\n\t\tcase \"tag\": {\n\t\t\t// An end tag, such as `endfor`, shows the docs of the tag it closes.\n\t\t\tconst tag =\n\t\t\t\tsymbol.name in TAGS ? symbol.name : symbol.name.replace(/^end/, \"\");\n\t\t\tconst doc = TAGS[tag];\n\t\t\tvalue = sections(\n\t\t\t\tdoc && codeBlock(tagSyntax(doc.snippet)),\n\t\t\t\tdocsFor(\"tags\", tag),\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\t\tcase \"filter\": {\n\t\t\tconst doc = FILTERS[symbol.name];\n\t\t\tvalue = doc\n\t\t\t\t? builtin(`|${doc.signature}`, \"filters\")\n\t\t\t\t: fromProject(project.manifest?.filters, \"filter\");\n\t\t\tbreak;\n\t\t}\n\t\tcase \"test\": {\n\t\t\tconst doc = TESTS[symbol.name];\n\t\t\tvalue = doc\n\t\t\t\t? builtin(`is ${doc.signature}`, \"tests\")\n\t\t\t\t: fromProject(project.manifest?.tests, \"test\");\n\t\t\tbreak;\n\t\t}\n\t\tcase \"loop\": {\n\t\t\tconst description = LOOP_PROPERTIES[symbol.name];\n\t\t\tvalue = description\n\t\t\t\t? sections(codeBlock(`loop.${symbol.name}`), description)\n\t\t\t\t: null;\n\t\t\tbreak;\n\t\t}\n\t\tcase \"function\": {\n\t\t\tconst macro = macrosIn(text).get(symbol.name);\n\t\t\tconst doc = FUNCTIONS[symbol.name];\n\t\t\tif (macro) value = codeBlock(`macro ${symbol.name}(${macro.params})`);\n\t\t\telse if (doc) value = builtin(doc.signature, \"functions\");\n\t\t\telse value = fromProject(project.manifest?.functions, \"function\");\n\t\t\tbreak;\n\t\t}\n\t\tcase \"variable\": {\n\t\t\tconst analysis = name ? await analyse(project, name, text) : null;\n\t\t\tconst prop = analysis?.props.find((item) => item.name === symbol.name);\n\t\t\tif (prop)\n\t\t\t\tvalue = sections(\n\t\t\t\t\tcodeBlock(describeProp(prop)),\n\t\t\t\t\t\"A prop of this template.\",\n\t\t\t\t);\n\t\t\telse if (symbol.name === analysis?.rest) {\n\t\t\t\tvalue = sections(\n\t\t\t\t\tcodeBlock(`...${symbol.name}`),\n\t\t\t\t\t\"The variables passed to this template that it doesn't declare.\",\n\t\t\t\t);\n\t\t\t} else if (project.manifest?.globals.includes(symbol.name)) {\n\t\t\t\tvalue = \"A global your project provides.\";\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase \"template\": {\n\t\t\tconst target = resolveName(symbol.name, name ?? undefined);\n\t\t\tconst analysis = target ? await analyse(project, target) : null;\n\t\t\tif (!target || !analysis) {\n\t\t\t\tvalue = project.manifest\n\t\t\t\t\t? `Template \\`${symbol.name}\\` was not found.`\n\t\t\t\t\t: null;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst lines = [`**${target}**`];\n\t\t\tif (analysis.props.length > 0 || analysis.rest) {\n\t\t\t\tconst props = analysis.props.map(describeProp);\n\t\t\t\tif (analysis.rest) props.push(`...${analysis.rest}`);\n\t\t\t\tlines.push(codeBlock(`props ${props.join(\", \")}`));\n\t\t\t}\n\t\t\tif (analysis.blocks.length > 0) {\n\t\t\t\tlines.push(\n\t\t\t\t\t`Blocks: ${analysis.blocks.map((block) => `\\`${block}\\``).join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tvalue = lines.join(\"\\n\\n\");\n\t\t}\n\t}\n\treturn value ? { contents: markdown(value), range } : null;\n};\n\nconst fileLocation = (\n\tproject: Project,\n\tname: string,\n\tline = 0,\n\tcharacter = 0,\n): Location => ({\n\turi: pathToFileURL(pathOf(project, name)).href,\n\trange: { start: { line, character }, end: { line, character } },\n});\n\n/** The line and column of an offset in another template's text. */\nconst positionIn = (text: string, offset: number) => {\n\tconst lines = text.slice(0, offset).split(\"\\n\");\n\treturn { line: lines.length - 1, character: (lines.at(-1) as string).length };\n};\n\nexport const definition = async (\n\topen: OpenTemplate,\n\toffset: number,\n): Promise<Location | null> => {\n\tconst { document, project, name } = open;\n\tconst text = document.getText();\n\tconst symbol = symbolAt(text, offset);\n\tif (!symbol) return null;\n\tconst load = (template: string) =>\n\t\tproject.overlay.get(template) ?? readTemplate(project, template);\n\n\tif (symbol.kind === \"template\") {\n\t\tconst target = resolveName(symbol.name, name ?? undefined);\n\t\treturn target ? fileLocation(project, target) : null;\n\t}\n\n\tif (symbol.kind === \"function\") {\n\t\tconst local = macrosIn(text).get(symbol.name);\n\t\tif (local) {\n\t\t\tconst position = document.positionAt(local.start);\n\t\t\treturn { uri: document.uri, range: { start: position, end: position } };\n\t\t}\n\t\t// A macro brought in with `from \"x\" import name`.\n\t\tfor (const match of text.matchAll(\n\t\t\t/\\{%-?\\s*from\\s+([\"'])([^\"']*)\\1\\s+import\\s+([^%]*)%\\}/g,\n\t\t)) {\n\t\t\tfor (const part of (match[3] as string).split(\",\")) {\n\t\t\t\tconst [original, alias = original] = part.trim().split(/\\s+as\\s+/);\n\t\t\t\tif (alias !== symbol.name || !original) continue;\n\t\t\t\tconst target = resolveName(match[2] as string, name ?? undefined);\n\t\t\t\tconst source = target ? await load(target) : null;\n\t\t\t\tconst macro = source ? macrosIn(source).get(original) : null;\n\t\t\t\tif (target && source && macro) {\n\t\t\t\t\tconst position = positionIn(source, macro.start);\n\t\t\t\t\treturn fileLocation(\n\t\t\t\t\t\tproject,\n\t\t\t\t\t\ttarget,\n\t\t\t\t\t\tposition.line,\n\t\t\t\t\t\tposition.character,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t// A block name goes to the same block in the layout this template extends.\n\tconst tag = /\\{%-?\\s*block\\s+$/.exec(\n\t\ttext.slice(Math.max(0, symbol.start - 40), symbol.start),\n\t);\n\tif (symbol.kind === \"variable\" && tag && name) {\n\t\tlet current = name;\n\t\tfor (let depth = 0; depth < 20; depth++) {\n\t\t\tconst analysis = await analyse(\n\t\t\t\tproject,\n\t\t\t\tcurrent,\n\t\t\t\tcurrent === name ? text : undefined,\n\t\t\t);\n\t\t\tconst parent = analysis?.templates.find(\n\t\t\t\t(ref) => ref.kind === \"extends\",\n\t\t\t)?.name;\n\t\t\tif (!parent) return null;\n\t\t\tconst source = await load(parent);\n\t\t\tconst block = source\n\t\t\t\t? new RegExp(`\\\\{%-?\\\\s*block\\\\s+${symbol.name}\\\\b`).exec(source)\n\t\t\t\t: null;\n\t\t\tif (source && block) {\n\t\t\t\tconst position = positionIn(source, block.index);\n\t\t\t\treturn fileLocation(project, parent, position.line, position.character);\n\t\t\t}\n\t\t\tcurrent = parent;\n\t\t}\n\t}\n\treturn null;\n};\n\nconst readTemplate = async (project: Project, name: string) => {\n\ttry {\n\t\treturn await readFile(pathOf(project, name), \"utf8\");\n\t} catch {\n\t\treturn null;\n\t}\n};\n\nexport const links = (open: OpenTemplate): DocumentLink[] => {\n\tconst { document, project, name } = open;\n\tif (!project.manifest) return [];\n\treturn templateStrings(document.getText()).flatMap((found) => {\n\t\tconst target = resolveName(found.name, name ?? undefined);\n\t\tif (!target) return [];\n\t\treturn [\n\t\t\t{\n\t\t\t\trange: {\n\t\t\t\t\tstart: document.positionAt(found.start),\n\t\t\t\t\tend: document.positionAt(found.end),\n\t\t\t\t},\n\t\t\t\ttarget: pathToFileURL(pathOf(project, target)).href,\n\t\t\t},\n\t\t];\n\t});\n};\n\nconst SYMBOL_KINDS: Record<string, SymbolKind> = {\n\tblock: SymbolKind.Module,\n\tmacro: SymbolKind.Function,\n\tslot: SymbolKind.Field,\n\tfill: SymbolKind.Field,\n};\n\nexport const symbols = (document: TextDocument): DocumentSymbol[] => {\n\tconst text = document.getText();\n\tconst found: DocumentSymbol[] = [];\n\tfor (const match of text.matchAll(\n\t\t/\\{%-?\\s*(block|macro|slot|fill)\\b\\s*([\"']?)([\\w-]*)\\2[^%]*%\\}/g,\n\t)) {\n\t\tconst kind = match[1] as string;\n\t\tconst range = {\n\t\t\tstart: document.positionAt(match.index as number),\n\t\t\tend: document.positionAt((match.index as number) + match[0].length),\n\t\t};\n\t\tfound.push({\n\t\t\tname: match[3] || (kind === \"slot\" ? \"default\" : kind),\n\t\t\tdetail: kind,\n\t\t\tkind: SYMBOL_KINDS[kind] ?? SymbolKind.Field,\n\t\t\trange,\n\t\t\tselectionRange: range,\n\t\t});\n\t}\n\treturn found;\n};\n","import { dirname, isAbsolute, relative, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { TextDocument } from \"vscode-languageserver-textdocument\";\nimport {\n\ttype Connection,\n\tDiagnosticSeverity,\n\tDidChangeWatchedFilesNotification,\n\ttype TextEdit,\n\tTextDocuments,\n\tTextDocumentSyncKind,\n} from \"vscode-languageserver/node\";\nimport {\n\tcomplete,\n\tdefinition,\n\thover,\n\tlinks,\n\ttype OpenTemplate,\n\tsymbols,\n} from \"./features.js\";\nimport {\n\tdiagnose,\n\tfindManifest,\n\tnameOf,\n\topenProject,\n\ttype Project,\n} from \"./project.js\";\n\n/** Serves Kiln templates over a language server connection. */\nexport const startServer = (connection: Connection): void => {\n\tconst documents = new TextDocuments(TextDocument);\n\n\tlet workspaceFolders: string[] = [];\n\tlet canWatchFiles = false;\n\t/** Open projects, by the path of their `kiln.json`, or their folder when they have none. */\n\tconst projects = new Map<string, Promise<Project>>();\n\n\tconnection.onInitialize((params) => {\n\t\tworkspaceFolders = (params.workspaceFolders ?? [])\n\t\t\t.map((folder) => folder.uri)\n\t\t\t.concat(\n\t\t\t\tparams.rootUri && !params.workspaceFolders ? [params.rootUri] : [],\n\t\t\t)\n\t\t\t.filter((uri) => uri.startsWith(\"file:\"))\n\t\t\t.map((uri) => fileURLToPath(uri));\n\t\tcanWatchFiles =\n\t\t\tparams.capabilities.workspace?.didChangeWatchedFiles\n\t\t\t\t?.dynamicRegistration === true;\n\t\treturn {\n\t\t\tcapabilities: {\n\t\t\t\ttextDocumentSync: TextDocumentSyncKind.Incremental,\n\t\t\t\tcompletionProvider: {\n\t\t\t\t\t// `<` and `=` start HTML element and attribute value completions.\n\t\t\t\t\ttriggerCharacters: [\n\t\t\t\t\t\t\"|\",\n\t\t\t\t\t\t\".\",\n\t\t\t\t\t\t\"%\",\n\t\t\t\t\t\t'\"',\n\t\t\t\t\t\t\"'\",\n\t\t\t\t\t\t\"/\",\n\t\t\t\t\t\t\" \",\n\t\t\t\t\t\t\"{\",\n\t\t\t\t\t\t\",\",\n\t\t\t\t\t\t\"<\",\n\t\t\t\t\t\t\"=\",\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\thoverProvider: true,\n\t\t\t\tdefinitionProvider: true,\n\t\t\t\tdocumentLinkProvider: { resolveProvider: false },\n\t\t\t\tdocumentSymbolProvider: true,\n\t\t\t\tdocumentFormattingProvider: true,\n\t\t\t\tdocumentRangeFormattingProvider: true,\n\t\t\t},\n\t\t\tserverInfo: { name: \"kiln-language-server\" },\n\t\t};\n\t});\n\n\tconnection.onInitialized(() => {\n\t\tif (!canWatchFiles) return;\n\t\tvoid connection.client.register(DidChangeWatchedFilesNotification.type, {\n\t\t\twatchers: [{ globPattern: \"**/*.kiln\" }, { globPattern: \"**/kiln.json\" }],\n\t\t});\n\t});\n\n\t/** The project a file belongs to: the nearest `kiln.json` inside its workspace folder, or the folder itself. */\n\tconst projectFor = async (path: string): Promise<Project> => {\n\t\tconst folder =\n\t\t\tworkspaceFolders\n\t\t\t\t.filter((candidate) => {\n\t\t\t\t\tconst name = relative(candidate, path);\n\t\t\t\t\treturn (\n\t\t\t\t\t\tname !== \"..\" && !name.startsWith(\"..\" + sep) && !isAbsolute(name)\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.sort((a, b) => b.length - a.length)[0] ?? null;\n\t\tconst manifestPath = await findManifest(dirname(path), folder);\n\t\tconst fallbackRoot = folder ?? dirname(path);\n\t\tconst key = manifestPath ?? fallbackRoot;\n\t\tlet project = projects.get(key);\n\t\tif (!project) {\n\t\t\tproject = openProject(manifestPath, fallbackRoot, (message) =>\n\t\t\t\tconnection.window.showWarningMessage(message),\n\t\t\t);\n\t\t\tprojects.set(key, project);\n\t\t}\n\t\treturn project;\n\t};\n\n\tconst openTemplate = async (\n\t\tdocument: TextDocument,\n\t): Promise<OpenTemplate | null> => {\n\t\tif (!document.uri.startsWith(\"file:\")) return null;\n\t\tconst path = fileURLToPath(document.uri);\n\t\tconst project = await projectFor(path);\n\t\treturn { document, project, name: nameOf(project, path) };\n\t};\n\n\tconst pending = new Map<string, ReturnType<typeof setTimeout>>();\n\n\t/** Checks a template shortly after its last change, and publishes its diagnostics. */\n\tconst scheduleCheck = (document: TextDocument) => {\n\t\tclearTimeout(pending.get(document.uri));\n\t\tpending.set(\n\t\t\tdocument.uri,\n\t\t\tsetTimeout(() => {\n\t\t\t\tpending.delete(document.uri);\n\t\t\t\tvoid check(document);\n\t\t\t}, 150),\n\t\t);\n\t};\n\n\tconst check = async (document: TextDocument) => {\n\t\tconst open = await openTemplate(document);\n\t\tif (!open?.name) return;\n\t\tconst { project, name } = open;\n\t\tconst version = document.version;\n\t\tconst diagnostics = await diagnose(project, name, document.getText()).catch(\n\t\t\t() => [],\n\t\t);\n\t\tif (documents.get(document.uri)?.version !== version) return;\n\t\tvoid connection.sendDiagnostics({\n\t\t\turi: document.uri,\n\t\t\tversion,\n\t\t\tdiagnostics: diagnostics.map((diagnostic) => ({\n\t\t\t\trange: {\n\t\t\t\t\tstart: document.positionAt(diagnostic.start.offset),\n\t\t\t\t\tend: document.positionAt(diagnostic.end.offset),\n\t\t\t\t},\n\t\t\t\tseverity:\n\t\t\t\t\tdiagnostic.severity === \"error\"\n\t\t\t\t\t\t? DiagnosticSeverity.Error\n\t\t\t\t\t\t: DiagnosticSeverity.Warning,\n\t\t\t\tcode: diagnostic.code,\n\t\t\t\tsource: \"kiln\",\n\t\t\t\tmessage: diagnostic.message,\n\t\t\t})),\n\t\t});\n\t};\n\n\t/** Every open template in a project sees an edit to any of them, since one template's props can break another. */\n\tconst checkProject = async (project: Project) => {\n\t\tfor (const document of documents.all()) {\n\t\t\tconst open = await openTemplate(document);\n\t\t\tif (open?.project === project) scheduleCheck(document);\n\t\t}\n\t};\n\n\tdocuments.onDidChangeContent(async ({ document }) => {\n\t\tconst open = await openTemplate(document);\n\t\tif (!open?.name) return;\n\t\topen.project.overlay.set(open.name, document.getText());\n\t\topen.project.kiln.invalidate(open.name);\n\t\tawait checkProject(open.project);\n\t});\n\n\tdocuments.onDidClose(async ({ document }) => {\n\t\tclearTimeout(pending.get(document.uri));\n\t\tvoid connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });\n\t\tconst open = await openTemplate(document);\n\t\tif (!open?.name) return;\n\t\topen.project.overlay.delete(open.name);\n\t\topen.project.kiln.invalidate(open.name);\n\t\tawait checkProject(open.project);\n\t});\n\n\tconnection.onDidChangeWatchedFiles(async ({ changes }) => {\n\t\tif (changes.some((change) => change.uri.endsWith(\"/kiln.json\"))) {\n\t\t\tprojects.clear();\n\t\t\t// Populate every new project before checking callers, so cross-template checks see all unsaved text.\n\t\t\tfor (const document of documents.all()) {\n\t\t\t\tconst open = await openTemplate(document);\n\t\t\t\tif (open?.name) {\n\t\t\t\t\topen.project.overlay.set(open.name, document.getText());\n\t\t\t\t\topen.project.kiln.invalidate(open.name);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor (const change of changes) {\n\t\t\t\tif (!change.uri.startsWith(\"file:\")) continue;\n\t\t\t\tconst path = fileURLToPath(change.uri);\n\t\t\t\tconst project = await projectFor(path);\n\t\t\t\tconst name = nameOf(project, path);\n\t\t\t\tif (name) project.kiln.invalidate(name);\n\t\t\t}\n\t\t}\n\t\tfor (const document of documents.all()) scheduleCheck(document);\n\t});\n\n\t/** Runs a handler with the open template for a request, or returns `fallback` when there is none. */\n\tconst withTemplate =\n\t\t<P extends { textDocument: { uri: string } }, R>(\n\t\t\tfallback: R,\n\t\t\thandler: (open: OpenTemplate, params: P) => R | Promise<R>,\n\t\t) =>\n\t\tasync (params: P): Promise<R> => {\n\t\t\tconst document = documents.get(params.textDocument.uri);\n\t\t\tconst open = document ? await openTemplate(document) : null;\n\t\t\treturn open ? handler(open, params) : fallback;\n\t\t};\n\n\tconnection.onCompletion(\n\t\twithTemplate([], (open, params) =>\n\t\t\tcomplete(open, open.document.offsetAt(params.position)),\n\t\t),\n\t);\n\tconnection.onHover(\n\t\twithTemplate(null, (open, params) =>\n\t\t\thover(open, open.document.offsetAt(params.position)),\n\t\t),\n\t);\n\tconnection.onDefinition(\n\t\twithTemplate(null, (open, params) =>\n\t\t\tdefinition(open, open.document.offsetAt(params.position)),\n\t\t),\n\t);\n\tconnection.onDocumentLinks(withTemplate([], (open) => links(open)));\n\tconnection.onDocumentSymbol(\n\t\twithTemplate([], (open) => symbols(open.document)),\n\t);\n\n\t/** Formats a template, or part of one, as edits. Templates with syntax errors are left alone. */\n\tconst formatTemplate = (\n\t\topen: OpenTemplate,\n\t\toptions: { tabSize: number; insertSpaces: boolean },\n\t\trange?: { start: number; end: number },\n\t): TextEdit[] => {\n\t\tconst { document, project } = open;\n\t\ttry {\n\t\t\treturn project.format\n\t\t\t\t.formatEdits(document.getText(), {\n\t\t\t\t\tuseTabs: !options.insertSpaces,\n\t\t\t\t\ttabWidth: options.tabSize,\n\t\t\t\t\trange,\n\t\t\t\t})\n\t\t\t\t.map((edit) => ({\n\t\t\t\t\trange: {\n\t\t\t\t\t\tstart: document.positionAt(edit.start),\n\t\t\t\t\t\tend: document.positionAt(edit.end),\n\t\t\t\t\t},\n\t\t\t\t\tnewText: edit.text,\n\t\t\t\t}));\n\t\t} catch (error) {\n\t\t\tif (error instanceof project.format.KilnFormatError) {\n\t\t\t\tconnection.window.showErrorMessage(error.message);\n\t\t\t}\n\t\t\treturn [];\n\t\t}\n\t};\n\n\tconnection.onDocumentFormatting(\n\t\twithTemplate([], (open, params) => formatTemplate(open, params.options)),\n\t);\n\tconnection.onDocumentRangeFormatting(\n\t\twithTemplate([], (open, params) =>\n\t\t\tformatTemplate(open, params.options, {\n\t\t\t\tstart: open.document.offsetAt(params.range.start),\n\t\t\t\tend: open.document.offsetAt(params.range.end),\n\t\t\t}),\n\t\t),\n\t);\n\n\tdocuments.listen(connection);\n\tconnection.listen();\n};\n","#!/usr/bin/env node\nimport { createConnection, ProposedFeatures } from \"vscode-languageserver/node\";\nimport { startServer } from \"./server.js\";\n\nstartServer(createConnection(ProposedFeatures.all));\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
- "name": "@lucidkiln/language-server",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@lucidkiln/language-server",
3
+ "version": "1.0.0",
4
+ "description": "Language server for Kiln templates: errors as you type, formatting, completions, hover and go to definition.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/buildlucid/kiln.git",
8
+ "directory": "packages/language-server"
9
+ },
10
+ "type": "module",
11
+ "bin": {
12
+ "kiln-language-server": "./dist/main.js"
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "dev": "vp pack --watch",
19
+ "prepack": "vp run build"
20
+ },
21
+ "keywords": [
22
+ "kiln",
23
+ "language server",
24
+ "lsp",
25
+ "template"
26
+ ],
27
+ "dependencies": {
28
+ "@lucidkiln/core": "^1.0.0",
29
+ "vscode-html-languageservice": "5.6.2",
30
+ "vscode-languageserver": "10.1.2",
31
+ "vscode-languageserver-textdocument": "1.0.15"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^24.19.2",
35
+ "typescript": "^7.0.2",
36
+ "vite-plus": "1.2.0"
37
+ },
38
+ "volta": {
39
+ "node": "24.22.0"
40
+ },
41
+ "engines": {
42
+ "node": "^24.0.0"
43
+ },
44
+ "license": "MIT",
45
+ "publishConfig": {
46
+ "access": "public"
47
+ }
48
+ }