diff --git a/src/server/client.ts b/src/server/client.ts
index caeab6e3f3cf5..251b626eed5f1 100644
--- a/src/server/client.ts
+++ b/src/server/client.ts
@@ -1,5 +1,5 @@
///
-
+
namespace ts.server {
export interface SessionClientHost extends LanguageServiceHost {
@@ -25,23 +25,23 @@ namespace ts.server {
private lineMaps: ts.Map = {};
private messages: string[] = [];
private lastRenameEntry: RenameEntry;
-
+
constructor(private host: SessionClientHost) {
}
- public onMessage(message: string): void {
+ public onMessage(message: string): void {
this.messages.push(message);
}
- private writeMessage(message: string): void {
+ private writeMessage(message: string): void {
this.host.writeMessage(message);
}
- private getLineMap(fileName: string): number[] {
+ 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()));
+ lineMap = this.lineMaps[fileName] = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()));
}
return lineMap;
}
@@ -82,34 +82,29 @@ namespace ts.server {
}
private processResponse(request: protocol.Request): T {
- var lastMessage = this.messages.shift();
- Debug.assert(!!lastMessage, "Did not receive 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) {
- throw new Error("Malformed response: Failed to parse server response: " + lastMessage + ". \r\n Error details: " + e.message);
+ let foundResponseMessage = false;
+ let lastMessage: string;
+ let response: T;
+ while (!foundResponseMessage) {
+ lastMessage = this.messages.shift();
+ Debug.assert(!!lastMessage, "Did not receive any responses.");
+ const responseBody = processMessage(lastMessage);
+ try {
+ response = JSON.parse(responseBody);
+ // the server may emit events before emitting the response. We
+ // want to ignore these events for testing purpose.
+ if (response.type === "response") {
+ foundResponseMessage = true;
+ }
+ }
+ catch (e) {
+ throw new Error("Malformed response: Failed to parse server response: " + lastMessage + ". \r\n Error details: " + e.message);
+ }
}
// verify the sequence numbers
Debug.assert(response.request_seq === request.seq, "Malformed response: response sequence number did not match request sequence number.");
-
+
// unmarshal errors
if (!response.success) {
throw new Error("Error " + response.message);
@@ -118,9 +113,27 @@ namespace ts.server {
Debug.assert(!!response.body, "Malformed response: Unexpected empty response body.");
return response;
+
+ function processMessage(message: string) {
+ // Read the content length
+ const contentLengthPrefix = "Content-Length: ";
+ const lines = message.split("\r\n");
+ Debug.assert(lines.length >= 2, "Malformed response: Expected 3 lines in the response.");
+
+ const contentLengthText = lines[0];
+ Debug.assert(contentLengthText.indexOf(contentLengthPrefix) === 0, "Malformed response: Response text did not contain content-length header.");
+ const contentLength = parseInt(contentLengthText.substring(contentLengthPrefix.length));
+
+ // Read the body
+ const responseBody = lines[2];
+
+ // Verify content length
+ Debug.assert(responseBody.length + 1 === contentLength, "Malformed response: Content length did not match the response's body length.");
+ return responseBody;
+ }
}
- openFile(fileName: string, content?: string, scriptKindName?: "TS" | "JS" | "TSX" | "JSX"): void {
+ openFile(fileName: string, content?: string, scriptKindName?: "TS" | "JS" | "TSX" | "JSX"): void {
var args: protocol.OpenRequestArgs = { file: fileName, fileContent: content, scriptKindName };
this.processRequest(CommandNames.Open, args);
}
@@ -186,7 +199,7 @@ namespace ts.server {
fileNames: response.body.fileNames
};
}
-
+
getCompletionsAtPosition(fileName: string, position: number): CompletionInfo {
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
var args: protocol.CompletionsRequestArgs = {
@@ -199,13 +212,13 @@ namespace ts.server {
var request = this.processRequest(CommandNames.Completions, args);
var response = this.processResponse(request);
- return {
+ return {
isMemberCompletion: false,
isNewIdentifierLocation: false,
entries: response.body
};
}
-
+
getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
var args: protocol.CompletionDetailsRequestArgs = {
@@ -234,7 +247,7 @@ namespace ts.server {
var fileName = entry.file;
var start = this.lineOffsetToPosition(fileName, entry.start);
var end = this.lineOffsetToPosition(fileName, entry.end);
-
+
return {
name: entry.name,
containerName: entry.containerName || "",
@@ -264,7 +277,7 @@ namespace ts.server {
var request = this.processRequest(CommandNames.Format, args);
var response = this.processResponse(request);
- return response.body.map(entry=> this.convertCodeEditsToTextChange(fileName, entry));
+ return response.body.map(entry => this.convertCodeEditsToTextChange(fileName, entry));
}
getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): ts.TextChange[] {
@@ -284,7 +297,7 @@ namespace ts.server {
var request = this.processRequest(CommandNames.Formatonkey, args);
var response = this.processResponse(request);
- return response.body.map(entry=> this.convertCodeEditsToTextChange(fileName, entry));
+ return response.body.map(entry => this.convertCodeEditsToTextChange(fileName, entry));
}
getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] {
@@ -339,7 +352,7 @@ namespace ts.server {
});
}
- findReferences(fileName: string, position: number): ReferencedSymbol[]{
+ findReferences(fileName: string, position: number): ReferencedSymbol[] {
// Not yet implemented.
return [];
}
@@ -444,7 +457,7 @@ namespace ts.server {
text: item.text,
kind: item.kind,
kindModifiers: item.kindModifiers || "",
- spans: item.spans.map(span=> createTextSpanFromBounds(this.lineOffsetToPosition(fileName, span.start), this.lineOffsetToPosition(fileName, span.end))),
+ spans: item.spans.map(span => createTextSpanFromBounds(this.lineOffsetToPosition(fileName, span.start), this.lineOffsetToPosition(fileName, span.end))),
childItems: this.decodeNavigationBarItems(item.childItems, fileName),
indent: 0,
bolded: false,
@@ -478,10 +491,10 @@ namespace ts.server {
line: lineOffset.line,
offset: lineOffset.offset
};
-
+
var request = this.processRequest(CommandNames.SignatureHelp, args);
var response = this.processResponse(request);
-
+
if (!response.body) {
return undefined;
}
@@ -490,7 +503,7 @@ namespace ts.server {
var span = helpItems.applicableSpan;
var start = this.lineOffsetToPosition(fileName, span.start);
var end = this.lineOffsetToPosition(fileName, span.end);
-
+
var result: SignatureHelpItems = {
items: helpItems.items,
applicableSpan: {
@@ -499,7 +512,7 @@ namespace ts.server {
},
selectedItemIndex: helpItems.selectedItemIndex,
argumentIndex: helpItems.argumentIndex,
- argumentCount: helpItems.argumentCount,
+ argumentCount: helpItems.argumentCount,
}
return result;
}
@@ -561,15 +574,15 @@ namespace ts.server {
}
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[] {
- throw new Error("Not Implemented Yet.");
+ throw new Error("Not Implemented Yet.");
}
-
+
getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion {
- throw new Error("Not Implemented Yet.");
+ throw new Error("Not Implemented Yet.");
}
isValidBraceCompletionAtPostion(fileName: string, position: number, openingBrace: number): boolean {
- throw new Error("Not Implemented Yet.");
+ throw new Error("Not Implemented Yet.");
}
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[] {
diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts
index 77908779d9fd7..62c8bf3ea1c0d 100644
--- a/src/server/editorServices.ts
+++ b/src/server/editorServices.ts
@@ -1122,8 +1122,13 @@ namespace ts.server {
return { configFileName, configFileErrors: configResult.errors };
}
else {
+ // even if opening config file was successful, it could still
+ // contain errors that were tolerated.
this.log("Opened configuration file " + configFileName, "Info");
this.configuredProjects.push(configResult.project);
+ if (configResult.errors && configResult.errors.length > 0) {
+ return { configFileName, configFileErrors: configResult.errors };
+ }
}
}
else {
@@ -1261,14 +1266,14 @@ namespace ts.server {
}
else {
const project = this.createProject(configFilename, projectOptions);
+ let errors: Diagnostic[];
for (const rootFilename of projectOptions.files) {
if (this.host.fileExists(rootFilename)) {
const info = this.openFile(rootFilename, /*openedByClient*/ clientFileName == rootFilename);
project.addRoot(info);
}
else {
- const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, rootFilename);
- return { success: false, errors: [error] };
+ (errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.File_0_not_found, rootFilename));
}
}
project.finishGraph();
@@ -1279,7 +1284,7 @@ namespace ts.server {
path => this.directoryWatchedForSourceFilesChanged(project, path),
/*recursive*/ true
);
- return { success: true, project: project };
+ return { success: true, project: project, errors };
}
}
@@ -1295,7 +1300,7 @@ namespace ts.server {
}
else {
const oldFileNames = project.compilerService.host.roots.map(info => info.fileName);
- const newFileNames = projectOptions.files;
+ const newFileNames = ts.filter(projectOptions.files, f => this.host.fileExists(f));
const fileNamesToRemove = oldFileNames.filter(f => newFileNames.indexOf(f) < 0);
const fileNamesToAdd = newFileNames.filter(f => oldFileNames.indexOf(f) < 0);
diff --git a/tests/cases/fourslash/server/projectWithNonExistentFiles.ts b/tests/cases/fourslash/server/projectWithNonExistentFiles.ts
new file mode 100644
index 0000000000000..ceba136bf964e
--- /dev/null
+++ b/tests/cases/fourslash/server/projectWithNonExistentFiles.ts
@@ -0,0 +1,13 @@
+///
+
+// @Filename: a.ts
+////export var test = "test String"
+
+// @Filename: b.ts
+////export var test2 = "test String"
+
+// @Filename: tsconfig.json
+////{ "files": ["a.ts", "c.ts", "b.ts"] }
+
+goTo.file("a.ts");
+verify.ProjectInfo(["lib.d.ts", "a.ts", "b.ts"])