From 29ca249959fed443e9b05534592370b128e1e939 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 29 Mar 2021 11:38:22 -0700 Subject: [PATCH] [typescript-language-features] Support import statement completions for TypeScript 4.3 (#119009) * Support import statement completions for TypeScript 4.3 * Fix forgotten argument * Add snippet preference Co-authored-by: Matt Bierner --- .../typescript-language-features/package.json | 18 +++++++++ .../package.nls.json | 2 + .../src/languageFeatures/completions.ts | 40 +++++++++++++++---- .../fileConfigurationManager.ts | 3 ++ .../src/utils/api.ts | 1 + 5 files changed, 56 insertions(+), 8 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 1bff46486cf..cf317858d04 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -258,6 +258,24 @@ "description": "%configuration.suggest.includeAutomaticOptionalChainCompletions%", "scope": "resource" }, + "javascript.suggest.includeCompletionsForImportStatements": { + "type": "boolean", + "default": true, + "description": "%configuration.suggest.includeCompletionsForImportStatements%", + "scope": "resource" + }, + "typescript.suggest.includeCompletionsForImportStatements": { + "type": "boolean", + "default": true, + "description": "%configuration.suggest.includeCompletionsForImportStatements%", + "scope": "resource" + }, + "typescript.suggest.includeCompletionsWithSnippetText": { + "type": "boolean", + "default": true, + "description": "%configuration.suggest.includeCompletionsWithSnippetText%", + "scope": "resource" + }, "typescript.reportStyleChecksAsWarnings": { "type": "boolean", "default": true, diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 0bdc8d5f36a..0d23dbdcebf 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -5,6 +5,8 @@ "configuration.typescript": "TypeScript", "configuration.suggest.completeFunctionCalls": "Complete functions with their parameter signature.", "configuration.suggest.includeAutomaticOptionalChainCompletions": "Enable/disable showing completions on potentially undefined values that insert an optional chain call. Requires TS 3.7+ and strict null checks to be enabled.", + "configuration.suggest.includeCompletionsForImportStatements": "Enable/disable auto-import-style completions on partially-typed import statements. Requires using TypeScript 4.3+ in the workspace.", + "configuration.suggest.includeCompletionsWithSnippetText": "Enable/disable snippet completions from TS Server. Requires using TypeScript 4.3+ in the workspace.", "typescript.tsdk.desc": "Specifies the folder path to the tsserver and lib*.d.ts files under a TypeScript install to use for IntelliSense, for example: `./node_modules/typescript/lib`.\n\n- When specified as a user setting, the TypeScript version from `typescript.tsdk` automatically replaces the built-in TypeScript version.\n- When specified as a workspace setting, `typescript.tsdk` allows you to switch to use that workspace version of TypeScript for IntelliSense with the `TypeScript: Select TypeScript version` command.\n\nSee the [TypeScript documentation](https://code.visualstudio.com/docs/typescript/typescript-compiling#_using-newer-typescript-versions) for more detail about managing TypeScript versions.", "typescript.disableAutomaticTypeAcquisition": "Disables automatic type acquisition. Automatic type acquisition fetches `@types` packages from npm to improve IntelliSense for external libraries.", "typescript.enablePromptUseWorkspaceTsdk": "Enables prompting of users to use the TypeScript version configured in the workspace for Intellisense.", diff --git a/extensions/typescript-language-features/src/languageFeatures/completions.ts b/extensions/typescript-language-features/src/languageFeatures/completions.ts index 372d621d977..97894c3079c 100644 --- a/extensions/typescript-language-features/src/languageFeatures/completions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/completions.ts @@ -63,7 +63,7 @@ class MyCompletionItem extends vscode.CompletionItem { ) { super(tsEntry.name, MyCompletionItem.convertKind(tsEntry.kind)); - if (tsEntry.source) { + if (tsEntry.source && tsEntry.hasAction) { // De-prioritze auto-imports // https://github.com/microsoft/vscode/issues/40311 this.sortText = '\uffff' + tsEntry.sortText; @@ -78,16 +78,25 @@ class MyCompletionItem extends vscode.CompletionItem { this.sortText = tsEntry.sortText; } + // @ts-expect-error until 4.3 protocol update + if (tsEntry.sourceDisplay) { + // @ts-expect-error + this.label2 = { name: tsEntry.name, qualifier: Previewer.plain(tsEntry.sourceDisplay) }; + } + this.preselect = tsEntry.isRecommended; this.position = position; this.useCodeSnippet = completionContext.useCodeSnippetsOnMethodSuggest && (this.kind === vscode.CompletionItemKind.Function || this.kind === vscode.CompletionItemKind.Method); this.range = this.getRangeFromReplacementSpan(tsEntry, completionContext); this.commitCharacters = MyCompletionItem.getCommitCharacters(completionContext, tsEntry); - this.insertText = tsEntry.insertText; + // @ts-expect-error until 4.3 protocol update + this.insertText = tsEntry.isSnippet && tsEntry.insertText + ? new vscode.SnippetString(tsEntry.insertText) + : tsEntry.insertText; this.filterText = this.getFilterText(completionContext.line, tsEntry.insertText); - if (completionContext.isMemberCompletion && completionContext.dotAccessorContext) { + if (completionContext.isMemberCompletion && completionContext.dotAccessorContext && !(this.insertText instanceof vscode.SnippetString)) { this.filterText = completionContext.dotAccessorContext.text + (this.insertText || this.label); if (!this.range) { const replacementRange = this.getFuzzyWordRange(); @@ -629,6 +638,7 @@ interface CompletionConfiguration { readonly nameSuggestions: boolean; readonly pathSuggestions: boolean; readonly autoImportSuggestions: boolean; + readonly importStatementSuggestions: boolean; } namespace CompletionConfiguration { @@ -636,6 +646,7 @@ namespace CompletionConfiguration { export const nameSuggestions = 'suggest.names'; export const pathSuggestions = 'suggest.paths'; export const autoImportSuggestions = 'suggest.autoImports'; + export const importStatementSuggestions = 'suggest.importStatements'; export function getConfigurationForResource( modeId: string, @@ -647,13 +658,14 @@ namespace CompletionConfiguration { pathSuggestions: config.get(CompletionConfiguration.pathSuggestions, true), autoImportSuggestions: config.get(CompletionConfiguration.autoImportSuggestions, true), nameSuggestions: config.get(CompletionConfiguration.nameSuggestions, true), + importStatementSuggestions: config.get(CompletionConfiguration.nameSuggestions, true), }; } } class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider { - public static readonly triggerCharacters = ['.', '"', '\'', '`', '/', '@', '<', '#']; + public static readonly triggerCharacters = ['.', '"', '\'', '`', '/', '@', '<', '#', ' ']; constructor( private readonly client: ITypeScriptServiceClient, @@ -695,7 +707,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< const line = document.lineAt(position.line); const completionConfiguration = CompletionConfiguration.getConfigurationForResource(this.modeId, document.uri); - if (!this.shouldTrigger(context, line, position)) { + if (!this.shouldTrigger(context, line, position, completionConfiguration)) { return undefined; } @@ -740,7 +752,8 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< dotAccessorContext = { range, text }; } } - isIncomplete = (response as any).metadata && (response as any).metadata.isIncomplete; + // @ts-expect-error until 4.3 protocol update + isIncomplete = !!response.body.isIncomplete || (response as any).metadata && (response as any).metadata.isIncomplete; entries = response.body.entries; metadata = response.metadata; } else { @@ -821,6 +834,10 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< case '#': // Workaround for https://github.com/microsoft/TypeScript/issues/36367 return this.client.apiVersion.lt(API.v381) ? undefined : '#'; + case ' ': + // @ts-expect-error until 4.3.0 protocol update + return this.client.apiVersion.gte(API.v430) ? ' ' : undefined; + case '.': case '"': case '\'': @@ -863,7 +880,8 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< private shouldTrigger( context: vscode.CompletionContext, line: vscode.TextLine, - position: vscode.Position + position: vscode.Position, + configuration: CompletionConfiguration, ): boolean { if (context.triggerCharacter && this.client.apiVersion.lt(API.v290)) { if ((context.triggerCharacter === '"' || context.triggerCharacter === '\'')) { @@ -894,7 +912,13 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< return false; } } - + if (context.triggerCharacter === ' ') { + if (!configuration.importStatementSuggestions || this.client.apiVersion.lt(API.v430)) { + return false; + } + const pre = line.text.slice(0, position.character); + return pre === 'import'; + } return true; } } diff --git a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts index 47ad333c61e..69cb3526d5a 100644 --- a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts @@ -183,6 +183,9 @@ export default class FileConfigurationManager extends Disposable { includeAutomaticOptionalChainCompletions: config.get('suggest.includeAutomaticOptionalChainCompletions', true), provideRefactorNotApplicableReason: true, generateReturnInDocTemplate: config.get('suggest.jsdoc.generateReturns', true), + // @ts-expect-error until 4.3 protocol update + includeCompletionsForImportStatements: config.get('suggest.includeCompletionsForImportStatements', true), + includeCompletionsWithSnippetText: config.get('suggest.includeCompletionsWithSnippetText', true), displayPartsForJSDoc: true, }; diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index 2282791a9dd..5ff6572be29 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -35,6 +35,7 @@ export default class API { public static readonly v400 = API.fromSimpleString('4.0.0'); public static readonly v401 = API.fromSimpleString('4.0.1'); public static readonly v420 = API.fromSimpleString('4.2.0'); + public static readonly v430 = API.fromSimpleString('4.3.0'); public static fromVersionString(versionString: string): API { let version = semver.valid(versionString);