feat(language-server): merge HTML completions and hover into one response

This commit is contained in:
2026-08-18 20:56:17 +05:30
parent d77638131b
commit 97801287f9
5 changed files with 22543 additions and 14 deletions
@@ -1,5 +1,10 @@
import { expect, test } from "bun:test";
import { isInsideHtml, viewRegions, virtualHtmlDocument } from "../src/html-regions.ts";
import {
clearHtmlRegionCache,
isInsideHtml,
viewRegions,
virtualHtmlDocument,
} from "../src/html-regions.ts";
function doc(text: string): { uri: string; text: string; version?: number } {
return { uri: `file:///${Math.random()}.wrn`, text };
@@ -99,6 +104,22 @@ test("regions are cached per document version", () => {
expect(isInsideHtml(first, PAGE.indexOf("<div"))).toBe(false);
});
test("clearHtmlRegionCache drops a closed document's cached regions", () => {
// A reopened document commonly restarts at version 1. Without clearing the
// cache on close, that version would match the stale entry from the prior
// session and serve regions scanned from the old text.
const first = doc(PAGE);
first.version = 1;
expect(isInsideHtml(first, PAGE.indexOf("<div"))).toBe(true);
clearHtmlRegionCache(first.uri);
// Same uri, same version 1, but a document with no view block at all: if
// the cache had survived, this would still report true from the old scan.
const reopened = { uri: first.uri, text: "page A { }", version: 1 };
expect(isInsideHtml(reopened, PAGE.indexOf("<div"))).toBe(false);
});
test("isInsideHtml distinguishes markup from surrounding code", () => {
const source = doc(PAGE);
const markupOffset = PAGE.indexOf("<div");
@@ -111,9 +111,7 @@ test("does not misfire inside a quoted attribute value containing a slash", () =
}
}
`;
expect(
htmlTagComplete(doc(singleQuoted), positionOf(singleQuoted, `src='/assets/`)),
).toBeNull();
expect(htmlTagComplete(doc(singleQuoted), positionOf(singleQuoted, `src='/assets/`))).toBeNull();
});
test("still completes a self-close after a preceding attribute", () => {
@@ -151,3 +149,26 @@ test("folding ranges stay inside view regions", () => {
const viewStartLine = text.slice(0, text.indexOf("view {")).split("\n").length - 1;
for (const range of ranges) expect(range.startLine).toBeGreaterThan(viewStartLine - 1);
});
import { mergeCompletions } from "../src/html-service.ts";
test("merging ranks WRNexus entries above HTML and drops exact collisions", () => {
const merged = mergeCompletions(
[
{ label: "Card", kind: 7 },
{ label: "table", kind: 7 },
],
[
{ label: "div", kind: 10, sortText: "1div" },
{ label: "table", kind: 10, sortText: "1table" },
],
);
const labels = merged.map((item) => item.label);
expect(labels.filter((label) => label === "table")).toHaveLength(1);
expect(merged.find((item) => item.label === "Card")?.sortText?.startsWith("0")).toBe(true);
expect(merged.find((item) => item.label === "div")?.sortText?.startsWith("1")).toBe(true);
const sorted = [...merged].sort((a, b) => (a.sortText ?? "").localeCompare(b.sortText ?? ""));
expect(sorted[0]!.label).toBe("Card");
});