@@ -115,9 +115,7 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
115115 const diagnostics = new Map < string , Diagnostic [ ] > ( )
116116 const diagnosticRegistrations = new Map < string , CapabilityRegistration > ( )
117117 function updateDiagnostics ( filePath : string , next : Diagnostic [ ] ) {
118- const exists = diagnostics . has ( filePath )
119118 diagnostics . set ( filePath , next )
120- if ( ! exists && input . serverID === "typescript" ) return
121119 Bus . publish ( Event . Diagnostics , { path : filePath , serverID : input . serverID } )
122120 }
123121 connection . onNotification ( "textDocument/publishDiagnostics" , ( params ) => {
@@ -127,6 +125,12 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
127125 path : filePath ,
128126 count : params . diagnostics . length ,
129127 } )
128+ // tsserver emits an initial empty publish before analysis completes; store
129+ // it so the next publish is treated as an update, but don't broadcast it.
130+ if ( input . serverID === "typescript" && ! diagnostics . has ( filePath ) ) {
131+ diagnostics . set ( filePath , params . diagnostics )
132+ return
133+ }
130134 updateDiagnostics ( filePath , params . diagnostics )
131135 } )
132136 connection . onRequest ( "window/workDoneProgress/create" , ( params ) => {
@@ -384,47 +388,50 @@ export async function create(input: { serverID: string; server: LSPServer.Handle
384388 path . isAbsolute ( request . path ) ? request . path : path . resolve ( input . directory , request . path ) ,
385389 )
386390 log . info ( "waiting for diagnostics" , { path : normalizedPath } )
387- let unsub : ( ( ) => void ) | undefined
391+
392+ const canPull = hasStaticPullDiagnostics || diagnosticRegistrations . size > 0
393+ const timeout = canPull ? DIAGNOSTICS_PULL_WAIT_TIMEOUT_MS : DIAGNOSTICS_WAIT_TIMEOUT_MS
394+
395+ // Shared stop flag: whichever branch wins the race (or the outer
396+ // timeout) sets this so the other branch bails before issuing more work.
397+ let stopped = false
388398 let debounceTimer : ReturnType < typeof setTimeout > | undefined
389- let done = false
390- let resolvePushed : ( ( ) => void ) | undefined
391- const timeout = hasStaticPullDiagnostics || diagnosticRegistrations . size > 0
392- ? DIAGNOSTICS_PULL_WAIT_TIMEOUT_MS
393- : DIAGNOSTICS_WAIT_TIMEOUT_MS
394- return await withTimeout (
395- Promise . race ( [
396- new Promise < void > ( ( resolve ) => {
397- resolvePushed = resolve
398- unsub = Bus . subscribe ( Event . Diagnostics , ( event ) => {
399- if ( event . properties . path !== normalizedPath || event . properties . serverID !== result . serverID ) return
400- if ( debounceTimer ) clearTimeout ( debounceTimer )
401- debounceTimer = setTimeout ( ( ) => {
402- log . info ( "got diagnostics" , { path : normalizedPath } )
403- resolve ( )
404- } , DIAGNOSTICS_DEBOUNCE_MS )
405- } )
406- } ) ,
407- ( async ( ) => {
408- let firstHandledAt : number | undefined
409- while ( ! done ) {
410- const handled = await requestDiagnostics ( normalizedPath )
411- if ( handled ) {
412- firstHandledAt = firstHandledAt ?? Date . now ( )
413- if ( Date . now ( ) - firstHandledAt >= DIAGNOSTICS_SETTLE_MS ) {
414- log . info ( "got diagnostics" , { path : normalizedPath } )
415- return
416- }
417- }
418- await new Promise ( ( resolve ) => setTimeout ( resolve , DIAGNOSTICS_POLL_MS ) )
399+ let unsub : ( ( ) => void ) | undefined
400+
401+ const waitForPush = new Promise < void > ( ( resolve ) => {
402+ unsub = Bus . subscribe ( Event . Diagnostics , ( event ) => {
403+ if ( event . properties . path !== normalizedPath || event . properties . serverID !== result . serverID ) return
404+ if ( debounceTimer ) clearTimeout ( debounceTimer )
405+ debounceTimer = setTimeout ( ( ) => {
406+ log . info ( "got diagnostics" , { path : normalizedPath } )
407+ stopped = true
408+ resolve ( )
409+ } , DIAGNOSTICS_DEBOUNCE_MS )
410+ } )
411+ } )
412+
413+ const waitForPull = async ( ) => {
414+ let firstHandledAt : number | undefined
415+ while ( ! stopped ) {
416+ const handled = await requestDiagnostics ( normalizedPath )
417+ if ( stopped ) return
418+ if ( handled ) {
419+ firstHandledAt = firstHandledAt ?? Date . now ( )
420+ if ( Date . now ( ) - firstHandledAt >= DIAGNOSTICS_SETTLE_MS ) {
421+ log . info ( "got diagnostics" , { path : normalizedPath } )
422+ stopped = true
423+ return
419424 }
420- } ) ( ) ,
421- ] ) ,
422- timeout ,
423- )
425+ }
426+ await new Promise ( ( resolve ) => setTimeout ( resolve , DIAGNOSTICS_POLL_MS ) )
427+ }
428+ }
429+
430+ const race = canPull ? Promise . race ( [ waitForPush , waitForPull ( ) ] ) : waitForPush
431+ return await withTimeout ( race , timeout )
424432 . catch ( ( ) => { } )
425433 . finally ( ( ) => {
426- done = true
427- resolvePushed ?.( )
434+ stopped = true
428435 if ( debounceTimer ) clearTimeout ( debounceTimer )
429436 unsub ?.( )
430437 } )
0 commit comments