mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-03 06:51:53 +01:00
Enable web TS Server (#102990)
This enables running the TS Server on web. This currently requires a special version of the TypeScript server
This commit is contained in:
@@ -4,14 +4,36 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { noopLogDirectoryProvider } from './tsServer/logDirectoryProvider';
|
||||
import { WorkerServerProcess } from './tsServer/workerServerProcess';
|
||||
import { Api, getExtensionApi } from './api';
|
||||
import { registerCommands } from './commands/index';
|
||||
import { LanguageConfigurationManager } from './features/languageConfiguration';
|
||||
import { createLazyClientHost, lazilyActivateClient } from './lazyClientHost';
|
||||
import { CommandManager } from './utils/commandManager';
|
||||
import { PluginManager } from './utils/plugins';
|
||||
import { noopRequestCancellerFactory } from './tsServer/cancellation';
|
||||
import { noopLogDirectoryProvider } from './tsServer/logDirectoryProvider';
|
||||
import API from './utils/api';
|
||||
import { CommandManager } from './utils/commandManager';
|
||||
import { TypeScriptServiceConfiguration } from './utils/configuration';
|
||||
import { PluginManager } from './utils/plugins';
|
||||
import { ITypeScriptVersionProvider, TypeScriptVersion, TypeScriptVersionSource } from './utils/versionProvider';
|
||||
|
||||
class StaticVersionProvider implements ITypeScriptVersionProvider {
|
||||
|
||||
constructor(
|
||||
private readonly _version: TypeScriptVersion
|
||||
) { }
|
||||
|
||||
updateConfiguration(_configuration: TypeScriptServiceConfiguration): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
get defaultVersion() { return this._version; }
|
||||
get bundledVersion() { return this._version; }
|
||||
|
||||
readonly globalVersion = undefined;
|
||||
readonly localVersion = undefined;
|
||||
readonly localVersions = [];
|
||||
}
|
||||
|
||||
export function activate(
|
||||
context: vscode.ExtensionContext
|
||||
@@ -25,7 +47,13 @@ export function activate(
|
||||
const onCompletionAccepted = new vscode.EventEmitter<vscode.CompletionItem>();
|
||||
context.subscriptions.push(onCompletionAccepted);
|
||||
|
||||
const lazyClientHost = createLazyClientHost(context, pluginManager, commandManager, noopLogDirectoryProvider, noopRequestCancellerFactory, item => {
|
||||
const versionProvider = new StaticVersionProvider(
|
||||
new TypeScriptVersion(
|
||||
TypeScriptVersionSource.Bundled,
|
||||
'/builtin-extension/typescript-language-features/dist/browser/typescript-web/tsserver.js',
|
||||
API.v400));
|
||||
|
||||
const lazyClientHost = createLazyClientHost(context, false, pluginManager, commandManager, noopLogDirectoryProvider, noopRequestCancellerFactory, versionProvider, WorkerServerProcess, item => {
|
||||
onCompletionAccepted.fire(item);
|
||||
});
|
||||
|
||||
|
||||
@@ -5,16 +5,19 @@
|
||||
|
||||
import * as rimraf from 'rimraf';
|
||||
import * as vscode from 'vscode';
|
||||
import { NodeLogDirectoryProvider } from './tsServer/logDirectoryProvider.electron';
|
||||
import { Api, getExtensionApi } from './api';
|
||||
import { registerCommands } from './commands/index';
|
||||
import { LanguageConfigurationManager } from './features/languageConfiguration';
|
||||
import * as task from './features/task';
|
||||
import { createLazyClientHost, lazilyActivateClient } from './lazyClientHost';
|
||||
import { nodeRequestCancellerFactory } from './tsServer/cancellation.electron';
|
||||
import { NodeLogDirectoryProvider } from './tsServer/logDirectoryProvider.electron';
|
||||
import { CommandManager } from './utils/commandManager';
|
||||
import * as electron from './utils/electron';
|
||||
import { onCaseInsenitiveFileSystem } from './utils/fileSystem';
|
||||
import { PluginManager } from './utils/plugins';
|
||||
import { ChildServerProcess } from './utils/serverProcess';
|
||||
import { DiskTypeScriptVersionProvider } from './utils/versionProvider.electron';
|
||||
|
||||
export function activate(
|
||||
context: vscode.ExtensionContext
|
||||
@@ -30,7 +33,9 @@ export function activate(
|
||||
|
||||
const logDirectoryProvider = new NodeLogDirectoryProvider(context);
|
||||
|
||||
const lazyClientHost = createLazyClientHost(context, pluginManager, commandManager, logDirectoryProvider, nodeRequestCancellerFactory, item => {
|
||||
const versionProvider = new DiskTypeScriptVersionProvider();
|
||||
|
||||
const lazyClientHost = createLazyClientHost(context, onCaseInsenitiveFileSystem(), pluginManager, commandManager, logDirectoryProvider, nodeRequestCancellerFactory, versionProvider, ChildServerProcess, item => {
|
||||
onCompletionAccepted.fire(item);
|
||||
});
|
||||
|
||||
|
||||
@@ -123,9 +123,9 @@ export function register(
|
||||
) {
|
||||
return conditionalRegistration([
|
||||
requireMinVersion(client, TypeScriptCallHierarchySupport.minVersion),
|
||||
requireSomeCapability(client, ClientCapability.EnhancedSyntax, ClientCapability.Semantic),
|
||||
requireSomeCapability(client, ClientCapability.Semantic),
|
||||
], () => {
|
||||
return vscode.languages.registerCallHierarchyProvider(selector.syntax,
|
||||
return vscode.languages.registerCallHierarchyProvider(selector.semantic,
|
||||
new TypeScriptCallHierarchySupport(client));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import API from '../utils/api';
|
||||
import { Disposable } from '../utils/dispose';
|
||||
import * as fileSchemes from '../utils/fileSchemes';
|
||||
import { onCaseInsenitiveFileSystem } from '../utils/fileSystem';
|
||||
import { isTypeScriptDocument } from '../utils/languageModeIds';
|
||||
import { equals } from '../utils/objects';
|
||||
import { ResourceMap } from '../utils/resourceMap';
|
||||
@@ -34,10 +33,11 @@ export default class FileConfigurationManager extends Disposable {
|
||||
private readonly formatOptions: ResourceMap<Promise<FileConfiguration | undefined>>;
|
||||
|
||||
public constructor(
|
||||
private readonly client: ITypeScriptServiceClient
|
||||
private readonly client: ITypeScriptServiceClient,
|
||||
onCaseInsenitiveFileSystem: boolean
|
||||
) {
|
||||
super();
|
||||
this.formatOptions = new ResourceMap(undefined, { onCaseInsenitiveFileSystem: onCaseInsenitiveFileSystem() });
|
||||
this.formatOptions = new ResourceMap(undefined, { onCaseInsenitiveFileSystem });
|
||||
vscode.workspace.onDidCloseTextDocument(textDocument => {
|
||||
// When a document gets closed delete the cached formatting options.
|
||||
// This is necessary since the tsserver now closed a project when its
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
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 { nulToken } from '../utils/cancellation';
|
||||
import { Command, CommandManager } from '../utils/commandManager';
|
||||
import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration';
|
||||
import { conditionalRegistration, requireMinVersion, requireSomeCapability } from '../utils/dependentRegistration';
|
||||
import { DocumentSelector } from '../utils/documentSelector';
|
||||
import { TelemetryReporter } from '../utils/telemetry';
|
||||
import * as typeconverts from '../utils/typeConverters';
|
||||
@@ -108,10 +108,10 @@ export function register(
|
||||
) {
|
||||
return conditionalRegistration([
|
||||
requireMinVersion(client, OrganizeImportsCodeActionProvider.minVersion),
|
||||
|
||||
requireSomeCapability(client, ClientCapability.Semantic),
|
||||
], () => {
|
||||
const organizeImportsProvider = new OrganizeImportsCodeActionProvider(client, commandManager, fileConfigurationManager, telemetryReporter);
|
||||
return vscode.languages.registerCodeActionsProvider(selector.syntax,
|
||||
return vscode.languages.registerCodeActionsProvider(selector.semantic,
|
||||
organizeImportsProvider,
|
||||
organizeImportsProvider.metadata);
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { TsServerProcessFactory } from './tsServer/server';
|
||||
import { OngoingRequestCancellerFactory } from './tsServer/cancellation';
|
||||
import { ILogDirectoryProvider } from './tsServer/logDirectoryProvider';
|
||||
import TypeScriptServiceClientHost from './typeScriptServiceClientHost';
|
||||
@@ -14,23 +15,30 @@ import * as ProjectStatus from './utils/largeProjectStatus';
|
||||
import { lazy, Lazy } from './utils/lazy';
|
||||
import ManagedFileContextManager from './utils/managedFileContext';
|
||||
import { PluginManager } from './utils/plugins';
|
||||
import { ITypeScriptVersionProvider } from './utils/versionProvider';
|
||||
|
||||
export function createLazyClientHost(
|
||||
context: vscode.ExtensionContext,
|
||||
onCaseInsenitiveFileSystem: boolean,
|
||||
pluginManager: PluginManager,
|
||||
commandManager: CommandManager,
|
||||
logDirectoryProvider: ILogDirectoryProvider,
|
||||
cancellerFactory: OngoingRequestCancellerFactory,
|
||||
versionProvider: ITypeScriptVersionProvider,
|
||||
processFactory: TsServerProcessFactory,
|
||||
onCompletionAccepted: (item: vscode.CompletionItem) => void,
|
||||
): Lazy<TypeScriptServiceClientHost> {
|
||||
return lazy(() => {
|
||||
const clientHost = new TypeScriptServiceClientHost(
|
||||
standardLanguageDescriptions,
|
||||
context.workspaceState,
|
||||
onCaseInsenitiveFileSystem,
|
||||
pluginManager,
|
||||
commandManager,
|
||||
logDirectoryProvider,
|
||||
cancellerFactory,
|
||||
versionProvider,
|
||||
processFactory,
|
||||
onCompletionAccepted);
|
||||
|
||||
context.subscriptions.push(clientHost);
|
||||
@@ -46,7 +54,6 @@ export function createLazyClientHost(
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function lazilyActivateClient(
|
||||
lazyClientHost: Lazy<TypeScriptServiceClientHost>,
|
||||
pluginManager: PluginManager,
|
||||
|
||||
@@ -7,14 +7,16 @@ import * as vscode from 'vscode';
|
||||
import type * as Proto from '../protocol';
|
||||
import { EventName } from '../protocol.const';
|
||||
import { CallbackMap } from '../tsServer/callbackMap';
|
||||
import { OngoingRequestCanceller } from './cancellation';
|
||||
import { RequestItem, RequestQueue, RequestQueueingType } from '../tsServer/requestQueue';
|
||||
import { TypeScriptServerError } from '../tsServer/serverError';
|
||||
import { ServerResponse, TypeScriptRequests } from '../typescriptService';
|
||||
import { TypeScriptServiceConfiguration } from '../utils/configuration';
|
||||
import { Disposable } from '../utils/dispose';
|
||||
import { TelemetryReporter } from '../utils/telemetry';
|
||||
import Tracer from '../utils/tracer';
|
||||
import { TypeScriptVersionManager } from '../utils/versionManager';
|
||||
import { TypeScriptVersion } from '../utils/versionProvider';
|
||||
import { OngoingRequestCanceller } from './cancellation';
|
||||
|
||||
export enum ExectuionTarget {
|
||||
Semantic,
|
||||
@@ -41,6 +43,23 @@ export interface TsServerDelegate {
|
||||
onFatalError(command: string, error: Error): void;
|
||||
}
|
||||
|
||||
export const enum TsServerProcessKind {
|
||||
Main = 'main',
|
||||
Syntax = 'syntax',
|
||||
Semantic = 'semantic',
|
||||
Diagnostics = 'diagnostics'
|
||||
}
|
||||
|
||||
export interface TsServerProcessFactory {
|
||||
fork(
|
||||
tsServerPath: string,
|
||||
args: readonly string[],
|
||||
kind: TsServerProcessKind,
|
||||
configuration: TypeScriptServiceConfiguration,
|
||||
versionManager: TypeScriptVersionManager,
|
||||
): TsServerProcess;
|
||||
}
|
||||
|
||||
export interface TsServerProcess {
|
||||
write(serverRequest: Proto.Request): void;
|
||||
|
||||
@@ -51,7 +70,6 @@ export interface TsServerProcess {
|
||||
kill(): void;
|
||||
}
|
||||
|
||||
|
||||
export class ProcessBasedTsServer extends Disposable implements ITypeScriptServer {
|
||||
private readonly _requestQueue = new RequestQueue();
|
||||
private readonly _callbacks = new CallbackMap<Proto.Response>();
|
||||
|
||||
@@ -9,23 +9,15 @@ import { OngoingRequestCancellerFactory } from '../tsServer/cancellation';
|
||||
import { ClientCapabilities, ClientCapability } from '../typescriptService';
|
||||
import API from '../utils/api';
|
||||
import { SeparateSyntaxServerConfiguration, TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration';
|
||||
import * as electron from '../utils/electron';
|
||||
import { ILogDirectoryProvider } from './logDirectoryProvider';
|
||||
import Logger from '../utils/logger';
|
||||
import { TypeScriptPluginPathsProvider } from '../utils/pluginPathsProvider';
|
||||
import { PluginManager } from '../utils/plugins';
|
||||
import { ChildServerProcess } from '../utils/serverProcess';
|
||||
import { TelemetryReporter } from '../utils/telemetry';
|
||||
import Tracer from '../utils/tracer';
|
||||
import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider';
|
||||
import { GetErrRoutingTsServer, ITypeScriptServer, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerDelegate } from './server';
|
||||
|
||||
const enum ServerKind {
|
||||
Main = 'main',
|
||||
Syntax = 'syntax',
|
||||
Semantic = 'semantic',
|
||||
Diagnostics = 'diagnostics'
|
||||
}
|
||||
import { TypeScriptVersionManager } from '../utils/versionManager';
|
||||
import { ITypeScriptVersionProvider, TypeScriptVersion } from '../utils/versionProvider';
|
||||
import { ILogDirectoryProvider } from './logDirectoryProvider';
|
||||
import { GetErrRoutingTsServer, ITypeScriptServer, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerDelegate, TsServerProcessFactory, TsServerProcessKind } from './server';
|
||||
|
||||
const enum CompositeServerType {
|
||||
/** Run a single server that handles all commands */
|
||||
@@ -43,12 +35,14 @@ const enum CompositeServerType {
|
||||
|
||||
export class TypeScriptServerSpawner {
|
||||
public constructor(
|
||||
private readonly _versionProvider: TypeScriptVersionProvider,
|
||||
private readonly _versionProvider: ITypeScriptVersionProvider,
|
||||
private readonly _versionManager: TypeScriptVersionManager,
|
||||
private readonly _logDirectoryProvider: ILogDirectoryProvider,
|
||||
private readonly _pluginPathsProvider: TypeScriptPluginPathsProvider,
|
||||
private readonly _logger: Logger,
|
||||
private readonly _telemetryReporter: TelemetryReporter,
|
||||
private readonly _tracer: Tracer,
|
||||
private readonly _factory: TsServerProcessFactory,
|
||||
) { }
|
||||
|
||||
public spawn(
|
||||
@@ -67,26 +61,26 @@ export class TypeScriptServerSpawner {
|
||||
{
|
||||
const enableDynamicRouting = serverType === CompositeServerType.DynamicSeparateSyntax;
|
||||
primaryServer = new SyntaxRoutingTsServer({
|
||||
syntax: this.spawnTsServer(ServerKind.Syntax, version, configuration, pluginManager, cancellerFactory),
|
||||
semantic: this.spawnTsServer(ServerKind.Semantic, version, configuration, pluginManager, cancellerFactory),
|
||||
syntax: this.spawnTsServer(TsServerProcessKind.Syntax, version, configuration, pluginManager, cancellerFactory),
|
||||
semantic: this.spawnTsServer(TsServerProcessKind.Semantic, version, configuration, pluginManager, cancellerFactory),
|
||||
}, delegate, enableDynamicRouting);
|
||||
break;
|
||||
}
|
||||
case CompositeServerType.Single:
|
||||
{
|
||||
primaryServer = this.spawnTsServer(ServerKind.Main, version, configuration, pluginManager, cancellerFactory);
|
||||
primaryServer = this.spawnTsServer(TsServerProcessKind.Main, version, configuration, pluginManager, cancellerFactory);
|
||||
break;
|
||||
}
|
||||
case CompositeServerType.SyntaxOnly:
|
||||
{
|
||||
primaryServer = this.spawnTsServer(ServerKind.Syntax, version, configuration, pluginManager, cancellerFactory);
|
||||
primaryServer = this.spawnTsServer(TsServerProcessKind.Syntax, version, configuration, pluginManager, cancellerFactory);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.shouldUseSeparateDiagnosticsServer(configuration)) {
|
||||
return new GetErrRoutingTsServer({
|
||||
getErr: this.spawnTsServer(ServerKind.Diagnostics, version, configuration, pluginManager, cancellerFactory),
|
||||
getErr: this.spawnTsServer(TsServerProcessKind.Diagnostics, version, configuration, pluginManager, cancellerFactory),
|
||||
primary: primaryServer,
|
||||
}, delegate);
|
||||
}
|
||||
@@ -124,7 +118,7 @@ export class TypeScriptServerSpawner {
|
||||
}
|
||||
|
||||
private spawnTsServer(
|
||||
kind: ServerKind,
|
||||
kind: TsServerProcessKind,
|
||||
version: TypeScriptVersion,
|
||||
configuration: TypeScriptServiceConfiguration,
|
||||
pluginManager: PluginManager,
|
||||
@@ -144,12 +138,12 @@ export class TypeScriptServerSpawner {
|
||||
}
|
||||
|
||||
this._logger.info(`<${kind}> Forking...`);
|
||||
const childProcess = electron.fork(version.tsServerPath, args, this.getForkOptions(kind, configuration));
|
||||
const process = this._factory.fork(version.tsServerPath, args, kind, configuration, this._versionManager);
|
||||
this._logger.info(`<${kind}> Starting...`);
|
||||
|
||||
return new ProcessBasedTsServer(
|
||||
kind,
|
||||
new ChildServerProcess(childProcess),
|
||||
process!,
|
||||
tsServerLogFile,
|
||||
canceller,
|
||||
version,
|
||||
@@ -157,20 +151,8 @@ export class TypeScriptServerSpawner {
|
||||
this._tracer);
|
||||
}
|
||||
|
||||
private getForkOptions(kind: ServerKind, configuration: TypeScriptServiceConfiguration) {
|
||||
const debugPort = TypeScriptServerSpawner.getDebugPort(kind);
|
||||
const inspectFlag = process.env['TSS_DEBUG_BRK'] ? '--inspect-brk' : '--inspect';
|
||||
const tsServerForkOptions: electron.ForkOptions = {
|
||||
execArgv: [
|
||||
...(debugPort ? [`${inspectFlag}=${debugPort}`] : []),
|
||||
...(configuration.maxTsServerMemory ? [`--max-old-space-size=${configuration.maxTsServerMemory}`] : [])
|
||||
]
|
||||
};
|
||||
return tsServerForkOptions;
|
||||
}
|
||||
|
||||
private getTsServerArgs(
|
||||
kind: ServerKind,
|
||||
kind: TsServerProcessKind,
|
||||
configuration: TypeScriptServiceConfiguration,
|
||||
currentVersion: TypeScriptVersion,
|
||||
apiVersion: API,
|
||||
@@ -180,7 +162,7 @@ export class TypeScriptServerSpawner {
|
||||
const args: string[] = [];
|
||||
let tsServerLogFile: string | undefined;
|
||||
|
||||
if (kind === ServerKind.Syntax) {
|
||||
if (kind === TsServerProcessKind.Syntax) {
|
||||
args.push('--syntaxOnly');
|
||||
}
|
||||
|
||||
@@ -190,11 +172,11 @@ export class TypeScriptServerSpawner {
|
||||
args.push('--useSingleInferredProject');
|
||||
}
|
||||
|
||||
if (configuration.disableAutomaticTypeAcquisition || kind === ServerKind.Syntax || kind === ServerKind.Diagnostics) {
|
||||
if (configuration.disableAutomaticTypeAcquisition || kind === TsServerProcessKind.Syntax || kind === TsServerProcessKind.Diagnostics) {
|
||||
args.push('--disableAutomaticTypingAcquisition');
|
||||
}
|
||||
|
||||
if (kind === ServerKind.Semantic || kind === ServerKind.Main) {
|
||||
if (kind === TsServerProcessKind.Semantic || kind === TsServerProcessKind.Main) {
|
||||
args.push('--enableTelemetry');
|
||||
}
|
||||
|
||||
@@ -247,21 +229,6 @@ export class TypeScriptServerSpawner {
|
||||
return { args, tsServerLogFile };
|
||||
}
|
||||
|
||||
private static getDebugPort(kind: ServerKind): number | undefined {
|
||||
if (kind === 'syntax') {
|
||||
// We typically only want to debug the main semantic server
|
||||
return undefined;
|
||||
}
|
||||
const value = process.env['TSS_DEBUG_BRK'] || process.env['TSS_DEBUG'];
|
||||
if (value) {
|
||||
const port = parseInt(value);
|
||||
if (!isNaN(port)) {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private static isLoggingEnabled(configuration: TypeScriptServiceConfiguration) {
|
||||
return configuration.tsServerLogLevel !== TsServerLogLevel.Off;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as Proto from '../protocol';
|
||||
import { TypeScriptServiceConfiguration } from '../utils/configuration';
|
||||
import { TsServerProcess, TsServerProcessKind } from './server';
|
||||
|
||||
declare const Worker: any;
|
||||
declare type Worker = any;
|
||||
|
||||
export class WorkerServerProcess implements TsServerProcess {
|
||||
|
||||
public static fork(
|
||||
tsServerPath: string,
|
||||
args: readonly string[],
|
||||
_kind: TsServerProcessKind,
|
||||
_configuration: TypeScriptServiceConfiguration,
|
||||
) {
|
||||
const worker = new Worker(tsServerPath);
|
||||
return new WorkerServerProcess(worker, args);
|
||||
}
|
||||
|
||||
private _onDataHandlers = new Set<(data: Proto.Response) => void>();
|
||||
private _onErrorHandlers = new Set<(err: Error) => void>();
|
||||
private _onExitHandlers = new Set<(code: number | null) => void>();
|
||||
|
||||
public constructor(
|
||||
private readonly worker: Worker,
|
||||
args: readonly string[],
|
||||
) {
|
||||
worker.addEventListener('message', (msg: any) => {
|
||||
for (const handler of this._onDataHandlers) {
|
||||
handler(msg.data);
|
||||
}
|
||||
});
|
||||
worker.postMessage(args);
|
||||
}
|
||||
|
||||
write(serverRequest: Proto.Request): void {
|
||||
this.worker.postMessage(serverRequest);
|
||||
}
|
||||
|
||||
onData(handler: (response: Proto.Response) => void): void {
|
||||
this._onDataHandlers.add(handler);
|
||||
}
|
||||
|
||||
onError(handler: (err: Error) => void): void {
|
||||
this._onErrorHandlers.add(handler);
|
||||
// Todo: not implemented
|
||||
}
|
||||
|
||||
onExit(handler: (code: number | null) => void): void {
|
||||
this._onExitHandlers.add(handler);
|
||||
// Todo: not implemented
|
||||
}
|
||||
|
||||
kill(): void {
|
||||
this.worker.terminate();
|
||||
}
|
||||
}
|
||||
@@ -15,16 +15,18 @@ import LanguageProvider from './languageProvider';
|
||||
import * as Proto from './protocol';
|
||||
import * as PConst from './protocol.const';
|
||||
import { OngoingRequestCancellerFactory } from './tsServer/cancellation';
|
||||
import { ILogDirectoryProvider } from './tsServer/logDirectoryProvider';
|
||||
import { TsServerProcessFactory } from './tsServer/server';
|
||||
import TypeScriptServiceClient from './typescriptServiceClient';
|
||||
import { coalesce, flatten } from './utils/arrays';
|
||||
import { CommandManager } from './utils/commandManager';
|
||||
import { Disposable } from './utils/dispose';
|
||||
import * as errorCodes from './utils/errorCodes';
|
||||
import { DiagnosticLanguage, LanguageDescription } from './utils/languageDescription';
|
||||
import { ILogDirectoryProvider } from './tsServer/logDirectoryProvider';
|
||||
import { PluginManager } from './utils/plugins';
|
||||
import * as typeConverters from './utils/typeConverters';
|
||||
import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus';
|
||||
import { ITypeScriptVersionProvider } from './utils/versionProvider';
|
||||
import VersionStatus from './utils/versionStatus';
|
||||
|
||||
namespace Experimental {
|
||||
@@ -59,10 +61,13 @@ export default class TypeScriptServiceClientHost extends Disposable {
|
||||
constructor(
|
||||
descriptions: LanguageDescription[],
|
||||
workspaceState: vscode.Memento,
|
||||
onCaseInsenitiveFileSystem: boolean,
|
||||
pluginManager: PluginManager,
|
||||
private readonly commandManager: CommandManager,
|
||||
logDirectoryProvider: ILogDirectoryProvider,
|
||||
cancellerFactory: OngoingRequestCancellerFactory,
|
||||
versionProvider: ITypeScriptVersionProvider,
|
||||
processFactory: TsServerProcessFactory,
|
||||
onCompletionAccepted: (item: vscode.CompletionItem) => void,
|
||||
) {
|
||||
super();
|
||||
@@ -70,9 +75,12 @@ export default class TypeScriptServiceClientHost extends Disposable {
|
||||
const allModeIds = this.getAllModeIds(descriptions, pluginManager);
|
||||
this.client = this._register(new TypeScriptServiceClient(
|
||||
workspaceState,
|
||||
onCaseInsenitiveFileSystem,
|
||||
pluginManager,
|
||||
logDirectoryProvider,
|
||||
cancellerFactory,
|
||||
versionProvider,
|
||||
processFactory,
|
||||
allModeIds));
|
||||
|
||||
this.client.onDiagnosticsReceived(({ kind, resource, diagnostics }) => {
|
||||
@@ -85,7 +93,7 @@ export default class TypeScriptServiceClientHost extends Disposable {
|
||||
this._register(new VersionStatus(this.client, commandManager));
|
||||
this._register(new AtaProgressReporter(this.client));
|
||||
this.typingsStatus = this._register(new TypingsStatus(this.client));
|
||||
this.fileConfigurationManager = this._register(new FileConfigurationManager(this.client));
|
||||
this.fileConfigurationManager = this._register(new FileConfigurationManager(this.client, onCaseInsenitiveFileSystem));
|
||||
|
||||
for (const description of descriptions) {
|
||||
const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager, onCompletionAccepted);
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { OngoingRequestCancellerFactory } from './tsServer/cancellation';
|
||||
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 { OngoingRequestCancellerFactory } from './tsServer/cancellation';
|
||||
import { ILogDirectoryProvider } from './tsServer/logDirectoryProvider';
|
||||
import { ITypeScriptServer, TsServerProcessFactory } from './tsServer/server';
|
||||
import { TypeScriptServerError } from './tsServer/serverError';
|
||||
import { TypeScriptServerSpawner } from './tsServer/spawner';
|
||||
import { ClientCapabilities, ClientCapability, ExecConfig, ITypeScriptServiceClient, ServerResponse, TypeScriptRequests } from './typescriptService';
|
||||
@@ -20,16 +20,15 @@ import API from './utils/api';
|
||||
import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration';
|
||||
import { Disposable } from './utils/dispose';
|
||||
import * as fileSchemes from './utils/fileSchemes';
|
||||
import { onCaseInsenitiveFileSystem } from './utils/fileSystem';
|
||||
import { ILogDirectoryProvider } from './tsServer/logDirectoryProvider';
|
||||
import Logger from './utils/logger';
|
||||
import { isWeb } from './utils/platform';
|
||||
import { TypeScriptPluginPathsProvider } from './utils/pluginPathsProvider';
|
||||
import { PluginManager } from './utils/plugins';
|
||||
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 { ITypeScriptVersionProvider, TypeScriptVersion } from './utils/versionProvider';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -100,7 +99,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
|
||||
private _onReady?: { promise: Promise<void>; resolve: () => void; reject: () => void; };
|
||||
private _configuration: TypeScriptServiceConfiguration;
|
||||
private versionProvider: TypeScriptVersionProvider;
|
||||
private pluginPathsProvider: TypeScriptPluginPathsProvider;
|
||||
private readonly _versionManager: TypeScriptVersionManager;
|
||||
|
||||
@@ -123,9 +121,12 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
|
||||
constructor(
|
||||
private readonly workspaceState: vscode.Memento,
|
||||
onCaseInsenitiveFileSystem: boolean,
|
||||
public readonly pluginManager: PluginManager,
|
||||
private readonly logDirectoryProvider: ILogDirectoryProvider,
|
||||
private readonly cancellerFactory: OngoingRequestCancellerFactory,
|
||||
private readonly versionProvider: ITypeScriptVersionProvider,
|
||||
private readonly processFactory: TsServerProcessFactory,
|
||||
allModeIds: readonly string[]
|
||||
) {
|
||||
super();
|
||||
@@ -143,17 +144,18 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
this.numberRestarts = 0;
|
||||
|
||||
this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace();
|
||||
this.versionProvider = new TypeScriptVersionProvider(this._configuration);
|
||||
this.versionProvider.updateConfiguration(this._configuration);
|
||||
|
||||
this.pluginPathsProvider = new TypeScriptPluginPathsProvider(this._configuration);
|
||||
this._versionManager = this._register(new TypeScriptVersionManager(this._configuration, this.versionProvider, this.workspaceState));
|
||||
this._register(this._versionManager.onDidPickNewVersion(() => {
|
||||
this.restartTsServer();
|
||||
}));
|
||||
|
||||
this.bufferSyncSupport = new BufferSyncSupport(this, allModeIds, onCaseInsenitiveFileSystem());
|
||||
this.bufferSyncSupport = new BufferSyncSupport(this, allModeIds, onCaseInsenitiveFileSystem);
|
||||
this.onReady(() => { this.bufferSyncSupport.listen(); });
|
||||
|
||||
this.diagnosticsManager = new DiagnosticsManager('typescript', onCaseInsenitiveFileSystem());
|
||||
this.diagnosticsManager = new DiagnosticsManager('typescript', onCaseInsenitiveFileSystem);
|
||||
this.bufferSyncSupport.onDelete(resource => {
|
||||
this.cancelInflightRequestsForResource(resource);
|
||||
this.diagnosticsManager.delete(resource);
|
||||
@@ -194,7 +196,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
return this.apiVersion.fullVersionString;
|
||||
}));
|
||||
|
||||
this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer);
|
||||
this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this._versionManager, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer, this.processFactory);
|
||||
|
||||
this._register(this.pluginManager.onDidUpdateConfig(update => {
|
||||
this.configurePlugin(update.pluginId, update.config);
|
||||
@@ -206,12 +208,19 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
}
|
||||
|
||||
public get capabilities() {
|
||||
if (isWeb()) {
|
||||
return new ClientCapabilities(
|
||||
ClientCapability.Syntax,
|
||||
ClientCapability.EnhancedSyntax);
|
||||
}
|
||||
|
||||
if (this.apiVersion.gte(API.v400)) {
|
||||
return new ClientCapabilities(
|
||||
ClientCapability.Syntax,
|
||||
ClientCapability.EnhancedSyntax,
|
||||
ClientCapability.Semantic);
|
||||
}
|
||||
|
||||
return new ClientCapabilities(
|
||||
ClientCapability.Syntax,
|
||||
ClientCapability.Semantic);
|
||||
@@ -345,12 +354,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
let version = this._versionManager.currentVersion;
|
||||
|
||||
this.info(`Using tsserver from: ${version.path}`);
|
||||
if (!fs.existsSync(version.tsServerPath)) {
|
||||
vscode.window.showWarningMessage(localize('noServerFound', 'The path {0} doesn\'t point to a valid tsserver install. Falling back to bundled TypeScript version.', version.path));
|
||||
|
||||
this._versionManager.reset();
|
||||
version = this._versionManager.currentVersion;
|
||||
}
|
||||
|
||||
const apiVersion = version.apiVersion || API.defaultVersion;
|
||||
let mytoken = ++this.token;
|
||||
@@ -433,7 +436,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
|
||||
handle.onEvent(event => this.dispatchEvent(event));
|
||||
|
||||
if (apiVersion.gte(API.v300)) {
|
||||
if (apiVersion.gte(API.v300) && this.capabilities.has(ClientCapability.Semantic)) {
|
||||
this.loadingIndicator.startedLoadingProject(undefined /* projectName */);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ export interface ForkOptions {
|
||||
|
||||
export function fork(
|
||||
modulePath: string,
|
||||
args: string[],
|
||||
args: readonly string[],
|
||||
options: ForkOptions,
|
||||
): cp.ChildProcess {
|
||||
const newEnv = generatePatchedEnv(process.env, modulePath);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export function isWeb(): boolean {
|
||||
return typeof process !== 'undefined';
|
||||
}
|
||||
@@ -3,13 +3,20 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChildProcess } from 'child_process';
|
||||
import * as stream from 'stream';
|
||||
import type { ChildProcess } from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import type { Readable } from 'stream';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import type * as Proto from '../protocol';
|
||||
import { TsServerProcess } from '../tsServer/server';
|
||||
import { TsServerProcess, TsServerProcessKind } from '../tsServer/server';
|
||||
import { TypeScriptServiceConfiguration } from '../utils/configuration';
|
||||
import { fork } from '../utils/electron';
|
||||
import { TypeScriptVersionManager } from '../utils/versionManager';
|
||||
import { Disposable } from './dispose';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
const defaultSize: number = 8192;
|
||||
const contentLength: string = 'Content-Length: ';
|
||||
const contentLengthSize: number = Buffer.byteLength(contentLength, 'utf8');
|
||||
@@ -88,7 +95,7 @@ class Reader<T> extends Disposable {
|
||||
private readonly buffer: ProtocolBuffer = new ProtocolBuffer();
|
||||
private nextMessageLength: number = -1;
|
||||
|
||||
public constructor(readable: stream.Readable) {
|
||||
public constructor(readable: Readable) {
|
||||
super();
|
||||
readable.on('data', data => this.onLengthData(data));
|
||||
}
|
||||
@@ -130,7 +137,50 @@ class Reader<T> extends Disposable {
|
||||
export class ChildServerProcess extends Disposable implements TsServerProcess {
|
||||
private readonly _reader: Reader<Proto.Response>;
|
||||
|
||||
public constructor(
|
||||
public static fork(
|
||||
tsServerPath: string,
|
||||
args: readonly string[],
|
||||
kind: TsServerProcessKind,
|
||||
configuration: TypeScriptServiceConfiguration,
|
||||
versionManager: TypeScriptVersionManager,
|
||||
): ChildServerProcess {
|
||||
if (!fs.existsSync(tsServerPath)) {
|
||||
vscode.window.showWarningMessage(localize('noServerFound', 'The path {0} doesn\'t point to a valid tsserver install. Falling back to bundled TypeScript version.', tsServerPath));
|
||||
versionManager.reset();
|
||||
tsServerPath = versionManager.currentVersion.tsServerPath;
|
||||
}
|
||||
const childProcess = fork(tsServerPath, args, this.getForkOptions(kind, configuration));
|
||||
return new ChildServerProcess(childProcess);
|
||||
}
|
||||
|
||||
private static getForkOptions(kind: TsServerProcessKind, configuration: TypeScriptServiceConfiguration) {
|
||||
const debugPort = this.getDebugPort(kind);
|
||||
const inspectFlag = process.env['TSS_DEBUG_BRK'] ? '--inspect-brk' : '--inspect';
|
||||
const tsServerForkOptions: any = {
|
||||
execArgv: [
|
||||
...(debugPort ? [`${inspectFlag}=${debugPort}`] : []),
|
||||
...(configuration.maxTsServerMemory ? [`--max-old-space-size=${configuration.maxTsServerMemory}`] : [])
|
||||
]
|
||||
};
|
||||
return tsServerForkOptions;
|
||||
}
|
||||
|
||||
private static getDebugPort(kind: TsServerProcessKind): number | undefined {
|
||||
if (kind === TsServerProcessKind.Syntax) {
|
||||
// We typically only want to debug the main semantic server
|
||||
return undefined;
|
||||
}
|
||||
const value = process.env['TSS_DEBUG_BRK'] || process.env['TSS_DEBUG'];
|
||||
if (value) {
|
||||
const port = parseInt(value);
|
||||
if (!isNaN(port)) {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private constructor(
|
||||
private readonly _process: ChildProcess,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
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';
|
||||
import { Disposable } from './dispose';
|
||||
import { ITypeScriptVersionProvider, TypeScriptVersion } from './versionProvider';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -24,7 +24,7 @@ export class TypeScriptVersionManager extends Disposable {
|
||||
|
||||
public constructor(
|
||||
private configuration: TypeScriptServiceConfiguration,
|
||||
private readonly versionProvider: TypeScriptVersionProvider,
|
||||
private readonly versionProvider: ITypeScriptVersionProvider,
|
||||
private readonly workspaceState: vscode.Memento
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import API from '../utils/api';
|
||||
import { TypeScriptServiceConfiguration } from './configuration';
|
||||
import { RelativeWorkspacePathResolver } from './relativePathResolver';
|
||||
import { ITypeScriptVersionProvider, localize, TypeScriptVersion, TypeScriptVersionSource } from './versionProvider';
|
||||
|
||||
export class DiskTypeScriptVersionProvider implements ITypeScriptVersionProvider {
|
||||
|
||||
public constructor(
|
||||
private configuration?: TypeScriptServiceConfiguration
|
||||
) { }
|
||||
|
||||
|
||||
public updateConfiguration(configuration: TypeScriptServiceConfiguration): void {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
public get defaultVersion(): TypeScriptVersion {
|
||||
return this.globalVersion || this.bundledVersion;
|
||||
}
|
||||
|
||||
public get globalVersion(): TypeScriptVersion | undefined {
|
||||
if (this.configuration?.globalTsdk) {
|
||||
const globals = this.loadVersionsFromSetting(TypeScriptVersionSource.UserSetting, this.configuration.globalTsdk);
|
||||
if (globals && globals.length) {
|
||||
return globals[0];
|
||||
}
|
||||
}
|
||||
return this.contributedTsNextVersion;
|
||||
}
|
||||
|
||||
public get localVersion(): TypeScriptVersion | undefined {
|
||||
const tsdkVersions = this.localTsdkVersions;
|
||||
if (tsdkVersions && tsdkVersions.length) {
|
||||
return tsdkVersions[0];
|
||||
}
|
||||
|
||||
const nodeVersions = this.localNodeModulesVersions;
|
||||
if (nodeVersions && nodeVersions.length === 1) {
|
||||
return nodeVersions[0];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
public get localVersions(): TypeScriptVersion[] {
|
||||
const allVersions = this.localTsdkVersions.concat(this.localNodeModulesVersions);
|
||||
const paths = new Set<string>();
|
||||
return allVersions.filter(x => {
|
||||
if (paths.has(x.path)) {
|
||||
return false;
|
||||
}
|
||||
paths.add(x.path);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public get bundledVersion(): TypeScriptVersion {
|
||||
const version = this.getContributedVersion(TypeScriptVersionSource.Bundled, 'vscode.typescript-language-features', ['..', 'node_modules']);
|
||||
if (version) {
|
||||
return version;
|
||||
}
|
||||
|
||||
vscode.window.showErrorMessage(localize(
|
||||
'noBundledServerFound',
|
||||
'VS Code\'s tsserver was deleted by another application such as a misbehaving virus detection tool. Please reinstall VS Code.'));
|
||||
throw new Error('Could not find bundled tsserver.js');
|
||||
}
|
||||
|
||||
private get contributedTsNextVersion(): TypeScriptVersion | undefined {
|
||||
return this.getContributedVersion(TypeScriptVersionSource.TsNightlyExtension, 'ms-vscode.vscode-typescript-next', ['node_modules']);
|
||||
}
|
||||
|
||||
private getContributedVersion(source: TypeScriptVersionSource, extensionId: string, pathToTs: readonly string[]): TypeScriptVersion | undefined {
|
||||
try {
|
||||
const extension = vscode.extensions.getExtension(extensionId);
|
||||
if (extension) {
|
||||
const typescriptPath = path.join(extension.extensionPath, ...pathToTs, 'typescript', 'lib');
|
||||
const bundledVersion = new TypeScriptVersion(source, path.join(typescriptPath, 'tsserver.js'), DiskTypeScriptVersionProvider.getApiVersion(typescriptPath), '');
|
||||
if (bundledVersion.isValid) {
|
||||
return bundledVersion;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private get localTsdkVersions(): TypeScriptVersion[] {
|
||||
const localTsdk = this.configuration?.localTsdk;
|
||||
return localTsdk ? this.loadVersionsFromSetting(TypeScriptVersionSource.WorkspaceSetting, localTsdk) : [];
|
||||
}
|
||||
|
||||
private loadVersionsFromSetting(source: TypeScriptVersionSource, tsdkPathSetting: string): TypeScriptVersion[] {
|
||||
if (path.isAbsolute(tsdkPathSetting)) {
|
||||
return [
|
||||
new TypeScriptVersion(source,
|
||||
path.join(tsdkPathSetting, 'tsserver.js'),
|
||||
DiskTypeScriptVersionProvider.getApiVersion(tsdkPathSetting))
|
||||
];
|
||||
}
|
||||
|
||||
const workspacePath = RelativeWorkspacePathResolver.asAbsoluteWorkspacePath(tsdkPathSetting);
|
||||
if (workspacePath !== undefined) {
|
||||
return [
|
||||
new TypeScriptVersion(source,
|
||||
path.join(workspacePath, 'tsserver.js'),
|
||||
DiskTypeScriptVersionProvider.getApiVersion(tsdkPathSetting),
|
||||
tsdkPathSetting)
|
||||
];
|
||||
}
|
||||
|
||||
return this.loadTypeScriptVersionsFromPath(source, tsdkPathSetting);
|
||||
}
|
||||
|
||||
private get localNodeModulesVersions(): TypeScriptVersion[] {
|
||||
return this.loadTypeScriptVersionsFromPath(TypeScriptVersionSource.NodeModules, path.join('node_modules', 'typescript', 'lib'))
|
||||
.filter(x => x.isValid);
|
||||
}
|
||||
|
||||
private loadTypeScriptVersionsFromPath(source: TypeScriptVersionSource, relativePath: string): TypeScriptVersion[] {
|
||||
if (!vscode.workspace.workspaceFolders) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const versions: TypeScriptVersion[] = [];
|
||||
for (const root of vscode.workspace.workspaceFolders) {
|
||||
let label: string = relativePath;
|
||||
if (vscode.workspace.workspaceFolders.length > 1) {
|
||||
label = path.join(root.name, relativePath);
|
||||
}
|
||||
|
||||
const serverPath = path.join(root.uri.fsPath, relativePath);
|
||||
versions.push(new TypeScriptVersion(source, path.join(serverPath, 'tsserver.js'), DiskTypeScriptVersionProvider.getApiVersion(serverPath), label));
|
||||
}
|
||||
return versions;
|
||||
}
|
||||
|
||||
private static getApiVersion(serverPath: string): API | undefined {
|
||||
const version = DiskTypeScriptVersionProvider.getTypeScriptVersion(serverPath);
|
||||
if (version) {
|
||||
return version;
|
||||
}
|
||||
|
||||
// Allow TS developers to provide custom version
|
||||
const tsdkVersion = vscode.workspace.getConfiguration().get<string | undefined>('typescript.tsdk_version', undefined);
|
||||
if (tsdkVersion) {
|
||||
return API.fromVersionString(tsdkVersion);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private static getTypeScriptVersion(serverPath: string): API | undefined {
|
||||
if (!fs.existsSync(serverPath)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const p = serverPath.split(path.sep);
|
||||
if (p.length <= 2) {
|
||||
return undefined;
|
||||
}
|
||||
const p2 = p.slice(0, -2);
|
||||
const modulePath = p2.join(path.sep);
|
||||
let fileName = path.join(modulePath, 'package.json');
|
||||
if (!fs.existsSync(fileName)) {
|
||||
// Special case for ts dev versions
|
||||
if (path.basename(modulePath) === 'built') {
|
||||
fileName = path.join(modulePath, '..', 'package.json');
|
||||
}
|
||||
}
|
||||
if (!fs.existsSync(fileName)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const contents = fs.readFileSync(fileName).toString();
|
||||
let desc: any = null;
|
||||
try {
|
||||
desc = JSON.parse(contents);
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
if (!desc || !desc.version) {
|
||||
return undefined;
|
||||
}
|
||||
return desc.version ? API.fromVersionString(desc.version) : undefined;
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,14 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import API from './api';
|
||||
import { TypeScriptServiceConfiguration } from './configuration';
|
||||
import { RelativeWorkspacePathResolver } from './relativePathResolver';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
export const localize = nls.loadMessageBundle();
|
||||
|
||||
const enum TypeScriptVersionSource {
|
||||
export const enum TypeScriptVersionSource {
|
||||
Bundled = 'bundled',
|
||||
TsNightlyExtension = 'ts-nightly-extension',
|
||||
NodeModules = 'node-modules',
|
||||
@@ -22,18 +19,15 @@ const enum TypeScriptVersionSource {
|
||||
|
||||
export class TypeScriptVersion {
|
||||
|
||||
public readonly apiVersion: API | undefined;
|
||||
|
||||
constructor(
|
||||
public readonly source: TypeScriptVersionSource,
|
||||
public readonly path: string,
|
||||
private readonly _pathLabel?: string
|
||||
) {
|
||||
this.apiVersion = TypeScriptVersion.getApiVersion(this.tsServerPath);
|
||||
}
|
||||
public readonly apiVersion: API | undefined,
|
||||
private readonly _pathLabel?: string,
|
||||
) { }
|
||||
|
||||
public get tsServerPath(): string {
|
||||
return path.join(this.path, 'tsserver.js');
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public get pathLabel(): string {
|
||||
@@ -63,176 +57,14 @@ export class TypeScriptVersion {
|
||||
return version ? version.displayName : localize(
|
||||
'couldNotLoadTsVersion', 'Could not load the TypeScript version at this path');
|
||||
}
|
||||
|
||||
public static getApiVersion(serverPath: string): API | undefined {
|
||||
const version = TypeScriptVersion.getTypeScriptVersion(serverPath);
|
||||
if (version) {
|
||||
return version;
|
||||
}
|
||||
|
||||
// Allow TS developers to provide custom version
|
||||
const tsdkVersion = vscode.workspace.getConfiguration().get<string | undefined>('typescript.tsdk_version', undefined);
|
||||
if (tsdkVersion) {
|
||||
return API.fromVersionString(tsdkVersion);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private static getTypeScriptVersion(serverPath: string): API | undefined {
|
||||
if (!fs.existsSync(serverPath)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const p = serverPath.split(path.sep);
|
||||
if (p.length <= 2) {
|
||||
return undefined;
|
||||
}
|
||||
const p2 = p.slice(0, -2);
|
||||
const modulePath = p2.join(path.sep);
|
||||
let fileName = path.join(modulePath, 'package.json');
|
||||
if (!fs.existsSync(fileName)) {
|
||||
// Special case for ts dev versions
|
||||
if (path.basename(modulePath) === 'built') {
|
||||
fileName = path.join(modulePath, '..', 'package.json');
|
||||
}
|
||||
}
|
||||
if (!fs.existsSync(fileName)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const contents = fs.readFileSync(fileName).toString();
|
||||
let desc: any = null;
|
||||
try {
|
||||
desc = JSON.parse(contents);
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
if (!desc || !desc.version) {
|
||||
return undefined;
|
||||
}
|
||||
return desc.version ? API.fromVersionString(desc.version) : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class TypeScriptVersionProvider {
|
||||
export interface ITypeScriptVersionProvider {
|
||||
updateConfiguration(configuration: TypeScriptServiceConfiguration): void;
|
||||
|
||||
public constructor(
|
||||
private configuration: TypeScriptServiceConfiguration
|
||||
) { }
|
||||
|
||||
public updateConfiguration(configuration: TypeScriptServiceConfiguration): void {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
public get defaultVersion(): TypeScriptVersion {
|
||||
return this.globalVersion || this.bundledVersion;
|
||||
}
|
||||
|
||||
public get globalVersion(): TypeScriptVersion | undefined {
|
||||
if (this.configuration.globalTsdk) {
|
||||
const globals = this.loadVersionsFromSetting(TypeScriptVersionSource.UserSetting, this.configuration.globalTsdk);
|
||||
if (globals && globals.length) {
|
||||
return globals[0];
|
||||
}
|
||||
}
|
||||
return this.contributedTsNextVersion;
|
||||
}
|
||||
|
||||
public get localVersion(): TypeScriptVersion | undefined {
|
||||
const tsdkVersions = this.localTsdkVersions;
|
||||
if (tsdkVersions && tsdkVersions.length) {
|
||||
return tsdkVersions[0];
|
||||
}
|
||||
|
||||
const nodeVersions = this.localNodeModulesVersions;
|
||||
if (nodeVersions && nodeVersions.length === 1) {
|
||||
return nodeVersions[0];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public get localVersions(): TypeScriptVersion[] {
|
||||
const allVersions = this.localTsdkVersions.concat(this.localNodeModulesVersions);
|
||||
const paths = new Set<string>();
|
||||
return allVersions.filter(x => {
|
||||
if (paths.has(x.path)) {
|
||||
return false;
|
||||
}
|
||||
paths.add(x.path);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public get bundledVersion(): TypeScriptVersion {
|
||||
const version = this.getContributedVersion(TypeScriptVersionSource.Bundled, 'vscode.typescript-language-features', ['..', 'node_modules']);
|
||||
if (version) {
|
||||
return version;
|
||||
}
|
||||
|
||||
vscode.window.showErrorMessage(localize(
|
||||
'noBundledServerFound',
|
||||
'VS Code\'s tsserver was deleted by another application such as a misbehaving virus detection tool. Please reinstall VS Code.'));
|
||||
throw new Error('Could not find bundled tsserver.js');
|
||||
}
|
||||
|
||||
private get contributedTsNextVersion(): TypeScriptVersion | undefined {
|
||||
return this.getContributedVersion(TypeScriptVersionSource.TsNightlyExtension, 'ms-vscode.vscode-typescript-next', ['node_modules']);
|
||||
}
|
||||
|
||||
private getContributedVersion(source: TypeScriptVersionSource, extensionId: string, pathToTs: readonly string[]): TypeScriptVersion | undefined {
|
||||
try {
|
||||
const extension = vscode.extensions.getExtension(extensionId);
|
||||
if (extension) {
|
||||
const typescriptPath = path.join(extension.extensionPath, ...pathToTs, 'typescript', 'lib');
|
||||
const bundledVersion = new TypeScriptVersion(source, typescriptPath, '');
|
||||
if (bundledVersion.isValid) {
|
||||
return bundledVersion;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private get localTsdkVersions(): TypeScriptVersion[] {
|
||||
const localTsdk = this.configuration.localTsdk;
|
||||
return localTsdk ? this.loadVersionsFromSetting(TypeScriptVersionSource.WorkspaceSetting, localTsdk) : [];
|
||||
}
|
||||
|
||||
private loadVersionsFromSetting(source: TypeScriptVersionSource, tsdkPathSetting: string): TypeScriptVersion[] {
|
||||
if (path.isAbsolute(tsdkPathSetting)) {
|
||||
return [new TypeScriptVersion(source, tsdkPathSetting)];
|
||||
}
|
||||
|
||||
const workspacePath = RelativeWorkspacePathResolver.asAbsoluteWorkspacePath(tsdkPathSetting);
|
||||
if (workspacePath !== undefined) {
|
||||
return [new TypeScriptVersion(source, workspacePath, tsdkPathSetting)];
|
||||
}
|
||||
|
||||
return this.loadTypeScriptVersionsFromPath(source, tsdkPathSetting);
|
||||
}
|
||||
|
||||
private get localNodeModulesVersions(): TypeScriptVersion[] {
|
||||
return this.loadTypeScriptVersionsFromPath(TypeScriptVersionSource.NodeModules, path.join('node_modules', 'typescript', 'lib'))
|
||||
.filter(x => x.isValid);
|
||||
}
|
||||
|
||||
private loadTypeScriptVersionsFromPath(source: TypeScriptVersionSource, relativePath: string): TypeScriptVersion[] {
|
||||
if (!vscode.workspace.workspaceFolders) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const versions: TypeScriptVersion[] = [];
|
||||
for (const root of vscode.workspace.workspaceFolders) {
|
||||
let label: string = relativePath;
|
||||
if (vscode.workspace.workspaceFolders.length > 1) {
|
||||
label = path.join(root.name, relativePath);
|
||||
}
|
||||
|
||||
versions.push(new TypeScriptVersion(source, path.join(root.uri.fsPath, relativePath), label));
|
||||
}
|
||||
return versions;
|
||||
}
|
||||
readonly defaultVersion: TypeScriptVersion;
|
||||
readonly globalVersion: TypeScriptVersion | undefined;
|
||||
readonly localVersion: TypeScriptVersion | undefined;
|
||||
readonly localVersions: readonly TypeScriptVersion[];
|
||||
readonly bundledVersion: TypeScriptVersion;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user