diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index ebf1b1d1814..6cc1eaea3b5 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1883,7 +1883,7 @@ module FourSlash { ); assert.equal( expected.join(","), - actual.fileNameList.map( file => { + actual.fileNames.map( file => { return file.replace(this.basePath + "/", ""); }).join(",") ); @@ -2808,4 +2808,4 @@ module FourSlash { fileName: fileName }; } -} \ No newline at end of file +} diff --git a/src/server/client.ts b/src/server/client.ts index cc89349b442..f938d478246 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -183,7 +183,7 @@ namespace ts.server { return { configFileName: response.body.configFileName, - fileNameList: response.body.fileNameList + fileNames: response.body.fileNames }; } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 64ec600783a..7eaf944e916 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -304,7 +304,7 @@ namespace ts.server { return this.projectService.openFile(filename, false); } - getFileNameList() { + getFileNames() { let sourceFiles = this.program.getSourceFiles(); return sourceFiles.map(sourceFile => sourceFile.fileName); } diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts index 837bce4f99d..99008770bba 100644 --- a/src/server/protocol.d.ts +++ b/src/server/protocol.d.ts @@ -116,7 +116,7 @@ declare namespace ts.server.protocol { /** * The list of normalized file name in the project, including 'lib.d.ts' */ - fileNameList?: string[]; + fileNames?: string[]; } /** @@ -894,6 +894,31 @@ declare namespace ts.server.protocol { export interface SignatureHelpResponse extends Response { body?: SignatureHelpItems; } + + /** + * Arguments for GeterrForProject request. + */ + export interface GeterrForProjectRequestArgs { + /** + * the file requesting project error list + */ + file: string; + + /** + * Delay in milliseconds to wait before starting to compute + * errors for the files in the file list + */ + delay: number; + } + + /** + * GeterrForProjectRequest request; value of command field is + * "geterrForProject". It works similarly with 'Geterr', only + * it request for every file in this project. + */ + export interface GeterrForProjectRequest extends Request { + arguments: GeterrForProjectRequestArgs + } /** * Arguments for geterr messages. diff --git a/src/server/session.ts b/src/server/session.ts index 54e4423d382..108cf4726dc 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -86,6 +86,7 @@ namespace ts.server { export const Format = "format"; export const Formatonkey = "formatonkey"; export const Geterr = "geterr"; + export const GeterrForProject = "geterrForProject"; export const NavBar = "navbar"; export const Navto = "navto"; export const Occurrences = "occurrences"; @@ -235,7 +236,7 @@ namespace ts.server { } private updateErrorCheck(checkList: PendingErrorCheck[], seq: number, - matchSeq: (seq: number) => boolean, ms = 1500, followMs = 200) { + matchSeq: (seq: number) => boolean, ms = 1500, followMs = 200, requireOpen = true) { if (followMs > ms) { followMs = ms; } @@ -250,7 +251,7 @@ namespace ts.server { var checkOne = () => { if (matchSeq(seq)) { var checkSpec = checkList[index++]; - if (checkSpec.project.getSourceFileFromName(checkSpec.fileName, true)) { + if (checkSpec.project.getSourceFileFromName(checkSpec.fileName, requireOpen)) { this.syntacticCheck(checkSpec.fileName, checkSpec.project); this.immediateId = setImmediate(() => { this.semanticCheck(checkSpec.fileName, checkSpec.project); @@ -389,7 +390,7 @@ namespace ts.server { } if (needFileNameList) { - projectInfo.fileNameList = project.getFileNameList(); + projectInfo.fileNames = project.getFileNames(); } return projectInfo; @@ -873,7 +874,53 @@ namespace ts.server { })); } - public exit() { + getDiagnosticsForProject(delay: number, fileName: string) { + let { configFileName, fileNames: fileNamesInProject } = this.getProjectInfo(fileName, true); + // No need to analyze lib.d.ts + fileNamesInProject = fileNamesInProject.filter((value, index, array) => value.indexOf("lib.d.ts") < 0); + + // Sort the file name list to make the recently touched files come first + let highPriorityFiles: string[] = []; + let mediumPriorityFiles: string[] = []; + let lowPriorityFiles: string[] = []; + let veryLowPriorityFiles: string[] = []; + let normalizedFileName = ts.normalizePath(fileName); + let project = this.projectService.getProjectForFile(normalizedFileName); + for (let fileNameInProject of fileNamesInProject) { + if (this.getCanonicalFileName(fileNameInProject) == this.getCanonicalFileName(fileName)) + highPriorityFiles.push(fileNameInProject); + else { + let info = this.projectService.getScriptInfo(fileNameInProject); + if (!info.isOpen) { + if (fileNameInProject.indexOf(".d.ts") > 0) + veryLowPriorityFiles.push(fileNameInProject); + else + lowPriorityFiles.push(fileNameInProject); + } + else + mediumPriorityFiles.push(fileNameInProject); + } + } + + fileNamesInProject = highPriorityFiles.concat(mediumPriorityFiles).concat(lowPriorityFiles).concat(veryLowPriorityFiles); + + if (fileNamesInProject.length > 0) { + let checkList = fileNamesInProject.map((fileName: string) => { + let normalizedFileName = ts.normalizePath(fileName); + return { fileName: normalizedFileName, project }; + }); + // Project level error analysis runs on background files too, therefore + // doesn't require the file to be opened + this.updateErrorCheck(checkList, this.changeSeq, (n) => n == this.changeSeq, delay, 200, /*requireOpen*/ false); + } + } + + getCanonicalFileName(fileName: string) { + let name = this.host.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase(); + return ts.normalizePath(name); + } + + exit() { } private handlers : Map<(request: protocol.Request) => {response?: any, responseRequired?: boolean}> = { @@ -931,6 +978,10 @@ namespace ts.server { var geterrArgs = request.arguments; return {response: this.getDiagnostics(geterrArgs.delay, geterrArgs.files), responseRequired: false}; }, + [CommandNames.GeterrForProject]: (request: protocol.Request) => { + let { file, delay } = request.arguments; + return {response: this.getDiagnosticsForProject(delay, file), responseRequired: false}; + }, [CommandNames.Change]: (request: protocol.Request) => { var changeArgs = request.arguments; this.change(changeArgs.line, changeArgs.offset, changeArgs.endLine, changeArgs.endOffset,