@@ -62,6 +62,7 @@ export async function createLSPClient(input: {
6262
6363 const diagnostics = new Map < string , Diagnostic [ ] > ( )
6464 const files : Record < string , number > = { }
65+ const diagnosticPulls : Record < string , number > = { }
6566 let diagnosticsListeners : Array < {
6667 path : string
6768 resolve : ( ) => void
@@ -96,13 +97,22 @@ export async function createLSPClient(input: {
9697 // Pull diagnostics for a single file (LSP textDocument/diagnostic). Used for
9798 // servers that advertise a diagnostic provider instead of pushing them
9899 // (e.g. typescript-go / tsgo). Failures are swallowed — absent diagnostics
99- // must never break opening a file.
100+ // must never break opening a file. `pullID` uniquely identifies this request;
101+ // the response is discarded if a newer open/change/close superseded it, so an
102+ // out-of-order reply can't overwrite fresher diagnostics with stale ones.
100103 const pullDiagnostics = async ( filePath : string ) : Promise < void > => {
101104 if ( ! supportsPullDiagnostics ) { return }
105+ const pullID = ( diagnosticPulls [ filePath ] ?? 0 ) + 1
106+ diagnosticPulls [ filePath ] = pullID
102107 try {
103- const report = ( await connection . sendRequest ( 'textDocument/diagnostic' , {
104- textDocument : { uri : pathToFileURL ( filePath ) . href } ,
105- } ) ) as { kind ?: string , items ?: Diagnostic [ ] } | null
108+ const report = ( await withTimeout (
109+ connection . sendRequest ( 'textDocument/diagnostic' , {
110+ textDocument : { uri : pathToFileURL ( filePath ) . href } ,
111+ } ) ,
112+ DIAGNOSTICS_WAIT_TIMEOUT_MS ,
113+ ) ) as { kind ?: string , items ?: Diagnostic [ ] } | null
114+ // Drop a response superseded by a newer pull or close.
115+ if ( diagnosticPulls [ filePath ] !== pullID ) { return }
106116 // A "full" report carries items; an "unchanged" report means keep the
107117 // previously reported set, so leave the map as-is.
108118 if ( report ?. kind === 'full' && Array . isArray ( report . items ) ) {
@@ -240,6 +250,7 @@ export async function createLSPClient(input: {
240250 } ,
241251 } )
242252 delete files [ filePath ]
253+ diagnosticPulls [ filePath ] = ( diagnosticPulls [ filePath ] ?? 0 ) + 1
243254 diagnostics . delete ( normalizePath ( filePath ) )
244255 } ,
245256 } ,
0 commit comments