@staticbolt/lsp 1.0.0-beta.33 → 1.0.0-beta.34
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.
- package/lib/index.mjs +65 -41
- package/lib/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/helpers/regions.ts +3 -3
- package/src/helpers/syntax-tokens.ts +2 -11
- package/src/helpers/virtual-document.ts +30 -11
- package/src/language-plugin.ts +4 -0
- package/src/services/syntax-tokens-service.ts +10 -10
- package/src/virtual-code.ts +51 -22
package/lib/index.mjs
CHANGED
|
@@ -255,8 +255,8 @@ function isInside(range, outer) {
|
|
|
255
255
|
}
|
|
256
256
|
/**
|
|
257
257
|
* The plugin regions by the file they share: the classic regions of a language together, each module on its own. Every group is
|
|
258
|
-
* in text order, with the regions of other languages inside its own as its holes: a placeholder inside a build-time script is
|
|
259
|
-
*
|
|
258
|
+
* in text order, with the regions of other languages inside its own as its holes: a placeholder inside a build-time script is the
|
|
259
|
+
* placeholder language's.
|
|
260
260
|
*/
|
|
261
261
|
function groupByFile(regions) {
|
|
262
262
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -272,7 +272,8 @@ function groupByFile(regions) {
|
|
|
272
272
|
};
|
|
273
273
|
group.regions.push({
|
|
274
274
|
start: region.start,
|
|
275
|
-
end: region.end
|
|
275
|
+
end: region.end,
|
|
276
|
+
isExpression: region.isExpression
|
|
276
277
|
});
|
|
277
278
|
group.holes.push(...holesOf(region, regions));
|
|
278
279
|
groups.set(id, group);
|
|
@@ -305,21 +306,35 @@ function holesOf(region, regions) {
|
|
|
305
306
|
//#endregion
|
|
306
307
|
//#region src/helpers/virtual-document.ts
|
|
307
308
|
/**
|
|
308
|
-
* The text
|
|
309
|
-
*
|
|
309
|
+
* The regions' text alone, one region per line, so an edit outside them leaves the code untouched. Every line opens with a `;`,
|
|
310
|
+
* which keeps the line a statement of its own whatever the line before it ends with; an expression region is wrapped as `;(…)`,
|
|
311
|
+
* so an object literal reads as one and not as a block.
|
|
310
312
|
*/
|
|
311
|
-
function
|
|
312
|
-
|
|
313
|
+
function codeFromRegions(text, regions, holes) {
|
|
314
|
+
const lines = [];
|
|
315
|
+
const starts = [];
|
|
316
|
+
const moved = [];
|
|
313
317
|
let cursor = 0;
|
|
314
318
|
for (const region of regions) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
cursor
|
|
319
|
+
const open = region.isExpression ? ";(" : ";";
|
|
320
|
+
const start = cursor + open.length;
|
|
321
|
+
const line = open + text.slice(region.start, region.end) + (region.isExpression ? ")" : "");
|
|
322
|
+
starts.push(start);
|
|
323
|
+
lines.push(line);
|
|
324
|
+
cursor += line.length + 1;
|
|
325
|
+
for (const hole of holes) {
|
|
326
|
+
if (hole.start < region.start || hole.end > region.end) continue;
|
|
327
|
+
moved.push({
|
|
328
|
+
start: hole.start - region.start + start,
|
|
329
|
+
end: hole.end - region.start + start
|
|
330
|
+
});
|
|
331
|
+
}
|
|
321
332
|
}
|
|
322
|
-
return
|
|
333
|
+
return {
|
|
334
|
+
text: lines.join("\n"),
|
|
335
|
+
starts,
|
|
336
|
+
holes: moved
|
|
337
|
+
};
|
|
323
338
|
}
|
|
324
339
|
/** The text with the regions blanked, keeping line breaks, so every offset means the same thing as in the text. */
|
|
325
340
|
function blankRegions(text, regions) {
|
|
@@ -433,13 +448,13 @@ var StaticboltCode = class {
|
|
|
433
448
|
html;
|
|
434
449
|
/** The regions the plugins embed, in text order. */
|
|
435
450
|
regions;
|
|
436
|
-
/** The
|
|
437
|
-
|
|
451
|
+
/** The TypeScript file of every plugin language with regions. */
|
|
452
|
+
codes;
|
|
438
453
|
/** The parsed HTML, on first use. */
|
|
439
454
|
#htmlDocument;
|
|
440
455
|
/** The document as the plugins see it, on first use. */
|
|
441
456
|
#info;
|
|
442
|
-
constructor(typescript, uri, languageId, snapshot, project) {
|
|
457
|
+
constructor(typescript, uri, languageId, snapshot, project, previous) {
|
|
443
458
|
const text = snapshot.getText(0, snapshot.getLength());
|
|
444
459
|
this.uri = uri;
|
|
445
460
|
this.languageId = languageId;
|
|
@@ -449,9 +464,8 @@ var StaticboltCode = class {
|
|
|
449
464
|
this.mappings = [identityMapping(text.length)];
|
|
450
465
|
this.html = languageId === "markdown" ? blankRegions(text, findMarkdownNonHtmlRegions(text)) : text;
|
|
451
466
|
this.regions = project ? findPluginRegions(this.info, project.embeddedLanguages) : [];
|
|
452
|
-
this.
|
|
453
|
-
this.embeddedCodes =
|
|
454
|
-
if (languageId === "markdown") this.embeddedCodes.unshift(createHtmlCode(typescript, this.html));
|
|
467
|
+
this.codes = groupByFile(this.regions).map((group) => createTypeScriptCode(typescript, this.info, group, previous));
|
|
468
|
+
this.embeddedCodes = languageId === "markdown" ? [createHtmlCode(typescript, this.html), ...this.codes] : [...this.codes];
|
|
455
469
|
}
|
|
456
470
|
/** The parsed HTML. */
|
|
457
471
|
get htmlDocument() {
|
|
@@ -486,32 +500,41 @@ function createHtmlCode(typescript, html) {
|
|
|
486
500
|
};
|
|
487
501
|
}
|
|
488
502
|
/**
|
|
489
|
-
* The TypeScript code of one file of a plugin language: the
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
503
|
+
* The TypeScript code of one file of a plugin language: the file's regions one per line, the regions of other languages inside
|
|
504
|
+
* them masked, and the language's prelude appended at the end. A module is made one with an `export {}`, so its top level is its
|
|
505
|
+
* own; a script's top level is the global scope, as it is in the browser. The regions map back to the document; what comes before
|
|
506
|
+
* the first and after the last maps onto their edges, so what TypeScript puts at the top or the bottom of the file, an import or
|
|
507
|
+
* a declaration it adds say, lands in the document.
|
|
508
|
+
*
|
|
509
|
+
* Nothing but the regions is in the text, so an edit outside them leaves it as it was: the previous file's snapshot is then kept,
|
|
510
|
+
* and Volar, which versions a file by the identity of its snapshot, hands TypeScript the program it already has.
|
|
494
511
|
*/
|
|
495
|
-
function createTypeScriptCode(typescript, info, group) {
|
|
512
|
+
function createTypeScriptCode(typescript, info, group, previous) {
|
|
496
513
|
const { id, language, isModule, regions, holes } = group;
|
|
497
514
|
const prelude = typeof language.prelude === "function" ? language.prelude(info) : language.prelude;
|
|
498
515
|
const suffix = [isModule ? "export {};" : "", prelude ?? ""].filter(Boolean).join("\n");
|
|
499
|
-
const
|
|
516
|
+
const code = codeFromRegions(info.text, regions, holes);
|
|
517
|
+
const body = mask(typescript, code.text, code.holes);
|
|
518
|
+
const text = `${body}\n${suffix}\n`;
|
|
500
519
|
const first = regions[0];
|
|
501
520
|
const last = regions.at(-1) ?? first;
|
|
521
|
+
const before = previous?.codes.find((candidate) => candidate.id === id);
|
|
502
522
|
return {
|
|
503
523
|
id,
|
|
504
524
|
languageId: "typescript",
|
|
505
|
-
|
|
525
|
+
language,
|
|
526
|
+
holes: code.holes,
|
|
527
|
+
text,
|
|
528
|
+
snapshot: before?.text === text ? before.snapshot : typescript.ScriptSnapshot.fromString(text),
|
|
506
529
|
mappings: [
|
|
507
530
|
{
|
|
508
531
|
sourceOffsets: regions.map((region) => region.start),
|
|
509
|
-
generatedOffsets:
|
|
532
|
+
generatedOffsets: code.starts,
|
|
510
533
|
lengths: regions.map((region) => region.end - region.start),
|
|
511
534
|
data: ALL_FEATURES
|
|
512
535
|
},
|
|
513
|
-
edgeMapping(topOf(info.text, first), 0,
|
|
514
|
-
edgeMapping(last.end,
|
|
536
|
+
edgeMapping(topOf(info.text, first), 0, code.starts[0]),
|
|
537
|
+
edgeMapping(last.end, body.length, text.length - body.length)
|
|
515
538
|
]
|
|
516
539
|
};
|
|
517
540
|
}
|
|
@@ -562,6 +585,9 @@ function createLanguagePlugin(typescript, projects) {
|
|
|
562
585
|
if (languageId !== "html" && languageId !== "markdown") return;
|
|
563
586
|
return new StaticboltCode(typescript, uri, languageId, snapshot, projects.of(uri.toString()));
|
|
564
587
|
},
|
|
588
|
+
updateVirtualCode(uri, previous, snapshot) {
|
|
589
|
+
return new StaticboltCode(typescript, uri, previous.languageId, snapshot, projects.of(uri.toString()), previous);
|
|
590
|
+
},
|
|
565
591
|
typescript: {
|
|
566
592
|
extraFileExtensions: [{
|
|
567
593
|
extension: "html",
|
|
@@ -1358,8 +1384,6 @@ const SCOPED_TYPES = {
|
|
|
1358
1384
|
modifiers: [SemanticTokenModifiers.defaultLibrary]
|
|
1359
1385
|
}
|
|
1360
1386
|
};
|
|
1361
|
-
/** The punctuation that is an operator; the brackets, separators and accessors are left to the default colour. */
|
|
1362
|
-
const OPERATORS = new Set("= == === != !== + - * / % ** ++ -- < > <= >= && || ?? ! ~ & | ^ << >> >>> ? : => ... += -= *= /= %= **= <<= >>= >>>= &= |= ^= &&= ||= ??=".split(" "));
|
|
1363
1387
|
/** The keyword table, with the `SyntaxKind` values of the TypeScript in use. */
|
|
1364
1388
|
function keywordsOf(typescript) {
|
|
1365
1389
|
const { SyntaxKind } = typescript;
|
|
@@ -1495,10 +1519,10 @@ function nameOf(typescript, keywords, node, isScoped) {
|
|
|
1495
1519
|
type: SemanticTokenTypes.regexp,
|
|
1496
1520
|
modifiers: []
|
|
1497
1521
|
};
|
|
1498
|
-
if (kind >= SyntaxKind.FirstPunctuation && kind <= SyntaxKind.LastPunctuation) return
|
|
1522
|
+
if (kind >= SyntaxKind.FirstPunctuation && kind <= SyntaxKind.LastPunctuation) return {
|
|
1499
1523
|
type: SemanticTokenTypes.operator,
|
|
1500
1524
|
modifiers: []
|
|
1501
|
-
}
|
|
1525
|
+
};
|
|
1502
1526
|
if (kind < SyntaxKind.FirstKeyword || kind > SyntaxKind.LastKeyword) return;
|
|
1503
1527
|
if (kind === SyntaxKind.VoidKeyword) return standardOrScoped(node.parent.kind === SyntaxKind.VoidExpression ? "keywordOperatorExpression" : "supportTypePrimitive", isScoped);
|
|
1504
1528
|
return standardOrScoped(keywords[kind] ?? "keywordControl", isScoped);
|
|
@@ -1571,7 +1595,7 @@ function createSyntaxTokensService(typescript, isScoped) {
|
|
|
1571
1595
|
return { provideDocumentSemanticTokens(document, _range, legend) {
|
|
1572
1596
|
if (document.languageId !== "typescript") return;
|
|
1573
1597
|
const embedded = embeddedOf(context, document);
|
|
1574
|
-
if (!embedded || embedded.
|
|
1598
|
+
if (!embedded || embedded.code.language.isColouredByEditor) return;
|
|
1575
1599
|
const [sourceFile, checker] = parse(document, embedded.fileName);
|
|
1576
1600
|
const tokens = [];
|
|
1577
1601
|
for (const token of tokenize(sourceFile, document, checker)) {
|
|
@@ -1581,7 +1605,7 @@ function createSyntaxTokensService(typescript, isScoped) {
|
|
|
1581
1605
|
line: token.line,
|
|
1582
1606
|
character: token.character
|
|
1583
1607
|
});
|
|
1584
|
-
if (isInRegions(embedded.
|
|
1608
|
+
if (isInRegions(embedded.code.holes, offset)) continue;
|
|
1585
1609
|
let modifiers = 0;
|
|
1586
1610
|
for (const modifier of token.modifiers) {
|
|
1587
1611
|
const bit = legend.tokenModifiers.indexOf(modifier);
|
|
@@ -1601,18 +1625,18 @@ function createSyntaxTokensService(typescript, isScoped) {
|
|
|
1601
1625
|
}
|
|
1602
1626
|
};
|
|
1603
1627
|
}
|
|
1604
|
-
/** The
|
|
1628
|
+
/** The code an embedded document was made from and its TypeScript file name, as `getExtraServiceScripts` names it. */
|
|
1605
1629
|
function embeddedOf(context, document) {
|
|
1606
1630
|
const decoded = context.decodeEmbeddedDocumentUri(URI.parse(document.uri));
|
|
1607
1631
|
if (!decoded) return;
|
|
1608
1632
|
const [sourceUri, codeId] = decoded;
|
|
1609
1633
|
const root = context.language.scripts.get(sourceUri)?.generated?.root;
|
|
1610
1634
|
if (!(root instanceof StaticboltCode)) return;
|
|
1611
|
-
const
|
|
1612
|
-
if (!
|
|
1635
|
+
const code = root.codes.find((candidate) => candidate.id === codeId);
|
|
1636
|
+
if (!code) return;
|
|
1613
1637
|
const documentFileName = context.project.typescript?.uriConverter.asFileName(sourceUri) ?? sourceUri.fsPath;
|
|
1614
1638
|
return {
|
|
1615
|
-
|
|
1639
|
+
code,
|
|
1616
1640
|
fileName: embeddedFileName(documentFileName, codeId)
|
|
1617
1641
|
};
|
|
1618
1642
|
}
|
package/lib/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["#htmlDocument","#info","#console","#onLoad","#watcher","#scheduleReload","#load","#reload","#collectPluginData","#starting","#loaded","#roots","#start"],"sources":["../src/helpers/inferred-project.ts","../src/helpers/document-elements.ts","../src/helpers/document-info.ts","../src/helpers/markdown-regions.ts","../src/helpers/regions.ts","../src/helpers/virtual-document.ts","../src/virtual-code.ts","../src/language-plugin.ts","../src/projects.ts","../src/helpers/merge-html-data.ts","../src/helpers/validation.ts","../src/helpers/document-context.ts","../src/services/staticbolt-service.ts","../src/helpers/syntax-tokens.ts","../src/services/syntax-tokens-service.ts","../src/services/typescript-service.ts","../src/index.ts"],"sourcesContent":["import type { TypeScriptProjectHost } from \"@volar/typescript\";\nimport type * as ts from \"typescript\";\n\n/**\n * The module kind of the project a document outside every config file is served by.\n *\n * TypeScript takes such a document into a project of its own and gives that project CommonJS modules, which a page's scripts are\n * not: `import.meta` alone is an error there. `preserve` leaves the imports as they are written, which is what a browser gets.\n *\n * The settings are only read once the project has its config, so they are taken as they are asked for, and the same object is\n * given back until they change.\n */\nexport function useModuleScripts(typescript: typeof ts, projectHost: TypeScriptProjectHost): void {\n const compilationSettings = projectHost.getCompilationSettings.bind(projectHost);\n let read: ts.CompilerOptions | undefined;\n let settings: ts.CompilerOptions | undefined;\n\n projectHost.getCompilationSettings = () => {\n const current = compilationSettings();\n\n if (current !== read) {\n read = current;\n settings = { ...current, module: typescript.ModuleKind.Preserve ?? typescript.ModuleKind.ESNext };\n }\n\n return settings!;\n };\n}\n","import vscodeHtml from \"vscode-html-languageservice\";\n\nimport type { AttributeInfo, ElementInfo, TextRange } from \"@staticbolt/core\";\nimport type { HTMLDocument, LanguageService, Node } from \"vscode-html-languageservice\";\n\n/** The token kinds the HTML scanner reports. */\nconst TokenType = vscodeHtml.TokenType;\n\n/**\n * Every element of a parsed document in document order, with the attribute offsets the parser leaves out: each start tag is\n * scanned again for them.\n */\nexport function parseElements(languageService: LanguageService, text: string, htmlDocument: HTMLDocument): ElementInfo[] {\n const elements: ElementInfo[] = [];\n\n for (const root of htmlDocument.roots) {\n collectElements(languageService, text, root, undefined, elements);\n }\n\n return elements;\n}\n\n/** The element for a node and, after it, the ones for its children; the tree only ever holds elements. */\nfunction collectElements(\n languageService: LanguageService,\n text: string,\n node: Node,\n parent: ElementInfo | undefined,\n elements: ElementInfo[]\n): void {\n const element = createElement(languageService, text, node, parent);\n\n elements.push(element);\n parent?.children.push(element);\n\n for (const child of node.children) {\n collectElements(languageService, text, child, element, elements);\n }\n}\n\n/** The element for a node, with its attributes scanned from its start tag. */\nfunction createElement(languageService: LanguageService, text: string, node: Node, parent: ElementInfo | undefined): ElementInfo {\n const tag = node.tag ?? \"\";\n const startTagEnd = node.startTagEnd ?? node.end;\n const attributes: AttributeInfo[] = [];\n\n const findAttribute = (name: string) => {\n const wanted = name.toLowerCase();\n\n return attributes.find(attribute => attribute.name.toLowerCase() === wanted);\n };\n\n const element: ElementInfo = {\n name: tag.toLowerCase(),\n attributes,\n parent,\n children: [],\n range: { start: node.start, end: node.end },\n nameRange: { start: node.start + 1, end: node.start + 1 + tag.length },\n contentRange: contentRangeOf(node, startTagEnd),\n attribute: findAttribute,\n has: name => findAttribute(name) !== undefined,\n };\n\n attributes.push(...scanAttributes(languageService, text, element, node.start, startTagEnd));\n\n return element;\n}\n\n/**\n * From the end of the start tag to the end tag, or to wherever the parser closed an element missing its end tag; nothing for an\n * element that is over with its start tag, void or self-closing.\n */\nfunction contentRangeOf(node: Node, startTagEnd: number): TextRange | undefined {\n const end = node.endTagStart ?? node.end;\n\n if (end <= startTagEnd) {\n return undefined;\n }\n\n return { start: startTagEnd, end };\n}\n\n/** The attributes in a start tag, with where their names and values sit. */\nfunction scanAttributes(\n languageService: LanguageService,\n text: string,\n element: ElementInfo,\n start: number,\n end: number\n): AttributeInfo[] {\n const attributes: AttributeInfo[] = [];\n const scanner = languageService.createScanner(text.slice(start, end));\n let pending: AttributeInfo | undefined;\n\n for (let token = scanner.scan(); token !== TokenType.EOS; token = scanner.scan()) {\n const range: TextRange = { start: start + scanner.getTokenOffset(), end: start + scanner.getTokenEnd() };\n\n if (token === TokenType.AttributeName) {\n pending = { name: scanner.getTokenText(), value: undefined, element, nameRange: range, valueRange: undefined };\n attributes.push(pending);\n continue;\n }\n\n if (token !== TokenType.AttributeValue || !pending) {\n continue;\n }\n\n const raw = scanner.getTokenText();\n const isQuoted = raw.startsWith('\"') || raw.startsWith(\"'\");\n const quote = isQuoted ? 1 : 0;\n\n pending.value = raw.slice(quote, raw.length - quote);\n pending.valueRange = { start: range.start + quote, end: range.end - quote };\n pending = undefined;\n }\n\n return attributes;\n}\n","import type { DocumentInfo, ElementInfo, Resolver } from \"@staticbolt/core\";\n\n/** The document as a plugin sees it: its text and elements, with lookups over them and the project's resolver. */\nexport function describeDocument(text: string, file: string, elements: ElementInfo[], resolver: Resolver): DocumentInfo {\n return {\n file,\n text,\n elements,\n\n select(...names) {\n const wanted = new Set(names.map(name => name.toLowerCase()));\n\n return elements.filter(element => wanted.has(element.name));\n },\n\n textOf(range) {\n return text.slice(range.start, range.end);\n },\n\n resolve(source) {\n const resolved = resolver.resolve(source, file);\n if (!resolved) {\n return;\n }\n\n return { path: resolved.path, exists: resolved.exists };\n },\n };\n}\n","/**\n * Markdown allows raw HTML anywhere, so a \".md\" file is served as HTML with the parts that can never be HTML — front matter, code\n * blocks and code spans — blanked out first. Blanking keeps every offset, so positions still point at the same place in the\n * file.\n */\nimport { markdownToMdast } from \"satteri\";\n\nimport type { TextRange } from \"@staticbolt/core\";\nimport type { MdastNode } from \"satteri\";\n\n/** None of these nest, so the regions they produce never overlap. */\nconst NON_HTML_NODES = new Set([\"code\", \"inlineCode\", \"yaml\", \"toml\"]);\n\n/** The regions of a markdown document that must not be treated as HTML, sorted by start offset. */\nexport function findMarkdownNonHtmlRegions(text: string): TextRange[] {\n let tree: MdastNode;\n\n try {\n tree = markdownToMdast(text);\n } catch (error) {\n console.warn(\"[staticbolt] could not parse markdown, its whole content is handled as HTML:\", error);\n return [];\n }\n\n const regions: TextRange[] = [];\n const pending: MdastNode[] = [tree];\n\n while (pending.length > 0) {\n const node = pending.pop()!;\n\n // The nodes looked for are leaves, so a node with children is never one of them\n if (\"children\" in node) {\n pending.push(...node.children);\n continue;\n }\n\n if (!NON_HTML_NODES.has(node.type)) continue;\n\n const start = node.position?.start.offset;\n const end = node.position?.end.offset;\n\n if (start === undefined || end === undefined) continue;\n\n regions.push({ start, end });\n }\n\n const sorted = regions.toSorted((a, b) => a.start - b.start);\n\n return toUtf16Offsets(text, sorted);\n}\n\n/**\n * The parser counts code points, the editor counts UTF-16 code units. The two only drift apart once a character outside the basic\n * plane, an emoji most of the time, sits before a region.\n */\nfunction toUtf16Offsets(text: string, regions: TextRange[]): TextRange[] {\n const hasAstral = /[\\uD800-\\uDBFF]/.test(text);\n\n if (!hasAstral) {\n return regions;\n }\n\n const astral: number[] = [];\n let codePoint = 0;\n\n for (const character of text) {\n if (character.length === 2) {\n astral.push(codePoint);\n }\n\n codePoint++;\n }\n\n /** Shifts a code point offset by the number of astral characters before it. */\n function toUtf16(offset: number): number {\n const astralBefore = astral.filter(position => position < offset).length;\n\n return offset + astralBefore;\n }\n\n return regions.map(region => ({ start: toUtf16(region.start), end: toUtf16(region.end) }));\n}\n","import type { DocumentInfo, EmbeddedLanguage, EmbeddedRegion, TextRange } from \"@staticbolt/core\";\n\n/** A region of a plugin language in a document. */\nexport interface PluginRegion extends EmbeddedRegion {\n /** The plugin language. */\n language: EmbeddedLanguage;\n}\n\n/** The regions of one plugin language that share a file: the classic ones together, a module on its own. */\nexport interface LanguageRegions {\n /** The file's id: the language's name, with the module's number for a module. */\n id: string;\n\n /** The plugin language. */\n language: EmbeddedLanguage;\n\n /** Whether the file is a module of one region, with a top level of its own, rather than a script whose top level is global. */\n isModule: boolean;\n\n /** Its regions, in text order. */\n regions: EmbeddedRegion[];\n\n /** The regions of other plugin languages inside its own, in text order: those languages serve them, this one sees them masked. */\n holes: TextRange[];\n}\n\n/** Regions in text order. */\nconst byStart = (a: TextRange, b: TextRange) => a.start - b.start;\n\n/**\n * The regions of every plugin language that claims the document, in text order. A region inside one a language earlier in the\n * config claimed already is left to that language: a plugin that runs a script elsewhere claims it before the core plugin, last\n * in the config, sees the browser in it. A region of another language inside this one is a hole, not a claim.\n */\nexport function findPluginRegions(document: DocumentInfo, languages: readonly EmbeddedLanguage[]): PluginRegion[] {\n const regions: PluginRegion[] = [];\n\n for (const language of languages) {\n if (!language.filter(document.file)) continue;\n\n for (const region of language.findRegions(document)) {\n if (regions.some(claimed => isInside(region, claimed))) continue;\n\n regions.push({ ...region, language });\n }\n }\n\n return regions.toSorted(byStart);\n}\n\n/** Whether a range lies within another. */\nfunction isInside(range: TextRange, outer: TextRange): boolean {\n return outer.start <= range.start && range.end <= outer.end;\n}\n\n/**\n * The plugin regions by the file they share: the classic regions of a language together, each module on its own. Every group is\n * in text order, with the regions of other languages inside its own as its holes: a placeholder inside a build-time script is\n * the placeholder language's.\n */\nexport function groupByFile(regions: readonly PluginRegion[]): LanguageRegions[] {\n const groups = new Map<string, LanguageRegions>();\n let modules = 0;\n\n for (const region of regions) {\n const id = region.isModule ? `${region.language.name}.module${modules++}` : region.language.name;\n const group = groups.get(id) ?? { id, language: region.language, isModule: region.isModule === true, regions: [], holes: [] };\n\n group.regions.push({ start: region.start, end: region.end });\n group.holes.push(...holesOf(region, regions));\n groups.set(id, group);\n }\n\n return groups.values().toArray();\n}\n\n/** The whole construct a region sits in, delimiters included, or the region itself when it has no more. */\nfunction extentOf(region: EmbeddedRegion): TextRange {\n return region.extent ?? { start: region.start, end: region.end };\n}\n\n/** Whether an offset falls in any of the regions, their ends included. */\nexport function isInRegions(regions: readonly TextRange[], offset: number): boolean {\n return regions.some(region => region.start <= offset && offset <= region.end);\n}\n\n/** The regions of other languages lying inside a region, as their whole constructs. */\nfunction holesOf(region: PluginRegion, regions: readonly PluginRegion[]): TextRange[] {\n const holes: TextRange[] = [];\n\n for (const other of regions) {\n if (other.language === region.language) continue;\n\n const hole = extentOf(other);\n if (hole.start < region.start || hole.end > region.end) continue;\n\n holes.push(hole);\n }\n\n return holes;\n}\n","import type { EmbeddedRegion, TextRange } from \"@staticbolt/core\";\nimport type * as ts from \"typescript\";\n\n/**\n * The text with everything outside the regions blanked, keeping line breaks, so every offset means the same thing as in the text.\n * The character right after a region becomes a `;`, so neighbouring regions on one line stay separate statements.\n */\nexport function blankAround(text: string, regions: readonly EmbeddedRegion[]): string {\n let result = \"\";\n let cursor = 0;\n\n for (const region of regions) {\n result += blank(text.slice(cursor, region.start)) + text.slice(region.start, region.end);\n cursor = region.end;\n\n const next = text[cursor];\n\n if (next === undefined || next === \"\\n\" || next === \"\\r\") continue;\n\n result += \";\";\n cursor++;\n }\n\n return result + blank(text.slice(cursor));\n}\n\n/** The text with the regions blanked, keeping line breaks, so every offset means the same thing as in the text. */\nexport function blankRegions(text: string, regions: readonly TextRange[]): string {\n let result = \"\";\n let cursor = 0;\n\n for (const region of regions) {\n result += text.slice(cursor, region.start) + blank(text.slice(region.start, region.end));\n cursor = region.end;\n }\n\n return result + text.slice(cursor);\n}\n\n/**\n * The holes masked, keeping line breaks, so the code around them still parses and types as it will once they are filled: a hole\n * in a string literal makes the whole literal `(\"\" + \"\")`, a `string` rather than a literal type; a hole in a template becomes a\n * `${<any>0}` substitution, for the same reason; any other hole reads as `(<any>0)`, a value of a type nobody knows yet.\n *\n * A mask stands where an expression stands, so it is parenthesised: whatever surrounds it, `+\"{{ n }}\"` say, binds to the mask as\n * a whole and not to a part of it. A hole too short for its mask gets the longest shorter one that fits, down to nothing.\n */\nexport function mask(typescript: typeof ts, text: string, holes: readonly TextRange[]): string {\n if (holes.length === 0) {\n return text;\n }\n\n const sourceFile = typescript.createSourceFile(\"mask.ts\", text, typescript.ScriptTarget.Latest, true);\n let result = text;\n\n for (const hole of holes) {\n const token = tokenAt(sourceFile, hole.start);\n\n if (token?.kind === typescript.SyntaxKind.StringLiteral) {\n const range = { start: token.getStart(sourceFile), end: token.getEnd() };\n\n result = replace(result, range, fill(text.slice(range.start, range.end), '(\"\" + \"\")', '(\"\"+\"\")', '(\"\")', '\"\"'));\n continue;\n }\n\n if (token && typescript.isTemplateLiteralToken(token)) {\n result = replace(result, hole, fill(text.slice(hole.start, hole.end), \"${<any>0}\", \"${0}\"));\n continue;\n }\n\n result = replace(result, hole, fill(text.slice(hole.start, hole.end), \"(<any>0)\", \"<any>0\", \"(0)\", \"0\"));\n }\n\n return result;\n}\n\n/**\n * The first replacement the original has room for, followed by the rest of the original blanked, so the length and the line\n * breaks are kept; nothing but the blanks when even the shortest is too long.\n */\nfunction fill(original: string, ...replacements: readonly string[]): string {\n const fitting = replacements.find(replacement => replacement.length <= original.length) ?? \"\";\n\n return fitting + blank(original.slice(fitting.length));\n}\n\n/** The token of a parsed text an offset falls in: the deepest node there that has no children. */\nfunction tokenAt(sourceFile: ts.SourceFile, offset: number): ts.Node | undefined {\n let node: ts.Node = sourceFile;\n\n while (true) {\n const child = node\n .getChildren(sourceFile)\n .find(candidate => candidate.getStart(sourceFile) <= offset && offset < candidate.getEnd());\n if (!child) {\n return node === sourceFile ? undefined : node;\n }\n\n node = child;\n }\n}\n\n/** The text with a range replaced by a replacement of the same length. */\nfunction replace(text: string, range: TextRange, replacement: string): string {\n return text.slice(0, range.start) + replacement + text.slice(range.end);\n}\n\n/** Every character but the line breaks replaced by a space. */\nfunction blank(text: string): string {\n return text.replaceAll(/[^\\n\\r]/g, \" \");\n}\n","import path from \"node:path\";\nimport { Resolver } from \"@staticbolt/core\";\nimport vscodeHtml from \"vscode-html-languageservice\";\nimport { TextDocument } from \"vscode-languageserver-textdocument\";\n\nimport { parseElements } from \"./helpers/document-elements.ts\";\nimport { describeDocument } from \"./helpers/document-info.ts\";\nimport { findMarkdownNonHtmlRegions } from \"./helpers/markdown-regions.ts\";\nimport { findPluginRegions, groupByFile } from \"./helpers/regions.ts\";\nimport { blankAround, blankRegions, mask } from \"./helpers/virtual-document.ts\";\n\nimport type { LanguageRegions, PluginRegion } from \"./helpers/regions.ts\";\nimport type { Project } from \"./projects.ts\";\nimport type { CodeMapping, IScriptSnapshot, VirtualCode } from \"@volar/language-core\";\nimport type { DocumentInfo, EmbeddedRegion } from \"@staticbolt/core\";\nimport type * as ts from \"typescript\";\nimport type { HTMLDocument, LanguageService } from \"vscode-html-languageservice\";\nimport type { URI } from \"vscode-uri\";\n\n/** The id of the root code, and of the HTML copy a markdown document is served through. */\nconst ROOT_ID = \"root\";\n\n/** The id of the embedded code holding the HTML of a document that is not HTML itself. */\nconst HTML_ID = \"html\";\n\n/** The TypeScript file name an embedded code of a document is served under. */\nexport function embeddedFileName(documentFileName: string, codeId: string): string {\n return `${documentFileName}.${codeId}.ts`;\n}\n\n/** What every feature is allowed to do on a mapped stretch of code. */\nconst ALL_FEATURES: CodeMapping[\"data\"] = {\n verification: true,\n completion: true,\n semantic: true,\n navigation: true,\n structure: true,\n format: false,\n};\n\n/** The HTML language service the codes parse with; no data provider, only the tree is wanted here. */\nconst htmlLanguageService: LanguageService = vscodeHtml.getLanguageService({ useDefaultDataProvider: false });\n\n/**\n * A document as the server sees it: the HTML (a markdown document's with everything that cannot be HTML blanked out), the regions\n * plugins embed in it, and an embedded TypeScript code per plugin language, served through the project's TypeScript.\n */\nexport class StaticboltCode implements VirtualCode {\n /** The root is the document. */\n readonly id = ROOT_ID;\n\n /** `html` or `markdown`. */\n readonly languageId: string;\n\n /** The document's text. */\n readonly snapshot: IScriptSnapshot;\n\n /** The whole document maps onto itself. */\n readonly mappings: CodeMapping[];\n\n /** The HTML copy of a markdown document, then the TypeScript code of every plugin language with regions. */\n readonly embeddedCodes: VirtualCode[];\n\n /** The document's uri. */\n readonly uri: URI;\n\n /** The project the document belongs to, or nothing when it is outside every loaded one. */\n readonly project: Project | undefined;\n\n /** The document's path relative to its project, or its whole path outside one. */\n readonly file: string;\n\n /** The document as HTML: the text itself, or for markdown the blanked copy. */\n readonly html: string;\n\n /** The regions the plugins embed, in text order. */\n readonly regions: PluginRegion[];\n\n /** The regions by plugin language. */\n readonly languages: LanguageRegions[];\n\n /** The parsed HTML, on first use. */\n #htmlDocument: HTMLDocument | undefined;\n\n /** The document as the plugins see it, on first use. */\n #info: DocumentInfo | undefined;\n\n constructor(typescript: typeof ts, uri: URI, languageId: string, snapshot: IScriptSnapshot, project: Project | undefined) {\n const text = snapshot.getText(0, snapshot.getLength());\n\n this.uri = uri;\n this.languageId = languageId;\n this.snapshot = snapshot;\n this.project = project;\n this.file = project ? path.relative(project.root, uri.fsPath) : uri.fsPath;\n this.mappings = [identityMapping(text.length)];\n this.html = languageId === \"markdown\" ? blankRegions(text, findMarkdownNonHtmlRegions(text)) : text;\n this.regions = project ? findPluginRegions(this.info, project.embeddedLanguages) : [];\n this.languages = groupByFile(this.regions);\n this.embeddedCodes = this.languages.map(group => createTypeScriptCode(typescript, this.info, group));\n\n if (languageId === \"markdown\") {\n this.embeddedCodes.unshift(createHtmlCode(typescript, this.html));\n }\n }\n\n /** The parsed HTML. */\n get htmlDocument(): HTMLDocument {\n this.#htmlDocument ??= htmlLanguageService.parseHTMLDocument(TextDocument.create(this.uri.toString(), \"html\", 0, this.html));\n\n return this.#htmlDocument;\n }\n\n /**\n * The document as the plugins see it: its elements with their attributes and where everything sits, and the project's resolver.\n * Outside a project, a resolver of the document's own directory.\n */\n get info(): DocumentInfo {\n this.#info ??= describeDocument(\n this.html,\n this.file,\n parseElements(htmlLanguageService, this.html, this.htmlDocument),\n this.project?.resolver ?? new Resolver(path.dirname(this.uri.fsPath), false, {}, false)\n );\n\n return this.#info;\n }\n}\n\n/** A mapping of a whole text onto itself. */\nfunction identityMapping(length: number): CodeMapping {\n return { sourceOffsets: [0], generatedOffsets: [0], lengths: [length], data: ALL_FEATURES };\n}\n\n/** A markdown document's HTML copy, at the same offsets. */\nfunction createHtmlCode(typescript: typeof ts, html: string): VirtualCode {\n return {\n id: HTML_ID,\n languageId: \"html\",\n snapshot: typescript.ScriptSnapshot.fromString(html),\n mappings: [identityMapping(html.length)],\n };\n}\n\n/**\n * The TypeScript code of one file of a plugin language: the document with everything outside the file's regions blanked, so every\n * offset means the same thing in both, the regions of other languages masked, and the language's prelude appended at the end. A\n * module is made one with an `export {}`, so its top level is its own; a script's top level is the global scope, as it is in the\n * browser. The regions map back to the document; what lies before the first and after the last maps onto their edges, so what\n * TypeScript puts at the top or the bottom of the file, an import or a declaration it adds say, lands in the code.\n */\nfunction createTypeScriptCode(typescript: typeof ts, info: DocumentInfo, group: LanguageRegions): VirtualCode {\n const { id, language, isModule, regions, holes } = group;\n const prelude = typeof language.prelude === \"function\" ? language.prelude(info) : language.prelude;\n const suffix = [isModule ? \"export {};\" : \"\", prelude ?? \"\"].filter(Boolean).join(\"\\n\");\n const text = `${mask(typescript, blankAround(info.text, regions), holes)}\\n${suffix}\\n`;\n const first = regions[0];\n const last = regions.at(-1) ?? first;\n\n return {\n id,\n languageId: \"typescript\",\n snapshot: typescript.ScriptSnapshot.fromString(text),\n mappings: [\n {\n sourceOffsets: regions.map(region => region.start),\n generatedOffsets: regions.map(region => region.start),\n lengths: regions.map(region => region.end - region.start),\n data: ALL_FEATURES,\n },\n edgeMapping(topOf(info.text, first), 0, first.start),\n edgeMapping(last.end, last.end, text.length - last.end),\n ],\n };\n}\n\n/**\n * A stretch of the generated text outside the regions mapped onto one spot of the document, for the edits of completions and code\n * actions only: nothing there is verified, coloured or folded.\n */\nfunction edgeMapping(sourceOffset: number, generatedOffset: number, generatedLength: number): CodeMapping {\n return {\n sourceOffsets: [sourceOffset],\n generatedOffsets: [generatedOffset],\n lengths: [0],\n generatedLengths: [generatedLength],\n data: { completion: true, navigation: true, verification: false, semantic: false, structure: false, format: false },\n };\n}\n\n/** Where the top of a file's code is in the document: the first region's start, past the line break a script body opens with. */\nfunction topOf(text: string, first: EmbeddedRegion): number {\n const lineBreak = /^\\r?\\n/.exec(text.slice(first.start, first.end));\n\n return first.start + (lineBreak?.[0].length ?? 0);\n}\n","import path from \"node:path\";\nimport { forEachEmbeddedCode } from \"@volar/language-core\";\n\nimport { embeddedFileName, StaticboltCode } from \"./virtual-code.ts\";\n\nimport type { Projects } from \"./projects.ts\";\nimport type { LanguagePlugin } from \"@volar/language-core\";\nimport type * as ts from \"typescript\";\nimport type { URI } from \"vscode-uri\";\n\n/** The language ids by file extension. */\nconst LANGUAGE_IDS: Record<string, string | undefined> = {\n \".html\": \"html\",\n \".md\": \"markdown\",\n};\n\n/**\n * Tells Volar what an HTML or markdown document is: the document itself, and a TypeScript file per plugin language, named after\n * the document and the language and served by the project's TypeScript next to it.\n */\nexport function createLanguagePlugin(typescript: typeof ts, projects: Projects): LanguagePlugin<URI, StaticboltCode> {\n return {\n getLanguageId(uri) {\n return LANGUAGE_IDS[path.extname(uri.path).toLowerCase()];\n },\n\n createVirtualCode(uri, languageId, snapshot) {\n if (uri.scheme !== \"file\") {\n return;\n }\n\n if (languageId !== \"html\" && languageId !== \"markdown\") {\n return;\n }\n\n return new StaticboltCode(typescript, uri, languageId, snapshot, projects.of(uri.toString()));\n },\n\n typescript: {\n extraFileExtensions: [\n { extension: \"html\", isMixedContent: true, scriptKind: typescript.ScriptKind.Deferred },\n { extension: \"md\", isMixedContent: true, scriptKind: typescript.ScriptKind.Deferred },\n ],\n\n getServiceScript() {\n return;\n },\n\n getExtraServiceScripts(fileName, root) {\n const scripts = [];\n\n for (const code of forEachEmbeddedCode(root)) {\n if (code.languageId !== \"typescript\") continue;\n\n scripts.push({\n fileName: embeddedFileName(fileName, code.id),\n code,\n extension: \".ts\",\n scriptKind: typescript.ScriptKind.TS,\n });\n }\n\n return scripts;\n },\n },\n };\n}\n","import { existsSync, globSync, watch } from \"node:fs\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { Resolver } from \"@staticbolt/core\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport type { FSWatcher } from \"node:fs\";\nimport type { AppConfig, EmbeddedLanguage, Plugin } from \"@staticbolt/core\";\nimport type { HTMLDataV1, IAttributeData, MarkupContent } from \"vscode-html-languageservice\";\nimport type { WorkspaceFolder } from \"vscode-languageserver-protocol\";\n\n/** Where the projects log. */\ntype RemoteConsole = Pick<Console, \"log\" | \"error\">;\n\n/** In order, so the first one that exists wins. */\nconst CONFIG_NAMES = [\".staticbolt.ts\", \".staticbolt.js\"];\n\n/** A save may come as several writes; the config is loaded once they have stopped. */\nconst RELOAD_DELAY_MS = 150;\n\n/** A plugin's document check, with the plugin's name to label what it reports. */\nexport interface Validator {\n /** The plugin's name. */\n name: string;\n\n /** The plugin's `lspValidate` hook. */\n validate: NonNullable<Plugin[\"lspValidate\"]>;\n\n /** Whether the hook threw already, so it is logged once; the next config load starts over. */\n hasFailed: boolean;\n}\n\n/** The directory of the nearest config file above a directory, which is the project it belongs to. */\nfunction findProjectRoot(directory: string): string | undefined {\n while (true) {\n const hasConfig = CONFIG_NAMES.some(name => existsSync(path.join(directory, name)));\n if (hasConfig) {\n return directory;\n }\n\n const parent = path.dirname(directory);\n if (parent === directory) return;\n\n directory = parent;\n }\n}\n\n/** A resolver for a root that does not log missing files, with the config's aliases on top of the tsconfig's. */\nfunction createResolver(root: string, aliases?: Record<string, string>): Resolver {\n return new Resolver(root, false, aliases, false);\n}\n\n/** Every project under the workspace folders, for the startup log. */\nexport function findProjectRoots(folders: WorkspaceFolder[]): string[] {\n return folders.flatMap(folder => {\n const cwd = vscodeUri.URI.parse(folder.uri).fsPath;\n const configs = globSync(`**/{${CONFIG_NAMES.join(\",\")}}`, { cwd, exclude: [\"**/node_modules/**\", \"**/.git/**\"] });\n\n return configs.map(config => path.join(cwd, path.dirname(config)));\n });\n}\n\n/**\n * A staticbolt project as the server sees it: its config, kept current while the config file changes, and what its plugins\n * contribute to the editor.\n */\nexport class Project {\n /** The directory holding the config file. */\n readonly root: string;\n\n /** The last config that loaded, or nothing when none did yet. */\n config: AppConfig | undefined;\n\n /** The tags and attributes the plugins contribute to HTML. A new array on every load, so consumers may cache on its identity. */\n htmlData: HTMLDataV1[] = [];\n\n /** The languages the plugins embed in HTML. */\n embeddedLanguages: EmbeddedLanguage[] = [];\n\n /** The plugins that check documents, by name. */\n validators: Validator[] = [];\n\n /** Resolves paths the way the project's build does: its tsconfig paths and config aliases. New on every load. */\n resolver: Resolver;\n\n /** Where to log. */\n readonly #console: RemoteConsole;\n\n /** Follows the root directory for saves of the config file. */\n #watcher: FSWatcher | undefined;\n\n /** The reload waiting for the save to finish. */\n #reload: NodeJS.Timeout | undefined;\n\n /** Told whenever the config loaded. */\n readonly #onLoad: () => void;\n\n /** Nothing is loaded until `start` is called. */\n constructor(root: string, console: RemoteConsole, onLoad: () => void) {\n this.root = root;\n this.resolver = createResolver(root);\n this.#console = console;\n this.#onLoad = onLoad;\n }\n\n /** Loads the config and starts following the config file. */\n async start(): Promise<void> {\n // The directory rather than the file: editors save through a temporary file and a rename, which a watch on the file misses\n this.#watcher = watch(this.root, { persistent: false }, (_event, filename) => {\n if (typeof filename !== \"string\") return;\n if (!CONFIG_NAMES.includes(filename)) return;\n this.#scheduleReload();\n });\n\n this.#watcher.on(\"error\", error => this.#console.error(`[staticbolt] watching ${this.root}: ${error.message}`));\n\n await this.#load();\n }\n\n /** Stops following the config file. */\n dispose(): void {\n clearTimeout(this.#reload);\n this.#watcher?.close();\n }\n\n /** Loads the config again once the save has stopped writing. */\n #scheduleReload(): void {\n clearTimeout(this.#reload);\n this.#reload = setTimeout(() => void this.#load(), RELOAD_DELAY_MS);\n }\n\n /** Loads the config file and takes what its plugins contribute; a failure is logged and leaves the last config in place. */\n async #load(): Promise<void> {\n const configPath = CONFIG_NAMES.map(name => path.join(this.root, name)).find(candidate => existsSync(candidate));\n\n if (!configPath) {\n this.#console.error(`[staticbolt] the config file of ${this.root} is gone`);\n return;\n }\n\n try {\n // A fresh URL each time, since a module is only ever evaluated once\n const module = (await import(`${pathToFileURL(configPath).href}?t=${Date.now()}`)) as { default?: AppConfig };\n if (!module.default) {\n throw new Error(\"it has no default export, use `export default { … }`\");\n }\n\n await this.#collectPluginData(module.default);\n this.config = module.default;\n this.resolver = createResolver(this.root, module.default.aliases);\n this.#onLoad();\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n\n this.#console.error(`[staticbolt] failed to load ${configPath}: ${reason}`);\n }\n }\n\n /** Asks every plugin of the config what it contributes to the editor. */\n async #collectPluginData(config: AppConfig): Promise<void> {\n const plugins = (config.plugins ?? []).flat();\n const embeddedLanguages: EmbeddedLanguage[] = [];\n const validators: Validator[] = [];\n const htmlData: HTMLDataV1[] = [];\n\n for (const plugin of plugins) {\n embeddedLanguages.push(...(plugin.lspEmbeddedLanguages?.() ?? []));\n\n if (plugin.lspValidate) {\n validators.push({ name: plugin.name, validate: plugin.lspValidate, hasFailed: false });\n }\n\n const data = await plugin.lspHtmlData?.();\n if (data) {\n htmlData.push(creditPlugin(data, plugin.name));\n }\n }\n\n this.embeddedLanguages = embeddedLanguages;\n this.validators = validators;\n this.htmlData = htmlData;\n }\n}\n\n/** The projects the server has been asked about, each started on first request. */\nexport class Projects {\n /** Where to log. */\n readonly #console: RemoteConsole;\n\n /** By root, from the moment a project was asked for. */\n readonly #starting = new Map<string, Promise<Project>>();\n\n /** By root, once the project's config loaded. */\n readonly #loaded = new Map<string, Project>();\n\n /** The project root of every directory a document was served from. */\n readonly #roots = new Map<string, string>();\n\n /** Told whenever any project's config loaded. */\n readonly #onLoad: () => void;\n\n /** Starts with no projects; each is started the first time `get` asks for it. */\n constructor(console: RemoteConsole, onLoad: () => void) {\n this.#console = console;\n this.#onLoad = onLoad;\n }\n\n /**\n * The project at a root, or nothing while its config cannot be loaded. The project keeps following its config file either way,\n * so a fixed config loads on its own.\n */\n async get(root: string): Promise<Project | undefined> {\n let project = this.#starting.get(root);\n\n if (!project) {\n project = this.#start(root);\n this.#starting.set(root, project);\n }\n\n const started = await project;\n\n return started.config ? started : undefined;\n }\n\n /**\n * The project a document belongs to, if it has loaded already. One that has not is started, and the document is served again\n * once it loads; a document outside any project gets nothing.\n */\n of(documentUri: string): Project | undefined {\n const directory = path.dirname(vscodeUri.URI.parse(documentUri).fsPath);\n const root = this.#roots.get(directory) ?? findProjectRoot(directory);\n if (root === undefined) {\n return undefined;\n }\n\n this.#roots.set(directory, root);\n\n const loaded = this.#loaded.get(root);\n if (!loaded) {\n void this.get(root);\n }\n\n return loaded;\n }\n\n /** Stops following every project. */\n async dispose(): Promise<void> {\n const projects = await Promise.all(this.#starting.values());\n\n for (const project of projects) {\n project.dispose();\n }\n\n this.#starting.clear();\n this.#loaded.clear();\n this.#roots.clear();\n }\n\n /** A project at a root, counted as loaded from the first time its config loads. */\n async #start(root: string): Promise<Project> {\n const project = new Project(root, this.#console, () => {\n this.#loaded.set(root, project);\n this.#onLoad();\n });\n\n await project.start();\n\n return project;\n }\n}\n\n/** The data with every description saying which plugin it comes from. */\nfunction creditPlugin(htmlData: HTMLDataV1, pluginName: string): HTMLDataV1 {\n const credit = `_Provided by **staticbolt** \\`${pluginName}\\` plugin._`;\n\n const withCredit = (description: string | MarkupContent | undefined): string | MarkupContent => {\n if (!description) {\n return credit;\n }\n\n const text = typeof description === \"string\" ? description : description.value;\n if (text.includes(credit)) {\n return description;\n }\n\n const credited = `${text}\\n\\n${credit}`;\n if (typeof description === \"string\") {\n return credited;\n }\n\n return { ...description, value: credited };\n };\n\n const creditAttributes = (attributes: IAttributeData[]): IAttributeData[] => {\n return attributes.map(attribute => ({ ...attribute, description: withCredit(attribute.description) }));\n };\n\n const tags = htmlData.tags?.map(tag => {\n return { ...tag, description: withCredit(tag.description), attributes: creditAttributes(tag.attributes) };\n });\n\n const globalAttributes = htmlData.globalAttributes ? creditAttributes(htmlData.globalAttributes) : undefined;\n\n return { ...htmlData, tags, globalAttributes };\n}\n","import type {\n HTMLDataV1,\n IAttributeData,\n IHTMLDataProvider,\n IReference,\n ITagData,\n IValueData,\n MarkupContent,\n} from \"vscode-html-languageservice\";\n\n/** A description as the data format allows it: plain, marked up, or absent. */\ntype Description = string | MarkupContent | undefined;\n\n/** Plain text or markdown. */\ntype DescriptionKind = MarkupContent[\"kind\"];\n\n/** The value sets by name. */\ntype ValueSets = Map<string, IValueData[]>;\n\n/** The `valueSet` the HTML language service reads as \"a boolean attribute\". */\nconst BOOLEAN_VALUE_SET = \"v\";\n\n/** Separates the documentation of two contributors, rendered as a horizontal rule. */\nconst MARKDOWN_SEPARATOR = \"\\n\\n---\\n\\n\";\n\n/** The same, for descriptions that are plain text. */\nconst PLAINTEXT_SEPARATOR = \"\\n\\n\";\n\n/** Groups entries by key, preserving the order of first appearance. */\nfunction groupBy<T>(items: T[], toKey: (item: T) => string): T[][] {\n const groups = new Map<string, T[]>();\n\n for (const item of items) {\n const key = toKey(item);\n const group = groups.get(key) ?? [];\n\n group.push(item);\n groups.set(key, group);\n }\n\n return groups.values().toArray();\n}\n\n/** Markdown wins over plain text, so a contributor asking for rich text still gets it. Plain strings imply no preference. */\nfunction mergeDescriptionKind(descriptions: Exclude<Description, undefined>[]): DescriptionKind | undefined {\n let kind: DescriptionKind | undefined;\n\n for (const description of descriptions) {\n if (typeof description === \"string\") continue;\n if (kind === \"markdown\") break;\n\n kind = description.kind;\n }\n\n return kind;\n}\n\n/** The text of a description, whichever shape it has. */\nfunction textOf(description: Exclude<Description, undefined>): string {\n if (typeof description === \"string\") {\n return description;\n }\n\n return description.value;\n}\n\n/** Concatenates every distinct description, so no contributor's documentation gets lost. */\nfunction mergeDescriptions(descriptions: Description[]): Description {\n const defined = descriptions.filter(description => description !== undefined);\n\n if (defined.length <= 1) {\n return defined[0];\n }\n\n const parts: string[] = [];\n const seen = new Set<string>();\n\n for (const description of defined) {\n const text = textOf(description).trim();\n if (!text || seen.has(text)) continue;\n\n seen.add(text);\n parts.push(text);\n }\n\n if (parts.length === 0) {\n return undefined;\n }\n\n const kind = mergeDescriptionKind(defined);\n const separator = kind === \"plaintext\" ? PLAINTEXT_SEPARATOR : MARKDOWN_SEPARATOR;\n const value = parts.join(separator);\n\n if (!kind) {\n return value;\n }\n\n return { kind, value };\n}\n\n/** Every distinct reference, a reference being the same as another when both name and url match. */\nfunction mergeReferences(references: (IReference[] | undefined)[]): IReference[] | undefined {\n const merged: IReference[] = [];\n const seen = new Set<string>();\n\n for (const list of references) {\n const entries = list ?? [];\n\n for (const reference of entries) {\n const key = `${reference.name} ${reference.url}`;\n if (seen.has(key)) continue;\n\n seen.add(key);\n merged.push(reference);\n }\n }\n\n if (merged.length === 0) {\n return undefined;\n }\n\n return merged;\n}\n\n/** Every distinct browser. */\nfunction mergeBrowsers(browsers: (string[] | undefined)[]): string[] | undefined {\n const merged = new Set<string>();\n\n for (const list of browsers) {\n const entries = list ?? [];\n\n for (const browser of entries) {\n merged.add(browser);\n }\n }\n\n if (merged.size === 0) {\n return undefined;\n }\n\n return [...merged];\n}\n\n/** The values an attribute contributes, with its `valueSet` reference expanded. */\nfunction resolveValues(attribute: IAttributeData, valueSets: ValueSets): IValueData[] {\n const values = attribute.values ?? [];\n\n if (!attribute.valueSet) {\n return values;\n }\n\n return [...(valueSets.get(attribute.valueSet) ?? []), ...values];\n}\n\n/** Merges entries sharing the same value name into one. Unlike tags and attributes, value names are case-sensitive. */\nfunction mergeValues(values: IValueData[]): IValueData[] {\n return groupBy(values, value => value.name).map(group => {\n if (group.length === 1) {\n return group[0];\n }\n\n return {\n name: group[0].name,\n description: mergeDescriptions(group.map(value => value.description)),\n references: mergeReferences(group.map(value => value.references)),\n browsers: mergeBrowsers(group.map(value => value.browsers)),\n status: group.find(value => value.status)?.status,\n };\n });\n}\n\n/**\n * Merges entries sharing the same attribute name into one, combining their descriptions, values, references and browser support.\n *\n * `valueSet` references are expanded into the merged `values`, so attributes contributed by different plugins can each bring\n * their own value set and still end up with a single, complete value list.\n */\nfunction mergeAttributes(attributes: IAttributeData[], valueSets: ValueSets): IAttributeData[] {\n return groupBy(attributes, attribute => attribute.name.toLowerCase()).map(group => {\n if (group.length === 1 && !group[0].valueSet) {\n return group[0];\n }\n\n const values = mergeValues(group.flatMap(attribute => resolveValues(attribute, valueSets)));\n const isBoolean = group.some(attribute => attribute.valueSet === BOOLEAN_VALUE_SET);\n\n return {\n name: group[0].name,\n description: mergeDescriptions(group.map(attribute => attribute.description)),\n valueSet: isBoolean ? BOOLEAN_VALUE_SET : undefined,\n values: values.length > 0 ? values : undefined,\n references: mergeReferences(group.map(attribute => attribute.references)),\n browsers: mergeBrowsers(group.map(attribute => attribute.browsers)),\n status: group.find(attribute => attribute.status)?.status,\n };\n });\n}\n\n/** Merges entries sharing the same tag name into one, including their attributes. */\nfunction mergeTags(tags: ITagData[], valueSets: ValueSets): ITagData[] {\n return groupBy(tags, tag => tag.name.toLowerCase()).map(group => {\n const attributes = mergeAttributes(\n group.flatMap(tag => tag.attributes ?? []),\n valueSets\n );\n\n if (group.length === 1) {\n return { ...group[0], attributes };\n }\n\n return {\n name: group[0].name,\n description: mergeDescriptions(group.map(tag => tag.description)),\n attributes,\n references: mergeReferences(group.map(tag => tag.references)),\n browsers: mergeBrowsers(group.map(tag => tag.browsers)),\n status: group.find(tag => tag.status)?.status,\n void: group.some(tag => tag.void),\n };\n });\n}\n\n/** Value sets sharing a name are merged, so an attribute referencing one gets the values of every contributor. */\nfunction collectValueSets(htmlData: HTMLDataV1[]): ValueSets {\n const valueSets: ValueSets = new Map();\n const collected = htmlData.flatMap(data => data.valueSets ?? []);\n\n for (const valueSet of collected) {\n const existing = valueSets.get(valueSet.name) ?? [];\n\n valueSets.set(valueSet.name, mergeValues([...existing, ...valueSet.values]));\n }\n\n return valueSets;\n}\n\n/**\n * Builds a single data provider out of every collected `HTMLDataV1`, merging tags, attributes and values that share the same name\n * instead of reporting them once per contributor.\n */\nexport function createMergedHtmlDataProvider(id: string, htmlData: HTMLDataV1[]): IHTMLDataProvider {\n const valueSets = collectValueSets(htmlData);\n\n const tags = mergeTags(\n htmlData.flatMap(data => data.tags ?? []),\n valueSets\n );\n\n const globalAttributes = mergeAttributes(\n htmlData.flatMap(data => data.globalAttributes ?? []),\n valueSets\n );\n\n const tagsByName = new Map(tags.map(tag => [tag.name.toLowerCase(), tag]));\n const attributesByTag = new Map<string, IAttributeData[]>();\n\n function provideAttributes(tag: string): IAttributeData[] {\n const key = tag.toLowerCase();\n\n const cached = attributesByTag.get(key);\n if (cached) {\n return cached;\n }\n\n // An unknown tag only sees the global attributes, which are merged already\n const tagAttributes = tagsByName.get(key)?.attributes;\n if (!tagAttributes || tagAttributes.length === 0) {\n return globalAttributes;\n }\n\n const attributes = mergeAttributes([...globalAttributes, ...tagAttributes], valueSets);\n attributesByTag.set(key, attributes);\n\n return attributes;\n }\n\n return {\n getId() {\n return id;\n },\n\n isApplicable(languageId) {\n return languageId === \"html\";\n },\n\n provideTags() {\n return tags;\n },\n\n provideAttributes,\n\n provideValues(tag, attribute) {\n const name = attribute.toLowerCase();\n const match = provideAttributes(tag).find(candidate => candidate.name.toLowerCase() === name);\n\n return match?.values ?? [];\n },\n };\n}\n","import { DiagnosticSeverity } from \"vscode-languageserver-protocol\";\n\nimport type { Project } from \"../projects.ts\";\nimport type { AttributeInfo, DocumentInfo, ElementInfo, ProblemReporter, ProblemTarget, TextRange } from \"@staticbolt/core\";\nimport type { Diagnostic } from \"vscode-languageserver-protocol\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\n\n/** What diagnostics from plugins are labelled with, followed by the plugin's name. */\nconst SOURCE = \"staticbolt\";\n\n/** What validating a document takes. */\nexport interface ValidationInput {\n /** The document as HTML, where the diagnostics go. */\n document: TextDocument;\n\n /** The document as the plugins see it. */\n info: DocumentInfo;\n\n /** The project whose plugins validate. */\n project: Project;\n\n /** Where a failing plugin is logged. */\n console: Pick<Console, \"error\">;\n}\n\n/**\n * Runs every validating plugin of the project over a document and gathers what they report as diagnostics. A plugin that throws\n * is skipped, so the others still report, and logged the first time.\n */\nexport async function validateDocument({ document, info, project, console }: ValidationInput): Promise<Diagnostic[]> {\n const diagnostics: Diagnostic[] = [];\n\n for (const validator of project.validators) {\n const report = createReporter(document, validator.name, diagnostics);\n\n try {\n await validator.validate(info, report);\n } catch (error) {\n if (validator.hasFailed) continue;\n\n validator.hasFailed = true;\n console.error(`[staticbolt] the ${validator.name} plugin failed to validate ${info.file}:`, error);\n }\n }\n\n return diagnostics;\n}\n\n/** A reporter adding to `diagnostics`, each one labelled with the plugin it comes from. */\nfunction createReporter(document: TextDocument, pluginName: string, diagnostics: Diagnostic[]): ProblemReporter {\n function reportAs(severity: DiagnosticSeverity) {\n return (target: ProblemTarget, message: string) => {\n const { start, end } = rangeOf(target);\n\n diagnostics.push({\n range: { start: document.positionAt(start), end: document.positionAt(end) },\n message,\n severity,\n source: SOURCE,\n code: pluginName,\n });\n };\n }\n\n return {\n error: reportAs(DiagnosticSeverity.Error),\n warn: reportAs(DiagnosticSeverity.Warning),\n info: reportAs(DiagnosticSeverity.Information),\n hint: reportAs(DiagnosticSeverity.Hint),\n };\n}\n\n/** An element underlines its tag name, an attribute its value or else its name, a range itself. */\nfunction rangeOf(target: ProblemTarget): TextRange {\n if (isElement(target)) {\n return target.nameRange;\n }\n\n if (isAttribute(target)) {\n return target.valueRange ?? target.nameRange;\n }\n\n return target;\n}\n\n/** Only an element has children. */\nfunction isElement(target: ProblemTarget): target is ElementInfo {\n return \"children\" in target;\n}\n\n/** Only an attribute is on an element. */\nfunction isAttribute(target: ProblemTarget): target is AttributeInfo {\n return \"element\" in target;\n}\n","import path from \"node:path\";\nimport { decodeEmbeddedDocumentUri } from \"@volar/language-service\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport type { Resolver } from \"@staticbolt/core\";\nimport type { DocumentContext } from \"vscode-html-languageservice\";\n\n/** A reference that carries its own scheme, `https:` or `mailto:` say. */\nconst WITH_SCHEME = /^[a-z][\\w+.-]*:/i;\n\n/**\n * How references in a document map to files: path aliases through the project's resolver, absolute paths against the project\n * root, everything else relative to the document.\n *\n * The language service bases a reference on the uri of the document it was given, which is the embedded copy the server serves\n * the document through; that copy carries no path, so it falls back to the document itself, and any other base, a `<base href>`\n * say, stands as given.\n */\nexport function getDocumentContext(documentUri: string, resolver: Resolver): DocumentContext {\n return {\n resolveReference(reference, base = documentUri) {\n if (WITH_SCHEME.test(reference)) {\n return reference;\n }\n\n if (decodeEmbeddedDocumentUri(vscodeUri.URI.parse(base)) !== undefined) {\n base = documentUri;\n }\n\n const aliased = resolver.resolveAlias(reference);\n if (aliased !== undefined) {\n return vscodeUri.URI.file(path.join(resolver.root, aliased)).toString(true);\n }\n\n if (reference.startsWith(\"/\")) {\n return vscodeUri.URI.file(path.join(resolver.root, reference)).toString(true);\n }\n\n const baseUri = vscodeUri.URI.parse(base);\n const baseDirectory = baseUri.path.endsWith(\"/\") ? baseUri : vscodeUri.Utils.dirname(baseUri);\n\n return vscodeUri.Utils.resolvePath(baseDirectory, reference).toString(true);\n },\n };\n}\n","import path from \"node:path\";\nimport vscodeHtml from \"vscode-html-languageservice\";\nimport { CompletionItemKind, SemanticTokenTypes, TextEdit } from \"vscode-languageserver-protocol\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport { createMergedHtmlDataProvider } from \"../helpers/merge-html-data.ts\";\nimport { isInRegions } from \"../helpers/regions.ts\";\nimport { validateDocument } from \"../helpers/validation.ts\";\nimport { getDocumentContext } from \"../helpers/document-context.ts\";\nimport { StaticboltCode } from \"../virtual-code.ts\";\n\nimport type { Project } from \"../projects.ts\";\nimport type { LanguageServiceContext, LanguageServicePlugin, SemanticToken } from \"@volar/language-service\";\nimport type { FileStat, FileSystemProvider, HTMLDataV1, LanguageService } from \"vscode-html-languageservice\";\nimport type { CompletionItem, Position } from \"vscode-languageserver-protocol\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\n\n/** The id the merged data provider registers under with the language service. */\nconst DATA_PROVIDER_ID = \"staticbolt\";\n\n/** The cursor inside a `src` or `href` value before any slash, capturing what is typed of its first segment. */\nconst PATH_VALUE_START = /(?:src|href)\\s*=\\s*[\"']([^\"'/\\s]*)$/;\n\n/** Has the editor open the completions again, right after an item is taken. */\nconst SUGGEST = { title: \"Suggest\", command: \"editor.action.triggerSuggest\" };\n\n/** A document of a loaded project, with the HTML language service that knows the project's tags and attributes. */\ninterface Found {\n /** The document's root code, holding its HTML and regions. */\n code: StaticboltCode;\n\n /** The project the document belongs to. */\n project: Project;\n\n /** The project's HTML language service. */\n languageService: LanguageService;\n}\n\n/**\n * What the plugins add to HTML: their tags and attributes for completion and hover, path completion that knows the project's\n * aliases, links resolved the way the build resolves them, and the problems the plugins find. The editor's own HTML support\n * covers the standard elements, and stays out of the regions the plugins embed.\n */\nexport function createStaticboltService(): LanguageServicePlugin {\n return {\n name: \"staticbolt\",\n\n capabilities: {\n completionProvider: { triggerCharacters: [\".\", \":\", \"<\", '\"', \"=\", \"/\"] },\n hoverProvider: true,\n documentLinkProvider: {},\n diagnosticProvider: { interFileDependencies: false, workspaceDiagnostics: false },\n semanticTokensProvider: { legend: { tokenTypes: [SemanticTokenTypes.operator], tokenModifiers: [] } },\n },\n\n create(context) {\n // One language service per project's data, which is a new array whenever its config is loaded again\n const languageServices = new WeakMap<HTMLDataV1[], LanguageService>();\n\n /** The HTML language service that knows a project's tags and attributes. */\n function languageServiceOf(project: Project): LanguageService {\n let languageService = languageServices.get(project.htmlData);\n\n if (!languageService) {\n languageService = vscodeHtml.getLanguageService({\n clientCapabilities: context.env.clientCapabilities,\n fileSystemProvider: fileSystemOf(context),\n useDefaultDataProvider: false,\n customDataProviders: [createMergedHtmlDataProvider(DATA_PROVIDER_ID, project.htmlData)],\n });\n\n languageServices.set(project.htmlData, languageService);\n }\n\n return languageService;\n }\n\n /** The document's code and project, or nothing when the document is no HTML of a loaded project. */\n function find(document: TextDocument): Found | undefined {\n if (document.languageId !== \"html\") {\n return undefined;\n }\n\n const code = codeOf(context, document);\n if (!code?.project) {\n return undefined;\n }\n\n return { code, project: code.project, languageService: languageServiceOf(code.project) };\n }\n\n return {\n async provideCompletionItems(document, position) {\n const found = find(document);\n if (!found) {\n return;\n }\n\n // Inside a plugin's region the code is the plugin language's, served through TypeScript\n if (isInRegions(found.code.regions, document.offsetAt(position))) {\n return;\n }\n\n const { code, project, languageService } = found;\n const documentContext = getDocumentContext(code.uri.toString(), project.resolver);\n const list = await languageService.doComplete2(document, position, code.htmlDocument, documentContext);\n\n list.items.push(...aliasCompletions(document, position, project.resolver.aliases));\n\n // Ahead of whatever the editor's own HTML support offers\n for (const item of list.items) {\n item.sortText = \"0_\" + item.label;\n }\n\n return list;\n },\n\n provideHover(document, position) {\n const found = find(document);\n if (!found) {\n return;\n }\n\n if (isInRegions(found.code.regions, document.offsetAt(position))) {\n return;\n }\n\n return found.languageService.doHover(document, position, found.code.htmlDocument);\n },\n\n provideDocumentLinks(document) {\n const found = find(document);\n if (!found) {\n return;\n }\n\n const { code, project, languageService } = found;\n const documentPath = code.uri.fsPath;\n const documentContext = getDocumentContext(code.uri.toString(), project.resolver);\n const links = languageService.findDocumentLinks(document, documentContext);\n\n // Resolved the way the build resolves sources: aliases, extensionless paths and directories with an index file\n for (const link of links) {\n if (!link.target) continue;\n\n const source = path.relative(path.dirname(documentPath), vscodeUri.URI.parse(link.target).fsPath);\n const resolved = project.resolver.resolve(source, code.file);\n if (!resolved) continue;\n\n link.target = vscodeUri.URI.file(resolved.path).toString();\n }\n\n return links;\n },\n\n /**\n * Colours the delimiters of the plugins' regions, the `{{` and `}}` of a placeholder: they are outside the code, so\n * nothing else colours them, and they would take the colour of whatever they sit in, an attribute's string say.\n */\n provideDocumentSemanticTokens(document, _range, legend) {\n const found = find(document);\n if (!found) {\n return;\n }\n\n const type = legend.tokenTypes.indexOf(SemanticTokenTypes.operator);\n const tokens: SemanticToken[] = [];\n\n for (const region of found.code.regions) {\n if (!region.extent) continue;\n\n for (const [start, end] of [\n [region.extent.start, region.start],\n [region.end, region.extent.end],\n ]) {\n if (end <= start) continue;\n\n const { line, character } = document.positionAt(start);\n\n tokens.push([line, character, end - start, type, 0]);\n }\n }\n\n return tokens;\n },\n\n provideDiagnostics(document) {\n const found = find(document);\n if (!found) {\n return;\n }\n\n return validateDocument({\n document,\n info: found.code.info,\n project: found.project,\n console: context.env.console ?? console,\n });\n },\n };\n },\n };\n}\n\n/** The editor's file system, as the HTML language service reads it for path completions; nothing is there without one. */\nfunction fileSystemOf(context: LanguageServiceContext): FileSystemProvider {\n const missing: FileStat = { type: vscodeHtml.FileType.Unknown, ctime: -1, mtime: -1, size: -1 };\n\n return {\n async stat(uri) {\n return (await context.env.fs?.stat(vscodeUri.URI.parse(uri))) ?? missing;\n },\n\n async readDirectory(uri) {\n return (await context.env.fs?.readDirectory(vscodeUri.URI.parse(uri))) ?? [];\n },\n };\n}\n\n/** The root code of the document a service is asked about, whether it is the document itself or its embedded HTML copy. */\nfunction codeOf(context: LanguageServiceContext, document: TextDocument): StaticboltCode | undefined {\n const uri = vscodeUri.URI.parse(document.uri);\n const [sourceUri] = context.decodeEmbeddedDocumentUri(uri) ?? [uri];\n const root = context.language.scripts.get(sourceUri)?.generated?.root;\n\n if (!(root instanceof StaticboltCode)) {\n return undefined;\n }\n\n return root;\n}\n\n/**\n * The path aliases, offered at the start of a `src` or `href` value: a partly typed one completes, and a directory alias opens\n * its listing right away.\n */\nfunction aliasCompletions(document: TextDocument, position: Position, aliases: Record<string, string>): CompletionItem[] {\n const lineBeforeCursor = document.getText({ start: { line: position.line, character: 0 }, end: position });\n const typed = PATH_VALUE_START.exec(lineBeforeCursor)?.[1];\n\n if (typed === undefined) {\n return [];\n }\n\n const range = { start: { line: position.line, character: position.character - typed.length }, end: position };\n\n return Object.keys(aliases).map(alias => {\n const textEdit = TextEdit.replace(range, alias);\n\n // A directory alias goes on to list its files\n if (alias.endsWith(\"/\")) {\n return { label: alias, kind: CompletionItemKind.Folder, textEdit, command: SUGGEST };\n }\n\n return { label: alias, kind: CompletionItemKind.File, textEdit };\n });\n}\n","import { SemanticTokenModifiers, SemanticTokenTypes } from \"vscode-languageserver-protocol\";\n\nimport type * as ts from \"typescript\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\n\n/** A token with the names of its type and modifiers. */\nexport interface SyntaxToken {\n /** The zero-based line the token is on. */\n line: number;\n\n /** The zero-based character the token starts at. */\n character: number;\n\n /** The number of characters the token spans. */\n length: number;\n\n /** The name of the token type. */\n type: string;\n\n /** The names of the token modifiers. */\n modifiers: readonly string[];\n}\n\n/** A standard token type with modifiers, what an editor that knows only the standard types is sent for a scoped type. */\ninterface StandardType {\n /** The standard type. */\n type: string;\n\n /** Its modifiers. */\n modifiers: readonly string[];\n}\n\n/** A member name, as TypeScript names the ones it knows. */\nconst PROPERTY: StandardType = { type: SemanticTokenTypes.property, modifiers: [] };\n\n/** The literals of the language as the standard types see them: read-only variables of the library. */\nconst LITERAL: StandardType = {\n type: SemanticTokenTypes.variable,\n modifiers: [SemanticTokenModifiers.readonly, SemanticTokenModifiers.defaultLibrary],\n};\n\n/**\n * The token types of what TypeScript's own tokens leave out, after the grammar scopes a TypeScript file gets them coloured by, so\n * an editor that maps them to those scopes colours the code as it colours TypeScript. Split where themes tell the scopes apart: a\n * `const` sits in `meta.var.expr`, a `class` in `meta.class`, an `import` in `meta.import`. The values are the standard types an\n * editor that knows only those is sent instead.\n */\nexport const SCOPED_TYPES = {\n /** `if`, `return`, `await`: `keyword.control`. */\n keywordControl: { type: SemanticTokenTypes.keyword, modifiers: [] },\n\n /** `import`, `export`, `from`, `as`: `meta.import keyword.control.import`. */\n keywordControlImport: { type: SemanticTokenTypes.keyword, modifiers: [] },\n\n /** `const`, `let`, `var`: `meta.var.expr storage.type`. */\n storageTypeVariable: { type: SemanticTokenTypes.modifier, modifiers: [] },\n\n /** `function`: `meta.function storage.type.function`. */\n storageTypeFunction: { type: SemanticTokenTypes.modifier, modifiers: [] },\n\n /** `class`: `meta.class storage.type.class`. */\n storageTypeClass: { type: SemanticTokenTypes.modifier, modifiers: [] },\n\n /** `interface`, `type`, `enum`, `namespace`: `storage.type`. */\n storageType: { type: SemanticTokenTypes.modifier, modifiers: [] },\n\n /** `async`, `static`, `readonly`, `extends`: `storage.modifier`. */\n storageModifier: { type: SemanticTokenTypes.modifier, modifiers: [] },\n\n /** `typeof`, `instanceof`, `in`: `keyword.operator.expression`. */\n keywordOperatorExpression: { type: SemanticTokenTypes.keyword, modifiers: [] },\n\n /** `new`: `new.expr keyword.operator.new`. */\n keywordOperatorNew: { type: SemanticTokenTypes.keyword, modifiers: [] },\n\n /** `true`, `null`, `undefined`: `constant.language`. */\n constantLanguage: LITERAL,\n\n /** `this`, `super`: `variable.language`. */\n variableLanguage: LITERAL,\n\n /** `string`, `number`, `boolean`: `meta.type.annotation support.type.primitive`. */\n supportTypePrimitive: { type: SemanticTokenTypes.type, modifiers: [SemanticTokenModifiers.defaultLibrary] },\n} as const satisfies Record<string, StandardType>;\n\n/** A scoped token type. */\ntype ScopedType = keyof typeof SCOPED_TYPES;\n\n/** The scoped type of each keyword TypeScript tokenizes as one; the control flow keywords are the rest. */\ntype Keywords = Partial<Record<ts.SyntaxKind, ScopedType>>;\n\n/** Names the tokens of a parsed TypeScript text. */\nexport type SyntaxTokenizer = (sourceFile: ts.SourceFile, document: TextDocument, checker?: ts.TypeChecker) => SyntaxToken[];\n\n/** The punctuation that is an operator; the brackets, separators and accessors are left to the default colour. */\nconst OPERATORS = new Set(\n (\n \"= == === != !== + - * / % ** ++ -- < > <= >= && || ?? ! ~ & | ^ << >> >>> ? : => ... += -= *= /= %= **= <<= >>= >>>= \" +\n \"&= |= ^= &&= ||= ??=\"\n ).split(\" \")\n);\n\n/** The keyword table, with the `SyntaxKind` values of the TypeScript in use. */\nfunction keywordsOf(typescript: typeof ts): Keywords {\n const { SyntaxKind } = typescript;\n const keywords: Keywords = {};\n\n const table: [ts.SyntaxKind[], ScopedType][] = [\n [[SyntaxKind.ImportKeyword, SyntaxKind.ExportKeyword, SyntaxKind.FromKeyword, SyntaxKind.AsKeyword], \"keywordControlImport\"],\n [[SyntaxKind.ConstKeyword, SyntaxKind.LetKeyword, SyntaxKind.VarKeyword], \"storageTypeVariable\"],\n [[SyntaxKind.FunctionKeyword], \"storageTypeFunction\"],\n [[SyntaxKind.ClassKeyword], \"storageTypeClass\"],\n [\n [\n SyntaxKind.InterfaceKeyword,\n SyntaxKind.TypeKeyword,\n SyntaxKind.EnumKeyword,\n SyntaxKind.NamespaceKeyword,\n SyntaxKind.ModuleKeyword,\n ],\n \"storageType\",\n ],\n [\n [\n SyntaxKind.AbstractKeyword,\n SyntaxKind.AccessorKeyword,\n SyntaxKind.AsyncKeyword,\n SyntaxKind.DeclareKeyword,\n SyntaxKind.ExtendsKeyword,\n SyntaxKind.ImplementsKeyword,\n SyntaxKind.OverrideKeyword,\n SyntaxKind.PrivateKeyword,\n SyntaxKind.ProtectedKeyword,\n SyntaxKind.PublicKeyword,\n SyntaxKind.ReadonlyKeyword,\n SyntaxKind.StaticKeyword,\n ],\n \"storageModifier\",\n ],\n [\n [\n SyntaxKind.DeleteKeyword,\n SyntaxKind.InKeyword,\n SyntaxKind.InferKeyword,\n SyntaxKind.InstanceOfKeyword,\n SyntaxKind.IsKeyword,\n SyntaxKind.KeyOfKeyword,\n SyntaxKind.OfKeyword,\n SyntaxKind.SatisfiesKeyword,\n SyntaxKind.TypeOfKeyword,\n ],\n \"keywordOperatorExpression\",\n ],\n [[SyntaxKind.NewKeyword], \"keywordOperatorNew\"],\n [[SyntaxKind.TrueKeyword, SyntaxKind.FalseKeyword, SyntaxKind.NullKeyword], \"constantLanguage\"],\n [[SyntaxKind.ThisKeyword, SyntaxKind.SuperKeyword], \"variableLanguage\"],\n [\n [\n SyntaxKind.AnyKeyword,\n SyntaxKind.BigIntKeyword,\n SyntaxKind.BooleanKeyword,\n SyntaxKind.NeverKeyword,\n SyntaxKind.NumberKeyword,\n SyntaxKind.ObjectKeyword,\n SyntaxKind.StringKeyword,\n SyntaxKind.SymbolKeyword,\n SyntaxKind.UndefinedKeyword,\n SyntaxKind.UnknownKeyword,\n ],\n \"supportTypePrimitive\",\n ],\n ];\n\n for (const [kinds, type] of table) {\n for (const kind of kinds) {\n keywords[kind] = type;\n }\n }\n\n return keywords;\n}\n\n/**\n * A tokenizer for what TypeScript's own tokens leave out of a parsed text: keywords by what they are where they stand, literals,\n * comments and operators. The identifiers are left to TypeScript, which knows what each is, except the member names it knows\n * nothing about, a key of a `Record` say, which it leaves out: those are properties all the same. Unless `isScoped`, the keywords\n * are named as the nearest standard types.\n */\nexport function createSyntaxTokenizer(typescript: typeof ts, isScoped: boolean): SyntaxTokenizer {\n const keywords = keywordsOf(typescript);\n\n return (sourceFile, document, checker) => {\n const text = sourceFile.text;\n const tokens: SyntaxToken[] = [];\n const commentEnds = new Set<number>();\n\n /** Whether an identifier names a member TypeScript has no symbol for, so it will not colour it. */\n function isUnknownMember(node: ts.Node): boolean {\n if (!checker || !typescript.isPropertyAccessExpression(node.parent) || node.parent.name !== node) {\n return false;\n }\n\n return checker.getSymbolAtLocation(node) === undefined;\n }\n\n /** The comments before a token, each once. */\n function collectComments(position: number): void {\n const comments = typescript.getLeadingCommentRanges(text, position) ?? [];\n\n for (const comment of comments) {\n if (commentEnds.has(comment.end)) continue;\n\n commentEnds.add(comment.end);\n tokens.push(...splitLines(document, comment.pos, comment.end, { type: SemanticTokenTypes.comment, modifiers: [] }));\n }\n }\n\n /** The tokens of a node and what is inside it. */\n function visit(node: ts.Node): void {\n const children = node.getChildren(sourceFile);\n\n if (children.length === 0) {\n collectComments(node.getFullStart());\n\n const named = isUnknownMember(node) ? PROPERTY : nameOf(typescript, keywords, node, isScoped);\n if (named) {\n tokens.push(...splitLines(document, node.getStart(sourceFile), node.getEnd(), named));\n }\n\n return;\n }\n\n for (const child of children) {\n visit(child);\n }\n }\n\n visit(sourceFile);\n collectComments(sourceFile.endOfFileToken.getFullStart());\n\n return tokens;\n };\n}\n\n/** The type and modifiers of a token, or nothing for the identifiers, plain punctuation and anything that is not a token. */\nfunction nameOf(typescript: typeof ts, keywords: Keywords, node: ts.Node, isScoped: boolean): StandardType | undefined {\n const { SyntaxKind } = typescript;\n const kind = node.kind;\n\n if (kind === SyntaxKind.Identifier) {\n return node.getText() === \"undefined\" ? standardOrScoped(\"constantLanguage\", isScoped) : undefined;\n }\n\n if (kind === SyntaxKind.StringLiteral || isTemplatePart(typescript, kind)) {\n return { type: SemanticTokenTypes.string, modifiers: [] };\n }\n\n if (kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.BigIntLiteral) {\n return { type: SemanticTokenTypes.number, modifiers: [] };\n }\n\n if (kind === SyntaxKind.RegularExpressionLiteral) {\n return { type: SemanticTokenTypes.regexp, modifiers: [] };\n }\n\n if (kind >= SyntaxKind.FirstPunctuation && kind <= SyntaxKind.LastPunctuation) {\n const isOperator = OPERATORS.has(typescript.tokenToString(kind) ?? \"\");\n\n return isOperator ? { type: SemanticTokenTypes.operator, modifiers: [] } : undefined;\n }\n\n if (kind < SyntaxKind.FirstKeyword || kind > SyntaxKind.LastKeyword) {\n return undefined;\n }\n\n // `void 0` is an operator, `: void` a type\n if (kind === SyntaxKind.VoidKeyword) {\n const isOperator = node.parent.kind === SyntaxKind.VoidExpression;\n\n return standardOrScoped(isOperator ? \"keywordOperatorExpression\" : \"supportTypePrimitive\", isScoped);\n }\n\n return standardOrScoped(keywords[kind] ?? \"keywordControl\", isScoped);\n}\n\n/** Whether a kind is one of the pieces a template literal is tokenized into. */\nfunction isTemplatePart(typescript: typeof ts, kind: ts.SyntaxKind): boolean {\n const { SyntaxKind } = typescript;\n\n return (\n kind === SyntaxKind.NoSubstitutionTemplateLiteral ||\n kind === SyntaxKind.TemplateHead ||\n kind === SyntaxKind.TemplateMiddle ||\n kind === SyntaxKind.TemplateTail\n );\n}\n\n/** A scoped type itself, or the standard type it stands for. */\nfunction standardOrScoped(type: ScopedType, isScoped: boolean): StandardType {\n if (isScoped) {\n return { type, modifiers: [] };\n }\n\n return SCOPED_TYPES[type];\n}\n\n/** A token per line of a stretch of the document, since a token may not span lines. */\nfunction splitLines(document: TextDocument, start: number, end: number, named: StandardType): SyntaxToken[] {\n const tokens: SyntaxToken[] = [];\n const first = document.positionAt(start);\n const last = document.positionAt(end);\n\n for (let line = first.line; line <= last.line; line++) {\n const character = line === first.line ? first.character : 0;\n const lineEnd =\n line === last.line\n ? last.character\n : document.offsetAt({ line: line + 1, character: 0 }) - document.offsetAt({ line, character: 0 });\n const length = lineEnd - character;\n\n if (length > 0) {\n tokens.push({ line, character, length, ...named });\n }\n }\n\n return tokens;\n}\n","import { SemanticTokenModifiers, SemanticTokenTypes } from \"vscode-languageserver-protocol\";\nimport { URI } from \"vscode-uri\";\n\nimport { isInRegions } from \"../helpers/regions.ts\";\nimport { createSyntaxTokenizer, SCOPED_TYPES } from \"../helpers/syntax-tokens.ts\";\nimport { embeddedFileName, StaticboltCode } from \"../virtual-code.ts\";\n\nimport type { LanguageRegions } from \"../helpers/regions.ts\";\nimport type { LanguageServiceContext, LanguageServicePlugin, SemanticToken } from \"@volar/language-service\";\nimport type * as ts from \"typescript\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\n\n/** What the TypeScript service shares with the other services. */\ninterface TypeScriptProvide {\n /** The language service over the project's files, the embedded codes among them. */\n \"typescript/languageService\": () => ts.LanguageService;\n}\n\n/** An embedded document with the plugin language it belongs to, as TypeScript knows the document. */\ninterface Embedded {\n /** The plugin language, with its regions and holes. */\n group: LanguageRegions;\n\n /** The embedded document as a TypeScript file. */\n fileName: string;\n}\n\n/**\n * Colours what TypeScript's own tokens leave out of the embedded code the editor's grammar cannot see, a placeholder say:\n * keywords, literals, operators and comments, named from TypeScript's parse of it. TypeScript names the identifiers; together\n * they colour the code the way a TypeScript file is coloured. The languages the editor colours itself get only the identifiers.\n * With `isScoped`, the keywords are sent as the scoped types an editor maps to grammar scopes, see `SCOPED_TYPES`; otherwise as\n * the nearest standard types.\n */\nexport function createSyntaxTokensService(typescript: typeof ts, isScoped: boolean): LanguageServicePlugin {\n const tokenize = createSyntaxTokenizer(typescript, isScoped);\n\n return {\n name: \"staticbolt-syntax-tokens\",\n\n capabilities: {\n semanticTokensProvider: {\n legend: {\n tokenTypes: [...Object.values(SemanticTokenTypes), ...Object.keys(SCOPED_TYPES)],\n tokenModifiers: Object.values(SemanticTokenModifiers),\n },\n },\n },\n\n create(context) {\n /**\n * The parsed file of an embedded document as the project's TypeScript holds it, with the checker that knows its symbols, or\n * a fresh parse alone when the project has none.\n */\n function parse(document: TextDocument, fileName: string): [ts.SourceFile, ts.TypeChecker | undefined] {\n const program = context.inject<TypeScriptProvide>(\"typescript/languageService\")?.getProgram();\n const parsed = program?.getSourceFile(fileName);\n if (program && parsed && parsed.text === document.getText()) {\n return [parsed, program.getTypeChecker()];\n }\n\n return [typescript.createSourceFile(fileName, document.getText(), typescript.ScriptTarget.Latest, true), undefined];\n }\n\n return {\n provideDocumentSemanticTokens(document, _range, legend) {\n if (document.languageId !== \"typescript\") {\n return;\n }\n\n const embedded = embeddedOf(context, document);\n if (!embedded || embedded.group.language.isColouredByEditor) {\n return;\n }\n\n const [sourceFile, checker] = parse(document, embedded.fileName);\n const tokens: SemanticToken[] = [];\n\n for (const token of tokenize(sourceFile, document, checker)) {\n const type = legend.tokenTypes.indexOf(token.type);\n if (type === -1) continue;\n\n // The masks standing in for other languages' code are not code to colour\n const offset = document.offsetAt({ line: token.line, character: token.character });\n if (isInRegions(embedded.group.holes, offset)) continue;\n\n let modifiers = 0;\n\n for (const modifier of token.modifiers) {\n const bit = legend.tokenModifiers.indexOf(modifier);\n if (bit === -1) continue;\n\n modifiers |= 1 << bit;\n }\n\n tokens.push([token.line, token.character, token.length, type, modifiers]);\n }\n\n return tokens;\n },\n };\n },\n };\n}\n\n/** The plugin language an embedded document belongs to and its TypeScript file name, as `getExtraServiceScripts` names it. */\nfunction embeddedOf(context: LanguageServiceContext, document: TextDocument): Embedded | undefined {\n const decoded = context.decodeEmbeddedDocumentUri(URI.parse(document.uri));\n if (!decoded) {\n return undefined;\n }\n\n const [sourceUri, codeId] = decoded;\n const root = context.language.scripts.get(sourceUri)?.generated?.root;\n if (!(root instanceof StaticboltCode)) {\n return undefined;\n }\n\n const group = root.languages.find(candidate => candidate.id === codeId);\n if (!group) {\n return undefined;\n }\n\n const documentFileName = context.project.typescript?.uriConverter.asFileName(sourceUri) ?? sourceUri.fsPath;\n\n return { group, fileName: embeddedFileName(documentFileName, codeId) };\n}\n","import { create } from \"volar-service-typescript\";\n\nimport type { LanguageServicePlugin } from \"@volar/language-service\";\nimport type * as ts from \"typescript\";\n\n/**\n * TypeScript's features for the embedded codes, through Volar's TypeScript service: completion, hover, diagnostics, semantic\n * tokens, definitions, references, rename, folding and the rest. Formatting is left out: a document is HTML to the editor, and\n * its own formatter takes care of the whole of it.\n */\nexport function createTypeScriptServices(typescript: typeof ts): LanguageServicePlugin[] {\n return create(typescript).map(plugin => ({\n ...plugin,\n capabilities: {\n ...plugin.capabilities,\n documentFormattingProvider: undefined,\n documentOnTypeFormattingProvider: undefined,\n },\n }));\n}\n","import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { format, stripVTControlCharacters } from \"node:util\";\nimport { createConnection, createServer, createTypeScriptProject, loadTsdkByPath } from \"@volar/language-server/node.js\";\n\nimport { useModuleScripts } from \"./helpers/inferred-project.ts\";\nimport { createLanguagePlugin } from \"./language-plugin.ts\";\nimport { findProjectRoots, Projects } from \"./projects.ts\";\nimport { createStaticboltService } from \"./services/staticbolt-service.ts\";\nimport { createSyntaxTokensService } from \"./services/syntax-tokens-service.ts\";\nimport { createTypeScriptServices } from \"./services/typescript-service.ts\";\n\nimport type { InitializeParams, WorkspaceFolder } from \"@volar/language-server/node.js\";\n\n/** What an editor may pass as `initializationOptions`. */\ninterface InitializationOptions {\n /** Where TypeScript is. */\n typescript?: {\n /** The directory holding `typescript.js`, `node_modules/typescript/lib` say. */\n tsdk?: string;\n };\n\n /** Whether the editor maps the scoped token types to grammar scopes, so the keywords are sent as those. */\n scopedTokens?: boolean;\n}\n\n/** The initialization options, with anything that is not an object read as none. */\nfunction initializationOptionsOf(parameters: InitializeParams): InitializationOptions {\n const options: unknown = parameters.initializationOptions;\n\n if (typeof options !== \"object\" || options === null) {\n return {};\n }\n\n return options;\n}\n\n/** The LSP connection to the editor, over stdio. */\nconst connection = createConnection();\n\n/** Volar's server on top of the connection: documents, projects and the language features. */\nconst server = createServer(connection);\n\n/**\n * `RemoteConsole` takes a single string, but the shared logger calls `console` with several arguments and colours them with\n * chalk. Passing the methods straight through drops everything after the first argument, and the output panel renders no ANSI —\n * so format the arguments the way `console` would, then strip the escapes.\n */\nfunction forward(write: (message: string) => void) {\n return (...messages: unknown[]) => {\n const text = stripVTControlCharacters(format(...messages));\n\n write(text);\n };\n}\n\nconsole.log = forward(connection.console.log.bind(connection.console));\nconsole.info = forward(connection.console.info.bind(connection.console));\nconsole.warn = forward(connection.console.warn.bind(connection.console));\nconsole.error = forward(connection.console.error.bind(connection.console));\n\nprocess.on(\"unhandledRejection\", (error: unknown) => {\n console.error(\"[staticbolt] unhandled rejection:\", error);\n});\n\n/** Whether a directory holds TypeScript with its API, which the native builds do not ship. */\nfunction hasTypeScriptApi(tsdk: string): boolean {\n return existsSync(path.join(tsdk, \"typescript.js\"));\n}\n\n/**\n * The directory of the TypeScript to run: the editor's choice from the initialization options, the `--tsdk` argument, or the\n * nearest `typescript` package with an API installed above the working directory.\n */\nfunction findTsdk(parameters: InitializeParams): string | undefined {\n const options = initializationOptionsOf(parameters);\n if (options.typescript?.tsdk) {\n return options.typescript.tsdk;\n }\n\n const argument = process.argv.find(value => value.startsWith(\"--tsdk=\"));\n if (argument) {\n return argument.slice(\"--tsdk=\".length);\n }\n\n let directory = process.cwd();\n\n while (true) {\n const tsdk = path.join(directory, \"node_modules\", \"typescript\", \"lib\");\n if (hasTypeScriptApi(tsdk)) {\n return tsdk;\n }\n\n const parent = path.dirname(directory);\n if (parent === directory) {\n return undefined;\n }\n\n directory = parent;\n }\n}\n\n/** The workspace folders, or the root uri of an editor that has no folders. */\nfunction foldersOf(parameters: InitializeParams): WorkspaceFolder[] {\n if (parameters.workspaceFolders) {\n return parameters.workspaceFolders;\n }\n\n if (parameters.rootUri) {\n return [{ name: \"\", uri: parameters.rootUri }];\n }\n\n return [];\n}\n\n/** The staticbolt projects of the workspace, from initialization on. */\nlet projects: Projects | undefined;\n\nconnection.listen();\n\nconnection.onInitialize(async parameters => {\n const tsdk = findTsdk(parameters);\n if (tsdk === undefined || !hasTypeScriptApi(tsdk)) {\n throw new Error(\n `[staticbolt] no TypeScript with an API ${tsdk ? `at ${tsdk}` : \"found\"}; point typescript.tsdk or --tsdk at one, 6.x say`\n );\n }\n\n const { typescript, diagnosticMessages } = loadTsdkByPath(tsdk, parameters.locale);\n console.log(`[staticbolt] TypeScript ${typescript.version} from ${tsdk}`);\n\n // Every project's plugins are asked what they contribute before the first document is served\n let isInitialized = false;\n const workspace = new Projects(connection.console, () => {\n if (!isInitialized) return;\n\n server.project.reload();\n });\n\n projects = workspace;\n\n const roots = findProjectRoots(foldersOf(parameters));\n console.log(`[staticbolt] discovered projects:\\n${roots.map(root => ` - ${root}`).join(\"\\n\")}`);\n await Promise.all(roots.map(root => workspace.get(root)));\n isInitialized = true;\n\n const languagePlugin = createLanguagePlugin(typescript, workspace);\n const project = createTypeScriptProject(typescript, diagnosticMessages, ({ configFileName, projectHost }) => {\n if (configFileName === undefined) {\n useModuleScripts(typescript, projectHost);\n }\n\n return { languagePlugins: [languagePlugin] };\n });\n const services = [\n createStaticboltService(),\n createSyntaxTokensService(typescript, initializationOptionsOf(parameters).scopedTokens === true),\n ...createTypeScriptServices(typescript),\n ];\n\n return server.initialize(parameters, project, services);\n});\n\n/** A TypeScript config, or one of those a config extends. */\nconst TS_CONFIG = /\\/(?:tsconfig|jsconfig)[^/]*\\.json$/;\n\nconnection.onInitialized(() => {\n server.initialized();\n\n // The editor reports the changes; Volar follows the source files itself, a config is reloaded whole since it may be extended\n void server.fileWatcher.watchFiles([\"**/*.{ts,mts,cts,js,mjs,cjs}\", \"**/{tsconfig,jsconfig}*.json\"]);\n\n server.fileWatcher.onDidChangeWatchedFiles(({ changes }) => {\n if (changes.every(change => !TS_CONFIG.test(change.uri))) return;\n\n server.project.reload();\n });\n});\nconnection.onShutdown(async () => {\n await projects?.dispose();\n server.shutdown();\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["#htmlDocument","#info","#console","#onLoad","#watcher","#scheduleReload","#load","#reload","#collectPluginData","#starting","#loaded","#roots","#start"],"sources":["../src/helpers/inferred-project.ts","../src/helpers/document-elements.ts","../src/helpers/document-info.ts","../src/helpers/markdown-regions.ts","../src/helpers/regions.ts","../src/helpers/virtual-document.ts","../src/virtual-code.ts","../src/language-plugin.ts","../src/projects.ts","../src/helpers/merge-html-data.ts","../src/helpers/validation.ts","../src/helpers/document-context.ts","../src/services/staticbolt-service.ts","../src/helpers/syntax-tokens.ts","../src/services/syntax-tokens-service.ts","../src/services/typescript-service.ts","../src/index.ts"],"sourcesContent":["import type { TypeScriptProjectHost } from \"@volar/typescript\";\nimport type * as ts from \"typescript\";\n\n/**\n * The module kind of the project a document outside every config file is served by.\n *\n * TypeScript takes such a document into a project of its own and gives that project CommonJS modules, which a page's scripts are\n * not: `import.meta` alone is an error there. `preserve` leaves the imports as they are written, which is what a browser gets.\n *\n * The settings are only read once the project has its config, so they are taken as they are asked for, and the same object is\n * given back until they change.\n */\nexport function useModuleScripts(typescript: typeof ts, projectHost: TypeScriptProjectHost): void {\n const compilationSettings = projectHost.getCompilationSettings.bind(projectHost);\n let read: ts.CompilerOptions | undefined;\n let settings: ts.CompilerOptions | undefined;\n\n projectHost.getCompilationSettings = () => {\n const current = compilationSettings();\n\n if (current !== read) {\n read = current;\n settings = { ...current, module: typescript.ModuleKind.Preserve ?? typescript.ModuleKind.ESNext };\n }\n\n return settings!;\n };\n}\n","import vscodeHtml from \"vscode-html-languageservice\";\n\nimport type { AttributeInfo, ElementInfo, TextRange } from \"@staticbolt/core\";\nimport type { HTMLDocument, LanguageService, Node } from \"vscode-html-languageservice\";\n\n/** The token kinds the HTML scanner reports. */\nconst TokenType = vscodeHtml.TokenType;\n\n/**\n * Every element of a parsed document in document order, with the attribute offsets the parser leaves out: each start tag is\n * scanned again for them.\n */\nexport function parseElements(languageService: LanguageService, text: string, htmlDocument: HTMLDocument): ElementInfo[] {\n const elements: ElementInfo[] = [];\n\n for (const root of htmlDocument.roots) {\n collectElements(languageService, text, root, undefined, elements);\n }\n\n return elements;\n}\n\n/** The element for a node and, after it, the ones for its children; the tree only ever holds elements. */\nfunction collectElements(\n languageService: LanguageService,\n text: string,\n node: Node,\n parent: ElementInfo | undefined,\n elements: ElementInfo[]\n): void {\n const element = createElement(languageService, text, node, parent);\n\n elements.push(element);\n parent?.children.push(element);\n\n for (const child of node.children) {\n collectElements(languageService, text, child, element, elements);\n }\n}\n\n/** The element for a node, with its attributes scanned from its start tag. */\nfunction createElement(languageService: LanguageService, text: string, node: Node, parent: ElementInfo | undefined): ElementInfo {\n const tag = node.tag ?? \"\";\n const startTagEnd = node.startTagEnd ?? node.end;\n const attributes: AttributeInfo[] = [];\n\n const findAttribute = (name: string) => {\n const wanted = name.toLowerCase();\n\n return attributes.find(attribute => attribute.name.toLowerCase() === wanted);\n };\n\n const element: ElementInfo = {\n name: tag.toLowerCase(),\n attributes,\n parent,\n children: [],\n range: { start: node.start, end: node.end },\n nameRange: { start: node.start + 1, end: node.start + 1 + tag.length },\n contentRange: contentRangeOf(node, startTagEnd),\n attribute: findAttribute,\n has: name => findAttribute(name) !== undefined,\n };\n\n attributes.push(...scanAttributes(languageService, text, element, node.start, startTagEnd));\n\n return element;\n}\n\n/**\n * From the end of the start tag to the end tag, or to wherever the parser closed an element missing its end tag; nothing for an\n * element that is over with its start tag, void or self-closing.\n */\nfunction contentRangeOf(node: Node, startTagEnd: number): TextRange | undefined {\n const end = node.endTagStart ?? node.end;\n\n if (end <= startTagEnd) {\n return undefined;\n }\n\n return { start: startTagEnd, end };\n}\n\n/** The attributes in a start tag, with where their names and values sit. */\nfunction scanAttributes(\n languageService: LanguageService,\n text: string,\n element: ElementInfo,\n start: number,\n end: number\n): AttributeInfo[] {\n const attributes: AttributeInfo[] = [];\n const scanner = languageService.createScanner(text.slice(start, end));\n let pending: AttributeInfo | undefined;\n\n for (let token = scanner.scan(); token !== TokenType.EOS; token = scanner.scan()) {\n const range: TextRange = { start: start + scanner.getTokenOffset(), end: start + scanner.getTokenEnd() };\n\n if (token === TokenType.AttributeName) {\n pending = { name: scanner.getTokenText(), value: undefined, element, nameRange: range, valueRange: undefined };\n attributes.push(pending);\n continue;\n }\n\n if (token !== TokenType.AttributeValue || !pending) {\n continue;\n }\n\n const raw = scanner.getTokenText();\n const isQuoted = raw.startsWith('\"') || raw.startsWith(\"'\");\n const quote = isQuoted ? 1 : 0;\n\n pending.value = raw.slice(quote, raw.length - quote);\n pending.valueRange = { start: range.start + quote, end: range.end - quote };\n pending = undefined;\n }\n\n return attributes;\n}\n","import type { DocumentInfo, ElementInfo, Resolver } from \"@staticbolt/core\";\n\n/** The document as a plugin sees it: its text and elements, with lookups over them and the project's resolver. */\nexport function describeDocument(text: string, file: string, elements: ElementInfo[], resolver: Resolver): DocumentInfo {\n return {\n file,\n text,\n elements,\n\n select(...names) {\n const wanted = new Set(names.map(name => name.toLowerCase()));\n\n return elements.filter(element => wanted.has(element.name));\n },\n\n textOf(range) {\n return text.slice(range.start, range.end);\n },\n\n resolve(source) {\n const resolved = resolver.resolve(source, file);\n if (!resolved) {\n return;\n }\n\n return { path: resolved.path, exists: resolved.exists };\n },\n };\n}\n","/**\n * Markdown allows raw HTML anywhere, so a \".md\" file is served as HTML with the parts that can never be HTML — front matter, code\n * blocks and code spans — blanked out first. Blanking keeps every offset, so positions still point at the same place in the\n * file.\n */\nimport { markdownToMdast } from \"satteri\";\n\nimport type { TextRange } from \"@staticbolt/core\";\nimport type { MdastNode } from \"satteri\";\n\n/** None of these nest, so the regions they produce never overlap. */\nconst NON_HTML_NODES = new Set([\"code\", \"inlineCode\", \"yaml\", \"toml\"]);\n\n/** The regions of a markdown document that must not be treated as HTML, sorted by start offset. */\nexport function findMarkdownNonHtmlRegions(text: string): TextRange[] {\n let tree: MdastNode;\n\n try {\n tree = markdownToMdast(text);\n } catch (error) {\n console.warn(\"[staticbolt] could not parse markdown, its whole content is handled as HTML:\", error);\n return [];\n }\n\n const regions: TextRange[] = [];\n const pending: MdastNode[] = [tree];\n\n while (pending.length > 0) {\n const node = pending.pop()!;\n\n // The nodes looked for are leaves, so a node with children is never one of them\n if (\"children\" in node) {\n pending.push(...node.children);\n continue;\n }\n\n if (!NON_HTML_NODES.has(node.type)) continue;\n\n const start = node.position?.start.offset;\n const end = node.position?.end.offset;\n\n if (start === undefined || end === undefined) continue;\n\n regions.push({ start, end });\n }\n\n const sorted = regions.toSorted((a, b) => a.start - b.start);\n\n return toUtf16Offsets(text, sorted);\n}\n\n/**\n * The parser counts code points, the editor counts UTF-16 code units. The two only drift apart once a character outside the basic\n * plane, an emoji most of the time, sits before a region.\n */\nfunction toUtf16Offsets(text: string, regions: TextRange[]): TextRange[] {\n const hasAstral = /[\\uD800-\\uDBFF]/.test(text);\n\n if (!hasAstral) {\n return regions;\n }\n\n const astral: number[] = [];\n let codePoint = 0;\n\n for (const character of text) {\n if (character.length === 2) {\n astral.push(codePoint);\n }\n\n codePoint++;\n }\n\n /** Shifts a code point offset by the number of astral characters before it. */\n function toUtf16(offset: number): number {\n const astralBefore = astral.filter(position => position < offset).length;\n\n return offset + astralBefore;\n }\n\n return regions.map(region => ({ start: toUtf16(region.start), end: toUtf16(region.end) }));\n}\n","import type { DocumentInfo, EmbeddedLanguage, EmbeddedRegion, TextRange } from \"@staticbolt/core\";\n\n/** A region of a plugin language in a document. */\nexport interface PluginRegion extends EmbeddedRegion {\n /** The plugin language. */\n language: EmbeddedLanguage;\n}\n\n/** The regions of one plugin language that share a file: the classic ones together, a module on its own. */\nexport interface LanguageRegions {\n /** The file's id: the language's name, with the module's number for a module. */\n id: string;\n\n /** The plugin language. */\n language: EmbeddedLanguage;\n\n /** Whether the file is a module of one region, with a top level of its own, rather than a script whose top level is global. */\n isModule: boolean;\n\n /** Its regions, in text order. */\n regions: EmbeddedRegion[];\n\n /** The regions of other plugin languages inside its own, in text order: those languages serve them, this one sees them masked. */\n holes: TextRange[];\n}\n\n/** Regions in text order. */\nconst byStart = (a: TextRange, b: TextRange) => a.start - b.start;\n\n/**\n * The regions of every plugin language that claims the document, in text order. A region inside one a language earlier in the\n * config claimed already is left to that language: a plugin that runs a script elsewhere claims it before the core plugin, last\n * in the config, sees the browser in it. A region of another language inside this one is a hole, not a claim.\n */\nexport function findPluginRegions(document: DocumentInfo, languages: readonly EmbeddedLanguage[]): PluginRegion[] {\n const regions: PluginRegion[] = [];\n\n for (const language of languages) {\n if (!language.filter(document.file)) continue;\n\n for (const region of language.findRegions(document)) {\n if (regions.some(claimed => isInside(region, claimed))) continue;\n\n regions.push({ ...region, language });\n }\n }\n\n return regions.toSorted(byStart);\n}\n\n/** Whether a range lies within another. */\nfunction isInside(range: TextRange, outer: TextRange): boolean {\n return outer.start <= range.start && range.end <= outer.end;\n}\n\n/**\n * The plugin regions by the file they share: the classic regions of a language together, each module on its own. Every group is\n * in text order, with the regions of other languages inside its own as its holes: a placeholder inside a build-time script is the\n * placeholder language's.\n */\nexport function groupByFile(regions: readonly PluginRegion[]): LanguageRegions[] {\n const groups = new Map<string, LanguageRegions>();\n let modules = 0;\n\n for (const region of regions) {\n const id = region.isModule ? `${region.language.name}.module${modules++}` : region.language.name;\n const group = groups.get(id) ?? { id, language: region.language, isModule: region.isModule === true, regions: [], holes: [] };\n\n group.regions.push({ start: region.start, end: region.end, isExpression: region.isExpression });\n group.holes.push(...holesOf(region, regions));\n groups.set(id, group);\n }\n\n return groups.values().toArray();\n}\n\n/** The whole construct a region sits in, delimiters included, or the region itself when it has no more. */\nfunction extentOf(region: EmbeddedRegion): TextRange {\n return region.extent ?? { start: region.start, end: region.end };\n}\n\n/** Whether an offset falls in any of the regions, their ends included. */\nexport function isInRegions(regions: readonly TextRange[], offset: number): boolean {\n return regions.some(region => region.start <= offset && offset <= region.end);\n}\n\n/** The regions of other languages lying inside a region, as their whole constructs. */\nfunction holesOf(region: PluginRegion, regions: readonly PluginRegion[]): TextRange[] {\n const holes: TextRange[] = [];\n\n for (const other of regions) {\n if (other.language === region.language) continue;\n\n const hole = extentOf(other);\n if (hole.start < region.start || hole.end > region.end) continue;\n\n holes.push(hole);\n }\n\n return holes;\n}\n","import type { EmbeddedRegion, TextRange } from \"@staticbolt/core\";\nimport type * as ts from \"typescript\";\n\n/** The code of a document's regions, with where everything landed in it. */\nexport interface RegionsCode {\n /** The code itself. */\n text: string;\n\n /** Where each region's own text starts in it, in the order the regions came in. */\n starts: number[];\n\n /** The holes, as offsets into it. */\n holes: TextRange[];\n}\n\n/**\n * The regions' text alone, one region per line, so an edit outside them leaves the code untouched. Every line opens with a `;`,\n * which keeps the line a statement of its own whatever the line before it ends with; an expression region is wrapped as `;(…)`,\n * so an object literal reads as one and not as a block.\n */\nexport function codeFromRegions(text: string, regions: readonly EmbeddedRegion[], holes: readonly TextRange[]): RegionsCode {\n const lines: string[] = [];\n const starts: number[] = [];\n const moved: TextRange[] = [];\n let cursor = 0;\n\n for (const region of regions) {\n const open = region.isExpression ? \";(\" : \";\";\n const start = cursor + open.length;\n const line = open + text.slice(region.start, region.end) + (region.isExpression ? \")\" : \"\");\n\n starts.push(start);\n lines.push(line);\n cursor += line.length + 1;\n\n for (const hole of holes) {\n if (hole.start < region.start || hole.end > region.end) continue;\n\n moved.push({ start: hole.start - region.start + start, end: hole.end - region.start + start });\n }\n }\n\n return { text: lines.join(\"\\n\"), starts, holes: moved };\n}\n\n/** The text with the regions blanked, keeping line breaks, so every offset means the same thing as in the text. */\nexport function blankRegions(text: string, regions: readonly TextRange[]): string {\n let result = \"\";\n let cursor = 0;\n\n for (const region of regions) {\n result += text.slice(cursor, region.start) + blank(text.slice(region.start, region.end));\n cursor = region.end;\n }\n\n return result + text.slice(cursor);\n}\n\n/**\n * The holes masked, keeping line breaks, so the code around them still parses and types as it will once they are filled: a hole\n * in a string literal makes the whole literal `(\"\" + \"\")`, a `string` rather than a literal type; a hole in a template becomes a\n * `${<any>0}` substitution, for the same reason; any other hole reads as `(<any>0)`, a value of a type nobody knows yet.\n *\n * A mask stands where an expression stands, so it is parenthesised: whatever surrounds it, `+\"{{ n }}\"` say, binds to the mask as\n * a whole and not to a part of it. A hole too short for its mask gets the longest shorter one that fits, down to nothing.\n */\nexport function mask(typescript: typeof ts, text: string, holes: readonly TextRange[]): string {\n if (holes.length === 0) {\n return text;\n }\n\n const sourceFile = typescript.createSourceFile(\"mask.ts\", text, typescript.ScriptTarget.Latest, true);\n let result = text;\n\n for (const hole of holes) {\n const token = tokenAt(sourceFile, hole.start);\n\n if (token?.kind === typescript.SyntaxKind.StringLiteral) {\n const range = { start: token.getStart(sourceFile), end: token.getEnd() };\n\n result = replace(result, range, fill(text.slice(range.start, range.end), '(\"\" + \"\")', '(\"\"+\"\")', '(\"\")', '\"\"'));\n continue;\n }\n\n if (token && typescript.isTemplateLiteralToken(token)) {\n result = replace(result, hole, fill(text.slice(hole.start, hole.end), \"${<any>0}\", \"${0}\"));\n continue;\n }\n\n result = replace(result, hole, fill(text.slice(hole.start, hole.end), \"(<any>0)\", \"<any>0\", \"(0)\", \"0\"));\n }\n\n return result;\n}\n\n/**\n * The first replacement the original has room for, followed by the rest of the original blanked, so the length and the line\n * breaks are kept; nothing but the blanks when even the shortest is too long.\n */\nfunction fill(original: string, ...replacements: readonly string[]): string {\n const fitting = replacements.find(replacement => replacement.length <= original.length) ?? \"\";\n\n return fitting + blank(original.slice(fitting.length));\n}\n\n/** The token of a parsed text an offset falls in: the deepest node there that has no children. */\nfunction tokenAt(sourceFile: ts.SourceFile, offset: number): ts.Node | undefined {\n let node: ts.Node = sourceFile;\n\n while (true) {\n const child = node\n .getChildren(sourceFile)\n .find(candidate => candidate.getStart(sourceFile) <= offset && offset < candidate.getEnd());\n if (!child) {\n return node === sourceFile ? undefined : node;\n }\n\n node = child;\n }\n}\n\n/** The text with a range replaced by a replacement of the same length. */\nfunction replace(text: string, range: TextRange, replacement: string): string {\n return text.slice(0, range.start) + replacement + text.slice(range.end);\n}\n\n/** Every character but the line breaks replaced by a space. */\nfunction blank(text: string): string {\n return text.replaceAll(/[^\\n\\r]/g, \" \");\n}\n","import path from \"node:path\";\nimport { Resolver } from \"@staticbolt/core\";\nimport vscodeHtml from \"vscode-html-languageservice\";\nimport { TextDocument } from \"vscode-languageserver-textdocument\";\n\nimport { parseElements } from \"./helpers/document-elements.ts\";\nimport { describeDocument } from \"./helpers/document-info.ts\";\nimport { findMarkdownNonHtmlRegions } from \"./helpers/markdown-regions.ts\";\nimport { findPluginRegions, groupByFile } from \"./helpers/regions.ts\";\nimport { blankRegions, codeFromRegions, mask } from \"./helpers/virtual-document.ts\";\n\nimport type { LanguageRegions, PluginRegion } from \"./helpers/regions.ts\";\nimport type { Project } from \"./projects.ts\";\nimport type { CodeMapping, IScriptSnapshot, VirtualCode } from \"@volar/language-core\";\nimport type { DocumentInfo, EmbeddedLanguage, EmbeddedRegion, TextRange } from \"@staticbolt/core\";\nimport type * as ts from \"typescript\";\nimport type { HTMLDocument, LanguageService } from \"vscode-html-languageservice\";\nimport type { URI } from \"vscode-uri\";\n\n/** The id of the root code, and of the HTML copy a markdown document is served through. */\nconst ROOT_ID = \"root\";\n\n/** The id of the embedded code holding the HTML of a document that is not HTML itself. */\nconst HTML_ID = \"html\";\n\n/** The TypeScript file name an embedded code of a document is served under. */\nexport function embeddedFileName(documentFileName: string, codeId: string): string {\n return `${documentFileName}.${codeId}.ts`;\n}\n\n/** What every feature is allowed to do on a mapped stretch of code. */\nconst ALL_FEATURES: CodeMapping[\"data\"] = {\n verification: true,\n completion: true,\n semantic: true,\n navigation: true,\n structure: true,\n format: false,\n};\n\n/** The HTML language service the codes parse with; no data provider, only the tree is wanted here. */\nconst htmlLanguageService: LanguageService = vscodeHtml.getLanguageService({ useDefaultDataProvider: false });\n\n/** The TypeScript file one plugin language's regions are served through. */\nexport interface TypeScriptCode extends VirtualCode {\n /** The plugin language whose regions it holds. */\n language: EmbeddedLanguage;\n\n /** The regions of other plugin languages inside its own, as offsets into its text. */\n holes: TextRange[];\n\n /** Its text, to tell a rebuild that changed nothing from one that did. */\n text: string;\n}\n\n/**\n * A document as the server sees it: the HTML (a markdown document's with everything that cannot be HTML blanked out), the regions\n * plugins embed in it, and an embedded TypeScript code per plugin language, served through the project's TypeScript.\n */\nexport class StaticboltCode implements VirtualCode {\n /** The root is the document. */\n readonly id = ROOT_ID;\n\n /** `html` or `markdown`. */\n readonly languageId: string;\n\n /** The document's text. */\n readonly snapshot: IScriptSnapshot;\n\n /** The whole document maps onto itself. */\n readonly mappings: CodeMapping[];\n\n /** The HTML copy of a markdown document, then the TypeScript code of every plugin language with regions. */\n readonly embeddedCodes: VirtualCode[];\n\n /** The document's uri. */\n readonly uri: URI;\n\n /** The project the document belongs to, or nothing when it is outside every loaded one. */\n readonly project: Project | undefined;\n\n /** The document's path relative to its project, or its whole path outside one. */\n readonly file: string;\n\n /** The document as HTML: the text itself, or for markdown the blanked copy. */\n readonly html: string;\n\n /** The regions the plugins embed, in text order. */\n readonly regions: PluginRegion[];\n\n /** The TypeScript file of every plugin language with regions. */\n readonly codes: TypeScriptCode[];\n\n /** The parsed HTML, on first use. */\n #htmlDocument: HTMLDocument | undefined;\n\n /** The document as the plugins see it, on first use. */\n #info: DocumentInfo | undefined;\n\n constructor(\n typescript: typeof ts,\n uri: URI,\n languageId: string,\n snapshot: IScriptSnapshot,\n project: Project | undefined,\n previous?: StaticboltCode\n ) {\n const text = snapshot.getText(0, snapshot.getLength());\n\n this.uri = uri;\n this.languageId = languageId;\n this.snapshot = snapshot;\n this.project = project;\n this.file = project ? path.relative(project.root, uri.fsPath) : uri.fsPath;\n this.mappings = [identityMapping(text.length)];\n this.html = languageId === \"markdown\" ? blankRegions(text, findMarkdownNonHtmlRegions(text)) : text;\n this.regions = project ? findPluginRegions(this.info, project.embeddedLanguages) : [];\n this.codes = groupByFile(this.regions).map(group => createTypeScriptCode(typescript, this.info, group, previous));\n this.embeddedCodes = languageId === \"markdown\" ? [createHtmlCode(typescript, this.html), ...this.codes] : [...this.codes];\n }\n\n /** The parsed HTML. */\n get htmlDocument(): HTMLDocument {\n this.#htmlDocument ??= htmlLanguageService.parseHTMLDocument(TextDocument.create(this.uri.toString(), \"html\", 0, this.html));\n\n return this.#htmlDocument;\n }\n\n /**\n * The document as the plugins see it: its elements with their attributes and where everything sits, and the project's resolver.\n * Outside a project, a resolver of the document's own directory.\n */\n get info(): DocumentInfo {\n this.#info ??= describeDocument(\n this.html,\n this.file,\n parseElements(htmlLanguageService, this.html, this.htmlDocument),\n this.project?.resolver ?? new Resolver(path.dirname(this.uri.fsPath), false, {}, false)\n );\n\n return this.#info;\n }\n}\n\n/** A mapping of a whole text onto itself. */\nfunction identityMapping(length: number): CodeMapping {\n return { sourceOffsets: [0], generatedOffsets: [0], lengths: [length], data: ALL_FEATURES };\n}\n\n/** A markdown document's HTML copy, at the same offsets. */\nfunction createHtmlCode(typescript: typeof ts, html: string): VirtualCode {\n return {\n id: HTML_ID,\n languageId: \"html\",\n snapshot: typescript.ScriptSnapshot.fromString(html),\n mappings: [identityMapping(html.length)],\n };\n}\n\n/**\n * The TypeScript code of one file of a plugin language: the file's regions one per line, the regions of other languages inside\n * them masked, and the language's prelude appended at the end. A module is made one with an `export {}`, so its top level is its\n * own; a script's top level is the global scope, as it is in the browser. The regions map back to the document; what comes before\n * the first and after the last maps onto their edges, so what TypeScript puts at the top or the bottom of the file, an import or\n * a declaration it adds say, lands in the document.\n *\n * Nothing but the regions is in the text, so an edit outside them leaves it as it was: the previous file's snapshot is then kept,\n * and Volar, which versions a file by the identity of its snapshot, hands TypeScript the program it already has.\n */\nfunction createTypeScriptCode(\n typescript: typeof ts,\n info: DocumentInfo,\n group: LanguageRegions,\n previous: StaticboltCode | undefined\n): TypeScriptCode {\n const { id, language, isModule, regions, holes } = group;\n const prelude = typeof language.prelude === \"function\" ? language.prelude(info) : language.prelude;\n const suffix = [isModule ? \"export {};\" : \"\", prelude ?? \"\"].filter(Boolean).join(\"\\n\");\n const code = codeFromRegions(info.text, regions, holes);\n const body = mask(typescript, code.text, code.holes);\n const text = `${body}\\n${suffix}\\n`;\n const first = regions[0];\n const last = regions.at(-1) ?? first;\n const before = previous?.codes.find(candidate => candidate.id === id);\n\n return {\n id,\n languageId: \"typescript\",\n language,\n holes: code.holes,\n text,\n snapshot: before?.text === text ? before.snapshot : typescript.ScriptSnapshot.fromString(text),\n mappings: [\n {\n sourceOffsets: regions.map(region => region.start),\n generatedOffsets: code.starts,\n lengths: regions.map(region => region.end - region.start),\n data: ALL_FEATURES,\n },\n edgeMapping(topOf(info.text, first), 0, code.starts[0]),\n edgeMapping(last.end, body.length, text.length - body.length),\n ],\n };\n}\n\n/**\n * A stretch of the generated text outside the regions mapped onto one spot of the document, for the edits of completions and code\n * actions only: nothing there is verified, coloured or folded.\n */\nfunction edgeMapping(sourceOffset: number, generatedOffset: number, generatedLength: number): CodeMapping {\n return {\n sourceOffsets: [sourceOffset],\n generatedOffsets: [generatedOffset],\n lengths: [0],\n generatedLengths: [generatedLength],\n data: { completion: true, navigation: true, verification: false, semantic: false, structure: false, format: false },\n };\n}\n\n/** Where the top of a file's code is in the document: the first region's start, past the line break a script body opens with. */\nfunction topOf(text: string, first: EmbeddedRegion): number {\n const lineBreak = /^\\r?\\n/.exec(text.slice(first.start, first.end));\n\n return first.start + (lineBreak?.[0].length ?? 0);\n}\n","import path from \"node:path\";\nimport { forEachEmbeddedCode } from \"@volar/language-core\";\n\nimport { embeddedFileName, StaticboltCode } from \"./virtual-code.ts\";\n\nimport type { Projects } from \"./projects.ts\";\nimport type { LanguagePlugin } from \"@volar/language-core\";\nimport type * as ts from \"typescript\";\nimport type { URI } from \"vscode-uri\";\n\n/** The language ids by file extension. */\nconst LANGUAGE_IDS: Record<string, string | undefined> = {\n \".html\": \"html\",\n \".md\": \"markdown\",\n};\n\n/**\n * Tells Volar what an HTML or markdown document is: the document itself, and a TypeScript file per plugin language, named after\n * the document and the language and served by the project's TypeScript next to it.\n */\nexport function createLanguagePlugin(typescript: typeof ts, projects: Projects): LanguagePlugin<URI, StaticboltCode> {\n return {\n getLanguageId(uri) {\n return LANGUAGE_IDS[path.extname(uri.path).toLowerCase()];\n },\n\n createVirtualCode(uri, languageId, snapshot) {\n if (uri.scheme !== \"file\") {\n return;\n }\n\n if (languageId !== \"html\" && languageId !== \"markdown\") {\n return;\n }\n\n return new StaticboltCode(typescript, uri, languageId, snapshot, projects.of(uri.toString()));\n },\n\n updateVirtualCode(uri, previous, snapshot) {\n return new StaticboltCode(typescript, uri, previous.languageId, snapshot, projects.of(uri.toString()), previous);\n },\n\n typescript: {\n extraFileExtensions: [\n { extension: \"html\", isMixedContent: true, scriptKind: typescript.ScriptKind.Deferred },\n { extension: \"md\", isMixedContent: true, scriptKind: typescript.ScriptKind.Deferred },\n ],\n\n getServiceScript() {\n return;\n },\n\n getExtraServiceScripts(fileName, root) {\n const scripts = [];\n\n for (const code of forEachEmbeddedCode(root)) {\n if (code.languageId !== \"typescript\") continue;\n\n scripts.push({\n fileName: embeddedFileName(fileName, code.id),\n code,\n extension: \".ts\",\n scriptKind: typescript.ScriptKind.TS,\n });\n }\n\n return scripts;\n },\n },\n };\n}\n","import { existsSync, globSync, watch } from \"node:fs\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { Resolver } from \"@staticbolt/core\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport type { FSWatcher } from \"node:fs\";\nimport type { AppConfig, EmbeddedLanguage, Plugin } from \"@staticbolt/core\";\nimport type { HTMLDataV1, IAttributeData, MarkupContent } from \"vscode-html-languageservice\";\nimport type { WorkspaceFolder } from \"vscode-languageserver-protocol\";\n\n/** Where the projects log. */\ntype RemoteConsole = Pick<Console, \"log\" | \"error\">;\n\n/** In order, so the first one that exists wins. */\nconst CONFIG_NAMES = [\".staticbolt.ts\", \".staticbolt.js\"];\n\n/** A save may come as several writes; the config is loaded once they have stopped. */\nconst RELOAD_DELAY_MS = 150;\n\n/** A plugin's document check, with the plugin's name to label what it reports. */\nexport interface Validator {\n /** The plugin's name. */\n name: string;\n\n /** The plugin's `lspValidate` hook. */\n validate: NonNullable<Plugin[\"lspValidate\"]>;\n\n /** Whether the hook threw already, so it is logged once; the next config load starts over. */\n hasFailed: boolean;\n}\n\n/** The directory of the nearest config file above a directory, which is the project it belongs to. */\nfunction findProjectRoot(directory: string): string | undefined {\n while (true) {\n const hasConfig = CONFIG_NAMES.some(name => existsSync(path.join(directory, name)));\n if (hasConfig) {\n return directory;\n }\n\n const parent = path.dirname(directory);\n if (parent === directory) return;\n\n directory = parent;\n }\n}\n\n/** A resolver for a root that does not log missing files, with the config's aliases on top of the tsconfig's. */\nfunction createResolver(root: string, aliases?: Record<string, string>): Resolver {\n return new Resolver(root, false, aliases, false);\n}\n\n/** Every project under the workspace folders, for the startup log. */\nexport function findProjectRoots(folders: WorkspaceFolder[]): string[] {\n return folders.flatMap(folder => {\n const cwd = vscodeUri.URI.parse(folder.uri).fsPath;\n const configs = globSync(`**/{${CONFIG_NAMES.join(\",\")}}`, { cwd, exclude: [\"**/node_modules/**\", \"**/.git/**\"] });\n\n return configs.map(config => path.join(cwd, path.dirname(config)));\n });\n}\n\n/**\n * A staticbolt project as the server sees it: its config, kept current while the config file changes, and what its plugins\n * contribute to the editor.\n */\nexport class Project {\n /** The directory holding the config file. */\n readonly root: string;\n\n /** The last config that loaded, or nothing when none did yet. */\n config: AppConfig | undefined;\n\n /** The tags and attributes the plugins contribute to HTML. A new array on every load, so consumers may cache on its identity. */\n htmlData: HTMLDataV1[] = [];\n\n /** The languages the plugins embed in HTML. */\n embeddedLanguages: EmbeddedLanguage[] = [];\n\n /** The plugins that check documents, by name. */\n validators: Validator[] = [];\n\n /** Resolves paths the way the project's build does: its tsconfig paths and config aliases. New on every load. */\n resolver: Resolver;\n\n /** Where to log. */\n readonly #console: RemoteConsole;\n\n /** Follows the root directory for saves of the config file. */\n #watcher: FSWatcher | undefined;\n\n /** The reload waiting for the save to finish. */\n #reload: NodeJS.Timeout | undefined;\n\n /** Told whenever the config loaded. */\n readonly #onLoad: () => void;\n\n /** Nothing is loaded until `start` is called. */\n constructor(root: string, console: RemoteConsole, onLoad: () => void) {\n this.root = root;\n this.resolver = createResolver(root);\n this.#console = console;\n this.#onLoad = onLoad;\n }\n\n /** Loads the config and starts following the config file. */\n async start(): Promise<void> {\n // The directory rather than the file: editors save through a temporary file and a rename, which a watch on the file misses\n this.#watcher = watch(this.root, { persistent: false }, (_event, filename) => {\n if (typeof filename !== \"string\") return;\n if (!CONFIG_NAMES.includes(filename)) return;\n this.#scheduleReload();\n });\n\n this.#watcher.on(\"error\", error => this.#console.error(`[staticbolt] watching ${this.root}: ${error.message}`));\n\n await this.#load();\n }\n\n /** Stops following the config file. */\n dispose(): void {\n clearTimeout(this.#reload);\n this.#watcher?.close();\n }\n\n /** Loads the config again once the save has stopped writing. */\n #scheduleReload(): void {\n clearTimeout(this.#reload);\n this.#reload = setTimeout(() => void this.#load(), RELOAD_DELAY_MS);\n }\n\n /** Loads the config file and takes what its plugins contribute; a failure is logged and leaves the last config in place. */\n async #load(): Promise<void> {\n const configPath = CONFIG_NAMES.map(name => path.join(this.root, name)).find(candidate => existsSync(candidate));\n\n if (!configPath) {\n this.#console.error(`[staticbolt] the config file of ${this.root} is gone`);\n return;\n }\n\n try {\n // A fresh URL each time, since a module is only ever evaluated once\n const module = (await import(`${pathToFileURL(configPath).href}?t=${Date.now()}`)) as { default?: AppConfig };\n if (!module.default) {\n throw new Error(\"it has no default export, use `export default { … }`\");\n }\n\n await this.#collectPluginData(module.default);\n this.config = module.default;\n this.resolver = createResolver(this.root, module.default.aliases);\n this.#onLoad();\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n\n this.#console.error(`[staticbolt] failed to load ${configPath}: ${reason}`);\n }\n }\n\n /** Asks every plugin of the config what it contributes to the editor. */\n async #collectPluginData(config: AppConfig): Promise<void> {\n const plugins = (config.plugins ?? []).flat();\n const embeddedLanguages: EmbeddedLanguage[] = [];\n const validators: Validator[] = [];\n const htmlData: HTMLDataV1[] = [];\n\n for (const plugin of plugins) {\n embeddedLanguages.push(...(plugin.lspEmbeddedLanguages?.() ?? []));\n\n if (plugin.lspValidate) {\n validators.push({ name: plugin.name, validate: plugin.lspValidate, hasFailed: false });\n }\n\n const data = await plugin.lspHtmlData?.();\n if (data) {\n htmlData.push(creditPlugin(data, plugin.name));\n }\n }\n\n this.embeddedLanguages = embeddedLanguages;\n this.validators = validators;\n this.htmlData = htmlData;\n }\n}\n\n/** The projects the server has been asked about, each started on first request. */\nexport class Projects {\n /** Where to log. */\n readonly #console: RemoteConsole;\n\n /** By root, from the moment a project was asked for. */\n readonly #starting = new Map<string, Promise<Project>>();\n\n /** By root, once the project's config loaded. */\n readonly #loaded = new Map<string, Project>();\n\n /** The project root of every directory a document was served from. */\n readonly #roots = new Map<string, string>();\n\n /** Told whenever any project's config loaded. */\n readonly #onLoad: () => void;\n\n /** Starts with no projects; each is started the first time `get` asks for it. */\n constructor(console: RemoteConsole, onLoad: () => void) {\n this.#console = console;\n this.#onLoad = onLoad;\n }\n\n /**\n * The project at a root, or nothing while its config cannot be loaded. The project keeps following its config file either way,\n * so a fixed config loads on its own.\n */\n async get(root: string): Promise<Project | undefined> {\n let project = this.#starting.get(root);\n\n if (!project) {\n project = this.#start(root);\n this.#starting.set(root, project);\n }\n\n const started = await project;\n\n return started.config ? started : undefined;\n }\n\n /**\n * The project a document belongs to, if it has loaded already. One that has not is started, and the document is served again\n * once it loads; a document outside any project gets nothing.\n */\n of(documentUri: string): Project | undefined {\n const directory = path.dirname(vscodeUri.URI.parse(documentUri).fsPath);\n const root = this.#roots.get(directory) ?? findProjectRoot(directory);\n if (root === undefined) {\n return undefined;\n }\n\n this.#roots.set(directory, root);\n\n const loaded = this.#loaded.get(root);\n if (!loaded) {\n void this.get(root);\n }\n\n return loaded;\n }\n\n /** Stops following every project. */\n async dispose(): Promise<void> {\n const projects = await Promise.all(this.#starting.values());\n\n for (const project of projects) {\n project.dispose();\n }\n\n this.#starting.clear();\n this.#loaded.clear();\n this.#roots.clear();\n }\n\n /** A project at a root, counted as loaded from the first time its config loads. */\n async #start(root: string): Promise<Project> {\n const project = new Project(root, this.#console, () => {\n this.#loaded.set(root, project);\n this.#onLoad();\n });\n\n await project.start();\n\n return project;\n }\n}\n\n/** The data with every description saying which plugin it comes from. */\nfunction creditPlugin(htmlData: HTMLDataV1, pluginName: string): HTMLDataV1 {\n const credit = `_Provided by **staticbolt** \\`${pluginName}\\` plugin._`;\n\n const withCredit = (description: string | MarkupContent | undefined): string | MarkupContent => {\n if (!description) {\n return credit;\n }\n\n const text = typeof description === \"string\" ? description : description.value;\n if (text.includes(credit)) {\n return description;\n }\n\n const credited = `${text}\\n\\n${credit}`;\n if (typeof description === \"string\") {\n return credited;\n }\n\n return { ...description, value: credited };\n };\n\n const creditAttributes = (attributes: IAttributeData[]): IAttributeData[] => {\n return attributes.map(attribute => ({ ...attribute, description: withCredit(attribute.description) }));\n };\n\n const tags = htmlData.tags?.map(tag => {\n return { ...tag, description: withCredit(tag.description), attributes: creditAttributes(tag.attributes) };\n });\n\n const globalAttributes = htmlData.globalAttributes ? creditAttributes(htmlData.globalAttributes) : undefined;\n\n return { ...htmlData, tags, globalAttributes };\n}\n","import type {\n HTMLDataV1,\n IAttributeData,\n IHTMLDataProvider,\n IReference,\n ITagData,\n IValueData,\n MarkupContent,\n} from \"vscode-html-languageservice\";\n\n/** A description as the data format allows it: plain, marked up, or absent. */\ntype Description = string | MarkupContent | undefined;\n\n/** Plain text or markdown. */\ntype DescriptionKind = MarkupContent[\"kind\"];\n\n/** The value sets by name. */\ntype ValueSets = Map<string, IValueData[]>;\n\n/** The `valueSet` the HTML language service reads as \"a boolean attribute\". */\nconst BOOLEAN_VALUE_SET = \"v\";\n\n/** Separates the documentation of two contributors, rendered as a horizontal rule. */\nconst MARKDOWN_SEPARATOR = \"\\n\\n---\\n\\n\";\n\n/** The same, for descriptions that are plain text. */\nconst PLAINTEXT_SEPARATOR = \"\\n\\n\";\n\n/** Groups entries by key, preserving the order of first appearance. */\nfunction groupBy<T>(items: T[], toKey: (item: T) => string): T[][] {\n const groups = new Map<string, T[]>();\n\n for (const item of items) {\n const key = toKey(item);\n const group = groups.get(key) ?? [];\n\n group.push(item);\n groups.set(key, group);\n }\n\n return groups.values().toArray();\n}\n\n/** Markdown wins over plain text, so a contributor asking for rich text still gets it. Plain strings imply no preference. */\nfunction mergeDescriptionKind(descriptions: Exclude<Description, undefined>[]): DescriptionKind | undefined {\n let kind: DescriptionKind | undefined;\n\n for (const description of descriptions) {\n if (typeof description === \"string\") continue;\n if (kind === \"markdown\") break;\n\n kind = description.kind;\n }\n\n return kind;\n}\n\n/** The text of a description, whichever shape it has. */\nfunction textOf(description: Exclude<Description, undefined>): string {\n if (typeof description === \"string\") {\n return description;\n }\n\n return description.value;\n}\n\n/** Concatenates every distinct description, so no contributor's documentation gets lost. */\nfunction mergeDescriptions(descriptions: Description[]): Description {\n const defined = descriptions.filter(description => description !== undefined);\n\n if (defined.length <= 1) {\n return defined[0];\n }\n\n const parts: string[] = [];\n const seen = new Set<string>();\n\n for (const description of defined) {\n const text = textOf(description).trim();\n if (!text || seen.has(text)) continue;\n\n seen.add(text);\n parts.push(text);\n }\n\n if (parts.length === 0) {\n return undefined;\n }\n\n const kind = mergeDescriptionKind(defined);\n const separator = kind === \"plaintext\" ? PLAINTEXT_SEPARATOR : MARKDOWN_SEPARATOR;\n const value = parts.join(separator);\n\n if (!kind) {\n return value;\n }\n\n return { kind, value };\n}\n\n/** Every distinct reference, a reference being the same as another when both name and url match. */\nfunction mergeReferences(references: (IReference[] | undefined)[]): IReference[] | undefined {\n const merged: IReference[] = [];\n const seen = new Set<string>();\n\n for (const list of references) {\n const entries = list ?? [];\n\n for (const reference of entries) {\n const key = `${reference.name} ${reference.url}`;\n if (seen.has(key)) continue;\n\n seen.add(key);\n merged.push(reference);\n }\n }\n\n if (merged.length === 0) {\n return undefined;\n }\n\n return merged;\n}\n\n/** Every distinct browser. */\nfunction mergeBrowsers(browsers: (string[] | undefined)[]): string[] | undefined {\n const merged = new Set<string>();\n\n for (const list of browsers) {\n const entries = list ?? [];\n\n for (const browser of entries) {\n merged.add(browser);\n }\n }\n\n if (merged.size === 0) {\n return undefined;\n }\n\n return [...merged];\n}\n\n/** The values an attribute contributes, with its `valueSet` reference expanded. */\nfunction resolveValues(attribute: IAttributeData, valueSets: ValueSets): IValueData[] {\n const values = attribute.values ?? [];\n\n if (!attribute.valueSet) {\n return values;\n }\n\n return [...(valueSets.get(attribute.valueSet) ?? []), ...values];\n}\n\n/** Merges entries sharing the same value name into one. Unlike tags and attributes, value names are case-sensitive. */\nfunction mergeValues(values: IValueData[]): IValueData[] {\n return groupBy(values, value => value.name).map(group => {\n if (group.length === 1) {\n return group[0];\n }\n\n return {\n name: group[0].name,\n description: mergeDescriptions(group.map(value => value.description)),\n references: mergeReferences(group.map(value => value.references)),\n browsers: mergeBrowsers(group.map(value => value.browsers)),\n status: group.find(value => value.status)?.status,\n };\n });\n}\n\n/**\n * Merges entries sharing the same attribute name into one, combining their descriptions, values, references and browser support.\n *\n * `valueSet` references are expanded into the merged `values`, so attributes contributed by different plugins can each bring\n * their own value set and still end up with a single, complete value list.\n */\nfunction mergeAttributes(attributes: IAttributeData[], valueSets: ValueSets): IAttributeData[] {\n return groupBy(attributes, attribute => attribute.name.toLowerCase()).map(group => {\n if (group.length === 1 && !group[0].valueSet) {\n return group[0];\n }\n\n const values = mergeValues(group.flatMap(attribute => resolveValues(attribute, valueSets)));\n const isBoolean = group.some(attribute => attribute.valueSet === BOOLEAN_VALUE_SET);\n\n return {\n name: group[0].name,\n description: mergeDescriptions(group.map(attribute => attribute.description)),\n valueSet: isBoolean ? BOOLEAN_VALUE_SET : undefined,\n values: values.length > 0 ? values : undefined,\n references: mergeReferences(group.map(attribute => attribute.references)),\n browsers: mergeBrowsers(group.map(attribute => attribute.browsers)),\n status: group.find(attribute => attribute.status)?.status,\n };\n });\n}\n\n/** Merges entries sharing the same tag name into one, including their attributes. */\nfunction mergeTags(tags: ITagData[], valueSets: ValueSets): ITagData[] {\n return groupBy(tags, tag => tag.name.toLowerCase()).map(group => {\n const attributes = mergeAttributes(\n group.flatMap(tag => tag.attributes ?? []),\n valueSets\n );\n\n if (group.length === 1) {\n return { ...group[0], attributes };\n }\n\n return {\n name: group[0].name,\n description: mergeDescriptions(group.map(tag => tag.description)),\n attributes,\n references: mergeReferences(group.map(tag => tag.references)),\n browsers: mergeBrowsers(group.map(tag => tag.browsers)),\n status: group.find(tag => tag.status)?.status,\n void: group.some(tag => tag.void),\n };\n });\n}\n\n/** Value sets sharing a name are merged, so an attribute referencing one gets the values of every contributor. */\nfunction collectValueSets(htmlData: HTMLDataV1[]): ValueSets {\n const valueSets: ValueSets = new Map();\n const collected = htmlData.flatMap(data => data.valueSets ?? []);\n\n for (const valueSet of collected) {\n const existing = valueSets.get(valueSet.name) ?? [];\n\n valueSets.set(valueSet.name, mergeValues([...existing, ...valueSet.values]));\n }\n\n return valueSets;\n}\n\n/**\n * Builds a single data provider out of every collected `HTMLDataV1`, merging tags, attributes and values that share the same name\n * instead of reporting them once per contributor.\n */\nexport function createMergedHtmlDataProvider(id: string, htmlData: HTMLDataV1[]): IHTMLDataProvider {\n const valueSets = collectValueSets(htmlData);\n\n const tags = mergeTags(\n htmlData.flatMap(data => data.tags ?? []),\n valueSets\n );\n\n const globalAttributes = mergeAttributes(\n htmlData.flatMap(data => data.globalAttributes ?? []),\n valueSets\n );\n\n const tagsByName = new Map(tags.map(tag => [tag.name.toLowerCase(), tag]));\n const attributesByTag = new Map<string, IAttributeData[]>();\n\n function provideAttributes(tag: string): IAttributeData[] {\n const key = tag.toLowerCase();\n\n const cached = attributesByTag.get(key);\n if (cached) {\n return cached;\n }\n\n // An unknown tag only sees the global attributes, which are merged already\n const tagAttributes = tagsByName.get(key)?.attributes;\n if (!tagAttributes || tagAttributes.length === 0) {\n return globalAttributes;\n }\n\n const attributes = mergeAttributes([...globalAttributes, ...tagAttributes], valueSets);\n attributesByTag.set(key, attributes);\n\n return attributes;\n }\n\n return {\n getId() {\n return id;\n },\n\n isApplicable(languageId) {\n return languageId === \"html\";\n },\n\n provideTags() {\n return tags;\n },\n\n provideAttributes,\n\n provideValues(tag, attribute) {\n const name = attribute.toLowerCase();\n const match = provideAttributes(tag).find(candidate => candidate.name.toLowerCase() === name);\n\n return match?.values ?? [];\n },\n };\n}\n","import { DiagnosticSeverity } from \"vscode-languageserver-protocol\";\n\nimport type { Project } from \"../projects.ts\";\nimport type { AttributeInfo, DocumentInfo, ElementInfo, ProblemReporter, ProblemTarget, TextRange } from \"@staticbolt/core\";\nimport type { Diagnostic } from \"vscode-languageserver-protocol\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\n\n/** What diagnostics from plugins are labelled with, followed by the plugin's name. */\nconst SOURCE = \"staticbolt\";\n\n/** What validating a document takes. */\nexport interface ValidationInput {\n /** The document as HTML, where the diagnostics go. */\n document: TextDocument;\n\n /** The document as the plugins see it. */\n info: DocumentInfo;\n\n /** The project whose plugins validate. */\n project: Project;\n\n /** Where a failing plugin is logged. */\n console: Pick<Console, \"error\">;\n}\n\n/**\n * Runs every validating plugin of the project over a document and gathers what they report as diagnostics. A plugin that throws\n * is skipped, so the others still report, and logged the first time.\n */\nexport async function validateDocument({ document, info, project, console }: ValidationInput): Promise<Diagnostic[]> {\n const diagnostics: Diagnostic[] = [];\n\n for (const validator of project.validators) {\n const report = createReporter(document, validator.name, diagnostics);\n\n try {\n await validator.validate(info, report);\n } catch (error) {\n if (validator.hasFailed) continue;\n\n validator.hasFailed = true;\n console.error(`[staticbolt] the ${validator.name} plugin failed to validate ${info.file}:`, error);\n }\n }\n\n return diagnostics;\n}\n\n/** A reporter adding to `diagnostics`, each one labelled with the plugin it comes from. */\nfunction createReporter(document: TextDocument, pluginName: string, diagnostics: Diagnostic[]): ProblemReporter {\n function reportAs(severity: DiagnosticSeverity) {\n return (target: ProblemTarget, message: string) => {\n const { start, end } = rangeOf(target);\n\n diagnostics.push({\n range: { start: document.positionAt(start), end: document.positionAt(end) },\n message,\n severity,\n source: SOURCE,\n code: pluginName,\n });\n };\n }\n\n return {\n error: reportAs(DiagnosticSeverity.Error),\n warn: reportAs(DiagnosticSeverity.Warning),\n info: reportAs(DiagnosticSeverity.Information),\n hint: reportAs(DiagnosticSeverity.Hint),\n };\n}\n\n/** An element underlines its tag name, an attribute its value or else its name, a range itself. */\nfunction rangeOf(target: ProblemTarget): TextRange {\n if (isElement(target)) {\n return target.nameRange;\n }\n\n if (isAttribute(target)) {\n return target.valueRange ?? target.nameRange;\n }\n\n return target;\n}\n\n/** Only an element has children. */\nfunction isElement(target: ProblemTarget): target is ElementInfo {\n return \"children\" in target;\n}\n\n/** Only an attribute is on an element. */\nfunction isAttribute(target: ProblemTarget): target is AttributeInfo {\n return \"element\" in target;\n}\n","import path from \"node:path\";\nimport { decodeEmbeddedDocumentUri } from \"@volar/language-service\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport type { Resolver } from \"@staticbolt/core\";\nimport type { DocumentContext } from \"vscode-html-languageservice\";\n\n/** A reference that carries its own scheme, `https:` or `mailto:` say. */\nconst WITH_SCHEME = /^[a-z][\\w+.-]*:/i;\n\n/**\n * How references in a document map to files: path aliases through the project's resolver, absolute paths against the project\n * root, everything else relative to the document.\n *\n * The language service bases a reference on the uri of the document it was given, which is the embedded copy the server serves\n * the document through; that copy carries no path, so it falls back to the document itself, and any other base, a `<base href>`\n * say, stands as given.\n */\nexport function getDocumentContext(documentUri: string, resolver: Resolver): DocumentContext {\n return {\n resolveReference(reference, base = documentUri) {\n if (WITH_SCHEME.test(reference)) {\n return reference;\n }\n\n if (decodeEmbeddedDocumentUri(vscodeUri.URI.parse(base)) !== undefined) {\n base = documentUri;\n }\n\n const aliased = resolver.resolveAlias(reference);\n if (aliased !== undefined) {\n return vscodeUri.URI.file(path.join(resolver.root, aliased)).toString(true);\n }\n\n if (reference.startsWith(\"/\")) {\n return vscodeUri.URI.file(path.join(resolver.root, reference)).toString(true);\n }\n\n const baseUri = vscodeUri.URI.parse(base);\n const baseDirectory = baseUri.path.endsWith(\"/\") ? baseUri : vscodeUri.Utils.dirname(baseUri);\n\n return vscodeUri.Utils.resolvePath(baseDirectory, reference).toString(true);\n },\n };\n}\n","import path from \"node:path\";\nimport vscodeHtml from \"vscode-html-languageservice\";\nimport { CompletionItemKind, SemanticTokenTypes, TextEdit } from \"vscode-languageserver-protocol\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport { createMergedHtmlDataProvider } from \"../helpers/merge-html-data.ts\";\nimport { isInRegions } from \"../helpers/regions.ts\";\nimport { validateDocument } from \"../helpers/validation.ts\";\nimport { getDocumentContext } from \"../helpers/document-context.ts\";\nimport { StaticboltCode } from \"../virtual-code.ts\";\n\nimport type { Project } from \"../projects.ts\";\nimport type { LanguageServiceContext, LanguageServicePlugin, SemanticToken } from \"@volar/language-service\";\nimport type { FileStat, FileSystemProvider, HTMLDataV1, LanguageService } from \"vscode-html-languageservice\";\nimport type { CompletionItem, Position } from \"vscode-languageserver-protocol\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\n\n/** The id the merged data provider registers under with the language service. */\nconst DATA_PROVIDER_ID = \"staticbolt\";\n\n/** The cursor inside a `src` or `href` value before any slash, capturing what is typed of its first segment. */\nconst PATH_VALUE_START = /(?:src|href)\\s*=\\s*[\"']([^\"'/\\s]*)$/;\n\n/** Has the editor open the completions again, right after an item is taken. */\nconst SUGGEST = { title: \"Suggest\", command: \"editor.action.triggerSuggest\" };\n\n/** A document of a loaded project, with the HTML language service that knows the project's tags and attributes. */\ninterface Found {\n /** The document's root code, holding its HTML and regions. */\n code: StaticboltCode;\n\n /** The project the document belongs to. */\n project: Project;\n\n /** The project's HTML language service. */\n languageService: LanguageService;\n}\n\n/**\n * What the plugins add to HTML: their tags and attributes for completion and hover, path completion that knows the project's\n * aliases, links resolved the way the build resolves them, and the problems the plugins find. The editor's own HTML support\n * covers the standard elements, and stays out of the regions the plugins embed.\n */\nexport function createStaticboltService(): LanguageServicePlugin {\n return {\n name: \"staticbolt\",\n\n capabilities: {\n completionProvider: { triggerCharacters: [\".\", \":\", \"<\", '\"', \"=\", \"/\"] },\n hoverProvider: true,\n documentLinkProvider: {},\n diagnosticProvider: { interFileDependencies: false, workspaceDiagnostics: false },\n semanticTokensProvider: { legend: { tokenTypes: [SemanticTokenTypes.operator], tokenModifiers: [] } },\n },\n\n create(context) {\n // One language service per project's data, which is a new array whenever its config is loaded again\n const languageServices = new WeakMap<HTMLDataV1[], LanguageService>();\n\n /** The HTML language service that knows a project's tags and attributes. */\n function languageServiceOf(project: Project): LanguageService {\n let languageService = languageServices.get(project.htmlData);\n\n if (!languageService) {\n languageService = vscodeHtml.getLanguageService({\n clientCapabilities: context.env.clientCapabilities,\n fileSystemProvider: fileSystemOf(context),\n useDefaultDataProvider: false,\n customDataProviders: [createMergedHtmlDataProvider(DATA_PROVIDER_ID, project.htmlData)],\n });\n\n languageServices.set(project.htmlData, languageService);\n }\n\n return languageService;\n }\n\n /** The document's code and project, or nothing when the document is no HTML of a loaded project. */\n function find(document: TextDocument): Found | undefined {\n if (document.languageId !== \"html\") {\n return undefined;\n }\n\n const code = codeOf(context, document);\n if (!code?.project) {\n return undefined;\n }\n\n return { code, project: code.project, languageService: languageServiceOf(code.project) };\n }\n\n return {\n async provideCompletionItems(document, position) {\n const found = find(document);\n if (!found) {\n return;\n }\n\n // Inside a plugin's region the code is the plugin language's, served through TypeScript\n if (isInRegions(found.code.regions, document.offsetAt(position))) {\n return;\n }\n\n const { code, project, languageService } = found;\n const documentContext = getDocumentContext(code.uri.toString(), project.resolver);\n const list = await languageService.doComplete2(document, position, code.htmlDocument, documentContext);\n\n list.items.push(...aliasCompletions(document, position, project.resolver.aliases));\n\n // Ahead of whatever the editor's own HTML support offers\n for (const item of list.items) {\n item.sortText = \"0_\" + item.label;\n }\n\n return list;\n },\n\n provideHover(document, position) {\n const found = find(document);\n if (!found) {\n return;\n }\n\n if (isInRegions(found.code.regions, document.offsetAt(position))) {\n return;\n }\n\n return found.languageService.doHover(document, position, found.code.htmlDocument);\n },\n\n provideDocumentLinks(document) {\n const found = find(document);\n if (!found) {\n return;\n }\n\n const { code, project, languageService } = found;\n const documentPath = code.uri.fsPath;\n const documentContext = getDocumentContext(code.uri.toString(), project.resolver);\n const links = languageService.findDocumentLinks(document, documentContext);\n\n // Resolved the way the build resolves sources: aliases, extensionless paths and directories with an index file\n for (const link of links) {\n if (!link.target) continue;\n\n const source = path.relative(path.dirname(documentPath), vscodeUri.URI.parse(link.target).fsPath);\n const resolved = project.resolver.resolve(source, code.file);\n if (!resolved) continue;\n\n link.target = vscodeUri.URI.file(resolved.path).toString();\n }\n\n return links;\n },\n\n /**\n * Colours the delimiters of the plugins' regions, the `{{` and `}}` of a placeholder: they are outside the code, so\n * nothing else colours them, and they would take the colour of whatever they sit in, an attribute's string say.\n */\n provideDocumentSemanticTokens(document, _range, legend) {\n const found = find(document);\n if (!found) {\n return;\n }\n\n const type = legend.tokenTypes.indexOf(SemanticTokenTypes.operator);\n const tokens: SemanticToken[] = [];\n\n for (const region of found.code.regions) {\n if (!region.extent) continue;\n\n for (const [start, end] of [\n [region.extent.start, region.start],\n [region.end, region.extent.end],\n ]) {\n if (end <= start) continue;\n\n const { line, character } = document.positionAt(start);\n\n tokens.push([line, character, end - start, type, 0]);\n }\n }\n\n return tokens;\n },\n\n provideDiagnostics(document) {\n const found = find(document);\n if (!found) {\n return;\n }\n\n return validateDocument({\n document,\n info: found.code.info,\n project: found.project,\n console: context.env.console ?? console,\n });\n },\n };\n },\n };\n}\n\n/** The editor's file system, as the HTML language service reads it for path completions; nothing is there without one. */\nfunction fileSystemOf(context: LanguageServiceContext): FileSystemProvider {\n const missing: FileStat = { type: vscodeHtml.FileType.Unknown, ctime: -1, mtime: -1, size: -1 };\n\n return {\n async stat(uri) {\n return (await context.env.fs?.stat(vscodeUri.URI.parse(uri))) ?? missing;\n },\n\n async readDirectory(uri) {\n return (await context.env.fs?.readDirectory(vscodeUri.URI.parse(uri))) ?? [];\n },\n };\n}\n\n/** The root code of the document a service is asked about, whether it is the document itself or its embedded HTML copy. */\nfunction codeOf(context: LanguageServiceContext, document: TextDocument): StaticboltCode | undefined {\n const uri = vscodeUri.URI.parse(document.uri);\n const [sourceUri] = context.decodeEmbeddedDocumentUri(uri) ?? [uri];\n const root = context.language.scripts.get(sourceUri)?.generated?.root;\n\n if (!(root instanceof StaticboltCode)) {\n return undefined;\n }\n\n return root;\n}\n\n/**\n * The path aliases, offered at the start of a `src` or `href` value: a partly typed one completes, and a directory alias opens\n * its listing right away.\n */\nfunction aliasCompletions(document: TextDocument, position: Position, aliases: Record<string, string>): CompletionItem[] {\n const lineBeforeCursor = document.getText({ start: { line: position.line, character: 0 }, end: position });\n const typed = PATH_VALUE_START.exec(lineBeforeCursor)?.[1];\n\n if (typed === undefined) {\n return [];\n }\n\n const range = { start: { line: position.line, character: position.character - typed.length }, end: position };\n\n return Object.keys(aliases).map(alias => {\n const textEdit = TextEdit.replace(range, alias);\n\n // A directory alias goes on to list its files\n if (alias.endsWith(\"/\")) {\n return { label: alias, kind: CompletionItemKind.Folder, textEdit, command: SUGGEST };\n }\n\n return { label: alias, kind: CompletionItemKind.File, textEdit };\n });\n}\n","import { SemanticTokenModifiers, SemanticTokenTypes } from \"vscode-languageserver-protocol\";\n\nimport type * as ts from \"typescript\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\n\n/** A token with the names of its type and modifiers. */\nexport interface SyntaxToken {\n /** The zero-based line the token is on. */\n line: number;\n\n /** The zero-based character the token starts at. */\n character: number;\n\n /** The number of characters the token spans. */\n length: number;\n\n /** The name of the token type. */\n type: string;\n\n /** The names of the token modifiers. */\n modifiers: readonly string[];\n}\n\n/** A standard token type with modifiers, what an editor that knows only the standard types is sent for a scoped type. */\ninterface StandardType {\n /** The standard type. */\n type: string;\n\n /** Its modifiers. */\n modifiers: readonly string[];\n}\n\n/** A member name, as TypeScript names the ones it knows. */\nconst PROPERTY: StandardType = { type: SemanticTokenTypes.property, modifiers: [] };\n\n/** The literals of the language as the standard types see them: read-only variables of the library. */\nconst LITERAL: StandardType = {\n type: SemanticTokenTypes.variable,\n modifiers: [SemanticTokenModifiers.readonly, SemanticTokenModifiers.defaultLibrary],\n};\n\n/**\n * The token types of what TypeScript's own tokens leave out, after the grammar scopes a TypeScript file gets them coloured by, so\n * an editor that maps them to those scopes colours the code as it colours TypeScript. Split where themes tell the scopes apart: a\n * `const` sits in `meta.var.expr`, a `class` in `meta.class`, an `import` in `meta.import`. The values are the standard types an\n * editor that knows only those is sent instead.\n */\nexport const SCOPED_TYPES = {\n /** `if`, `return`, `await`: `keyword.control`. */\n keywordControl: { type: SemanticTokenTypes.keyword, modifiers: [] },\n\n /** `import`, `export`, `from`, `as`: `meta.import keyword.control.import`. */\n keywordControlImport: { type: SemanticTokenTypes.keyword, modifiers: [] },\n\n /** `const`, `let`, `var`: `meta.var.expr storage.type`. */\n storageTypeVariable: { type: SemanticTokenTypes.modifier, modifiers: [] },\n\n /** `function`: `meta.function storage.type.function`. */\n storageTypeFunction: { type: SemanticTokenTypes.modifier, modifiers: [] },\n\n /** `class`: `meta.class storage.type.class`. */\n storageTypeClass: { type: SemanticTokenTypes.modifier, modifiers: [] },\n\n /** `interface`, `type`, `enum`, `namespace`: `storage.type`. */\n storageType: { type: SemanticTokenTypes.modifier, modifiers: [] },\n\n /** `async`, `static`, `readonly`, `extends`: `storage.modifier`. */\n storageModifier: { type: SemanticTokenTypes.modifier, modifiers: [] },\n\n /** `typeof`, `instanceof`, `in`: `keyword.operator.expression`. */\n keywordOperatorExpression: { type: SemanticTokenTypes.keyword, modifiers: [] },\n\n /** `new`: `new.expr keyword.operator.new`. */\n keywordOperatorNew: { type: SemanticTokenTypes.keyword, modifiers: [] },\n\n /** `true`, `null`, `undefined`: `constant.language`. */\n constantLanguage: LITERAL,\n\n /** `this`, `super`: `variable.language`. */\n variableLanguage: LITERAL,\n\n /** `string`, `number`, `boolean`: `meta.type.annotation support.type.primitive`. */\n supportTypePrimitive: { type: SemanticTokenTypes.type, modifiers: [SemanticTokenModifiers.defaultLibrary] },\n} as const satisfies Record<string, StandardType>;\n\n/** A scoped token type. */\ntype ScopedType = keyof typeof SCOPED_TYPES;\n\n/** The scoped type of each keyword TypeScript tokenizes as one; the control flow keywords are the rest. */\ntype Keywords = Partial<Record<ts.SyntaxKind, ScopedType>>;\n\n/** Names the tokens of a parsed TypeScript text. */\nexport type SyntaxTokenizer = (sourceFile: ts.SourceFile, document: TextDocument, checker?: ts.TypeChecker) => SyntaxToken[];\n\n/** The keyword table, with the `SyntaxKind` values of the TypeScript in use. */\nfunction keywordsOf(typescript: typeof ts): Keywords {\n const { SyntaxKind } = typescript;\n const keywords: Keywords = {};\n\n const table: [ts.SyntaxKind[], ScopedType][] = [\n [[SyntaxKind.ImportKeyword, SyntaxKind.ExportKeyword, SyntaxKind.FromKeyword, SyntaxKind.AsKeyword], \"keywordControlImport\"],\n [[SyntaxKind.ConstKeyword, SyntaxKind.LetKeyword, SyntaxKind.VarKeyword], \"storageTypeVariable\"],\n [[SyntaxKind.FunctionKeyword], \"storageTypeFunction\"],\n [[SyntaxKind.ClassKeyword], \"storageTypeClass\"],\n [\n [\n SyntaxKind.InterfaceKeyword,\n SyntaxKind.TypeKeyword,\n SyntaxKind.EnumKeyword,\n SyntaxKind.NamespaceKeyword,\n SyntaxKind.ModuleKeyword,\n ],\n \"storageType\",\n ],\n [\n [\n SyntaxKind.AbstractKeyword,\n SyntaxKind.AccessorKeyword,\n SyntaxKind.AsyncKeyword,\n SyntaxKind.DeclareKeyword,\n SyntaxKind.ExtendsKeyword,\n SyntaxKind.ImplementsKeyword,\n SyntaxKind.OverrideKeyword,\n SyntaxKind.PrivateKeyword,\n SyntaxKind.ProtectedKeyword,\n SyntaxKind.PublicKeyword,\n SyntaxKind.ReadonlyKeyword,\n SyntaxKind.StaticKeyword,\n ],\n \"storageModifier\",\n ],\n [\n [\n SyntaxKind.DeleteKeyword,\n SyntaxKind.InKeyword,\n SyntaxKind.InferKeyword,\n SyntaxKind.InstanceOfKeyword,\n SyntaxKind.IsKeyword,\n SyntaxKind.KeyOfKeyword,\n SyntaxKind.OfKeyword,\n SyntaxKind.SatisfiesKeyword,\n SyntaxKind.TypeOfKeyword,\n ],\n \"keywordOperatorExpression\",\n ],\n [[SyntaxKind.NewKeyword], \"keywordOperatorNew\"],\n [[SyntaxKind.TrueKeyword, SyntaxKind.FalseKeyword, SyntaxKind.NullKeyword], \"constantLanguage\"],\n [[SyntaxKind.ThisKeyword, SyntaxKind.SuperKeyword], \"variableLanguage\"],\n [\n [\n SyntaxKind.AnyKeyword,\n SyntaxKind.BigIntKeyword,\n SyntaxKind.BooleanKeyword,\n SyntaxKind.NeverKeyword,\n SyntaxKind.NumberKeyword,\n SyntaxKind.ObjectKeyword,\n SyntaxKind.StringKeyword,\n SyntaxKind.SymbolKeyword,\n SyntaxKind.UndefinedKeyword,\n SyntaxKind.UnknownKeyword,\n ],\n \"supportTypePrimitive\",\n ],\n ];\n\n for (const [kinds, type] of table) {\n for (const kind of kinds) {\n keywords[kind] = type;\n }\n }\n\n return keywords;\n}\n\n/**\n * A tokenizer for what TypeScript's own tokens leave out of a parsed text: keywords by what they are where they stand, literals,\n * comments and operators. The identifiers are left to TypeScript, which knows what each is, except the member names it knows\n * nothing about, a key of a `Record` say, which it leaves out: those are properties all the same. Unless `isScoped`, the keywords\n * are named as the nearest standard types.\n */\nexport function createSyntaxTokenizer(typescript: typeof ts, isScoped: boolean): SyntaxTokenizer {\n const keywords = keywordsOf(typescript);\n\n return (sourceFile, document, checker) => {\n const text = sourceFile.text;\n const tokens: SyntaxToken[] = [];\n const commentEnds = new Set<number>();\n\n /** Whether an identifier names a member TypeScript has no symbol for, so it will not colour it. */\n function isUnknownMember(node: ts.Node): boolean {\n if (!checker || !typescript.isPropertyAccessExpression(node.parent) || node.parent.name !== node) {\n return false;\n }\n\n return checker.getSymbolAtLocation(node) === undefined;\n }\n\n /** The comments before a token, each once. */\n function collectComments(position: number): void {\n const comments = typescript.getLeadingCommentRanges(text, position) ?? [];\n\n for (const comment of comments) {\n if (commentEnds.has(comment.end)) continue;\n\n commentEnds.add(comment.end);\n tokens.push(...splitLines(document, comment.pos, comment.end, { type: SemanticTokenTypes.comment, modifiers: [] }));\n }\n }\n\n /** The tokens of a node and what is inside it. */\n function visit(node: ts.Node): void {\n const children = node.getChildren(sourceFile);\n\n if (children.length === 0) {\n collectComments(node.getFullStart());\n\n const named = isUnknownMember(node) ? PROPERTY : nameOf(typescript, keywords, node, isScoped);\n if (named) {\n tokens.push(...splitLines(document, node.getStart(sourceFile), node.getEnd(), named));\n }\n\n return;\n }\n\n for (const child of children) {\n visit(child);\n }\n }\n\n visit(sourceFile);\n collectComments(sourceFile.endOfFileToken.getFullStart());\n\n return tokens;\n };\n}\n\n/** The type and modifiers of a token, or nothing for the identifiers, plain punctuation and anything that is not a token. */\nfunction nameOf(typescript: typeof ts, keywords: Keywords, node: ts.Node, isScoped: boolean): StandardType | undefined {\n const { SyntaxKind } = typescript;\n const kind = node.kind;\n\n if (kind === SyntaxKind.Identifier) {\n return node.getText() === \"undefined\" ? standardOrScoped(\"constantLanguage\", isScoped) : undefined;\n }\n\n if (kind === SyntaxKind.StringLiteral || isTemplatePart(typescript, kind)) {\n return { type: SemanticTokenTypes.string, modifiers: [] };\n }\n\n if (kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.BigIntLiteral) {\n return { type: SemanticTokenTypes.number, modifiers: [] };\n }\n\n if (kind === SyntaxKind.RegularExpressionLiteral) {\n return { type: SemanticTokenTypes.regexp, modifiers: [] };\n }\n\n // Brackets, separators and accessors too: inside an attribute value no grammar colours them, and they read as string otherwise\n if (kind >= SyntaxKind.FirstPunctuation && kind <= SyntaxKind.LastPunctuation) {\n return { type: SemanticTokenTypes.operator, modifiers: [] };\n }\n\n if (kind < SyntaxKind.FirstKeyword || kind > SyntaxKind.LastKeyword) {\n return undefined;\n }\n\n // `void 0` is an operator, `: void` a type\n if (kind === SyntaxKind.VoidKeyword) {\n const isOperator = node.parent.kind === SyntaxKind.VoidExpression;\n\n return standardOrScoped(isOperator ? \"keywordOperatorExpression\" : \"supportTypePrimitive\", isScoped);\n }\n\n return standardOrScoped(keywords[kind] ?? \"keywordControl\", isScoped);\n}\n\n/** Whether a kind is one of the pieces a template literal is tokenized into. */\nfunction isTemplatePart(typescript: typeof ts, kind: ts.SyntaxKind): boolean {\n const { SyntaxKind } = typescript;\n\n return (\n kind === SyntaxKind.NoSubstitutionTemplateLiteral ||\n kind === SyntaxKind.TemplateHead ||\n kind === SyntaxKind.TemplateMiddle ||\n kind === SyntaxKind.TemplateTail\n );\n}\n\n/** A scoped type itself, or the standard type it stands for. */\nfunction standardOrScoped(type: ScopedType, isScoped: boolean): StandardType {\n if (isScoped) {\n return { type, modifiers: [] };\n }\n\n return SCOPED_TYPES[type];\n}\n\n/** A token per line of a stretch of the document, since a token may not span lines. */\nfunction splitLines(document: TextDocument, start: number, end: number, named: StandardType): SyntaxToken[] {\n const tokens: SyntaxToken[] = [];\n const first = document.positionAt(start);\n const last = document.positionAt(end);\n\n for (let line = first.line; line <= last.line; line++) {\n const character = line === first.line ? first.character : 0;\n const lineEnd =\n line === last.line\n ? last.character\n : document.offsetAt({ line: line + 1, character: 0 }) - document.offsetAt({ line, character: 0 });\n const length = lineEnd - character;\n\n if (length > 0) {\n tokens.push({ line, character, length, ...named });\n }\n }\n\n return tokens;\n}\n","import { SemanticTokenModifiers, SemanticTokenTypes } from \"vscode-languageserver-protocol\";\nimport { URI } from \"vscode-uri\";\n\nimport { isInRegions } from \"../helpers/regions.ts\";\nimport { createSyntaxTokenizer, SCOPED_TYPES } from \"../helpers/syntax-tokens.ts\";\nimport { embeddedFileName, StaticboltCode } from \"../virtual-code.ts\";\n\nimport type { TypeScriptCode } from \"../virtual-code.ts\";\nimport type { LanguageServiceContext, LanguageServicePlugin, SemanticToken } from \"@volar/language-service\";\nimport type * as ts from \"typescript\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\n\n/** What the TypeScript service shares with the other services. */\ninterface TypeScriptProvide {\n /** The language service over the project's files, the embedded codes among them. */\n \"typescript/languageService\": () => ts.LanguageService;\n}\n\n/** An embedded document with the code it was made from, as TypeScript knows the document. */\ninterface Embedded {\n /** The code, with its plugin language and its holes. */\n code: TypeScriptCode;\n\n /** The embedded document as a TypeScript file. */\n fileName: string;\n}\n\n/**\n * Colours what TypeScript's own tokens leave out of the embedded code the editor's grammar cannot see, a placeholder say:\n * keywords, literals, operators and comments, named from TypeScript's parse of it. TypeScript names the identifiers; together\n * they colour the code the way a TypeScript file is coloured. The languages the editor colours itself get only the identifiers.\n * With `isScoped`, the keywords are sent as the scoped types an editor maps to grammar scopes, see `SCOPED_TYPES`; otherwise as\n * the nearest standard types.\n */\nexport function createSyntaxTokensService(typescript: typeof ts, isScoped: boolean): LanguageServicePlugin {\n const tokenize = createSyntaxTokenizer(typescript, isScoped);\n\n return {\n name: \"staticbolt-syntax-tokens\",\n\n capabilities: {\n semanticTokensProvider: {\n legend: {\n tokenTypes: [...Object.values(SemanticTokenTypes), ...Object.keys(SCOPED_TYPES)],\n tokenModifiers: Object.values(SemanticTokenModifiers),\n },\n },\n },\n\n create(context) {\n /**\n * The parsed file of an embedded document as the project's TypeScript holds it, with the checker that knows its symbols, or\n * a fresh parse alone when the project has none.\n */\n function parse(document: TextDocument, fileName: string): [ts.SourceFile, ts.TypeChecker | undefined] {\n const program = context.inject<TypeScriptProvide>(\"typescript/languageService\")?.getProgram();\n const parsed = program?.getSourceFile(fileName);\n if (program && parsed && parsed.text === document.getText()) {\n return [parsed, program.getTypeChecker()];\n }\n\n return [typescript.createSourceFile(fileName, document.getText(), typescript.ScriptTarget.Latest, true), undefined];\n }\n\n return {\n provideDocumentSemanticTokens(document, _range, legend) {\n if (document.languageId !== \"typescript\") {\n return;\n }\n\n const embedded = embeddedOf(context, document);\n if (!embedded || embedded.code.language.isColouredByEditor) {\n return;\n }\n\n const [sourceFile, checker] = parse(document, embedded.fileName);\n const tokens: SemanticToken[] = [];\n\n for (const token of tokenize(sourceFile, document, checker)) {\n const type = legend.tokenTypes.indexOf(token.type);\n if (type === -1) continue;\n\n // The masks standing in for other languages' code are not code to colour\n const offset = document.offsetAt({ line: token.line, character: token.character });\n if (isInRegions(embedded.code.holes, offset)) continue;\n\n let modifiers = 0;\n\n for (const modifier of token.modifiers) {\n const bit = legend.tokenModifiers.indexOf(modifier);\n if (bit === -1) continue;\n\n modifiers |= 1 << bit;\n }\n\n tokens.push([token.line, token.character, token.length, type, modifiers]);\n }\n\n return tokens;\n },\n };\n },\n };\n}\n\n/** The code an embedded document was made from and its TypeScript file name, as `getExtraServiceScripts` names it. */\nfunction embeddedOf(context: LanguageServiceContext, document: TextDocument): Embedded | undefined {\n const decoded = context.decodeEmbeddedDocumentUri(URI.parse(document.uri));\n if (!decoded) {\n return undefined;\n }\n\n const [sourceUri, codeId] = decoded;\n const root = context.language.scripts.get(sourceUri)?.generated?.root;\n if (!(root instanceof StaticboltCode)) {\n return undefined;\n }\n\n const code = root.codes.find(candidate => candidate.id === codeId);\n if (!code) {\n return undefined;\n }\n\n const documentFileName = context.project.typescript?.uriConverter.asFileName(sourceUri) ?? sourceUri.fsPath;\n\n return { code, fileName: embeddedFileName(documentFileName, codeId) };\n}\n","import { create } from \"volar-service-typescript\";\n\nimport type { LanguageServicePlugin } from \"@volar/language-service\";\nimport type * as ts from \"typescript\";\n\n/**\n * TypeScript's features for the embedded codes, through Volar's TypeScript service: completion, hover, diagnostics, semantic\n * tokens, definitions, references, rename, folding and the rest. Formatting is left out: a document is HTML to the editor, and\n * its own formatter takes care of the whole of it.\n */\nexport function createTypeScriptServices(typescript: typeof ts): LanguageServicePlugin[] {\n return create(typescript).map(plugin => ({\n ...plugin,\n capabilities: {\n ...plugin.capabilities,\n documentFormattingProvider: undefined,\n documentOnTypeFormattingProvider: undefined,\n },\n }));\n}\n","import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { format, stripVTControlCharacters } from \"node:util\";\nimport { createConnection, createServer, createTypeScriptProject, loadTsdkByPath } from \"@volar/language-server/node.js\";\n\nimport { useModuleScripts } from \"./helpers/inferred-project.ts\";\nimport { createLanguagePlugin } from \"./language-plugin.ts\";\nimport { findProjectRoots, Projects } from \"./projects.ts\";\nimport { createStaticboltService } from \"./services/staticbolt-service.ts\";\nimport { createSyntaxTokensService } from \"./services/syntax-tokens-service.ts\";\nimport { createTypeScriptServices } from \"./services/typescript-service.ts\";\n\nimport type { InitializeParams, WorkspaceFolder } from \"@volar/language-server/node.js\";\n\n/** What an editor may pass as `initializationOptions`. */\ninterface InitializationOptions {\n /** Where TypeScript is. */\n typescript?: {\n /** The directory holding `typescript.js`, `node_modules/typescript/lib` say. */\n tsdk?: string;\n };\n\n /** Whether the editor maps the scoped token types to grammar scopes, so the keywords are sent as those. */\n scopedTokens?: boolean;\n}\n\n/** The initialization options, with anything that is not an object read as none. */\nfunction initializationOptionsOf(parameters: InitializeParams): InitializationOptions {\n const options: unknown = parameters.initializationOptions;\n\n if (typeof options !== \"object\" || options === null) {\n return {};\n }\n\n return options;\n}\n\n/** The LSP connection to the editor, over stdio. */\nconst connection = createConnection();\n\n/** Volar's server on top of the connection: documents, projects and the language features. */\nconst server = createServer(connection);\n\n/**\n * `RemoteConsole` takes a single string, but the shared logger calls `console` with several arguments and colours them with\n * chalk. Passing the methods straight through drops everything after the first argument, and the output panel renders no ANSI —\n * so format the arguments the way `console` would, then strip the escapes.\n */\nfunction forward(write: (message: string) => void) {\n return (...messages: unknown[]) => {\n const text = stripVTControlCharacters(format(...messages));\n\n write(text);\n };\n}\n\nconsole.log = forward(connection.console.log.bind(connection.console));\nconsole.info = forward(connection.console.info.bind(connection.console));\nconsole.warn = forward(connection.console.warn.bind(connection.console));\nconsole.error = forward(connection.console.error.bind(connection.console));\n\nprocess.on(\"unhandledRejection\", (error: unknown) => {\n console.error(\"[staticbolt] unhandled rejection:\", error);\n});\n\n/** Whether a directory holds TypeScript with its API, which the native builds do not ship. */\nfunction hasTypeScriptApi(tsdk: string): boolean {\n return existsSync(path.join(tsdk, \"typescript.js\"));\n}\n\n/**\n * The directory of the TypeScript to run: the editor's choice from the initialization options, the `--tsdk` argument, or the\n * nearest `typescript` package with an API installed above the working directory.\n */\nfunction findTsdk(parameters: InitializeParams): string | undefined {\n const options = initializationOptionsOf(parameters);\n if (options.typescript?.tsdk) {\n return options.typescript.tsdk;\n }\n\n const argument = process.argv.find(value => value.startsWith(\"--tsdk=\"));\n if (argument) {\n return argument.slice(\"--tsdk=\".length);\n }\n\n let directory = process.cwd();\n\n while (true) {\n const tsdk = path.join(directory, \"node_modules\", \"typescript\", \"lib\");\n if (hasTypeScriptApi(tsdk)) {\n return tsdk;\n }\n\n const parent = path.dirname(directory);\n if (parent === directory) {\n return undefined;\n }\n\n directory = parent;\n }\n}\n\n/** The workspace folders, or the root uri of an editor that has no folders. */\nfunction foldersOf(parameters: InitializeParams): WorkspaceFolder[] {\n if (parameters.workspaceFolders) {\n return parameters.workspaceFolders;\n }\n\n if (parameters.rootUri) {\n return [{ name: \"\", uri: parameters.rootUri }];\n }\n\n return [];\n}\n\n/** The staticbolt projects of the workspace, from initialization on. */\nlet projects: Projects | undefined;\n\nconnection.listen();\n\nconnection.onInitialize(async parameters => {\n const tsdk = findTsdk(parameters);\n if (tsdk === undefined || !hasTypeScriptApi(tsdk)) {\n throw new Error(\n `[staticbolt] no TypeScript with an API ${tsdk ? `at ${tsdk}` : \"found\"}; point typescript.tsdk or --tsdk at one, 6.x say`\n );\n }\n\n const { typescript, diagnosticMessages } = loadTsdkByPath(tsdk, parameters.locale);\n console.log(`[staticbolt] TypeScript ${typescript.version} from ${tsdk}`);\n\n // Every project's plugins are asked what they contribute before the first document is served\n let isInitialized = false;\n const workspace = new Projects(connection.console, () => {\n if (!isInitialized) return;\n\n server.project.reload();\n });\n\n projects = workspace;\n\n const roots = findProjectRoots(foldersOf(parameters));\n console.log(`[staticbolt] discovered projects:\\n${roots.map(root => ` - ${root}`).join(\"\\n\")}`);\n await Promise.all(roots.map(root => workspace.get(root)));\n isInitialized = true;\n\n const languagePlugin = createLanguagePlugin(typescript, workspace);\n const project = createTypeScriptProject(typescript, diagnosticMessages, ({ configFileName, projectHost }) => {\n if (configFileName === undefined) {\n useModuleScripts(typescript, projectHost);\n }\n\n return { languagePlugins: [languagePlugin] };\n });\n const services = [\n createStaticboltService(),\n createSyntaxTokensService(typescript, initializationOptionsOf(parameters).scopedTokens === true),\n ...createTypeScriptServices(typescript),\n ];\n\n return server.initialize(parameters, project, services);\n});\n\n/** A TypeScript config, or one of those a config extends. */\nconst TS_CONFIG = /\\/(?:tsconfig|jsconfig)[^/]*\\.json$/;\n\nconnection.onInitialized(() => {\n server.initialized();\n\n // The editor reports the changes; Volar follows the source files itself, a config is reloaded whole since it may be extended\n void server.fileWatcher.watchFiles([\"**/*.{ts,mts,cts,js,mjs,cjs}\", \"**/{tsconfig,jsconfig}*.json\"]);\n\n server.fileWatcher.onDidChangeWatchedFiles(({ changes }) => {\n if (changes.every(change => !TS_CONFIG.test(change.uri))) return;\n\n server.project.reload();\n });\n});\nconnection.onShutdown(async () => {\n await projects?.dispose();\n server.shutdown();\n});\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@staticbolt/lsp",
|
|
3
3
|
"description": "Language Server Protocol for staticbolt",
|
|
4
|
-
"version": "1.0.0-beta.
|
|
4
|
+
"version": "1.0.0-beta.34",
|
|
5
5
|
"author": "Ahmed ALABSI",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@staticbolt/core": "1.0.0-beta.
|
|
7
|
+
"@staticbolt/core": "1.0.0-beta.34",
|
|
8
8
|
"@volar/language-core": "^2.4.28",
|
|
9
9
|
"@volar/language-server": "^2.4.28",
|
|
10
10
|
"@volar/language-service": "^2.4.28",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
],
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@staticbolt/core": "1.0.0-beta.
|
|
31
|
+
"@staticbolt/core": "1.0.0-beta.34"
|
|
32
32
|
},
|
|
33
33
|
"private": false,
|
|
34
34
|
"scripts": {
|
package/src/helpers/regions.ts
CHANGED
|
@@ -55,8 +55,8 @@ function isInside(range: TextRange, outer: TextRange): boolean {
|
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* The plugin regions by the file they share: the classic regions of a language together, each module on its own. Every group is
|
|
58
|
-
* in text order, with the regions of other languages inside its own as its holes: a placeholder inside a build-time script is
|
|
59
|
-
*
|
|
58
|
+
* in text order, with the regions of other languages inside its own as its holes: a placeholder inside a build-time script is the
|
|
59
|
+
* placeholder language's.
|
|
60
60
|
*/
|
|
61
61
|
export function groupByFile(regions: readonly PluginRegion[]): LanguageRegions[] {
|
|
62
62
|
const groups = new Map<string, LanguageRegions>();
|
|
@@ -66,7 +66,7 @@ export function groupByFile(regions: readonly PluginRegion[]): LanguageRegions[]
|
|
|
66
66
|
const id = region.isModule ? `${region.language.name}.module${modules++}` : region.language.name;
|
|
67
67
|
const group = groups.get(id) ?? { id, language: region.language, isModule: region.isModule === true, regions: [], holes: [] };
|
|
68
68
|
|
|
69
|
-
group.regions.push({ start: region.start, end: region.end });
|
|
69
|
+
group.regions.push({ start: region.start, end: region.end, isExpression: region.isExpression });
|
|
70
70
|
group.holes.push(...holesOf(region, regions));
|
|
71
71
|
groups.set(id, group);
|
|
72
72
|
}
|
|
@@ -92,14 +92,6 @@ type Keywords = Partial<Record<ts.SyntaxKind, ScopedType>>;
|
|
|
92
92
|
/** Names the tokens of a parsed TypeScript text. */
|
|
93
93
|
export type SyntaxTokenizer = (sourceFile: ts.SourceFile, document: TextDocument, checker?: ts.TypeChecker) => SyntaxToken[];
|
|
94
94
|
|
|
95
|
-
/** The punctuation that is an operator; the brackets, separators and accessors are left to the default colour. */
|
|
96
|
-
const OPERATORS = new Set(
|
|
97
|
-
(
|
|
98
|
-
"= == === != !== + - * / % ** ++ -- < > <= >= && || ?? ! ~ & | ^ << >> >>> ? : => ... += -= *= /= %= **= <<= >>= >>>= " +
|
|
99
|
-
"&= |= ^= &&= ||= ??="
|
|
100
|
-
).split(" ")
|
|
101
|
-
);
|
|
102
|
-
|
|
103
95
|
/** The keyword table, with the `SyntaxKind` values of the TypeScript in use. */
|
|
104
96
|
function keywordsOf(typescript: typeof ts): Keywords {
|
|
105
97
|
const { SyntaxKind } = typescript;
|
|
@@ -263,10 +255,9 @@ function nameOf(typescript: typeof ts, keywords: Keywords, node: ts.Node, isScop
|
|
|
263
255
|
return { type: SemanticTokenTypes.regexp, modifiers: [] };
|
|
264
256
|
}
|
|
265
257
|
|
|
258
|
+
// Brackets, separators and accessors too: inside an attribute value no grammar colours them, and they read as string otherwise
|
|
266
259
|
if (kind >= SyntaxKind.FirstPunctuation && kind <= SyntaxKind.LastPunctuation) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
return isOperator ? { type: SemanticTokenTypes.operator, modifiers: [] } : undefined;
|
|
260
|
+
return { type: SemanticTokenTypes.operator, modifiers: [] };
|
|
270
261
|
}
|
|
271
262
|
|
|
272
263
|
if (kind < SyntaxKind.FirstKeyword || kind > SyntaxKind.LastKeyword) {
|
|
@@ -1,27 +1,46 @@
|
|
|
1
1
|
import type { EmbeddedRegion, TextRange } from "@staticbolt/core";
|
|
2
2
|
import type * as ts from "typescript";
|
|
3
3
|
|
|
4
|
+
/** The code of a document's regions, with where everything landed in it. */
|
|
5
|
+
export interface RegionsCode {
|
|
6
|
+
/** The code itself. */
|
|
7
|
+
text: string;
|
|
8
|
+
|
|
9
|
+
/** Where each region's own text starts in it, in the order the regions came in. */
|
|
10
|
+
starts: number[];
|
|
11
|
+
|
|
12
|
+
/** The holes, as offsets into it. */
|
|
13
|
+
holes: TextRange[];
|
|
14
|
+
}
|
|
15
|
+
|
|
4
16
|
/**
|
|
5
|
-
* The text
|
|
6
|
-
*
|
|
17
|
+
* The regions' text alone, one region per line, so an edit outside them leaves the code untouched. Every line opens with a `;`,
|
|
18
|
+
* which keeps the line a statement of its own whatever the line before it ends with; an expression region is wrapped as `;(…)`,
|
|
19
|
+
* so an object literal reads as one and not as a block.
|
|
7
20
|
*/
|
|
8
|
-
export function
|
|
9
|
-
|
|
21
|
+
export function codeFromRegions(text: string, regions: readonly EmbeddedRegion[], holes: readonly TextRange[]): RegionsCode {
|
|
22
|
+
const lines: string[] = [];
|
|
23
|
+
const starts: number[] = [];
|
|
24
|
+
const moved: TextRange[] = [];
|
|
10
25
|
let cursor = 0;
|
|
11
26
|
|
|
12
27
|
for (const region of regions) {
|
|
13
|
-
|
|
14
|
-
|
|
28
|
+
const open = region.isExpression ? ";(" : ";";
|
|
29
|
+
const start = cursor + open.length;
|
|
30
|
+
const line = open + text.slice(region.start, region.end) + (region.isExpression ? ")" : "");
|
|
15
31
|
|
|
16
|
-
|
|
32
|
+
starts.push(start);
|
|
33
|
+
lines.push(line);
|
|
34
|
+
cursor += line.length + 1;
|
|
17
35
|
|
|
18
|
-
|
|
36
|
+
for (const hole of holes) {
|
|
37
|
+
if (hole.start < region.start || hole.end > region.end) continue;
|
|
19
38
|
|
|
20
|
-
|
|
21
|
-
|
|
39
|
+
moved.push({ start: hole.start - region.start + start, end: hole.end - region.start + start });
|
|
40
|
+
}
|
|
22
41
|
}
|
|
23
42
|
|
|
24
|
-
return
|
|
43
|
+
return { text: lines.join("\n"), starts, holes: moved };
|
|
25
44
|
}
|
|
26
45
|
|
|
27
46
|
/** The text with the regions blanked, keeping line breaks, so every offset means the same thing as in the text. */
|
package/src/language-plugin.ts
CHANGED
|
@@ -36,6 +36,10 @@ export function createLanguagePlugin(typescript: typeof ts, projects: Projects):
|
|
|
36
36
|
return new StaticboltCode(typescript, uri, languageId, snapshot, projects.of(uri.toString()));
|
|
37
37
|
},
|
|
38
38
|
|
|
39
|
+
updateVirtualCode(uri, previous, snapshot) {
|
|
40
|
+
return new StaticboltCode(typescript, uri, previous.languageId, snapshot, projects.of(uri.toString()), previous);
|
|
41
|
+
},
|
|
42
|
+
|
|
39
43
|
typescript: {
|
|
40
44
|
extraFileExtensions: [
|
|
41
45
|
{ extension: "html", isMixedContent: true, scriptKind: typescript.ScriptKind.Deferred },
|
|
@@ -5,7 +5,7 @@ import { isInRegions } from "../helpers/regions.ts";
|
|
|
5
5
|
import { createSyntaxTokenizer, SCOPED_TYPES } from "../helpers/syntax-tokens.ts";
|
|
6
6
|
import { embeddedFileName, StaticboltCode } from "../virtual-code.ts";
|
|
7
7
|
|
|
8
|
-
import type {
|
|
8
|
+
import type { TypeScriptCode } from "../virtual-code.ts";
|
|
9
9
|
import type { LanguageServiceContext, LanguageServicePlugin, SemanticToken } from "@volar/language-service";
|
|
10
10
|
import type * as ts from "typescript";
|
|
11
11
|
import type { TextDocument } from "vscode-languageserver-textdocument";
|
|
@@ -16,10 +16,10 @@ interface TypeScriptProvide {
|
|
|
16
16
|
"typescript/languageService": () => ts.LanguageService;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
/** An embedded document with the
|
|
19
|
+
/** An embedded document with the code it was made from, as TypeScript knows the document. */
|
|
20
20
|
interface Embedded {
|
|
21
|
-
/** The
|
|
22
|
-
|
|
21
|
+
/** The code, with its plugin language and its holes. */
|
|
22
|
+
code: TypeScriptCode;
|
|
23
23
|
|
|
24
24
|
/** The embedded document as a TypeScript file. */
|
|
25
25
|
fileName: string;
|
|
@@ -69,7 +69,7 @@ export function createSyntaxTokensService(typescript: typeof ts, isScoped: boole
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
const embedded = embeddedOf(context, document);
|
|
72
|
-
if (!embedded || embedded.
|
|
72
|
+
if (!embedded || embedded.code.language.isColouredByEditor) {
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -82,7 +82,7 @@ export function createSyntaxTokensService(typescript: typeof ts, isScoped: boole
|
|
|
82
82
|
|
|
83
83
|
// The masks standing in for other languages' code are not code to colour
|
|
84
84
|
const offset = document.offsetAt({ line: token.line, character: token.character });
|
|
85
|
-
if (isInRegions(embedded.
|
|
85
|
+
if (isInRegions(embedded.code.holes, offset)) continue;
|
|
86
86
|
|
|
87
87
|
let modifiers = 0;
|
|
88
88
|
|
|
@@ -103,7 +103,7 @@ export function createSyntaxTokensService(typescript: typeof ts, isScoped: boole
|
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
/** The
|
|
106
|
+
/** The code an embedded document was made from and its TypeScript file name, as `getExtraServiceScripts` names it. */
|
|
107
107
|
function embeddedOf(context: LanguageServiceContext, document: TextDocument): Embedded | undefined {
|
|
108
108
|
const decoded = context.decodeEmbeddedDocumentUri(URI.parse(document.uri));
|
|
109
109
|
if (!decoded) {
|
|
@@ -116,12 +116,12 @@ function embeddedOf(context: LanguageServiceContext, document: TextDocument): Em
|
|
|
116
116
|
return undefined;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
const
|
|
120
|
-
if (!
|
|
119
|
+
const code = root.codes.find(candidate => candidate.id === codeId);
|
|
120
|
+
if (!code) {
|
|
121
121
|
return undefined;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
const documentFileName = context.project.typescript?.uriConverter.asFileName(sourceUri) ?? sourceUri.fsPath;
|
|
125
125
|
|
|
126
|
-
return {
|
|
126
|
+
return { code, fileName: embeddedFileName(documentFileName, codeId) };
|
|
127
127
|
}
|
package/src/virtual-code.ts
CHANGED
|
@@ -7,12 +7,12 @@ import { parseElements } from "./helpers/document-elements.ts";
|
|
|
7
7
|
import { describeDocument } from "./helpers/document-info.ts";
|
|
8
8
|
import { findMarkdownNonHtmlRegions } from "./helpers/markdown-regions.ts";
|
|
9
9
|
import { findPluginRegions, groupByFile } from "./helpers/regions.ts";
|
|
10
|
-
import {
|
|
10
|
+
import { blankRegions, codeFromRegions, mask } from "./helpers/virtual-document.ts";
|
|
11
11
|
|
|
12
12
|
import type { LanguageRegions, PluginRegion } from "./helpers/regions.ts";
|
|
13
13
|
import type { Project } from "./projects.ts";
|
|
14
14
|
import type { CodeMapping, IScriptSnapshot, VirtualCode } from "@volar/language-core";
|
|
15
|
-
import type { DocumentInfo, EmbeddedRegion } from "@staticbolt/core";
|
|
15
|
+
import type { DocumentInfo, EmbeddedLanguage, EmbeddedRegion, TextRange } from "@staticbolt/core";
|
|
16
16
|
import type * as ts from "typescript";
|
|
17
17
|
import type { HTMLDocument, LanguageService } from "vscode-html-languageservice";
|
|
18
18
|
import type { URI } from "vscode-uri";
|
|
@@ -41,6 +41,18 @@ const ALL_FEATURES: CodeMapping["data"] = {
|
|
|
41
41
|
/** The HTML language service the codes parse with; no data provider, only the tree is wanted here. */
|
|
42
42
|
const htmlLanguageService: LanguageService = vscodeHtml.getLanguageService({ useDefaultDataProvider: false });
|
|
43
43
|
|
|
44
|
+
/** The TypeScript file one plugin language's regions are served through. */
|
|
45
|
+
export interface TypeScriptCode extends VirtualCode {
|
|
46
|
+
/** The plugin language whose regions it holds. */
|
|
47
|
+
language: EmbeddedLanguage;
|
|
48
|
+
|
|
49
|
+
/** The regions of other plugin languages inside its own, as offsets into its text. */
|
|
50
|
+
holes: TextRange[];
|
|
51
|
+
|
|
52
|
+
/** Its text, to tell a rebuild that changed nothing from one that did. */
|
|
53
|
+
text: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
44
56
|
/**
|
|
45
57
|
* A document as the server sees it: the HTML (a markdown document's with everything that cannot be HTML blanked out), the regions
|
|
46
58
|
* plugins embed in it, and an embedded TypeScript code per plugin language, served through the project's TypeScript.
|
|
@@ -76,8 +88,8 @@ export class StaticboltCode implements VirtualCode {
|
|
|
76
88
|
/** The regions the plugins embed, in text order. */
|
|
77
89
|
readonly regions: PluginRegion[];
|
|
78
90
|
|
|
79
|
-
/** The
|
|
80
|
-
readonly
|
|
91
|
+
/** The TypeScript file of every plugin language with regions. */
|
|
92
|
+
readonly codes: TypeScriptCode[];
|
|
81
93
|
|
|
82
94
|
/** The parsed HTML, on first use. */
|
|
83
95
|
#htmlDocument: HTMLDocument | undefined;
|
|
@@ -85,7 +97,14 @@ export class StaticboltCode implements VirtualCode {
|
|
|
85
97
|
/** The document as the plugins see it, on first use. */
|
|
86
98
|
#info: DocumentInfo | undefined;
|
|
87
99
|
|
|
88
|
-
constructor(
|
|
100
|
+
constructor(
|
|
101
|
+
typescript: typeof ts,
|
|
102
|
+
uri: URI,
|
|
103
|
+
languageId: string,
|
|
104
|
+
snapshot: IScriptSnapshot,
|
|
105
|
+
project: Project | undefined,
|
|
106
|
+
previous?: StaticboltCode
|
|
107
|
+
) {
|
|
89
108
|
const text = snapshot.getText(0, snapshot.getLength());
|
|
90
109
|
|
|
91
110
|
this.uri = uri;
|
|
@@ -96,12 +115,8 @@ export class StaticboltCode implements VirtualCode {
|
|
|
96
115
|
this.mappings = [identityMapping(text.length)];
|
|
97
116
|
this.html = languageId === "markdown" ? blankRegions(text, findMarkdownNonHtmlRegions(text)) : text;
|
|
98
117
|
this.regions = project ? findPluginRegions(this.info, project.embeddedLanguages) : [];
|
|
99
|
-
this.
|
|
100
|
-
this.embeddedCodes =
|
|
101
|
-
|
|
102
|
-
if (languageId === "markdown") {
|
|
103
|
-
this.embeddedCodes.unshift(createHtmlCode(typescript, this.html));
|
|
104
|
-
}
|
|
118
|
+
this.codes = groupByFile(this.regions).map(group => createTypeScriptCode(typescript, this.info, group, previous));
|
|
119
|
+
this.embeddedCodes = languageId === "markdown" ? [createHtmlCode(typescript, this.html), ...this.codes] : [...this.codes];
|
|
105
120
|
}
|
|
106
121
|
|
|
107
122
|
/** The parsed HTML. */
|
|
@@ -143,33 +158,47 @@ function createHtmlCode(typescript: typeof ts, html: string): VirtualCode {
|
|
|
143
158
|
}
|
|
144
159
|
|
|
145
160
|
/**
|
|
146
|
-
* The TypeScript code of one file of a plugin language: the
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
161
|
+
* The TypeScript code of one file of a plugin language: the file's regions one per line, the regions of other languages inside
|
|
162
|
+
* them masked, and the language's prelude appended at the end. A module is made one with an `export {}`, so its top level is its
|
|
163
|
+
* own; a script's top level is the global scope, as it is in the browser. The regions map back to the document; what comes before
|
|
164
|
+
* the first and after the last maps onto their edges, so what TypeScript puts at the top or the bottom of the file, an import or
|
|
165
|
+
* a declaration it adds say, lands in the document.
|
|
166
|
+
*
|
|
167
|
+
* Nothing but the regions is in the text, so an edit outside them leaves it as it was: the previous file's snapshot is then kept,
|
|
168
|
+
* and Volar, which versions a file by the identity of its snapshot, hands TypeScript the program it already has.
|
|
151
169
|
*/
|
|
152
|
-
function createTypeScriptCode(
|
|
170
|
+
function createTypeScriptCode(
|
|
171
|
+
typescript: typeof ts,
|
|
172
|
+
info: DocumentInfo,
|
|
173
|
+
group: LanguageRegions,
|
|
174
|
+
previous: StaticboltCode | undefined
|
|
175
|
+
): TypeScriptCode {
|
|
153
176
|
const { id, language, isModule, regions, holes } = group;
|
|
154
177
|
const prelude = typeof language.prelude === "function" ? language.prelude(info) : language.prelude;
|
|
155
178
|
const suffix = [isModule ? "export {};" : "", prelude ?? ""].filter(Boolean).join("\n");
|
|
156
|
-
const
|
|
179
|
+
const code = codeFromRegions(info.text, regions, holes);
|
|
180
|
+
const body = mask(typescript, code.text, code.holes);
|
|
181
|
+
const text = `${body}\n${suffix}\n`;
|
|
157
182
|
const first = regions[0];
|
|
158
183
|
const last = regions.at(-1) ?? first;
|
|
184
|
+
const before = previous?.codes.find(candidate => candidate.id === id);
|
|
159
185
|
|
|
160
186
|
return {
|
|
161
187
|
id,
|
|
162
188
|
languageId: "typescript",
|
|
163
|
-
|
|
189
|
+
language,
|
|
190
|
+
holes: code.holes,
|
|
191
|
+
text,
|
|
192
|
+
snapshot: before?.text === text ? before.snapshot : typescript.ScriptSnapshot.fromString(text),
|
|
164
193
|
mappings: [
|
|
165
194
|
{
|
|
166
195
|
sourceOffsets: regions.map(region => region.start),
|
|
167
|
-
generatedOffsets:
|
|
196
|
+
generatedOffsets: code.starts,
|
|
168
197
|
lengths: regions.map(region => region.end - region.start),
|
|
169
198
|
data: ALL_FEATURES,
|
|
170
199
|
},
|
|
171
|
-
edgeMapping(topOf(info.text, first), 0,
|
|
172
|
-
edgeMapping(last.end,
|
|
200
|
+
edgeMapping(topOf(info.text, first), 0, code.starts[0]),
|
|
201
|
+
edgeMapping(last.end, body.length, text.length - body.length),
|
|
173
202
|
],
|
|
174
203
|
};
|
|
175
204
|
}
|