diff --git a/extensions/typescript-language-features/src/extension.ts b/extensions/typescript-language-features/src/extension.ts index 36783cc5e6a..657755e964a 100644 --- a/extensions/typescript-language-features/src/extension.ts +++ b/extensions/typescript-language-features/src/extension.ts @@ -3,23 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as rimraf from 'rimraf'; import * as vscode from 'vscode'; import { Api, getExtensionApi } from './api'; import { registerCommands } from './commands/index'; import { LanguageConfigurationManager } from './features/languageConfiguration'; +import * as task from './features/task'; import TypeScriptServiceClientHost from './typeScriptServiceClientHost'; import { flatten } from './utils/arrays'; -import * as electron from './utils/electron'; -import * as rimraf from 'rimraf'; import { CommandManager } from './utils/commandManager'; +import * as electron from './utils/electron'; import * as fileSchemes from './utils/fileSchemes'; import { standardLanguageDescriptions } from './utils/languageDescription'; +import * as ProjectStatus from './utils/largeProjectStatus'; import { lazy, Lazy } from './utils/lazy'; import LogDirectoryProvider from './utils/logDirectoryProvider'; import ManagedFileContextManager from './utils/managedFileContext'; import { PluginManager } from './utils/plugins'; -import * as ProjectStatus from './utils/largeProjectStatus'; -import TscTaskProvider from './features/task'; export function activate( context: vscode.ExtensionContext @@ -38,7 +38,7 @@ export function activate( }); registerCommands(commandManager, lazyClientHost, pluginManager); - context.subscriptions.push(vscode.tasks.registerTaskProvider('typescript', new TscTaskProvider(lazyClientHost.map(x => x.serviceClient)))); + context.subscriptions.push(task.register(lazyClientHost.map(x => x.serviceClient))); context.subscriptions.push(new LanguageConfigurationManager()); import('./features/tsconfig').then(module => { diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index 647db7a1187..7fb00fbc894 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import type * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ITypeScriptServiceClient, ClientCapability } from '../typescriptService'; import API from '../utils/api'; import { coalesce } from '../utils/arrays'; import { Delayer } from '../utils/async'; @@ -302,9 +302,9 @@ class GetErrRequest { onDone: () => void ) { const allFiles = coalesce(Array.from(files.entries).map(entry => client.normalizedPath(entry.resource))); - if (!allFiles.length) { + if (!allFiles.length || !client.capabilities.has(ClientCapability.Semantic)) { this._done = true; - onDone(); + setImmediate(onDone); } else { const request = client.configuration.enableProjectDiagnostics // Note that geterrForProject is almost certainly not the api we want here as it ends up computing far diff --git a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts index 1e8b5bdeec4..4e56d6499e7 100644 --- a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts +++ b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts @@ -7,11 +7,11 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import type * as Proto from '../protocol'; import * as PConst from '../protocol.const'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import { conditionalRegistration, requireConfiguration } from '../utils/dependentRegistration'; -import { TypeScriptBaseCodeLensProvider, ReferencesCodeLens, getSymbolRange } from './baseCodeLensProvider'; import { CachedResponse } from '../tsServer/cachedResponse'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import { conditionalRegistration, requireCapability, requireConfiguration } from '../utils/dependentRegistration'; import * as typeConverters from '../utils/typeConverters'; +import { getSymbolRange, ReferencesCodeLens, TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'; const localize = nls.loadMessageBundle(); @@ -96,6 +96,7 @@ export function register( ) { return conditionalRegistration([ requireConfiguration(modeId, 'implementationsCodeLens.enabled'), + requireCapability(client, ClientCapability.Semantic), ], () => { return vscode.languages.registerCodeLensProvider(selector, new TypeScriptImplementationsCodeLensProvider(client, cachedResponse)); diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index 57b6a2f0f46..1d6fd6aa349 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import type * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ITypeScriptServiceClient, ClientCapability } from '../typescriptService'; import API from '../utils/api'; import { nulToken } from '../utils/cancellation'; import { applyCodeActionCommands, getEditForCodeAction } from '../utils/codeAction'; @@ -18,6 +18,7 @@ import * as typeConverters from '../utils/typeConverters'; import { DiagnosticsManager } from './diagnostics'; import FileConfigurationManager from './fileConfigurationManager'; import { equals } from '../utils/objects'; +import { conditionalRegistration, requireCapability } from '../utils/dependentRegistration'; const localize = nls.loadMessageBundle(); @@ -409,7 +410,11 @@ export function register( diagnosticsManager: DiagnosticsManager, telemetryReporter: TelemetryReporter ) { - return vscode.languages.registerCodeActionsProvider(selector, - new TypeScriptQuickFixProvider(client, fileConfigurationManager, commandManager, diagnosticsManager, telemetryReporter), - TypeScriptQuickFixProvider.metadata); + return conditionalRegistration([ + requireCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerCodeActionsProvider(selector, + new TypeScriptQuickFixProvider(client, fileConfigurationManager, commandManager, diagnosticsManager, telemetryReporter), + TypeScriptQuickFixProvider.metadata); + }); } diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index 9c6ac23977c..a56267acb42 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -7,11 +7,11 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { LearnMoreAboutRefactoringsCommand } from '../commands/learnMoreAboutRefactorings'; import type * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { nulToken } from '../utils/cancellation'; import { Command, CommandManager } from '../utils/commandManager'; -import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireCapability, requireMinVersion } from '../utils/dependentRegistration'; import * as fileSchemes from '../utils/fileSchemes'; import { TelemetryReporter } from '../utils/telemetry'; import * as typeConverters from '../utils/typeConverters'; @@ -403,7 +403,8 @@ export function register( telemetryReporter: TelemetryReporter, ) { return conditionalRegistration([ - requireMinVersion(client, TypeScriptRefactorProvider.minVersion) + requireMinVersion(client, TypeScriptRefactorProvider.minVersion), + requireCapability(client, ClientCapability.Semantic), ], () => { return vscode.languages.registerCodeActionsProvider(selector, new TypeScriptRefactorProvider(client, formattingOptionsManager, commandManager, telemetryReporter), diff --git a/extensions/typescript-language-features/src/features/referencesCodeLens.ts b/extensions/typescript-language-features/src/features/referencesCodeLens.ts index 1bf3c404ad7..bfda4f07230 100644 --- a/extensions/typescript-language-features/src/features/referencesCodeLens.ts +++ b/extensions/typescript-language-features/src/features/referencesCodeLens.ts @@ -8,8 +8,8 @@ import * as nls from 'vscode-nls'; import type * as Proto from '../protocol'; import * as PConst from '../protocol.const'; import { CachedResponse } from '../tsServer/cachedResponse'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import { conditionalRegistration, requireConfiguration } from '../utils/dependentRegistration'; +import { ITypeScriptServiceClient, ClientCapability } from '../typescriptService'; +import { conditionalRegistration, requireConfiguration, requireCapability } from '../utils/dependentRegistration'; import * as typeConverters from '../utils/typeConverters'; import { getSymbolRange, ReferencesCodeLens, TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'; @@ -128,7 +128,8 @@ export function register( cachedResponse: CachedResponse, ) { return conditionalRegistration([ - requireConfiguration(modeId, 'referencesCodeLens.enabled') + requireConfiguration(modeId, 'referencesCodeLens.enabled'), + requireCapability(client, ClientCapability.Semantic), ], () => { return vscode.languages.registerCodeLensProvider(selector, new TypeScriptReferencesCodeLensProvider(client, cachedResponse, modeId)); diff --git a/extensions/typescript-language-features/src/features/rename.ts b/extensions/typescript-language-features/src/features/rename.ts index 9074faa4372..da784760d66 100644 --- a/extensions/typescript-language-features/src/features/rename.ts +++ b/extensions/typescript-language-features/src/features/rename.ts @@ -7,8 +7,9 @@ import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import type * as Proto from '../protocol'; -import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; import API from '../utils/api'; +import { conditionalRegistration, requireCapability } from '../utils/dependentRegistration'; import * as typeConverters from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; @@ -141,6 +142,10 @@ export function register( client: ITypeScriptServiceClient, fileConfigurationManager: FileConfigurationManager, ) { - return vscode.languages.registerRenameProvider(selector, - new TypeScriptRenameProvider(client, fileConfigurationManager)); + return conditionalRegistration([ + requireCapability(client, ClientCapability.Semantic), + ], () => { + return vscode.languages.registerRenameProvider(selector, + new TypeScriptRenameProvider(client, fileConfigurationManager)); + }); } diff --git a/extensions/typescript-language-features/src/features/semanticTokens.ts b/extensions/typescript-language-features/src/features/semanticTokens.ts index 4546a1e6bc8..6174b51c8e5 100644 --- a/extensions/typescript-language-features/src/features/semanticTokens.ts +++ b/extensions/typescript-language-features/src/features/semanticTokens.ts @@ -7,9 +7,9 @@ import { TokenEncodingConsts, TokenModifier, TokenType, VersionRequirement } from 'typescript-vscode-sh-plugin/lib/constants'; import * as vscode from 'vscode'; import * as Proto from '../protocol'; -import { ExecConfig, ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import { ClientCapability, ExecConfig, ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; import API from '../utils/api'; -import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireCapability, requireMinVersion } from '../utils/dependentRegistration'; const minTypeScriptVersion = API.fromVersionString(`${VersionRequirement.major}.${VersionRequirement.minor}`); @@ -20,6 +20,7 @@ const CONTENT_LENGTH_LIMIT = 100000; export function register(selector: vscode.DocumentSelector, client: ITypeScriptServiceClient) { return conditionalRegistration([ requireMinVersion(client, minTypeScriptVersion), + requireCapability(client, ClientCapability.Semantic), ], () => { const provider = new DocumentSemanticTokensProvider(client); return vscode.Disposable.from( diff --git a/extensions/typescript-language-features/src/features/task.ts b/extensions/typescript-language-features/src/features/task.ts index b63ff25019f..4fa8bba2bdb 100644 --- a/extensions/typescript-language-features/src/features/task.ts +++ b/extensions/typescript-language-features/src/features/task.ts @@ -35,7 +35,7 @@ interface TypeScriptTaskDefinition extends vscode.TaskDefinition { /** * Provides tasks for building `tsconfig.json` files in a project. */ -export default class TscTaskProvider implements vscode.TaskProvider { +class TscTaskProvider implements vscode.TaskProvider { private readonly projectInfoRequestTimeout = 2000; private autoDetect: AutoDetect = 'on'; @@ -291,3 +291,9 @@ export default class TscTaskProvider implements vscode.TaskProvider { this.autoDetect = typeof type === 'undefined' ? 'on' : type; } } + +export function register( + lazyClient: Lazy, +) { + return vscode.tasks.registerTaskProvider('typescript', new TscTaskProvider(lazyClient)); +} diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 408d5100017..84171e18fb7 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -7,11 +7,11 @@ import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import type * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { Delayer } from '../utils/async'; import { nulToken } from '../utils/cancellation'; -import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; +import { conditionalRegistration, requireCapability, requireMinVersion } from '../utils/dependentRegistration'; import { Disposable } from '../utils/dispose'; import * as fileSchemes from '../utils/fileSchemes'; import { doesResourceLookLikeATypeScriptFile } from '../utils/languageDescription'; @@ -296,6 +296,7 @@ export function register( ) { return conditionalRegistration([ requireMinVersion(client, UpdateImportsOnFileRenameHandler.minVersion), + requireCapability(client, ClientCapability.Semantic), ], () => { return new UpdateImportsOnFileRenameHandler(client, fileConfigurationManager, handles); }); diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index eefd944d70e..c84ef656484 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -8,6 +8,7 @@ import * as path from 'path'; import * as stream from 'stream'; import * as vscode from 'vscode'; import type * as Proto from '../protocol'; +import { ClientCapability } from '../typescriptService'; import API from '../utils/api'; import { SeparateSyntaxServerConfiguration, TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration'; import * as electron from '../utils/electron'; @@ -36,6 +37,9 @@ const enum CompositeServerType { /** Use a separate syntax server while the project is loading */ DynamicSeparateSyntax, + + /** Only enable the syntax server */ + SyntaxOnly } export class TypeScriptServerSpawner { @@ -50,12 +54,13 @@ export class TypeScriptServerSpawner { public spawn( version: TypeScriptVersion, + capabilities: Set, configuration: TypeScriptServiceConfiguration, pluginManager: PluginManager, delegate: TsServerDelegate, ): ITypeScriptServer { let primaryServer: ITypeScriptServer; - const serverType = this.getCompositeServerType(version, configuration); + const serverType = this.getCompositeServerType(version, capabilities, configuration); switch (serverType) { case CompositeServerType.SeparateSyntax: case CompositeServerType.DynamicSeparateSyntax: @@ -72,6 +77,11 @@ export class TypeScriptServerSpawner { primaryServer = this.spawnTsServer(ServerKind.Main, version, configuration, pluginManager); break; } + case CompositeServerType.SyntaxOnly: + { + primaryServer = this.spawnTsServer(ServerKind.Syntax, version, configuration, pluginManager); + break; + } } if (this.shouldUseSeparateDiagnosticsServer(configuration)) { @@ -86,8 +96,13 @@ export class TypeScriptServerSpawner { private getCompositeServerType( version: TypeScriptVersion, + capabilities: Set, configuration: TypeScriptServiceConfiguration, ): CompositeServerType { + if (!capabilities.has(ClientCapability.Semantic)) { + return CompositeServerType.SyntaxOnly; + } + switch (configuration.separateSyntaxServer) { case SeparateSyntaxServerConfiguration.Disabled: return CompositeServerType.Single; diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index e3d89eb2bc7..bd7437936e2 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -85,6 +85,11 @@ export type ExecConfig = { readonly cancelOnResourceChange?: vscode.Uri }; +export enum ClientCapability { + Syntax, + Semantic, +} + export interface ITypeScriptServiceClient { /** * Convert a resource (VS Code) to a normalized path (TypeScript). @@ -120,6 +125,9 @@ export interface ITypeScriptServiceClient { readonly onDidEndInstallTypings: vscode.Event; readonly onTypesInstallerInitializationFailed: vscode.Event; + readonly capabilities: Set; + readonly onDidChangeCapabilities: vscode.Event>; + onReady(f: () => void): Promise; showVersionPicker(): void; diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 61c176a2ad3..e1c750725d2 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -10,10 +10,11 @@ import * as nls from 'vscode-nls'; import BufferSyncSupport from './features/bufferSyncSupport'; import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics'; import * as Proto from './protocol'; +import { EventName } from './protocol.const'; import { ITypeScriptServer } from './tsServer/server'; import { TypeScriptServerError } from './tsServer/serverError'; import { TypeScriptServerSpawner } from './tsServer/spawner'; -import { ExecConfig, ITypeScriptServiceClient, ServerResponse, TypeScriptRequests } from './typescriptService'; +import { ClientCapability, ExecConfig, ITypeScriptServiceClient, ServerResponse, TypeScriptRequests } from './typescriptService'; import API from './utils/api'; import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'; import { Disposable } from './utils/dispose'; @@ -22,12 +23,11 @@ import LogDirectoryProvider from './utils/logDirectoryProvider'; import Logger from './utils/logger'; import { TypeScriptPluginPathsProvider } from './utils/pluginPathsProvider'; import { PluginManager } from './utils/plugins'; -import { TelemetryReporter, VSCodeTelemetryReporter, TelemetryProperties } from './utils/telemetry'; +import { TelemetryProperties, TelemetryReporter, VSCodeTelemetryReporter } from './utils/telemetry'; import Tracer from './utils/tracer'; import { inferredProjectCompilerOptions, ProjectType } from './utils/tsconfig'; import { TypeScriptVersionManager } from './utils/versionManager'; import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionProvider'; -import { EventName } from './protocol.const'; const localize = nls.loadMessageBundle(); @@ -203,6 +203,16 @@ export default class TypeScriptServiceClient extends Disposable implements IType })); } + public get capabilities() { + return new Set([ + ClientCapability.Semantic, + ClientCapability.Syntax, + ]); + } + + private readonly _onDidChangeCapabilities = this._register(new vscode.EventEmitter>()); + readonly onDidChangeCapabilities = this._onDidChangeCapabilities.event; + private cancelInflightRequestsForResource(resource: vscode.Uri): void { if (this.serverState.type !== ServerState.Type.Running) { return; @@ -337,7 +347,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType const apiVersion = version.apiVersion || API.defaultVersion; let mytoken = ++this.token; - const handle = this.typescriptServerSpawner.spawn(version, this.configuration, this.pluginManager, { + const handle = this.typescriptServerSpawner.spawn(version, this.capabilities, this.configuration, this.pluginManager, { onFatalError: (command, err) => this.fatalError(command, err), }); this.serverState = new ServerState.Running(handle, apiVersion, undefined, true); diff --git a/extensions/typescript-language-features/src/utils/dependentRegistration.ts b/extensions/typescript-language-features/src/utils/dependentRegistration.ts index 6a87f286e6d..a10a33ba80c 100644 --- a/extensions/typescript-language-features/src/utils/dependentRegistration.ts +++ b/extensions/typescript-language-features/src/utils/dependentRegistration.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ITypeScriptServiceClient, ClientCapability } from '../typescriptService'; import API from './api'; import { Disposable } from './dispose'; @@ -95,3 +95,13 @@ export function requireConfiguration( vscode.workspace.onDidChangeConfiguration ); } + +export function requireCapability( + client: ITypeScriptServiceClient, + requiredCapability: ClientCapability, +) { + return new Condition( + () => client.capabilities.has(requiredCapability), + client.onDidChangeCapabilities + ); +}