This commit is contained in:
Matt Bierner 2021-11-26 14:33:23 +00:00 committed by GitHub
commit 2088d3e120
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 114 additions and 69 deletions

View file

@ -12,7 +12,7 @@ import { LanguageConfigurationManager } from './languageFeatures/languageConfigu
import { createLazyClientHost, lazilyActivateClient } from './lazyClientHost';
import { nodeRequestCancellerFactory } from './tsServer/cancellation.electron';
import { NodeLogDirectoryProvider } from './tsServer/logDirectoryProvider.electron';
import { ChildServerProcess } from './tsServer/serverProcess.electron';
import { ElectronServiceProcessFactory } from './tsServer/serverProcess.electron';
import { DiskTypeScriptVersionProvider } from './tsServer/versionProvider.electron';
import { ActiveJsTsEditorTracker } from './utils/activeJsTsEditorTracker';
import { ElectronServiceConfigurationProvider } from './utils/configuration.electron';
@ -46,7 +46,7 @@ export function activate(
logDirectoryProvider,
cancellerFactory: nodeRequestCancellerFactory,
versionProvider,
processFactory: ChildServerProcess,
processFactory: new ElectronServiceProcessFactory(),
activeJsTsEditorTracker,
serviceConfigurationProvider: new ElectronServiceConfigurationProvider(),
}, item => {

View file

@ -59,7 +59,7 @@ export const enum TsServerProcessKind {
export interface TsServerProcessFactory {
fork(
tsServerPath: string,
version: TypeScriptVersion,
args: readonly string[],
kind: TsServerProcessKind,
configuration: TypeScriptServiceConfiguration,

View file

@ -9,6 +9,7 @@ import type * as Proto from '../protocol';
import { TypeScriptServiceConfiguration } from '../utils/configuration';
import { memoize } from '../utils/memoize';
import { TsServerProcess, TsServerProcessKind } from './server';
import { TypeScriptVersion } from './versionProvider';
const localize = nls.loadMessageBundle();
@ -19,11 +20,12 @@ declare type Worker = any;
export class WorkerServerProcess implements TsServerProcess {
public static fork(
tsServerPath: string,
version: TypeScriptVersion,
args: readonly string[],
_kind: TsServerProcessKind,
_configuration: TypeScriptServiceConfiguration,
) {
const tsServerPath = version.tsServerPath;
const worker = new Worker(tsServerPath);
return new WorkerServerProcess(worker, [
...args,

View file

@ -10,10 +10,12 @@ import type { Readable } from 'stream';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import type * as Proto from '../protocol';
import API from '../utils/api';
import { TypeScriptServiceConfiguration } from '../utils/configuration';
import { Disposable } from '../utils/dispose';
import { TsServerProcess, TsServerProcessKind } from './server';
import { TsServerProcess, TsServerProcessFactory, TsServerProcessKind } from './server';
import { TypeScriptVersionManager } from './versionManager';
import { TypeScriptVersion } from './versionProvider';
const localize = nls.loadMessageBundle();
@ -134,84 +136,89 @@ class Reader<T> extends Disposable {
}
}
export class ChildServerProcess extends Disposable implements TsServerProcess {
private readonly _reader: Reader<Proto.Response>;
function generatePatchedEnv(env: any, modulePath: string): any {
const newEnv = Object.assign({}, env);
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;
}
newEnv['ELECTRON_RUN_AS_NODE'] = '1';
newEnv['NODE_PATH'] = path.join(modulePath, '..', '..', '..');
const childProcess = child_process.fork(tsServerPath, args, {
silent: true,
cwd: undefined,
env: this.generatePatchedEnv(process.env, tsServerPath),
execArgv: this.getExecArgv(kind, configuration),
});
// Ensure we always have a PATH set
newEnv['PATH'] = newEnv['PATH'] || process.env.PATH;
return new ChildServerProcess(childProcess);
return newEnv;
}
function getExecArgv(kind: TsServerProcessKind, configuration: TypeScriptServiceConfiguration): string[] {
const args: string[] = [];
const debugPort = getDebugPort(kind);
if (debugPort) {
const inspectFlag = getTssDebugBrk() ? '--inspect-brk' : '--inspect';
args.push(`${inspectFlag}=${debugPort}`);
}
private static generatePatchedEnv(env: any, modulePath: string): any {
const newEnv = Object.assign({}, env);
newEnv['ELECTRON_RUN_AS_NODE'] = '1';
newEnv['NODE_PATH'] = path.join(modulePath, '..', '..', '..');
// Ensure we always have a PATH set
newEnv['PATH'] = newEnv['PATH'] || process.env.PATH;
return newEnv;
if (configuration.maxTsServerMemory) {
args.push(`--max-old-space-size=${configuration.maxTsServerMemory}`);
}
private static getExecArgv(kind: TsServerProcessKind, configuration: TypeScriptServiceConfiguration): string[] {
const args: string[] = [];
return args;
}
const debugPort = this.getDebugPort(kind);
if (debugPort) {
const inspectFlag = ChildServerProcess.getTssDebugBrk() ? '--inspect-brk' : '--inspect';
args.push(`${inspectFlag}=${debugPort}`);
}
if (configuration.maxTsServerMemory) {
args.push(`--max-old-space-size=${configuration.maxTsServerMemory}`);
}
return args;
}
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 = ChildServerProcess.getTssDebugBrk() || ChildServerProcess.getTssDebug();
if (value) {
const port = parseInt(value);
if (!isNaN(port)) {
return port;
}
}
function getDebugPort(kind: TsServerProcessKind): number | undefined {
if (kind === TsServerProcessKind.Syntax) {
// We typically only want to debug the main semantic server
return undefined;
}
const value = getTssDebugBrk() || getTssDebug();
if (value) {
const port = parseInt(value);
if (!isNaN(port)) {
return port;
}
}
return undefined;
}
private static getTssDebug(): string | undefined {
return process.env[vscode.env.remoteName ? 'TSS_REMOTE_DEBUG' : 'TSS_DEBUG'];
function getTssDebug(): string | undefined {
return process.env[vscode.env.remoteName ? 'TSS_REMOTE_DEBUG' : 'TSS_DEBUG'];
}
function getTssDebugBrk(): string | undefined {
return process.env[vscode.env.remoteName ? 'TSS_REMOTE_DEBUG_BRK' : 'TSS_DEBUG_BRK'];
}
class IpcChildServerProcess extends Disposable implements TsServerProcess {
constructor(
private readonly _process: child_process.ChildProcess,
) {
super();
}
private static getTssDebugBrk(): string | undefined {
return process.env[vscode.env.remoteName ? 'TSS_REMOTE_DEBUG_BRK' : 'TSS_DEBUG_BRK'];
write(serverRequest: Proto.Request): void {
this._process.send(serverRequest);
}
private constructor(
onData(handler: (data: Proto.Response) => void): void {
this._process.on('message', handler);
}
onExit(handler: (code: number | null, signal: string | null) => void): void {
this._process.on('exit', handler);
}
onError(handler: (err: Error) => void): void {
this._process.on('error', handler);
}
kill(): void {
this._process.kill();
}
}
class StdioChildServerProcess extends Disposable implements TsServerProcess {
private readonly _reader: Reader<Proto.Response>;
constructor(
private readonly _process: child_process.ChildProcess,
) {
super();
@ -240,3 +247,39 @@ export class ChildServerProcess extends Disposable implements TsServerProcess {
this._reader.dispose();
}
}
export class ElectronServiceProcessFactory implements TsServerProcessFactory {
fork(
version: TypeScriptVersion,
args: readonly string[],
kind: TsServerProcessKind,
configuration: TypeScriptServiceConfiguration,
versionManager: TypeScriptVersionManager,
): TsServerProcess {
let tsServerPath = version.tsServerPath;
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;
}
// TODO: use 4.6+ instead
const useIpc = version.apiVersion?.gte(API.v440);
const runtimeArgs = [...args];
if (useIpc) {
runtimeArgs.push('--useNodeIpc');
}
const childProcess = child_process.fork(tsServerPath, runtimeArgs, {
silent: true,
cwd: undefined,
env: generatePatchedEnv(process.env, tsServerPath),
execArgv: getExecArgv(kind, configuration),
stdio: useIpc ? ['pipe', 'pipe', 'pipe', 'ipc'] : undefined,
});
return useIpc ? new IpcChildServerProcess(childProcess) : new StdioChildServerProcess(childProcess);
}
}

View file

@ -152,7 +152,7 @@ export class TypeScriptServerSpawner {
}
this._logger.info(`<${kind}> Forking...`);
const process = this._factory.fork(version.tsServerPath, args, kind, configuration, this._versionManager);
const process = this._factory.fork(version, args, kind, configuration, this._versionManager);
this._logger.info(`<${kind}> Starting...`);
return new ProcessBasedTsServer(