merge with origin/master

This commit is contained in:
Vladimir Matveev
2016-06-20 15:22:00 -07:00
parent 2ffc36349b
commit 3225e55548
3 changed files with 209 additions and 58 deletions
+190 -41
View File
@@ -123,7 +123,66 @@ namespace ts.server {
}
}
export class LSHost implements ts.LanguageServiceHost, ModuleResolutionHost {
function throwLanguageServiceIsDisabledError() {;
throw new Error("LanguageService is disabled");
}
const nullLanguageService: ts.LanguageService = {
cleanupSemanticCache: (): any => throwLanguageServiceIsDisabledError(),
getSyntacticDiagnostics: (): any => throwLanguageServiceIsDisabledError(),
getSemanticDiagnostics: (): any => throwLanguageServiceIsDisabledError(),
getCompilerOptionsDiagnostics: (): any => throwLanguageServiceIsDisabledError(),
getSyntacticClassifications: (): any => throwLanguageServiceIsDisabledError(),
getEncodedSyntacticClassifications: (): any => throwLanguageServiceIsDisabledError(),
getSemanticClassifications: (): any => throwLanguageServiceIsDisabledError(),
getEncodedSemanticClassifications: (): any => throwLanguageServiceIsDisabledError(),
getCompletionsAtPosition: (): any => throwLanguageServiceIsDisabledError(),
findReferences: (): any => throwLanguageServiceIsDisabledError(),
getCompletionEntryDetails: (): any => throwLanguageServiceIsDisabledError(),
getQuickInfoAtPosition: (): any => throwLanguageServiceIsDisabledError(),
findRenameLocations: (): any => throwLanguageServiceIsDisabledError(),
getNameOrDottedNameSpan: (): any => throwLanguageServiceIsDisabledError(),
getBreakpointStatementAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getBraceMatchingAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getSignatureHelpItems: (): any => throwLanguageServiceIsDisabledError(),
getDefinitionAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getRenameInfo: (): any => throwLanguageServiceIsDisabledError(),
getTypeDefinitionAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getReferencesAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getDocumentHighlights: (): any => throwLanguageServiceIsDisabledError(),
getOccurrencesAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getNavigateToItems: (): any => throwLanguageServiceIsDisabledError(),
getNavigationBarItems: (): any => throwLanguageServiceIsDisabledError(),
getOutliningSpans: (): any => throwLanguageServiceIsDisabledError(),
getTodoComments: (): any => throwLanguageServiceIsDisabledError(),
getIndentationAtPosition: (): any => throwLanguageServiceIsDisabledError(),
getFormattingEditsForRange: (): any => throwLanguageServiceIsDisabledError(),
getFormattingEditsForDocument: (): any => throwLanguageServiceIsDisabledError(),
getFormattingEditsAfterKeystroke: (): any => throwLanguageServiceIsDisabledError(),
getDocCommentTemplateAtPosition: (): any => throwLanguageServiceIsDisabledError(),
isValidBraceCompletionAtPostion: (): any => throwLanguageServiceIsDisabledError(),
getEmitOutput: (): any => throwLanguageServiceIsDisabledError(),
getProgram: (): any => throwLanguageServiceIsDisabledError(),
getNonBoundSourceFile: (): any => throwLanguageServiceIsDisabledError(),
dispose: (): any => throwLanguageServiceIsDisabledError(),
};
interface ServerLanguageServiceHost {
getCompilationSettings(): CompilerOptions;
setCompilationSettings(options: CompilerOptions): void;
removeRoot(info: ScriptInfo): void;
removeReferencedFile(info: ScriptInfo): void;
}
const nullLanguageServiceHost: ServerLanguageServiceHost = {
getCompilationSettings: () => undefined,
setCompilationSettings: () => undefined,
removeRoot: () => undefined,
removeReferencedFile: () => undefined
};
export class LSHost implements ts.LanguageServiceHost, ModuleResolutionHost, ServerLanguageServiceHost {
private compilationSettings: ts.CompilerOptions;
private resolvedModuleNames: ts.FileMap<Map<ResolvedModuleWithFailedLookupLocations>>;
private resolvedTypeReferenceDirectives: ts.FileMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>;
@@ -302,31 +361,62 @@ namespace ts.server {
export abstract class Project {
private rootFiles: ScriptInfo[] = [];
private rootFilesMap: FileMap<ScriptInfo> = createFileMap<ScriptInfo>();
private readonly lsHost: LSHost;
private lsHost: ServerLanguageServiceHost;
readonly languageService: LanguageService;
languageService: LanguageService;
protected program: ts.Program;
constructor(
readonly projectKind: ProjectKind,
readonly projectService: ProjectService,
documentRegistry: ts.DocumentRegistry,
private documentRegistry: ts.DocumentRegistry,
hasExplicitListOfFiles: boolean,
compilerOptions: CompilerOptions) {
public languageServiceEnabled: boolean,
private compilerOptions: CompilerOptions) {
if (!compilerOptions) {
compilerOptions = ts.getDefaultCompilerOptions();
compilerOptions.allowNonTsExtensions = true;
compilerOptions.allowJs = true;
if (!this.compilerOptions) {
this.compilerOptions = ts.getDefaultCompilerOptions();
this.compilerOptions.allowNonTsExtensions = true;
this.compilerOptions.allowJs = true;
}
else if (hasExplicitListOfFiles) {
// If files are listed explicitly, allow all extensions
compilerOptions.allowNonTsExtensions = true;
this.compilerOptions.allowNonTsExtensions = true;
}
this.lsHost = new LSHost(this.projectService.host, this, this.projectService.cancellationToken);
this.lsHost.setCompilationSettings(compilerOptions);
this.languageService = ts.createLanguageService(this.lsHost, documentRegistry);
if (languageServiceEnabled) {
this.enableLanguageServiceWorker();
}
else {
this.disableLanguageServiceWorker();
}
}
enableLanguageService() {
if (!this.languageServiceEnabled) {
this.enableLanguageServiceWorker();
}
}
private enableLanguageServiceWorker() {
const lsHost = new LSHost(this.projectService.host, this, this.projectService.cancellationToken);
lsHost.setCompilationSettings(this.compilerOptions);
this.languageService = ts.createLanguageService(lsHost, this.documentRegistry);
this.lsHost = lsHost;
this.languageServiceEnabled = true;
}
disableLanguageService() {
if (this.languageServiceEnabled) {
this.disableLanguageServiceWorker();
}
}
private disableLanguageServiceWorker() {
this.languageService = nullLanguageService;
this.lsHost = nullLanguageServiceHost;
this.languageServiceEnabled = false;
}
getProjectFileName(): string {
@@ -343,29 +433,29 @@ namespace ts.server {
}
getRootFiles() {
if (!this.languageServiceEnabled && this.projectKind === ProjectKind.Inferred) {
return undefined;
}
return this.rootFiles.map(info => info.fileName);
}
getFileNames() {
if (this.languageServiceDiabled) {
if (!this.projectOptions) {
return undefined;
if (!this.languageServiceEnabled) {
let rootFiles = this.getRootFiles();
if (this.compilerOptions) {
const defaultLibrary = getDefaultLibFilePath(this.compilerOptions);
if (defaultLibrary) {
(rootFiles || (rootFiles = [])).push(defaultLibrary);
}
}
const fileNames: string[] = [];
if (this.projectOptions && this.projectOptions.compilerOptions) {
fileNames.push(getDefaultLibFilePath(this.projectOptions.compilerOptions));
}
ts.addRange(fileNames, this.projectOptions.files);
return fileNames;
return rootFiles;
}
const sourceFiles = this.program.getSourceFiles();
return sourceFiles.map(sourceFile => sourceFile.fileName);
}
containsScriptInfo(info: ScriptInfo): boolean {
return this.program.getSourceFileByPath(info.path) !== undefined;
return this.program && this.program.getSourceFileByPath(info.path) !== undefined;
}
containsFile(filename: string, requireOpen?: boolean) {
@@ -455,8 +545,13 @@ namespace ts.server {
// Used to keep track of what directories are watched for this project
directoriesWatchedForTsconfig: string[] = [];
constructor(projectService: ProjectService, documentRegistry: ts.DocumentRegistry) {
super(ProjectKind.Inferred, projectService, documentRegistry, /*files*/ undefined, /*compilerOptions*/ undefined);
constructor(projectService: ProjectService, documentRegistry: ts.DocumentRegistry, languageServiceEnabled: boolean) {
super(ProjectKind.Inferred,
projectService,
documentRegistry,
/*files*/ undefined,
languageServiceEnabled,
/*compilerOptions*/ undefined);
}
close() {
@@ -483,6 +578,9 @@ namespace ts.server {
currentVersion: number = 1;
updateGraph() {
if (!this.languageServiceEnabled) {
return;
}
const oldProgram = this.program;
super.updateGraph();
@@ -538,8 +636,13 @@ namespace ts.server {
/** Used for configured projects which may have multiple open roots */
openRefCount = 0;
constructor(readonly configFileName: string, projectService: ProjectService, documentRegistry: ts.DocumentRegistry, hasExplicitListOfFiles: boolean, compilerOptions: CompilerOptions) {
super(ProjectKind.Configured, projectService, documentRegistry, hasExplicitListOfFiles, compilerOptions);
constructor(readonly configFileName: string,
projectService: ProjectService,
documentRegistry: ts.DocumentRegistry,
hasExplicitListOfFiles: boolean,
compilerOptions: CompilerOptions,
languageServiceEnabled: boolean) {
super(ProjectKind.Configured, projectService, documentRegistry, hasExplicitListOfFiles, languageServiceEnabled, compilerOptions);
}
getProjectFileName() {
@@ -551,20 +654,29 @@ namespace ts.server {
}
watchConfigDirectory(callback: (project: ConfiguredProject, path: string) => void) {
if (this.directoryWatcher) {
return;
}
const directoryToWatch = ts.getDirectoryPath(this.configFileName);
this.projectService.log(`Add recursive watcher for: ${directoryToWatch}`);
this.directoryWatcher = this.projectService.host.watchDirectory(directoryToWatch, path => callback(this, path), /*recursive*/ true);
}
stopWatchingDirectory() {
if (this.directoryWatcher) {
this.directoryWatcher.close();
this.directoryWatcher = undefined;
}
}
close() {
super.close();
if (this.projectFileWatcher) {
this.projectFileWatcher.close();
}
if (this.directoryWatcher) {
this.directoryWatcher.close();
}
this.stopWatchingDirectory();
}
addOpenRef() {
@@ -578,8 +690,12 @@ namespace ts.server {
}
class ExternalProject extends VersionedProject {
constructor(readonly projectFileName: string, projectService: ProjectService, documentRegistry: ts.DocumentRegistry, compilerOptions: CompilerOptions) {
super(ProjectKind.External, projectService, documentRegistry, /*hasExplicitListOfFiles*/ true, compilerOptions);
constructor(readonly projectFileName: string,
projectService: ProjectService,
documentRegistry: ts.DocumentRegistry,
compilerOptions: CompilerOptions,
languageServiceEnabled: boolean) {
super(ProjectKind.External, projectService, documentRegistry, /*hasExplicitListOfFiles*/ true, languageServiceEnabled, compilerOptions);
}
getProjectFileName() {
@@ -1149,7 +1265,10 @@ namespace ts.server {
}
}
private exceedTotalNonTsFileSizeLimit(fileNames: string[]) {
private exceedTotalNonTsFileSizeLimit(options: CompilerOptions, fileNames: string[]) {
if (options && options.disableSizeLimit) {
return false;
}
let totalNonTsFileSize = 0;
if (!this.host.getFileSize) {
return false;
@@ -1168,23 +1287,38 @@ namespace ts.server {
}
private createAndAddExternalProject(projectFileName: string, files: string[], compilerOptions: CompilerOptions, clientFileName?: string) {
const project = new ExternalProject(projectFileName, this, this.documentRegistry, compilerOptions);
const sizeLimitExceeded = this.exceedTotalNonTsFileSizeLimit(compilerOptions, files);
const project = new ExternalProject(projectFileName, this, this.documentRegistry, compilerOptions, !sizeLimitExceeded);
const errors = this.addFilesToProject(project, files, clientFileName);
this.externalProjects.push(project);
return { project, errors };
}
private createAndAddConfiguredProject(configFileName: string, projectOptions: ProjectOptions, clientFileName?: string) {
const project = new ConfiguredProject(configFileName, this, this.documentRegistry, projectOptions.configHasFilesProperty, projectOptions.compilerOptions);
const sizeLimitExceeded = this.exceedTotalNonTsFileSizeLimit(projectOptions.compilerOptions, projectOptions.files);
const project = new ConfiguredProject(
configFileName,
this,
this.documentRegistry,
projectOptions.configHasFilesProperty,
projectOptions.compilerOptions,
!sizeLimitExceeded);
const errors = this.addFilesToProject(project, projectOptions.files, clientFileName);
project.watchConfigFile(project => this.onConfigChangedForConfiguredProject(project));
if (!projectOptions.configHasFilesProperty) {
project.watchConfigDirectory((project, path) => this.onSourceFileInDirectoryChangedForConfiguredProject(project, path));
if (!sizeLimitExceeded) {
this.watchConfigDirectoryForProject(project, projectOptions);
}
this.configuredProjects.push(project);
return { project, errors };
}
private watchConfigDirectoryForProject(project: ConfiguredProject, options: ProjectOptions) {
if (!options.configHasFilesProperty) {
project.watchConfigDirectory((project, path) => this.onSourceFileInDirectoryChangedForConfiguredProject(project, path));
}
}
private addFilesToProject(project: ConfiguredProject | ExternalProject, files: string[], clientFileName: string): Diagnostic[] {
let errors: Diagnostic[];
for (const rootFilename of files) {
@@ -1266,13 +1400,28 @@ namespace ts.server {
return errors;
}
else {
this.updateVersionedProjectWorker(project, projectOptions.files, projectOptions.compilerOptions);
if (this.exceedTotalNonTsFileSizeLimit(projectOptions.compilerOptions, projectOptions.files)) {
project.setCompilerOptions(projectOptions.compilerOptions);
if (!project.languageServiceEnabled) {
// language service is already disabled
return;
}
project.disableLanguageService();
project.stopWatchingDirectory();
}
else {
if (!project.languageServiceEnabled) {
project.enableLanguageService();
}
this.watchConfigDirectoryForProject(project, projectOptions);
this.updateVersionedProjectWorker(project, projectOptions.files, projectOptions.compilerOptions);
}
}
}
}
createAndAddInferredProject(root: ScriptInfo) {
const project = new InferredProject(this, this.documentRegistry);
const project = new InferredProject(this, this.documentRegistry, /*languageServiceEnabled*/ true);
project.addRoot(root);
let currentPath = ts.getDirectoryPath(root.fileName);
+17 -15
View File
@@ -421,7 +421,7 @@ namespace ts.server {
private getDefinition(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.FileSpan[] | DefinitionInfo[] {
const file = ts.normalizePath(args.file);
const project = this.projectService.getProjectForFile(file);
if (!project || project.languageServiceDiabled) {
if (!project) {
throw Errors.NoProject;
}
@@ -451,7 +451,7 @@ namespace ts.server {
private getTypeDefinition(line: number, offset: number, fileName: string): protocol.FileSpan[] {
const file = ts.normalizePath(fileName);
const project = this.projectService.getProjectForFile(file);
if (!project || project.languageServiceDiabled) {
if (!project) {
throw Errors.NoProject;
}
@@ -477,7 +477,7 @@ namespace ts.server {
fileName = ts.normalizePath(fileName);
const project = this.projectService.getProjectForFile(fileName);
if (!project || project.languageServiceDiabled) {
if (!project) {
throw Errors.NoProject;
}
@@ -508,7 +508,7 @@ namespace ts.server {
const fileName = ts.normalizePath(args.file);
const project = this.projectService.getProjectForFile(fileName);
if (!project || project.languageServiceDiabled) {
if (!project) {
throw Errors.NoProject;
}
@@ -554,7 +554,8 @@ namespace ts.server {
}
const projectInfo: protocol.ProjectInfo = {
configFileName: project.getProjectFileName()
configFileName: project.getProjectFileName(),
languageServiceDisabled: !project.languageServiceEnabled
};
if (needFileNameList) {
@@ -583,6 +584,7 @@ namespace ts.server {
const info = this.projectService.getScriptInfo(file);
projects = this.projectService.findReferencingProjects(info);
}
projects = filter(projects, p => p.languageServiceEnabled);
if (!projects || !projects.length) {
throw Errors.NoProject;
}
@@ -781,7 +783,7 @@ namespace ts.server {
private getFileAndProject(fileName: string) {
const file = ts.normalizePath(fileName);
const project = this.projectService.getProjectForFile(file);
if (!project || project.languageServiceDiabled) {
if (!project) {
throw Errors.NoProject;
}
return { file, project };
@@ -862,7 +864,7 @@ namespace ts.server {
private getFormattingEditsForRange(line: number, offset: number, endLine: number, endOffset: number, fileName: string): protocol.CodeEdit[] {
const file = ts.normalizePath(fileName);
const project = this.projectService.getProjectForFile(file);
if (!project || project.languageServiceDiabled) {
if (!project) {
throw Errors.NoProject;
}
@@ -919,7 +921,7 @@ namespace ts.server {
const file = ts.normalizePath(fileName);
const project = this.projectService.getProjectForFile(file);
if (!project || project.languageServiceDiabled) {
if (!project) {
throw Errors.NoProject;
}
@@ -990,7 +992,7 @@ namespace ts.server {
const prefix = args.prefix || "";
const file = ts.normalizePath(args.file);
const project = this.projectService.getProjectForFile(file);
if (!project || project.languageServiceDiabled) {
if (!project) {
throw Errors.NoProject;
}
@@ -1017,7 +1019,7 @@ namespace ts.server {
private getCompletionEntryDetails(args: protocol.CompletionDetailsRequestArgs): protocol.CompletionEntryDetails[] {
const file = ts.normalizePath(args.file);
const project = this.projectService.getProjectForFile(file);
if (!project || project.languageServiceDiabled) {
if (!project) {
throw Errors.NoProject;
}
@@ -1036,7 +1038,7 @@ namespace ts.server {
private getSignatureHelpItems(args: protocol.SignatureHelpRequestArgs, simplifiedResult: boolean): protocol.SignatureHelpItems | SignatureHelpItems {
const file = ts.normalizePath(args.file);
const project = this.projectService.getProjectForFile(file);
if (!project || project.languageServiceDiabled) {
if (!project) {
throw Errors.NoProject;
}
@@ -1069,7 +1071,7 @@ namespace ts.server {
const checkList = fileNames.reduce((accum: PendingErrorCheck[], fileName: string) => {
fileName = ts.normalizePath(fileName);
const project = this.projectService.getProjectForFile(fileName);
if (project && !project.languageServiceDiabled) {
if (project) {
accum.push({ fileName, project });
}
return accum;
@@ -1099,7 +1101,7 @@ namespace ts.server {
const file = ts.normalizePath(fileName);
const tmpfile = ts.normalizePath(tempFileName);
const project = this.projectService.getProjectForFile(file);
if (project && !project.languageServiceDiabled) {
if (project) {
this.changeSeq++;
// make sure no changes happen before this one is finished
project.reloadScript(file, tmpfile, () => {
@@ -1126,7 +1128,7 @@ namespace ts.server {
this.projectService.closeClientFile(file);
}
private decorateNavigationBarItem(project: Project, fileName: string, items: ts.NavigationBarItem[], lineIndex: LineIndex): protocol.NavigationBarItem[] {
private decorateNavigationBarItem(project: Project, fileName: string, items: ts.NavigationBarItem[]): protocol.NavigationBarItem[] {
if (!items) {
return undefined;
}
@@ -1141,7 +1143,7 @@ namespace ts.server {
start: scriptInfo.positionToLineOffset(span.start),
end: scriptInfo.positionToLineOffset(ts.textSpanEnd(span))
})),
childItems: this.decorateNavigationBarItem(project, fileName, item.childItems, lineIndex),
childItems: this.decorateNavigationBarItem(project, fileName, item.childItems),
indent: item.indent
}));
}
@@ -604,7 +604,7 @@ namespace ts {
}`
};
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [file1, file2, configFile]);
const projectService = new server.ProjectService(host, nullLogger);
const projectService = new server.ProjectService(host, nullLogger, nullCancellationToken);
projectService.openClientFile(file1.path);
projectService.closeClientFile(file1.path);
projectService.openClientFile(file2.path);
@@ -631,7 +631,7 @@ namespace ts {
}`
};
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [file1, file2, configFile]);
const projectService = new server.ProjectService(host, nullLogger);
const projectService = new server.ProjectService(host, nullLogger, nullCancellationToken);
projectService.openClientFile(file1.path);
projectService.closeClientFile(file1.path);
projectService.openClientFile(file2.path);