diff --git a/lib/auto-languageclient.ts b/lib/auto-languageclient.ts index 0b96204..32b7cca 100644 --- a/lib/auto-languageclient.ts +++ b/lib/auto-languageclient.ts @@ -313,6 +313,12 @@ export default class AutoLanguageClient { connection, capabilities: initializeResponse.capabilities, disposable: new CompositeDisposable(), + additionalPaths: new Set(), + considerDefinitionPath: (defPath: string): void => { + if (!defPath.startsWith(projectPath)) { + newServer.additionalPaths.add(path.dirname(defPath)); + } + }, }; this.postInitialization(newServer); connection.initialized(); @@ -523,13 +529,25 @@ export default class AutoLanguageClient { } this.definitions = this.definitions || new DefinitionAdapter(); - return this.definitions.getDefinition( + const queryPromise = this.definitions.getDefinition( server.connection, server.capabilities, this.getLanguageName(), editor, point, ); + + if (this.serversSupportDefinitionDestinations()) { + queryPromise.then((query) => { + if (query) { + for (const def of query.definitions) { + server.considerDefinitionPath(def.path); + } + } + }); + } + + return queryPromise; } // Outline View via LS documentSymbol--------------------------------- @@ -786,6 +804,16 @@ export default class AutoLanguageClient { stderr.split('\n').filter((l) => l).forEach((line) => this.logger.warn(`stderr ${line}`)); } + /** + * Indicates that the language server can support LSP functionality for + * out of project files indicated by `textDocument/definition` responses. + * + * Default: false + */ + protected serversSupportDefinitionDestinations(): boolean { + return false; + } + private getServerAdapter( server: ActiveServer, adapter: T, ): ServerAdapters[T] | undefined { diff --git a/lib/server-manager.ts b/lib/server-manager.ts index a8a0edb..cea9dbc 100644 --- a/lib/server-manager.ts +++ b/lib/server-manager.ts @@ -33,6 +33,10 @@ export interface ActiveServer { process: LanguageServerProcess; connection: ls.LanguageClientConnection; capabilities: ls.ServerCapabilities; + // Out of project directories that this server can also support. + additionalPaths: Set; + // Considers a path from `textDocument/definition` for inclusion in `additionalPaths`. + considerDefinitionPath(path: string): void; } interface RestartCounter { @@ -259,7 +263,15 @@ export class ServerManager { if (filePath == null) { return null; } - return this._normalizedProjectPaths.find((d) => filePath.startsWith(d)) || null; + + const projectPath = this._normalizedProjectPaths.find((d) => filePath.startsWith(d)); + if (projectPath) { + return projectPath; + } + + const serverWithClaim = this._activeServers + .find((s) => s.additionalPaths.has(path.dirname(filePath))); + return serverWithClaim && this.normalizePath(serverWithClaim.projectPath) || null; } public updateNormalizedProjectPaths(): void { diff --git a/test/adapters/autocomplete-adapter.test.ts b/test/adapters/autocomplete-adapter.test.ts index c1531c9..fe1d844 100644 --- a/test/adapters/autocomplete-adapter.test.ts +++ b/test/adapters/autocomplete-adapter.test.ts @@ -20,6 +20,8 @@ describe('AutoCompleteAdapter', () => { disposable: new CompositeDisposable(), process: undefined as any, projectPath: '/', + additionalPaths: new Set(), + considerDefinitionPath: (_: string): void => {}, }; }