fix(watcher): close deleted open documents
This commit is contained in:
@@ -247,6 +247,14 @@ export class LspClient {
|
|||||||
return uri;
|
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.
|
// Send Raw LSP Request - Passthrough used by the command dispatcher.
|
||||||
sendRequest<R = unknown>(method: string, params: unknown): Promise<R> {
|
sendRequest<R = unknown>(method: string, params: unknown): Promise<R> {
|
||||||
return this.conn.sendRequest(method, params) as 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 DEFAULT_IDLE_TTL_MS = 5 * 60 * 1000;
|
||||||
const WATCHER_READY_TIMEOUT_MS = 5000;
|
const WATCHER_READY_TIMEOUT_MS = 5000;
|
||||||
|
const FILE_CHANGE_DELETED = 3;
|
||||||
|
|
||||||
// Client Entry - One LspClient per (server.id, rootDir), plus the bookkeeping
|
// Client Entry - One LspClient per (server.id, rootDir), plus the bookkeeping
|
||||||
// needed to keep files in sync and evict on idleness.
|
// 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 {
|
function forwardEvents(entry: ClientEntry, events: FileEvent[]): void {
|
||||||
try {
|
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) {
|
if (process.env.LSP_DEBUG) {
|
||||||
log(`watcher fire`, entry.server.id, JSON.stringify(events));
|
log(`watcher fire`, entry.server.id, JSON.stringify(events));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ interface DiagResult {
|
|||||||
[serverId: string]: { diagnostics?: { message: string }[] };
|
[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 tmpDir: string;
|
||||||
let mainFile: string;
|
let mainFile: string;
|
||||||
let helperFile: 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)}`,
|
`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