@@ -261,23 +261,36 @@ export namespace LSP {
261261 return result
262262 }
263263
264+ export async function hasClients ( file : string ) {
265+ const s = await state ( )
266+ const extension = path . parse ( file ) . ext || file
267+ for ( const server of Object . values ( s . servers ) ) {
268+ if ( server . extensions . length && ! server . extensions . includes ( extension ) ) continue
269+ const root = await server . root ( file )
270+ if ( ! root ) continue
271+ if ( s . broken . has ( root + server . id ) ) continue
272+ return true
273+ }
274+ return false
275+ }
276+
264277 export async function touchFile ( input : string , waitForDiagnostics ?: boolean ) {
265278 log . info ( "touching file" , { file : input } )
266279 const clients = await getClients ( input )
267- await run ( async ( client ) => {
268- if ( ! clients . includes ( client ) ) return
269- const wait = waitForDiagnostics ? client . waitForDiagnostics ( { path : input } ) : Promise . resolve ( )
270- await client . notify . open ( { path : input } )
271-
272- return wait
273- } ) . catch ( ( err ) => {
280+ await Promise . all (
281+ clients . map ( async ( client ) => {
282+ const wait = waitForDiagnostics ? client . waitForDiagnostics ( { path : input } ) : Promise . resolve ( )
283+ await client . notify . open ( { path : input } )
284+ return wait
285+ } ) ,
286+ ) . catch ( ( err ) => {
274287 log . error ( "failed to touch file" , { err, file : input } )
275288 } )
276289 }
277290
278291 export async function diagnostics ( ) {
279292 const results : Record < string , LSPClient . Diagnostic [ ] > = { }
280- for ( const result of await run ( async ( client ) => client . diagnostics ) ) {
293+ for ( const result of await runAll ( async ( client ) => client . diagnostics ) ) {
281294 for ( const [ path , diagnostics ] of result . entries ( ) ) {
282295 const arr = results [ path ] || [ ]
283296 arr . push ( ...diagnostics )
@@ -288,16 +301,18 @@ export namespace LSP {
288301 }
289302
290303 export async function hover ( input : { file : string ; line : number ; character : number } ) {
291- return run ( ( client ) => {
292- return client . connection . sendRequest ( "textDocument/hover" , {
293- textDocument : {
294- uri : pathToFileURL ( input . file ) . href ,
295- } ,
296- position : {
297- line : input . line ,
298- character : input . character ,
299- } ,
300- } )
304+ return run ( input . file , ( client ) => {
305+ return client . connection
306+ . sendRequest ( "textDocument/hover" , {
307+ textDocument : {
308+ uri : pathToFileURL ( input . file ) . href ,
309+ } ,
310+ position : {
311+ line : input . line ,
312+ character : input . character ,
313+ } ,
314+ } )
315+ . catch ( ( ) => null )
301316 } )
302317 }
303318
@@ -342,7 +357,7 @@ export namespace LSP {
342357 ]
343358
344359 export async function workspaceSymbol ( query : string ) {
345- return run ( ( client ) =>
360+ return runAll ( ( client ) =>
346361 client . connection
347362 . sendRequest ( "workspace/symbol" , {
348363 query,
@@ -354,7 +369,8 @@ export namespace LSP {
354369 }
355370
356371 export async function documentSymbol ( uri : string ) {
357- return run ( ( client ) =>
372+ const file = new URL ( uri ) . pathname
373+ return run ( file , ( client ) =>
358374 client . connection
359375 . sendRequest ( "textDocument/documentSymbol" , {
360376 textDocument : {
@@ -367,12 +383,89 @@ export namespace LSP {
367383 . then ( ( result ) => result . filter ( Boolean ) )
368384 }
369385
370- async function run < T > ( input : ( client : LSPClient . Info ) => Promise < T > ) : Promise < T [ ] > {
386+ export async function definition ( input : { file : string ; line : number ; character : number } ) {
387+ return run ( input . file , ( client ) =>
388+ client . connection
389+ . sendRequest ( "textDocument/definition" , {
390+ textDocument : { uri : pathToFileURL ( input . file ) . href } ,
391+ position : { line : input . line , character : input . character } ,
392+ } )
393+ . catch ( ( ) => null ) ,
394+ ) . then ( ( result ) => result . flat ( ) . filter ( Boolean ) )
395+ }
396+
397+ export async function references ( input : { file : string ; line : number ; character : number } ) {
398+ return run ( input . file , ( client ) =>
399+ client . connection
400+ . sendRequest ( "textDocument/references" , {
401+ textDocument : { uri : pathToFileURL ( input . file ) . href } ,
402+ position : { line : input . line , character : input . character } ,
403+ context : { includeDeclaration : true } ,
404+ } )
405+ . catch ( ( ) => [ ] ) ,
406+ ) . then ( ( result ) => result . flat ( ) . filter ( Boolean ) )
407+ }
408+
409+ export async function implementation ( input : { file : string ; line : number ; character : number } ) {
410+ return run ( input . file , ( client ) =>
411+ client . connection
412+ . sendRequest ( "textDocument/implementation" , {
413+ textDocument : { uri : pathToFileURL ( input . file ) . href } ,
414+ position : { line : input . line , character : input . character } ,
415+ } )
416+ . catch ( ( ) => null ) ,
417+ ) . then ( ( result ) => result . flat ( ) . filter ( Boolean ) )
418+ }
419+
420+ export async function prepareCallHierarchy ( input : { file : string ; line : number ; character : number } ) {
421+ return run ( input . file , ( client ) =>
422+ client . connection
423+ . sendRequest ( "textDocument/prepareCallHierarchy" , {
424+ textDocument : { uri : pathToFileURL ( input . file ) . href } ,
425+ position : { line : input . line , character : input . character } ,
426+ } )
427+ . catch ( ( ) => [ ] ) ,
428+ ) . then ( ( result ) => result . flat ( ) . filter ( Boolean ) )
429+ }
430+
431+ export async function incomingCalls ( input : { file : string ; line : number ; character : number } ) {
432+ return run ( input . file , async ( client ) => {
433+ const items = ( await client . connection
434+ . sendRequest ( "textDocument/prepareCallHierarchy" , {
435+ textDocument : { uri : pathToFileURL ( input . file ) . href } ,
436+ position : { line : input . line , character : input . character } ,
437+ } )
438+ . catch ( ( ) => [ ] ) ) as any [ ]
439+ if ( ! items ?. length ) return [ ]
440+ return client . connection . sendRequest ( "callHierarchy/incomingCalls" , { item : items [ 0 ] } ) . catch ( ( ) => [ ] )
441+ } ) . then ( ( result ) => result . flat ( ) . filter ( Boolean ) )
442+ }
443+
444+ export async function outgoingCalls ( input : { file : string ; line : number ; character : number } ) {
445+ return run ( input . file , async ( client ) => {
446+ const items = ( await client . connection
447+ . sendRequest ( "textDocument/prepareCallHierarchy" , {
448+ textDocument : { uri : pathToFileURL ( input . file ) . href } ,
449+ position : { line : input . line , character : input . character } ,
450+ } )
451+ . catch ( ( ) => [ ] ) ) as any [ ]
452+ if ( ! items ?. length ) return [ ]
453+ return client . connection . sendRequest ( "callHierarchy/outgoingCalls" , { item : items [ 0 ] } ) . catch ( ( ) => [ ] )
454+ } ) . then ( ( result ) => result . flat ( ) . filter ( Boolean ) )
455+ }
456+
457+ async function runAll < T > ( input : ( client : LSPClient . Info ) => Promise < T > ) : Promise < T [ ] > {
371458 const clients = await state ( ) . then ( ( x ) => x . clients )
372459 const tasks = clients . map ( ( x ) => input ( x ) )
373460 return Promise . all ( tasks )
374461 }
375462
463+ async function run < T > ( file : string , input : ( client : LSPClient . Info ) => Promise < T > ) : Promise < T [ ] > {
464+ const clients = await getClients ( file )
465+ const tasks = clients . map ( ( x ) => input ( x ) )
466+ return Promise . all ( tasks )
467+ }
468+
376469 export namespace Diagnostic {
377470 export function pretty ( diagnostic : LSPClient . Diagnostic ) {
378471 const severityMap = {
0 commit comments