From dbd7d6fca60fcf2886887ca9e5d83b5bb4b5e865 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 08:28:51 +0000 Subject: [PATCH 1/3] Initial plan From 1e232baa4eaf9d11bb40e2bb66961e46172ac234 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 08:37:58 +0000 Subject: [PATCH 2/3] Add testbed reproducing diagnostics pull on saved unsaved-but-named buffer --- testbed/client/src/extension.ts | 4 ++- testbed/package.json | 11 +++++++ testbed/server/src/server.ts | 22 +++++++++++++- testbed/workspace/repro-1797.md | 51 ++++++++++++++++++++++++++++++++ testbed/workspace/sample.testbed | 2 ++ 5 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 testbed/workspace/repro-1797.md create mode 100644 testbed/workspace/sample.testbed diff --git a/testbed/client/src/extension.ts b/testbed/client/src/extension.ts index 89575fd5e..cbfdc2be1 100644 --- a/testbed/client/src/extension.ts +++ b/testbed/client/src/extension.ts @@ -24,6 +24,7 @@ export async function activate(context: ExtensionContext) { documentSelector: [ { language: 'bat' }, { language: 'bat', notebook: '*' }, + { language: 'testbed' }, { scheme: 'file', pattern: '**/.vscode/test.txt' } ], synchronize: { @@ -53,7 +54,8 @@ export async function activate(context: ExtensionContext) { onFocus: true, match: (selector, resource) => { const fsPath = resource.fsPath; - return path.extname(fsPath) === '.bat'; + const ext = path.extname(fsPath); + return ext === '.bat' || ext === '.testbed'; } }, textSynchronization: { diff --git a/testbed/package.json b/testbed/package.json index b167a9312..e2e9f23c2 100644 --- a/testbed/package.json +++ b/testbed/package.json @@ -13,6 +13,17 @@ "enabledApiProposals": [ ], "contributes": { + "languages": [ + { + "id": "testbed", + "aliases": [ + "Testbed" + ], + "extensions": [ + ".testbed" + ] + } + ], "commands": [ { "command": "testbed.openFile", diff --git a/testbed/server/src/server.ts b/testbed/server/src/server.ts index 95110dc67..b421cb6cc 100644 --- a/testbed/server/src/server.ts +++ b/testbed/server/src/server.ts @@ -18,7 +18,7 @@ import { SemanticTokensClientCapabilities, SemanticTokensLegend, SemanticTokensBuilder, SemanticTokensRegistrationType, SemanticTokensRegistrationOptions, ProtocolNotificationType, ChangeAnnotation, WorkspaceChange, CompletionItemKind, DiagnosticSeverity, DocumentDiagnosticReportKind, WorkspaceDiagnosticReport, NotebookDocuments, CompletionList, DidChangeConfigurationNotification, - NotificationType + NotificationType, ErrorCodes } from 'vscode-languageserver/node'; import { @@ -229,10 +229,18 @@ documents.onDidChangeContent((_event) => { // void connection.sendDiagnostics({ uri: document.uri, diagnostics: validate(document) }); }); +documents.onDidOpen((event) => { + connection.console.info(`Document opened: ${event.document.uri} ${event.document.version}`); +}); + documents.onDidSave((event) => { connection.console.info(`Document got saved: ${event.document.uri} ${event.document.version}`); }); +documents.onDidClose((event) => { + connection.console.info(`Document closed: ${event.document.uri}`); +}); + connection.onDidChangeWatchedFiles((params) => { connection.console.log('File change event received'); // documents.all().forEach(document => { @@ -335,6 +343,18 @@ let versionCounter: number = 1; connection.languages.diagnostics.on(async (param) => { const uri = URI.parse(param.textDocument.uri); const document = documents.get(param.textDocument.uri); + connection.console.info(`Diagnostic pull request for ${param.textDocument.uri} (open: ${document !== undefined})`); + // Reproduces microsoft/vscode-languageserver-node#1797 / #1771: after saving an + // "unsaved but named" buffer, the client closes the `untitled:` document and then + // still issues a `textDocument/diagnostic` request for that same, now closed, + // `untitled:` document. A real language server (e.g. tsserver) has no project for + // such a URI anymore and fails the request. We mimic that behavior here so the + // problematic flow surfaces as an error instead of being silently swallowed. + if (document === undefined && uri.scheme !== 'file') { + const message = `no project found for URI ${param.textDocument.uri}`; + connection.console.error(`error handling method 'textDocument/diagnostic': ${message}`); + throw new ResponseError(ErrorCodes.InvalidParams, message); + } const content = document !== undefined ? document.getText() : uri.scheme === 'file' diff --git a/testbed/workspace/repro-1797.md b/testbed/workspace/repro-1797.md new file mode 100644 index 000000000..29a5433ea --- /dev/null +++ b/testbed/workspace/repro-1797.md @@ -0,0 +1,51 @@ +# Repro: Diagnostics requested after saving an unsaved-but-named buffer + +Reproduces [microsoft/vscode-languageserver-node#1797](https://github.com/microsoft/vscode-languageserver-node/issues/1797) +(a manifestation of [#1771](https://github.com/microsoft/vscode-languageserver-node/issues/1771)). + +## What the testbed sets up + +* The client registers a fake programming language `testbed` (files ending in + `.testbed`) in `../package.json` and adds it to both the `documentSelector` and + the `diagnosticPullOptions.match` filter in `../client/src/extension.ts`. +* The server (`../server/src/server.ts`) registers for the text document + notifications (`didOpen`, `didChange`, `didSave`, `didClose`) and the + `textDocument/diagnostic` pull request, logging each so the problematic + sequence is visible in the `Testbed` output channel. +* When a diagnostic pull arrives for a document that is **not open** and whose + scheme is **not** `file` (i.e. a closed `untitled:` buffer), the server fails + the request with `no project found for URI `, mirroring how a real + language server (e.g. tsserver) reacts. + +## Steps to reproduce + +1. Build the repo and the testbed: + ```sh + npm install + npm run compile:testbed + ``` +2. Open this `testbed` folder in VS Code and run the `Launch Client` debug + configuration so the extension loads with the `workspace` folder. +3. In the Extension Development Host, open an **unsaved but named** buffer for the + fake language, e.g. from a terminal: + ```sh + code-insiders ./newFile.testbed + ``` + (or create a new file, set its language to `Testbed`, and keep it unsaved). +4. Type some content, for example: + ``` + const x = new Map(); + x. + ``` +5. Save the file. + +## Expected problematic behavior + +On save, VS Code sends `didSave` for the `file:` URI, then `didChange` and +`didClose` for the original `untitled:` URI, and finally a +`textDocument/diagnostic` request for the now closed `untitled:` document. The +`Testbed` output channel shows: + +``` +error handling method 'textDocument/diagnostic': no project found for URI untitled:/.../newFile.testbed +``` diff --git a/testbed/workspace/sample.testbed b/testbed/workspace/sample.testbed new file mode 100644 index 000000000..418c35315 --- /dev/null +++ b/testbed/workspace/sample.testbed @@ -0,0 +1,2 @@ +const x = new Map(); +x. From 02618e610e5a287647a0e7806e0a876dcfc9e745 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 08:39:17 +0000 Subject: [PATCH 3/3] Mention both code and code-insiders in repro instructions --- testbed/workspace/repro-1797.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testbed/workspace/repro-1797.md b/testbed/workspace/repro-1797.md index 29a5433ea..68e2fd6b1 100644 --- a/testbed/workspace/repro-1797.md +++ b/testbed/workspace/repro-1797.md @@ -29,7 +29,7 @@ Reproduces [microsoft/vscode-languageserver-node#1797](https://github.com/micros 3. In the Extension Development Host, open an **unsaved but named** buffer for the fake language, e.g. from a terminal: ```sh - code-insiders ./newFile.testbed + code-insiders ./newFile.testbed # or: code ./newFile.testbed ``` (or create a new file, set its language to `Testbed`, and keep it unsaved). 4. Type some content, for example: