Add the concept of client capabilities for TypeScript
For serverless, we will only be able to run the TypeScript syntax server which does not support all features. This change makes this possible by adding the concept of client capabilities. Providers such as rename will only be registered when the client has semantic capabilities
This commit is contained in:
parent
16c6b81b3e
commit
0857489caf
|
@ -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 => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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<Proto.NavTreeResponse>,
|
||||
) {
|
||||
return conditionalRegistration([
|
||||
requireConfiguration(modeId, 'referencesCodeLens.enabled')
|
||||
requireConfiguration(modeId, 'referencesCodeLens.enabled'),
|
||||
requireCapability(client, ClientCapability.Semantic),
|
||||
], () => {
|
||||
return vscode.languages.registerCodeLensProvider(selector,
|
||||
new TypeScriptReferencesCodeLensProvider(client, cachedResponse, modeId));
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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<ITypeScriptServiceClient>,
|
||||
) {
|
||||
return vscode.tasks.registerTaskProvider('typescript', new TscTaskProvider(lazyClient));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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<ClientCapability>,
|
||||
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<ClientCapability>,
|
||||
configuration: TypeScriptServiceConfiguration,
|
||||
): CompositeServerType {
|
||||
if (!capabilities.has(ClientCapability.Semantic)) {
|
||||
return CompositeServerType.SyntaxOnly;
|
||||
}
|
||||
|
||||
switch (configuration.separateSyntaxServer) {
|
||||
case SeparateSyntaxServerConfiguration.Disabled:
|
||||
return CompositeServerType.Single;
|
||||
|
|
|
@ -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<Proto.EndInstallTypesEventBody>;
|
||||
readonly onTypesInstallerInitializationFailed: vscode.Event<Proto.TypesInstallerInitializationFailedEventBody>;
|
||||
|
||||
readonly capabilities: Set<ClientCapability>;
|
||||
readonly onDidChangeCapabilities: vscode.Event<Set<ClientCapability>>;
|
||||
|
||||
onReady(f: () => void): Promise<void>;
|
||||
|
||||
showVersionPicker(): void;
|
||||
|
|
|
@ -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>([
|
||||
ClientCapability.Semantic,
|
||||
ClientCapability.Syntax,
|
||||
]);
|
||||
}
|
||||
|
||||
private readonly _onDidChangeCapabilities = this._register(new vscode.EventEmitter<Set<ClientCapability>>());
|
||||
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);
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue