diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index fedca16ded9bc..e94daeed5cc5c 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -140,6 +140,12 @@ "usesOnlineServices" ] }, + "typescript.enablePromptUseWorkspaceTsdk": { + "type": "boolean", + "default": false, + "description": "%typescript.enablePromptUseWorkspaceTsdk%", + "scope": "window" + }, "typescript.npm": { "type": [ "string", diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 331d8d1face87..70bbf57df250b 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -7,6 +7,7 @@ "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.", "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.", "typescript.tsserver.log": "Enables logging of the TS server to a file. This log can be used to diagnose TS Server issues. The log may contain file paths, source code, and other potentially sensitive information from your project.", "typescript.tsserver.pluginPaths": "Additional paths to discover TypeScript Language Service plugins. Requires using TypeScript 2.3.0 or newer in the workspace.", "typescript.tsserver.pluginPaths.item": "Either an absolute or relative path. Relative path will be resolved against workspace folder(s).", diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 2b5ba716d2181..e5bbb61ed2664 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -140,7 +140,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace(); this.versionProvider = new TypeScriptVersionProvider(this._configuration); this.pluginPathsProvider = new TypeScriptPluginPathsProvider(this._configuration); - this._versionManager = this._register(new TypeScriptVersionManager(this.versionProvider, this.workspaceState)); + this._versionManager = this._register(new TypeScriptVersionManager(this._configuration, this.versionProvider, this.workspaceState)); this._register(this._versionManager.onDidPickNewVersion(() => { this.restartTsServer(); })); @@ -163,6 +163,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace(); this.versionProvider.updateConfiguration(this._configuration); + this._versionManager.updateConfiguration(this._configuration); this.pluginPathsProvider.updateConfiguration(this._configuration); this.tracer.updateConfiguration(); diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index f42b0c780b256..f306503b83dc9 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -59,6 +59,7 @@ export class TypeScriptServiceConfiguration { public readonly useSeparateSyntaxServer: boolean; public readonly enableProjectDiagnostics: boolean; public readonly maxTsServerMemory: number; + public readonly enablePromptUseWorkspaceTsdk: boolean; public readonly watchOptions: protocol.WatchOptions | undefined; public static loadFromWorkspace(): TypeScriptServiceConfiguration { @@ -80,6 +81,7 @@ export class TypeScriptServiceConfiguration { this.useSeparateSyntaxServer = TypeScriptServiceConfiguration.readUseSeparateSyntaxServer(configuration); this.enableProjectDiagnostics = TypeScriptServiceConfiguration.readEnableProjectDiagnostics(configuration); this.maxTsServerMemory = TypeScriptServiceConfiguration.readMaxTsServerMemory(configuration); + this.enablePromptUseWorkspaceTsdk = TypeScriptServiceConfiguration.readEnablePromptUseWorkspaceTsdk(configuration); this.watchOptions = TypeScriptServiceConfiguration.readWatchOptions(configuration); } @@ -96,7 +98,8 @@ export class TypeScriptServiceConfiguration { && this.useSeparateSyntaxServer === other.useSeparateSyntaxServer && this.enableProjectDiagnostics === other.enableProjectDiagnostics && this.maxTsServerMemory === other.maxTsServerMemory - && objects.equals(this.watchOptions, other.watchOptions); + && objects.equals(this.watchOptions, other.watchOptions) + && this.enablePromptUseWorkspaceTsdk === other.enablePromptUseWorkspaceTsdk; } private static fixPathPrefixes(inspectValue: string): string { @@ -175,4 +178,8 @@ export class TypeScriptServiceConfiguration { } return Math.max(memoryInMB, minimumMaxMemory); } + + private static readEnablePromptUseWorkspaceTsdk(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get('typescript.enablePromptUseWorkspaceTsdk', false); + } } diff --git a/extensions/typescript-language-features/src/utils/versionManager.ts b/extensions/typescript-language-features/src/utils/versionManager.ts index 254f2deb008ac..08f31787e4093 100644 --- a/extensions/typescript-language-features/src/utils/versionManager.ts +++ b/extensions/typescript-language-features/src/utils/versionManager.ts @@ -7,10 +7,12 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { TypeScriptVersion, TypeScriptVersionProvider } from './versionProvider'; import { Disposable } from './dispose'; +import { TypeScriptServiceConfiguration } from '../utils/configuration'; const localize = nls.loadMessageBundle(); const useWorkspaceTsdkStorageKey = 'typescript.useWorkspaceTsdk'; +const suppressPromptWorkspaceTsdkStorageKey = 'typescript.suppressPromptWorkspaceTsdk'; interface QuickPickItem extends vscode.QuickPickItem { run(): void; @@ -21,6 +23,7 @@ export class TypeScriptVersionManager extends Disposable { private _currentVersion: TypeScriptVersion; public constructor( + private configuration: TypeScriptServiceConfiguration, private readonly versionProvider: TypeScriptVersionProvider, private readonly workspaceState: vscode.Memento ) { @@ -34,11 +37,30 @@ export class TypeScriptVersionManager extends Disposable { this._currentVersion = localVersion; } } + + if (this.isInPromptWorkspaceTsdkState(configuration)) { + setImmediate(() => { + this.promptUseWorkspaceTsdk(); + }); + } + } private readonly _onDidPickNewVersion = this._register(new vscode.EventEmitter()); public readonly onDidPickNewVersion = this._onDidPickNewVersion.event; + public updateConfiguration(nextConfiguration: TypeScriptServiceConfiguration) { + const lastConfiguration = this.configuration; + this.configuration = nextConfiguration; + + if ( + !this.isInPromptWorkspaceTsdkState(lastConfiguration) + && this.isInPromptWorkspaceTsdkState(nextConfiguration) + ) { + this.promptUseWorkspaceTsdk(); + } + } + public get currentVersion(): TypeScriptVersion { return this._currentVersion; } @@ -71,7 +93,7 @@ export class TypeScriptVersionManager extends Disposable { detail: bundledVersion.pathLabel, run: async () => { await this.workspaceState.update(useWorkspaceTsdkStorageKey, false); - this.updateForPickedVersion(bundledVersion); + this.updateActiveVersion(bundledVersion); }, }; } @@ -88,13 +110,38 @@ export class TypeScriptVersionManager extends Disposable { await this.workspaceState.update(useWorkspaceTsdkStorageKey, true); const tsConfig = vscode.workspace.getConfiguration('typescript'); await tsConfig.update('tsdk', version.pathLabel, false); - this.updateForPickedVersion(version); + this.updateActiveVersion(version); }, }; }); } - private updateForPickedVersion(pickedVersion: TypeScriptVersion) { + private async promptUseWorkspaceTsdk(): Promise { + const workspaceVersion = this.versionProvider.localVersion; + + if (workspaceVersion === undefined) { + throw new Error('Could not prompt to use workspace TypeScript version because no workspace version is specified'); + } + + const allowIt = localize('allow', 'Allow'); + const dismissPrompt = localize('dismiss', 'Dismiss'); + const suppressPrompt = localize('suppress prompt', 'Never in this Workspace'); + + const result = await vscode.window.showInformationMessage(localize('promptUseWorkspaceTsdk', 'This workspace contains a TypeScript version. Would you like to use the workspace TypeScript version for TypeScript and JavaScript language features?'), + allowIt, + dismissPrompt, + suppressPrompt + ); + + if (result === allowIt) { + await this.workspaceState.update(useWorkspaceTsdkStorageKey, true); + this.updateActiveVersion(workspaceVersion); + } else if (result === suppressPrompt) { + await this.workspaceState.update(suppressPromptWorkspaceTsdkStorageKey, true); + } + } + + private updateActiveVersion(pickedVersion: TypeScriptVersion) { const oldVersion = this.currentVersion; this._currentVersion = pickedVersion; if (!oldVersion.eq(pickedVersion)) { @@ -105,6 +152,19 @@ export class TypeScriptVersionManager extends Disposable { private get useWorkspaceTsdkSetting(): boolean { return this.workspaceState.get(useWorkspaceTsdkStorageKey, false); } + + private get suppressPromptWorkspaceTsdkSetting(): boolean { + return this.workspaceState.get(suppressPromptWorkspaceTsdkStorageKey, false); + } + + private isInPromptWorkspaceTsdkState(configuration: TypeScriptServiceConfiguration) { + return ( + configuration.localTsdk !== null + && configuration.enablePromptUseWorkspaceTsdk === true + && this.suppressPromptWorkspaceTsdkSetting === false + && this.useWorkspaceTsdkSetting === false + ); + } } const LearnMorePickItem: QuickPickItem = {