@staticbolt/lsp 1.0.0-beta.25 → 1.0.0-beta.26
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 +205 -29
- package/lib/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/helpers/config-loader.ts +5 -2
- package/src/helpers/merge-html-data.ts +253 -0
- package/src/index.ts +14 -2
- package/src/modes/html-mode.ts +17 -33
package/lib/index.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { format, stripVTControlCharacters } from "node:util";
|
|
1
2
|
import * as vs from "vscode-languageserver/node";
|
|
2
3
|
import * as vscode$1 from "vscode-languageserver";
|
|
3
4
|
import vscode, { RequestType } from "vscode-languageserver";
|
|
@@ -121,7 +122,7 @@ var ConfigManager = class {
|
|
|
121
122
|
for (const configLoader of this.configs.values()) configLoader.dispose();
|
|
122
123
|
}
|
|
123
124
|
async collectData(config) {
|
|
124
|
-
|
|
125
|
+
const collected = [];
|
|
125
126
|
const plugins = config.plugins ?? [];
|
|
126
127
|
for (const pluginOrArray of plugins) {
|
|
127
128
|
const plugins = Array.isArray(pluginOrArray) ? pluginOrArray : [pluginOrArray];
|
|
@@ -130,10 +131,11 @@ var ConfigManager = class {
|
|
|
130
131
|
const htmlData = await plugin.lspHtmlData();
|
|
131
132
|
if (htmlData) {
|
|
132
133
|
htmlDataInjectPluginName(htmlData, plugin.name);
|
|
133
|
-
|
|
134
|
+
collected.push(htmlData);
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
}
|
|
138
|
+
this.lspHtmlData = collected;
|
|
137
139
|
}
|
|
138
140
|
};
|
|
139
141
|
function htmlDataInjectPluginName(htmlData, pluginName) {
|
|
@@ -439,23 +441,210 @@ function getAttributeLanguage(attributeName) {
|
|
|
439
441
|
return match[1] ? "css" : "javascript";
|
|
440
442
|
}
|
|
441
443
|
|
|
444
|
+
//#endregion
|
|
445
|
+
//#region src/helpers/merge-html-data.ts
|
|
446
|
+
/** Separates the documentation of two contributors. Rendered as a horizontal rule, unless the description is plain text. */
|
|
447
|
+
const MARKDOWN_SEPARATOR = "\n\n---\n\n";
|
|
448
|
+
const PLAINTEXT_SEPARATOR = "\n\n";
|
|
449
|
+
/** Groups entries by key, preserving the order of first appearance. */
|
|
450
|
+
function groupBy(items, toKey) {
|
|
451
|
+
const groups = /* @__PURE__ */ new Map();
|
|
452
|
+
const ordered = [];
|
|
453
|
+
for (const item of items) {
|
|
454
|
+
const key = toKey(item);
|
|
455
|
+
let group = groups.get(key);
|
|
456
|
+
if (!group) {
|
|
457
|
+
group = [];
|
|
458
|
+
groups.set(key, group);
|
|
459
|
+
ordered.push(group);
|
|
460
|
+
}
|
|
461
|
+
group.push(item);
|
|
462
|
+
}
|
|
463
|
+
return ordered;
|
|
464
|
+
}
|
|
465
|
+
/** Markdown wins over plain text, so a contributor asking for rich text still gets it. Plain strings imply no preference. */
|
|
466
|
+
function mergeDescriptionKind(descriptions) {
|
|
467
|
+
let kind;
|
|
468
|
+
for (const description of descriptions) {
|
|
469
|
+
if (typeof description === "string" || kind === "markdown") continue;
|
|
470
|
+
kind = description.kind;
|
|
471
|
+
}
|
|
472
|
+
return kind;
|
|
473
|
+
}
|
|
474
|
+
/** Concatenates every distinct description, so no contributor's documentation gets lost. */
|
|
475
|
+
function mergeDescriptions(descriptions) {
|
|
476
|
+
const defined = descriptions.filter((description) => description !== void 0);
|
|
477
|
+
if (defined.length <= 1) return defined[0];
|
|
478
|
+
const parts = [];
|
|
479
|
+
const seen = /* @__PURE__ */ new Set();
|
|
480
|
+
for (const description of defined) {
|
|
481
|
+
const text = (typeof description === "string" ? description : description.value).trim();
|
|
482
|
+
if (!text || seen.has(text)) continue;
|
|
483
|
+
seen.add(text);
|
|
484
|
+
parts.push(text);
|
|
485
|
+
}
|
|
486
|
+
if (parts.length === 0) return void 0;
|
|
487
|
+
const kind = mergeDescriptionKind(defined);
|
|
488
|
+
const value = parts.join(kind === "plaintext" ? PLAINTEXT_SEPARATOR : MARKDOWN_SEPARATOR);
|
|
489
|
+
return kind ? {
|
|
490
|
+
kind,
|
|
491
|
+
value
|
|
492
|
+
} : value;
|
|
493
|
+
}
|
|
494
|
+
function mergeReferences(references) {
|
|
495
|
+
const merged = [];
|
|
496
|
+
const seen = /* @__PURE__ */ new Set();
|
|
497
|
+
for (const list of references) {
|
|
498
|
+
const entries = list ?? [];
|
|
499
|
+
for (const reference of entries) {
|
|
500
|
+
const key = `${reference.name} ${reference.url}`;
|
|
501
|
+
if (seen.has(key)) continue;
|
|
502
|
+
seen.add(key);
|
|
503
|
+
merged.push(reference);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return merged.length > 0 ? merged : void 0;
|
|
507
|
+
}
|
|
508
|
+
function mergeBrowsers(browsers) {
|
|
509
|
+
const merged = /* @__PURE__ */ new Set();
|
|
510
|
+
for (const list of browsers) {
|
|
511
|
+
const entries = list ?? [];
|
|
512
|
+
for (const browser of entries) merged.add(browser);
|
|
513
|
+
}
|
|
514
|
+
return merged.size > 0 ? [...merged] : void 0;
|
|
515
|
+
}
|
|
516
|
+
/** The values an attribute contributes, with its `valueSet` reference expanded. */
|
|
517
|
+
function resolveValues(attribute, valueSets) {
|
|
518
|
+
const values = attribute.values ?? [];
|
|
519
|
+
if (!attribute.valueSet) return values;
|
|
520
|
+
return [...valueSets.get(attribute.valueSet) ?? [], ...values];
|
|
521
|
+
}
|
|
522
|
+
/** Merges entries sharing the same value name into one. Unlike tags and attributes, value names are case-sensitive. */
|
|
523
|
+
function mergeValues(values) {
|
|
524
|
+
return groupBy(values, (value) => value.name).map((group) => {
|
|
525
|
+
if (group.length === 1) return group[0];
|
|
526
|
+
return {
|
|
527
|
+
name: group[0].name,
|
|
528
|
+
description: mergeDescriptions(group.map((value) => value.description)),
|
|
529
|
+
references: mergeReferences(group.map((value) => value.references)),
|
|
530
|
+
browsers: mergeBrowsers(group.map((value) => value.browsers)),
|
|
531
|
+
status: group.find((value) => value.status)?.status
|
|
532
|
+
};
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Merges entries sharing the same attribute name into one, combining their descriptions, values, references and browser support.
|
|
537
|
+
*
|
|
538
|
+
* `valueSet` references are expanded into the merged `values`, so attributes contributed by different plugins can each bring
|
|
539
|
+
* their own value set and still end up with a single, complete value list.
|
|
540
|
+
*/
|
|
541
|
+
function mergeAttributes(attributes, valueSets) {
|
|
542
|
+
return groupBy(attributes, (attribute) => attribute.name.toLowerCase()).map((group) => {
|
|
543
|
+
if (group.length === 1 && !group[0].valueSet) return group[0];
|
|
544
|
+
const values = mergeValues(group.flatMap((attribute) => resolveValues(attribute, valueSets)));
|
|
545
|
+
return {
|
|
546
|
+
name: group[0].name,
|
|
547
|
+
description: mergeDescriptions(group.map((attribute) => attribute.description)),
|
|
548
|
+
values: values.length > 0 ? values : void 0,
|
|
549
|
+
references: mergeReferences(group.map((attribute) => attribute.references)),
|
|
550
|
+
browsers: mergeBrowsers(group.map((attribute) => attribute.browsers)),
|
|
551
|
+
status: group.find((attribute) => attribute.status)?.status
|
|
552
|
+
};
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
/** Merges entries sharing the same tag name into one, including their attributes. */
|
|
556
|
+
function mergeTags(tags, valueSets) {
|
|
557
|
+
return groupBy(tags, (tag) => tag.name.toLowerCase()).map((group) => {
|
|
558
|
+
const attributes = mergeAttributes(group.flatMap((tag) => tag.attributes ?? []), valueSets);
|
|
559
|
+
if (group.length === 1) return {
|
|
560
|
+
...group[0],
|
|
561
|
+
attributes
|
|
562
|
+
};
|
|
563
|
+
return {
|
|
564
|
+
name: group[0].name,
|
|
565
|
+
description: mergeDescriptions(group.map((tag) => tag.description)),
|
|
566
|
+
attributes,
|
|
567
|
+
references: mergeReferences(group.map((tag) => tag.references)),
|
|
568
|
+
browsers: mergeBrowsers(group.map((tag) => tag.browsers)),
|
|
569
|
+
status: group.find((tag) => tag.status)?.status,
|
|
570
|
+
void: group.some((tag) => tag.void)
|
|
571
|
+
};
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
/** Value sets sharing a name are merged, so an attribute referencing one gets the values of every contributor. */
|
|
575
|
+
function collectValueSets(htmlData) {
|
|
576
|
+
const valueSets = /* @__PURE__ */ new Map();
|
|
577
|
+
const collected = htmlData.flatMap((data) => data.valueSets ?? []);
|
|
578
|
+
for (const valueSet of collected) {
|
|
579
|
+
const existing = valueSets.get(valueSet.name);
|
|
580
|
+
valueSets.set(valueSet.name, existing ? mergeValues([...existing, ...valueSet.values]) : valueSet.values);
|
|
581
|
+
}
|
|
582
|
+
return valueSets;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Builds a single data provider out of every collected `HTMLDataV1`, merging tags, attributes and values that share the same name
|
|
586
|
+
* instead of reporting them once per contributor.
|
|
587
|
+
*/
|
|
588
|
+
function createMergedHtmlDataProvider(id, htmlData) {
|
|
589
|
+
const valueSets = collectValueSets(htmlData);
|
|
590
|
+
const tags = mergeTags(htmlData.flatMap((data) => data.tags ?? []), valueSets);
|
|
591
|
+
const globalAttributes = mergeAttributes(htmlData.flatMap((data) => data.globalAttributes ?? []), valueSets);
|
|
592
|
+
const tagsByName = new Map(tags.map((tag) => [tag.name.toLowerCase(), tag]));
|
|
593
|
+
const attributesByTag = /* @__PURE__ */ new Map();
|
|
594
|
+
function provideAttributes(tag) {
|
|
595
|
+
const key = tag.toLowerCase();
|
|
596
|
+
const cached = attributesByTag.get(key);
|
|
597
|
+
if (cached) return cached;
|
|
598
|
+
const tagAttributes = tagsByName.get(key)?.attributes;
|
|
599
|
+
if (!tagAttributes || tagAttributes.length === 0) return globalAttributes;
|
|
600
|
+
const attributes = mergeAttributes([...globalAttributes, ...tagAttributes], valueSets);
|
|
601
|
+
attributesByTag.set(key, attributes);
|
|
602
|
+
return attributes;
|
|
603
|
+
}
|
|
604
|
+
return {
|
|
605
|
+
getId() {
|
|
606
|
+
return id;
|
|
607
|
+
},
|
|
608
|
+
isApplicable(languageId) {
|
|
609
|
+
return languageId === "html";
|
|
610
|
+
},
|
|
611
|
+
provideTags() {
|
|
612
|
+
return tags;
|
|
613
|
+
},
|
|
614
|
+
provideAttributes,
|
|
615
|
+
provideValues(tag, attribute) {
|
|
616
|
+
const name = attribute.toLowerCase();
|
|
617
|
+
return provideAttributes(tag).find((a) => a.name.toLowerCase() === name)?.values ?? [];
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
|
|
442
622
|
//#endregion
|
|
443
623
|
//#region src/modes/html-mode.ts
|
|
444
624
|
function getHTMLMode(htmlLanguageService) {
|
|
445
625
|
const htmlDocuments = getLanguageModelCache(10, 60, (document) => htmlLanguageService.parseHTMLDocument(document));
|
|
626
|
+
let lastHtmlData;
|
|
627
|
+
let lastProvider;
|
|
628
|
+
function setHtmlDataProviders(htmlData) {
|
|
629
|
+
if (!lastProvider || lastHtmlData !== htmlData) {
|
|
630
|
+
lastHtmlData = htmlData;
|
|
631
|
+
lastProvider = createMergedHtmlDataProvider(FILE_PROTOCOL, htmlData);
|
|
632
|
+
}
|
|
633
|
+
htmlLanguageService.setDataProviders(false, [lastProvider]);
|
|
634
|
+
}
|
|
446
635
|
return {
|
|
447
636
|
getId() {
|
|
448
637
|
return "html";
|
|
449
638
|
},
|
|
450
639
|
async doComplete(document, position, documentContext, htmlData) {
|
|
451
|
-
setHtmlDataProviders(
|
|
640
|
+
setHtmlDataProviders(htmlData);
|
|
452
641
|
const htmlDocument = htmlDocuments.get(document);
|
|
453
642
|
const completionList = await htmlLanguageService.doComplete2(document, position, htmlDocument, documentContext);
|
|
454
643
|
for (const item of completionList.items) item.sortText = "0_" + item.label;
|
|
455
644
|
return completionList;
|
|
456
645
|
},
|
|
457
646
|
async doHover(document, position, htmlData) {
|
|
458
|
-
setHtmlDataProviders(
|
|
647
|
+
setHtmlDataProviders(htmlData);
|
|
459
648
|
return htmlLanguageService.doHover(document, position, htmlDocuments.get(document));
|
|
460
649
|
},
|
|
461
650
|
async onDocumentRemoved(document) {
|
|
@@ -480,29 +669,6 @@ function getHTMLMode(htmlLanguageService) {
|
|
|
480
669
|
}
|
|
481
670
|
};
|
|
482
671
|
}
|
|
483
|
-
function setHtmlDataProviders(htmlLanguageService, htmlData) {
|
|
484
|
-
htmlLanguageService.setDataProviders(false, [{
|
|
485
|
-
getId() {
|
|
486
|
-
return FILE_PROTOCOL;
|
|
487
|
-
},
|
|
488
|
-
isApplicable(languageId) {
|
|
489
|
-
return languageId === "html";
|
|
490
|
-
},
|
|
491
|
-
provideValues(tag, attribute) {
|
|
492
|
-
const attributes = htmlData.flatMap((data) => data.tags?.filter((t) => t.name === tag) ?? []).flatMap((t) => t.attributes?.filter((a) => a.name === attribute) ?? []);
|
|
493
|
-
return [...htmlData.flatMap((data) => data.globalAttributes?.filter((a) => a.name === attribute) ?? []), ...attributes].flatMap((a) => a.values ?? []);
|
|
494
|
-
},
|
|
495
|
-
provideTags() {
|
|
496
|
-
return htmlData.flatMap((data) => data.tags ?? []);
|
|
497
|
-
},
|
|
498
|
-
provideAttributes(tag) {
|
|
499
|
-
const tags = htmlData.flatMap((data) => data.tags?.filter((t) => t.name === tag) ?? []);
|
|
500
|
-
const globalAttributes = htmlData.flatMap((data) => data.globalAttributes ?? []);
|
|
501
|
-
const attributes = tags.flatMap((t) => t.attributes ?? []);
|
|
502
|
-
return [...globalAttributes, ...attributes];
|
|
503
|
-
}
|
|
504
|
-
}]);
|
|
505
|
-
}
|
|
506
672
|
|
|
507
673
|
//#endregion
|
|
508
674
|
//#region src/modes/language-modes.ts
|
|
@@ -882,8 +1048,18 @@ function getNodeFileFS() {
|
|
|
882
1048
|
//#endregion
|
|
883
1049
|
//#region src/index.ts
|
|
884
1050
|
const connection = vs.createConnection();
|
|
885
|
-
|
|
886
|
-
console
|
|
1051
|
+
/**
|
|
1052
|
+
* `RemoteConsole` takes a single string, but the shared logger calls `console` with several arguments and colours them with
|
|
1053
|
+
* chalk. Passing the methods straight through drops everything after the first argument, and the output panel renders no ANSI —
|
|
1054
|
+
* so format the arguments the way `console` would, then strip the escapes.
|
|
1055
|
+
*/
|
|
1056
|
+
function forward(write) {
|
|
1057
|
+
return (...messages) => write(stripVTControlCharacters(format(...messages)));
|
|
1058
|
+
}
|
|
1059
|
+
console.log = forward(connection.console.log.bind(connection.console));
|
|
1060
|
+
console.info = forward(connection.console.info.bind(connection.console));
|
|
1061
|
+
console.warn = forward(connection.console.warn.bind(connection.console));
|
|
1062
|
+
console.error = forward(connection.console.error.bind(connection.console));
|
|
887
1063
|
process.on("unhandledRejection", (error) => {
|
|
888
1064
|
connection.console.error(formatError(`Unhandled exception`, error));
|
|
889
1065
|
});
|
package/lib/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["path","FileType","path","vscode"],"sources":["../src/helpers/config-loader.ts","../src/helpers/find-projects.ts","../src/language-model-cache.ts","../src/modes/embedded-support.ts","../src/modes/html-mode.ts","../src/modes/language-modes.ts","../src/requests.ts","../src/utils/arrays.ts","../src/utils/strings.ts","../src/utils/document-context.ts","../src/utils/find-project-root.ts","../src/utils/runner.ts","../src/html-server.ts","../src/utils/node-fs.ts","../src/index.ts"],"sourcesContent":["import { EventEmitter } from \"node:events\";\nimport { watch, type FSWatcher } from \"node:fs\";\nimport { access, constants } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nimport type { AppConfig } from \"@staticbolt/core\";\nimport type { HTMLDataV1 } from \"vscode-html-languageservice\";\nimport type { MarkupContent, RemoteConsole } from \"vscode-languageserver\";\n\nexport type StaticBoltConfig = AppConfig;\n\nexport interface ConfigLoaderEvents {\n change: [config: StaticBoltConfig];\n error: [err: Error];\n}\n\nconst CONFIG_CANDIDATES = [\".staticbolt.ts\", \".staticbolt.js\"] as const;\n\nasync function findConfigFile(workspaceRoot: string): Promise<string | null> {\n for (const name of CONFIG_CANDIDATES) {\n const full = join(workspaceRoot, name);\n\n try {\n await access(full, constants.R_OK);\n return full;\n } catch {\n // not found or not readable — try next\n }\n }\n\n return null;\n}\n\nasync function importFresh(absolutePath: string): Promise<StaticBoltConfig> {\n const url = `${pathToFileURL(absolutePath).href}?t=${Date.now()}`;\n const module_ = (await import(url)) as { default: StaticBoltConfig };\n\n if (!(\"default\" in module_)) {\n throw new Error(`Config file \"${absolutePath}\" has no default export. Use \\`export default { … }\\`.`);\n }\n\n return module_.default;\n}\n\n// eslint-disable-next-line unicorn/prefer-event-target\nexport class ConfigLoader extends EventEmitter<ConfigLoaderEvents> {\n private readonly workspaceRoot: string;\n private configPath: string | null = null;\n private watcher: FSWatcher | null = null;\n private current: StaticBoltConfig | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly DEBOUNCE_MS = 150;\n\n constructor(workspaceRoot: string) {\n super();\n this.workspaceRoot = workspaceRoot;\n }\n\n /** Start watching. Resolves with the initial config (or null if absent). */\n async start(): Promise<StaticBoltConfig | null> {\n this.configPath = await findConfigFile(this.workspaceRoot);\n\n if (!this.configPath) {\n return null;\n }\n\n await this.load();\n this.watch(this.configPath);\n return this.current;\n }\n\n /** Stop watching and release all resources. */\n dispose(): void {\n if (this.debounceTimer !== null) {\n clearTimeout(this.debounceTimer);\n }\n this.watcher?.close();\n this.watcher = null;\n }\n\n /** The last successfully loaded config, or null if none loaded yet. */\n get config(): StaticBoltConfig | null {\n return this.current;\n }\n\n private async load(): Promise<void> {\n if (!this.configPath) return;\n\n try {\n this.current = await importFresh(this.configPath);\n this.emit(\"change\", this.current);\n } catch (error) {\n this.emit(\"error\", error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n private watch(configPath: string): void {\n this.watcher = watch(configPath, { persistent: false }, event => {\n if (event === \"rename\" || event === \"change\") {\n this.scheduleReload();\n }\n });\n\n this.watcher.on(\"error\", error => this.emit(\"error\", error));\n }\n\n private scheduleReload(): void {\n if (this.debounceTimer !== null) {\n clearTimeout(this.debounceTimer);\n }\n\n this.debounceTimer = setTimeout(async () => {\n this.debounceTimer = null;\n\n // The file might have been atomically replaced (Vim, VS Code safe-write).\n // Re-check existence; if it disappeared we just bail.\n if (!this.configPath) return;\n\n try {\n await access(this.configPath, constants.R_OK);\n } catch {\n this.emit(\"error\", new Error(`Config file was removed: ${this.configPath}`));\n return;\n }\n\n await this.load();\n }, this.DEBOUNCE_MS);\n }\n}\n\nexport class ConfigManager {\n private readonly console: RemoteConsole;\n readonly configs = new Map<string, ConfigLoader>();\n lspHtmlData: HTMLDataV1[] = [];\n\n constructor(console: RemoteConsole) {\n this.console = console;\n }\n\n async get(workspaceRoot: string): Promise<AppConfig | null> {\n const configLoader = this.configs.get(workspaceRoot);\n if (configLoader) {\n if (!configLoader.config) {\n this.configs.delete(workspaceRoot);\n return null;\n }\n\n return configLoader.config;\n }\n\n const newConfigLoader = new ConfigLoader(workspaceRoot);\n this.configs.set(workspaceRoot, newConfigLoader);\n\n newConfigLoader.on(\"error\", error => {\n this.console.error(`Failed to load config file: ${error.message}`);\n });\n\n newConfigLoader.on(\"change\", async config => {\n await this.collectData(config);\n });\n\n await newConfigLoader.start();\n\n if (!newConfigLoader.config) {\n return null;\n }\n\n return newConfigLoader.config;\n }\n\n dispose(): void {\n for (const configLoader of this.configs.values()) {\n configLoader.dispose();\n }\n }\n\n private async collectData(config: StaticBoltConfig) {\n this.lspHtmlData = [];\n\n const plugins = config.plugins ?? [];\n\n for (const pluginOrArray of plugins) {\n const plugins = Array.isArray(pluginOrArray) ? pluginOrArray : [pluginOrArray];\n\n for (const plugin of plugins) {\n if (!plugin.lspHtmlData) {\n continue;\n }\n\n const htmlData = await plugin.lspHtmlData();\n if (htmlData) {\n htmlDataInjectPluginName(htmlData, plugin.name);\n this.lspHtmlData.push(htmlData);\n }\n }\n }\n }\n}\n\nfunction htmlDataInjectPluginName(htmlData: HTMLDataV1, pluginName: string) {\n const tags = htmlData.tags ?? [];\n for (const tag of tags) {\n tag.description = replaceDescription(tag.description, pluginName);\n\n const attributes = tag.attributes ?? [];\n for (const attribute of attributes) {\n attribute.description = replaceDescription(attribute.description, pluginName);\n }\n }\n\n const globalAttributes = htmlData.globalAttributes ?? [];\n for (const attribute of globalAttributes) {\n attribute.description = replaceDescription(attribute.description, pluginName);\n }\n}\n\nfunction replaceDescription(description: string | MarkupContent | undefined, pluginName: string) {\n const info = `_Provided by **staticbolt** \\`${pluginName}\\` plugin._`;\n\n if (!description) {\n return info;\n }\n\n if (typeof description === \"string\") {\n if (description.includes(info)) {\n return description;\n }\n\n return `${description}\\n\\n${info}`;\n }\n\n if (description.value.includes(info)) {\n return description;\n }\n\n description.value = `${description.value}\\n\\n${info}`;\n return description;\n}\n","import { globSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport type { WorkspaceFolder } from \"vscode-languageserver\";\n\nexport function findStaticboltProjects(searchRoots: WorkspaceFolder[]): WorkspaceFolder[] {\n const results: WorkspaceFolder[] = [];\n\n for (const root of searchRoots) {\n const rootPath = vscodeUri.URI.parse(root.uri).fsPath;\n\n const configs = globSync(\"**/.staticbolt.{ts,js}\", { cwd: rootPath, exclude: [\"**/node_modules/**\", \"**/.git/**\"] });\n\n for (const configPath of configs) {\n const absDirectory = path.join(rootPath, path.dirname(configPath));\n const projectUri = vscodeUri.URI.file(absDirectory).toString();\n results.push({ name: path.basename(absDirectory), uri: projectUri });\n }\n }\n\n return results;\n}\n","import type { TextDocument } from \"vscode-html-languageservice\";\n\nexport interface LanguageModelCache<T> {\n get(document: TextDocument): T;\n onDocumentRemoved(document: TextDocument): void;\n dispose(): void;\n}\n\nexport function getLanguageModelCache<T>(\n maxEntries: number,\n cleanupIntervalTimeInSec: number,\n parse: (document: TextDocument) => T\n): LanguageModelCache<T> {\n let languageModels: { [uri: string]: { version: number; languageId: string; cTime: number; languageModel: T } } = {};\n let nModels = 0;\n\n let cleanupInterval: NodeJS.Timeout | undefined;\n\n if (cleanupIntervalTimeInSec > 0) {\n cleanupInterval = setInterval(() => {\n const cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000;\n const uris = Object.keys(languageModels);\n\n for (const uri of uris) {\n const languageModelInfo = languageModels[uri];\n if (languageModelInfo.cTime < cutoffTime) {\n delete languageModels[uri];\n nModels--;\n }\n }\n }, cleanupIntervalTimeInSec * 1000);\n }\n\n return {\n get(document: TextDocument): T {\n const version = document.version;\n const languageId = document.languageId;\n const languageModelInfo = languageModels[document.uri];\n\n if (languageModelInfo && languageModelInfo.version === version && languageModelInfo.languageId === languageId) {\n languageModelInfo.cTime = Date.now();\n return languageModelInfo.languageModel;\n }\n\n const languageModel = parse(document);\n languageModels[document.uri] = { languageModel, version, languageId, cTime: Date.now() };\n if (!languageModelInfo) {\n nModels++;\n }\n\n if (nModels === maxEntries) {\n let oldestTime = Number.MAX_VALUE;\n let oldestUri = null;\n\n for (const uri in languageModels) {\n const languageModelInfo = languageModels[uri];\n if (languageModelInfo.cTime < oldestTime) {\n oldestUri = uri;\n oldestTime = languageModelInfo.cTime;\n }\n }\n\n if (oldestUri) {\n delete languageModels[oldestUri];\n nModels--;\n }\n }\n\n return languageModel;\n },\n\n onDocumentRemoved(document: TextDocument) {\n const uri = document.uri;\n if (Object.hasOwn(languageModels, uri)) {\n delete languageModels[uri];\n nModels--;\n }\n },\n\n dispose() {\n if (cleanupInterval === undefined) {\n return;\n }\n\n clearInterval(cleanupInterval);\n cleanupInterval = undefined;\n languageModels = {};\n nModels = 0;\n },\n };\n}\n","import vscodeHtml from \"vscode-html-languageservice\";\nimport { TextDocument } from \"vscode-languageserver-textdocument\";\n\nimport type { LanguageService, Range } from \"vscode-html-languageservice\";\n\nexport interface LanguageRange extends Range {\n languageId: string | undefined;\n attributeValue?: boolean;\n}\n\nexport interface HTMLDocumentRegions {\n getEmbeddedDocument(languageId: string, shouldIgnoreAttributeValues?: boolean): TextDocument;\n getLanguageRanges(range: Range): LanguageRange[];\n getLanguageAtPosition(position: Position): string | undefined;\n getLanguagesInDocument(): string[];\n getImportedScripts(): string[];\n}\n\nconst CSS_STYLE_RULE = \"__\";\nconst TokenType = vscodeHtml.TokenType;\nconst Position = vscodeHtml.Position;\ntype Position = vscodeHtml.Position;\n\ninterface EmbeddedRegion {\n languageId: string | undefined;\n start: number;\n end: number;\n attributeValue?: boolean;\n}\n\nexport function getDocumentRegions(languageService: LanguageService, document: TextDocument): HTMLDocumentRegions {\n const regions: EmbeddedRegion[] = [];\n const scanner = languageService.createScanner(document.getText());\n let lastTagName: string = \"\";\n let lastAttributeName: string | null = null;\n let languageIdFromType: string | undefined;\n const importedScripts: string[] = [];\n\n let token = scanner.scan();\n\n while (token !== TokenType.EOS) {\n switch (token) {\n case TokenType.StartTag: {\n lastTagName = scanner.getTokenText();\n lastAttributeName = null;\n languageIdFromType = \"javascript\";\n break;\n }\n case TokenType.Styles: {\n regions.push({ languageId: \"css\", start: scanner.getTokenOffset(), end: scanner.getTokenEnd() });\n break;\n }\n case TokenType.Script: {\n regions.push({ languageId: languageIdFromType, start: scanner.getTokenOffset(), end: scanner.getTokenEnd() });\n break;\n }\n case TokenType.AttributeName: {\n lastAttributeName = scanner.getTokenText();\n break;\n }\n case TokenType.AttributeValue: {\n if (lastAttributeName === \"src\" && lastTagName.toLowerCase() === \"script\") {\n let value = scanner.getTokenText();\n if (value[0] === \"'\" || value[0] === '\"') {\n value = value.slice(1, -1);\n }\n importedScripts.push(value);\n } else if (lastAttributeName === \"type\" && lastTagName.toLowerCase() === \"script\") {\n const token = scanner.getTokenText();\n if (/[\"'](module|(text|application)\\/(java|ecma)script|text\\/babel)[\"']/.test(token) || token === \"module\") {\n languageIdFromType = \"javascript\";\n } else if (/[\"']text\\/typescript[\"']/.test(token)) {\n languageIdFromType = \"typescript\";\n } else {\n languageIdFromType = undefined;\n }\n } else {\n const attributeLanguageId = getAttributeLanguage(lastAttributeName!);\n if (attributeLanguageId) {\n let start = scanner.getTokenOffset();\n let end = scanner.getTokenEnd();\n const firstChar = document.getText()[start];\n if (firstChar === \"'\" || firstChar === '\"') {\n start++;\n end--;\n }\n regions.push({ languageId: attributeLanguageId, start, end, attributeValue: true });\n }\n }\n lastAttributeName = null;\n break;\n }\n }\n token = scanner.scan();\n }\n return {\n getLanguageRanges: (range: Range) => getLanguageRanges(document, regions, range),\n getEmbeddedDocument: (languageId: string, shouldIgnoreAttributeValues: boolean) =>\n getEmbeddedDocument(document, regions, languageId, shouldIgnoreAttributeValues),\n getLanguageAtPosition: (position: Position) => getLanguageAtPosition(document, regions, position),\n getLanguagesInDocument: () => getLanguagesInDocument(document, regions),\n getImportedScripts: () => importedScripts,\n };\n}\n\nfunction getLanguageRanges(document: TextDocument, regions: EmbeddedRegion[], range: Range): LanguageRange[] {\n const result: LanguageRange[] = [];\n let currentPos = range ? range.start : Position.create(0, 0);\n let currentOffset = range ? document.offsetAt(range.start) : 0;\n const endOffset = range ? document.offsetAt(range.end) : document.getText().length;\n for (const region of regions) {\n if (!(region.end > currentOffset && region.start < endOffset)) {\n continue;\n }\n\n const start = Math.max(region.start, currentOffset);\n const startPos = document.positionAt(start);\n if (currentOffset < region.start) {\n result.push({\n start: currentPos,\n end: startPos,\n languageId: \"html\",\n });\n }\n const end = Math.min(region.end, endOffset);\n const endPos = document.positionAt(end);\n if (end > region.start) {\n result.push({\n start: startPos,\n end: endPos,\n languageId: region.languageId,\n attributeValue: region.attributeValue,\n });\n }\n currentOffset = end;\n currentPos = endPos;\n }\n if (currentOffset < endOffset) {\n const endPos = range ? range.end : document.positionAt(endOffset);\n result.push({\n start: currentPos,\n end: endPos,\n languageId: \"html\",\n });\n }\n return result;\n}\n\nfunction getLanguagesInDocument(_document: TextDocument, regions: EmbeddedRegion[]): string[] {\n const result: string[] = [];\n\n for (const region of regions) {\n if (!(region.languageId && !result.includes(region.languageId))) {\n continue;\n }\n\n result.push(region.languageId);\n if (result.length === 3) {\n return result;\n }\n }\n\n result.push(\"html\");\n\n return result;\n}\n\nfunction getLanguageAtPosition(document: TextDocument, regions: EmbeddedRegion[], position: Position): string | undefined {\n const offset = document.offsetAt(position);\n for (const region of regions) {\n if (region.start <= offset) {\n if (offset <= region.end) {\n return region.languageId;\n }\n } else {\n break;\n }\n }\n return \"html\";\n}\n\nfunction getEmbeddedDocument(\n document: TextDocument,\n contents: EmbeddedRegion[],\n languageId: string,\n shouldIgnoreAttributeValues: boolean\n): TextDocument {\n let currentPos = 0;\n const oldContent = document.getText();\n let result = \"\";\n let lastSuffix = \"\";\n for (const c of contents) {\n if (!(c.languageId === languageId && (!shouldIgnoreAttributeValues || !c.attributeValue))) {\n continue;\n }\n\n result = substituteWithWhitespace(result, currentPos, c.start, oldContent, lastSuffix, getPrefix(c));\n result += updateContent(c, oldContent.slice(c.start, c.end));\n currentPos = c.end;\n lastSuffix = getSuffix(c);\n }\n result = substituteWithWhitespace(result, currentPos, oldContent.length, oldContent, lastSuffix, \"\");\n return TextDocument.create(document.uri, languageId, document.version, result);\n}\n\nfunction getPrefix(c: EmbeddedRegion) {\n if (c.attributeValue) {\n switch (c.languageId) {\n case \"css\": {\n return CSS_STYLE_RULE + \"{\";\n }\n }\n }\n return \"\";\n}\nfunction getSuffix(c: EmbeddedRegion) {\n if (c.attributeValue) {\n switch (c.languageId) {\n case \"css\": {\n return \"}\";\n }\n case \"javascript\": {\n return \";\";\n }\n }\n }\n return \"\";\n}\nfunction updateContent(c: EmbeddedRegion, content: string): string {\n if (!c.attributeValue && c.languageId === \"javascript\") {\n return content.replace(`<!--`, `/* `).replace(`-->`, ` */`);\n }\n if (c.languageId === \"css\") {\n const quoteEscape = /("|")/g;\n return content.replace(quoteEscape, (match, _, offset: number) => {\n const spaces = \" \".repeat(match.length - 1);\n const afterChar = content[offset + match.length];\n if (!afterChar || afterChar.includes(\" \")) {\n return `${spaces}\"`;\n }\n return `\"${spaces}`;\n });\n }\n return content;\n}\n\nfunction substituteWithWhitespace(result: string, start: number, end: number, oldContent: string, before: string, after: string) {\n result += before;\n let accumulatedWS = -before.length; // start with a negative value to account for the before string\n for (let index = start; index < end; index++) {\n const ch = oldContent[index];\n if (ch === \"\\n\" || ch === \"\\r\") {\n // only write new lines, skip the whitespace\n accumulatedWS = 0;\n result += ch;\n } else {\n accumulatedWS++;\n }\n }\n result = append(result, \" \", accumulatedWS - after.length);\n result += after;\n return result;\n}\n\nfunction append(result: string, string_: string, n: number): string {\n while (n > 0) {\n if (n & 1) {\n result += string_;\n }\n n >>= 1;\n string_ += string_;\n }\n return result;\n}\n\nfunction getAttributeLanguage(attributeName: string): string | null {\n const match = attributeName.match(/^(style)$|^(on\\w+)$/i);\n if (!match) {\n return null;\n }\n return match[1] ? \"css\" : \"javascript\";\n}\n","/* eslint-disable @typescript-eslint/require-await */\nimport path from \"node:path\";\nimport { Resolver } from \"@staticbolt/core\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport { getLanguageModelCache } from \"../language-model-cache.ts\";\nimport { FILE_PROTOCOL } from \"./language-modes.ts\";\n\nimport type { LanguageMode } from \"./language-modes.ts\";\nimport type { HTMLDataV1, HTMLDocument, LanguageService, TextDocument } from \"vscode-html-languageservice\";\n\nexport function getHTMLMode(htmlLanguageService: LanguageService): LanguageMode {\n const htmlDocuments = getLanguageModelCache<HTMLDocument>(10, 60, document => htmlLanguageService.parseHTMLDocument(document));\n\n return {\n getId() {\n return \"html\";\n },\n\n async doComplete(document, position, documentContext, htmlData) {\n setHtmlDataProviders(htmlLanguageService, htmlData);\n\n const htmlDocument = htmlDocuments.get(document);\n const completionList = await htmlLanguageService.doComplete2(document, position, htmlDocument, documentContext);\n\n for (const item of completionList.items) {\n item.sortText = \"0_\" + item.label;\n }\n\n return completionList;\n },\n\n async doHover(document, position, htmlData) {\n setHtmlDataProviders(htmlLanguageService, htmlData);\n\n return htmlLanguageService.doHover(document, position, htmlDocuments.get(document));\n },\n\n async onDocumentRemoved(document: TextDocument) {\n htmlDocuments.onDocumentRemoved(document);\n },\n\n async findDocumentLinks(document, documentContext, projectRoot) {\n const resolver = new Resolver(projectRoot);\n const documentFs = vscodeUri.URI.parse(document.uri).fsPath;\n const projectRootRelative = path.relative(projectRoot, documentFs);\n\n const links = htmlLanguageService.findDocumentLinks(document, documentContext);\n\n for (const link of links) {\n if (!link.target) continue;\n\n const linkFs = vscodeUri.URI.parse(link.target ?? \"\").fsPath;\n const source = path.relative(path.dirname(documentFs), linkFs);\n\n const resolved = resolver.resolve(source, projectRootRelative);\n if (resolved) {\n link.target = vscodeUri.URI.file(resolved.path).toString();\n }\n }\n\n return links;\n },\n\n dispose() {\n htmlDocuments.dispose();\n },\n };\n}\n\nfunction setHtmlDataProviders(htmlLanguageService: LanguageService, htmlData: HTMLDataV1[]) {\n htmlLanguageService.setDataProviders(false, [\n {\n getId() {\n return FILE_PROTOCOL;\n },\n isApplicable(languageId) {\n return languageId === \"html\";\n },\n provideValues(tag, attribute) {\n const tags = htmlData.flatMap(data => data.tags?.filter(t => t.name === tag) ?? []);\n const attributes = tags.flatMap(t => t.attributes?.filter(a => a.name === attribute) ?? []);\n const globalAttributes = htmlData.flatMap(data => data.globalAttributes?.filter(a => a.name === attribute) ?? []);\n const values = [...globalAttributes, ...attributes].flatMap(a => a.values ?? []);\n return values;\n },\n provideTags() {\n const tags = htmlData.flatMap(data => data.tags ?? []);\n return tags;\n },\n provideAttributes(tag: string) {\n const tags = htmlData.flatMap(data => data.tags?.filter(t => t.name === tag) ?? []);\n const globalAttributes = htmlData.flatMap(data => data.globalAttributes ?? []);\n const attributes = tags.flatMap(t => t.attributes ?? []);\n return [...globalAttributes, ...attributes];\n },\n },\n ]);\n}\n","import vscodeHtml from \"vscode-html-languageservice\";\n\nimport { getLanguageModelCache } from \"../language-model-cache.ts\";\nimport { getDocumentRegions } from \"./embedded-support.ts\";\nimport { getHTMLMode } from \"./html-mode.ts\";\n\nimport type { LanguageModelCache } from \"../language-model-cache.ts\";\nimport type { FileSystemProvider } from \"../requests.ts\";\nimport type { HTMLDocumentRegions } from \"./embedded-support.ts\";\nimport type {\n ClientCapabilities,\n CompletionConfiguration,\n DocumentContext,\n HoverSettings,\n HTMLDataV1,\n IHTMLDataProvider,\n} from \"vscode-html-languageservice\";\nimport type {\n CompletionItem,\n CompletionList,\n DocumentLink,\n Hover,\n Position,\n Range,\n WorkspaceFolder,\n} from \"vscode-languageserver\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\n\nexport interface Settings {\n hover?: HoverSettings;\n autoClosingTags?: boolean;\n completion?: CompletionConfiguration;\n suggest?: CompletionConfiguration;\n}\n\nexport interface Workspace {\n readonly settings: Settings;\n readonly folders: WorkspaceFolder[];\n}\n\nexport interface SemanticTokenData {\n start: Position;\n length: number;\n typeIdx: number;\n modifierSet: number;\n}\n\nexport type CompletionItemData = {\n languageId: string;\n uri: string;\n offset: number;\n};\n\nexport function isCompletionItemData(value: Record<string, unknown>): value is CompletionItemData {\n return value && typeof value.languageId === \"string\" && typeof value.uri === \"string\" && typeof value.offset === \"number\";\n}\n\nexport interface LanguageMode {\n getId(): string;\n doComplete?: (\n document: TextDocument,\n position: Position,\n documentContext: DocumentContext,\n htmlData: HTMLDataV1[]\n ) => Promise<CompletionList>;\n doResolve?: (document: TextDocument, item: CompletionItem) => Promise<CompletionItem>;\n doHover?: (document: TextDocument, position: Position, htmlData: HTMLDataV1[]) => Promise<Hover | null>;\n findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext, projectRoot: string) => Promise<DocumentLink[]>;\n onDocumentRemoved(document: TextDocument): void;\n dispose(): void;\n}\n\nexport interface LanguageModes {\n updateDataProviders(dataProviders: IHTMLDataProvider[]): void;\n getModeAtPosition(document: TextDocument, position: Position): LanguageMode | undefined;\n getModesInRange(document: TextDocument, range: Range): LanguageModeRange[];\n getAllModes(): LanguageMode[];\n getAllModesInDocument(document: TextDocument): LanguageMode[];\n getMode(languageId: string): LanguageMode | undefined;\n onDocumentRemoved(document: TextDocument): void;\n dispose(): void;\n}\n\nexport interface LanguageModeRange extends Range {\n mode: LanguageMode | undefined;\n attributeValue?: boolean;\n}\n\nexport const FILE_PROTOCOL = \"staticbolt-server\";\n\nexport function getLanguageModes(clientCapabilities: ClientCapabilities, requestService: FileSystemProvider): LanguageModes {\n const htmlLanguageService = vscodeHtml.getLanguageService({\n clientCapabilities,\n fileSystemProvider: requestService,\n customDataProviders: [\n {\n getId() {\n return FILE_PROTOCOL;\n },\n isApplicable(languageId) {\n return languageId === \"html\";\n },\n provideValues(tag, attribute) {\n return [{ name: `tag:${tag} attribute:${attribute}` }];\n },\n provideTags() {\n return [{ name: \"staticbolt\", description: \"# staticbolt-description\", attributes: [] }];\n },\n\n provideAttributes(tag: string) {\n if (tag === \"staticbolt\") {\n return [\n {\n name: \"staticbolt-attribute\",\n description: \"# staticbolt-attribute-description\",\n values: [{ name: \"staticbolt-attribute-value\" }],\n },\n ];\n }\n\n return [{ name: \"global-attribute\" }];\n },\n },\n ],\n useDefaultDataProvider: false,\n });\n\n const documentRegions = getLanguageModelCache<HTMLDocumentRegions>(10, 60, document =>\n getDocumentRegions(htmlLanguageService, document)\n );\n\n let modelCaches: LanguageModelCache<unknown>[] = [documentRegions];\n\n let modes = Object.create(null) as { [languageId: string]: LanguageMode };\n modes[\"html\"] = getHTMLMode(htmlLanguageService);\n\n return {\n // eslint-disable-next-line @typescript-eslint/require-await\n async updateDataProviders(dataProviders: IHTMLDataProvider[]): Promise<void> {\n htmlLanguageService.setDataProviders(true, dataProviders);\n },\n\n getModeAtPosition(document: TextDocument, position: Position): LanguageMode | undefined {\n const languageId = documentRegions.get(document).getLanguageAtPosition(position);\n if (languageId) {\n return modes[languageId];\n }\n\n return undefined;\n },\n\n getModesInRange(document: TextDocument, range: Range): LanguageModeRange[] {\n return documentRegions\n .get(document)\n .getLanguageRanges(range)\n .map((r): LanguageModeRange => {\n return {\n start: r.start,\n end: r.end,\n mode: r.languageId ? modes[r.languageId] : undefined,\n attributeValue: r.attributeValue,\n };\n });\n },\n\n getAllModesInDocument(document: TextDocument): LanguageMode[] {\n const result: LanguageMode[] = [];\n for (const languageId of documentRegions.get(document).getLanguagesInDocument()) {\n const mode = modes[languageId];\n if (mode) {\n result.push(mode);\n }\n }\n\n return result;\n },\n\n getAllModes(): LanguageMode[] {\n const result = [];\n for (const languageId in modes) {\n const mode = modes[languageId];\n if (mode) {\n result.push(mode);\n }\n }\n\n return result;\n },\n\n getMode(languageId: string): LanguageMode {\n return modes[languageId];\n },\n\n onDocumentRemoved(document: TextDocument) {\n for (const mc of modelCaches) {\n mc.onDocumentRemoved(document);\n }\n\n for (const mode in modes) {\n modes[mode].onDocumentRemoved(document);\n }\n },\n\n dispose(): void {\n for (const mc of modelCaches) {\n mc.dispose();\n }\n\n modelCaches = [];\n for (const mode in modes) {\n modes[mode].dispose();\n }\n\n modes = {};\n },\n };\n}\n","import { RequestType } from \"vscode-languageserver\";\n\nimport type { RuntimeEnvironment } from \"./html-server.ts\";\nimport type { Connection } from \"vscode-languageserver\";\n\n// Interface\nexport const FsStatRequest = { type: new RequestType<string, FileStat, unknown>(\"fs/stat\") } as const;\n\n// Interface\nexport const FsReadDirectoryRequest = { type: new RequestType<string, [string, FileType][], unknown>(\"fs/readDir\") } as const;\n\nexport const FileType = Object.freeze({\n /** The file type is unknown. */\n Unknown: 0,\n\n /** A regular file. */\n File: 1,\n\n /** A directory. */\n Directory: 2,\n\n /** A symbolic link to a file. */\n SymbolicLink: 64,\n});\n\nexport type FileType = (typeof FileType)[keyof typeof FileType];\n\nexport interface FileStat {\n /** The type of the file, e.g. is a regular file, a directory, or symbolic link to a file. */\n type: FileType;\n\n /** The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. */\n ctime: number;\n\n /** The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. */\n mtime: number;\n\n /** The size in bytes. */\n size: number;\n}\n\nexport interface FileSystemProvider {\n stat(uri: string): Promise<FileStat>;\n readDirectory(uri: string): Promise<[string, FileType][]>;\n}\n\nexport function getFileSystemProvider(\n handledSchemas: string[],\n connection: Connection,\n runtime: RuntimeEnvironment\n): FileSystemProvider {\n const fileFs = runtime.fileFs && handledSchemas.includes(\"file\") ? runtime.fileFs : undefined;\n\n return {\n async stat(uri: string): Promise<FileStat> {\n if (fileFs && uri.startsWith(\"file:\")) {\n return fileFs.stat(uri);\n }\n\n const result = await connection.sendRequest(FsStatRequest.type, uri);\n return result;\n },\n\n readDirectory(uri: string): Promise<[string, FileType][]> {\n if (fileFs && uri.startsWith(\"file:\")) {\n return fileFs.readDirectory(uri);\n }\n\n return connection.sendRequest(FsReadDirectoryRequest.type, uri);\n },\n };\n}\n","export function pushAll<T>(to: T[], from: T[]) {\n if (from) {\n for (const item of from) {\n to.push(item);\n }\n }\n}\n\n// eslint-disable-next-line unicorn/consistent-boolean-name\nexport function contains<T>(array: T[], value: T) {\n return array.includes(value);\n}\n\n/**\n * Like `Array#sort` but always stable. Usually runs a little slower `than Array#sort` so only use this when actually needing\n * stable sort.\n */\nexport function mergeSort<T>(data: T[], compare: (a: T, b: T) => number): T[] {\n _divideAndMerge(data, compare);\n return data;\n}\n\nfunction _divideAndMerge<T>(data: T[], compare: (a: T, b: T) => number): void {\n if (data.length <= 1) {\n // sorted\n return;\n }\n const p = Math.trunc(data.length / 2);\n const left = data.slice(0, p);\n const right = data.slice(p);\n\n _divideAndMerge(left, compare);\n _divideAndMerge(right, compare);\n\n let leftIndex = 0;\n let rightIndex = 0;\n let index = 0;\n while (leftIndex < left.length && rightIndex < right.length) {\n const returnValue = compare(left[leftIndex], right[rightIndex]);\n if (returnValue <= 0) {\n // smaller_equal -> take left to preserve order\n data[index++] = left[leftIndex++];\n } else {\n // greater -> take right\n data[index++] = right[rightIndex++];\n }\n }\n while (leftIndex < left.length) {\n data[index++] = left[leftIndex++];\n }\n while (rightIndex < right.length) {\n data[index++] = right[rightIndex++];\n }\n}\n\nexport function binarySearch<T>(array: T[], key: T, comparator: (op1: T, op2: T) => number): number {\n let low = 0,\n high = array.length - 1;\n\n while (low <= high) {\n const mid = Math.trunc((low + high) / 2);\n const comp = comparator(array[mid], key);\n if (comp < 0) {\n low = mid + 1;\n } else if (comp > 0) {\n high = mid - 1;\n } else {\n return mid;\n }\n }\n\n return -(low + 1);\n}\n","export function getWordAtText(text: string, offset: number, wordDefinition: RegExp): { start: number; length: number } {\n let lineStart = offset;\n while (lineStart > 0 && !isNewlineCharacter(text.codePointAt(lineStart - 1)!)) {\n lineStart--;\n }\n const offsetInLine = offset - lineStart;\n const lineText = text.slice(lineStart);\n\n // make a copy of the regex as to not keep the state\n const flags = wordDefinition.ignoreCase ? \"gi\" : \"g\";\n wordDefinition = new RegExp(wordDefinition.source, flags);\n\n let match = wordDefinition.exec(lineText);\n while (match && match.index + match[0].length < offsetInLine) {\n match = wordDefinition.exec(lineText);\n }\n if (match && match.index <= offsetInLine) {\n return { start: match.index + lineStart, length: match[0].length };\n }\n\n return { start: offset, length: 0 };\n}\n\nexport function isStartingWith(haystack: string, needle: string): boolean {\n if (haystack.length < needle.length) {\n return false;\n }\n\n // eslint-disable-next-line unicorn/no-for-loop\n for (let index = 0; index < needle.length; index++) {\n if (haystack[index] !== needle[index]) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function isEndingWith(haystack: string, needle: string): boolean {\n const diff = haystack.length - needle.length;\n if (diff > 0) {\n return haystack.indexOf(needle, diff) === diff;\n }\n\n if (diff === 0) {\n return haystack === needle;\n }\n\n return false;\n}\n\nexport function repeat(value: string, count: number) {\n let s = \"\";\n while (count > 0) {\n if ((count & 1) === 1) {\n s += value;\n }\n value += value;\n count >>>= 1;\n }\n return s;\n}\n\nexport function isWhitespaceOnly(string_: string) {\n return /^\\s*$/.test(string_);\n}\n\nexport function isEOL(content: string, offset: number) {\n return isNewlineCharacter(content.codePointAt(offset)!);\n}\n\nconst CR = \"\\r\".codePointAt(0);\nconst NL = \"\\n\".codePointAt(0);\nexport function isNewlineCharacter(charCode: number) {\n return charCode === CR || charCode === NL;\n}\n","import * as vscodeUri from \"vscode-uri\";\n\nimport { isEndingWith, isStartingWith } from \"../utils/strings.ts\";\n\nimport type { DocumentContext } from \"vscode-css-languageservice\";\nimport type { WorkspaceFolder } from \"vscode-languageserver\";\n\nexport function getDocumentContext(documentUri: string, workspaceFolders: WorkspaceFolder[]): DocumentContext {\n function getRootFolder(): string | undefined {\n for (const folder of workspaceFolders) {\n let folderURI = folder.uri;\n if (!isEndingWith(folderURI, \"/\")) {\n folderURI += \"/\";\n }\n\n if (isStartingWith(documentUri, folderURI)) {\n return folderURI;\n }\n }\n\n return undefined;\n }\n\n return {\n resolveReference: (reference: string, base = documentUri) => {\n if (/^\\w[\\w\\d+.-]*:/.test(reference)) {\n // starts with a schema\n return reference;\n }\n\n if (reference[0] === \"/\") {\n // resolve absolute path against the current workspace folder\n const folderUri = getRootFolder();\n if (folderUri) {\n return folderUri + reference.slice(1);\n }\n }\n\n const baseUri = vscodeUri.URI.parse(base);\n const baseUriDirectory = baseUri.path.endsWith(\"/\") ? baseUri : vscodeUri.Utils.dirname(baseUri);\n return vscodeUri.Utils.resolvePath(baseUriDirectory, reference).toString(true);\n },\n };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as vscodeUri from \"vscode-uri\";\n\nexport function findProjectRoot(fileUri: string): string | null {\n let directory = path.dirname(vscodeUri.URI.parse(fileUri).fsPath);\n\n while (true) {\n for (const config of [\".staticbolt.ts\", \".staticbolt.js\"]) {\n if (fs.existsSync(path.join(directory, config))) {\n return directory;\n }\n }\n\n const parent = path.dirname(directory);\n\n // reached fs root, no config found\n if (parent === directory) {\n return null;\n }\n\n directory = parent;\n }\n}\n","import vscode from \"vscode-languageserver\";\n\nimport type { RuntimeEnvironment } from \"../html-server.ts\";\nimport type { CancellationToken, ResponseError } from \"vscode-languageserver\";\n\nexport function formatError(message: string, error: unknown): string {\n if (error instanceof Error) {\n return `${message}: ${error.message}\\n${error.stack}`;\n }\n\n if (typeof error === \"string\") {\n return `${message}: ${error}`;\n }\n\n if (error) {\n return `${message}: ${error as string}`;\n }\n\n return message;\n}\n\nexport function runSafe<T>(\n runtime: RuntimeEnvironment,\n function_: () => Thenable<T>,\n errorValue: T,\n errorMessage: string,\n token: CancellationToken\n): Thenable<T | ResponseError<never>> {\n return new Promise<T | ResponseError<never>>(resolve => {\n runtime.timer.setImmediate(() => {\n if (token.isCancellationRequested) {\n resolve(cancelValue());\n return;\n }\n return function_().then(\n result => {\n if (token.isCancellationRequested) {\n resolve(cancelValue());\n return;\n }\n\n resolve(result);\n },\n\n error => {\n console.error(formatError(errorMessage, error));\n resolve(errorValue);\n }\n );\n });\n });\n}\n\nfunction cancelValue<E>() {\n return new vscode.ResponseError<E>(vscode.LSPErrorCodes.RequestCancelled, \"Request cancelled\");\n}\n","import * as vscode from \"vscode-languageserver\";\nimport { TextDocument } from \"vscode-languageserver-textdocument\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport { ConfigManager } from \"./helpers/config-loader.ts\";\nimport { findStaticboltProjects } from \"./helpers/find-projects.ts\";\nimport { getLanguageModes } from \"./modes/language-modes.ts\";\nimport { getFileSystemProvider } from \"./requests.ts\";\nimport { pushAll } from \"./utils/arrays.ts\";\nimport { getDocumentContext } from \"./utils/document-context.ts\";\nimport { findProjectRoot } from \"./utils/find-project-root.ts\";\nimport { runSafe } from \"./utils/runner.ts\";\n\nimport type { CompletionItemData, LanguageModes, Settings } from \"./modes/language-modes.ts\";\nimport type { FileSystemProvider } from \"./requests.ts\";\nimport type { Connection, Disposable, InitializeParams, WorkspaceFolder } from \"vscode-languageserver\";\n\nexport interface RuntimeEnvironment {\n fileFs?: FileSystemProvider;\n\n configureHttpRequests?(proxy: string | undefined, isStrictSSL: boolean): void;\n\n readonly timer: {\n setImmediate(callback: (...arguments_: unknown[]) => void, ...arguments_: unknown[]): Disposable;\n setTimeout(callback: (...arguments_: unknown[]) => void, ms: number, ...arguments_: unknown[]): Disposable;\n };\n}\n\nexport interface CustomDataRequestService {\n getContent(uri: string): Promise<string>;\n}\n\nexport function startServer(connection: Connection, runtime: RuntimeEnvironment) {\n // Create a text document manager.\n const documents = new vscode.TextDocuments(TextDocument);\n // Make the text document manager listen on the connection\n // for open, change and close text document events\n documents.listen(connection);\n\n let lspSearchRoots: WorkspaceFolder[] = [];\n let workspaceFolders: WorkspaceFolder[] = [];\n let configManager: ConfigManager;\n\n let languageModes: LanguageModes;\n\n const documentSettings: { [key: string]: Thenable<Settings> } = {};\n\n // remove document settings on close\n documents.onDidClose(document => {\n delete documentSettings[document.document.uri];\n });\n\n connection.onInitialize((parameters: InitializeParams) => {\n if (Array.isArray(parameters.workspaceFolders)) {\n lspSearchRoots = parameters.workspaceFolders;\n } else {\n lspSearchRoots = [];\n if (parameters.rootPath) {\n lspSearchRoots.push({ name: \"\", uri: vscodeUri.URI.file(parameters.rootPath).toString() });\n }\n }\n\n workspaceFolders = findStaticboltProjects(lspSearchRoots);\n connection.console.log(\n `[staticbolt] discovered projects:\\n` + workspaceFolders.map(f => ` - [${f.name}]: ${f.uri}`).join(\"\\n\")\n );\n\n const fileSystemProvider = getFileSystemProvider([\"file\"], connection, runtime);\n languageModes = getLanguageModes(parameters.capabilities, fileSystemProvider);\n\n configManager = new ConfigManager(connection.console);\n\n documents.onDidClose(document => {\n languageModes.onDocumentRemoved(document.document);\n });\n\n connection.onShutdown(() => {\n languageModes.dispose();\n configManager.dispose();\n });\n\n return {\n capabilities: {\n textDocumentSync: vscode.TextDocumentSyncKind.Incremental,\n completionProvider: {\n resolveProvider: true,\n triggerCharacters: [\".\", \":\", \"<\", '\"', \"=\", \"/\"],\n },\n hoverProvider: true,\n documentLinkProvider: { resolveProvider: false },\n },\n };\n });\n\n connection.onInitialized(() => {});\n\n connection.onCompletion(async (textDocumentPosition, token) => {\n return runSafe(\n runtime,\n async () => {\n const projectRoot = findProjectRoot(textDocumentPosition.textDocument.uri);\n if (!projectRoot) return null;\n\n const document = documents.get(textDocumentPosition.textDocument.uri);\n if (!document) return null;\n\n const mode = languageModes.getModeAtPosition(document, textDocumentPosition.position);\n if (!mode?.doComplete) return { isIncomplete: true, items: [] };\n\n const config = await configManager.get(projectRoot);\n if (!config) return { isIncomplete: true, items: [] };\n\n const documentContext = getDocumentContext(document.uri, workspaceFolders);\n return mode.doComplete(document, textDocumentPosition.position, documentContext, configManager.lspHtmlData);\n },\n null,\n `Error while computing completions for ${textDocumentPosition.textDocument.uri}`,\n token\n );\n });\n\n connection.onCompletionResolve((item, token) => {\n return runSafe(\n runtime,\n async () => {\n const data = item.data as Record<string, unknown>;\n if (!isCompletionItemData(data)) return item;\n\n const document = documents.get(data.uri);\n if (!document) return item;\n\n const mode = languageModes.getMode(data.languageId);\n if (!mode?.doResolve) return item;\n\n return mode.doResolve(document, item);\n },\n item,\n `Error while resolving completion proposal`,\n token\n );\n });\n\n connection.onHover((textDocumentPosition, token) => {\n return runSafe(\n runtime,\n async () => {\n const projectRoot = findProjectRoot(textDocumentPosition.textDocument.uri);\n if (!projectRoot) return null;\n\n const document = documents.get(textDocumentPosition.textDocument.uri);\n if (!document) return null;\n\n const mode = languageModes.getModeAtPosition(document, textDocumentPosition.position);\n if (!mode?.doHover) return null;\n\n const config = await configManager.get(projectRoot);\n if (!config) return null;\n\n return mode.doHover(document, textDocumentPosition.position, configManager.lspHtmlData);\n },\n null,\n `Error while computing hover for ${textDocumentPosition.textDocument.uri}`,\n token\n );\n });\n\n connection.onDocumentLinks((documentLinkParameter, token) => {\n return runSafe(\n runtime,\n async () => {\n const projectRoot = findProjectRoot(documentLinkParameter.textDocument.uri);\n if (!projectRoot) return null;\n\n const document = documents.get(documentLinkParameter.textDocument.uri);\n if (!document) return [];\n\n const links: vscode.DocumentLink[] = [];\n\n const documentContext = getDocumentContext(document.uri, workspaceFolders);\n for (const mode of languageModes.getAllModesInDocument(document)) {\n if (mode.findDocumentLinks) {\n pushAll(links, await mode.findDocumentLinks(document, documentContext, projectRoot));\n }\n }\n\n return links;\n },\n [],\n `Error while document links for ${documentLinkParameter.textDocument.uri}`,\n token\n );\n });\n\n // Listen on the connection\n connection.listen();\n}\n\nexport function isCompletionItemData(value: Record<string, unknown>): value is CompletionItemData {\n return value && typeof value.languageId === \"string\" && typeof value.uri === \"string\" && typeof value.offset === \"number\";\n}\n","import * as fs from \"node:fs\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport type { FileSystemProvider } from \"../requests.ts\";\n\nconst FileType = Object.freeze({\n /** The file type is unknown. */\n Unknown: 0,\n /** A regular file. */\n File: 1,\n /** A directory. */\n Directory: 2,\n /** A symbolic link to a file. */\n SymbolicLink: 64,\n});\n\ntype FileType = (typeof FileType)[keyof typeof FileType];\n\nexport function getNodeFileFS(): FileSystemProvider {\n function ensureFileUri(location: string) {\n if (!location.startsWith(\"file:\")) {\n throw new Error(\"fileSystemProvider can only handle file URLs\");\n }\n }\n return {\n stat(location: string) {\n ensureFileUri(location);\n return new Promise((c, error_) => {\n const uri = vscodeUri.URI.parse(location);\n fs.stat(uri.fsPath, (error, stats) => {\n if (error) {\n return error.code === \"ENOENT\" ? c({ type: FileType.Unknown, ctime: -1, mtime: -1, size: -1 }) : error_(error);\n }\n\n let type: FileType = FileType.Unknown;\n if (stats.isFile()) {\n type = FileType.File;\n } else if (stats.isDirectory()) {\n type = FileType.Directory;\n } else if (stats.isSymbolicLink()) {\n type = FileType.SymbolicLink;\n }\n\n c({\n type,\n ctime: stats.ctime.getTime(),\n mtime: stats.mtime.getTime(),\n size: stats.size,\n });\n });\n });\n },\n readDirectory(location: string) {\n ensureFileUri(location);\n return new Promise((c, error_) => {\n const path = vscodeUri.URI.parse(location).fsPath;\n\n fs.readdir(path, { withFileTypes: true }, (error, children) => {\n if (error) {\n return error_(error);\n }\n c(\n children.map(stat => {\n if (stat.isSymbolicLink()) {\n return [stat.name, FileType.SymbolicLink];\n }\n if (stat.isDirectory()) {\n return [stat.name, FileType.Directory];\n }\n return stat.isFile() ? [stat.name, FileType.File] : [stat.name, FileType.Unknown];\n })\n );\n });\n });\n },\n };\n}\n","import * as vs from \"vscode-languageserver/node\";\n\nimport { startServer } from \"./html-server.ts\";\nimport { getNodeFileFS } from \"./utils/node-fs.ts\";\nimport { formatError } from \"./utils/runner.ts\";\n\nimport type { RuntimeEnvironment } from \"./html-server.ts\";\nimport type { Connection, Disposable } from \"vscode-languageserver/node\";\n\n// Create a connection for the server.\nconst connection: Connection = vs.createConnection();\n\nconsole.log = connection.console.log.bind(connection.console);\nconsole.error = connection.console.error.bind(connection.console);\n\nprocess.on(\"unhandledRejection\", (error: unknown) => {\n connection.console.error(formatError(`Unhandled exception`, error));\n});\n\nconst runtime: RuntimeEnvironment = {\n timer: {\n setImmediate(callback: (...arguments_: unknown[]) => void, ...arguments_: unknown[]): Disposable {\n const handle = setImmediate(callback, ...arguments_);\n return { dispose: () => clearImmediate(handle) };\n },\n\n setTimeout(callback: (...arguments_: unknown[]) => void, ms: number, ...arguments_: unknown[]): Disposable {\n const handle = setTimeout(callback, ms, ...arguments_);\n return { dispose: () => clearTimeout(handle) };\n },\n },\n\n fileFs: getNodeFileFS(),\n};\n\nconnection.console.log(\"[staticbolt] starting server\");\nstartServer(connection, runtime);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["path","FileType","path","vscode"],"sources":["../src/helpers/config-loader.ts","../src/helpers/find-projects.ts","../src/language-model-cache.ts","../src/modes/embedded-support.ts","../src/helpers/merge-html-data.ts","../src/modes/html-mode.ts","../src/modes/language-modes.ts","../src/requests.ts","../src/utils/arrays.ts","../src/utils/strings.ts","../src/utils/document-context.ts","../src/utils/find-project-root.ts","../src/utils/runner.ts","../src/html-server.ts","../src/utils/node-fs.ts","../src/index.ts"],"sourcesContent":["import { EventEmitter } from \"node:events\";\nimport { watch, type FSWatcher } from \"node:fs\";\nimport { access, constants } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nimport type { AppConfig } from \"@staticbolt/core\";\nimport type { HTMLDataV1 } from \"vscode-html-languageservice\";\nimport type { MarkupContent, RemoteConsole } from \"vscode-languageserver\";\n\nexport type StaticBoltConfig = AppConfig;\n\nexport interface ConfigLoaderEvents {\n change: [config: StaticBoltConfig];\n error: [err: Error];\n}\n\nconst CONFIG_CANDIDATES = [\".staticbolt.ts\", \".staticbolt.js\"] as const;\n\nasync function findConfigFile(workspaceRoot: string): Promise<string | null> {\n for (const name of CONFIG_CANDIDATES) {\n const full = join(workspaceRoot, name);\n\n try {\n await access(full, constants.R_OK);\n return full;\n } catch {\n // not found or not readable — try next\n }\n }\n\n return null;\n}\n\nasync function importFresh(absolutePath: string): Promise<StaticBoltConfig> {\n const url = `${pathToFileURL(absolutePath).href}?t=${Date.now()}`;\n const module_ = (await import(url)) as { default: StaticBoltConfig };\n\n if (!(\"default\" in module_)) {\n throw new Error(`Config file \"${absolutePath}\" has no default export. Use \\`export default { … }\\`.`);\n }\n\n return module_.default;\n}\n\n// eslint-disable-next-line unicorn/prefer-event-target\nexport class ConfigLoader extends EventEmitter<ConfigLoaderEvents> {\n private readonly workspaceRoot: string;\n private configPath: string | null = null;\n private watcher: FSWatcher | null = null;\n private current: StaticBoltConfig | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly DEBOUNCE_MS = 150;\n\n constructor(workspaceRoot: string) {\n super();\n this.workspaceRoot = workspaceRoot;\n }\n\n /** Start watching. Resolves with the initial config (or null if absent). */\n async start(): Promise<StaticBoltConfig | null> {\n this.configPath = await findConfigFile(this.workspaceRoot);\n\n if (!this.configPath) {\n return null;\n }\n\n await this.load();\n this.watch(this.configPath);\n return this.current;\n }\n\n /** Stop watching and release all resources. */\n dispose(): void {\n if (this.debounceTimer !== null) {\n clearTimeout(this.debounceTimer);\n }\n this.watcher?.close();\n this.watcher = null;\n }\n\n /** The last successfully loaded config, or null if none loaded yet. */\n get config(): StaticBoltConfig | null {\n return this.current;\n }\n\n private async load(): Promise<void> {\n if (!this.configPath) return;\n\n try {\n this.current = await importFresh(this.configPath);\n this.emit(\"change\", this.current);\n } catch (error) {\n this.emit(\"error\", error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n private watch(configPath: string): void {\n this.watcher = watch(configPath, { persistent: false }, event => {\n if (event === \"rename\" || event === \"change\") {\n this.scheduleReload();\n }\n });\n\n this.watcher.on(\"error\", error => this.emit(\"error\", error));\n }\n\n private scheduleReload(): void {\n if (this.debounceTimer !== null) {\n clearTimeout(this.debounceTimer);\n }\n\n this.debounceTimer = setTimeout(async () => {\n this.debounceTimer = null;\n\n // The file might have been atomically replaced (Vim, VS Code safe-write).\n // Re-check existence; if it disappeared we just bail.\n if (!this.configPath) return;\n\n try {\n await access(this.configPath, constants.R_OK);\n } catch {\n this.emit(\"error\", new Error(`Config file was removed: ${this.configPath}`));\n return;\n }\n\n await this.load();\n }, this.DEBOUNCE_MS);\n }\n}\n\nexport class ConfigManager {\n private readonly console: RemoteConsole;\n readonly configs = new Map<string, ConfigLoader>();\n lspHtmlData: HTMLDataV1[] = [];\n\n constructor(console: RemoteConsole) {\n this.console = console;\n }\n\n async get(workspaceRoot: string): Promise<AppConfig | null> {\n const configLoader = this.configs.get(workspaceRoot);\n if (configLoader) {\n if (!configLoader.config) {\n this.configs.delete(workspaceRoot);\n return null;\n }\n\n return configLoader.config;\n }\n\n const newConfigLoader = new ConfigLoader(workspaceRoot);\n this.configs.set(workspaceRoot, newConfigLoader);\n\n newConfigLoader.on(\"error\", error => {\n this.console.error(`Failed to load config file: ${error.message}`);\n });\n\n newConfigLoader.on(\"change\", async config => {\n await this.collectData(config);\n });\n\n await newConfigLoader.start();\n\n if (!newConfigLoader.config) {\n return null;\n }\n\n return newConfigLoader.config;\n }\n\n dispose(): void {\n for (const configLoader of this.configs.values()) {\n configLoader.dispose();\n }\n }\n\n private async collectData(config: StaticBoltConfig) {\n // collected into a new array and published at the end, consumers cache on its identity\n const collected: HTMLDataV1[] = [];\n\n const plugins = config.plugins ?? [];\n\n for (const pluginOrArray of plugins) {\n const plugins = Array.isArray(pluginOrArray) ? pluginOrArray : [pluginOrArray];\n\n for (const plugin of plugins) {\n if (!plugin.lspHtmlData) {\n continue;\n }\n\n const htmlData = await plugin.lspHtmlData();\n if (htmlData) {\n htmlDataInjectPluginName(htmlData, plugin.name);\n collected.push(htmlData);\n }\n }\n }\n\n this.lspHtmlData = collected;\n }\n}\n\nfunction htmlDataInjectPluginName(htmlData: HTMLDataV1, pluginName: string) {\n const tags = htmlData.tags ?? [];\n for (const tag of tags) {\n tag.description = replaceDescription(tag.description, pluginName);\n\n const attributes = tag.attributes ?? [];\n for (const attribute of attributes) {\n attribute.description = replaceDescription(attribute.description, pluginName);\n }\n }\n\n const globalAttributes = htmlData.globalAttributes ?? [];\n for (const attribute of globalAttributes) {\n attribute.description = replaceDescription(attribute.description, pluginName);\n }\n}\n\nfunction replaceDescription(description: string | MarkupContent | undefined, pluginName: string) {\n const info = `_Provided by **staticbolt** \\`${pluginName}\\` plugin._`;\n\n if (!description) {\n return info;\n }\n\n if (typeof description === \"string\") {\n if (description.includes(info)) {\n return description;\n }\n\n return `${description}\\n\\n${info}`;\n }\n\n if (description.value.includes(info)) {\n return description;\n }\n\n description.value = `${description.value}\\n\\n${info}`;\n return description;\n}\n","import { globSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport type { WorkspaceFolder } from \"vscode-languageserver\";\n\nexport function findStaticboltProjects(searchRoots: WorkspaceFolder[]): WorkspaceFolder[] {\n const results: WorkspaceFolder[] = [];\n\n for (const root of searchRoots) {\n const rootPath = vscodeUri.URI.parse(root.uri).fsPath;\n\n const configs = globSync(\"**/.staticbolt.{ts,js}\", { cwd: rootPath, exclude: [\"**/node_modules/**\", \"**/.git/**\"] });\n\n for (const configPath of configs) {\n const absDirectory = path.join(rootPath, path.dirname(configPath));\n const projectUri = vscodeUri.URI.file(absDirectory).toString();\n results.push({ name: path.basename(absDirectory), uri: projectUri });\n }\n }\n\n return results;\n}\n","import type { TextDocument } from \"vscode-html-languageservice\";\n\nexport interface LanguageModelCache<T> {\n get(document: TextDocument): T;\n onDocumentRemoved(document: TextDocument): void;\n dispose(): void;\n}\n\nexport function getLanguageModelCache<T>(\n maxEntries: number,\n cleanupIntervalTimeInSec: number,\n parse: (document: TextDocument) => T\n): LanguageModelCache<T> {\n let languageModels: { [uri: string]: { version: number; languageId: string; cTime: number; languageModel: T } } = {};\n let nModels = 0;\n\n let cleanupInterval: NodeJS.Timeout | undefined;\n\n if (cleanupIntervalTimeInSec > 0) {\n cleanupInterval = setInterval(() => {\n const cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000;\n const uris = Object.keys(languageModels);\n\n for (const uri of uris) {\n const languageModelInfo = languageModels[uri];\n if (languageModelInfo.cTime < cutoffTime) {\n delete languageModels[uri];\n nModels--;\n }\n }\n }, cleanupIntervalTimeInSec * 1000);\n }\n\n return {\n get(document: TextDocument): T {\n const version = document.version;\n const languageId = document.languageId;\n const languageModelInfo = languageModels[document.uri];\n\n if (languageModelInfo && languageModelInfo.version === version && languageModelInfo.languageId === languageId) {\n languageModelInfo.cTime = Date.now();\n return languageModelInfo.languageModel;\n }\n\n const languageModel = parse(document);\n languageModels[document.uri] = { languageModel, version, languageId, cTime: Date.now() };\n if (!languageModelInfo) {\n nModels++;\n }\n\n if (nModels === maxEntries) {\n let oldestTime = Number.MAX_VALUE;\n let oldestUri = null;\n\n for (const uri in languageModels) {\n const languageModelInfo = languageModels[uri];\n if (languageModelInfo.cTime < oldestTime) {\n oldestUri = uri;\n oldestTime = languageModelInfo.cTime;\n }\n }\n\n if (oldestUri) {\n delete languageModels[oldestUri];\n nModels--;\n }\n }\n\n return languageModel;\n },\n\n onDocumentRemoved(document: TextDocument) {\n const uri = document.uri;\n if (Object.hasOwn(languageModels, uri)) {\n delete languageModels[uri];\n nModels--;\n }\n },\n\n dispose() {\n if (cleanupInterval === undefined) {\n return;\n }\n\n clearInterval(cleanupInterval);\n cleanupInterval = undefined;\n languageModels = {};\n nModels = 0;\n },\n };\n}\n","import vscodeHtml from \"vscode-html-languageservice\";\nimport { TextDocument } from \"vscode-languageserver-textdocument\";\n\nimport type { LanguageService, Range } from \"vscode-html-languageservice\";\n\nexport interface LanguageRange extends Range {\n languageId: string | undefined;\n attributeValue?: boolean;\n}\n\nexport interface HTMLDocumentRegions {\n getEmbeddedDocument(languageId: string, shouldIgnoreAttributeValues?: boolean): TextDocument;\n getLanguageRanges(range: Range): LanguageRange[];\n getLanguageAtPosition(position: Position): string | undefined;\n getLanguagesInDocument(): string[];\n getImportedScripts(): string[];\n}\n\nconst CSS_STYLE_RULE = \"__\";\nconst TokenType = vscodeHtml.TokenType;\nconst Position = vscodeHtml.Position;\ntype Position = vscodeHtml.Position;\n\ninterface EmbeddedRegion {\n languageId: string | undefined;\n start: number;\n end: number;\n attributeValue?: boolean;\n}\n\nexport function getDocumentRegions(languageService: LanguageService, document: TextDocument): HTMLDocumentRegions {\n const regions: EmbeddedRegion[] = [];\n const scanner = languageService.createScanner(document.getText());\n let lastTagName: string = \"\";\n let lastAttributeName: string | null = null;\n let languageIdFromType: string | undefined;\n const importedScripts: string[] = [];\n\n let token = scanner.scan();\n\n while (token !== TokenType.EOS) {\n switch (token) {\n case TokenType.StartTag: {\n lastTagName = scanner.getTokenText();\n lastAttributeName = null;\n languageIdFromType = \"javascript\";\n break;\n }\n case TokenType.Styles: {\n regions.push({ languageId: \"css\", start: scanner.getTokenOffset(), end: scanner.getTokenEnd() });\n break;\n }\n case TokenType.Script: {\n regions.push({ languageId: languageIdFromType, start: scanner.getTokenOffset(), end: scanner.getTokenEnd() });\n break;\n }\n case TokenType.AttributeName: {\n lastAttributeName = scanner.getTokenText();\n break;\n }\n case TokenType.AttributeValue: {\n if (lastAttributeName === \"src\" && lastTagName.toLowerCase() === \"script\") {\n let value = scanner.getTokenText();\n if (value[0] === \"'\" || value[0] === '\"') {\n value = value.slice(1, -1);\n }\n importedScripts.push(value);\n } else if (lastAttributeName === \"type\" && lastTagName.toLowerCase() === \"script\") {\n const token = scanner.getTokenText();\n if (/[\"'](module|(text|application)\\/(java|ecma)script|text\\/babel)[\"']/.test(token) || token === \"module\") {\n languageIdFromType = \"javascript\";\n } else if (/[\"']text\\/typescript[\"']/.test(token)) {\n languageIdFromType = \"typescript\";\n } else {\n languageIdFromType = undefined;\n }\n } else {\n const attributeLanguageId = getAttributeLanguage(lastAttributeName!);\n if (attributeLanguageId) {\n let start = scanner.getTokenOffset();\n let end = scanner.getTokenEnd();\n const firstChar = document.getText()[start];\n if (firstChar === \"'\" || firstChar === '\"') {\n start++;\n end--;\n }\n regions.push({ languageId: attributeLanguageId, start, end, attributeValue: true });\n }\n }\n lastAttributeName = null;\n break;\n }\n }\n token = scanner.scan();\n }\n return {\n getLanguageRanges: (range: Range) => getLanguageRanges(document, regions, range),\n getEmbeddedDocument: (languageId: string, shouldIgnoreAttributeValues: boolean) =>\n getEmbeddedDocument(document, regions, languageId, shouldIgnoreAttributeValues),\n getLanguageAtPosition: (position: Position) => getLanguageAtPosition(document, regions, position),\n getLanguagesInDocument: () => getLanguagesInDocument(document, regions),\n getImportedScripts: () => importedScripts,\n };\n}\n\nfunction getLanguageRanges(document: TextDocument, regions: EmbeddedRegion[], range: Range): LanguageRange[] {\n const result: LanguageRange[] = [];\n let currentPos = range ? range.start : Position.create(0, 0);\n let currentOffset = range ? document.offsetAt(range.start) : 0;\n const endOffset = range ? document.offsetAt(range.end) : document.getText().length;\n for (const region of regions) {\n if (!(region.end > currentOffset && region.start < endOffset)) {\n continue;\n }\n\n const start = Math.max(region.start, currentOffset);\n const startPos = document.positionAt(start);\n if (currentOffset < region.start) {\n result.push({\n start: currentPos,\n end: startPos,\n languageId: \"html\",\n });\n }\n const end = Math.min(region.end, endOffset);\n const endPos = document.positionAt(end);\n if (end > region.start) {\n result.push({\n start: startPos,\n end: endPos,\n languageId: region.languageId,\n attributeValue: region.attributeValue,\n });\n }\n currentOffset = end;\n currentPos = endPos;\n }\n if (currentOffset < endOffset) {\n const endPos = range ? range.end : document.positionAt(endOffset);\n result.push({\n start: currentPos,\n end: endPos,\n languageId: \"html\",\n });\n }\n return result;\n}\n\nfunction getLanguagesInDocument(_document: TextDocument, regions: EmbeddedRegion[]): string[] {\n const result: string[] = [];\n\n for (const region of regions) {\n if (!(region.languageId && !result.includes(region.languageId))) {\n continue;\n }\n\n result.push(region.languageId);\n if (result.length === 3) {\n return result;\n }\n }\n\n result.push(\"html\");\n\n return result;\n}\n\nfunction getLanguageAtPosition(document: TextDocument, regions: EmbeddedRegion[], position: Position): string | undefined {\n const offset = document.offsetAt(position);\n for (const region of regions) {\n if (region.start <= offset) {\n if (offset <= region.end) {\n return region.languageId;\n }\n } else {\n break;\n }\n }\n return \"html\";\n}\n\nfunction getEmbeddedDocument(\n document: TextDocument,\n contents: EmbeddedRegion[],\n languageId: string,\n shouldIgnoreAttributeValues: boolean\n): TextDocument {\n let currentPos = 0;\n const oldContent = document.getText();\n let result = \"\";\n let lastSuffix = \"\";\n for (const c of contents) {\n if (!(c.languageId === languageId && (!shouldIgnoreAttributeValues || !c.attributeValue))) {\n continue;\n }\n\n result = substituteWithWhitespace(result, currentPos, c.start, oldContent, lastSuffix, getPrefix(c));\n result += updateContent(c, oldContent.slice(c.start, c.end));\n currentPos = c.end;\n lastSuffix = getSuffix(c);\n }\n result = substituteWithWhitespace(result, currentPos, oldContent.length, oldContent, lastSuffix, \"\");\n return TextDocument.create(document.uri, languageId, document.version, result);\n}\n\nfunction getPrefix(c: EmbeddedRegion) {\n if (c.attributeValue) {\n switch (c.languageId) {\n case \"css\": {\n return CSS_STYLE_RULE + \"{\";\n }\n }\n }\n return \"\";\n}\nfunction getSuffix(c: EmbeddedRegion) {\n if (c.attributeValue) {\n switch (c.languageId) {\n case \"css\": {\n return \"}\";\n }\n case \"javascript\": {\n return \";\";\n }\n }\n }\n return \"\";\n}\nfunction updateContent(c: EmbeddedRegion, content: string): string {\n if (!c.attributeValue && c.languageId === \"javascript\") {\n return content.replace(`<!--`, `/* `).replace(`-->`, ` */`);\n }\n if (c.languageId === \"css\") {\n const quoteEscape = /("|")/g;\n return content.replace(quoteEscape, (match, _, offset: number) => {\n const spaces = \" \".repeat(match.length - 1);\n const afterChar = content[offset + match.length];\n if (!afterChar || afterChar.includes(\" \")) {\n return `${spaces}\"`;\n }\n return `\"${spaces}`;\n });\n }\n return content;\n}\n\nfunction substituteWithWhitespace(result: string, start: number, end: number, oldContent: string, before: string, after: string) {\n result += before;\n let accumulatedWS = -before.length; // start with a negative value to account for the before string\n for (let index = start; index < end; index++) {\n const ch = oldContent[index];\n if (ch === \"\\n\" || ch === \"\\r\") {\n // only write new lines, skip the whitespace\n accumulatedWS = 0;\n result += ch;\n } else {\n accumulatedWS++;\n }\n }\n result = append(result, \" \", accumulatedWS - after.length);\n result += after;\n return result;\n}\n\nfunction append(result: string, string_: string, n: number): string {\n while (n > 0) {\n if (n & 1) {\n result += string_;\n }\n n >>= 1;\n string_ += string_;\n }\n return result;\n}\n\nfunction getAttributeLanguage(attributeName: string): string | null {\n const match = attributeName.match(/^(style)$|^(on\\w+)$/i);\n if (!match) {\n return null;\n }\n return match[1] ? \"css\" : \"javascript\";\n}\n","import type {\n HTMLDataV1,\n IAttributeData,\n IHTMLDataProvider,\n IReference,\n ITagData,\n IValueData,\n MarkupContent,\n} from \"vscode-html-languageservice\";\n\ntype Description = string | MarkupContent | undefined;\ntype DescriptionKind = MarkupContent[\"kind\"];\ntype ValueSets = Map<string, IValueData[]>;\n\n/** Separates the documentation of two contributors. Rendered as a horizontal rule, unless the description is plain text. */\nconst MARKDOWN_SEPARATOR = \"\\n\\n---\\n\\n\";\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 const ordered: T[][] = [];\n\n for (const item of items) {\n const key = toKey(item);\n\n let group = groups.get(key);\n if (!group) {\n group = [];\n groups.set(key, group);\n ordered.push(group);\n }\n\n group.push(item);\n }\n\n return ordered;\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\" || kind === \"markdown\") continue;\n kind = description.kind;\n }\n\n return kind;\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 // nothing to merge, keep the original as-is\n if (defined.length <= 1) return defined[0];\n\n const parts: string[] = [];\n const seen = new Set<string>();\n\n for (const description of defined) {\n const text = (typeof description === \"string\" ? description : description.value).trim();\n if (!text || seen.has(text)) continue;\n\n seen.add(text);\n parts.push(text);\n }\n\n if (parts.length === 0) return undefined;\n\n const kind = mergeDescriptionKind(defined);\n const value = parts.join(kind === \"plaintext\" ? PLAINTEXT_SEPARATOR : MARKDOWN_SEPARATOR);\n\n return kind ? { kind, value } : value;\n}\n\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 return merged.length > 0 ? merged : undefined;\n}\n\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 return merged.size > 0 ? [...merged] : undefined;\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 if (!attribute.valueSet) return values;\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) return group[0];\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 // nothing to merge and no value set to expand, keep the original as-is\n if (group.length === 1 && !group[0].valueSet) return group[0];\n\n const values = mergeValues(group.flatMap(attribute => resolveValues(attribute, valueSets)));\n\n return {\n name: group[0].name,\n description: mergeDescriptions(group.map(attribute => attribute.description)),\n // `valueSet` is dropped on purpose: every referenced set is now expanded into `values`\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) return { ...group[0], attributes };\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 valueSets.set(valueSet.name, existing ? mergeValues([...existing, ...valueSet.values]) : 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 // the language service asks for tags on every parse, and for attributes on every completion/hover, so merging happens once here\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) return cached;\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) return globalAttributes;\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\n // merged attributes already carry their expanded value sets\n return provideAttributes(tag).find(a => a.name.toLowerCase() === name)?.values ?? [];\n },\n };\n}\n","/* eslint-disable @typescript-eslint/require-await */\nimport path from \"node:path\";\nimport { Resolver } from \"@staticbolt/core\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport { createMergedHtmlDataProvider } from \"../helpers/merge-html-data.ts\";\nimport { getLanguageModelCache } from \"../language-model-cache.ts\";\nimport { FILE_PROTOCOL } from \"./language-modes.ts\";\n\nimport type { LanguageMode } from \"./language-modes.ts\";\nimport type { HTMLDataV1, HTMLDocument, IHTMLDataProvider, LanguageService, TextDocument } from \"vscode-html-languageservice\";\n\nexport function getHTMLMode(htmlLanguageService: LanguageService): LanguageMode {\n const htmlDocuments = getLanguageModelCache<HTMLDocument>(10, 60, document => htmlLanguageService.parseHTMLDocument(document));\n\n // the merged provider is rebuilt only when the collected html data actually changes\n let lastHtmlData: HTMLDataV1[] | undefined;\n let lastProvider: IHTMLDataProvider | undefined;\n\n function setHtmlDataProviders(htmlData: HTMLDataV1[]) {\n if (!lastProvider || lastHtmlData !== htmlData) {\n lastHtmlData = htmlData;\n lastProvider = createMergedHtmlDataProvider(FILE_PROTOCOL, htmlData);\n }\n\n htmlLanguageService.setDataProviders(false, [lastProvider]);\n }\n\n return {\n getId() {\n return \"html\";\n },\n\n async doComplete(document, position, documentContext, htmlData) {\n setHtmlDataProviders(htmlData);\n\n const htmlDocument = htmlDocuments.get(document);\n const completionList = await htmlLanguageService.doComplete2(document, position, htmlDocument, documentContext);\n\n for (const item of completionList.items) {\n item.sortText = \"0_\" + item.label;\n }\n\n return completionList;\n },\n\n async doHover(document, position, htmlData) {\n setHtmlDataProviders(htmlData);\n\n return htmlLanguageService.doHover(document, position, htmlDocuments.get(document));\n },\n\n async onDocumentRemoved(document: TextDocument) {\n htmlDocuments.onDocumentRemoved(document);\n },\n\n async findDocumentLinks(document, documentContext, projectRoot) {\n const resolver = new Resolver(projectRoot);\n const documentFs = vscodeUri.URI.parse(document.uri).fsPath;\n const projectRootRelative = path.relative(projectRoot, documentFs);\n\n const links = htmlLanguageService.findDocumentLinks(document, documentContext);\n\n for (const link of links) {\n if (!link.target) continue;\n\n const linkFs = vscodeUri.URI.parse(link.target ?? \"\").fsPath;\n const source = path.relative(path.dirname(documentFs), linkFs);\n\n const resolved = resolver.resolve(source, projectRootRelative);\n if (resolved) {\n link.target = vscodeUri.URI.file(resolved.path).toString();\n }\n }\n\n return links;\n },\n\n dispose() {\n htmlDocuments.dispose();\n },\n };\n}\n","import vscodeHtml from \"vscode-html-languageservice\";\n\nimport { getLanguageModelCache } from \"../language-model-cache.ts\";\nimport { getDocumentRegions } from \"./embedded-support.ts\";\nimport { getHTMLMode } from \"./html-mode.ts\";\n\nimport type { LanguageModelCache } from \"../language-model-cache.ts\";\nimport type { FileSystemProvider } from \"../requests.ts\";\nimport type { HTMLDocumentRegions } from \"./embedded-support.ts\";\nimport type {\n ClientCapabilities,\n CompletionConfiguration,\n DocumentContext,\n HoverSettings,\n HTMLDataV1,\n IHTMLDataProvider,\n} from \"vscode-html-languageservice\";\nimport type {\n CompletionItem,\n CompletionList,\n DocumentLink,\n Hover,\n Position,\n Range,\n WorkspaceFolder,\n} from \"vscode-languageserver\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\n\nexport interface Settings {\n hover?: HoverSettings;\n autoClosingTags?: boolean;\n completion?: CompletionConfiguration;\n suggest?: CompletionConfiguration;\n}\n\nexport interface Workspace {\n readonly settings: Settings;\n readonly folders: WorkspaceFolder[];\n}\n\nexport interface SemanticTokenData {\n start: Position;\n length: number;\n typeIdx: number;\n modifierSet: number;\n}\n\nexport type CompletionItemData = {\n languageId: string;\n uri: string;\n offset: number;\n};\n\nexport function isCompletionItemData(value: Record<string, unknown>): value is CompletionItemData {\n return value && typeof value.languageId === \"string\" && typeof value.uri === \"string\" && typeof value.offset === \"number\";\n}\n\nexport interface LanguageMode {\n getId(): string;\n doComplete?: (\n document: TextDocument,\n position: Position,\n documentContext: DocumentContext,\n htmlData: HTMLDataV1[]\n ) => Promise<CompletionList>;\n doResolve?: (document: TextDocument, item: CompletionItem) => Promise<CompletionItem>;\n doHover?: (document: TextDocument, position: Position, htmlData: HTMLDataV1[]) => Promise<Hover | null>;\n findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext, projectRoot: string) => Promise<DocumentLink[]>;\n onDocumentRemoved(document: TextDocument): void;\n dispose(): void;\n}\n\nexport interface LanguageModes {\n updateDataProviders(dataProviders: IHTMLDataProvider[]): void;\n getModeAtPosition(document: TextDocument, position: Position): LanguageMode | undefined;\n getModesInRange(document: TextDocument, range: Range): LanguageModeRange[];\n getAllModes(): LanguageMode[];\n getAllModesInDocument(document: TextDocument): LanguageMode[];\n getMode(languageId: string): LanguageMode | undefined;\n onDocumentRemoved(document: TextDocument): void;\n dispose(): void;\n}\n\nexport interface LanguageModeRange extends Range {\n mode: LanguageMode | undefined;\n attributeValue?: boolean;\n}\n\nexport const FILE_PROTOCOL = \"staticbolt-server\";\n\nexport function getLanguageModes(clientCapabilities: ClientCapabilities, requestService: FileSystemProvider): LanguageModes {\n const htmlLanguageService = vscodeHtml.getLanguageService({\n clientCapabilities,\n fileSystemProvider: requestService,\n customDataProviders: [\n {\n getId() {\n return FILE_PROTOCOL;\n },\n isApplicable(languageId) {\n return languageId === \"html\";\n },\n provideValues(tag, attribute) {\n return [{ name: `tag:${tag} attribute:${attribute}` }];\n },\n provideTags() {\n return [{ name: \"staticbolt\", description: \"# staticbolt-description\", attributes: [] }];\n },\n\n provideAttributes(tag: string) {\n if (tag === \"staticbolt\") {\n return [\n {\n name: \"staticbolt-attribute\",\n description: \"# staticbolt-attribute-description\",\n values: [{ name: \"staticbolt-attribute-value\" }],\n },\n ];\n }\n\n return [{ name: \"global-attribute\" }];\n },\n },\n ],\n useDefaultDataProvider: false,\n });\n\n const documentRegions = getLanguageModelCache<HTMLDocumentRegions>(10, 60, document =>\n getDocumentRegions(htmlLanguageService, document)\n );\n\n let modelCaches: LanguageModelCache<unknown>[] = [documentRegions];\n\n let modes = Object.create(null) as { [languageId: string]: LanguageMode };\n modes[\"html\"] = getHTMLMode(htmlLanguageService);\n\n return {\n // eslint-disable-next-line @typescript-eslint/require-await\n async updateDataProviders(dataProviders: IHTMLDataProvider[]): Promise<void> {\n htmlLanguageService.setDataProviders(true, dataProviders);\n },\n\n getModeAtPosition(document: TextDocument, position: Position): LanguageMode | undefined {\n const languageId = documentRegions.get(document).getLanguageAtPosition(position);\n if (languageId) {\n return modes[languageId];\n }\n\n return undefined;\n },\n\n getModesInRange(document: TextDocument, range: Range): LanguageModeRange[] {\n return documentRegions\n .get(document)\n .getLanguageRanges(range)\n .map((r): LanguageModeRange => {\n return {\n start: r.start,\n end: r.end,\n mode: r.languageId ? modes[r.languageId] : undefined,\n attributeValue: r.attributeValue,\n };\n });\n },\n\n getAllModesInDocument(document: TextDocument): LanguageMode[] {\n const result: LanguageMode[] = [];\n for (const languageId of documentRegions.get(document).getLanguagesInDocument()) {\n const mode = modes[languageId];\n if (mode) {\n result.push(mode);\n }\n }\n\n return result;\n },\n\n getAllModes(): LanguageMode[] {\n const result = [];\n for (const languageId in modes) {\n const mode = modes[languageId];\n if (mode) {\n result.push(mode);\n }\n }\n\n return result;\n },\n\n getMode(languageId: string): LanguageMode {\n return modes[languageId];\n },\n\n onDocumentRemoved(document: TextDocument) {\n for (const mc of modelCaches) {\n mc.onDocumentRemoved(document);\n }\n\n for (const mode in modes) {\n modes[mode].onDocumentRemoved(document);\n }\n },\n\n dispose(): void {\n for (const mc of modelCaches) {\n mc.dispose();\n }\n\n modelCaches = [];\n for (const mode in modes) {\n modes[mode].dispose();\n }\n\n modes = {};\n },\n };\n}\n","import { RequestType } from \"vscode-languageserver\";\n\nimport type { RuntimeEnvironment } from \"./html-server.ts\";\nimport type { Connection } from \"vscode-languageserver\";\n\n// Interface\nexport const FsStatRequest = { type: new RequestType<string, FileStat, unknown>(\"fs/stat\") } as const;\n\n// Interface\nexport const FsReadDirectoryRequest = { type: new RequestType<string, [string, FileType][], unknown>(\"fs/readDir\") } as const;\n\nexport const FileType = Object.freeze({\n /** The file type is unknown. */\n Unknown: 0,\n\n /** A regular file. */\n File: 1,\n\n /** A directory. */\n Directory: 2,\n\n /** A symbolic link to a file. */\n SymbolicLink: 64,\n});\n\nexport type FileType = (typeof FileType)[keyof typeof FileType];\n\nexport interface FileStat {\n /** The type of the file, e.g. is a regular file, a directory, or symbolic link to a file. */\n type: FileType;\n\n /** The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. */\n ctime: number;\n\n /** The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. */\n mtime: number;\n\n /** The size in bytes. */\n size: number;\n}\n\nexport interface FileSystemProvider {\n stat(uri: string): Promise<FileStat>;\n readDirectory(uri: string): Promise<[string, FileType][]>;\n}\n\nexport function getFileSystemProvider(\n handledSchemas: string[],\n connection: Connection,\n runtime: RuntimeEnvironment\n): FileSystemProvider {\n const fileFs = runtime.fileFs && handledSchemas.includes(\"file\") ? runtime.fileFs : undefined;\n\n return {\n async stat(uri: string): Promise<FileStat> {\n if (fileFs && uri.startsWith(\"file:\")) {\n return fileFs.stat(uri);\n }\n\n const result = await connection.sendRequest(FsStatRequest.type, uri);\n return result;\n },\n\n readDirectory(uri: string): Promise<[string, FileType][]> {\n if (fileFs && uri.startsWith(\"file:\")) {\n return fileFs.readDirectory(uri);\n }\n\n return connection.sendRequest(FsReadDirectoryRequest.type, uri);\n },\n };\n}\n","export function pushAll<T>(to: T[], from: T[]) {\n if (from) {\n for (const item of from) {\n to.push(item);\n }\n }\n}\n\n// eslint-disable-next-line unicorn/consistent-boolean-name\nexport function contains<T>(array: T[], value: T) {\n return array.includes(value);\n}\n\n/**\n * Like `Array#sort` but always stable. Usually runs a little slower `than Array#sort` so only use this when actually needing\n * stable sort.\n */\nexport function mergeSort<T>(data: T[], compare: (a: T, b: T) => number): T[] {\n _divideAndMerge(data, compare);\n return data;\n}\n\nfunction _divideAndMerge<T>(data: T[], compare: (a: T, b: T) => number): void {\n if (data.length <= 1) {\n // sorted\n return;\n }\n const p = Math.trunc(data.length / 2);\n const left = data.slice(0, p);\n const right = data.slice(p);\n\n _divideAndMerge(left, compare);\n _divideAndMerge(right, compare);\n\n let leftIndex = 0;\n let rightIndex = 0;\n let index = 0;\n while (leftIndex < left.length && rightIndex < right.length) {\n const returnValue = compare(left[leftIndex], right[rightIndex]);\n if (returnValue <= 0) {\n // smaller_equal -> take left to preserve order\n data[index++] = left[leftIndex++];\n } else {\n // greater -> take right\n data[index++] = right[rightIndex++];\n }\n }\n while (leftIndex < left.length) {\n data[index++] = left[leftIndex++];\n }\n while (rightIndex < right.length) {\n data[index++] = right[rightIndex++];\n }\n}\n\nexport function binarySearch<T>(array: T[], key: T, comparator: (op1: T, op2: T) => number): number {\n let low = 0,\n high = array.length - 1;\n\n while (low <= high) {\n const mid = Math.trunc((low + high) / 2);\n const comp = comparator(array[mid], key);\n if (comp < 0) {\n low = mid + 1;\n } else if (comp > 0) {\n high = mid - 1;\n } else {\n return mid;\n }\n }\n\n return -(low + 1);\n}\n","export function getWordAtText(text: string, offset: number, wordDefinition: RegExp): { start: number; length: number } {\n let lineStart = offset;\n while (lineStart > 0 && !isNewlineCharacter(text.codePointAt(lineStart - 1)!)) {\n lineStart--;\n }\n const offsetInLine = offset - lineStart;\n const lineText = text.slice(lineStart);\n\n // make a copy of the regex as to not keep the state\n const flags = wordDefinition.ignoreCase ? \"gi\" : \"g\";\n wordDefinition = new RegExp(wordDefinition.source, flags);\n\n let match = wordDefinition.exec(lineText);\n while (match && match.index + match[0].length < offsetInLine) {\n match = wordDefinition.exec(lineText);\n }\n if (match && match.index <= offsetInLine) {\n return { start: match.index + lineStart, length: match[0].length };\n }\n\n return { start: offset, length: 0 };\n}\n\nexport function isStartingWith(haystack: string, needle: string): boolean {\n if (haystack.length < needle.length) {\n return false;\n }\n\n // eslint-disable-next-line unicorn/no-for-loop\n for (let index = 0; index < needle.length; index++) {\n if (haystack[index] !== needle[index]) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function isEndingWith(haystack: string, needle: string): boolean {\n const diff = haystack.length - needle.length;\n if (diff > 0) {\n return haystack.indexOf(needle, diff) === diff;\n }\n\n if (diff === 0) {\n return haystack === needle;\n }\n\n return false;\n}\n\nexport function repeat(value: string, count: number) {\n let s = \"\";\n while (count > 0) {\n if ((count & 1) === 1) {\n s += value;\n }\n value += value;\n count >>>= 1;\n }\n return s;\n}\n\nexport function isWhitespaceOnly(string_: string) {\n return /^\\s*$/.test(string_);\n}\n\nexport function isEOL(content: string, offset: number) {\n return isNewlineCharacter(content.codePointAt(offset)!);\n}\n\nconst CR = \"\\r\".codePointAt(0);\nconst NL = \"\\n\".codePointAt(0);\nexport function isNewlineCharacter(charCode: number) {\n return charCode === CR || charCode === NL;\n}\n","import * as vscodeUri from \"vscode-uri\";\n\nimport { isEndingWith, isStartingWith } from \"../utils/strings.ts\";\n\nimport type { DocumentContext } from \"vscode-css-languageservice\";\nimport type { WorkspaceFolder } from \"vscode-languageserver\";\n\nexport function getDocumentContext(documentUri: string, workspaceFolders: WorkspaceFolder[]): DocumentContext {\n function getRootFolder(): string | undefined {\n for (const folder of workspaceFolders) {\n let folderURI = folder.uri;\n if (!isEndingWith(folderURI, \"/\")) {\n folderURI += \"/\";\n }\n\n if (isStartingWith(documentUri, folderURI)) {\n return folderURI;\n }\n }\n\n return undefined;\n }\n\n return {\n resolveReference: (reference: string, base = documentUri) => {\n if (/^\\w[\\w\\d+.-]*:/.test(reference)) {\n // starts with a schema\n return reference;\n }\n\n if (reference[0] === \"/\") {\n // resolve absolute path against the current workspace folder\n const folderUri = getRootFolder();\n if (folderUri) {\n return folderUri + reference.slice(1);\n }\n }\n\n const baseUri = vscodeUri.URI.parse(base);\n const baseUriDirectory = baseUri.path.endsWith(\"/\") ? baseUri : vscodeUri.Utils.dirname(baseUri);\n return vscodeUri.Utils.resolvePath(baseUriDirectory, reference).toString(true);\n },\n };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as vscodeUri from \"vscode-uri\";\n\nexport function findProjectRoot(fileUri: string): string | null {\n let directory = path.dirname(vscodeUri.URI.parse(fileUri).fsPath);\n\n while (true) {\n for (const config of [\".staticbolt.ts\", \".staticbolt.js\"]) {\n if (fs.existsSync(path.join(directory, config))) {\n return directory;\n }\n }\n\n const parent = path.dirname(directory);\n\n // reached fs root, no config found\n if (parent === directory) {\n return null;\n }\n\n directory = parent;\n }\n}\n","import vscode from \"vscode-languageserver\";\n\nimport type { RuntimeEnvironment } from \"../html-server.ts\";\nimport type { CancellationToken, ResponseError } from \"vscode-languageserver\";\n\nexport function formatError(message: string, error: unknown): string {\n if (error instanceof Error) {\n return `${message}: ${error.message}\\n${error.stack}`;\n }\n\n if (typeof error === \"string\") {\n return `${message}: ${error}`;\n }\n\n if (error) {\n return `${message}: ${error as string}`;\n }\n\n return message;\n}\n\nexport function runSafe<T>(\n runtime: RuntimeEnvironment,\n function_: () => Thenable<T>,\n errorValue: T,\n errorMessage: string,\n token: CancellationToken\n): Thenable<T | ResponseError<never>> {\n return new Promise<T | ResponseError<never>>(resolve => {\n runtime.timer.setImmediate(() => {\n if (token.isCancellationRequested) {\n resolve(cancelValue());\n return;\n }\n return function_().then(\n result => {\n if (token.isCancellationRequested) {\n resolve(cancelValue());\n return;\n }\n\n resolve(result);\n },\n\n error => {\n console.error(formatError(errorMessage, error));\n resolve(errorValue);\n }\n );\n });\n });\n}\n\nfunction cancelValue<E>() {\n return new vscode.ResponseError<E>(vscode.LSPErrorCodes.RequestCancelled, \"Request cancelled\");\n}\n","import * as vscode from \"vscode-languageserver\";\nimport { TextDocument } from \"vscode-languageserver-textdocument\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport { ConfigManager } from \"./helpers/config-loader.ts\";\nimport { findStaticboltProjects } from \"./helpers/find-projects.ts\";\nimport { getLanguageModes } from \"./modes/language-modes.ts\";\nimport { getFileSystemProvider } from \"./requests.ts\";\nimport { pushAll } from \"./utils/arrays.ts\";\nimport { getDocumentContext } from \"./utils/document-context.ts\";\nimport { findProjectRoot } from \"./utils/find-project-root.ts\";\nimport { runSafe } from \"./utils/runner.ts\";\n\nimport type { CompletionItemData, LanguageModes, Settings } from \"./modes/language-modes.ts\";\nimport type { FileSystemProvider } from \"./requests.ts\";\nimport type { Connection, Disposable, InitializeParams, WorkspaceFolder } from \"vscode-languageserver\";\n\nexport interface RuntimeEnvironment {\n fileFs?: FileSystemProvider;\n\n configureHttpRequests?(proxy: string | undefined, isStrictSSL: boolean): void;\n\n readonly timer: {\n setImmediate(callback: (...arguments_: unknown[]) => void, ...arguments_: unknown[]): Disposable;\n setTimeout(callback: (...arguments_: unknown[]) => void, ms: number, ...arguments_: unknown[]): Disposable;\n };\n}\n\nexport interface CustomDataRequestService {\n getContent(uri: string): Promise<string>;\n}\n\nexport function startServer(connection: Connection, runtime: RuntimeEnvironment) {\n // Create a text document manager.\n const documents = new vscode.TextDocuments(TextDocument);\n // Make the text document manager listen on the connection\n // for open, change and close text document events\n documents.listen(connection);\n\n let lspSearchRoots: WorkspaceFolder[] = [];\n let workspaceFolders: WorkspaceFolder[] = [];\n let configManager: ConfigManager;\n\n let languageModes: LanguageModes;\n\n const documentSettings: { [key: string]: Thenable<Settings> } = {};\n\n // remove document settings on close\n documents.onDidClose(document => {\n delete documentSettings[document.document.uri];\n });\n\n connection.onInitialize((parameters: InitializeParams) => {\n if (Array.isArray(parameters.workspaceFolders)) {\n lspSearchRoots = parameters.workspaceFolders;\n } else {\n lspSearchRoots = [];\n if (parameters.rootPath) {\n lspSearchRoots.push({ name: \"\", uri: vscodeUri.URI.file(parameters.rootPath).toString() });\n }\n }\n\n workspaceFolders = findStaticboltProjects(lspSearchRoots);\n connection.console.log(\n `[staticbolt] discovered projects:\\n` + workspaceFolders.map(f => ` - [${f.name}]: ${f.uri}`).join(\"\\n\")\n );\n\n const fileSystemProvider = getFileSystemProvider([\"file\"], connection, runtime);\n languageModes = getLanguageModes(parameters.capabilities, fileSystemProvider);\n\n configManager = new ConfigManager(connection.console);\n\n documents.onDidClose(document => {\n languageModes.onDocumentRemoved(document.document);\n });\n\n connection.onShutdown(() => {\n languageModes.dispose();\n configManager.dispose();\n });\n\n return {\n capabilities: {\n textDocumentSync: vscode.TextDocumentSyncKind.Incremental,\n completionProvider: {\n resolveProvider: true,\n triggerCharacters: [\".\", \":\", \"<\", '\"', \"=\", \"/\"],\n },\n hoverProvider: true,\n documentLinkProvider: { resolveProvider: false },\n },\n };\n });\n\n connection.onInitialized(() => {});\n\n connection.onCompletion(async (textDocumentPosition, token) => {\n return runSafe(\n runtime,\n async () => {\n const projectRoot = findProjectRoot(textDocumentPosition.textDocument.uri);\n if (!projectRoot) return null;\n\n const document = documents.get(textDocumentPosition.textDocument.uri);\n if (!document) return null;\n\n const mode = languageModes.getModeAtPosition(document, textDocumentPosition.position);\n if (!mode?.doComplete) return { isIncomplete: true, items: [] };\n\n const config = await configManager.get(projectRoot);\n if (!config) return { isIncomplete: true, items: [] };\n\n const documentContext = getDocumentContext(document.uri, workspaceFolders);\n return mode.doComplete(document, textDocumentPosition.position, documentContext, configManager.lspHtmlData);\n },\n null,\n `Error while computing completions for ${textDocumentPosition.textDocument.uri}`,\n token\n );\n });\n\n connection.onCompletionResolve((item, token) => {\n return runSafe(\n runtime,\n async () => {\n const data = item.data as Record<string, unknown>;\n if (!isCompletionItemData(data)) return item;\n\n const document = documents.get(data.uri);\n if (!document) return item;\n\n const mode = languageModes.getMode(data.languageId);\n if (!mode?.doResolve) return item;\n\n return mode.doResolve(document, item);\n },\n item,\n `Error while resolving completion proposal`,\n token\n );\n });\n\n connection.onHover((textDocumentPosition, token) => {\n return runSafe(\n runtime,\n async () => {\n const projectRoot = findProjectRoot(textDocumentPosition.textDocument.uri);\n if (!projectRoot) return null;\n\n const document = documents.get(textDocumentPosition.textDocument.uri);\n if (!document) return null;\n\n const mode = languageModes.getModeAtPosition(document, textDocumentPosition.position);\n if (!mode?.doHover) return null;\n\n const config = await configManager.get(projectRoot);\n if (!config) return null;\n\n return mode.doHover(document, textDocumentPosition.position, configManager.lspHtmlData);\n },\n null,\n `Error while computing hover for ${textDocumentPosition.textDocument.uri}`,\n token\n );\n });\n\n connection.onDocumentLinks((documentLinkParameter, token) => {\n return runSafe(\n runtime,\n async () => {\n const projectRoot = findProjectRoot(documentLinkParameter.textDocument.uri);\n if (!projectRoot) return null;\n\n const document = documents.get(documentLinkParameter.textDocument.uri);\n if (!document) return [];\n\n const links: vscode.DocumentLink[] = [];\n\n const documentContext = getDocumentContext(document.uri, workspaceFolders);\n for (const mode of languageModes.getAllModesInDocument(document)) {\n if (mode.findDocumentLinks) {\n pushAll(links, await mode.findDocumentLinks(document, documentContext, projectRoot));\n }\n }\n\n return links;\n },\n [],\n `Error while document links for ${documentLinkParameter.textDocument.uri}`,\n token\n );\n });\n\n // Listen on the connection\n connection.listen();\n}\n\nexport function isCompletionItemData(value: Record<string, unknown>): value is CompletionItemData {\n return value && typeof value.languageId === \"string\" && typeof value.uri === \"string\" && typeof value.offset === \"number\";\n}\n","import * as fs from \"node:fs\";\nimport * as vscodeUri from \"vscode-uri\";\n\nimport type { FileSystemProvider } from \"../requests.ts\";\n\nconst FileType = Object.freeze({\n /** The file type is unknown. */\n Unknown: 0,\n /** A regular file. */\n File: 1,\n /** A directory. */\n Directory: 2,\n /** A symbolic link to a file. */\n SymbolicLink: 64,\n});\n\ntype FileType = (typeof FileType)[keyof typeof FileType];\n\nexport function getNodeFileFS(): FileSystemProvider {\n function ensureFileUri(location: string) {\n if (!location.startsWith(\"file:\")) {\n throw new Error(\"fileSystemProvider can only handle file URLs\");\n }\n }\n return {\n stat(location: string) {\n ensureFileUri(location);\n return new Promise((c, error_) => {\n const uri = vscodeUri.URI.parse(location);\n fs.stat(uri.fsPath, (error, stats) => {\n if (error) {\n return error.code === \"ENOENT\" ? c({ type: FileType.Unknown, ctime: -1, mtime: -1, size: -1 }) : error_(error);\n }\n\n let type: FileType = FileType.Unknown;\n if (stats.isFile()) {\n type = FileType.File;\n } else if (stats.isDirectory()) {\n type = FileType.Directory;\n } else if (stats.isSymbolicLink()) {\n type = FileType.SymbolicLink;\n }\n\n c({\n type,\n ctime: stats.ctime.getTime(),\n mtime: stats.mtime.getTime(),\n size: stats.size,\n });\n });\n });\n },\n readDirectory(location: string) {\n ensureFileUri(location);\n return new Promise((c, error_) => {\n const path = vscodeUri.URI.parse(location).fsPath;\n\n fs.readdir(path, { withFileTypes: true }, (error, children) => {\n if (error) {\n return error_(error);\n }\n c(\n children.map(stat => {\n if (stat.isSymbolicLink()) {\n return [stat.name, FileType.SymbolicLink];\n }\n if (stat.isDirectory()) {\n return [stat.name, FileType.Directory];\n }\n return stat.isFile() ? [stat.name, FileType.File] : [stat.name, FileType.Unknown];\n })\n );\n });\n });\n },\n };\n}\n","import { format, stripVTControlCharacters } from \"node:util\";\nimport * as vs from \"vscode-languageserver/node\";\n\nimport { startServer } from \"./html-server.ts\";\nimport { getNodeFileFS } from \"./utils/node-fs.ts\";\nimport { formatError } from \"./utils/runner.ts\";\n\nimport type { RuntimeEnvironment } from \"./html-server.ts\";\nimport type { Connection, Disposable } from \"vscode-languageserver/node\";\n\n// Create a connection for the server.\nconst connection: Connection = vs.createConnection();\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[]) => write(stripVTControlCharacters(format(...messages)));\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 connection.console.error(formatError(`Unhandled exception`, error));\n});\n\nconst runtime: RuntimeEnvironment = {\n timer: {\n setImmediate(callback: (...arguments_: unknown[]) => void, ...arguments_: unknown[]): Disposable {\n const handle = setImmediate(callback, ...arguments_);\n return { dispose: () => clearImmediate(handle) };\n },\n\n setTimeout(callback: (...arguments_: unknown[]) => void, ms: number, ...arguments_: unknown[]): Disposable {\n const handle = setTimeout(callback, ms, ...arguments_);\n return { dispose: () => clearTimeout(handle) };\n },\n },\n\n fileFs: getNodeFileFS(),\n};\n\nconnection.console.log(\"[staticbolt] starting server\");\nstartServer(connection, runtime);\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,MAAM,oBAAoB,CAAC,kBAAkB,gBAAgB;AAE7D,eAAe,eAAe,eAA+C;CAC3E,KAAK,MAAM,QAAQ,mBAAmB;EACpC,MAAM,OAAO,KAAK,eAAe,IAAI;EAErC,IAAI;GACF,MAAM,OAAO,MAAM,UAAU,IAAI;GACjC,OAAO;EACT,QAAQ,CAER;CACF;CAEA,OAAO;AACT;AAEA,eAAe,YAAY,cAAiD;CAE1E,MAAM,UAAW,MAAM,OAAO,GADf,cAAc,YAAY,CAAC,CAAC,KAAK,KAAK,KAAK,IAAI;CAG9D,IAAI,EAAE,aAAa,UACjB,MAAM,IAAI,MAAM,gBAAgB,aAAa,uDAAuD;CAGtG,OAAO,QAAQ;AACjB;AAGA,IAAa,eAAb,cAAkC,aAAiC;CACjE,AAAiB;CACjB,AAAQ,aAA4B;CACpC,AAAQ,UAA4B;CACpC,AAAQ,UAAmC;CAC3C,AAAQ,gBAAsD;CAC9D,AAAiB,cAAc;CAE/B,YAAY,eAAuB;EACjC,MAAM;EACN,KAAK,gBAAgB;CACvB;;CAGA,MAAM,QAA0C;EAC9C,KAAK,aAAa,MAAM,eAAe,KAAK,aAAa;EAEzD,IAAI,CAAC,KAAK,YACR,OAAO;EAGT,MAAM,KAAK,KAAK;EAChB,KAAK,MAAM,KAAK,UAAU;EAC1B,OAAO,KAAK;CACd;;CAGA,UAAgB;EACd,IAAI,KAAK,kBAAkB,MACzB,aAAa,KAAK,aAAa;EAEjC,KAAK,SAAS,MAAM;EACpB,KAAK,UAAU;CACjB;;CAGA,IAAI,SAAkC;EACpC,OAAO,KAAK;CACd;CAEA,MAAc,OAAsB;EAClC,IAAI,CAAC,KAAK,YAAY;EAEtB,IAAI;GACF,KAAK,UAAU,MAAM,YAAY,KAAK,UAAU;GAChD,KAAK,KAAK,UAAU,KAAK,OAAO;EAClC,SAAS,OAAO;GACd,KAAK,KAAK,SAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EAC9E;CACF;CAEA,AAAQ,MAAM,YAA0B;EACtC,KAAK,UAAU,MAAM,YAAY,EAAE,YAAY,MAAM,IAAG,UAAS;GAC/D,IAAI,UAAU,YAAY,UAAU,UAClC,KAAK,eAAe;EAExB,CAAC;EAED,KAAK,QAAQ,GAAG,UAAS,UAAS,KAAK,KAAK,SAAS,KAAK,CAAC;CAC7D;CAEA,AAAQ,iBAAuB;EAC7B,IAAI,KAAK,kBAAkB,MACzB,aAAa,KAAK,aAAa;EAGjC,KAAK,gBAAgB,WAAW,YAAY;GAC1C,KAAK,gBAAgB;GAIrB,IAAI,CAAC,KAAK,YAAY;GAEtB,IAAI;IACF,MAAM,OAAO,KAAK,YAAY,UAAU,IAAI;GAC9C,QAAQ;IACN,KAAK,KAAK,yBAAS,IAAI,MAAM,4BAA4B,KAAK,YAAY,CAAC;IAC3E;GACF;GAEA,MAAM,KAAK,KAAK;EAClB,GAAG,KAAK,WAAW;CACrB;AACF;AAEA,IAAa,gBAAb,MAA2B;CACzB,AAAiB;CACjB,AAAS,0BAAU,IAAI,IAA0B;CACjD,cAA4B,CAAC;CAE7B,YAAY,SAAwB;EAClC,KAAK,UAAU;CACjB;CAEA,MAAM,IAAI,eAAkD;EAC1D,MAAM,eAAe,KAAK,QAAQ,IAAI,aAAa;EACnD,IAAI,cAAc;GAChB,IAAI,CAAC,aAAa,QAAQ;IACxB,KAAK,QAAQ,OAAO,aAAa;IACjC,OAAO;GACT;GAEA,OAAO,aAAa;EACtB;EAEA,MAAM,kBAAkB,IAAI,aAAa,aAAa;EACtD,KAAK,QAAQ,IAAI,eAAe,eAAe;EAE/C,gBAAgB,GAAG,UAAS,UAAS;GACnC,KAAK,QAAQ,MAAM,+BAA+B,MAAM,SAAS;EACnE,CAAC;EAED,gBAAgB,GAAG,UAAU,OAAM,WAAU;GAC3C,MAAM,KAAK,YAAY,MAAM;EAC/B,CAAC;EAED,MAAM,gBAAgB,MAAM;EAE5B,IAAI,CAAC,gBAAgB,QACnB,OAAO;EAGT,OAAO,gBAAgB;CACzB;CAEA,UAAgB;EACd,KAAK,MAAM,gBAAgB,KAAK,QAAQ,OAAO,GAC7C,aAAa,QAAQ;CAEzB;CAEA,MAAc,YAAY,QAA0B;EAElD,MAAM,YAA0B,CAAC;EAEjC,MAAM,UAAU,OAAO,WAAW,CAAC;EAEnC,KAAK,MAAM,iBAAiB,SAAS;GACnC,MAAM,UAAU,MAAM,QAAQ,aAAa,IAAI,gBAAgB,CAAC,aAAa;GAE7E,KAAK,MAAM,UAAU,SAAS;IAC5B,IAAI,CAAC,OAAO,aACV;IAGF,MAAM,WAAW,MAAM,OAAO,YAAY;IAC1C,IAAI,UAAU;KACZ,yBAAyB,UAAU,OAAO,IAAI;KAC9C,UAAU,KAAK,QAAQ;IACzB;GACF;EACF;EAEA,KAAK,cAAc;CACrB;AACF;AAEA,SAAS,yBAAyB,UAAsB,YAAoB;CAC1E,MAAM,OAAO,SAAS,QAAQ,CAAC;CAC/B,KAAK,MAAM,OAAO,MAAM;EACtB,IAAI,cAAc,mBAAmB,IAAI,aAAa,UAAU;EAEhE,MAAM,aAAa,IAAI,cAAc,CAAC;EACtC,KAAK,MAAM,aAAa,YACtB,UAAU,cAAc,mBAAmB,UAAU,aAAa,UAAU;CAEhF;CAEA,MAAM,mBAAmB,SAAS,oBAAoB,CAAC;CACvD,KAAK,MAAM,aAAa,kBACtB,UAAU,cAAc,mBAAmB,UAAU,aAAa,UAAU;AAEhF;AAEA,SAAS,mBAAmB,aAAiD,YAAoB;CAC/F,MAAM,OAAO,iCAAiC,WAAW;CAEzD,IAAI,CAAC,aACH,OAAO;CAGT,IAAI,OAAO,gBAAgB,UAAU;EACnC,IAAI,YAAY,SAAS,IAAI,GAC3B,OAAO;EAGT,OAAO,GAAG,YAAY,MAAM;CAC9B;CAEA,IAAI,YAAY,MAAM,SAAS,IAAI,GACjC,OAAO;CAGT,YAAY,QAAQ,GAAG,YAAY,MAAM,MAAM;CAC/C,OAAO;AACT;;;;AC3OA,SAAgB,uBAAuB,aAAmD;CACxF,MAAM,UAA6B,CAAC;CAEpC,KAAK,MAAM,QAAQ,aAAa;EAC9B,MAAM,WAAW,UAAU,IAAI,MAAM,KAAK,GAAG,CAAC,CAAC;EAE/C,MAAM,UAAU,SAAS,0BAA0B;GAAE,KAAK;GAAU,SAAS,CAAC,sBAAsB,YAAY;EAAE,CAAC;EAEnH,KAAK,MAAM,cAAc,SAAS;GAChC,MAAM,eAAeA,OAAK,KAAK,UAAUA,OAAK,QAAQ,UAAU,CAAC;GACjE,MAAM,aAAa,UAAU,IAAI,KAAK,YAAY,CAAC,CAAC,SAAS;GAC7D,QAAQ,KAAK;IAAE,MAAMA,OAAK,SAAS,YAAY;IAAG,KAAK;GAAW,CAAC;EACrE;CACF;CAEA,OAAO;AACT;;;;ACdA,SAAgB,sBACd,YACA,0BACA,OACuB;CACvB,IAAI,iBAA8G,CAAC;CACnH,IAAI,UAAU;CAEd,IAAI;CAEJ,IAAI,2BAA2B,GAC7B,kBAAkB,kBAAkB;EAClC,MAAM,aAAa,KAAK,IAAI,IAAI,2BAA2B;EAC3D,MAAM,OAAO,OAAO,KAAK,cAAc;EAEvC,KAAK,MAAM,OAAO,MAEhB,IAD0B,eAAe,IACpB,CAAC,QAAQ,YAAY;GACxC,OAAO,eAAe;GACtB;EACF;CAEJ,GAAG,2BAA2B,GAAI;CAGpC,OAAO;EACL,IAAI,UAA2B;GAC7B,MAAM,UAAU,SAAS;GACzB,MAAM,aAAa,SAAS;GAC5B,MAAM,oBAAoB,eAAe,SAAS;GAElD,IAAI,qBAAqB,kBAAkB,YAAY,WAAW,kBAAkB,eAAe,YAAY;IAC7G,kBAAkB,QAAQ,KAAK,IAAI;IACnC,OAAO,kBAAkB;GAC3B;GAEA,MAAM,gBAAgB,MAAM,QAAQ;GACpC,eAAe,SAAS,OAAO;IAAE;IAAe;IAAS;IAAY,OAAO,KAAK,IAAI;GAAE;GACvF,IAAI,CAAC,mBACH;GAGF,IAAI,YAAY,YAAY;IAC1B,IAAI,aAAa,OAAO;IACxB,IAAI,YAAY;IAEhB,KAAK,MAAM,OAAO,gBAAgB;KAChC,MAAM,oBAAoB,eAAe;KACzC,IAAI,kBAAkB,QAAQ,YAAY;MACxC,YAAY;MACZ,aAAa,kBAAkB;KACjC;IACF;IAEA,IAAI,WAAW;KACb,OAAO,eAAe;KACtB;IACF;GACF;GAEA,OAAO;EACT;EAEA,kBAAkB,UAAwB;GACxC,MAAM,MAAM,SAAS;GACrB,IAAI,OAAO,OAAO,gBAAgB,GAAG,GAAG;IACtC,OAAO,eAAe;IACtB;GACF;EACF;EAEA,UAAU;GACR,IAAI,oBAAoB,QACtB;GAGF,cAAc,eAAe;GAC7B,kBAAkB;GAClB,iBAAiB,CAAC;GAClB,UAAU;EACZ;CACF;AACF;;;;ACvEA,MAAM,YAAY,WAAW;AAC7B,MAAM,WAAW,WAAW;AAU5B,SAAgB,mBAAmB,iBAAkC,UAA6C;CAChH,MAAM,UAA4B,CAAC;CACnC,MAAM,UAAU,gBAAgB,cAAc,SAAS,QAAQ,CAAC;CAChE,IAAI,cAAsB;CAC1B,IAAI,oBAAmC;CACvC,IAAI;CACJ,MAAM,kBAA4B,CAAC;CAEnC,IAAI,QAAQ,QAAQ,KAAK;CAEzB,OAAO,UAAU,UAAU,KAAK;EAC9B,QAAQ,OAAR;GACE,KAAK,UAAU;IACb,cAAc,QAAQ,aAAa;IACnC,oBAAoB;IACpB,qBAAqB;IACrB;GAEF,KAAK,UAAU;IACb,QAAQ,KAAK;KAAE,YAAY;KAAO,OAAO,QAAQ,eAAe;KAAG,KAAK,QAAQ,YAAY;IAAE,CAAC;IAC/F;GAEF,KAAK,UAAU;IACb,QAAQ,KAAK;KAAE,YAAY;KAAoB,OAAO,QAAQ,eAAe;KAAG,KAAK,QAAQ,YAAY;IAAE,CAAC;IAC5G;GAEF,KAAK,UAAU;IACb,oBAAoB,QAAQ,aAAa;IACzC;GAEF,KAAK,UAAU;IACb,IAAI,sBAAsB,SAAS,YAAY,YAAY,MAAM,UAAU;KACzE,IAAI,QAAQ,QAAQ,aAAa;KACjC,IAAI,MAAM,OAAO,OAAO,MAAM,OAAO,MACnC,QAAQ,MAAM,MAAM,GAAG,EAAE;KAE3B,gBAAgB,KAAK,KAAK;IAC5B,OAAO,IAAI,sBAAsB,UAAU,YAAY,YAAY,MAAM,UAAU;KACjF,MAAM,QAAQ,QAAQ,aAAa;KACnC,IAAI,qEAAqE,KAAK,KAAK,KAAK,UAAU,UAChG,qBAAqB;UAChB,IAAI,2BAA2B,KAAK,KAAK,GAC9C,qBAAqB;UAErB,qBAAqB;IAEzB,OAAO;KACL,MAAM,sBAAsB,qBAAqB,iBAAkB;KACnE,IAAI,qBAAqB;MACvB,IAAI,QAAQ,QAAQ,eAAe;MACnC,IAAI,MAAM,QAAQ,YAAY;MAC9B,MAAM,YAAY,SAAS,QAAQ,CAAC,CAAC;MACrC,IAAI,cAAc,OAAO,cAAc,MAAK;OAC1C;OACA;MACF;MACA,QAAQ,KAAK;OAAE,YAAY;OAAqB;OAAO;OAAK,gBAAgB;MAAK,CAAC;KACpF;IACF;IACA,oBAAoB;EAGxB;EACA,QAAQ,QAAQ,KAAK;CACvB;CACA,OAAO;EACL,oBAAoB,UAAiB,kBAAkB,UAAU,SAAS,KAAK;EAC/E,sBAAsB,YAAoB,gCACxC,oBAAoB,UAAU,SAAS,YAAY,2BAA2B;EAChF,wBAAwB,aAAuB,sBAAsB,UAAU,SAAS,QAAQ;EAChG,8BAA8B,uBAAuB,UAAU,OAAO;EACtE,0BAA0B;CAC5B;AACF;AAEA,SAAS,kBAAkB,UAAwB,SAA2B,OAA+B;CAC3G,MAAM,SAA0B,CAAC;CACjC,IAAI,aAAa,QAAQ,MAAM,QAAQ,SAAS,OAAO,GAAG,CAAC;CAC3D,IAAI,gBAAgB,QAAQ,SAAS,SAAS,MAAM,KAAK,IAAI;CAC7D,MAAM,YAAY,QAAQ,SAAS,SAAS,MAAM,GAAG,IAAI,SAAS,QAAQ,CAAC,CAAC;CAC5E,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,EAAE,OAAO,MAAM,iBAAiB,OAAO,QAAQ,YACjD;EAGF,MAAM,QAAQ,KAAK,IAAI,OAAO,OAAO,aAAa;EAClD,MAAM,WAAW,SAAS,WAAW,KAAK;EAC1C,IAAI,gBAAgB,OAAO,OACzB,OAAO,KAAK;GACV,OAAO;GACP,KAAK;GACL,YAAY;EACd,CAAC;EAEH,MAAM,MAAM,KAAK,IAAI,OAAO,KAAK,SAAS;EAC1C,MAAM,SAAS,SAAS,WAAW,GAAG;EACtC,IAAI,MAAM,OAAO,OACf,OAAO,KAAK;GACV,OAAO;GACP,KAAK;GACL,YAAY,OAAO;GACnB,gBAAgB,OAAO;EACzB,CAAC;EAEH,gBAAgB;EAChB,aAAa;CACf;CACA,IAAI,gBAAgB,WAAW;EAC7B,MAAM,SAAS,QAAQ,MAAM,MAAM,SAAS,WAAW,SAAS;EAChE,OAAO,KAAK;GACV,OAAO;GACP,KAAK;GACL,YAAY;EACd,CAAC;CACH;CACA,OAAO;AACT;AAEA,SAAS,uBAAuB,WAAyB,SAAqC;CAC5F,MAAM,SAAmB,CAAC;CAE1B,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,EAAE,OAAO,cAAc,CAAC,OAAO,SAAS,OAAO,UAAU,IAC3D;EAGF,OAAO,KAAK,OAAO,UAAU;EAC7B,IAAI,OAAO,WAAW,GACpB,OAAO;CAEX;CAEA,OAAO,KAAK,MAAM;CAElB,OAAO;AACT;AAEA,SAAS,sBAAsB,UAAwB,SAA2B,UAAwC;CACxH,MAAM,SAAS,SAAS,SAAS,QAAQ;CACzC,KAAK,MAAM,UAAU,SACnB,IAAI,OAAO,SAAS,QAClB;MAAI,UAAU,OAAO,KACnB,OAAO,OAAO;CAChB,OAEA;CAGJ,OAAO;AACT;AAEA,SAAS,oBACP,UACA,UACA,YACA,6BACc;CACd,IAAI,aAAa;CACjB,MAAM,aAAa,SAAS,QAAQ;CACpC,IAAI,SAAS;CACb,IAAI,aAAa;CACjB,KAAK,MAAM,KAAK,UAAU;EACxB,IAAI,EAAE,EAAE,eAAe,eAAe,CAAC,+BAA+B,CAAC,EAAE,kBACvE;EAGF,SAAS,yBAAyB,QAAQ,YAAY,EAAE,OAAO,YAAY,YAAY,UAAU,CAAC,CAAC;EACnG,UAAU,cAAc,GAAG,WAAW,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC;EAC3D,aAAa,EAAE;EACf,aAAa,UAAU,CAAC;CAC1B;CACA,SAAS,yBAAyB,QAAQ,YAAY,WAAW,QAAQ,YAAY,YAAY,EAAE;CACnG,OAAO,aAAa,OAAO,SAAS,KAAK,YAAY,SAAS,SAAS,MAAM;AAC/E;AAEA,SAAS,UAAU,GAAmB;CACpC,IAAI,EAAE,gBACJ,QAAQ,EAAE,YAAV;EACE,KAAK,OACH,OAAO;CAEX;CAEF,OAAO;AACT;AACA,SAAS,UAAU,GAAmB;CACpC,IAAI,EAAE,gBACJ,QAAQ,EAAE,YAAV;EACE,KAAK,OACH,OAAO;EAET,KAAK,cACH,OAAO;CAEX;CAEF,OAAO;AACT;AACA,SAAS,cAAc,GAAmB,SAAyB;CACjE,IAAI,CAAC,EAAE,kBAAkB,EAAE,eAAe,cACxC,OAAO,QAAQ,QAAQ,QAAQ,KAAK,CAAC,CAAC,QAAQ,OAAO,KAAK;CAE5D,IAAI,EAAE,eAAe,OAEnB,OAAO,QAAQ,QAAQ,oBAAc,OAAO,GAAG,WAAmB;EAChE,MAAM,SAAS,IAAI,OAAO,MAAM,SAAS,CAAC;EAC1C,MAAM,YAAY,QAAQ,SAAS,MAAM;EACzC,IAAI,CAAC,aAAa,UAAU,SAAS,GAAG,GACtC,OAAO,GAAG,OAAO;EAEnB,OAAO,IAAI;CACb,CAAC;CAEH,OAAO;AACT;AAEA,SAAS,yBAAyB,QAAgB,OAAe,KAAa,YAAoB,QAAgB,OAAe;CAC/H,UAAU;CACV,IAAI,gBAAgB,CAAC,OAAO;CAC5B,KAAK,IAAI,QAAQ,OAAO,QAAQ,KAAK,SAAS;EAC5C,MAAM,KAAK,WAAW;EACtB,IAAI,OAAO,QAAQ,OAAO,MAAM;GAE9B,gBAAgB;GAChB,UAAU;EACZ,OACE;CAEJ;CACA,SAAS,OAAO,QAAQ,KAAK,gBAAgB,MAAM,MAAM;CACzD,UAAU;CACV,OAAO;AACT;AAEA,SAAS,OAAO,QAAgB,SAAiB,GAAmB;CAClE,OAAO,IAAI,GAAG;EACZ,IAAI,IAAI,GACN,UAAU;EAEZ,MAAM;EACN,WAAW;CACb;CACA,OAAO;AACT;AAEA,SAAS,qBAAqB,eAAsC;CAClE,MAAM,QAAQ,cAAc,MAAM,sBAAsB;CACxD,IAAI,CAAC,OACH,OAAO;CAET,OAAO,MAAM,KAAK,QAAQ;AAC5B;;;;;AC1QA,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;;AAG5B,SAAS,QAAW,OAAY,OAAmC;CACjE,MAAM,yBAAS,IAAI,IAAiB;CACpC,MAAM,UAAiB,CAAC;CAExB,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,MAAM,MAAM,IAAI;EAEtB,IAAI,QAAQ,OAAO,IAAI,GAAG;EAC1B,IAAI,CAAC,OAAO;GACV,QAAQ,CAAC;GACT,OAAO,IAAI,KAAK,KAAK;GACrB,QAAQ,KAAK,KAAK;EACpB;EAEA,MAAM,KAAK,IAAI;CACjB;CAEA,OAAO;AACT;;AAGA,SAAS,qBAAqB,cAA8E;CAC1G,IAAI;CAEJ,KAAK,MAAM,eAAe,cAAc;EACtC,IAAI,OAAO,gBAAgB,YAAY,SAAS,YAAY;EAC5D,OAAO,YAAY;CACrB;CAEA,OAAO;AACT;;AAGA,SAAS,kBAAkB,cAA0C;CACnE,MAAM,UAAU,aAAa,QAAO,gBAAe,gBAAgB,MAAS;CAG5E,IAAI,QAAQ,UAAU,GAAG,OAAO,QAAQ;CAExC,MAAM,QAAkB,CAAC;CACzB,MAAM,uBAAO,IAAI,IAAY;CAE7B,KAAK,MAAM,eAAe,SAAS;EACjC,MAAM,QAAQ,OAAO,gBAAgB,WAAW,cAAc,YAAY,MAAK,CAAE,KAAK;EACtF,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,GAAG;EAE7B,KAAK,IAAI,IAAI;EACb,MAAM,KAAK,IAAI;CACjB;CAEA,IAAI,MAAM,WAAW,GAAG,OAAO;CAE/B,MAAM,OAAO,qBAAqB,OAAO;CACzC,MAAM,QAAQ,MAAM,KAAK,SAAS,cAAc,sBAAsB,kBAAkB;CAExF,OAAO,OAAO;EAAE;EAAM;CAAM,IAAI;AAClC;AAEA,SAAS,gBAAgB,YAAoE;CAC3F,MAAM,SAAuB,CAAC;CAC9B,MAAM,uBAAO,IAAI,IAAY;CAE7B,KAAK,MAAM,QAAQ,YAAY;EAC7B,MAAM,UAAU,QAAQ,CAAC;EAEzB,KAAK,MAAM,aAAa,SAAS;GAC/B,MAAM,MAAM,GAAG,UAAU,KAAK,GAAG,UAAU;GAC3C,IAAI,KAAK,IAAI,GAAG,GAAG;GAEnB,KAAK,IAAI,GAAG;GACZ,OAAO,KAAK,SAAS;EACvB;CACF;CAEA,OAAO,OAAO,SAAS,IAAI,SAAS;AACtC;AAEA,SAAS,cAAc,UAA0D;CAC/E,MAAM,yBAAS,IAAI,IAAY;CAE/B,KAAK,MAAM,QAAQ,UAAU;EAC3B,MAAM,UAAU,QAAQ,CAAC;EAEzB,KAAK,MAAM,WAAW,SACpB,OAAO,IAAI,OAAO;CAEtB;CAEA,OAAO,OAAO,OAAO,IAAI,CAAC,GAAG,MAAM,IAAI;AACzC;;AAGA,SAAS,cAAc,WAA2B,WAAoC;CACpF,MAAM,SAAS,UAAU,UAAU,CAAC;CACpC,IAAI,CAAC,UAAU,UAAU,OAAO;CAEhC,OAAO,CAAC,GAAI,UAAU,IAAI,UAAU,QAAQ,KAAK,CAAC,GAAI,GAAG,MAAM;AACjE;;AAGA,SAAS,YAAY,QAAoC;CACvD,OAAO,QAAQ,SAAQ,UAAS,MAAM,IAAI,CAAC,CAAC,KAAI,UAAS;EACvD,IAAI,MAAM,WAAW,GAAG,OAAO,MAAM;EAErC,OAAO;GACL,MAAM,MAAM,EAAE,CAAC;GACf,aAAa,kBAAkB,MAAM,KAAI,UAAS,MAAM,WAAW,CAAC;GACpE,YAAY,gBAAgB,MAAM,KAAI,UAAS,MAAM,UAAU,CAAC;GAChE,UAAU,cAAc,MAAM,KAAI,UAAS,MAAM,QAAQ,CAAC;GAC1D,QAAQ,MAAM,MAAK,UAAS,MAAM,MAAM,CAAC,EAAE;EAC7C;CACF,CAAC;AACH;;;;;;;AAQA,SAAS,gBAAgB,YAA8B,WAAwC;CAC7F,OAAO,QAAQ,aAAY,cAAa,UAAU,KAAK,YAAY,CAAC,CAAC,CAAC,KAAI,UAAS;EAEjF,IAAI,MAAM,WAAW,KAAK,CAAC,MAAM,EAAE,CAAC,UAAU,OAAO,MAAM;EAE3D,MAAM,SAAS,YAAY,MAAM,SAAQ,cAAa,cAAc,WAAW,SAAS,CAAC,CAAC;EAE1F,OAAO;GACL,MAAM,MAAM,EAAE,CAAC;GACf,aAAa,kBAAkB,MAAM,KAAI,cAAa,UAAU,WAAW,CAAC;GAE5E,QAAQ,OAAO,SAAS,IAAI,SAAS;GACrC,YAAY,gBAAgB,MAAM,KAAI,cAAa,UAAU,UAAU,CAAC;GACxE,UAAU,cAAc,MAAM,KAAI,cAAa,UAAU,QAAQ,CAAC;GAClE,QAAQ,MAAM,MAAK,cAAa,UAAU,MAAM,CAAC,EAAE;EACrD;CACF,CAAC;AACH;;AAGA,SAAS,UAAU,MAAkB,WAAkC;CACrE,OAAO,QAAQ,OAAM,QAAO,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,KAAI,UAAS;EAC/D,MAAM,aAAa,gBACjB,MAAM,SAAQ,QAAO,IAAI,cAAc,CAAC,CAAC,GACzC,SACF;EAEA,IAAI,MAAM,WAAW,GAAG,OAAO;GAAE,GAAG,MAAM;GAAI;EAAW;EAEzD,OAAO;GACL,MAAM,MAAM,EAAE,CAAC;GACf,aAAa,kBAAkB,MAAM,KAAI,QAAO,IAAI,WAAW,CAAC;GAChE;GACA,YAAY,gBAAgB,MAAM,KAAI,QAAO,IAAI,UAAU,CAAC;GAC5D,UAAU,cAAc,MAAM,KAAI,QAAO,IAAI,QAAQ,CAAC;GACtD,QAAQ,MAAM,MAAK,QAAO,IAAI,MAAM,CAAC,EAAE;GACvC,MAAM,MAAM,MAAK,QAAO,IAAI,IAAI;EAClC;CACF,CAAC;AACH;;AAGA,SAAS,iBAAiB,UAAmC;CAC3D,MAAM,4BAAuB,IAAI,IAAI;CACrC,MAAM,YAAY,SAAS,SAAQ,SAAQ,KAAK,aAAa,CAAC,CAAC;CAE/D,KAAK,MAAM,YAAY,WAAW;EAChC,MAAM,WAAW,UAAU,IAAI,SAAS,IAAI;EAC5C,UAAU,IAAI,SAAS,MAAM,WAAW,YAAY,CAAC,GAAG,UAAU,GAAG,SAAS,MAAM,CAAC,IAAI,SAAS,MAAM;CAC1G;CAEA,OAAO;AACT;;;;;AAMA,SAAgB,6BAA6B,IAAY,UAA2C;CAClG,MAAM,YAAY,iBAAiB,QAAQ;CAG3C,MAAM,OAAO,UACX,SAAS,SAAQ,SAAQ,KAAK,QAAQ,CAAC,CAAC,GACxC,SACF;CAEA,MAAM,mBAAmB,gBACvB,SAAS,SAAQ,SAAQ,KAAK,oBAAoB,CAAC,CAAC,GACpD,SACF;CAEA,MAAM,aAAa,IAAI,IAAI,KAAK,KAAI,QAAO,CAAC,IAAI,KAAK,YAAY,GAAG,GAAG,CAAC,CAAC;CACzE,MAAM,kCAAkB,IAAI,IAA8B;CAE1D,SAAS,kBAAkB,KAA+B;EACxD,MAAM,MAAM,IAAI,YAAY;EAE5B,MAAM,SAAS,gBAAgB,IAAI,GAAG;EACtC,IAAI,QAAQ,OAAO;EAGnB,MAAM,gBAAgB,WAAW,IAAI,GAAG,CAAC,EAAE;EAC3C,IAAI,CAAC,iBAAiB,cAAc,WAAW,GAAG,OAAO;EAEzD,MAAM,aAAa,gBAAgB,CAAC,GAAG,kBAAkB,GAAG,aAAa,GAAG,SAAS;EACrF,gBAAgB,IAAI,KAAK,UAAU;EAEnC,OAAO;CACT;CAEA,OAAO;EACL,QAAQ;GACN,OAAO;EACT;EAEA,aAAa,YAAY;GACvB,OAAO,eAAe;EACxB;EAEA,cAAc;GACZ,OAAO;EACT;EAEA;EAEA,cAAc,KAAK,WAAW;GAC5B,MAAM,OAAO,UAAU,YAAY;GAGnC,OAAO,kBAAkB,GAAG,CAAC,CAAC,MAAK,MAAK,EAAE,KAAK,YAAY,MAAM,IAAI,CAAC,EAAE,UAAU,CAAC;EACrF;CACF;AACF;;;;AChPA,SAAgB,YAAY,qBAAoD;CAC9E,MAAM,gBAAgB,sBAAoC,IAAI,KAAI,aAAY,oBAAoB,kBAAkB,QAAQ,CAAC;CAG7H,IAAI;CACJ,IAAI;CAEJ,SAAS,qBAAqB,UAAwB;EACpD,IAAI,CAAC,gBAAgB,iBAAiB,UAAU;GAC9C,eAAe;GACf,eAAe,6BAA6B,eAAe,QAAQ;EACrE;EAEA,oBAAoB,iBAAiB,OAAO,CAAC,YAAY,CAAC;CAC5D;CAEA,OAAO;EACL,QAAQ;GACN,OAAO;EACT;EAEA,MAAM,WAAW,UAAU,UAAU,iBAAiB,UAAU;GAC9D,qBAAqB,QAAQ;GAE7B,MAAM,eAAe,cAAc,IAAI,QAAQ;GAC/C,MAAM,iBAAiB,MAAM,oBAAoB,YAAY,UAAU,UAAU,cAAc,eAAe;GAE9G,KAAK,MAAM,QAAQ,eAAe,OAChC,KAAK,WAAW,OAAO,KAAK;GAG9B,OAAO;EACT;EAEA,MAAM,QAAQ,UAAU,UAAU,UAAU;GAC1C,qBAAqB,QAAQ;GAE7B,OAAO,oBAAoB,QAAQ,UAAU,UAAU,cAAc,IAAI,QAAQ,CAAC;EACpF;EAEA,MAAM,kBAAkB,UAAwB;GAC9C,cAAc,kBAAkB,QAAQ;EAC1C;EAEA,MAAM,kBAAkB,UAAU,iBAAiB,aAAa;GAC9D,MAAM,WAAW,IAAI,SAAS,WAAW;GACzC,MAAM,aAAa,UAAU,IAAI,MAAM,SAAS,GAAG,CAAC,CAAC;GACrD,MAAM,sBAAsB,KAAK,SAAS,aAAa,UAAU;GAEjE,MAAM,QAAQ,oBAAoB,kBAAkB,UAAU,eAAe;GAE7E,KAAK,MAAM,QAAQ,OAAO;IACxB,IAAI,CAAC,KAAK,QAAQ;IAElB,MAAM,SAAS,UAAU,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC,CAAC;IACtD,MAAM,SAAS,KAAK,SAAS,KAAK,QAAQ,UAAU,GAAG,MAAM;IAE7D,MAAM,WAAW,SAAS,QAAQ,QAAQ,mBAAmB;IAC7D,IAAI,UACF,KAAK,SAAS,UAAU,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS;GAE7D;GAEA,OAAO;EACT;EAEA,UAAU;GACR,cAAc,QAAQ;EACxB;CACF;AACF;;;;ACMA,MAAa,gBAAgB;AAE7B,SAAgB,iBAAiB,oBAAwC,gBAAmD;CAC1H,MAAM,sBAAsB,WAAW,mBAAmB;EACxD;EACA,oBAAoB;EACpB,qBAAqB,CACnB;GACE,QAAQ;IACN,OAAO;GACT;GACA,aAAa,YAAY;IACvB,OAAO,eAAe;GACxB;GACA,cAAc,KAAK,WAAW;IAC5B,OAAO,CAAC,EAAE,MAAM,OAAO,IAAI,aAAa,YAAY,CAAC;GACvD;GACA,cAAc;IACZ,OAAO,CAAC;KAAE,MAAM;KAAc,aAAa;KAA4B,YAAY,CAAC;IAAE,CAAC;GACzF;GAEA,kBAAkB,KAAa;IAC7B,IAAI,QAAQ,cACV,OAAO,CACL;KACE,MAAM;KACN,aAAa;KACb,QAAQ,CAAC,EAAE,MAAM,6BAA6B,CAAC;IACjD,CACF;IAGF,OAAO,CAAC,EAAE,MAAM,mBAAmB,CAAC;GACtC;EACF,CACF;EACA,wBAAwB;CAC1B,CAAC;CAED,MAAM,kBAAkB,sBAA2C,IAAI,KAAI,aACzE,mBAAmB,qBAAqB,QAAQ,CAClD;CAEA,IAAI,cAA6C,CAAC,eAAe;CAEjE,IAAI,QAAQ,OAAO,OAAO,IAAI;CAC9B,MAAM,UAAU,YAAY,mBAAmB;CAE/C,OAAO;EAEL,MAAM,oBAAoB,eAAmD;GAC3E,oBAAoB,iBAAiB,MAAM,aAAa;EAC1D;EAEA,kBAAkB,UAAwB,UAA8C;GACtF,MAAM,aAAa,gBAAgB,IAAI,QAAQ,CAAC,CAAC,sBAAsB,QAAQ;GAC/E,IAAI,YACF,OAAO,MAAM;EAIjB;EAEA,gBAAgB,UAAwB,OAAmC;GACzE,OAAO,gBACJ,IAAI,QAAQ,CAAC,CACb,kBAAkB,KAAK,CAAC,CACxB,KAAK,MAAyB;IAC7B,OAAO;KACL,OAAO,EAAE;KACT,KAAK,EAAE;KACP,MAAM,EAAE,aAAa,MAAM,EAAE,cAAc;KAC3C,gBAAgB,EAAE;IACpB;GACF,CAAC;EACL;EAEA,sBAAsB,UAAwC;GAC5D,MAAM,SAAyB,CAAC;GAChC,KAAK,MAAM,cAAc,gBAAgB,IAAI,QAAQ,CAAC,CAAC,uBAAuB,GAAG;IAC/E,MAAM,OAAO,MAAM;IACnB,IAAI,MACF,OAAO,KAAK,IAAI;GAEpB;GAEA,OAAO;EACT;EAEA,cAA8B;GAC5B,MAAM,SAAS,CAAC;GAChB,KAAK,MAAM,cAAc,OAAO;IAC9B,MAAM,OAAO,MAAM;IACnB,IAAI,MACF,OAAO,KAAK,IAAI;GAEpB;GAEA,OAAO;EACT;EAEA,QAAQ,YAAkC;GACxC,OAAO,MAAM;EACf;EAEA,kBAAkB,UAAwB;GACxC,KAAK,MAAM,MAAM,aACf,GAAG,kBAAkB,QAAQ;GAG/B,KAAK,MAAM,QAAQ,OACjB,MAAM,KAAK,CAAC,kBAAkB,QAAQ;EAE1C;EAEA,UAAgB;GACd,KAAK,MAAM,MAAM,aACf,GAAG,QAAQ;GAGb,cAAc,CAAC;GACf,KAAK,MAAM,QAAQ,OACjB,MAAM,KAAK,CAAC,QAAQ;GAGtB,QAAQ,CAAC;EACX;CACF;AACF;;;;AClNA,MAAa,gBAAgB,EAAE,MAAM,IAAI,YAAuC,SAAS,EAAE;AAG3F,MAAa,yBAAyB,EAAE,MAAM,IAAI,YAAmD,YAAY,EAAE;AAEnH,MAAaC,aAAW,OAAO,OAAO;;CAEpC,SAAS;;CAGT,MAAM;;CAGN,WAAW;;CAGX,cAAc;AAChB,CAAC;AAuBD,SAAgB,sBACd,gBACA,YACA,SACoB;CACpB,MAAM,SAAS,QAAQ,UAAU,eAAe,SAAS,MAAM,IAAI,QAAQ,SAAS;CAEpF,OAAO;EACL,MAAM,KAAK,KAAgC;GACzC,IAAI,UAAU,IAAI,WAAW,OAAO,GAClC,OAAO,OAAO,KAAK,GAAG;GAIxB,OAAO,MADc,WAAW,YAAY,cAAc,MAAM,GAAG;EAErE;EAEA,cAAc,KAA4C;GACxD,IAAI,UAAU,IAAI,WAAW,OAAO,GAClC,OAAO,OAAO,cAAc,GAAG;GAGjC,OAAO,WAAW,YAAY,uBAAuB,MAAM,GAAG;EAChE;CACF;AACF;;;;ACvEA,SAAgB,QAAW,IAAS,MAAW;CAC7C,IAAI,MACF,KAAK,MAAM,QAAQ,MACjB,GAAG,KAAK,IAAI;AAGlB;;;;ACiBA,SAAgB,eAAe,UAAkB,QAAyB;CACxE,IAAI,SAAS,SAAS,OAAO,QAC3B,OAAO;CAIT,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SACzC,IAAI,SAAS,WAAW,OAAO,QAC7B,OAAO;CAIX,OAAO;AACT;AAEA,SAAgB,aAAa,UAAkB,QAAyB;CACtE,MAAM,OAAO,SAAS,SAAS,OAAO;CACtC,IAAI,OAAO,GACT,OAAO,SAAS,QAAQ,QAAQ,IAAI,MAAM;CAG5C,IAAI,SAAS,GACX,OAAO,aAAa;CAGtB,OAAO;AACT;AAsBA,MAAM,KAAK,KAAK,YAAY,CAAC;AAC7B,MAAM,KAAK,KAAK,YAAY,CAAC;;;;ACjE7B,SAAgB,mBAAmB,aAAqB,kBAAsD;CAC5G,SAAS,gBAAoC;EAC3C,KAAK,MAAM,UAAU,kBAAkB;GACrC,IAAI,YAAY,OAAO;GACvB,IAAI,CAAC,aAAa,WAAW,GAAG,GAC9B,aAAa;GAGf,IAAI,eAAe,aAAa,SAAS,GACvC,OAAO;EAEX;CAGF;CAEA,OAAO,EACL,mBAAmB,WAAmB,OAAO,gBAAgB;EAC3D,IAAI,iBAAiB,KAAK,SAAS,GAEjC,OAAO;EAGT,IAAI,UAAU,OAAO,KAAK;GAExB,MAAM,YAAY,cAAc;GAChC,IAAI,WACF,OAAO,YAAY,UAAU,MAAM,CAAC;EAExC;EAEA,MAAM,UAAU,UAAU,IAAI,MAAM,IAAI;EACxC,MAAM,mBAAmB,QAAQ,KAAK,SAAS,GAAG,IAAI,UAAU,UAAU,MAAM,QAAQ,OAAO;EAC/F,OAAO,UAAU,MAAM,YAAY,kBAAkB,SAAS,CAAC,CAAC,SAAS,IAAI;CAC/E,EACF;AACF;;;;ACvCA,SAAgB,gBAAgB,SAAgC;CAC9D,IAAI,YAAYC,OAAK,QAAQ,UAAU,IAAI,MAAM,OAAO,CAAC,CAAC,MAAM;CAEhE,OAAO,MAAM;EACX,KAAK,MAAM,UAAU,CAAC,kBAAkB,gBAAgB,GACtD,IAAI,GAAG,WAAWA,OAAK,KAAK,WAAW,MAAM,CAAC,GAC5C,OAAO;EAIX,MAAM,SAASA,OAAK,QAAQ,SAAS;EAGrC,IAAI,WAAW,WACb,OAAO;EAGT,YAAY;CACd;AACF;;;;AClBA,SAAgB,YAAY,SAAiB,OAAwB;CACnE,IAAI,iBAAiB,OACnB,OAAO,GAAG,QAAQ,IAAI,MAAM,QAAQ,IAAI,MAAM;CAGhD,IAAI,OAAO,UAAU,UACnB,OAAO,GAAG,QAAQ,IAAI;CAGxB,IAAI,OACF,OAAO,GAAG,QAAQ,IAAI;CAGxB,OAAO;AACT;AAEA,SAAgB,QACd,SACA,WACA,YACA,cACA,OACoC;CACpC,OAAO,IAAI,SAAkC,YAAW;EACtD,QAAQ,MAAM,mBAAmB;GAC/B,IAAI,MAAM,yBAAyB;IACjC,QAAQ,YAAY,CAAC;IACrB;GACF;GACA,OAAO,UAAU,CAAC,CAAC,MACjB,WAAU;IACR,IAAI,MAAM,yBAAyB;KACjC,QAAQ,YAAY,CAAC;KACrB;IACF;IAEA,QAAQ,MAAM;GAChB,IAEA,UAAS;IACP,QAAQ,MAAM,YAAY,cAAc,KAAK,CAAC;IAC9C,QAAQ,UAAU;GACpB,CACF;EACF,CAAC;CACH,CAAC;AACH;AAEA,SAAS,cAAiB;CACxB,OAAO,IAAI,OAAO,cAAiB,OAAO,cAAc,kBAAkB,mBAAmB;AAC/F;;;;ACvBA,SAAgB,YAAY,YAAwB,SAA6B;CAE/E,MAAM,YAAY,IAAIC,SAAO,cAAc,YAAY;CAGvD,UAAU,OAAO,UAAU;CAE3B,IAAI,iBAAoC,CAAC;CACzC,IAAI,mBAAsC,CAAC;CAC3C,IAAI;CAEJ,IAAI;CAEJ,MAAM,mBAA0D,CAAC;CAGjE,UAAU,YAAW,aAAY;EAC/B,OAAO,iBAAiB,SAAS,SAAS;CAC5C,CAAC;CAED,WAAW,cAAc,eAAiC;EACxD,IAAI,MAAM,QAAQ,WAAW,gBAAgB,GAC3C,iBAAiB,WAAW;OACvB;GACL,iBAAiB,CAAC;GAClB,IAAI,WAAW,UACb,eAAe,KAAK;IAAE,MAAM;IAAI,KAAK,UAAU,IAAI,KAAK,WAAW,QAAQ,CAAC,CAAC,SAAS;GAAE,CAAC;EAE7F;EAEA,mBAAmB,uBAAuB,cAAc;EACxD,WAAW,QAAQ,IACjB,wCAAwC,iBAAiB,KAAI,MAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI,CAC3G;EAEA,MAAM,qBAAqB,sBAAsB,CAAC,MAAM,GAAG,YAAY,OAAO;EAC9E,gBAAgB,iBAAiB,WAAW,cAAc,kBAAkB;EAE5E,gBAAgB,IAAI,cAAc,WAAW,OAAO;EAEpD,UAAU,YAAW,aAAY;GAC/B,cAAc,kBAAkB,SAAS,QAAQ;EACnD,CAAC;EAED,WAAW,iBAAiB;GAC1B,cAAc,QAAQ;GACtB,cAAc,QAAQ;EACxB,CAAC;EAED,OAAO,EACL,cAAc;GACZ,kBAAkBA,SAAO,qBAAqB;GAC9C,oBAAoB;IAClB,iBAAiB;IACjB,mBAAmB;KAAC;KAAK;KAAK;KAAK;KAAK;KAAK;IAAG;GAClD;GACA,eAAe;GACf,sBAAsB,EAAE,iBAAiB,MAAM;EACjD,EACF;CACF,CAAC;CAED,WAAW,oBAAoB,CAAC,CAAC;CAEjC,WAAW,aAAa,OAAO,sBAAsB,UAAU;EAC7D,OAAO,QACL,SACA,YAAY;GACV,MAAM,cAAc,gBAAgB,qBAAqB,aAAa,GAAG;GACzE,IAAI,CAAC,aAAa,OAAO;GAEzB,MAAM,WAAW,UAAU,IAAI,qBAAqB,aAAa,GAAG;GACpE,IAAI,CAAC,UAAU,OAAO;GAEtB,MAAM,OAAO,cAAc,kBAAkB,UAAU,qBAAqB,QAAQ;GACpF,IAAI,CAAC,MAAM,YAAY,OAAO;IAAE,cAAc;IAAM,OAAO,CAAC;GAAE;GAG9D,IAAI,CAAC,MADgB,cAAc,IAAI,WAAW,GACrC,OAAO;IAAE,cAAc;IAAM,OAAO,CAAC;GAAE;GAEpD,MAAM,kBAAkB,mBAAmB,SAAS,KAAK,gBAAgB;GACzE,OAAO,KAAK,WAAW,UAAU,qBAAqB,UAAU,iBAAiB,cAAc,WAAW;EAC5G,GACA,MACA,yCAAyC,qBAAqB,aAAa,OAC3E,KACF;CACF,CAAC;CAED,WAAW,qBAAqB,MAAM,UAAU;EAC9C,OAAO,QACL,SACA,YAAY;GACV,MAAM,OAAO,KAAK;GAClB,IAAI,CAAC,qBAAqB,IAAI,GAAG,OAAO;GAExC,MAAM,WAAW,UAAU,IAAI,KAAK,GAAG;GACvC,IAAI,CAAC,UAAU,OAAO;GAEtB,MAAM,OAAO,cAAc,QAAQ,KAAK,UAAU;GAClD,IAAI,CAAC,MAAM,WAAW,OAAO;GAE7B,OAAO,KAAK,UAAU,UAAU,IAAI;EACtC,GACA,MACA,6CACA,KACF;CACF,CAAC;CAED,WAAW,SAAS,sBAAsB,UAAU;EAClD,OAAO,QACL,SACA,YAAY;GACV,MAAM,cAAc,gBAAgB,qBAAqB,aAAa,GAAG;GACzE,IAAI,CAAC,aAAa,OAAO;GAEzB,MAAM,WAAW,UAAU,IAAI,qBAAqB,aAAa,GAAG;GACpE,IAAI,CAAC,UAAU,OAAO;GAEtB,MAAM,OAAO,cAAc,kBAAkB,UAAU,qBAAqB,QAAQ;GACpF,IAAI,CAAC,MAAM,SAAS,OAAO;GAG3B,IAAI,CAAC,MADgB,cAAc,IAAI,WAAW,GACrC,OAAO;GAEpB,OAAO,KAAK,QAAQ,UAAU,qBAAqB,UAAU,cAAc,WAAW;EACxF,GACA,MACA,mCAAmC,qBAAqB,aAAa,OACrE,KACF;CACF,CAAC;CAED,WAAW,iBAAiB,uBAAuB,UAAU;EAC3D,OAAO,QACL,SACA,YAAY;GACV,MAAM,cAAc,gBAAgB,sBAAsB,aAAa,GAAG;GAC1E,IAAI,CAAC,aAAa,OAAO;GAEzB,MAAM,WAAW,UAAU,IAAI,sBAAsB,aAAa,GAAG;GACrE,IAAI,CAAC,UAAU,OAAO,CAAC;GAEvB,MAAM,QAA+B,CAAC;GAEtC,MAAM,kBAAkB,mBAAmB,SAAS,KAAK,gBAAgB;GACzE,KAAK,MAAM,QAAQ,cAAc,sBAAsB,QAAQ,GAC7D,IAAI,KAAK,mBACP,QAAQ,OAAO,MAAM,KAAK,kBAAkB,UAAU,iBAAiB,WAAW,CAAC;GAIvF,OAAO;EACT,GACA,CAAC,GACD,kCAAkC,sBAAsB,aAAa,OACrE,KACF;CACF,CAAC;CAGD,WAAW,OAAO;AACpB;AAEA,SAAgB,qBAAqB,OAA6D;CAChG,OAAO,SAAS,OAAO,MAAM,eAAe,YAAY,OAAO,MAAM,QAAQ,YAAY,OAAO,MAAM,WAAW;AACnH;;;;AClMA,MAAM,WAAW,OAAO,OAAO;;CAE7B,SAAS;;CAET,MAAM;;CAEN,WAAW;;CAEX,cAAc;AAChB,CAAC;AAID,SAAgB,gBAAoC;CAClD,SAAS,cAAc,UAAkB;EACvC,IAAI,CAAC,SAAS,WAAW,OAAO,GAC9B,MAAM,IAAI,MAAM,8CAA8C;CAElE;CACA,OAAO;EACL,KAAK,UAAkB;GACrB,cAAc,QAAQ;GACtB,OAAO,IAAI,SAAS,GAAG,WAAW;IAChC,MAAM,MAAM,UAAU,IAAI,MAAM,QAAQ;IACxC,GAAG,KAAK,IAAI,SAAS,OAAO,UAAU;KACpC,IAAI,OACF,OAAO,MAAM,SAAS,WAAW,EAAE;MAAE,MAAM,SAAS;MAAS,OAAO;MAAI,OAAO;MAAI,MAAM;KAAG,CAAC,IAAI,OAAO,KAAK;KAG/G,IAAI,OAAiB,SAAS;KAC9B,IAAI,MAAM,OAAO,GACf,OAAO,SAAS;UACX,IAAI,MAAM,YAAY,GAC3B,OAAO,SAAS;UACX,IAAI,MAAM,eAAe,GAC9B,OAAO,SAAS;KAGlB,EAAE;MACA;MACA,OAAO,MAAM,MAAM,QAAQ;MAC3B,OAAO,MAAM,MAAM,QAAQ;MAC3B,MAAM,MAAM;KACd,CAAC;IACH,CAAC;GACH,CAAC;EACH;EACA,cAAc,UAAkB;GAC9B,cAAc,QAAQ;GACtB,OAAO,IAAI,SAAS,GAAG,WAAW;IAChC,MAAM,OAAO,UAAU,IAAI,MAAM,QAAQ,CAAC,CAAC;IAE3C,GAAG,QAAQ,MAAM,EAAE,eAAe,KAAK,IAAI,OAAO,aAAa;KAC7D,IAAI,OACF,OAAO,OAAO,KAAK;KAErB,EACE,SAAS,KAAI,SAAQ;MACnB,IAAI,KAAK,eAAe,GACtB,OAAO,CAAC,KAAK,MAAM,SAAS,YAAY;MAE1C,IAAI,KAAK,YAAY,GACnB,OAAO,CAAC,KAAK,MAAM,SAAS,SAAS;MAEvC,OAAO,KAAK,OAAO,IAAI,CAAC,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,KAAK,MAAM,SAAS,OAAO;KAClF,CAAC,CACH;IACF,CAAC;GACH,CAAC;EACH;CACF;AACF;;;;ACjEA,MAAM,aAAyB,GAAG,iBAAiB;;;;;;AAOnD,SAAS,QAAQ,OAAkC;CACjD,QAAQ,GAAG,aAAwB,MAAM,yBAAyB,OAAO,GAAG,QAAQ,CAAC,CAAC;AACxF;AAEA,QAAQ,MAAM,QAAQ,WAAW,QAAQ,IAAI,KAAK,WAAW,OAAO,CAAC;AACrE,QAAQ,OAAO,QAAQ,WAAW,QAAQ,KAAK,KAAK,WAAW,OAAO,CAAC;AACvE,QAAQ,OAAO,QAAQ,WAAW,QAAQ,KAAK,KAAK,WAAW,OAAO,CAAC;AACvE,QAAQ,QAAQ,QAAQ,WAAW,QAAQ,MAAM,KAAK,WAAW,OAAO,CAAC;AAEzE,QAAQ,GAAG,uBAAuB,UAAmB;CACnD,WAAW,QAAQ,MAAM,YAAY,uBAAuB,KAAK,CAAC;AACpE,CAAC;AAED,MAAM,UAA8B;CAClC,OAAO;EACL,aAAa,UAA8C,GAAG,YAAmC;GAC/F,MAAM,SAAS,aAAa,UAAU,GAAG,UAAU;GACnD,OAAO,EAAE,eAAe,eAAe,MAAM,EAAE;EACjD;EAEA,WAAW,UAA8C,IAAY,GAAG,YAAmC;GACzG,MAAM,SAAS,WAAW,UAAU,IAAI,GAAG,UAAU;GACrD,OAAO,EAAE,eAAe,aAAa,MAAM,EAAE;EAC/C;CACF;CAEA,QAAQ,cAAc;AACxB;AAEA,WAAW,QAAQ,IAAI,8BAA8B;AACrD,YAAY,YAAY,OAAO"}
|
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.26",
|
|
5
5
|
"author": "Ahmed ALABSI",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@staticbolt/core": "1.0.0-beta.
|
|
7
|
+
"@staticbolt/core": "1.0.0-beta.26",
|
|
8
8
|
"vscode-html-languageservice": "^5.6.2",
|
|
9
9
|
"vscode-languageserver": "^10.0.1",
|
|
10
10
|
"vscode-languageserver-textdocument": "^1.0.13",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
],
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@staticbolt/core": "1.0.0-beta.
|
|
26
|
+
"@staticbolt/core": "1.0.0-beta.26"
|
|
27
27
|
},
|
|
28
28
|
"private": false,
|
|
29
29
|
"scripts": {
|
|
@@ -176,7 +176,8 @@ export class ConfigManager {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
private async collectData(config: StaticBoltConfig) {
|
|
179
|
-
|
|
179
|
+
// collected into a new array and published at the end, consumers cache on its identity
|
|
180
|
+
const collected: HTMLDataV1[] = [];
|
|
180
181
|
|
|
181
182
|
const plugins = config.plugins ?? [];
|
|
182
183
|
|
|
@@ -191,10 +192,12 @@ export class ConfigManager {
|
|
|
191
192
|
const htmlData = await plugin.lspHtmlData();
|
|
192
193
|
if (htmlData) {
|
|
193
194
|
htmlDataInjectPluginName(htmlData, plugin.name);
|
|
194
|
-
|
|
195
|
+
collected.push(htmlData);
|
|
195
196
|
}
|
|
196
197
|
}
|
|
197
198
|
}
|
|
199
|
+
|
|
200
|
+
this.lspHtmlData = collected;
|
|
198
201
|
}
|
|
199
202
|
}
|
|
200
203
|
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
HTMLDataV1,
|
|
3
|
+
IAttributeData,
|
|
4
|
+
IHTMLDataProvider,
|
|
5
|
+
IReference,
|
|
6
|
+
ITagData,
|
|
7
|
+
IValueData,
|
|
8
|
+
MarkupContent,
|
|
9
|
+
} from "vscode-html-languageservice";
|
|
10
|
+
|
|
11
|
+
type Description = string | MarkupContent | undefined;
|
|
12
|
+
type DescriptionKind = MarkupContent["kind"];
|
|
13
|
+
type ValueSets = Map<string, IValueData[]>;
|
|
14
|
+
|
|
15
|
+
/** Separates the documentation of two contributors. Rendered as a horizontal rule, unless the description is plain text. */
|
|
16
|
+
const MARKDOWN_SEPARATOR = "\n\n---\n\n";
|
|
17
|
+
const PLAINTEXT_SEPARATOR = "\n\n";
|
|
18
|
+
|
|
19
|
+
/** Groups entries by key, preserving the order of first appearance. */
|
|
20
|
+
function groupBy<T>(items: T[], toKey: (item: T) => string): T[][] {
|
|
21
|
+
const groups = new Map<string, T[]>();
|
|
22
|
+
const ordered: T[][] = [];
|
|
23
|
+
|
|
24
|
+
for (const item of items) {
|
|
25
|
+
const key = toKey(item);
|
|
26
|
+
|
|
27
|
+
let group = groups.get(key);
|
|
28
|
+
if (!group) {
|
|
29
|
+
group = [];
|
|
30
|
+
groups.set(key, group);
|
|
31
|
+
ordered.push(group);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
group.push(item);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return ordered;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Markdown wins over plain text, so a contributor asking for rich text still gets it. Plain strings imply no preference. */
|
|
41
|
+
function mergeDescriptionKind(descriptions: Exclude<Description, undefined>[]): DescriptionKind | undefined {
|
|
42
|
+
let kind: DescriptionKind | undefined;
|
|
43
|
+
|
|
44
|
+
for (const description of descriptions) {
|
|
45
|
+
if (typeof description === "string" || kind === "markdown") continue;
|
|
46
|
+
kind = description.kind;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return kind;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Concatenates every distinct description, so no contributor's documentation gets lost. */
|
|
53
|
+
function mergeDescriptions(descriptions: Description[]): Description {
|
|
54
|
+
const defined = descriptions.filter(description => description !== undefined);
|
|
55
|
+
|
|
56
|
+
// nothing to merge, keep the original as-is
|
|
57
|
+
if (defined.length <= 1) return defined[0];
|
|
58
|
+
|
|
59
|
+
const parts: string[] = [];
|
|
60
|
+
const seen = new Set<string>();
|
|
61
|
+
|
|
62
|
+
for (const description of defined) {
|
|
63
|
+
const text = (typeof description === "string" ? description : description.value).trim();
|
|
64
|
+
if (!text || seen.has(text)) continue;
|
|
65
|
+
|
|
66
|
+
seen.add(text);
|
|
67
|
+
parts.push(text);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (parts.length === 0) return undefined;
|
|
71
|
+
|
|
72
|
+
const kind = mergeDescriptionKind(defined);
|
|
73
|
+
const value = parts.join(kind === "plaintext" ? PLAINTEXT_SEPARATOR : MARKDOWN_SEPARATOR);
|
|
74
|
+
|
|
75
|
+
return kind ? { kind, value } : value;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function mergeReferences(references: (IReference[] | undefined)[]): IReference[] | undefined {
|
|
79
|
+
const merged: IReference[] = [];
|
|
80
|
+
const seen = new Set<string>();
|
|
81
|
+
|
|
82
|
+
for (const list of references) {
|
|
83
|
+
const entries = list ?? [];
|
|
84
|
+
|
|
85
|
+
for (const reference of entries) {
|
|
86
|
+
const key = `${reference.name} ${reference.url}`;
|
|
87
|
+
if (seen.has(key)) continue;
|
|
88
|
+
|
|
89
|
+
seen.add(key);
|
|
90
|
+
merged.push(reference);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return merged.length > 0 ? merged : undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function mergeBrowsers(browsers: (string[] | undefined)[]): string[] | undefined {
|
|
98
|
+
const merged = new Set<string>();
|
|
99
|
+
|
|
100
|
+
for (const list of browsers) {
|
|
101
|
+
const entries = list ?? [];
|
|
102
|
+
|
|
103
|
+
for (const browser of entries) {
|
|
104
|
+
merged.add(browser);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return merged.size > 0 ? [...merged] : undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The values an attribute contributes, with its `valueSet` reference expanded. */
|
|
112
|
+
function resolveValues(attribute: IAttributeData, valueSets: ValueSets): IValueData[] {
|
|
113
|
+
const values = attribute.values ?? [];
|
|
114
|
+
if (!attribute.valueSet) return values;
|
|
115
|
+
|
|
116
|
+
return [...(valueSets.get(attribute.valueSet) ?? []), ...values];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Merges entries sharing the same value name into one. Unlike tags and attributes, value names are case-sensitive. */
|
|
120
|
+
function mergeValues(values: IValueData[]): IValueData[] {
|
|
121
|
+
return groupBy(values, value => value.name).map(group => {
|
|
122
|
+
if (group.length === 1) return group[0];
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
name: group[0].name,
|
|
126
|
+
description: mergeDescriptions(group.map(value => value.description)),
|
|
127
|
+
references: mergeReferences(group.map(value => value.references)),
|
|
128
|
+
browsers: mergeBrowsers(group.map(value => value.browsers)),
|
|
129
|
+
status: group.find(value => value.status)?.status,
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Merges entries sharing the same attribute name into one, combining their descriptions, values, references and browser support.
|
|
136
|
+
*
|
|
137
|
+
* `valueSet` references are expanded into the merged `values`, so attributes contributed by different plugins can each bring
|
|
138
|
+
* their own value set and still end up with a single, complete value list.
|
|
139
|
+
*/
|
|
140
|
+
function mergeAttributes(attributes: IAttributeData[], valueSets: ValueSets): IAttributeData[] {
|
|
141
|
+
return groupBy(attributes, attribute => attribute.name.toLowerCase()).map(group => {
|
|
142
|
+
// nothing to merge and no value set to expand, keep the original as-is
|
|
143
|
+
if (group.length === 1 && !group[0].valueSet) return group[0];
|
|
144
|
+
|
|
145
|
+
const values = mergeValues(group.flatMap(attribute => resolveValues(attribute, valueSets)));
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
name: group[0].name,
|
|
149
|
+
description: mergeDescriptions(group.map(attribute => attribute.description)),
|
|
150
|
+
// `valueSet` is dropped on purpose: every referenced set is now expanded into `values`
|
|
151
|
+
values: values.length > 0 ? values : undefined,
|
|
152
|
+
references: mergeReferences(group.map(attribute => attribute.references)),
|
|
153
|
+
browsers: mergeBrowsers(group.map(attribute => attribute.browsers)),
|
|
154
|
+
status: group.find(attribute => attribute.status)?.status,
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Merges entries sharing the same tag name into one, including their attributes. */
|
|
160
|
+
function mergeTags(tags: ITagData[], valueSets: ValueSets): ITagData[] {
|
|
161
|
+
return groupBy(tags, tag => tag.name.toLowerCase()).map(group => {
|
|
162
|
+
const attributes = mergeAttributes(
|
|
163
|
+
group.flatMap(tag => tag.attributes ?? []),
|
|
164
|
+
valueSets
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
if (group.length === 1) return { ...group[0], attributes };
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
name: group[0].name,
|
|
171
|
+
description: mergeDescriptions(group.map(tag => tag.description)),
|
|
172
|
+
attributes,
|
|
173
|
+
references: mergeReferences(group.map(tag => tag.references)),
|
|
174
|
+
browsers: mergeBrowsers(group.map(tag => tag.browsers)),
|
|
175
|
+
status: group.find(tag => tag.status)?.status,
|
|
176
|
+
void: group.some(tag => tag.void),
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Value sets sharing a name are merged, so an attribute referencing one gets the values of every contributor. */
|
|
182
|
+
function collectValueSets(htmlData: HTMLDataV1[]): ValueSets {
|
|
183
|
+
const valueSets: ValueSets = new Map();
|
|
184
|
+
const collected = htmlData.flatMap(data => data.valueSets ?? []);
|
|
185
|
+
|
|
186
|
+
for (const valueSet of collected) {
|
|
187
|
+
const existing = valueSets.get(valueSet.name);
|
|
188
|
+
valueSets.set(valueSet.name, existing ? mergeValues([...existing, ...valueSet.values]) : valueSet.values);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return valueSets;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Builds a single data provider out of every collected `HTMLDataV1`, merging tags, attributes and values that share the same name
|
|
196
|
+
* instead of reporting them once per contributor.
|
|
197
|
+
*/
|
|
198
|
+
export function createMergedHtmlDataProvider(id: string, htmlData: HTMLDataV1[]): IHTMLDataProvider {
|
|
199
|
+
const valueSets = collectValueSets(htmlData);
|
|
200
|
+
|
|
201
|
+
// the language service asks for tags on every parse, and for attributes on every completion/hover, so merging happens once here
|
|
202
|
+
const tags = mergeTags(
|
|
203
|
+
htmlData.flatMap(data => data.tags ?? []),
|
|
204
|
+
valueSets
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const globalAttributes = mergeAttributes(
|
|
208
|
+
htmlData.flatMap(data => data.globalAttributes ?? []),
|
|
209
|
+
valueSets
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const tagsByName = new Map(tags.map(tag => [tag.name.toLowerCase(), tag]));
|
|
213
|
+
const attributesByTag = new Map<string, IAttributeData[]>();
|
|
214
|
+
|
|
215
|
+
function provideAttributes(tag: string): IAttributeData[] {
|
|
216
|
+
const key = tag.toLowerCase();
|
|
217
|
+
|
|
218
|
+
const cached = attributesByTag.get(key);
|
|
219
|
+
if (cached) return cached;
|
|
220
|
+
|
|
221
|
+
// an unknown tag only sees the global attributes, which are merged already
|
|
222
|
+
const tagAttributes = tagsByName.get(key)?.attributes;
|
|
223
|
+
if (!tagAttributes || tagAttributes.length === 0) return globalAttributes;
|
|
224
|
+
|
|
225
|
+
const attributes = mergeAttributes([...globalAttributes, ...tagAttributes], valueSets);
|
|
226
|
+
attributesByTag.set(key, attributes);
|
|
227
|
+
|
|
228
|
+
return attributes;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
getId() {
|
|
233
|
+
return id;
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
isApplicable(languageId) {
|
|
237
|
+
return languageId === "html";
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
provideTags() {
|
|
241
|
+
return tags;
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
provideAttributes,
|
|
245
|
+
|
|
246
|
+
provideValues(tag, attribute) {
|
|
247
|
+
const name = attribute.toLowerCase();
|
|
248
|
+
|
|
249
|
+
// merged attributes already carry their expanded value sets
|
|
250
|
+
return provideAttributes(tag).find(a => a.name.toLowerCase() === name)?.values ?? [];
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { format, stripVTControlCharacters } from "node:util";
|
|
1
2
|
import * as vs from "vscode-languageserver/node";
|
|
2
3
|
|
|
3
4
|
import { startServer } from "./html-server.ts";
|
|
@@ -10,8 +11,19 @@ import type { Connection, Disposable } from "vscode-languageserver/node";
|
|
|
10
11
|
// Create a connection for the server.
|
|
11
12
|
const connection: Connection = vs.createConnection();
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
console
|
|
14
|
+
/**
|
|
15
|
+
* `RemoteConsole` takes a single string, but the shared logger calls `console` with several arguments and colours them with
|
|
16
|
+
* chalk. Passing the methods straight through drops everything after the first argument, and the output panel renders no ANSI —
|
|
17
|
+
* so format the arguments the way `console` would, then strip the escapes.
|
|
18
|
+
*/
|
|
19
|
+
function forward(write: (message: string) => void) {
|
|
20
|
+
return (...messages: unknown[]) => write(stripVTControlCharacters(format(...messages)));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log = forward(connection.console.log.bind(connection.console));
|
|
24
|
+
console.info = forward(connection.console.info.bind(connection.console));
|
|
25
|
+
console.warn = forward(connection.console.warn.bind(connection.console));
|
|
26
|
+
console.error = forward(connection.console.error.bind(connection.console));
|
|
15
27
|
|
|
16
28
|
process.on("unhandledRejection", (error: unknown) => {
|
|
17
29
|
connection.console.error(formatError(`Unhandled exception`, error));
|
package/src/modes/html-mode.ts
CHANGED
|
@@ -3,22 +3,36 @@ import path from "node:path";
|
|
|
3
3
|
import { Resolver } from "@staticbolt/core";
|
|
4
4
|
import * as vscodeUri from "vscode-uri";
|
|
5
5
|
|
|
6
|
+
import { createMergedHtmlDataProvider } from "../helpers/merge-html-data.ts";
|
|
6
7
|
import { getLanguageModelCache } from "../language-model-cache.ts";
|
|
7
8
|
import { FILE_PROTOCOL } from "./language-modes.ts";
|
|
8
9
|
|
|
9
10
|
import type { LanguageMode } from "./language-modes.ts";
|
|
10
|
-
import type { HTMLDataV1, HTMLDocument, LanguageService, TextDocument } from "vscode-html-languageservice";
|
|
11
|
+
import type { HTMLDataV1, HTMLDocument, IHTMLDataProvider, LanguageService, TextDocument } from "vscode-html-languageservice";
|
|
11
12
|
|
|
12
13
|
export function getHTMLMode(htmlLanguageService: LanguageService): LanguageMode {
|
|
13
14
|
const htmlDocuments = getLanguageModelCache<HTMLDocument>(10, 60, document => htmlLanguageService.parseHTMLDocument(document));
|
|
14
15
|
|
|
16
|
+
// the merged provider is rebuilt only when the collected html data actually changes
|
|
17
|
+
let lastHtmlData: HTMLDataV1[] | undefined;
|
|
18
|
+
let lastProvider: IHTMLDataProvider | undefined;
|
|
19
|
+
|
|
20
|
+
function setHtmlDataProviders(htmlData: HTMLDataV1[]) {
|
|
21
|
+
if (!lastProvider || lastHtmlData !== htmlData) {
|
|
22
|
+
lastHtmlData = htmlData;
|
|
23
|
+
lastProvider = createMergedHtmlDataProvider(FILE_PROTOCOL, htmlData);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
htmlLanguageService.setDataProviders(false, [lastProvider]);
|
|
27
|
+
}
|
|
28
|
+
|
|
15
29
|
return {
|
|
16
30
|
getId() {
|
|
17
31
|
return "html";
|
|
18
32
|
},
|
|
19
33
|
|
|
20
34
|
async doComplete(document, position, documentContext, htmlData) {
|
|
21
|
-
setHtmlDataProviders(
|
|
35
|
+
setHtmlDataProviders(htmlData);
|
|
22
36
|
|
|
23
37
|
const htmlDocument = htmlDocuments.get(document);
|
|
24
38
|
const completionList = await htmlLanguageService.doComplete2(document, position, htmlDocument, documentContext);
|
|
@@ -31,7 +45,7 @@ export function getHTMLMode(htmlLanguageService: LanguageService): LanguageMode
|
|
|
31
45
|
},
|
|
32
46
|
|
|
33
47
|
async doHover(document, position, htmlData) {
|
|
34
|
-
setHtmlDataProviders(
|
|
48
|
+
setHtmlDataProviders(htmlData);
|
|
35
49
|
|
|
36
50
|
return htmlLanguageService.doHover(document, position, htmlDocuments.get(document));
|
|
37
51
|
},
|
|
@@ -67,33 +81,3 @@ export function getHTMLMode(htmlLanguageService: LanguageService): LanguageMode
|
|
|
67
81
|
},
|
|
68
82
|
};
|
|
69
83
|
}
|
|
70
|
-
|
|
71
|
-
function setHtmlDataProviders(htmlLanguageService: LanguageService, htmlData: HTMLDataV1[]) {
|
|
72
|
-
htmlLanguageService.setDataProviders(false, [
|
|
73
|
-
{
|
|
74
|
-
getId() {
|
|
75
|
-
return FILE_PROTOCOL;
|
|
76
|
-
},
|
|
77
|
-
isApplicable(languageId) {
|
|
78
|
-
return languageId === "html";
|
|
79
|
-
},
|
|
80
|
-
provideValues(tag, attribute) {
|
|
81
|
-
const tags = htmlData.flatMap(data => data.tags?.filter(t => t.name === tag) ?? []);
|
|
82
|
-
const attributes = tags.flatMap(t => t.attributes?.filter(a => a.name === attribute) ?? []);
|
|
83
|
-
const globalAttributes = htmlData.flatMap(data => data.globalAttributes?.filter(a => a.name === attribute) ?? []);
|
|
84
|
-
const values = [...globalAttributes, ...attributes].flatMap(a => a.values ?? []);
|
|
85
|
-
return values;
|
|
86
|
-
},
|
|
87
|
-
provideTags() {
|
|
88
|
-
const tags = htmlData.flatMap(data => data.tags ?? []);
|
|
89
|
-
return tags;
|
|
90
|
-
},
|
|
91
|
-
provideAttributes(tag: string) {
|
|
92
|
-
const tags = htmlData.flatMap(data => data.tags?.filter(t => t.name === tag) ?? []);
|
|
93
|
-
const globalAttributes = htmlData.flatMap(data => data.globalAttributes ?? []);
|
|
94
|
-
const attributes = tags.flatMap(t => t.attributes ?? []);
|
|
95
|
-
return [...globalAttributes, ...attributes];
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
]);
|
|
99
|
-
}
|