feat(language-server): add tag folding and linked editing
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
||||
htmlCompletions,
|
||||
htmlFoldingRanges,
|
||||
htmlHover,
|
||||
htmlLinkedEditingRanges,
|
||||
htmlTagComplete,
|
||||
} from "../src/html-service.ts";
|
||||
|
||||
@@ -172,3 +173,25 @@ test("merging ranks WRNexus entries above HTML and drops exact collisions", () =
|
||||
const sorted = [...merged].sort((a, b) => (a.sortText ?? "").localeCompare(b.sortText ?? ""));
|
||||
expect(sorted[0]!.label).toBe("Card");
|
||||
});
|
||||
|
||||
test("linked editing returns both the opening and closing tag names", () => {
|
||||
const text = `page A {
|
||||
view {
|
||||
<div>x</div>
|
||||
}
|
||||
}
|
||||
`;
|
||||
const ranges = htmlLinkedEditingRanges(doc(text), positionOf(text, "<di"));
|
||||
expect(ranges).not.toBeNull();
|
||||
expect(ranges).toHaveLength(2);
|
||||
});
|
||||
|
||||
test("linked editing returns null outside a view block", () => {
|
||||
const text = `page A {
|
||||
functions {
|
||||
function go() { }
|
||||
}
|
||||
}
|
||||
`;
|
||||
expect(htmlLinkedEditingRanges(doc(text), positionOf(text, "func"))).toBeNull();
|
||||
});
|
||||
|
||||
@@ -305,3 +305,69 @@ test("didClose drops the region cache so a reopened document at the same version
|
||||
process.kill();
|
||||
await process.exited;
|
||||
});
|
||||
|
||||
test("advertises and serves folding ranges and linked editing ranges over the wire", async () => {
|
||||
const process = Bun.spawn(
|
||||
["bun", "run", fileURLToPath(new URL("../src/server.ts", import.meta.url))],
|
||||
{ stdin: "pipe", stdout: "pipe", stderr: "pipe" },
|
||||
);
|
||||
const uri = "file:///fold.wrn";
|
||||
const text = `page A {
|
||||
view {
|
||||
<div>x</div>
|
||||
}
|
||||
}
|
||||
`;
|
||||
const reader = process.stdout.getReader();
|
||||
let output = "";
|
||||
async function readUntil(marker: string): Promise<void> {
|
||||
while (!output.includes(marker)) {
|
||||
const chunk = await reader.read();
|
||||
if (chunk.done) break;
|
||||
output += new TextDecoder().decode(chunk.value);
|
||||
}
|
||||
}
|
||||
|
||||
process.stdin.write(packet({ jsonrpc: "2.0", id: 1, method: "initialize", params: {} }));
|
||||
await process.stdin.flush();
|
||||
await readUntil('"id":1');
|
||||
const initReply = output.slice(output.indexOf('"id":1'));
|
||||
expect(initReply).toContain('"foldingRangeProvider":true');
|
||||
expect(initReply).toContain('"linkedEditingRangeProvider":true');
|
||||
|
||||
process.stdin.write(
|
||||
packet({
|
||||
jsonrpc: "2.0",
|
||||
method: "textDocument/didOpen",
|
||||
params: { textDocument: { uri, version: 1, text } },
|
||||
}),
|
||||
);
|
||||
process.stdin.write(
|
||||
packet({
|
||||
jsonrpc: "2.0",
|
||||
id: 2,
|
||||
method: "textDocument/foldingRange",
|
||||
params: { textDocument: { uri } },
|
||||
}),
|
||||
);
|
||||
await process.stdin.flush();
|
||||
await readUntil('"id":2');
|
||||
const foldReply = output.slice(output.indexOf('"id":2'));
|
||||
expect(foldReply).toContain('"result":[');
|
||||
|
||||
process.stdin.write(
|
||||
packet({
|
||||
jsonrpc: "2.0",
|
||||
id: 3,
|
||||
method: "textDocument/linkedEditingRange",
|
||||
params: { textDocument: { uri }, position: { line: 2, character: 6 } },
|
||||
}),
|
||||
);
|
||||
await process.stdin.flush();
|
||||
await readUntil('"id":3');
|
||||
const linkedReply = output.slice(output.indexOf('"id":3'));
|
||||
expect(linkedReply).toContain('"ranges"');
|
||||
|
||||
process.kill();
|
||||
await process.exited;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user