From 498202bd6612166e06bd94075ab9c5aa979e7d55 Mon Sep 17 00:00:00 2001 From: Donnie West Date: Wed, 11 Nov 2020 21:12:10 -0600 Subject: [PATCH 1/2] Allow the user to enable more detailed completions --- server/src/cli.ts | 10 ++++++++- server/src/completion.ts | 21 +++++++++++++++++++ server/src/lsp-connection.ts | 8 ++++++-- server/src/lsp-server.ts | 39 +++++++++++++++++++++++++++++++----- server/src/test-utils.ts | 2 ++ 5 files changed, 72 insertions(+), 8 deletions(-) diff --git a/server/src/cli.ts b/server/src/cli.ts index ad4232a7..13e76914 100644 --- a/server/src/cli.ts +++ b/server/src/cli.ts @@ -15,6 +15,8 @@ const program = new Command('typescript-language-server') .version(require('../package.json').version) .option('--stdio', 'use stdio') .option('--node-ipc', 'use node-ipc') + .option('--detailed-completions', 'use detailed completions') + .option('--detailed-completions-limit ', 'limit the number of completions provided with details. Defaults to `500`') .option('--log-level ', 'A number indicating the log level (4 = log, 3 = info, 2 = warn, 1 = error). Defaults to `2`.') .option('--socket ', 'use socket. example: --socket=5000') .option('--tsserver-log-file ', 'Specify a tsserver log file. example: --tsserver-log-file ts-logs.txt') @@ -32,6 +34,10 @@ if (program.tsserverLogFile && !program.tsserverLogVerbosity) { program.tsserverLogVerbosity = 'normal' } +if (!program.detailedCompletionsLimit) { + program.detailedCompletionsLimit = 500 +} + let logLevel = lsp.MessageType.Warning if (program.logLevel) { logLevel = parseInt(program.logLevel, 10); @@ -45,5 +51,7 @@ createLspConnection({ tsserverPath: program.tsserverPath as string, tsserverLogFile: program.tsserverLogFile as string, tsserverLogVerbosity: program.tsserverLogVerbosity as string, - showMessageLevel: logLevel as lsp.MessageType + showMessageLevel: logLevel as lsp.MessageType, + detailedCompletionsLimit: program.detailedCompletionsLimit, + detailedCompletions: program.detailedCompletions, }).listen(); diff --git a/server/src/completion.ts b/server/src/completion.ts index 25b6ba42..fc591a56 100644 --- a/server/src/completion.ts +++ b/server/src/completion.ts @@ -145,6 +145,27 @@ export function asCommitCharacters(kind: ScriptElementKind): string[] | undefine return commitCharacters.length === 0 ? undefined : commitCharacters; } +export function asDetailedCompletionItems(items: tsp.CompletionEntryDetails[], file: string, line: number, offset: number): TSCompletionItem[] { + return items.map(entry => { + const item: TSCompletionItem = { + label: entry.name, + kind: asCompletionItemKind(entry.kind), + commitCharacters: asCommitCharacters(entry.kind), + detail: asDetail(entry), + documentation: asDocumentation(entry), + data: { + file, + line, + offset, + entryNames: [ + entry.source ? { name: entry.name, source: entry.source.join(' ') } : entry.name + ] + } + } + return { ...item, ...asCodeActions(entry, file) } + }) +} + export function asResolvedCompletionItem(item: TSCompletionItem, details: tsp.CompletionEntryDetails): TSCompletionItem { item.detail = asDetail(details); item.documentation = asDocumentation(details); diff --git a/server/src/lsp-connection.ts b/server/src/lsp-connection.ts index 15661679..106357f7 100644 --- a/server/src/lsp-connection.ts +++ b/server/src/lsp-connection.ts @@ -16,7 +16,9 @@ export interface IServerOptions { tsserverPath: string; tsserverLogFile?: string; tsserverLogVerbosity?: string; - showMessageLevel: lsp.MessageType + showMessageLevel: lsp.MessageType; + detailedCompletionsLimit: number; + detailedCompletions: boolean; } export function createLspConnection(options: IServerOptions): lsp.IConnection { @@ -28,7 +30,9 @@ export function createLspConnection(options: IServerOptions): lsp.IConnection { lspClient, tsserverPath: options.tsserverPath, tsserverLogFile: options.tsserverLogFile, - tsserverLogVerbosity: options.tsserverLogVerbosity + tsserverLogVerbosity: options.tsserverLogVerbosity, + detailedCompletions: options.detailedCompletions, + detailedCompletionsLimit: options.detailedCompletionsLimit, }); connection.onInitialize(server.initialize.bind(server)); diff --git a/server/src/lsp-server.ts b/server/src/lsp-server.ts index 9536d3fe..429fd229 100644 --- a/server/src/lsp-server.ts +++ b/server/src/lsp-server.ts @@ -29,7 +29,7 @@ import { } from './protocol-translation'; import { getTsserverExecutable } from './utils'; import { LspDocuments, LspDocument } from './document'; -import { asCompletionItem, TSCompletionItem, asResolvedCompletionItem } from './completion'; +import { asCompletionItem, TSCompletionItem, asResolvedCompletionItem, asDetailedCompletionItems } from './completion'; import { asSignatureHelp } from './hover'; import { Commands } from './commands'; import { provideQuickFix } from './quickfix'; @@ -45,6 +45,8 @@ export interface IServerOptions { tsserverLogFile?: string; tsserverLogVerbosity?: string; lspClient: LspClient; + detailedCompletions: boolean; + detailedCompletionsLimit: number; } export class LspServer { @@ -444,10 +446,21 @@ export class LspServer { includeExternalModuleExports: true, includeInsertTextCompletions: true })); + const body = result.body || []; - return body - .filter(entry => entry.kind !== 'warning') - .map(entry => asCompletionItem(entry, file, params.position, document)); + + if (!this.options.detailedCompletions || body.length > this.options.detailedCompletionsLimit) { + return body + .filter(entry => entry.kind !== 'warning') + .map(entry => asCompletionItem(entry, file, params.position, document)); + } + + return this.completionDetails( + body, + file, + params.position.line + 1, + params.position.character + 1, + ) } catch (error) { if (error.message === "No content available.") { this.logger.info('No content was available for completion request'); @@ -458,7 +471,23 @@ export class LspServer { } } - async completionResolve(item: TSCompletionItem): Promise { + async completionDetails( + items: tsp.CompletionEntry[], + file: string, + line: number, + offset: number + ): Promise { + const { body } = await this.interuptDiagnostics(() => this.tspClient.request(CommandTypes.CompletionDetails, { + file, + line, + offset, + entryNames: items.map(entry => entry.name) + })) + + return asDetailedCompletionItems(body || [], file, line, offset) + } + + async completionResolve(item: TSCompletionItem): Promise { this.logger.log('completion/resolve', item); const { body } = await this.interuptDiagnostics(() => this.tspClient.request(CommandTypes.CompletionDetails, item.data)); const details = body && body.length && body[0]; diff --git a/server/src/test-utils.ts b/server/src/test-utils.ts index a2d18530..0332fe17 100644 --- a/server/src/test-utils.ts +++ b/server/src/test-utils.ts @@ -67,6 +67,8 @@ export async function createServer(options: { applyWorkspaceEdit: () => Promise.reject(new Error('unsupported')), rename: () => Promise.reject(new Error('unsupported')) }, + detailedCompletions: false, + detailedCompletionsLimit: 500, }); await server.initialize({ From f0e08a543d9ee15239638ce43f08a0934d2d91af Mon Sep 17 00:00:00 2001 From: Donnie West Date: Mon, 16 Nov 2020 20:26:15 -0600 Subject: [PATCH 2/2] Filter warnings before processing --- server/src/lsp-server.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/src/lsp-server.ts b/server/src/lsp-server.ts index 429fd229..b8b8f179 100644 --- a/server/src/lsp-server.ts +++ b/server/src/lsp-server.ts @@ -447,11 +447,10 @@ export class LspServer { includeInsertTextCompletions: true })); - const body = result.body || []; + const body = (result.body || []).filter(entry => entry.kind !== 'warning'); if (!this.options.detailedCompletions || body.length > this.options.detailedCompletionsLimit) { return body - .filter(entry => entry.kind !== 'warning') .map(entry => asCompletionItem(entry, file, params.position, document)); }