fix(watcher): close deleted open documents
This commit is contained in:
@@ -247,6 +247,14 @@ export class LspClient {
|
||||
return uri;
|
||||
}
|
||||
|
||||
closeDocument(uri: string): void {
|
||||
this.versions.delete(uri);
|
||||
this.diagnostics.delete(uri);
|
||||
this.conn.sendNotification("textDocument/didClose", {
|
||||
textDocument: { uri },
|
||||
});
|
||||
}
|
||||
|
||||
// Send Raw LSP Request - Passthrough used by the command dispatcher.
|
||||
sendRequest<R = unknown>(method: string, params: unknown): Promise<R> {
|
||||
return this.conn.sendRequest(method, params) as Promise<R>;
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
|
||||
const DEFAULT_IDLE_TTL_MS = 5 * 60 * 1000;
|
||||
const WATCHER_READY_TIMEOUT_MS = 5000;
|
||||
const FILE_CHANGE_DELETED = 3;
|
||||
|
||||
// Client Entry - One LspClient per (server.id, rootDir), plus the bookkeeping
|
||||
// needed to keep files in sync and evict on idleness.
|
||||
@@ -152,6 +153,11 @@ async function waitForWatcherReady(entry: ClientEntry): Promise<void> {
|
||||
|
||||
function forwardEvents(entry: ClientEntry, events: FileEvent[]): void {
|
||||
try {
|
||||
for (const event of events) {
|
||||
if (event.type !== FILE_CHANGE_DELETED || !entry.opened.has(event.uri)) continue;
|
||||
entry.client.closeDocument(event.uri);
|
||||
entry.opened.delete(event.uri);
|
||||
}
|
||||
if (process.env.LSP_DEBUG) {
|
||||
log(`watcher fire`, entry.server.id, JSON.stringify(events));
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ interface DiagResult {
|
||||
[serverId: string]: { diagnostics?: { message: string }[] };
|
||||
}
|
||||
|
||||
describe("watcher: gopls picks up externally-created files", { skip: skip ?? undefined }, () => {
|
||||
describe("watcher: gopls picks up external file changes", { skip: skip ?? undefined }, () => {
|
||||
let tmpDir: string;
|
||||
let mainFile: string;
|
||||
let helperFile: string;
|
||||
@@ -122,4 +122,48 @@ describe("watcher: gopls picks up externally-created files", { skip: skip ?? und
|
||||
`Expected diagnostic to clear after creating helper.go, still got: ${JSON.stringify(finalDiags)}`,
|
||||
);
|
||||
});
|
||||
|
||||
it("closes an opened file when it is deleted externally", async () => {
|
||||
fs.writeFileSync(
|
||||
helperFile,
|
||||
"package main\n\nfunc Helper(x int) {}\n",
|
||||
);
|
||||
await runCliJson([helperFile, "diagnostics", '{"timeoutMs":3000}'], env);
|
||||
|
||||
await pollUntil(
|
||||
async () =>
|
||||
(await runCliJson(
|
||||
[mainFile, "diagnostics", '{"timeoutMs":3000}'],
|
||||
env,
|
||||
)) as DiagResult,
|
||||
(r) => {
|
||||
const diags = r["gopls"]?.diagnostics ?? [];
|
||||
return diags.some((d) => d.message.includes("not enough arguments"));
|
||||
},
|
||||
15000,
|
||||
500,
|
||||
);
|
||||
|
||||
fs.rmSync(helperFile);
|
||||
|
||||
const result = await pollUntil(
|
||||
async () =>
|
||||
(await runCliJson(
|
||||
[mainFile, "diagnostics", '{"timeoutMs":3000}'],
|
||||
env,
|
||||
)) as DiagResult,
|
||||
(r) => {
|
||||
const diags = r["gopls"]?.diagnostics ?? [];
|
||||
return diags.some((d) => d.message.toLowerCase().includes("undefined"));
|
||||
},
|
||||
15000,
|
||||
500,
|
||||
);
|
||||
|
||||
const finalDiags = result["gopls"]?.diagnostics ?? [];
|
||||
assert.ok(
|
||||
finalDiags.some((d) => d.message.toLowerCase().includes("undefined")),
|
||||
`Expected undefined-symbol diagnostic after deleting opened helper.go, got: ${JSON.stringify(finalDiags)}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user