From ab1512fb1360a33007c891ddc2030b1f507041bc Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 9 Jun 2015 17:00:35 -0700 Subject: [PATCH 1/4] Export server & session without any node specifics --- Jakefile.js | 24 ++++++++++++++++++++ src/harness/harnessLanguageService.ts | 2 +- src/server/editorServices.ts | 29 ++++++++++++------------ src/server/nodeimpl.ts | 7 ++++++ src/server/server.ts | 10 ++++----- src/server/session.ts | 32 +++++++++++++++++++-------- src/server/tsconfig.json | 1 + 7 files changed, 75 insertions(+), 30 deletions(-) create mode 100644 src/server/nodeimpl.ts diff --git a/Jakefile.js b/Jakefile.js index 9b95744d3921e..9a2f39ee1300b 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -100,11 +100,20 @@ var serverSources = [ "editorServices.ts", "protocol.d.ts", "session.ts", + "nodeimpl.ts", "server.ts" ].map(function (f) { return path.join(serverDirectory, f); }); +var languageServiceLibrarySources = [ + "editorServices.ts", + "protocol.d.ts", + "session.ts" +].map(function (f) { + return path.join(serverDirectory, f); +}); + var harnessSources = [ "harness.ts", "sourceMapRecorder.ts", @@ -137,6 +146,7 @@ var harnessSources = [ "protocol.d.ts", "session.ts", "client.ts", + "nodeimpl.ts", "editorServices.ts", ].map(function (f) { return path.join(serverDirectory, f); @@ -369,6 +379,20 @@ compileFile(servicesFile, servicesSources,[builtLocalDirectory, copyright].conca var serverFile = path.join(builtLocalDirectory, "tsserver.js"); compileFile(serverFile, serverSources,[builtLocalDirectory, copyright].concat(serverSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true); +var lsslFile = path.join(builtLocalDirectory, "tslssl.js"); +compileFile( + lsslFile, + languageServiceLibrarySources, + [builtLocalDirectory, copyright].concat(serverSources).concat(languageServiceLibrarySources), + /*prefixes*/ [copyright], + /*useBuiltCompiler*/ true, + /*noOutFile*/ false, + /*generateDeclarations*/ true); + +// Local target to build the language service server library +desc("Builds language service server library"); +task("lssl", [lsslFile]); + // Local target to build the compiler and services desc("Builds the full compiler and services"); task("local", ["generate-diagnostics", "lib", tscFile, servicesFile, nodeDefinitionsFile, serverFile]); diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 0e0b8d829183d..3796c84863b47 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -583,7 +583,7 @@ module Harness.LanguageService { // This host is just a proxy for the clientHost, it uses the client // host to answer server queries about files on disk var serverHost = new SessionServerHost(clientHost); - var server = new ts.server.Session(serverHost, serverHost); + var server = new ts.server.Session(serverHost, new ts.server.NodeEnvironment(), serverHost); // Fake the connection between the client and the server serverHost.writeMessage = client.onMessage.bind(client); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index bc9e685825c08..ffb22cc505b6d 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -2,7 +2,6 @@ /// /// /// -/// module ts.server { export interface Logger { @@ -28,7 +27,7 @@ module ts.server { }); } - class ScriptInfo { + export class ScriptInfo { svc: ScriptVersionCache; children: ScriptInfo[] = []; // files referenced by this file defaultProject: Project; // project to use by default for file @@ -80,7 +79,7 @@ module ts.server { } } - class LSHost implements ts.LanguageServiceHost { + export class LSHost implements ts.LanguageServiceHost { ls: ts.LanguageService = null; compilationSettings: ts.CompilerOptions; filenameToScript: ts.Map = {}; @@ -273,7 +272,7 @@ module ts.server { } } - interface ProjectOptions { + export interface ProjectOptions { // these fields can be present in the project file files?: string[]; compilerOptions?: ts.CompilerOptions; @@ -376,7 +375,7 @@ module ts.server { } } - interface ProjectOpenResult { + export interface ProjectOpenResult { success?: boolean; errorMsg?: string; project?: Project; @@ -392,11 +391,11 @@ module ts.server { return copiedList; } - interface ProjectServiceEventHandler { + export interface ProjectServiceEventHandler { (eventName: string, project: Project, fileName: string): void; } - interface HostConfiguration { + export interface HostConfiguration { formatCodeOptions: ts.FormatCodeOptions; hostInfo: string; } @@ -953,7 +952,7 @@ module ts.server { } - class CompilerService { + export class CompilerService { host: LSHost; languageService: ts.LanguageService; classifier: ts.Classifier; @@ -999,7 +998,7 @@ module ts.server { } - interface LineCollection { + export interface LineCollection { charCount(): number; lineCount(): number; isLeaf(): boolean; @@ -1013,7 +1012,7 @@ module ts.server { leaf?: LineLeaf; } - enum CharRangeSection { + export enum CharRangeSection { PreStart, Start, Entire, @@ -1022,7 +1021,7 @@ module ts.server { PostEnd } - interface ILineIndexWalker { + export interface ILineIndexWalker { goSubtree: boolean; done: boolean; leaf(relativeStart: number, relativeLength: number, lineCollection: LineLeaf): void; @@ -1248,7 +1247,7 @@ module ts.server { } // text change information - class TextChange { + export class TextChange { constructor(public pos: number, public deleteLen: number, public insertedText?: string) { } @@ -1371,7 +1370,7 @@ module ts.server { } } - class LineIndexSnapshot implements ts.IScriptSnapshot { + export class LineIndexSnapshot implements ts.IScriptSnapshot { index: LineIndex; changesSincePreviousVersion: TextChange[] = []; @@ -1605,7 +1604,7 @@ module ts.server { } } - class LineNode implements LineCollection { + export class LineNode implements LineCollection { totalChars = 0; totalLines = 0; children: LineCollection[] = []; @@ -1891,7 +1890,7 @@ module ts.server { } } - class LineLeaf implements LineCollection { + export class LineLeaf implements LineCollection { udata: any; constructor(public text: string) { diff --git a/src/server/nodeimpl.ts b/src/server/nodeimpl.ts new file mode 100644 index 0000000000000..9694b49aa85a7 --- /dev/null +++ b/src/server/nodeimpl.ts @@ -0,0 +1,7 @@ +/// +module ts.server { + export class NodeEnvironment implements Environment { + byteLength = Buffer.byteLength; + hrtime = process.hrtime; + } +} \ No newline at end of file diff --git a/src/server/server.ts b/src/server/server.ts index 828deca2b2d2a..d9737a554d012 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -11,7 +11,7 @@ module ts.server { input: process.stdin, output: process.stdout, terminal: false, - }); + }); class Logger implements ts.server.Logger { fd = -1; @@ -170,11 +170,11 @@ module ts.server { removeFile(file: WatchedFile) { this.watchedFiles = WatchedFileSet.copyListRemovingItem(file, this.watchedFiles); } - } + } class IOSession extends Session { - constructor(host: ServerHost, logger: ts.server.Logger) { - super(host, logger); + constructor(host: ServerHost, env: NodeEnvironment, logger: ts.server.Logger) { + super(host, env, logger); } exit() { @@ -265,7 +265,7 @@ module ts.server { } }; - var ioSession = new IOSession(ts.sys, logger); + var ioSession = new IOSession(ts.sys, new NodeEnvironment(), logger); process.on('uncaughtException', function(err: Error) { ioSession.logError(err, "unknown"); }); diff --git a/src/server/session.ts b/src/server/session.ts index a4cb3e59b4ebc..60ae0b8ee2167 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1,6 +1,5 @@ /// /// -/// /// /// @@ -61,7 +60,7 @@ module ts.server { }; } - interface PendingErrorCheck { + export interface PendingErrorCheck { fileName: string; project: Project; } @@ -108,17 +107,32 @@ module ts.server { export interface ServerHost extends ts.System { } + + export interface Environment { + byteLength: (buf: string, encoding?: string) => number; + hrtime: (start?: number[]) => number[]; //array of seconds, nanoseconds + } + + export interface Message { + type: string, + seq: number + } + + export interface Event extends Message { + event: string; + body?: any; + } export class Session { projectService: ProjectService; pendingOperation = false; fileHash: ts.Map = {}; nextFileId = 1; - errorTimer: NodeJS.Timer; + errorTimer: any; /*NodeJS.Timer | number*/ immediateId: any; changeSeq = 0; - constructor(private host: ServerHost, private logger: Logger) { + constructor(private host: ServerHost, private environment: Environment, private logger: Logger) { this.projectService = new ProjectService(host, logger, (eventName,project,fileName) => { this.handleEvent(eventName, project, fileName); @@ -149,17 +163,17 @@ module ts.server { this.host.write(line + this.host.newLine); } - send(msg: NodeJS._debugger.Message) { + send(msg: Message) { var json = JSON.stringify(msg); if (this.logger.isVerbose()) { this.logger.info(msg.type + ": " + json); } - this.sendLineToClient('Content-Length: ' + (1 + Buffer.byteLength(json, 'utf8')) + + this.sendLineToClient('Content-Length: ' + (1 + this.environment.byteLength(json, 'utf8')) + '\r\n\r\n' + json); } event(info: any, eventName: string) { - var ev: NodeJS._debugger.Event = { + var ev: Event = { seq: 0, type: "event", event: eventName, @@ -838,7 +852,7 @@ module ts.server { onMessage(message: string) { if (this.logger.isVerbose()) { this.logger.info("request: " + message); - var start = process.hrtime(); + var start = this.environment.hrtime(); } try { var request = JSON.parse(message); @@ -980,7 +994,7 @@ module ts.server { } if (this.logger.isVerbose()) { - var elapsed = process.hrtime(start); + var elapsed = this.environment.hrtime(start); var seconds = elapsed[0] var nanoseconds = elapsed[1]; var elapsedMs = ((1e9 * seconds) + nanoseconds)/1000000.0; diff --git a/src/server/tsconfig.json b/src/server/tsconfig.json index 2c8538c61e32a..bdc0a36cb862a 100644 --- a/src/server/tsconfig.json +++ b/src/server/tsconfig.json @@ -12,6 +12,7 @@ "editorServices.ts", "protocol.d.ts", "server.ts", + "nodeimpl.ts", "session.ts" ] } From 85547915743a3bda34117713e75a4c3f390aa1a8 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 10 Jun 2015 16:19:10 -0700 Subject: [PATCH 2/4] Decouple ts.sys from the editorServices where possible --- src/server/editorServices.ts | 12 +++++++----- tests/cases/unittests/versionCache.ts | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index ffb22cc505b6d..d31ea53b5f99e 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -35,7 +35,7 @@ module ts.server { formatCodeOptions = ts.clone(CompilerService.defaultFormatCodeOptions); constructor(private host: ServerHost, public fileName: string, public content: string, public isOpen = false) { - this.svc = ScriptVersionCache.fromString(content); + this.svc = ScriptVersionCache.fromString(host, content); } setFormatOptions(formatOptions: protocol.FormatOptions): void { @@ -915,7 +915,7 @@ module ts.server { return rawConfig.error; } else { - var parsedCommandLine = ts.parseConfigFile(rawConfig.config, ts.sys, dirPath); + var parsedCommandLine = ts.parseConfigFile(rawConfig.config, this.host, dirPath); if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) { return { errorMsg: "tsconfig option errors" }; } @@ -984,7 +984,7 @@ module ts.server { static defaultFormatCodeOptions: ts.FormatCodeOptions = { IndentSize: 4, TabSize: 4, - NewLineCharacter: ts.sys.newLine, + NewLineCharacter: ts.sys ? ts.sys.newLine : '\n', ConvertTabsToSpaces: true, InsertSpaceAfterCommaDelimiter: true, InsertSpaceAfterSemicolonInForStatements: true, @@ -1262,6 +1262,7 @@ module ts.server { versions: LineIndexSnapshot[] = []; minVersion = 0; // no versions earlier than min version will maintain change history private currentVersion = 0; + private host: System; static changeNumberThreshold = 8; static changeLengthThreshold = 256; @@ -1289,7 +1290,7 @@ module ts.server { } reloadFromFile(filename: string, cb?: () => any) { - var content = ts.sys.readFile(filename); + var content = this.host.readFile(filename); this.reload(content); if (cb) cb(); @@ -1359,10 +1360,11 @@ module ts.server { } } - static fromString(script: string) { + static fromString(host: System, script: string) { var svc = new ScriptVersionCache(); var snap = new LineIndexSnapshot(0, svc); svc.versions[svc.currentVersion] = snap; + svc.host = host; snap.index = new LineIndex(); var lm = LineIndex.linesFromText(script); snap.index.load(lm.lines); diff --git a/tests/cases/unittests/versionCache.ts b/tests/cases/unittests/versionCache.ts index 12cf9c1b817b9..06c2cadbfe97e 100644 --- a/tests/cases/unittests/versionCache.ts +++ b/tests/cases/unittests/versionCache.ts @@ -231,7 +231,7 @@ and grew 1cm per day`; }); it("Edit ScriptVersionCache ", () => { - let svc = server.ScriptVersionCache.fromString(testContent); + let svc = server.ScriptVersionCache.fromString(ts.sys, testContent); let checkText = testContent; for (let i = 0; i < iterationCount; i++) { From b8f6ada5d83113a901db54d0b65bd9e8f9edd73c Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 10 Jun 2015 16:48:43 -0700 Subject: [PATCH 3/4] Factor actually handling messages away from parsing them --- src/server/session.ts | 292 +++++++++++++++++++++--------------------- 1 file changed, 149 insertions(+), 143 deletions(-) diff --git a/src/server/session.ts b/src/server/session.ts index 60ae0b8ee2167..a2ad01752dae2 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -848,6 +848,153 @@ module ts.server { exit() { } + + onMessageParsed(request: protocol.Request) : boolean { + var response: any; + var errorMessage: string; + var responseRequired = true; + switch (request.command) { + case CommandNames.Exit: { + this.exit(); + responseRequired = false; + break; + } + case CommandNames.Definition: { + var defArgs = request.arguments; + response = this.getDefinition(defArgs.line, defArgs.offset, defArgs.file); + break; + } + case CommandNames.TypeDefinition: { + var defArgs = request.arguments; + response = this.getTypeDefinition(defArgs.line, defArgs.offset, defArgs.file); + break; + } + case CommandNames.References: { + var refArgs = request.arguments; + response = this.getReferences(refArgs.line, refArgs.offset, refArgs.file); + break; + } + case CommandNames.Rename: { + var renameArgs = request.arguments; + response = this.getRenameLocations(renameArgs.line, renameArgs.offset, renameArgs.file, renameArgs.findInComments, renameArgs.findInStrings); + break; + } + case CommandNames.Open: { + var openArgs = request.arguments; + this.openClientFile(openArgs.file); + responseRequired = false; + break; + } + case CommandNames.Quickinfo: { + var quickinfoArgs = request.arguments; + response = this.getQuickInfo(quickinfoArgs.line, quickinfoArgs.offset, quickinfoArgs.file); + break; + } + case CommandNames.Format: { + var formatArgs = request.arguments; + response = this.getFormattingEditsForRange(formatArgs.line, formatArgs.offset, formatArgs.endLine, formatArgs.endOffset, formatArgs.file); + break; + } + case CommandNames.Formatonkey: { + var formatOnKeyArgs = request.arguments; + response = this.getFormattingEditsAfterKeystroke(formatOnKeyArgs.line, formatOnKeyArgs.offset, formatOnKeyArgs.key, formatOnKeyArgs.file); + break; + } + case CommandNames.Completions: { + var completionsArgs = request.arguments; + response = this.getCompletions(completionsArgs.line, completionsArgs.offset, completionsArgs.prefix, completionsArgs.file); + break; + } + case CommandNames.CompletionDetails: { + var completionDetailsArgs = request.arguments; + response = + this.getCompletionEntryDetails(completionDetailsArgs.line,completionDetailsArgs.offset, + completionDetailsArgs.entryNames,completionDetailsArgs.file); + break; + } + case CommandNames.SignatureHelp: { + var signatureHelpArgs = request.arguments; + response = this.getSignatureHelpItems(signatureHelpArgs.line, signatureHelpArgs.offset, signatureHelpArgs.file); + break; + } + case CommandNames.Geterr: { + var geterrArgs = request.arguments; + response = this.getDiagnostics(geterrArgs.delay, geterrArgs.files); + responseRequired = false; + break; + } + case CommandNames.Change: { + var changeArgs = request.arguments; + this.change(changeArgs.line, changeArgs.offset, changeArgs.endLine, changeArgs.endOffset, + changeArgs.insertString, changeArgs.file); + responseRequired = false; + break; + } + case CommandNames.Configure: { + var configureArgs = request.arguments; + this.projectService.setHostConfiguration(configureArgs); + this.output(undefined, CommandNames.Configure, request.seq); + responseRequired = false; + break; + } + case CommandNames.Reload: { + var reloadArgs = request.arguments; + this.reload(reloadArgs.file, reloadArgs.tmpfile, request.seq); + responseRequired = false; + break; + } + case CommandNames.Saveto: { + var savetoArgs = request.arguments; + this.saveToTmp(savetoArgs.file, savetoArgs.tmpfile); + responseRequired = false; + break; + } + case CommandNames.Close: { + var closeArgs = request.arguments; + this.closeClientFile(closeArgs.file); + responseRequired = false; + break; + } + case CommandNames.Navto: { + var navtoArgs = request.arguments; + response = this.getNavigateToItems(navtoArgs.searchValue, navtoArgs.file, navtoArgs.maxResultCount); + break; + } + case CommandNames.Brace: { + var braceArguments = request.arguments; + response = this.getBraceMatching(braceArguments.line, braceArguments.offset, braceArguments.file); + break; + } + case CommandNames.NavBar: { + var navBarArgs = request.arguments; + response = this.getNavigationBarItems(navBarArgs.file); + break; + } + case CommandNames.Occurrences: { + var { line, offset, file: fileName } = request.arguments; + response = this.getOccurrences(line, offset, fileName); + break; + } + case CommandNames.ProjectInfo: { + var { file, needFileNameList } = request.arguments; + response = this.getProjectInfo(file, needFileNameList); + break; + } + default: { + this.projectService.log("Unrecognized JSON command: " + JSON.stringify(request)); + this.output(undefined, CommandNames.Unknown, request.seq, "Unrecognized JSON command: " + request.command); + break; + } + } + if (response) { + this.output(response, request.command, request.seq); + } + else if (responseRequired) { + this.output(undefined, request.command, request.seq, "No content available."); + } + + return responseRequired; + } onMessage(message: string) { if (this.logger.isVerbose()) { @@ -856,142 +1003,7 @@ module ts.server { } try { var request = JSON.parse(message); - var response: any; - var errorMessage: string; - var responseRequired = true; - switch (request.command) { - case CommandNames.Exit: { - this.exit(); - responseRequired = false; - break; - } - case CommandNames.Definition: { - var defArgs = request.arguments; - response = this.getDefinition(defArgs.line, defArgs.offset, defArgs.file); - break; - } - case CommandNames.TypeDefinition: { - var defArgs = request.arguments; - response = this.getTypeDefinition(defArgs.line, defArgs.offset, defArgs.file); - break; - } - case CommandNames.References: { - var refArgs = request.arguments; - response = this.getReferences(refArgs.line, refArgs.offset, refArgs.file); - break; - } - case CommandNames.Rename: { - var renameArgs = request.arguments; - response = this.getRenameLocations(renameArgs.line, renameArgs.offset, renameArgs.file, renameArgs.findInComments, renameArgs.findInStrings); - break; - } - case CommandNames.Open: { - var openArgs = request.arguments; - this.openClientFile(openArgs.file); - responseRequired = false; - break; - } - case CommandNames.Quickinfo: { - var quickinfoArgs = request.arguments; - response = this.getQuickInfo(quickinfoArgs.line, quickinfoArgs.offset, quickinfoArgs.file); - break; - } - case CommandNames.Format: { - var formatArgs = request.arguments; - response = this.getFormattingEditsForRange(formatArgs.line, formatArgs.offset, formatArgs.endLine, formatArgs.endOffset, formatArgs.file); - break; - } - case CommandNames.Formatonkey: { - var formatOnKeyArgs = request.arguments; - response = this.getFormattingEditsAfterKeystroke(formatOnKeyArgs.line, formatOnKeyArgs.offset, formatOnKeyArgs.key, formatOnKeyArgs.file); - break; - } - case CommandNames.Completions: { - var completionsArgs = request.arguments; - response = this.getCompletions(completionsArgs.line, completionsArgs.offset, completionsArgs.prefix, completionsArgs.file); - break; - } - case CommandNames.CompletionDetails: { - var completionDetailsArgs = request.arguments; - response = - this.getCompletionEntryDetails(completionDetailsArgs.line,completionDetailsArgs.offset, - completionDetailsArgs.entryNames,completionDetailsArgs.file); - break; - } - case CommandNames.SignatureHelp: { - var signatureHelpArgs = request.arguments; - response = this.getSignatureHelpItems(signatureHelpArgs.line, signatureHelpArgs.offset, signatureHelpArgs.file); - break; - } - case CommandNames.Geterr: { - var geterrArgs = request.arguments; - response = this.getDiagnostics(geterrArgs.delay, geterrArgs.files); - responseRequired = false; - break; - } - case CommandNames.Change: { - var changeArgs = request.arguments; - this.change(changeArgs.line, changeArgs.offset, changeArgs.endLine, changeArgs.endOffset, - changeArgs.insertString, changeArgs.file); - responseRequired = false; - break; - } - case CommandNames.Configure: { - var configureArgs = request.arguments; - this.projectService.setHostConfiguration(configureArgs); - this.output(undefined, CommandNames.Configure, request.seq); - responseRequired = false; - break; - } - case CommandNames.Reload: { - var reloadArgs = request.arguments; - this.reload(reloadArgs.file, reloadArgs.tmpfile, request.seq); - responseRequired = false; - break; - } - case CommandNames.Saveto: { - var savetoArgs = request.arguments; - this.saveToTmp(savetoArgs.file, savetoArgs.tmpfile); - responseRequired = false; - break; - } - case CommandNames.Close: { - var closeArgs = request.arguments; - this.closeClientFile(closeArgs.file); - responseRequired = false; - break; - } - case CommandNames.Navto: { - var navtoArgs = request.arguments; - response = this.getNavigateToItems(navtoArgs.searchValue, navtoArgs.file, navtoArgs.maxResultCount); - break; - } - case CommandNames.Brace: { - var braceArguments = request.arguments; - response = this.getBraceMatching(braceArguments.line, braceArguments.offset, braceArguments.file); - break; - } - case CommandNames.NavBar: { - var navBarArgs = request.arguments; - response = this.getNavigationBarItems(navBarArgs.file); - break; - } - case CommandNames.Occurrences: { - var { line, offset, file: fileName } = request.arguments; - response = this.getOccurrences(line, offset, fileName); - break; - } - case CommandNames.ProjectInfo: { - var { file, needFileNameList } = request.arguments; - response = this.getProjectInfo(file, needFileNameList); - break; - } - default: { - this.projectService.log("Unrecognized JSON command: " + message); - this.output(undefined, CommandNames.Unknown, request.seq, "Unrecognized JSON command: " + request.command); - break; - } - } + var responseRequired = this.onMessageParsed(request); if (this.logger.isVerbose()) { var elapsed = this.environment.hrtime(start); @@ -1003,13 +1015,7 @@ module ts.server { leader = "Async elapsed time (in milliseconds)"; } this.logger.msg(leader + ": " + elapsedMs.toFixed(4).toString(), "Perf"); - } - if (response) { - this.output(response, request.command, request.seq); - } - else if (responseRequired) { - this.output(undefined, request.command, request.seq, "No content available."); - } + } } catch (err) { if (err instanceof OperationCanceledException) { // Handle cancellation exceptions From 9a3fead30d36511ba9e74a75f5688f5b02d01406 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 11 Jun 2015 15:39:55 -0700 Subject: [PATCH 4/4] Add ability to dynamically handle protocol messages --- src/server/session.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/server/session.ts b/src/server/session.ts index a2ad01752dae2..627f64a8df643 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -122,6 +122,8 @@ module ts.server { event: string; body?: any; } + + export type ProtocolHandler = (request: protocol.Request) => boolean; export class Session { projectService: ProjectService; @@ -849,6 +851,14 @@ module ts.server { exit() { } + private handlers : Map = {}; + addProtocolHandler(command: string, handler: ProtocolHandler) { + this.handlers[command] = handler; + } + removeProtocolHandler(command: string, handler: ProtocolHandler) { + delete this.handlers[command]; + } + onMessageParsed(request: protocol.Request) : boolean { var response: any; var errorMessage: string; @@ -981,8 +991,13 @@ module ts.server { break; } default: { - this.projectService.log("Unrecognized JSON command: " + JSON.stringify(request)); - this.output(undefined, CommandNames.Unknown, request.seq, "Unrecognized JSON command: " + request.command); + if (this.handlers[request.command]) { + this.logger.info(`Invoking dynamic protocol handler "${request.command}"`); + return this.handlers[request.command](request); + } else { + this.projectService.log("Unrecognized JSON command: " + JSON.stringify(request)); + this.output(undefined, CommandNames.Unknown, request.seq, "Unrecognized JSON command: " + request.command); + } break; } }