@@ -9,6 +9,7 @@ import { ensureServerRunning } from "./serverLauncher";
99import serverRegistry from "./serverRegistry" ;
1010import { createTransport } from "./transport" ;
1111import AcodeWorkspace from "./workspace" ;
12+ import Uri from "utils/Uri" ;
1213
1314function asArray ( value ) {
1415 if ( ! value ) return [ ] ;
@@ -157,8 +158,10 @@ export class LspClientManager {
157158 }
158159
159160 async #ensureClient( server , context ) {
160- const rootUri = await this . #resolveRootUri( server , context ) ;
161- const key = pluginKey ( server . id , rootUri ) ;
161+ const resolvedRoot = await this . #resolveRootUri( server , context ) ;
162+ const { normalizedRootUri, originalRootUri } =
163+ normalizeRootUriForServer ( server , resolvedRoot ) ;
164+ const key = pluginKey ( server . id , normalizedRootUri ) ;
162165 if ( this . #clients. has ( key ) ) {
163166 return this . #clients. get ( key ) ;
164167 }
@@ -224,8 +227,12 @@ export class LspClientManager {
224227 new AcodeWorkspace ( client , workspaceOptions ) ;
225228 }
226229
227- if ( rootUri && ! clientConfig . rootUri ) {
228- clientConfig . rootUri = rootUri ;
230+ if ( normalizedRootUri && ! clientConfig . rootUri ) {
231+ clientConfig . rootUri = normalizedRootUri ;
232+ }
233+
234+ if ( ! normalizedRootUri && clientConfig . rootUri ) {
235+ delete clientConfig . rootUri ;
229236 }
230237
231238 if ( server . startupTimeout && ! clientConfig . timeout ) {
@@ -239,7 +246,8 @@ export class LspClientManager {
239246 await ensureServerRunning ( server ) ;
240247 transportHandle = createTransport ( server , {
241248 ...context ,
242- rootUri,
249+ rootUri : normalizedRootUri ?? null ,
250+ originalRootUri,
243251 } ) ;
244252 await transportHandle . ready ;
245253 client = new LSPClient ( clientConfig ) ;
@@ -250,8 +258,25 @@ export class LspClientManager {
250258 if ( info ) {
251259 console . info ( `[LSP:${ server . id } ] server info` , info ) ;
252260 }
253- if ( rootUri ) {
254- console . info ( `[LSP:${ server . id } ] root` , rootUri ) ;
261+ if ( normalizedRootUri ) {
262+ if (
263+ originalRootUri &&
264+ originalRootUri !== normalizedRootUri
265+ ) {
266+ console . info (
267+ `[LSP:${ server . id } ] root ${ normalizedRootUri } (from ${ originalRootUri } )` ,
268+ ) ;
269+ } else {
270+ console . info (
271+ `[LSP:${ server . id } ] root` ,
272+ normalizedRootUri ,
273+ ) ;
274+ }
275+ } else if ( originalRootUri ) {
276+ console . info (
277+ `[LSP:${ server . id } ] root ignored` ,
278+ originalRootUri ,
279+ ) ;
255280 }
256281 client . __acodeLoggedInfo = true ;
257282 }
@@ -265,21 +290,30 @@ export class LspClientManager {
265290 server,
266291 client,
267292 transportHandle,
268- rootUri,
293+ normalizedRootUri,
294+ originalRootUri,
269295 } ) ;
270296
271297 this . #clients. set ( key , state ) ;
272298 return state ;
273299 }
274300
275- #createClientState( { key, server, client, transportHandle, rootUri } ) {
301+ #createClientState( {
302+ key,
303+ server,
304+ client,
305+ transportHandle,
306+ normalizedRootUri,
307+ originalRootUri,
308+ } ) {
276309 const fileRefs = new Map ( ) ;
310+ const effectiveRoot = normalizedRootUri ?? originalRootUri ?? null ;
277311
278312 const attach = ( uri , view ) => {
279313 const existing = fileRefs . get ( uri ) || new Set ( ) ;
280314 existing . add ( view ) ;
281315 fileRefs . set ( uri , existing ) ;
282- const suffix = rootUri ? ` (root ${ rootUri } )` : "" ;
316+ const suffix = effectiveRoot ? ` (root ${ effectiveRoot } )` : "" ;
283317 console . info ( `[LSP:${ server . id } ] attached to ${ uri } ${ suffix } ` ) ;
284318 } ;
285319
@@ -297,7 +331,11 @@ export class LspClientManager {
297331 }
298332
299333 if ( ! fileRefs . size ) {
300- this . options . onClientIdle ?. ( { server, client, rootUri } ) ;
334+ this . options . onClientIdle ?. ( {
335+ server,
336+ client,
337+ rootUri : effectiveRoot ,
338+ } ) ;
301339 }
302340 } ;
303341
@@ -319,7 +357,7 @@ export class LspClientManager {
319357 server,
320358 client,
321359 transport : transportHandle ,
322- rootUri,
360+ rootUri : effectiveRoot ,
323361 attach,
324362 detach,
325363 dispose,
@@ -417,3 +455,104 @@ function resolveIndentWidth(unit) {
417455const defaultManager = new LspClientManager ( ) ;
418456
419457export default defaultManager ;
458+
459+ const FILE_SCHEME_REQUIRED_SERVERS = new Set ( [ "typescript" ] ) ;
460+
461+ function normalizeRootUriForServer ( server , rootUri ) {
462+ if ( ! rootUri || typeof rootUri !== "string" ) {
463+ return { normalizedRootUri : null , originalRootUri : null } ;
464+ }
465+ const schemeMatch = / ^ ( [ a - z A - Z ] [ \w + \- . ] * ) : / . exec ( rootUri ) ;
466+ const scheme = schemeMatch ? schemeMatch [ 1 ] . toLowerCase ( ) : null ;
467+ if ( scheme === "file" ) {
468+ return { normalizedRootUri : rootUri , originalRootUri : rootUri } ;
469+ }
470+
471+ if ( scheme === "content" ) {
472+ const fileUri = contentUriToFileUri ( rootUri ) ;
473+ if ( fileUri ) {
474+ return { normalizedRootUri : fileUri , originalRootUri : rootUri } ;
475+ }
476+ if ( FILE_SCHEME_REQUIRED_SERVERS . has ( server . id ) ) {
477+ return { normalizedRootUri : null , originalRootUri : rootUri } ;
478+ }
479+ }
480+
481+ return { normalizedRootUri : rootUri , originalRootUri : rootUri } ;
482+ }
483+
484+ function contentUriToFileUri ( uri ) {
485+ try {
486+ const parsed = Uri . parse ( uri ) ;
487+ if ( ! parsed || typeof parsed !== "object" ) return null ;
488+ const { docId, rootUri, isFileUri } = parsed ;
489+ if ( ! docId ) return null ;
490+
491+ if ( isFileUri && rootUri ) {
492+ return rootUri ;
493+ }
494+
495+ const providerMatch =
496+ / ^ c o n t e n t : \/ \/ c o m \. ( (? ! [: <> " / \\ | ? * ] ) .* ) \. d o c u m e n t s \/ / . exec ( rootUri ) ;
497+ const providerId = providerMatch ? providerMatch [ 1 ] : null ;
498+
499+ let normalized = docId . trim ( ) ;
500+ if ( ! normalized ) return null ;
501+
502+ switch ( providerId ) {
503+ case "foxdebug.acode" :
504+ normalized = normalized . replace ( / : + $ / , "" ) ;
505+ if ( ! normalized ) return null ;
506+ if ( normalized . startsWith ( "raw:/" ) ) {
507+ normalized = normalized . slice ( 4 ) ;
508+ } else if ( normalized . startsWith ( "raw:" ) ) {
509+ normalized = normalized . slice ( 4 ) ;
510+ }
511+ if ( ! normalized . startsWith ( "/" ) ) return null ;
512+ return buildFileUri ( normalized ) ;
513+ case "android.externalstorage" :
514+ normalized = normalized . replace ( / : + $ / , "" ) ;
515+ if ( ! normalized ) return null ;
516+
517+ if ( normalized . startsWith ( "/" ) ) {
518+ return buildFileUri ( normalized ) ;
519+ }
520+
521+ if ( normalized . startsWith ( "raw:/" ) ) {
522+ return buildFileUri ( normalized . slice ( 4 ) ) ;
523+ }
524+
525+ if ( normalized . startsWith ( "raw:" ) ) {
526+ return buildFileUri ( normalized . slice ( 4 ) ) ;
527+ }
528+
529+ const separator = normalized . indexOf ( ":" ) ;
530+ if ( separator === - 1 ) return null ;
531+
532+ const root = normalized . slice ( 0 , separator ) ;
533+ const remainder = normalized . slice ( separator + 1 ) ;
534+ if ( ! remainder ) return null ;
535+
536+ switch ( root ) {
537+ case "primary" :
538+ return buildFileUri ( `/storage/emulated/0/${ remainder } ` ) ;
539+ default :
540+ if ( / ^ [ 0 - 9 A - F a - f ] { 4 } - [ 0 - 9 A - F a - f ] { 4 } $ / . test ( root ) ) {
541+ return buildFileUri ( `/storage/${ root } /${ remainder } ` ) ;
542+ }
543+ }
544+ return null ;
545+ default :
546+ return null ;
547+ }
548+ } catch ( _ ) {
549+ return null ;
550+ }
551+ }
552+
553+ function buildFileUri ( pathname ) {
554+ if ( ! pathname ) return null ;
555+ const normalized = pathname . startsWith ( "/" ) ? pathname : `/${ pathname } ` ;
556+ const encoded = encodeURI ( normalized ) . replace ( / # / g, "%23" ) ;
557+ return `file://${ encoded } ` ;
558+ }
0 commit comments