diff --git a/src/compiler/core.ts b/src/compiler/core.ts
index 77cc379acc2..cab5c21ad90 100644
--- a/src/compiler/core.ts
+++ b/src/compiler/core.ts
@@ -1,5 +1,6 @@
///
///
+///
/* @internal */
diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts
index a7437e34599..813ea189157 100644
--- a/src/harness/unittests/tsserverProjectSystem.ts
+++ b/src/harness/unittests/tsserverProjectSystem.ts
@@ -36,7 +36,7 @@ namespace ts {
}
safeFileList = "";
- postInstallActions: (( map: (t: string[]) => string[]) => void)[] = [];
+ postInstallActions: ((map: (t: string[]) => string[]) => void)[] = [];
runPostInstallActions(map: (t: string[]) => string[]) {
for (const f of this.postInstallActions) {
@@ -281,7 +281,7 @@ namespace ts {
private timeoutCallbacks = new Callbacks();
private immediateCallbacks = new Callbacks();
- readonly watchedDirectories = createMap<{ cb: DirectoryWatcherCallback, recursive: boolean }[]>();
+ readonly watchedDirectories = createMap<{ cb: DirectoryWatcherCallback, recursive: boolean }[]>();
readonly watchedFiles = createMap();
private filesOrFolders: FileOrFolder[];
@@ -2011,7 +2011,7 @@ namespace ts {
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const p = projectService.configuredProjects[0];
- checkProjectActualFiles(p, [ file1.path ]);
+ checkProjectActualFiles(p, [file1.path]);
assert(host.fileExists(combinePaths(installer.cachePath, "tsd.json")));
@@ -2021,10 +2021,10 @@ namespace ts {
return ["jquery/jquery.d.ts"];
});
checkNumberOfProjects(projectService, { configuredProjects: 1 });
- checkProjectActualFiles(p, [ file1.path, jquery.path ]);
+ checkProjectActualFiles(p, [file1.path, jquery.path]);
});
- it ("inferred project (tsd installed)", () => {
+ it("inferred project (tsd installed)", () => {
const file1 = {
path: "/a/b/app.js",
content: ""
@@ -2051,7 +2051,7 @@ namespace ts {
checkNumberOfProjects(projectService, { inferredProjects: 1 });
const p = projectService.inferredProjects[0];
- checkProjectActualFiles(p, [ file1.path ]);
+ checkProjectActualFiles(p, [file1.path]);
assert(host.fileExists(combinePaths(installer.cachePath, "tsd.json")));
@@ -2061,10 +2061,10 @@ namespace ts {
return ["jquery/jquery.d.ts"];
});
checkNumberOfProjects(projectService, { inferredProjects: 1 });
- checkProjectActualFiles(p, [ file1.path, jquery.path ]);
+ checkProjectActualFiles(p, [file1.path, jquery.path]);
});
- it ("external project - no typing options, no .d.ts/js files", () => {
+ it("external project - no typing options, no .d.ts/js files", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
@@ -2091,7 +2091,7 @@ namespace ts {
projectService.checkNumberOfProjects({ externalProjects: 1 });
});
- it ("external project - no autoDiscovery in typing options, no .d.ts/js files", () => {
+ it("external project - no autoDiscovery in typing options, no .d.ts/js files", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
@@ -2119,7 +2119,7 @@ namespace ts {
projectService.checkNumberOfProjects({ externalProjects: 1 });
});
- it ("external project - autoDiscovery = true, no .d.ts/js files", () => {
+ it("external project - autoDiscovery = true, no .d.ts/js files", () => {
const file1 = {
path: "/a/b/app.ts",
content: ""
@@ -2156,4 +2156,209 @@ namespace ts {
assert.isTrue(runTsdIsCalled, "expected 'runTsdIsCalled' to be true");
});
});
+
+ describe("Project errors", () => {
+ function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: string[]) {
+ assert.isTrue(projectFiles !== undefined, "missing project files");
+ const errors = projectFiles.projectErrors;
+ assert.equal(errors ? errors.length : 0, expectedErrors.length, `expected ${expectedErrors.length} error in the list`);
+ if (expectedErrors.length) {
+ for (let i = 0; i < errors.length; i++) {
+ const actualMessage = flattenDiagnosticMessageText(errors[i].messageText, "\n");
+ const expectedMessage = expectedErrors[i];
+ assert.equal(actualMessage, expectedMessage, "error message does not match");
+ }
+ }
+ }
+
+ it("external project - diagnostics for missing files", () => {
+ const file1 = {
+ path: "/a/b/app.ts",
+ content: ""
+ };
+ const file2 = {
+ path: "/a/b/lib.ts",
+ content: ""
+ };
+ // only file1 exists - expect error
+ const host = createServerHost([file1]);
+ const projectService = createProjectService(host);
+ const projectFileName = "/a/b/test.csproj";
+
+ {
+ projectService.openExternalProject({
+ projectFileName,
+ options: {},
+ rootFiles: toExternalFiles([file1.path, file2.path])
+ });
+
+ projectService.checkNumberOfProjects({ externalProjects: 1 });
+ const knownProjects = projectService.synchronizeProjectList([]);
+ checkProjectErrors(knownProjects[0], ["File '/a/b/lib.ts' not found."]);
+ }
+ // only file2 exists - expect error
+ host.reloadFS([file2]);
+ {
+ projectService.openExternalProject({
+ projectFileName,
+ options: {},
+ rootFiles: toExternalFiles([file1.path, file2.path])
+ });
+ projectService.checkNumberOfProjects({ externalProjects: 1 });
+ const knownProjects = projectService.synchronizeProjectList([]);
+ checkProjectErrors(knownProjects[0], ["File '/a/b/app.ts' not found."]);
+ }
+
+ // both files exist - expect no errors
+ host.reloadFS([file1, file2]);
+ {
+ projectService.openExternalProject({
+ projectFileName,
+ options: {},
+ rootFiles: toExternalFiles([file1.path, file2.path])
+ });
+
+ projectService.checkNumberOfProjects({ externalProjects: 1 });
+ const knownProjects = projectService.synchronizeProjectList([]);
+ checkProjectErrors(knownProjects[0], []);
+ }
+ });
+
+ it("configured projects - diagnostics for missing files", () => {
+ const file1 = {
+ path: "/a/b/app.ts",
+ content: ""
+ };
+ const file2 = {
+ path: "/a/b/lib.ts",
+ content: ""
+ };
+ const config = {
+ path: "/a/b/tsconfig.json",
+ content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
+ };
+ const host = createServerHost([file1, config]);
+ const projectService = createProjectService(host);
+
+ projectService.openClientFile(file1.path);
+ projectService.checkNumberOfProjects({ configuredProjects: 1 });
+ checkProjectErrors(projectService.synchronizeProjectList([])[0], ["File '/a/b/lib.ts' not found."]);
+
+ host.reloadFS([file1, file2, config]);
+
+ projectService.openClientFile(file1.path);
+ projectService.checkNumberOfProjects({ configuredProjects: 1 });
+ checkProjectErrors(projectService.synchronizeProjectList([])[0], []);
+ });
+
+ it("configured projects - diagnostics for corrupted config 1", () => {
+ const file1 = {
+ path: "/a/b/app.ts",
+ content: ""
+ };
+ const file2 = {
+ path: "/a/b/lib.ts",
+ content: ""
+ };
+ const correctConfig = {
+ path: "/a/b/tsconfig.json",
+ content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
+ };
+ const corruptedConfig = {
+ path: correctConfig.path,
+ content: correctConfig.content.substr(1)
+ };
+ const host = createServerHost([file1, file2, corruptedConfig]);
+ const projectService = createProjectService(host);
+
+ projectService.openClientFile(file1.path);
+ {
+ projectService.checkNumberOfProjects({ inferredProjects: 1, configuredProjects: 1 });
+ const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
+ assert.isTrue(configuredProject !== undefined, "should find configured project");
+ checkProjectErrors(configuredProject, [`Failed to parse file \'/a/b/tsconfig.json\': Unexpected token : in JSON at position 7.`]);
+ }
+ // fix config and trigger watcher
+ host.reloadFS([file1, file2, correctConfig]);
+ host.triggerFileWatcherCallback(correctConfig.path, /*false*/);
+ {
+ projectService.checkNumberOfProjects({ configuredProjects: 1 });
+ const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
+ assert.isTrue(configuredProject !== undefined, "should find configured project");
+ checkProjectErrors(configuredProject, []);
+ }
+ });
+
+ it("configured projects - diagnostics for corrupted config 2", () => {
+ const file1 = {
+ path: "/a/b/app.ts",
+ content: ""
+ };
+ const file2 = {
+ path: "/a/b/lib.ts",
+ content: ""
+ };
+ const correctConfig = {
+ path: "/a/b/tsconfig.json",
+ content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
+ };
+ const corruptedConfig = {
+ path: correctConfig.path,
+ content: correctConfig.content.substr(1)
+ };
+ const host = createServerHost([file1, file2, correctConfig]);
+ const projectService = createProjectService(host);
+
+ projectService.openClientFile(file1.path);
+ {
+ projectService.checkNumberOfProjects({ configuredProjects: 1 });
+ const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
+ assert.isTrue(configuredProject !== undefined, "should find configured project");
+ checkProjectErrors(configuredProject, []);
+ }
+ // fix config and trigger watcher
+ host.reloadFS([file1, file2, corruptedConfig]);
+ host.triggerFileWatcherCallback(corruptedConfig.path, /*false*/);
+ {
+ projectService.checkNumberOfProjects({ inferredProjects: 1, configuredProjects: 1 });
+ const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
+ assert.isTrue(configuredProject !== undefined, "should find configured project");
+ checkProjectErrors(configuredProject, [`Failed to parse file \'/a/b/tsconfig.json\': Unexpected token : in JSON at position 7.`]);
+ }
+ });
+ });
+
+ describe("Proper errors", () => {
+ it("document is not contained in project", () => {
+ const file1 = {
+ path: "/a/b/app.ts",
+ content: ""
+ };
+ const corruptedConfig = {
+ path: "/a/b/tsconfig.json",
+ content: "{"
+ };
+ const host = createServerHost([file1, corruptedConfig]);
+ const projectService = createProjectService(host);
+
+ projectService.openClientFile(file1.path);
+ projectService.checkNumberOfProjects({ inferredProjects: 1, configuredProjects: 1 });
+
+ const project = projectService.findProject(corruptedConfig.path);
+ let expectedMessage: string;
+ try {
+ server.Errors.ThrowProjectDoesNotContainDocument(file1.path, project);
+ assert(false, "should not get there");
+ }
+ catch (e) {
+ expectedMessage = (e).message;
+ }
+ try {
+ project.getScriptInfo(file1.path);
+ }
+ catch (e) {
+ assert.equal((e).message, expectedMessage, "Unexpected error");
+ }
+ });
+ });
}
\ No newline at end of file
diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts
index ba2d4386a75..267165f2657 100644
--- a/src/server/editorServices.ts
+++ b/src/server/editorServices.ts
@@ -30,7 +30,7 @@ namespace ts.server {
interface ConfigFileConversionResult {
success: boolean;
- errors?: Diagnostic[];
+ configFileErrors?: Diagnostic[];
projectOptions?: ProjectOptions;
}
@@ -73,6 +73,10 @@ namespace ts.server {
}
}
+ function createFileNotFoundDiagnostic(fileName: string) {
+ return createCompilerDiagnostic(Diagnostics.File_0_not_found, fileName);
+ }
+
/**
* TODO: enforce invariants:
* - script info can be never migrate to state - root file in inferred project, this is only a starting point
@@ -655,7 +659,7 @@ namespace ts.server {
const configObj = parseConfigFileTextToJson(configFilename, this.host.readFile(configFilename));
if (configObj.error) {
- return { success: false, errors: [configObj.error] };
+ return { success: false, configFileErrors: [configObj.error] };
}
const parsedCommandLine = parseJsonConfigFileContent(
@@ -668,12 +672,12 @@ namespace ts.server {
Debug.assert(!!parsedCommandLine.fileNames);
if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) {
- return { success: false, errors: parsedCommandLine.errors };
+ return { success: false, configFileErrors: parsedCommandLine.errors };
}
if (parsedCommandLine.fileNames.length === 0) {
const error = createCompilerDiagnostic(Diagnostics.The_config_file_0_found_doesn_t_contain_any_source_files, configFilename);
- return { success: false, errors: [error] };
+ return { success: false, configFileErrors: [error] };
}
const projectOptions: ProjectOptions = {
@@ -714,10 +718,9 @@ namespace ts.server {
/*languageServiceEnabled*/ !this.exceededTotalSizeLimitForNonTsFiles(options, files, externalFilePropertyReader),
options.compileOnSave === undefined ? true : options.compileOnSave);
- const errors = this.addFilesToProjectAndUpdateGraph(project, files, externalFilePropertyReader, /*clientFileName*/ undefined, typingOptions);
-
+ this.addFilesToProjectAndUpdateGraph(project, files, externalFilePropertyReader, /*clientFileName*/ undefined, typingOptions);
this.externalProjects.push(project);
- return { project, errors };
+ return project;
}
private createAndAddConfiguredProject(configFileName: NormalizedPath, projectOptions: ProjectOptions, clientFileName?: string) {
@@ -732,7 +735,7 @@ namespace ts.server {
/*languageServiceEnabled*/ !sizeLimitExceeded,
projectOptions.compileOnSave === undefined ? false : projectOptions.compileOnSave);
- const errors = this.addFilesToProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typingOptions);
+ this.addFilesToProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typingOptions);
project.watchConfigFile(project => this.onConfigChangedForConfiguredProject(project));
if (!sizeLimitExceeded) {
@@ -741,7 +744,7 @@ namespace ts.server {
project.watchWildcards((project, path) => this.onSourceFileInDirectoryChangedForConfiguredProject(project, path));
this.configuredProjects.push(project);
- return { project, errors };
+ return project;
}
private watchConfigDirectoryForProject(project: ConfiguredProject, options: ProjectOptions): void {
@@ -750,7 +753,7 @@ namespace ts.server {
}
}
- private addFilesToProjectAndUpdateGraph(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader, clientFileName: string, typingOptions: TypingOptions): Diagnostic[] {
+ private addFilesToProjectAndUpdateGraph(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader, clientFileName: string, typingOptions: TypingOptions): void {
let errors: Diagnostic[];
for (const f of files) {
const rootFilename = propertyReader.getFileName(f);
@@ -761,32 +764,37 @@ namespace ts.server {
project.addRoot(info);
}
else {
- (errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.File_0_not_found, rootFilename));
+ (errors || (errors = [])).push(createFileNotFoundDiagnostic(rootFilename));
}
}
+ project.setProjectErrors(errors);
project.setTypingOptions(typingOptions);
project.updateGraph();
- return errors;
}
private openConfigFile(configFileName: NormalizedPath, clientFileName?: string): OpenConfigFileResult {
const conversionResult = this.convertConfigFileContentToProjectOptions(configFileName);
if (!conversionResult.success) {
- return { success: false, errors: conversionResult.errors };
+ // open project with no files and set errors on the project
+ const project = this.createAndAddConfiguredProject(configFileName, { files: [], compilerOptions: {} }, clientFileName);
+ project.setProjectErrors(conversionResult.configFileErrors);
+ return { success: false, errors: conversionResult.configFileErrors };
}
- const { project, errors } = this.createAndAddConfiguredProject(configFileName, conversionResult.projectOptions, clientFileName);
- return { success: true, project, errors };
+ const project = this.createAndAddConfiguredProject(configFileName, conversionResult.projectOptions, clientFileName);
+ return { success: true, project, errors: project.getProjectErrors() };
}
- private updateNonInferredProject(project: ExternalProject | ConfiguredProject, newUncheckedFiles: T[], propertyReader: FilePropertyReader, newOptions: CompilerOptions, newTypingOptions: TypingOptions, compileOnSave: boolean) {
+ private updateNonInferredProject(project: ExternalProject | ConfiguredProject, newUncheckedFiles: T[], propertyReader: FilePropertyReader, newOptions: CompilerOptions, newTypingOptions: TypingOptions, compileOnSave: boolean, configFileErrors: Diagnostic[]) {
const oldRootScriptInfos = project.getRootScriptInfos();
const newRootScriptInfos: ScriptInfo[] = [];
const newRootScriptInfoMap: NormalizedPathMap = createNormalizedPathMap();
+ let projectErrors: Diagnostic[];
let rootFilesChanged = false;
for (const f of newUncheckedFiles) {
const newRootFile = propertyReader.getFileName(f);
if (!this.host.fileExists(newRootFile)) {
+ (projectErrors || (projectErrors = [])).push(createFileNotFoundDiagnostic(newRootFile));
continue;
}
const normalizedPath = toNormalizedPath(newRootFile);
@@ -840,6 +848,8 @@ namespace ts.server {
project.setCompilerOptions(newOptions);
(project).setTypingOptions(newTypingOptions);
project.compileOnSaveEnabled = !!compileOnSave;
+ project.setProjectErrors(concatenate(configFileErrors, projectErrors));
+
project.updateGraph();
}
@@ -850,9 +860,11 @@ namespace ts.server {
return;
}
- const { success, projectOptions, errors } = this.convertConfigFileContentToProjectOptions(project.configFileName);
+ const { success, projectOptions, configFileErrors } = this.convertConfigFileContentToProjectOptions(project.configFileName);
if (!success) {
- return errors;
+ // reset project settings to default
+ this.updateNonInferredProject(project, [], fileNamePropertyReader, {}, {}, /*compileOnSave*/false, configFileErrors);
+ return configFileErrors;
}
if (this.exceededTotalSizeLimitForNonTsFiles(projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader)) {
@@ -869,7 +881,7 @@ namespace ts.server {
project.enableLanguageService();
}
this.watchConfigDirectoryForProject(project, projectOptions);
- this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typingOptions, projectOptions.compileOnSave);
+ this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typingOptions, projectOptions.compileOnSave, configFileErrors);
}
}
@@ -1052,15 +1064,15 @@ namespace ts.server {
this.printProjects();
}
- private collectChanges(lastKnownProjectVersions: protocol.ProjectVersionInfo[], currentProjects: Project[], result: protocol.ProjectFiles[]): void {
+ private collectChanges(lastKnownProjectVersions: protocol.ProjectVersionInfo[], currentProjects: Project[], result: ProjectFilesWithTSDiagnostics[]): void {
for (const proj of currentProjects) {
const knownProject = forEach(lastKnownProjectVersions, p => p.projectName === proj.getProjectName() && p);
result.push(proj.getChangesSinceVersion(knownProject && knownProject.version));
}
}
- synchronizeProjectList(knownProjects: protocol.ProjectVersionInfo[]): protocol.ProjectFiles[] {
- const files: protocol.ProjectFiles[] = [];
+ synchronizeProjectList(knownProjects: protocol.ProjectVersionInfo[]): ProjectFilesWithTSDiagnostics[] {
+ const files: ProjectFilesWithTSDiagnostics[] = [];
this.collectChanges(knownProjects, this.externalProjects, files);
this.collectChanges(knownProjects, this.configuredProjects, files);
this.collectChanges(knownProjects, this.inferredProjects, files);
@@ -1139,7 +1151,7 @@ namespace ts.server {
openExternalProject(proj: protocol.ExternalProject): void {
const externalProject = this.findExternalProjectByProjectName(proj.projectFileName);
if (externalProject) {
- this.updateNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, proj.options, proj.typingOptions, proj.options.compileOnSave);
+ this.updateNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, proj.options, proj.typingOptions, proj.options.compileOnSave, /*configFileErrors*/ undefined);
return;
}
diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts
index 4bc7b27db18..4e5017f2946 100644
--- a/src/server/lsHost.ts
+++ b/src/server/lsHost.ts
@@ -76,6 +76,10 @@ namespace ts.server {
return this.compilationSettings;
}
+ useCaseSensitiveFileNames() {
+ return this.host.useCaseSensitiveFileNames;
+ }
+
getCancellationToken() {
return this.cancellationToken;
}
diff --git a/src/server/project.ts b/src/server/project.ts
index 8d12c418ac1..4adbc7ca059 100644
--- a/src/server/project.ts
+++ b/src/server/project.ts
@@ -26,6 +26,10 @@ namespace ts.server {
return project.getRootScriptInfos().every(f => fileExtensionIsAny(f.fileName, jsOrDts));
}
+ export interface ProjectFilesWithTSDiagnostics extends protocol.ProjectFiles {
+ projectErrors: Diagnostic[];
+ }
+
export abstract class Project {
private rootFiles: ScriptInfo[] = [];
private rootFilesMap: FileMap = createFileMap();
@@ -57,6 +61,8 @@ namespace ts.server {
private typingFiles: TypingsArray;
+ protected projectErrors: Diagnostic[];
+
constructor(
readonly projectKind: ProjectKind,
readonly projectService: ProjectService,
@@ -87,6 +93,10 @@ namespace ts.server {
this.markAsDirty();
}
+ getProjectErrors() {
+ return this.projectErrors;
+ }
+
getLanguageService(ensureSynchronized = true): LanguageService {
if (ensureSynchronized) {
this.updateGraph();
@@ -329,7 +339,9 @@ namespace ts.server {
getScriptInfoForNormalizedPath(fileName: NormalizedPath) {
const scriptInfo = this.projectService.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ false);
- Debug.assert(!scriptInfo || scriptInfo.isAttached(this));
+ if (scriptInfo && !scriptInfo.isAttached(this)) {
+ return Errors.ThrowProjectDoesNotContainDocument(fileName, this);
+ }
return scriptInfo;
}
@@ -371,7 +383,7 @@ namespace ts.server {
return false;
}
- getChangesSinceVersion(lastKnownVersion?: number): protocol.ProjectFiles {
+ getChangesSinceVersion(lastKnownVersion?: number): ProjectFilesWithTSDiagnostics {
this.updateGraph();
const info = {
@@ -384,7 +396,7 @@ namespace ts.server {
if (this.lastReportedFileNames && lastKnownVersion === this.lastReportedVersion) {
// if current structure version is the same - return info witout any changes
if (this.projectStructureVersion == this.lastReportedVersion) {
- return { info };
+ return { info, projectErrors: this.projectErrors };
}
// compute and return the difference
const lastReportedFileNames = this.lastReportedFileNames;
@@ -406,14 +418,14 @@ namespace ts.server {
this.lastReportedFileNames = currentFiles;
this.lastReportedVersion = this.projectStructureVersion;
- return { info, changes: { added, removed } };
+ return { info, changes: { added, removed }, projectErrors: this.projectErrors };
}
else {
// unknown version - return everything
const projectFileNames = this.getFileNames();
this.lastReportedFileNames = arrayToMap(projectFileNames, x => x);
this.lastReportedVersion = this.projectStructureVersion;
- return { info, files: projectFileNames };
+ return { info, files: projectFileNames, projectErrors: this.projectErrors };
}
}
@@ -544,6 +556,10 @@ namespace ts.server {
super(ProjectKind.Configured, projectService, documentRegistry, hasExplicitListOfFiles, languageServiceEnabled, compilerOptions, compileOnSaveEnabled);
}
+ setProjectErrors(projectErrors: Diagnostic[]) {
+ this.projectErrors = projectErrors;
+ }
+
setTypingOptions(newTypingOptions: TypingOptions): void {
this.typingOptions = newTypingOptions;
}
@@ -636,6 +652,10 @@ namespace ts.server {
return this.typingOptions;
}
+ setProjectErrors(projectErrors: Diagnostic[]) {
+ this.projectErrors = projectErrors;
+ }
+
setTypingOptions(newTypingOptions: TypingOptions): void {
if (!newTypingOptions) {
// set default typings options
diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts
index f72e7456cbe..79fd49d747c 100644
--- a/src/server/protocol.d.ts
+++ b/src/server/protocol.d.ts
@@ -535,6 +535,10 @@ declare namespace ts.server.protocol {
changes?: ProjectChanges;
}
+ export interface ProjectFilesWithDiagnostics extends ProjectFiles {
+ projectErrors: DiagnosticWithLinePosition[];
+ }
+
export interface ChangedOpenFile {
fileName: string;
changes: ts.TextChange[];
diff --git a/src/server/session.ts b/src/server/session.ts
index 978cfe79a48..1fdd75c037f 100644
--- a/src/server/session.ts
+++ b/src/server/session.ts
@@ -1261,7 +1261,21 @@ namespace ts.server {
},
[CommandNames.SynchronizeProjectList]: (request: protocol.SynchronizeProjectListRequest) => {
const result = this.projectService.synchronizeProjectList(request.arguments.knownProjects);
- return this.requiredResponse(result);
+ if (!result.some(p => p.projectErrors && p.projectErrors.length !== 0)) {
+ return this.requiredResponse(result);
+ }
+ const converted = map(result, p => {
+ if (!p.projectErrors || p.projectErrors.length === 0) {
+ return p;
+ }
+ return {
+ info: p.info,
+ changes: p.changes,
+ files: p.files,
+ projectErrors: this.convertToDiagnosticsWithLinePosition(p.projectErrors, /*scriptInfo*/ undefined)
+ };
+ });
+ return this.requiredResponse(converted);
},
[CommandNames.ApplyChangedToOpenFiles]: (request: protocol.ApplyChangedToOpenFilesRequest) => {
this.projectService.applyChangesInOpenFiles(request.arguments.openFiles, request.arguments.changedFiles, request.arguments.closedFiles);
diff --git a/src/server/utilities.ts b/src/server/utilities.ts
index d483acdf31b..a7a0160ea62 100644
--- a/src/server/utilities.ts
+++ b/src/server/utilities.ts
@@ -64,6 +64,9 @@ namespace ts.server {
export function ThrowProjectLanguageServiceDisabled(): never {
throw new Error("The project's language service is disabled.");
}
+ export function ThrowProjectDoesNotContainDocument(fileName: string, project: Project): never {
+ throw new Error(`Project '${project.getProjectName()}' does not contain document '${fileName}'`);
+ }
}
export function getDefaultFormatCodeSettings(host: ServerHost): FormatCodeSettings {
diff --git a/src/services/services.ts b/src/services/services.ts
index 6d81cc4dc86..7fdb8edc248 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -3053,7 +3053,7 @@ namespace ts {
let program: Program;
let lastProjectVersion: string;
- const useCaseSensitivefileNames = false;
+ const useCaseSensitivefileNames = host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames();
const cancellationToken = new CancellationTokenObject(host.getCancellationToken && host.getCancellationToken());
const currentDirectory = host.getCurrentDirectory();