Merge branch 'master' into addTsConfigWatcher

This commit is contained in:
zhengbli
2015-08-19 12:44:29 -07:00
parent 58ac077a49
commit 42942a5dc4
986 changed files with 26180 additions and 10749 deletions

View File

@@ -183,7 +183,7 @@ namespace ts.server {
return {
configFileName: response.body.configFileName,
fileNameList: response.body.fileNameList
fileNames: response.body.fileNames
};
}
@@ -527,8 +527,33 @@ namespace ts.server {
});
}
getDocumentHighlights(fileName: string, position: number): DocumentHighlights[] {
throw new Error("Not Implemented Yet.");
getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[] {
let { line, offset } = this.positionToOneBasedLineOffset(fileName, position);
let args: protocol.DocumentHighlightsRequestArgs = { file: fileName, line, offset, filesToSearch };
let request = this.processRequest<protocol.DocumentHighlightsRequest>(CommandNames.DocumentHighlights, args);
let response = this.processResponse<protocol.DocumentHighlightsResponse>(request);
let self = this;
return response.body.map(convertToDocumentHighlights);
function convertToDocumentHighlights(item: ts.server.protocol.DocumentHighlightsItem): ts.DocumentHighlights {
let { file, highlightSpans } = item;
return {
fileName: file,
highlightSpans: highlightSpans.map(convertHighlightSpan)
};
function convertHighlightSpan(span: ts.server.protocol.HighlightSpan): ts.HighlightSpan {
let start = self.lineOffsetToPosition(file, span.start);
let end = self.lineOffsetToPosition(file, span.end);
return {
textSpan: ts.createTextSpanFromBounds(start, end),
kind: span.kind
};
}
}
}
getOutliningSpans(fileName: string): OutliningSpan[] {
@@ -538,6 +563,10 @@ namespace ts.server {
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[] {
throw new Error("Not Implemented Yet.");
}
getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion {
throw new Error("Not Implemented Yet.");
}
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[] {
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);

View File

@@ -78,15 +78,76 @@ namespace ts.server {
return this.snap().getChangeRange(oldSnapshot);
}
}
interface TimestampedResolvedModule extends ResolvedModule {
lastCheckTime: number;
}
export class LSHost implements ts.LanguageServiceHost {
ls: ts.LanguageService = null;
compilationSettings: ts.CompilerOptions;
filenameToScript: ts.Map<ScriptInfo> = {};
roots: ScriptInfo[] = [];
private resolvedModuleNames: ts.FileMap<Map<TimestampedResolvedModule>>;
private moduleResolutionHost: ts.ModuleResolutionHost;
constructor(public host: ServerHost, public project: Project) {
this.resolvedModuleNames = ts.createFileMap<Map<TimestampedResolvedModule>>(ts.createGetCanonicalFileName(host.useCaseSensitiveFileNames))
this.moduleResolutionHost = {
fileExists: fileName => this.fileExists(fileName),
readFile: fileName => this.host.readFile(fileName)
}
}
resolveModuleNames(moduleNames: string[], containingFile: string): string[] {
let currentResolutionsInFile = this.resolvedModuleNames.get(containingFile);
let newResolutions: Map<TimestampedResolvedModule> = {};
let resolvedFileNames: string[] = [];
let compilerOptions = this.getCompilationSettings();
for (let moduleName of moduleNames) {
// check if this is a duplicate entry in the list
let resolution = lookUp(newResolutions, moduleName);
if (!resolution) {
let existingResolution = currentResolutionsInFile && ts.lookUp(currentResolutionsInFile, moduleName);
if (moduleResolutionIsValid(existingResolution)) {
// ok, it is safe to use existing module resolution results
resolution = existingResolution;
}
else {
resolution = <TimestampedResolvedModule>resolveModuleName(moduleName, containingFile, compilerOptions, this.moduleResolutionHost);
resolution.lastCheckTime = Date.now();
newResolutions[moduleName] = resolution;
}
}
ts.Debug.assert(resolution !== undefined);
resolvedFileNames.push(resolution.resolvedFileName);
}
// replace old results with a new one
this.resolvedModuleNames.set(containingFile, newResolutions);
return resolvedFileNames;
function moduleResolutionIsValid(resolution: TimestampedResolvedModule): boolean {
if (!resolution) {
return false;
}
if (resolution.resolvedFileName) {
// TODO: consider checking failedLookupLocations
// TODO: use lastCheckTime to track expiration for module name resolution
return true;
}
// consider situation if we have no candidate locations as valid resolution.
// after all there is no point to invalidate it if we have no idea where to look for the module.
return resolution.failedLookupLocations.length === 0;
}
}
getDefaultLibFileName() {
var nodeModuleBinDir = ts.getDirectoryPath(ts.normalizePath(this.host.getExecutingFilePath()));
@@ -102,6 +163,8 @@ namespace ts.server {
setCompilationSettings(opt: ts.CompilerOptions) {
this.compilationSettings = opt;
// conservatively assume that changing compiler options might affect module resolution strategy
this.resolvedModuleNames.clear();
}
lineAffectsRefs(filename: string, line: number) {
@@ -137,6 +200,7 @@ namespace ts.server {
removeReferencedFile(info: ScriptInfo) {
if (!info.isOpen) {
this.filenameToScript[info.fileName] = undefined;
this.resolvedModuleNames.remove(info.fileName);
}
}
@@ -313,7 +377,7 @@ namespace ts.server {
return this.projectService.openFile(filename, false);
}
getFileNameList() {
getFileNames() {
let sourceFiles = this.program.getSourceFiles();
return sourceFiles.map(sourceFile => sourceFile.fileName);
}
@@ -555,7 +619,7 @@ namespace ts.server {
project.projectFileWatcher.close();
this.configuredProjects = copyListRemovingItem(project, this.configuredProjects);
let fileNames = project.getFileNameList();
let fileNames = project.getFileNames();
for (let fileName of fileNames) {
this.getScriptInfo(fileName).defaultProject = undefined;
}
@@ -853,9 +917,6 @@ namespace ts.server {
} else {
this.log("no config file");
}
if (configFileName) {
configFileName = getAbsolutePath(configFileName, searchPath);
}
if (configFileName) {
let project = this.findConfiguredProjectByConfigFile(configFileName);
if (!project) {
@@ -966,7 +1027,8 @@ namespace ts.server {
configFilename = ts.normalizePath(configFilename);
// file references will be relative to dirPath (or absolute)
var dirPath = ts.getDirectoryPath(configFilename);
var rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.readConfigFile(configFilename);
var contents = this.host.readFile(configFilename)
var rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.parseConfigFileText(configFilename, contents);
if (rawConfig.error) {
return { succeeded: false, error: rawConfig.error };
}
@@ -1106,6 +1168,7 @@ namespace ts.server {
InsertSpaceAfterKeywordsInControlFlowStatements: true,
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
PlaceOpenBraceOnNewLineForFunctions: false,
PlaceOpenBraceOnNewLineForControlBlocks: false,
}

File diff suppressed because it is too large Load Diff

View File

@@ -123,9 +123,14 @@ declare module NodeJS {
export interface ReadWriteStream extends ReadableStream, WritableStream { }
interface WindowSize {
columns: number;
rows: number;
}
export interface Process extends EventEmitter {
stdout: WritableStream;
stderr: WritableStream;
stdout: WritableStream & WindowSize;
stderr: WritableStream & WindowSize;
stdin: ReadableStream;
argv: string[];
execPath: string;

View File

@@ -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[];
}
/**
@@ -156,6 +156,17 @@ declare namespace ts.server.protocol {
arguments: FileLocationRequestArgs;
}
/**
* Arguments in document highlight request; include: filesToSearch, file,
* line, offset.
*/
export interface DocumentHighlightsRequestArgs extends FileLocationRequestArgs {
/**
* List of files to search for document highlights.
*/
filesToSearch: string[];
}
/**
* Go to definition request; value of command field is
* "definition". Return response giving the file locations that
@@ -238,6 +249,35 @@ declare namespace ts.server.protocol {
body?: OccurrencesResponseItem[];
}
/**
* Get document highlights request; value of command field is
* "documentHighlights". Return response giving spans that are relevant
* in the file at a given line and column.
*/
export interface DocumentHighlightsRequest extends FileLocationRequest {
arguments: DocumentHighlightsRequestArgs
}
export interface HighlightSpan extends TextSpan {
kind: string
}
export interface DocumentHighlightsItem {
/**
* File containing highlight spans.
*/
file: string,
/**
* Spans to highlight in file.
*/
highlightSpans: HighlightSpan[];
}
export interface DocumentHighlightsResponse extends Response {
body?: DocumentHighlightsItem[];
}
/**
* Find references request; value of command field is
* "references". Return response giving the file locations that
@@ -412,6 +452,9 @@ declare namespace ts.server.protocol {
/** Defines space handling after opening and before closing non empty parenthesis. Default value is false. */
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis?: boolean;
/** Defines space handling after opening and before closing non empty brackets. Default value is false. */
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets?: boolean;
/** Defines whether an open brace is put onto a new line for functions or not. Default value is false. */
placeOpenBraceOnNewLineForFunctions?: boolean;
@@ -854,6 +897,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.

View File

@@ -86,9 +86,11 @@ 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";
export const DocumentHighlights = "documentHighlights";
export const Open = "open";
export const Quickinfo = "quickinfo";
export const References = "references";
@@ -234,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;
}
@@ -249,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);
@@ -313,7 +315,7 @@ namespace ts.server {
}));
}
private getOccurrences(line: number, offset: number, fileName: string): protocol.OccurrencesResponseItem[]{
private getOccurrences(line: number, offset: number, fileName: string): protocol.OccurrencesResponseItem[] {
fileName = ts.normalizePath(fileName);
let project = this.projectService.getProjectForFile(fileName);
@@ -343,6 +345,42 @@ namespace ts.server {
});
}
private getDocumentHighlights(line: number, offset: number, fileName: string, filesToSearch: string[]): protocol.DocumentHighlightsItem[] {
fileName = ts.normalizePath(fileName);
let project = this.projectService.getProjectForFile(fileName);
if (!project) {
throw Errors.NoProject;
}
let { compilerService } = project;
let position = compilerService.host.lineOffsetToPosition(fileName, line, offset);
let documentHighlights = compilerService.languageService.getDocumentHighlights(fileName, position, filesToSearch);
if (!documentHighlights) {
return undefined;
}
return documentHighlights.map(convertToDocumentHighlightsItem);
function convertToDocumentHighlightsItem(documentHighlights: ts.DocumentHighlights): ts.server.protocol.DocumentHighlightsItem {
let { fileName, highlightSpans } = documentHighlights;
return {
file: fileName,
highlightSpans: highlightSpans.map(convertHighlightSpan)
};
function convertHighlightSpan(highlightSpan: ts.HighlightSpan): ts.server.protocol.HighlightSpan {
let { textSpan, kind } = highlightSpan;
let start = compilerService.host.positionToLineOffset(fileName, textSpan.start);
let end = compilerService.host.positionToLineOffset(fileName, ts.textSpanEnd(textSpan));
return { start, end, kind };
}
}
}
private getProjectInfo(fileName: string, needFileNameList: boolean): protocol.ProjectInfo {
fileName = ts.normalizePath(fileName)
let project = this.projectService.getProjectForFile(fileName)
@@ -352,7 +390,7 @@ namespace ts.server {
}
if (needFileNameList) {
projectInfo.fileNameList = project.getFileNameList();
projectInfo.fileNames = project.getFileNames();
}
return projectInfo;
@@ -836,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<PendingErrorCheck>((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}> = {
@@ -894,6 +978,10 @@ namespace ts.server {
var geterrArgs = <protocol.GeterrRequestArgs>request.arguments;
return {response: this.getDiagnostics(geterrArgs.delay, geterrArgs.files), responseRequired: false};
},
[CommandNames.GeterrForProject]: (request: protocol.Request) => {
let { file, delay } = <protocol.GeterrForProjectRequestArgs>request.arguments;
return {response: this.getDiagnosticsForProject(delay, file), responseRequired: false};
},
[CommandNames.Change]: (request: protocol.Request) => {
var changeArgs = <protocol.ChangeRequestArgs>request.arguments;
this.change(changeArgs.line, changeArgs.offset, changeArgs.endLine, changeArgs.endOffset,
@@ -937,6 +1025,10 @@ namespace ts.server {
var { line, offset, file: fileName } = <protocol.FileLocationRequestArgs>request.arguments;
return {response: this.getOccurrences(line, offset, fileName), responseRequired: true};
},
[CommandNames.DocumentHighlights]: (request: protocol.Request) => {
var { line, offset, file: fileName, filesToSearch } = <protocol.DocumentHighlightsRequestArgs>request.arguments;
return {response: this.getDocumentHighlights(line, offset, fileName, filesToSearch), responseRequired: true};
},
[CommandNames.ProjectInfo]: (request: protocol.Request) => {
var { file, needFileNameList } = <protocol.ProjectInfoRequestArgs>request.arguments;
return {response: this.getProjectInfo(file, needFileNameList), responseRequired: true};