@@ -26,7 +26,7 @@ import type {
2626 BuiltinExtensionsConfig ,
2727 ClientManagerOptions ,
2828 ClientState ,
29- EnsureServerResult ,
29+ DocumentUriContext ,
3030 FileMetadata ,
3131 FormattingOptions ,
3232 LspServerDefinition ,
@@ -242,30 +242,23 @@ export class LspClientManager {
242242 const servers = serverRegistry . getServersForLanguage ( effectiveLang ) ;
243243 if ( ! servers . length ) return [ ] ;
244244
245- // Normalize the document URI for LSP (convert content:// to file://)
246- let normalizedUri = normalizeDocumentUri ( originalUri ) ;
247- if ( ! normalizedUri ) {
248- // Fall back to cache file path for unrecognized URIs
249- // This allows LSP to work with any file system provider using the local cache
250- const cacheFile = file ?. cacheFile ;
251- if ( cacheFile && typeof cacheFile === "string" ) {
252- normalizedUri = buildFileUri ( cacheFile . replace ( / ^ f i l e : \/ \/ / , "" ) ) ;
253- if ( normalizedUri ) {
254- console . info (
255- `LSP using cache path for unrecognized URI: ${ originalUri } -> ${ normalizedUri } ` ,
256- ) ;
257- }
258- }
259- if ( ! normalizedUri ) {
260- console . warn ( `Cannot normalize document URI for LSP: ${ originalUri } ` ) ;
261- return [ ] ;
262- }
263- }
264-
265245 const lspExtensions : Extension [ ] = [ ] ;
266246 const diagnosticsUiExtension = this . options . diagnosticsUiExtension ;
267247
268248 for ( const server of servers ) {
249+ const normalizedUri = await this . #resolveDocumentUri( server , {
250+ uri : originalUri ,
251+ file,
252+ view,
253+ languageId : effectiveLang ,
254+ rootUri,
255+ } ) ;
256+ if ( ! normalizedUri ) {
257+ console . warn (
258+ `Cannot resolve document URI for LSP server ${ server . id } : ${ originalUri } ` ,
259+ ) ;
260+ continue ;
261+ }
269262 let targetLanguageId = effectiveLang ;
270263 if ( server . resolveLanguageId ) {
271264 try {
@@ -296,7 +289,9 @@ export class LspClientManager {
296289 normalizedUri ,
297290 targetLanguageId ,
298291 ) ;
299- clientState . attach ( normalizedUri , view as EditorView ) ;
292+ const aliases =
293+ originalUri && originalUri !== normalizedUri ? [ originalUri ] : [ ] ;
294+ clientState . attach ( normalizedUri , view as EditorView , aliases ) ;
300295 lspExtensions . push ( plugin ) ;
301296 } catch ( error ) {
302297 const lspError = error as LSPError ;
@@ -328,26 +323,25 @@ export class LspClientManager {
328323 const effectiveLang = safeString ( languageId ?? languageName ) . toLowerCase ( ) ;
329324 if ( ! effectiveLang || ! view ) return false ;
330325
331- let normalizedUri = normalizeDocumentUri ( originalUri ) ;
332- if ( ! normalizedUri ) {
333- const cacheFile = file ?. cacheFile ;
334- if ( cacheFile && typeof cacheFile === "string" ) {
335- normalizedUri = buildFileUri ( cacheFile . replace ( / ^ f i l e : \/ \/ / , "" ) ) ;
336- }
337- if ( ! normalizedUri ) {
338- console . warn (
339- `Cannot normalize document URI for formatting: ${ originalUri } ` ,
340- ) ;
341- return false ;
342- }
343- }
344-
345326 const servers = serverRegistry . getServersForLanguage ( effectiveLang ) ;
346327 if ( ! servers . length ) return false ;
347328
348329 for ( const server of servers ) {
349330 if ( ! supportsBuiltinFormatting ( server ) ) continue ;
350331 try {
332+ const normalizedUri = await this . #resolveDocumentUri( server , {
333+ uri : originalUri ,
334+ file,
335+ view,
336+ languageId : effectiveLang ,
337+ rootUri : metadata . rootUri ,
338+ } ) ;
339+ if ( ! normalizedUri ) {
340+ console . warn (
341+ `Cannot resolve document URI for formatting with ${ server . id } : ${ originalUri } ` ,
342+ ) ;
343+ continue ;
344+ }
351345 const context : RootUriContext = {
352346 uri : normalizedUri ,
353347 languageId : effectiveLang ,
@@ -834,28 +828,44 @@ export class LspClientManager {
834828 originalRootUri,
835829 } = params ;
836830 const fileRefs = new Map < string , Set < EditorView > > ( ) ;
831+ const uriAliases = new Map < string , string > ( ) ;
837832 const effectiveRoot = normalizedRootUri ?? originalRootUri ?? null ;
838833
839- const attach = ( uri : string , view : EditorView ) : void => {
834+ const attach = (
835+ uri : string ,
836+ view : EditorView ,
837+ aliases : string [ ] = [ ] ,
838+ ) : void => {
840839 const existing = fileRefs . get ( uri ) ?? new Set ( ) ;
841840 existing . add ( view ) ;
842841 fileRefs . set ( uri , existing ) ;
842+ uriAliases . set ( uri , uri ) ;
843+ for ( const alias of aliases ) {
844+ if ( ! alias || alias === uri ) continue ;
845+ uriAliases . set ( alias , uri ) ;
846+ }
843847 const suffix = effectiveRoot ? ` (root ${ effectiveRoot } )` : "" ;
844848 logLspInfo ( `[LSP:${ server . id } ] attached to ${ uri } ${ suffix } ` ) ;
845849 } ;
846850
847851 const detach = ( uri : string , view ?: EditorView ) : void => {
848- const existing = fileRefs . get ( uri ) ;
852+ const actualUri = uriAliases . get ( uri ) ?? uri ;
853+ const existing = fileRefs . get ( actualUri ) ;
849854 if ( ! existing ) return ;
850855 if ( view ) existing . delete ( view ) ;
851856 if ( ! view || ! existing . size ) {
852- fileRefs . delete ( uri ) ;
857+ fileRefs . delete ( actualUri ) ;
858+ for ( const [ alias , target ] of uriAliases . entries ( ) ) {
859+ if ( target === actualUri ) {
860+ uriAliases . delete ( alias ) ;
861+ }
862+ }
853863 try {
854864 // Only pass uri to closeFile - view is not needed for closing
855865 // and passing it may cause issues if the view is already disposed
856- ( client . workspace as AcodeWorkspace ) ?. closeFile ?.( uri ) ;
866+ ( client . workspace as AcodeWorkspace ) ?. closeFile ?.( actualUri ) ;
857867 } catch ( error ) {
858- console . warn ( `Failed to close LSP file ${ uri } ` , error ) ;
868+ console . warn ( `Failed to close LSP file ${ actualUri } ` , error ) ;
859869 }
860870 }
861871
@@ -897,8 +907,6 @@ export class LspClientManager {
897907 server : LspServerDefinition ,
898908 context : RootUriContext ,
899909 ) : Promise < string | null > {
900- if ( context ?. rootUri ) return context . rootUri ;
901-
902910 if ( typeof server . rootUri === "function" ) {
903911 try {
904912 const value = await server . rootUri ( context ?. uri ?? "" , context ) ;
@@ -908,6 +916,8 @@ export class LspClientManager {
908916 }
909917 }
910918
919+ if ( context ?. rootUri ) return safeString ( context . rootUri ) ;
920+
911921 if ( typeof this . options . resolveRoot === "function" ) {
912922 try {
913923 const value = await this . options . resolveRoot ( context ) ;
@@ -919,6 +929,45 @@ export class LspClientManager {
919929
920930 return null ;
921931 }
932+
933+ async #resolveDocumentUri(
934+ server : LspServerDefinition ,
935+ context : RootUriContext ,
936+ ) : Promise < string | null > {
937+ const originalUri = context ?. uri ;
938+ if ( ! originalUri ) return null ;
939+
940+ let normalizedUri = normalizeDocumentUri ( originalUri ) ;
941+ if ( ! normalizedUri ) {
942+ // Fall back to cache file path for providers that do not expose a file:// URI.
943+ const cacheFile = context . file ?. cacheFile ;
944+ if ( cacheFile && typeof cacheFile === "string" ) {
945+ normalizedUri = buildFileUri ( cacheFile . replace ( / ^ f i l e : \/ \/ / , "" ) ) ;
946+ if ( normalizedUri ) {
947+ console . info (
948+ `LSP using cache path for unrecognized URI: ${ originalUri } -> ${ normalizedUri } ` ,
949+ ) ;
950+ }
951+ }
952+ }
953+
954+ if ( typeof server . documentUri === "function" ) {
955+ try {
956+ const value = await server . documentUri ( originalUri , {
957+ ...context ,
958+ normalizedUri,
959+ } as DocumentUriContext ) ;
960+ if ( value ) return safeString ( value ) ;
961+ } catch ( error ) {
962+ console . warn (
963+ `Server document URI resolver failed for ${ server . id } ` ,
964+ error ,
965+ ) ;
966+ }
967+ }
968+
969+ return normalizedUri ;
970+ }
922971}
923972
924973interface Change {
0 commit comments