From adb726643a9837bc631778083759f2c3b06613b2 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 14 Jun 2016 17:30:55 -0700 Subject: [PATCH] add cancellation support --- Jakefile.js | 11 ++++++++++- src/server/cancellationToken.ts | 34 +++++++++++++++++++++++++++++++++ src/server/editorServices.ts | 13 ++++++++++--- src/server/server.ts | 20 ++++++++++++++++--- src/server/session.ts | 12 +++++++++--- 5 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 src/server/cancellationToken.ts diff --git a/Jakefile.js b/Jakefile.js index bf20bf29275..2c3f9f3a2fd 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -110,6 +110,12 @@ var serverCoreSources = [ return path.join(serverDirectory, f); }); +var cancellationTokenSources = [ + "cancellationToken.ts" +].map(function (f) { + return path.join(serverDirectory, f); +}); + var serverSources = serverCoreSources.concat(servicesSources); var languageServiceLibrarySources = [ @@ -517,8 +523,11 @@ compileFile(servicesFile, servicesSources,[builtLocalDirectory, copyright].conca }); +var cancellationTokenFile = path.join(builtLocalDirectory, "cancellationToken.js"); +compileFile(cancellationTokenFile, cancellationTokenSources, [builtLocalDirectory].concat(cancellationTokenSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { outDir: builtLocalDirectory, noOutFile: true }); + var serverFile = path.join(builtLocalDirectory, "tsserver.js"); -compileFile(serverFile, serverSources,[builtLocalDirectory, copyright].concat(serverSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true); +compileFile(serverFile, serverSources,[builtLocalDirectory, copyright, cancellationTokenFile].concat(serverSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true); var tsserverLibraryFile = path.join(builtLocalDirectory, "tsserverlibrary.js"); var tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibrary.d.ts"); diff --git a/src/server/cancellationToken.ts b/src/server/cancellationToken.ts new file mode 100644 index 00000000000..ac5adeab6d7 --- /dev/null +++ b/src/server/cancellationToken.ts @@ -0,0 +1,34 @@ +/// + + +// TODO: extract services types +interface HostCancellationToken { + isCancellationRequested(): boolean; +} + +const fs: typeof NodeJS.fs = require("fs"); + +function createCancellationToken(args: string[]): HostCancellationToken { + let cancellationPipeName: string; + for (let i = 0; i < args.length - 1; i++) { + if (args[i] === "--cancellationPipeName") { + cancellationPipeName = args[i + 1]; + break; + } + } + if (!cancellationPipeName) { + return { isCancellationRequested: () => false }; + } + return { + isCancellationRequested() { + try { + fs.statSync(cancellationPipeName); + return true; + } + catch(e) { + return false; + } + } + }; +} +export = createCancellationToken \ No newline at end of file diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index f2e5eabb4e3..87ae34ca239 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -129,7 +129,7 @@ namespace ts.server { private resolvedTypeReferenceDirectives: ts.FileMap>; private getCanonicalFileName: (fileName: string) => string; - constructor(private host: ServerHost, private project: Project) { + constructor(private host: ServerHost, private project: Project, private cancellationToken: HostCancellationToken) { this.getCanonicalFileName = ts.createGetCanonicalFileName(this.host.useCaseSensitiveFileNames); this.resolvedModuleNames = createFileMap>(); this.resolvedTypeReferenceDirectives = createFileMap>(); @@ -188,6 +188,10 @@ namespace ts.server { } } + getCancellationToken() { + return this.cancellationToken; + } + resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] { return this.resolveNamesWithLocalCache(typeDirectiveNames, containingFile, this.resolvedTypeReferenceDirectives, resolveTypeReferenceDirective, m => m.resolvedTypeReferenceDirective); } @@ -316,7 +320,7 @@ namespace ts.server { compilerOptions.allowNonTsExtensions = true; } - this.lsHost = new LSHost(this.projectService.host, this); + this.lsHost = new LSHost(this.projectService.host, this, this.projectService.cancellationToken); this.lsHost.setCompilationSettings(compilerOptions); this.languageService = ts.createLanguageService(this.lsHost, documentRegistry); } @@ -642,7 +646,10 @@ namespace ts.server { private documentRegistry: ts.DocumentRegistry; - constructor(public host: ServerHost, public psLogger: Logger, public eventHandler?: ProjectServiceEventHandler) { + constructor(public host: ServerHost, + public psLogger: Logger, + public cancellationToken: HostCancellationToken, + public eventHandler?: ProjectServiceEventHandler) { // ts.disableIncrementalParsing = true; this.setDefaultHostConfiguration(); this.documentRegistry = ts.createDocumentRegistry(host.useCaseSensitiveFileNames, host.getCurrentDirectory()); diff --git a/src/server/server.ts b/src/server/server.ts index efd01741f86..da1996d341f 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -92,8 +92,8 @@ namespace ts.server { } class IOSession extends Session { - constructor(host: ServerHost, logger: ts.server.Logger) { - super(host, Buffer.byteLength, process.hrtime, logger); + constructor(host: ServerHost, cancellationToken: HostCancellationToken, logger: ts.server.Logger) { + super(host, cancellationToken, Buffer.byteLength, process.hrtime, logger); } exit() { @@ -294,7 +294,21 @@ namespace ts.server { sys.setTimeout = setTimeout; sys.clearTimeout = clearTimeout; - const ioSession = new IOSession(sys, logger); + let cancellationToken: HostCancellationToken; + try { + let cancellationPipeName: string; + if (cancellationPipeName) { + const factory = require("./cancellationToken"); + cancellationToken = factory(sys.args); + } + } + catch(e) { + cancellationToken = { + isCancellationRequested: () => false + } + } + + const ioSession = new IOSession(sys, cancellationToken, logger); process.on("uncaughtException", function(err: Error) { ioSession.logError(err, "unknown"); }); diff --git a/src/server/session.ts b/src/server/session.ts index b5616ae2bf9..86d8dc0734f 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -156,12 +156,12 @@ namespace ts.server { constructor( private host: ServerHost, + private cancellationToken: HostCancellationToken, private byteLength: (buf: string, encoding?: string) => number, private hrtime: (start?: number[]) => number[], - private logger: Logger - ) { + private logger: Logger) { this.projectService = - new ProjectService(host, logger, (eventName, project, fileName) => { + new ProjectService(host, logger, cancellationToken, (eventName, project, fileName) => { this.handleEvent(eventName, project, fileName); }); } @@ -1067,6 +1067,10 @@ namespace ts.server { return { response, responseRequired: true }; } + private canceledResponse() { + return { canceled: true, responseRequired: true }; + } + private handlers: Map<(request: protocol.Request) => { response?: any, responseRequired?: boolean }> = { [CommandNames.OpenExternalProject]: (request: protocol.OpenExternalProjectRequest) => { this.projectService.openExternalProject(request.arguments); @@ -1282,6 +1286,8 @@ namespace ts.server { catch (err) { if (err instanceof OperationCanceledException) { // Handle cancellation exceptions + this.output({ canceled: true }, request.command, request.seq); + return; } this.logError(err, message); this.output(