diff --git a/Readme.md b/Readme.md index baadd25..6c1e422 100644 --- a/Readme.md +++ b/Readme.md @@ -116,7 +116,6 @@ for guide of coc.nvim's configuration. - `tsserver.log`:Log level of tsserver, default: `"off"` - `tsserver.trace.server`:Trace level of tsserver, default: `"off"` - `tsserver.pluginPaths`:Folders contains tsserver plugins, default: `[]` -- `tsserver.debugPort`:Debug port number of tsserver - `tsserver.watchOptions`:Configure which watching strategies should be used to keep track of files and directories. Requires using TypeScript 3.8+ in the workspace, default: `undefined` diff --git a/package.json b/package.json index 1391a7f..8a27220 100644 --- a/package.json +++ b/package.json @@ -238,10 +238,6 @@ }, "description": "Folders contains tsserver plugins" }, - "tsserver.debugPort": { - "type": "number", - "description": "Debug port number of tsserver" - }, "tsserver.reportStyleChecksAsWarnings": { "type": "boolean", "default": true diff --git a/src/server/typescriptServiceClient.ts b/src/server/typescriptServiceClient.ts index 6e99388..e009667 100644 --- a/src/server/typescriptServiceClient.ts +++ b/src/server/typescriptServiceClient.ts @@ -25,22 +25,22 @@ import Tracer from './utils/tracer' import { inferredProjectConfig } from './utils/tsconfig' import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionProvider' import VersionStatus from './utils/versionStatus' -import { ICallback, Reader } from './utils/wireProtocol' +import { Reader } from './utils/wireProtocol' interface ToCancelOnResourceChanged { readonly resource: string cancel(): void } -class ForkedTsServerProcess { - constructor(private childProcess: cp.ChildProcess) {} +class ForkedTsServerProcess implements Disposable { + private readonly _reader: Reader + + constructor(private childProcess: cp.ChildProcess) { + this._reader = new Reader(this.childProcess.stdout) + } public readonly toCancelOnResourceChange = new Set() - public onError(cb: (err: Error) => void): void { - this.childProcess.on('error', cb) - } - public onExit(cb: (err: any) => void): void { this.childProcess.on('exit', cb) } @@ -52,16 +52,22 @@ class ForkedTsServerProcess { ) } - public createReader( - callback: ICallback, - onError: (error: any) => void - ): void { - // tslint:disable-next-line:no-unused-expression - new Reader(this.childProcess.stdout, callback, onError) + public onData(handler: (data: Proto.Response) => void): void { + this._reader.onData(handler) + } + + public onError(handler: (err: Error) => void): void { + this.childProcess.on('error', handler) + this._reader.onError(handler) } public kill(): void { this.childProcess.kill() + this._reader.dispose() + } + + public dispose(): void { + this._reader.dispose() } } @@ -296,19 +302,26 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient this.versionStatus.onDidChangeTypeScriptVersion(currentVersion) this.lastError = null const tsServerForkArgs = await this.getTsServerArgs(currentVersion) - const debugPort = this._configuration.debugPort - const maxTsServerMemory = this._configuration.maxTsServerMemory - const options = { - execArgv: [ - ...(debugPort ? [`--inspect=${debugPort}`] : []), // [`--debug-brk=5859`] - ...(maxTsServerMemory ? [`--max-old-space-size=${maxTsServerMemory}`] : []), - ], - cwd: workspace.root - } + const options = { execArgv: this.getExecArgv() } this.servicePromise = this.startProcess(currentVersion, tsServerForkArgs, options, resendModels) return this.servicePromise } + private getExecArgv(): string[] { + const args: string[] = [] + const debugPort = getDebugPort() + if (debugPort) { + const isBreak = process.env[process.env.remoteName ? 'TSS_REMOTE_DEBUG_BRK' : 'TSS_DEBUG_BRK'] !== undefined + const inspectFlag = isBreak ? '--inspect-brk' : '--inspect' + args.push(`${inspectFlag}=${debugPort}`) + } + const maxTsServerMemory = this._configuration.maxTsServerMemory + if (maxTsServerMemory) { + args.push(`--max-old-space-size=${maxTsServerMemory}`) + } + return args + } + private startProcess(currentVersion: TypeScriptVersion, args: string[], options: IForkOptions, resendModels: boolean): Promise { this.state = ServiceStat.Starting return new Promise((resolve, reject) => { @@ -347,16 +360,11 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient this.info(`TSServer log file: ${this.tsServerLogFile || ''}`) this.serviceExited(!this.isRestarting) this.isRestarting = false + handle.dispose() + }) + handle.onData(msg => { + this.dispatchMessage(msg) }) - - handle.createReader( - msg => { - this.dispatchMessage(msg) - }, - error => { - this.error('ReaderError', error) - } - ) resolve(handle) this.serviceStarted(resendModels) this._onTsServerStarted.fire(currentVersion.version) @@ -1013,3 +1021,15 @@ function getQueueingType( } return lowPriority ? RequestQueueingType.LowPriority : RequestQueueingType.Normal } + +function getDebugPort(): number | undefined { + let debugBrk = process.env[process.env.remoteName ? 'TSS_REMOTE_DEBUG_BRK' : 'TSS_DEBUG_BRK'] + let value = debugBrk || process.env[process.env.remoteName ? 'TSS_REMOTE_DEBUG_BRK' : 'TSS_DEBUG_BRK'] + if (value) { + const port = parseInt(value) + if (!isNaN(port)) { + return port + } + } + return undefined +} diff --git a/src/server/utils/configuration.ts b/src/server/utils/configuration.ts index bac12d9..c320899 100644 --- a/src/server/utils/configuration.ts +++ b/src/server/utils/configuration.ts @@ -108,10 +108,6 @@ export class TypeScriptServiceConfiguration { return this._configuration.get('maxTsServerMemory', 0) } - public get debugPort(): number | null { - return this._configuration.get('debugPort', parseInt(process.env['TSS_DEBUG'], 10)) - } - public get npmLocation(): string | null { let path = this._configuration.get('npm', '') if (path) return workspace.expand(path) diff --git a/src/server/utils/process.ts b/src/server/utils/process.ts index ba2c2bb..c994d17 100644 --- a/src/server/utils/process.ts +++ b/src/server/utils/process.ts @@ -98,16 +98,12 @@ export function fork( ): void { let callbackCalled = false const resolve = (result: cp.ChildProcess) => { - if (callbackCalled) { - return - } + if (callbackCalled) return callbackCalled = true callback(null, result) } const reject = (err: any) => { - if (callbackCalled) { - return - } + if (callbackCalled) return callbackCalled = true callback(err, null) } @@ -123,7 +119,7 @@ export function fork( stdOutPipeName, stdErrPipeName ) - newEnv['NODE_PATH'] = path.join(modulePath, '..', '..', '..') // tslint:disable-line + newEnv['NODE_PATH'] = path.join(modulePath, '..', '..', '..') let childProcess: cp.ChildProcess // Begin listening to stderr pipe @@ -165,6 +161,7 @@ export function fork( const bootstrapperPath = path.resolve(__dirname, '../bin/tsserverForkStart') childProcess = cp.fork(bootstrapperPath, [modulePath].concat(args), { silent: true, + cwd: undefined, env: newEnv, execArgv: options.execArgv }) diff --git a/src/server/utils/wireProtocol.ts b/src/server/utils/wireProtocol.ts index f71b11f..2b24126 100644 --- a/src/server/utils/wireProtocol.ts +++ b/src/server/utils/wireProtocol.ts @@ -2,7 +2,9 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { disposeAll } from 'coc.nvim' import stream from 'stream' +import { Disposable, Emitter } from 'vscode-languageserver-protocol' const DefaultSize = 8192 const ContentLength = 'Content-Length: ' @@ -99,18 +101,30 @@ export interface ICallback { (data: T): void // tslint:disable-line } -export class Reader { +export class Reader implements Disposable { private readonly buffer: ProtocolBuffer = new ProtocolBuffer() private nextMessageLength = -1 + private disposables: Disposable[] = [] - public constructor( - private readonly readable: stream.Readable, - private readonly callback: ICallback, - private readonly onError: (error: any) => void - ) { - this.readable.on('data', (data: Buffer) => { + private readonly _onError = new Emitter() + public readonly onError = this._onError.event + + private readonly _onData = new Emitter() + public readonly onData = this._onData.event + + public constructor(readable: stream.Readable) { + const onData = (data: Buffer) => { this.onLengthData(data) + } + readable.on('data', onData) + + this.disposables.push({ + dispose: () => { + readable.off('data', onData) + } }) + this.disposables.push(this._onError) + this.disposables.push(this._onData) } private onLengthData(data: Buffer): void { @@ -129,10 +143,14 @@ export class Reader { } this.nextMessageLength = -1 const json = JSON.parse(msg) - this.callback(json) + this._onData.fire(json) } } catch (e) { - this.onError(e) + this._onError.fire(e) } } + + public dispose(): void { + disposeAll(this.disposables) + } }