diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts
index 39f59948cb..2e50ca59b9 100644
--- a/src/harness/harnessLanguageService.ts
+++ b/src/harness/harnessLanguageService.ts
@@ -128,7 +128,9 @@ module Harness.LanguageService {
protected settings = ts.getDefaultCompilerOptions()) {
}
- public getNewLine(): string {
return "\r\n";
}
+ public getNewLine(): string {
+ return "\r\n";
+ }
public getFilenames(): string[] {
var fileNames: string[] = [];
@@ -435,17 +437,26 @@ module Harness.LanguageService {
}
// Server adapter
- class ServerLanguageServiceHost extends NativeLanguageServiceHost {
+ class SessionClientHost extends NativeLanguageServiceHost implements ts.server.SessionClientHost {
private client: ts.server.SessionClient;
+
constructor(cancellationToken: ts.CancellationToken, settings: ts.CompilerOptions) {
super(cancellationToken, settings);
}
- setClient(client: ts.server.SessionClient) {
+ onMessage(message: string): void {
+
+ }
+
+ writeMessage(message: string): void {
+
+ }
+
+ setClient(client: ts.server.SessionClient) {
this.client = client;
}
- openFile(fileName: string): void {
+ openFile(fileName: string): void {
super.openFile(fileName);
this.client.openFile(fileName);
}
@@ -456,15 +467,135 @@ module Harness.LanguageService {
}
}
+ class SessionServerHost implements ts.server.ServerHost, ts.server.Logger {
+ args: string[] = [];
+ newLine: string;
+ useCaseSensitiveFileNames: boolean = false;
+
+ constructor(private host: NativeLanguageServiceHost) {
+ this.newLine = this.host.getNewLine();
+ }
+
+ onMessage(message: string): void {
+
+ }
+
+ writeMessage(message: string): void {
+ }
+
+ write(message: string): void {
+ this.writeMessage(message);
+ }
+
+ readFile(fileName: string): string {
+ var snapshot = this.host.getScriptSnapshot(fileName);
+ return snapshot && snapshot.getText(0, snapshot.getLength());
+ }
+
+ writeFile(name: string, text: string, writeByteOrderMark: boolean): void {
+ }
+
+ resolvePath(path: string): string {
+ return path;
+ }
+
+ fileExists(path: string): boolean {
+ return !!this.host.getScriptSnapshot(path);
+ }
+
+ directoryExists(path: string): boolean {
+ return false;
+ }
+
+ getExecutingFilePath(): string {
+ return "";
+ }
+
+ exit(exitCode: number): void {
+ }
+
+ createDirectory(directoryName: string): void {
+ throw new Error("Not Implemented Yet.");
+ }
+
+ getCurrentDirectory(): string {
+ return this.host.getCurrentDirectory();
+ }
+
+ readDirectory(path: string, extension?: string): string[] {
+ throw new Error("Not implemented Yet.");
+ }
+
+ getModififedTime(fileName: string): Date {
+ return new Date();
+ }
+
+ stat(path: string, callback?: (err: any, stats: any) => any) {
+ return 0;
+ }
+
+ lineColToPosition(fileName: string, line: number, col: number): number {
+ return this.host.lineColToPosition(fileName, line, col);
+ }
+
+ positionToZeroBasedLineCol(fileName: string, position: number): ts.LineAndCharacter {
+ return this.host.positionToZeroBasedLineCol(fileName, position);
+ }
+
+ getFileLength(fileName: string): number {
+ return this.host.getScriptSnapshot(fileName).getLength();
+ }
+
+ getFileNames(): string[] {
+ return this.host.getScriptFileNames();
+ }
+
+ close(): void {
+ }
+
+ info(message: string): void {
+ return this.host.log(message);
+ }
+
+ msg(message: string) {
+ return this.host.log(message);
+ }
+
+ endGroup(): void {
+ }
+
+ perftrc(message: string): void {
+ return this.host.log(message);
+ }
+
+ startGroup(): void {
+ }
+ }
+
export class ServerLanugageServiceAdapter implements LanguageServiceAdapter {
- private host: ServerLanguageServiceHost;
+ private host: SessionClientHost;
private client: ts.server.SessionClient;
constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) {
- debugger;
+ // This is the main host that tests use to direct tests
+ var clientHost = new SessionClientHost(cancellationToken, options);
+ var client = new ts.server.SessionClient(clientHost);
- this.host = new ServerLanguageServiceHost(cancellationToken, options);
- this.client = new ts.server.SessionClient(this.host, /*abbreviate*/ true);
- this.host.setClient(this.client);
+ // 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, /*useProtocol*/ true, /*prettyJSON*/ false);
+
+ // Fake the connection between the client and the server
+ serverHost.writeMessage = client.onMessage.bind(client);
+ clientHost.writeMessage = server.onMessage.bind(server);
+
+ // Wire the client to the host to get notifications when a file is open
+ // or edited.
+ clientHost.setClient(client);
+
+ // Set the properties
+ this.client = client;
+ this.host = clientHost;
}
getHost() { return this.host; }
getLanguageService(): ts.LanguageService { return this.client; }
diff --git a/src/server/client.ts b/src/server/client.ts
index a93177adb3..1aa7547f54 100644
--- a/src/server/client.ts
+++ b/src/server/client.ts
@@ -1,177 +1,49 @@
-///
-
-module ts.server {
-
- export interface SessionClientHost extends LanguageServiceHost {
- lineColToPosition(fileName: string, line: number, col: number): number;
- positionToZeroBasedLineCol(fileName: string, position: number): ts.LineAndCharacter;
- }
-
- class SessionClientHostProxy implements ServerHost, Logger {
- args: string[] = [];
- newLine: string;
- useCaseSensitiveFileNames: boolean = false;
- lastReply: string;
-
- constructor(private host: SessionClientHost) {
- this.newLine = this.host.getNewLine();
- }
+///
- write(message: string): void {
- this.lastReply = message;
- }
+module ts.server {
- readFile(fileName: string): string {
- var snapshot = this.host.getScriptSnapshot(fileName);
- return snapshot && snapshot.getText(0, snapshot.getLength());
- }
-
- writeFile(name: string, text:string, writeByteOrderMark: boolean): void {
- }
-
- resolvePath(path: string): string {
- return path;
- }
-
- fileExists(path: string): boolean {
- return !!this.host.getScriptSnapshot(path);
- }
-
- directoryExists(path: string): boolean {
- return false;
- }
-
- getExecutingFilePath(): string {
- return "";
- }
-
- exit(exitCode: number): void {
- }
-
- createDirectory(directoryName: string): void {
- throw new Error("Not Implemented Yet.");
- }
-
- getCurrentDirectory(): string {
- return this.host.getCurrentDirectory();
- }
-
- readDirectory(path: string, extension?: string): string[] {
- throw new Error("Not implemented Yet.");
- }
-
- getModififedTime(fileName: string): Date {
- return new Date();
- }
-
- stat(path: string, callback?: (err: any, stats: any) => any) {
- return 0;
- }
-
- lineColToPosition(fileName: string, line: number, col: number): number {
- return this.host.lineColToPosition(fileName, line, col);
- }
-
- positionToZeroBasedLineCol(fileName: string, position: number): ts.LineAndCharacter {
- return this.host.positionToZeroBasedLineCol(fileName, position);
- }
-
- getFileLength(fileName: string): number {
- return this.host.getScriptSnapshot(fileName).getLength();
- }
-
- getFileNames(): string[] {
- return this.host.getScriptFileNames();
- }
-
- close(): void {
- }
-
- info(message: string): void {
- return this.host.log(message);
- }
-
- msg(message: string) {
- return this.host.log(message);
- }
-
- endGroup(): void {
- }
-
- perftrc(message: string): void {
- return this.host.log(message);
- }
-
- startGroup(): void {
- }
+ export interface SessionClientHost extends LanguageServiceHost {
+ writeMessage(message: string): void;
}
-
- export class SessionClient implements LanguageService {
- private session: Session;
- private sequence: number = 0;
- private host: SessionClientHostProxy;
- private expantionTable: ts.Map;
- private fileMapping: ts.Map = {};
-
- constructor(host: SessionClientHost, abbreviate: boolean) {
- this.sequence = 0;
- this.host = new SessionClientHostProxy(host);
- this.session = new Session(this.host, this.host, /* useProtocol */ true, /*prettyJSON*/ false);
-
- // Setup the abbreviation table
- if (abbreviate) {
- this.setupExpantionTable()
- }
- }
-
- private setupExpantionTable(): void {
- var request = this.processRequest(CommandNames.Abbrev);
- var response = this.processResponse(request);
- var abbriviationTable = response.body;
- Debug.assert(!!abbriviationTable, "Could not setup abbreviation. Abbreviation table was empty.");
-
- var expantionTable: ts.Map = {};
- for (var p in abbriviationTable) {
- if (abbriviationTable.hasOwnProperty(p)) {
- expantionTable[abbriviationTable[p]] = p;
- }
- }
- this.expantionTable = expantionTable;
- }
-
- private expand(obj: T): T {
- for (var p in obj) {
- if (obj.hasOwnProperty(p)) {
- if (typeof (obj)[p] === "object") {
- // Expand the property value
- (obj)[p] = this.expand((obj)[p]);
- }
-
- // Substitute the name if applicaple
- var substitution = ts.lookUp(this.expantionTable, p);
- if (substitution) {
- (obj)[substitution] = (obj)[p];
- (obj)[p] = undefined;
- }
- }
- }
-
- return obj;
- }
-
- private lineColToPosition(fileName: string, lineCol: ServerProtocol.LineCol): number {
- return this.host.lineColToPosition(fileName, lineCol.line, lineCol.col);
+ export class SessionClient implements LanguageService {
+ private sequence: number = 0;
+ private fileMapping: ts.Map = {};
+ private lineMaps: ts.Map = {};
+ private messages: string[] = [];
+
+ constructor(private host: SessionClientHost) {
}
- private positionToOneBasedLineCol(fileName: string, position: number): ServerProtocol.LineCol {
- var lineCol = this.host.positionToZeroBasedLineCol(fileName, position);
+ public onMessage(message: string): void {
+ this.messages.push(message);
+ }
+
+ private writeMessage(message: string): void {
+ this.host.writeMessage(message);
+ }
+
+ private getLineMap(fileName: string): number[] {
+ var lineMap = ts.lookUp(this.lineMaps, fileName);
+ if (!lineMap) {
+ var scriptSnapshot = this.host.getScriptSnapshot(fileName);
+ lineMap = this.lineMaps[fileName] = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()));
+ }
+ return lineMap;
+ }
+
+ private lineColToPosition(fileName: string, lineCol: ServerProtocol.LineCol): number {
+ return ts.computePositionFromLineAndCharacter(this.getLineMap(fileName), lineCol.line, lineCol.col);
+ }
+
+ private positionToOneBasedLineCol(fileName: string, position: number): ServerProtocol.LineCol {
+ var lineCol = ts.computeLineAndCharacterOfPosition(this.getLineMap(fileName), position);
return {
- line: lineCol.line + 1,
- col: lineCol.character + 1
+ line: lineCol.line,
+ col: lineCol.character
};
- }
-
+ }
+
private convertCodeEditsToTextChange(fileName: string, codeEdit: ServerProtocol.CodeEdit): ts.TextChange {
var start = this.lineColToPosition(fileName, codeEdit.start);
var end = this.lineColToPosition(fileName, codeEdit.end);
@@ -180,8 +52,8 @@ module ts.server {
span: ts.createTextSpanFromBounds(start, end),
newText: codeEdit.newText
};
- }
-
+ }
+
private getFileNameFromEncodedFile(fileId: ServerProtocol.EncodedFile): string {
var fileName: string;
if (typeof fileId === "object") {
@@ -196,9 +68,9 @@ module ts.server {
Debug.fail("Got unexpedted fileId type.");
}
return fileName;
- }
-
- private processRequest(command: string, arguments?: any): T {
+ }
+
+ private processRequest(command: string, arguments?: any): T {
var request: ServerProtocol.Request = {
seq: this.sequence++,
type: "request",
@@ -206,34 +78,33 @@ module ts.server {
arguments: arguments
};
- this.session.executeJSONcmd(JSON.stringify(request));
-
- return request;
- }
-
- private processResponse(request: ServerProtocol.Request): T {
- debugger;
-
- var lastMessage = this.host.lastReply;
- this.host.lastReply = undefined;
- Debug.assert(!!lastMessage, "Did not recieve any responses.");
-
- // Read the content length
- var contentLengthPrefix = "Content-Length: ";
- var lines = lastMessage.split("\r\n");
- Debug.assert(lines.length >= 2, "Malformed response: Expected 3 lines in the response.");
-
- var contentLengthText = lines[0];
- Debug.assert(contentLengthText.indexOf(contentLengthPrefix) === 0, "Malformed response: Response text did not contain content-length header.");
- var contentLength = parseInt(contentLengthText.substring(contentLengthPrefix.length));
-
- // Read the body
- var responseBody = lines[2];
-
- // Verify content length
- Debug.assert(responseBody.length + 1 === contentLength, "Malformed response: Content length did not match the response's body length.");
-
- try {
+ this.writeMessage(JSON.stringify(request));
+
+ return request;
+ }
+
+ private processResponse(request: ServerProtocol.Request): T {
+ debugger;
+
+ var lastMessage = this.messages.shift();
+ Debug.assert(!!lastMessage, "Did not recieve any responses.");
+
+ // Read the content length
+ var contentLengthPrefix = "Content-Length: ";
+ var lines = lastMessage.split("\r\n");
+ Debug.assert(lines.length >= 2, "Malformed response: Expected 3 lines in the response.");
+
+ var contentLengthText = lines[0];
+ Debug.assert(contentLengthText.indexOf(contentLengthPrefix) === 0, "Malformed response: Response text did not contain content-length header.");
+ var contentLength = parseInt(contentLengthText.substring(contentLengthPrefix.length));
+
+ // Read the body
+ var responseBody = lines[2];
+
+ // Verify content length
+ Debug.assert(responseBody.length + 1 === contentLength, "Malformed response: Content length did not match the response's body length.");
+
+ try {
var response: T = JSON.parse(responseBody);
}
catch (e) {
@@ -250,13 +121,9 @@ module ts.server {
Debug.assert(!!response.body, "Malformed response: Unexpected empty response body.");
- if (this.expantionTable) {
- // Expand the response if abbreviated
- return this.expand(response);
- }
return response;
- }
-
+ }
+
openFile(fileName: string): void {
var args: ServerProtocol.FileRequestArgs = { file: fileName };
this.processRequest(CommandNames.Open, args);
@@ -268,6 +135,9 @@ module ts.server {
}
changeFile(fileName: string, start: number, end: number, newText: string): void {
+ // clear the line map after an edit
+ this.lineMaps[fileName] = undefined;
+
var lineCol = this.positionToOneBasedLineCol(fileName, start);
var args: ServerProtocol.ChangeRequestArgs = {
file: fileName,
@@ -326,7 +196,7 @@ module ts.server {
getNavigateToItems(searchTerm: string): NavigateToItem[] {
var args: ServerProtocol.NavtoRequestArgs = {
searchTerm,
- file: this.host.getFileNames()[0]
+ file: this.host.getScriptFileNames()[0]
};
var request = this.processRequest(CommandNames.Navto, args);
@@ -357,7 +227,7 @@ module ts.server {
file: fileName,
line: startLineCol.line,
col: startLineCol.col,
- endLine: endLineCol.line,
+ endLine: endLineCol.line,
endCol: endLineCol.col,
};
@@ -369,7 +239,7 @@ module ts.server {
}
getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): ts.TextChange[] {
- return this.getFormattingEditsForRange(fileName, 0, this.host.getFileLength(fileName), options);
+ return this.getFormattingEditsForRange(fileName, 0, this.host.getScriptSnapshot(fileName).getLength(), options);
}
getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): ts.TextChange[] {
@@ -537,7 +407,7 @@ module ts.server {
}
dispose(): void {
- throw new Error("dispose is not available through the server layer.");
- }
- }
-}
+ throw new Error("dispose is not available through the server layer.");
+ }
+ }
+}
diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts
index 933435f93a..7007704a16 100644
--- a/src/server/editorServices.ts
+++ b/src/server/editorServices.ts
@@ -425,7 +425,8 @@ module ts.server {
if (projectOptions.compilerOptions) {
this.compilerService.setCompilerOptions(projectOptions.compilerOptions);
}
- // TODO: format code options
}
+ // TODO: format code options
+ }
}
export interface ProjectOpenResult {
diff --git a/src/server/protocol.ts b/src/server/protocol.ts
index 6187d8e614..16abbf029d 100644
--- a/src/server/protocol.ts
+++ b/src/server/protocol.ts
@@ -33,39 +33,6 @@ module ts.server {
return spaceCache[n];
}
- function isTypeName(name: string, suffix?: string) {
- for (var i = 0, len = typeNames.length; i < len; i++) {
- if (typeNames[i] == name) {
- return true;
- }
- else if (suffix && ((typeNames[i] + suffix) == name)) {
- return true;
- }
- }
- return false;
- }
-
- function parseTypeName(displayParts: ts.SymbolDisplayPart[]) {
- var len = displayParts.length;
- for (var i = len - 1; i >= 0; i--) {
- if (isTypeName(displayParts[i].kind, "Name")) {
- return displayParts[i].text;
- }
- }
- return undefined;
- }
-
- function findExactMatchType(items: ts.NavigateToItem[]) {
- for (var i = 0, len = items.length; i < len; i++) {
- var navItem = items[i];
- if (navItem.matchKind == "exact") {
- if (isTypeName(navItem.kind)) {
- return navItem;
- }
- }
- }
- }
-
interface FileStart {
file: string;
start: ILineInfo;
@@ -81,14 +48,6 @@ module ts.server {
else return 1;
}
- function printObject(obj: any) {
- for (var p in obj) {
- if (obj.hasOwnProperty(p)) {
- console.log(p + ": " + obj[p]);
- }
- }
- }
-
function compareFileStart(a: FileStart, b: FileStart) {
if (a.file < b.file) {
return -1;
@@ -128,178 +87,7 @@ module ts.server {
}
})
}
-
- //function SourceInfo(body: NodeJS._debugger.BreakResponse) {
- // var result = body.exception ? 'exception in ' : 'break in ';
-
- // if (body.script) {
- // if (body.script.name) {
- // var name = body.script.name,
- // dir = path.resolve() + '/';
-
- // // Change path to relative, if possible
- // if (name.indexOf(dir) === 0) {
- // name = name.slice(dir.length);
- // }
-
- // result += name;
- // } else {
- // result += '[unnamed]';
- // }
- // }
- // result += ':';
- // result += body.sourceLine + 1;
-
- // if (body.exception) result += '\n' + body.exception.text;
-
- // return result;
- //}
-
- class JsDebugSession {
- host = 'localhost';
- port = 5858;
-
- constructor(public client: NodeJS._debugger.Client) {
- this.init();
- }
-
- cont(cb: NodeJS._debugger.RequestHandler) {
- this.client.reqContinue(cb);
- }
-
- listSrc() {
- this.client.reqScripts((err: any) => {
- if (err) {
- console.log("rscr error: " + err);
- }
- else {
- console.log("req scripts");
- for (var id in this.client.scripts) {
- var script = this.client.scripts[id];
- if ((typeof script === "object") && script.name) {
- console.log(id + ": " + script.name);
- }
- }
- }
- });
- }
-
- findScript(file: string) {
- if (file) {
- var script: NodeJS._debugger.ScriptDesc;
- var scripts = this.client.scripts;
- var keys: any[] = Object.keys(scripts);
- var ambiguous = false;
- for (var v = 0; v < keys.length; v++) {
- var id = keys[v];
- if (scripts[id] &&
- scripts[id].name &&
- scripts[id].name.indexOf(file) !== -1) {
- if (script) {
- ambiguous = true;
- }
- script = scripts[id];
- }
- }
- return { script: script, ambiguous: ambiguous };
- }
- }
-
- // TODO: condition
- setBreakpointOnLine(line: number, file?: string) {
- if (!file) {
- file = this.client.currentScript;
- }
- var script: NodeJS._debugger.ScriptDesc;
- var scriptResult = this.findScript(file);
- if (scriptResult) {
- if (scriptResult.ambiguous) {
- // TODO: send back error
- script = undefined;
- }
- else {
- script = scriptResult.script;
- }
- }
- // TODO: set breakpoint when script not loaded
- if (script) {
- var brkmsg: NodeJS._debugger.BreakpointMessageBody = {
- type: 'scriptId',
- target: script.id,
- line: line - 1,
- }
- this.client.setBreakpoint(brkmsg,(err, bod) => {
- // TODO: remember breakpoint
- if (err) {
- console.log("Error: set breakpoint: " + err);
- }
- });
- }
-
- }
-
- init() {
- var connectionAttempts = 0;
- this.client.on('break',(res: NodeJS._debugger.Event) => {
- this.handleBreak(res.body);
- });
- this.client.on('exception',(res: NodeJS._debugger.Event) => {
- this.handleBreak(res.body);
- });
- this.client.on('error',() => {
- setTimeout(() => {
- ++connectionAttempts;
- this.client.connect(this.port, this.host);
- }, 500);
- });
- this.client.once('ready',() => {
- });
- this.client.on('unhandledResponse',() => {
- });
- this.client.connect(this.port, this.host);
- }
-
- evaluate(code: string) {
- var frame = this.client.currentFrame;
- this.client.reqFrameEval(code, frame,(err, bod) => {
- if (err) {
- console.log("Error: evaluate: " + err);
- return;
- }
-
- console.log("Value: " + bod.toString());
- if (typeof bod === "object") {
- printObject(bod);
- }
-
- // Request object by handles (and it's sub-properties)
- this.client.mirrorObject(bod, 3,(err, mirror) => {
- if (mirror) {
- if (typeof mirror === "object") {
- printObject(mirror);
- }
- console.log(mirror.toString());
- }
- else {
- console.log("undefined");
- }
- });
-
- });
- }
-
- handleBreak(breakInfo: NodeJS._debugger.BreakResponse) {
- this.client.currentSourceLine = breakInfo.sourceLine;
- this.client.currentSourceLineText = breakInfo.sourceLineText;
- this.client.currentSourceColumn = breakInfo.sourceColumn;
- this.client.currentFrame = 0;
- this.client.currentScript = breakInfo.script && breakInfo.script.name;
-
- //console.log(SourceInfo(breakInfo));
- // TODO: watchers
- }
- }
-
+
interface FileRange {
file?: string;
start: ILineInfo;
@@ -320,7 +108,7 @@ module ts.server {
}
interface PendingErrorCheck {
- filename: string;
+ fileName: string;
project: Project;
}
@@ -334,7 +122,6 @@ module ts.server {
}
export module CommandNames {
- export var Abbrev = "abbrev";
export var Change = "change";
export var Close = "close";
export var Completions = "completions";
@@ -349,22 +136,22 @@ module ts.server {
export var Reload = "reload";
export var Rename = "rename";
export var Saveto = "saveto";
- export var Type = "type";
export var Brace = "brace";
export var Unknown = "unknown";
}
+ module Errors {
+ export var NoProject = new Error("No Project.");
+ export var NoContent = new Error("No Content.");
+ }
+
export interface ServerHost extends ts.System {
- getDebuggerClient? (): NodeJS._debugger.Client;
}
export class Session {
projectService: ProjectService;
- debugSession: JsDebugSession;
pendingOperation = false;
fileHash: ts.Map = {};
- abbrevTable: ts.Map;
- fetchedAbbrev = false;
nextFileId = 1;
errorTimer: NodeJS.Timer;
immediateId: any;
@@ -372,7 +159,6 @@ module ts.server {
constructor(private host: ServerHost, private logger: Logger, protected useProtocol: boolean, protected prettyJSON: boolean) {
this.projectService = new ProjectService(host, logger);
- this.initAbbrevTable();
}
logError(err: Error, cmd: string) {
@@ -387,8 +173,8 @@ module ts.server {
this.projectService.log(msg);
}
- sendLineToClient(line: string) {
- this.host.write(line + this.host.newLine);
+ sendLineToClient(line: string) {
+ this.host.write(line + this.host.newLine);
}
send(msg: NodeJS._debugger.Message) {
@@ -430,51 +216,15 @@ module ts.server {
this.send(res);
}
- initAbbrevTable() {
- this.abbrevTable = {
- name: "n",
- kind: "k",
- fileName: "f",
- containerName: "cn",
- containerKind: "ck",
- kindModifiers: "km",
- start: "s",
- end: "e",
- line: "l",
- col: "c",
- "interface": "i",
- "function": "fn",
- };
- }
-
- encodeFilename(filename: string): ServerProtocol.EncodedFile {
- if (!this.fetchedAbbrev) {
- return filename;
+ encodeFilename(fileName: string): ServerProtocol.EncodedFile {
+ var id = ts.lookUp(this.fileHash, fileName);
+ if (!id) {
+ id = this.nextFileId++;
+ this.fileHash[fileName] = id;
+ return { id: id, file: fileName };
}
else {
- var id = ts.lookUp(this.fileHash, filename);
- if (!id) {
- id = this.nextFileId++;
- this.fileHash[filename] = id;
- return { id: id, file: filename };
- }
- else {
- return id;
- }
- }
- }
-
- abbreviate(obj: any) {
- if (this.fetchedAbbrev && (!this.prettyJSON)) {
- for (var p in obj) {
- if (obj.hasOwnProperty(p)) {
- var sub = ts.lookUp(this.abbrevTable, p);
- if (sub) {
- obj[sub] = obj[p];
- obj[p] = undefined;
- }
- }
- }
+ return id;
}
}
@@ -544,10 +294,10 @@ module ts.server {
var checkOne = () => {
if (matchSeq(seq)) {
var checkSpec = checkList[index++];
- if (checkSpec.project.getSourceFileFromName(checkSpec.filename)) {
- this.syntacticCheck(checkSpec.filename, checkSpec.project);
+ if (checkSpec.project.getSourceFileFromName(checkSpec.fileName)) {
+ this.syntacticCheck(checkSpec.fileName, checkSpec.project);
this.immediateId = setImmediate(() => {
- this.semanticCheck(checkSpec.filename, checkSpec.project);
+ this.semanticCheck(checkSpec.fileName, checkSpec.project);
this.immediateId = undefined;
if (checkList.length > index) {
this.errorTimer = setTimeout(checkOne, followMs);
@@ -564,181 +314,140 @@ module ts.server {
}
}
- goToDefinition(line: number, col: number, rawfile: string, reqSeq = 0) {
- var file = ts.normalizePath(rawfile);
+ goToDefinition(line: number, col: number, fileName: string): ServerProtocol.CodeSpan[] {
+ var file = ts.normalizePath(fileName);
var project = this.projectService.getProjectForFile(file);
- if (project) {
- var compilerService = project.compilerService;
- var pos = compilerService.host.lineColToPosition(file, line, col);
- var locs = compilerService.languageService.getDefinitionAtPosition(file, pos);
- if (locs) {
- var info: ServerProtocol.CodeSpan[] = locs.map(def => ({
- file: def && def.fileName,
- start: def &&
- compilerService.host.positionToLineCol(def.fileName, def.textSpan.start),
- end: def &&
- compilerService.host.positionToLineCol(def.fileName, ts.textSpanEnd(def.textSpan))
- }));
- this.output(info, CommandNames.Definition, reqSeq);
- }
- else {
- this.output(undefined, CommandNames.Definition, reqSeq, "could not find def");
- }
+ if (!project) {
+ throw Errors.NoProject;
}
- else {
- this.output(undefined, CommandNames.Definition, reqSeq, "no project for " + file);
+
+ var compilerService = project.compilerService;
+ var position = compilerService.host.lineColToPosition(file, line, col);
+
+ var definitions = compilerService.languageService.getDefinitionAtPosition(file, position);
+ if (!definitions) {
+ throw Errors.NoContent;
}
+
+ return definitions.map(def => ({
+ file: def && def.fileName,
+ start: def &&
+ compilerService.host.positionToLineCol(def.fileName, def.textSpan.start),
+ end: def &&
+ compilerService.host.positionToLineCol(def.fileName, ts.textSpanEnd(def.textSpan))
+ }));
}
- rename(line: number, col: number, rawfile: string, reqSeq = 0) {
- var file = ts.normalizePath(rawfile);
+ rename(line: number, col: number, fileName: string): { info: RenameInfo; locs: ServerProtocol.CodeSpan[] } {
+ var file = ts.normalizePath(fileName);
var project = this.projectService.getProjectForFile(file);
- if (project) {
- var compilerService = project.compilerService;
- var pos = compilerService.host.lineColToPosition(file, line, col);
- var renameInfo = compilerService.languageService.getRenameInfo(file, pos);
- if (renameInfo) {
- if (renameInfo.canRename) {
- var renameLocs = compilerService.languageService.findRenameLocations(file, pos, false, false);
- if (renameLocs) {
- var bakedRenameLocs = renameLocs.map(loc=> ({
- file: loc.fileName,
- start: compilerService.host.positionToLineCol(loc.fileName, loc.textSpan.start),
- end: compilerService.host.positionToLineCol(loc.fileName, ts.textSpanEnd(loc.textSpan)),
- })).sort((a, b) => {
- if (a.file < b.file) {
- return -1;
- }
- else if (a.file > b.file) {
- return 1;
- }
- else {
- // reverse sort assuming no overlap
- if (a.start.line < b.start.line) {
- return 1;
- }
- else if (a.start.line > b.start.line) {
- return -1;
- }
- else {
- return b.start.col - a.start.col;
- }
- }
- }).reduce((accum: FileRanges[], cur: FileRange) => {
- var curFileAccum: FileRanges;
- if (accum.length > 0) {
- curFileAccum = accum[accum.length - 1];
- if (curFileAccum.file != cur.file) {
- curFileAccum = undefined;
- }
- }
- if (!curFileAccum) {
- curFileAccum = { file: cur.file, locs: [] };
- accum.push(curFileAccum);
- }
- curFileAccum.locs.push({ start: cur.start, end: cur.end });
- return accum;
- }, []);
- this.output({ info: renameInfo, locs: bakedRenameLocs }, CommandNames.Rename, reqSeq);
+ if (!project) {
+ throw Errors.NoProject;
+ }
+
+ var compilerService = project.compilerService;
+ var position = compilerService.host.lineColToPosition(file, line, col);
+ var renameInfo = compilerService.languageService.getRenameInfo(file, position);
+ if (!renameInfo) {
+ throw Errors.NoContent;
+ }
+
+ if (!renameInfo.canRename) {
+ return {
+ info: renameInfo,
+ locs: []
+ };
+ }
+
+ var renameLocs = compilerService.languageService.findRenameLocations(file, position, false, false);
+ if (renameLocs) {
+ var bakedRenameLocs = renameLocs.map(loc=> ({
+ file: loc.fileName,
+ start: compilerService.host.positionToLineCol(loc.fileName, loc.textSpan.start),
+ end: compilerService.host.positionToLineCol(loc.fileName, ts.textSpanEnd(loc.textSpan)),
+ })).sort((a, b) => {
+ if (a.file < b.file) {
+ return -1;
+ }
+ else if (a.file > b.file) {
+ return 1;
+ }
+ else {
+ // reverse sort assuming no overlap
+ if (a.start.line < b.start.line) {
+ return 1;
+ }
+ else if (a.start.line > b.start.line) {
+ return -1;
}
else {
- this.output({ info: renameInfo, locs: [] }, CommandNames.Rename, reqSeq);
+ return b.start.col - a.start.col;
}
}
- else {
- this.output(undefined, CommandNames.Rename, reqSeq, renameInfo.localizedErrorMessage);
+ }).reduce((accum, cur) => {
+ var curFileAccum: FileRanges;
+ if (accum.length > 0) {
+ curFileAccum = accum[accum.length - 1];
+ if (curFileAccum.file != cur.file) {
+ curFileAccum = undefined;
+ }
}
- }
- else {
- this.output(undefined, CommandNames.Rename, reqSeq, "no rename information at cursor");
- }
+ if (!curFileAccum) {
+ curFileAccum = { file: cur.file, locs: [] };
+ accum.push(curFileAccum);
+ }
+ curFileAccum.locs.push({ start: cur.start, end: cur.end });
+ return accum;
+ }, []);
}
+
+ return { info: renameInfo, locs: bakedRenameLocs };
}
- findReferences(line: number, col: number, rawfile: string, reqSeq = 0) {
+ findReferences(line: number, col: number, fileName: string): ServerProtocol.ReferencesResponseBody {
// TODO: get all projects for this file; report refs for all projects deleting duplicates
// can avoid duplicates by eliminating same ref file from subsequent projects
- var file = ts.normalizePath(rawfile);
+ var file = ts.normalizePath(fileName);
var project = this.projectService.getProjectForFile(file);
- if (project) {
- var compilerService = project.compilerService;
- var pos = compilerService.host.lineColToPosition(file, line, col);
- var refs = compilerService.languageService.getReferencesAtPosition(file, pos);
- if (refs) {
- var nameInfo = compilerService.languageService.getQuickInfoAtPosition(file, pos);
- if (nameInfo) {
- var displayString = ts.displayPartsToString(nameInfo.displayParts);
- var nameSpan = nameInfo.textSpan;
- var nameColStart =
- compilerService.host.positionToLineCol(file, nameSpan.start).col;
- var nameText =
- compilerService.host.getScriptSnapshot(file).getText(nameSpan.start, ts.textSpanEnd(nameSpan));
- var bakedRefs: ServerProtocol.ReferencesResponseItem[] = refs.map((ref) => {
- var start = compilerService.host.positionToLineCol(ref.fileName, ref.textSpan.start);
- var refLineSpan = compilerService.host.lineToTextSpan(ref.fileName, start.line - 1);
- var snap = compilerService.host.getScriptSnapshot(ref.fileName);
- var lineText = snap.getText(refLineSpan.start, ts.textSpanEnd(refLineSpan)).replace(/\r|\n/g, "");
- return {
- file: ref.fileName,
- start: start,
- lineText: lineText,
- end: compilerService.host.positionToLineCol(ref.fileName, ts.textSpanEnd(ref.textSpan)),
- };
- }).sort(compareFileStart);
- var response: ServerProtocol.ReferencesResponseBody = {
- refs: bakedRefs,
- symbolName: nameText,
- symbolStartCol: nameColStart,
- symbolDisplayString: displayString
- };
- this.output(response, CommandNames.References, reqSeq);
- }
- else {
- this.output(undefined, CommandNames.References, reqSeq, "no references at this position");
- }
- }
- else {
- this.output(undefined, CommandNames.References, reqSeq, "no references at this position");
- }
+ if (!project) {
+ throw Errors.NoProject;
}
- }
- // TODO: implement this as ls api that can return multiple def sites
- goToType(line: number, col: number, rawfile: string, reqSeq = 0) {
- var file = ts.normalizePath(rawfile);
- var project = this.projectService.getProjectForFile(file);
- if (project) {
- var compilerService = project.compilerService;
- var pos = compilerService.host.lineColToPosition(file, line, col);
- var quickInfo = compilerService.languageService.getQuickInfoAtPosition(file, pos);
- var typeLoc: any;
+ var compilerService = project.compilerService;
+ var position = compilerService.host.lineColToPosition(file, line, col);
- if (quickInfo && (quickInfo.kind == "var") || (quickInfo.kind == "local var")) {
- var typeName = parseTypeName(quickInfo.displayParts);
- if (typeName) {
- var navItems = compilerService.languageService.getNavigateToItems(typeName);
- var navItem = findExactMatchType(navItems);
- if (navItem) {
- typeLoc = {
- file: navItem.fileName,
- start: compilerService.host.positionToLineCol(navItem.fileName,
- navItem.textSpan.start),
- end: compilerService.host.positionToLineCol(navItem.fileName,
- ts.textSpanEnd(navItem.textSpan)),
- };
- }
- }
- }
- if (typeLoc) {
- this.output([typeLoc], CommandNames.Type, reqSeq);
- }
- else {
- this.output(undefined, CommandNames.Type, reqSeq, "no info at this location");
- }
+ var references = compilerService.languageService.getReferencesAtPosition(file, position);
+ if (!references) {
+ throw Errors.NoContent;
}
- else {
- this.output(undefined, CommandNames.Type, reqSeq, "no project for " + file);
+
+ var nameInfo = compilerService.languageService.getQuickInfoAtPosition(file, position);
+ if (!nameInfo) {
+ throw Errors.NoContent;
}
+
+ var displayString = ts.displayPartsToString(nameInfo.displayParts);
+ var nameSpan = nameInfo.textSpan;
+ var nameColStart = compilerService.host.positionToLineCol(file, nameSpan.start).col;
+ var nameText = compilerService.host.getScriptSnapshot(file).getText(nameSpan.start, ts.textSpanEnd(nameSpan));
+ var bakedRefs: ServerProtocol.ReferencesResponseItem[] = references.map((ref) => {
+ var start = compilerService.host.positionToLineCol(ref.fileName, ref.textSpan.start);
+ var refLineSpan = compilerService.host.lineToTextSpan(ref.fileName, start.line - 1);
+ var snap = compilerService.host.getScriptSnapshot(ref.fileName);
+ var lineText = snap.getText(refLineSpan.start, ts.textSpanEnd(refLineSpan)).replace(/\r|\n/g, "");
+ return {
+ file: ref.fileName,
+ start: start,
+ lineText: lineText,
+ end: compilerService.host.positionToLineCol(ref.fileName, ts.textSpanEnd(ref.textSpan)),
+ };
+ }).sort(compareFileStart);
+ return {
+ refs: bakedRefs,
+ symbolName: nameText,
+ symbolStartCol: nameColStart,
+ symbolDisplayString: displayString
+ };
}
openClientFile(rawfile: string) {
@@ -746,173 +455,151 @@ module ts.server {
this.projectService.openClientFile(file);
}
- quickInfo(line: number, col: number, rawfile: string, reqSeq = 0) {
- var file = ts.normalizePath(rawfile);
+ quickInfo(line: number, col: number, fileName: string): ServerProtocol.QuickInfoResponseBody {
+ var file = ts.normalizePath(fileName);
var project = this.projectService.getProjectForFile(file);
- if (project) {
- var compilerService = project.compilerService;
- var pos = compilerService.host.lineColToPosition(file, line, col);
- var quickInfo = compilerService.languageService.getQuickInfoAtPosition(file, pos);
- if (quickInfo) {
- var displayString = ts.displayPartsToString(quickInfo.displayParts);
- var docString = ts.displayPartsToString(quickInfo.documentation);
- var qi: ServerProtocol.QuickInfoResponseBody = {
- kind: quickInfo.kind,
- kindModifiers: quickInfo.kindModifiers,
- start: compilerService.host.positionToLineCol(file, quickInfo.textSpan.start),
- end: compilerService.host.positionToLineCol(file, ts.textSpanEnd(quickInfo.textSpan)),
- displayString: displayString,
- documentation: docString,
- };
- this.output(qi, CommandNames.Quickinfo, reqSeq);
- }
- else {
- this.output(undefined, CommandNames.Quickinfo, reqSeq, "no info")
- }
+ if (!project) {
+ throw Errors.NoProject;
}
+
+ var compilerService = project.compilerService;
+ var position = compilerService.host.lineColToPosition(file, line, col);
+ var quickInfo = compilerService.languageService.getQuickInfoAtPosition(file, position);
+ if (!quickInfo) {
+ throw Errors.NoContent;
+ }
+
+ var displayString = ts.displayPartsToString(quickInfo.displayParts);
+ var docString = ts.displayPartsToString(quickInfo.documentation);
+ return {
+ kind: quickInfo.kind,
+ kindModifiers: quickInfo.kindModifiers,
+ start: compilerService.host.positionToLineCol(file, quickInfo.textSpan.start),
+ end: compilerService.host.positionToLineCol(file, ts.textSpanEnd(quickInfo.textSpan)),
+ displayString: displayString,
+ documentation: docString,
+ };
}
- format(line: number, col: number, endLine: number, endCol: number, rawfile: string, cmd: string, reqSeq = 0) {
- var file = ts.normalizePath(rawfile);
+ format(line: number, col: number, endLine: number, endCol: number, fileName: string): ServerProtocol.CodeEdit[] {
+ var file = ts.normalizePath(fileName);
var project = this.projectService.getProjectForFile(file);
- if (project) {
- var compilerService = project.compilerService;
- var pos = compilerService.host.lineColToPosition(file, line, col);
- var endPos = compilerService.host.lineColToPosition(file, endLine, endCol);
- var edits: ts.TextChange[];
- // TODO: avoid duplicate code (with formatonkey)
- try {
- edits = compilerService.languageService.getFormattingEditsForRange(file, pos, endPos,
- compilerService.formatCodeOptions);
- }
- catch (err) {
- this.logError(err, cmd);
- edits = undefined;
- }
- if (edits) {
- var bakedEdits: ServerProtocol.CodeEdit[] = edits.map((edit) => {
- return {
- start: compilerService.host.positionToLineCol(file,
- edit.span.start),
- end: compilerService.host.positionToLineCol(file,
- ts.textSpanEnd(edit.span)),
- newText: edit.newText ? edit.newText : ""
- };
- });
- this.output(bakedEdits, CommandNames.Format, reqSeq);
- }
- else {
- this.output(undefined, CommandNames.Format, reqSeq, "no edits")
- }
+ if (!project) {
+ throw Errors.NoProject;
}
+
+ var compilerService = project.compilerService;
+ var startPosition = compilerService.host.lineColToPosition(file, line, col);
+ var endPosition = compilerService.host.lineColToPosition(file, endLine, endCol);
+
+ // TODO: avoid duplicate code (with formatonkey)
+ var edits = compilerService.languageService.getFormattingEditsForRange(file, startPosition, endPosition, compilerService.formatCodeOptions);
+ if (!edits) {
+ throw Errors.NoContent;
+ }
+
+ return edits.map((edit) => {
+ return {
+ start: compilerService.host.positionToLineCol(file, edit.span.start),
+ end: compilerService.host.positionToLineCol(file, ts.textSpanEnd(edit.span)),
+ newText: edit.newText ? edit.newText : ""
+ };
+ });
}
- formatOnKey(line: number, col: number, key: string, rawfile: string, cmd: string, reqSeq = 0) {
- var file = ts.normalizePath(rawfile);
+ formatOnKey(line: number, col: number, key: string, fileName: string): ServerProtocol.CodeEdit[] {
+ var file = ts.normalizePath(fileName);
+
var project = this.projectService.getProjectForFile(file);
- if (project) {
- var compilerService = project.compilerService;
- var pos = compilerService.host.lineColToPosition(file, line, col);
- var edits: ts.TextChange[];
- try {
- edits = compilerService.languageService.getFormattingEditsAfterKeystroke(file, pos, key,
- compilerService.formatCodeOptions);
- if ((key == "\n") && ((!edits) || (edits.length == 0) || allEditsBeforePos(edits, pos))) {
- // TODO: get these options from host
- var editorOptions: ts.EditorOptions = {
- IndentSize: 4,
- TabSize: 4,
- NewLineCharacter: "\n",
- ConvertTabsToSpaces: true,
- };
- var indentPosition = compilerService.languageService.getIndentationAtPosition(file, pos, editorOptions);
- var spaces = generateSpaces(indentPosition);
- if (indentPosition > 0) {
- edits.push({ span: ts.createTextSpanFromBounds(pos, pos), newText: spaces });
- }
- }
- }
- catch (err) {
- this.logError(err, cmd);
- edits = undefined;
- }
- if (edits) {
- var bakedEdits: ServerProtocol.CodeEdit[] = edits.map((edit) => {
- return {
- start: compilerService.host.positionToLineCol(file,
- edit.span.start),
- end: compilerService.host.positionToLineCol(file,
- ts.textSpanEnd(edit.span)),
- newText: edit.newText ? edit.newText : ""
- };
- });
- this.output(bakedEdits, CommandNames.Formatonkey, reqSeq);
- }
- else {
- this.output(undefined, CommandNames.Formatonkey, reqSeq, "no edits")
+ if (!project) {
+ throw Errors.NoProject;
+ }
+
+ var compilerService = project.compilerService;
+ var position = compilerService.host.lineColToPosition(file, line, col);
+ var edits = compilerService.languageService.getFormattingEditsAfterKeystroke(file, position, key,
+ compilerService.formatCodeOptions);
+ if ((key == "\n") && ((!edits) || (edits.length == 0) || allEditsBeforePos(edits, position))) {
+ // TODO: get these options from host
+ var editorOptions: ts.EditorOptions = {
+ IndentSize: 4,
+ TabSize: 4,
+ NewLineCharacter: "\n",
+ ConvertTabsToSpaces: true,
+ };
+ var indentPosition = compilerService.languageService.getIndentationAtPosition(file, position, editorOptions);
+ var spaces = generateSpaces(indentPosition);
+ if (indentPosition > 0) {
+ edits.push({ span: ts.createTextSpanFromBounds(position, position), newText: spaces });
}
}
+
+ if (!edits) {
+ throw Errors.NoContent;
+ }
+
+ return edits.map((edit) => {
+ return {
+ start: compilerService.host.positionToLineCol(file,
+ edit.span.start),
+ end: compilerService.host.positionToLineCol(file,
+ ts.textSpanEnd(edit.span)),
+ newText: edit.newText ? edit.newText : ""
+ };
+ });
}
- completions(line: number, col: number, prefix: string, rawfile: string, cmd: string, reqSeq = 0) {
+ completions(line: number, col: number, prefix: string, fileName: string): ServerProtocol.CompletionItem[] {
if (!prefix) {
prefix = "";
}
- var file = ts.normalizePath(rawfile);
+ var file = ts.normalizePath(fileName);
var project = this.projectService.getProjectForFile(file);
- var completions: ts.CompletionInfo = undefined;
- if (project) {
- var compilerService = project.compilerService;
- var pos = compilerService.host.lineColToPosition(file, line, col);
- if (pos >= 0) {
- try {
- completions = compilerService.languageService.getCompletionsAtPosition(file, pos);
- }
- catch (err) {
- this.logError(err, cmd);
- completions = undefined;
- }
- if (completions) {
- var compressedEntries: ServerProtocol.CompletionItem[] =
- completions.entries.reduce((accum: ts.CompletionEntryDetails[], entry: ts.CompletionEntry) => {
- if (entry.name.indexOf(prefix) == 0) {
- var protoEntry = {};
- protoEntry.name = entry.name;
- protoEntry.kind = entry.kind;
- if (entry.kindModifiers && (entry.kindModifiers.length > 0)) {
- protoEntry.kindModifiers = entry.kindModifiers;
- }
- var details = compilerService.languageService.getCompletionEntryDetails(file, pos, entry.name);
- if (details && (details.documentation) && (details.documentation.length > 0)) {
- protoEntry.documentation = details.documentation;
- }
- if (details && (details.displayParts) && (details.displayParts.length > 0)) {
- protoEntry.displayParts = details.documentation;
- }
- accum.push(protoEntry);
- }
- return accum;
- }, []);
- this.output(compressedEntries, CommandNames.Completions, reqSeq);
- }
- }
+ if (!project) {
+ throw Errors.NoProject;
}
- if (!completions) {
- this.output(undefined, CommandNames.Completions, reqSeq, "no completions");
- }
- }
- geterr(ms: number, files: string[]) {
- var checkList = files.reduce((accum: PendingErrorCheck[], filename: string) => {
- filename = ts.normalizePath(filename);
- var project = this.projectService.getProjectForFile(filename);
- if (project) {
- accum.push({ filename: filename, project: project });
+ var compilerService = project.compilerService;
+ var position = compilerService.host.lineColToPosition(file, line, col);
+
+ var completions = compilerService.languageService.getCompletionsAtPosition(file, position);
+ if (!completions) {
+ throw Errors.NoContent;
+ }
+
+ return completions.entries.reduce((accum: ts.CompletionEntryDetails[], entry: ts.CompletionEntry) => {
+ if (entry.name.indexOf(prefix) == 0) {
+ var protoEntry = {};
+ protoEntry.name = entry.name;
+ protoEntry.kind = entry.kind;
+ if (entry.kindModifiers && (entry.kindModifiers.length > 0)) {
+ protoEntry.kindModifiers = entry.kindModifiers;
+ }
+ var details = compilerService.languageService.getCompletionEntryDetails(file, position, entry.name);
+ if (details && (details.documentation) && (details.documentation.length > 0)) {
+ protoEntry.documentation = details.documentation;
+ }
+ if (details && (details.displayParts) && (details.displayParts.length > 0)) {
+ protoEntry.displayParts = details.documentation;
+ }
+ accum.push(protoEntry);
}
return accum;
}, []);
+ }
+
+ geterr(delay: number, fileNames: string[]) {
+ var checkList = fileNames.reduce((accum: PendingErrorCheck[], fileName: string) => {
+ fileName = ts.normalizePath(fileName);
+ var project = this.projectService.getProjectForFile(fileName);
+ if (project) {
+ accum.push({ fileName, project });
+ }
+ return accum;
+ }, []);
+
if (checkList.length > 0) {
- this.updateErrorCheck(checkList, this.changeSeq,(n) => n == this.changeSeq, ms)
+ this.updateErrorCheck(checkList, this.changeSeq,(n) => n == this.changeSeq, delay)
}
}
@@ -997,352 +684,165 @@ module ts.server {
}
}
- navto(searchTerm: string, rawfile: string, cmd: string, reqSeq = 0) {
- var file = ts.normalizePath(rawfile);
+ navto(searchTerm: string, fileName: string): ServerProtocol.NavtoItem[] {
+ var file = ts.normalizePath(fileName);
var project = this.projectService.getProjectForFile(file);
- if (project) {
- var compilerService = project.compilerService;
- var navItems: ts.NavigateToItem[];
- var cancellationToken = compilerService.host.getCancellationToken();
- if (this.pendingOperation) {
- cancellationToken.cancel();
- cancellationToken.reset();
- }
- try {
- this.pendingOperation = true;
- navItems = sortNavItems(compilerService.languageService.getNavigateToItems(searchTerm));
- }
- catch (err) {
- this.logError(err, cmd);
- navItems = undefined;
- }
- this.pendingOperation = false;
- if (navItems) {
- var bakedNavItems: ServerProtocol.NavtoItem[] = navItems.map((navItem) => {
- var start = compilerService.host.positionToLineCol(navItem.fileName,
- navItem.textSpan.start);
- var end = compilerService.host.positionToLineCol(navItem.fileName, ts.textSpanEnd(navItem.textSpan));
- this.abbreviate(start);
- var bakedItem: ServerProtocol.NavtoItem = {
- name: navItem.name,
- kind: navItem.kind,
- file: this.encodeFilename(navItem.fileName),
- start: start,
- end: end,
- };
- if (navItem.kindModifiers && (navItem.kindModifiers != "")) {
- bakedItem.kindModifiers = navItem.kindModifiers;
- }
- if (navItem.matchKind != 'none') {
- bakedItem.matchKind = navItem.matchKind;
- }
- if (navItem.containerName && (navItem.containerName.length > 0)) {
- bakedItem.containerName = navItem.containerName;
- }
- if (navItem.containerKind && (navItem.containerKind.length > 0)) {
- bakedItem.containerKind = navItem.containerKind;
- }
- this.abbreviate(bakedItem);
- return bakedItem;
- });
+ if (!project) {
+ throw Errors.NoProject;
+ }
- this.output(bakedNavItems, CommandNames.Navto, reqSeq);
- }
- else {
- this.output(undefined, CommandNames.Navto, reqSeq, "no nav items");
- }
- }
- else {
- this.output(undefined, CommandNames.Navto, reqSeq, "no nav items");
+ var compilerService = project.compilerService;
+ var navItems = sortNavItems(compilerService.languageService.getNavigateToItems(searchTerm));
+ if (!navItems) {
+ throw Errors.NoContent;
}
+
+ return navItems.map((navItem) => {
+ var start = compilerService.host.positionToLineCol(navItem.fileName, navItem.textSpan.start);
+ var end = compilerService.host.positionToLineCol(navItem.fileName, ts.textSpanEnd(navItem.textSpan));
+ var bakedItem: ServerProtocol.NavtoItem = {
+ name: navItem.name,
+ kind: navItem.kind,
+ file: this.encodeFilename(navItem.fileName),
+ start: start,
+ end: end,
+ };
+ if (navItem.kindModifiers && (navItem.kindModifiers != "")) {
+ bakedItem.kindModifiers = navItem.kindModifiers;
+ }
+ if (navItem.matchKind != 'none') {
+ bakedItem.matchKind = navItem.matchKind;
+ }
+ if (navItem.containerName && (navItem.containerName.length > 0)) {
+ bakedItem.containerName = navItem.containerName;
+ }
+ if (navItem.containerKind && (navItem.containerKind.length > 0)) {
+ bakedItem.containerKind = navItem.containerKind;
+ }
+ return bakedItem;
+ });
}
- getMatchingBrace(line: number, col: number, rawfile: string, reqSeq = 0) {
- var file = ts.normalizePath(rawfile);
+ getBraceMatching(line: number, col: number, fileName: string): ServerProtocol.TextSpan[] {
+ var file = ts.normalizePath(fileName);
+
var project = this.projectService.getProjectForFile(file);
- if (project) {
- var compilerService = project.compilerService;
- var pos = compilerService.host.lineColToPosition(file, line, col);
- var spans: ts.TextSpan[];
- try {
- spans = compilerService.languageService.getBraceMatchingAtPosition(file, pos);
- }
- catch (err) {
- this.logError(err, CommandNames.Brace);
- spans = undefined;
- }
- if (spans) {
- var bakedSpans: ServerProtocol.TextSpan[] = spans.map(span => ({
- start: span &&
- compilerService.host.positionToLineCol(file, span.start),
- end: span &&
- compilerService.host.positionToLineCol(file, span.start + span.length)
- }));
- this.output(bakedSpans, CommandNames.Brace, reqSeq);
- }
- else {
- this.output(undefined, CommandNames.Brace, reqSeq, "no matching braces");
- }
+ if (!project) {
+ throw Errors.NoProject;
}
- else {
- this.output(undefined, CommandNames.Brace, reqSeq, "no matching braces");
+
+ var compilerService = project.compilerService;
+ var position = compilerService.host.lineColToPosition(file, line, col);
+
+ var spans = compilerService.languageService.getBraceMatchingAtPosition(file, position);
+ if (!spans) {
+ throw Errors.NoContent;
}
+
+ return spans.map(span => ({
+ start: compilerService.host.positionToLineCol(file, span.start),
+ end: compilerService.host.positionToLineCol(file, span.start + span.length)
+ }));
}
- executeJSONcmd(cmd: string) {
+ onMessage(message: string) {
try {
- var req = JSON.parse(cmd);
- switch (req.command) {
+ var request = JSON.parse(message);
+ var response: any;
+ switch (request.command) {
case CommandNames.Definition: {
- var defArgs = req.arguments;
- this.goToDefinition(defArgs.line, defArgs.col, defArgs.file, req.seq);
+ var defArgs = request.arguments;
+ response = this.goToDefinition(defArgs.line, defArgs.col, defArgs.file);
break;
}
case CommandNames.References: {
- var refArgs = req.arguments;
- this.findReferences(refArgs.line, refArgs.col, refArgs.file, req.seq);
+ var refArgs = request.arguments;
+ response = this.findReferences(refArgs.line, refArgs.col, refArgs.file);
break;
}
case CommandNames.Rename: {
- var renameArgs = req.arguments;
- this.rename(renameArgs.line, renameArgs.col, renameArgs.file, req.seq);
- break;
- }
- case CommandNames.Type: {
- var typeArgs = req.arguments;
- this.goToType(typeArgs.line, typeArgs.col, typeArgs.file, req.seq);
+ var renameArgs = request.arguments;
+ response = this.rename(renameArgs.line, renameArgs.col, renameArgs.file);
break;
}
case CommandNames.Open: {
- var openArgs = req.arguments;
+ var openArgs = request.arguments;
this.openClientFile(openArgs.file);
break;
}
case CommandNames.Quickinfo: {
- var quickinfoArgs = req.arguments;
- this.quickInfo(quickinfoArgs.line, quickinfoArgs.col, quickinfoArgs.file, req.seq);
+ var quickinfoArgs = request.arguments;
+ response = this.quickInfo(quickinfoArgs.line, quickinfoArgs.col, quickinfoArgs.file);
break;
}
case CommandNames.Format: {
- var formatArgs = req.arguments;
- this.format(formatArgs.line, formatArgs.col, formatArgs.endLine, formatArgs.endCol, formatArgs.file,
- cmd, req.seq);
+ var formatArgs = request.arguments;
+ response = this.format(formatArgs.line, formatArgs.col, formatArgs.endLine, formatArgs.endCol, formatArgs.file);
break;
}
case CommandNames.Formatonkey: {
- var formatOnKeyArgs = req.arguments;
- this.formatOnKey(formatOnKeyArgs.line, formatOnKeyArgs.col, formatOnKeyArgs.key, formatOnKeyArgs.file,
- cmd, req.seq);
+ var formatOnKeyArgs = request.arguments;
+ response = this.formatOnKey(formatOnKeyArgs.line, formatOnKeyArgs.col, formatOnKeyArgs.key, formatOnKeyArgs.file);
break;
}
case CommandNames.Completions: {
- var completionsArgs = req.arguments;
- this.completions(req.arguments.line, req.arguments.col, completionsArgs.prefix, req.arguments.file,
- cmd, req.seq);
+ var completionsArgs = request.arguments;
+ response = this.completions(request.arguments.line, request.arguments.col, completionsArgs.prefix, request.arguments.file);
break;
}
case CommandNames.Geterr: {
- var geterrArgs = req.arguments;
- this.geterr(geterrArgs.delay, geterrArgs.files);
+ var geterrArgs = request.arguments;
+ response = this.geterr(geterrArgs.delay, geterrArgs.files);
break;
}
case CommandNames.Change: {
- var changeArgs = req.arguments;
+ var changeArgs = request.arguments;
this.change(changeArgs.line, changeArgs.col, changeArgs.deleteLen, changeArgs.insertString,
changeArgs.file);
break;
}
case CommandNames.Reload: {
- var reloadArgs = req.arguments;
- this.reload(reloadArgs.file, reloadArgs.tmpfile, req.seq);
+ var reloadArgs = request.arguments;
+ this.reload(reloadArgs.file, reloadArgs.tmpfile, request.seq);
break;
}
case CommandNames.Saveto: {
- var savetoArgs = req.arguments;
+ var savetoArgs = request.arguments;
this.saveToTmp(savetoArgs.file, savetoArgs.tmpfile);
break;
}
case CommandNames.Close: {
- var closeArgs = req.arguments;
+ var closeArgs = request.arguments;
this.closeClientFile(closeArgs.file);
break;
}
case CommandNames.Navto: {
- var navtoArgs = req.arguments;
- this.navto(navtoArgs.searchTerm, navtoArgs.file, cmd, req.seq);
- break;
- }
- case CommandNames.Abbrev: {
- this.sendAbbrev();
+ var navtoArgs = request.arguments;
+ response = this.navto(navtoArgs.searchTerm, navtoArgs.file);
break;
}
case CommandNames.Brace: {
- var defArgs = req.arguments;
- this.getMatchingBrace(defArgs.line, defArgs.col, defArgs.file, req.seq);
+ var braceArguments = request.arguments;
+ response = this.getBraceMatching(braceArguments.line, braceArguments.col, braceArguments.file);
break;
}
default: {
- this.projectService.log("Unrecognized JSON command: " + cmd);
+ this.projectService.log("Unrecognized JSON command: " + message);
+ this.output(undefined, CommandNames.Unknown, request.seq, "Unrecognized JSON command: " + request.command);
break;
}
}
+
+ if (response) {
+ this.output(response, request.command, request.seq);
+ }
+
} catch (err) {
- this.logError(err, cmd);
- }
- }
-
- sendAbbrev(reqSeq = 0) {
- if (!this.fetchedAbbrev) {
- this.output(this.abbrevTable, CommandNames.Abbrev, reqSeq);
- }
- this.fetchedAbbrev = true;
- }
-
- executeCmd(cmd: string) {
- var line: number, col: number, file: string;
- var m: string[];
-
- try {
- if (m = cmd.match(/^dbg start$/)) {
- this.debugSession = new JsDebugSession(this.host.getDebuggerClient());
+ if (err instanceof OperationCanceledException) {
+ // Handle cancellation exceptions
}
- else if (m = cmd.match(/^dbg cont$/)) {
- if (this.debugSession) {
- this.debugSession.cont((err, body, res) => {
- });
- }
- }
- else if (m = cmd.match(/^dbg src$/)) {
- if (this.debugSession) {
- this.debugSession.listSrc();
- }
- }
- else if (m = cmd.match(/^dbg brk (\d+) (.*)$/)) {
- line = parseInt(m[1]);
- file = ts.normalizePath(m[2]);
- if (this.debugSession) {
- this.debugSession.setBreakpointOnLine(line, file);
- }
- }
- else if (m = cmd.match(/^dbg eval (.*)$/)) {
- var code = m[1];
- if (this.debugSession) {
- this.debugSession.evaluate(code);
- }
- }
- else if (m = cmd.match(/^definition (\d+) (\d+) (.*)$/)) {
- line = parseInt(m[1]);
- col = parseInt(m[2]);
- file = m[3];
- this.goToDefinition(line, col, file);
- }
- else if (m = cmd.match(/^rename (\d+) (\d+) (.*)$/)) {
- line = parseInt(m[1]);
- col = parseInt(m[2]);
- file = m[3];
- this.rename(line, col, file);
- }
- else if (m = cmd.match(/^type (\d+) (\d+) (.*)$/)) {
- line = parseInt(m[1]);
- col = parseInt(m[2]);
- file = m[3];
- this.goToType(line, col, file);
- }
- else if (m = cmd.match(/^open (.*)$/)) {
- file = m[1];
- this.openClientFile(file);
- }
- else if (m = cmd.match(/^references (\d+) (\d+) (.*)$/)) {
- line = parseInt(m[1]);
- col = parseInt(m[2]);
- file = m[3];
- this.findReferences(line, col, file);
- }
- else if (m = cmd.match(/^quickinfo (\d+) (\d+) (.*)$/)) {
- line = parseInt(m[1]);
- col = parseInt(m[2]);
- file = m[3];
- this.quickInfo(line, col, file);
- }
- else if (m = cmd.match(/^format (\d+) (\d+) (\d+) (\d+) (.*)$/)) {
- // format line col endLine endCol file
- line = parseInt(m[1]);
- col = parseInt(m[2]);
- var endLine = parseInt(m[3]);
- var endCol = parseInt(m[4]);
- file = m[5];
- this.format(line, col, endLine, endCol, file, cmd);
- }
- else if (m = cmd.match(/^formatonkey (\d+) (\d+) (\{\".*\"\})\s* (.*)$/)) {
- line = parseInt(m[1]);
- col = parseInt(m[2]);
- var key = JSON.parse(m[3].substring(1, m[3].length - 1));
- file = m[4];
- this.formatOnKey(line, col, key, file, cmd);
- }
- else if (m = cmd.match(/^completions (\d+) (\d+) (\{.*\})?\s*(.*)$/)) {
- line = parseInt(m[1]);
- col = parseInt(m[2]);
- var prefix = "";
- file = m[4];
- if (m[3]) {
- prefix = m[3].substring(1, m[3].length - 1);
- }
- this.completions(line, col, prefix, file, cmd);
- }
- else if (m = cmd.match(/^geterr (\d+) (.*)$/)) {
- var ms = parseInt(m[1]);
- var rawFiles = m[2];
- this.geterr(ms, rawFiles.split(';'));
- }
- else if (m = cmd.match(/^change (\d+) (\d+) (\d+) (\d+) (\{\".*\"\})?\s*(.*)$/)) {
- line = parseInt(m[1]);
- col = parseInt(m[2]);
- var deleteLen = parseInt(m[3]);
- var insertLen = parseInt(m[4]);
- var insertString: string;
- if (insertLen) {
- insertString = JSON.parse(m[5].substring(1, m[5].length - 1));
- }
- file = m[6];
- this.change(line, col, deleteLen, insertString, file);
- }
- else if (m = cmd.match(/^reload (.*) from (.*)$/)) {
- this.reload(m[1], m[2]);
- }
- // TODO: change this to saveto
- else if (m = cmd.match(/^save (.*) to (.*)$/)) {
- this.saveToTmp(m[1], m[2]);
- }
- else if (m = cmd.match(/^close (.*)$/)) {
- this.closeClientFile(m[1]);
- }
- else if (m = cmd.match(/^navto (\{.*\}) (.*)$/)) {
- var searchTerm = m[1];
- searchTerm = searchTerm.substring(1, searchTerm.length - 1);
- this.navto(searchTerm, m[2], cmd);
- }
- else if (m = cmd.match(/^navbar (.*)$/)) {
- this.navbar(m[1]);
- }
- else if (m = cmd.match(/^abbrev/)) {
- this.sendAbbrev();
- }
- else if (m = cmd.match(/^pretty/)) {
- this.prettyJSON = true;
- }
- else if (m = cmd.match(/^printproj/)) {
- this.projectService.printProjects();
- }
- else if (m = cmd.match(/^fileproj (.*)$/)) {
- file = ts.normalizePath(m[1]);
- this.projectService.printProjectsForFile(file);
- }
- else {
- this.output(undefined, CommandNames.Unknown, 0, "Unrecognized command " + cmd);
- }
- } catch (err) {
- this.logError(err, cmd);
+ this.logError(err, message);
+ this.output(undefined, request ? request.command : CommandNames.Unknown, request ? request.seq : 0, "Error processing request. " + err.message);
}
}
}
diff --git a/src/server/protodef.d.ts b/src/server/protodef.d.ts
index 73c3940415..95e671f561 100644
--- a/src/server/protodef.d.ts
+++ b/src/server/protodef.d.ts
@@ -188,18 +188,6 @@ declare module ServerProtocol {
}
}
- /**
- Type request; value of command field is "type". Return response
- giving the code locations that define the type of the symbol
- found in file at location line, col.
- */
- export interface TypeRequest extends CodeLocationRequest {
- }
-
- export interface TypeResponse extends Response {
- body?: CodeSpan[];
- }
-
/**
Open request; value of command field is "open". Notify the
server that the client has file open. The server will not
@@ -483,8 +471,8 @@ declare module ServerProtocol {
kindModifiers?: string;
/**
The file in which the symbol is found; the value of this
- field will always be a string unless the client opts-in to
- file encoding by sending the "abbrev" request.
+ field will always be a string, number of a mapping between
+ a string and a number.
*/
file: EncodedFile;
/** The location within file at which the symbol is found*/
@@ -528,27 +516,6 @@ declare module ServerProtocol {
arguments: ChangeRequestArgs;
}
- /*
- The following messages describe an OPTIONAL compression scheme
- that clients can choose to use. If a client does not opt-in to
- this scheme by sending an "abbrev" request, the server will not
- compress messages.
- */
-
- /**
- Abbrev request message; value of command field is "abbrev".
- Server returns an array of mappings from common message field
- names and common message field string values to shortened
- versions of these strings. Once a client opts-in by requesting
- the abbreviations, the server may send responses whose field
- names are shortened. The server may also send file names as
- instances of AssignFileId, or as file ids, if the corresponding
- id assignment had been previously returned. Currently, only
- responses to the "navto" request can be encoded.
- */
- export interface AbbrevRequest extends Request {
- }
-
/**
If an object of this type is returned in place of a string as
the value of a file field in a response message, add the
@@ -572,25 +539,6 @@ declare module ServerProtocol {
*/
export type EncodedFile = number | IdFile | string;
- /**
- Response to abbrev opt-in request message. This is a map of
- string field names and common string field values to shortened
- strings. Once the server sends this response, it will assume
- that it can use the shortened field names and field values. In
- addition, the server will assume it can assign ids to file
- names by returning an AssignFileId instance in place of a file
- name. Once an AssignFileId instance is returned, the server
- may send the file id (a number) in place of the file name.
- */
- export interface AbbrevResponse extends Response {
- body?: {
- /** Map from full string (either field name or string
- field value) to shortened string */
- [fullString: string]: string;
- }
- }
-
-
/** Response to "brace" request. */
export interface BraceResponse extends Response {
body?: TextSpan[];
diff --git a/src/server/server.ts b/src/server/server.ts
index ec0ee293ce..9f06e9b027 100644
--- a/src/server/server.ts
+++ b/src/server/server.ts
@@ -1,18 +1,18 @@
-///
-///
-
-module ts.server {
+///
+///
+
+module ts.server {
var nodeproto: typeof NodeJS._debugger = require('_debugger');
var readline: NodeJS.ReadLine = require('readline');
- var path: NodeJS.Path = require('path');
+ var path: NodeJS.Path = require('path');
var fs: typeof NodeJS.fs = require('fs');
-
+
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false,
- });
-
+ });
+
class Logger implements ts.server.Logger {
fd = -1;
seq = 0;
@@ -71,17 +71,17 @@ module ts.server {
}
}
}
-
- class IOSession extends Session {
- protocol: NodeJS._debugger.Protocol;
-
- constructor(host: ServerHost, logger: ts.server.Logger, useProtocol: boolean, prettyJSON: boolean) {
- super(host, logger, useProtocol, prettyJSON);
+
+ class IOSession extends Session {
+ protocol: NodeJS._debugger.Protocol;
+
+ constructor(host: ServerHost, logger: ts.server.Logger, useProtocol: boolean, prettyJSON: boolean) {
+ super(host, logger, useProtocol, prettyJSON);
if (useProtocol) {
this.initProtocol();
- }
- }
-
+ }
+ }
+
initProtocol() {
this.protocol = new nodeproto.Protocol();
// note: onResponse was named by nodejs authors; we are re-purposing the Protocol
@@ -89,25 +89,16 @@ module ts.server {
this.protocol.onResponse = (pkt) => {
this.handleRequest(pkt);
};
- }
-
+ }
+
handleRequest(req: NodeJS._debugger.Packet) {
this.projectService.log("Got JSON msg:\n" + req.raw);
- }
-
+ }
+
listen() {
rl.on('line',(input: string) => {
- var cmd = input.trim();
- if (cmd.indexOf("{") == 0) {
- // assumption is JSON on single line
- // plan is to also carry this protocol
- // over tcp, in which case JSON would
- // have a Content-Length header
- this.executeJSONcmd(cmd);
- }
- else {
- this.executeCmd(cmd);
- }
+ var message = input.trim();
+ this.onMessage(message);
});
rl.on('close',() => {
@@ -115,20 +106,13 @@ module ts.server {
this.projectService.log("Exiting...");
process.exit(0);
});
- }
- }
-
+ }
+ }
+
// This places log file in the directory containing editorServices.js
// TODO: check that this location is writable
- var logger = new Logger(__dirname + "/.log" + process.pid.toString());
-
- var host: ServerHost = ts.sys;
-
- // Wire the debugging interface
- if (!host.getDebuggerClient) {
- host.getDebuggerClient = () => new nodeproto.Client();
- }
-
- // Start listening
- new IOSession(host, logger, /* useProtocol */ true, /* prettyJSON */ false).listen();
+ var logger = new Logger(__dirname + "/.log" + process.pid.toString());
+
+ // Start listening
+ new IOSession(ts.sys, logger, /* useProtocol */ true, /* prettyJSON */ false).listen();
}
\ No newline at end of file