Revert "Merge pull request #11354 from Microsoft/map4"

This reverts commit adfdae0dc4, reversing
changes made to aad663cebf.
This commit is contained in:
Andy Hanson
2016-10-27 15:50:21 -07:00
parent adfdae0dc4
commit 12f6dcefa1
68 changed files with 1431 additions and 1782 deletions

View File

@@ -346,26 +346,25 @@ namespace ts.server {
// Use slice to clone the array to avoid manipulating in place
const queue = fileInfo.referencedBy.slice(0);
const fileNameSet = createMap<string, ScriptInfo>();
fileNameSet.set(scriptInfo.fileName, scriptInfo);
const fileNameSet = createMap<ScriptInfo>();
fileNameSet[scriptInfo.fileName] = scriptInfo;
while (queue.length > 0) {
const processingFileInfo = queue.pop();
if (processingFileInfo.updateShapeSignature() && processingFileInfo.referencedBy.length > 0) {
for (const potentialFileInfo of processingFileInfo.referencedBy) {
if (!fileNameSet.get(potentialFileInfo.scriptInfo.fileName)) {
if (!fileNameSet[potentialFileInfo.scriptInfo.fileName]) {
queue.push(potentialFileInfo);
}
}
}
fileNameSet.set(processingFileInfo.scriptInfo.fileName, processingFileInfo.scriptInfo);
fileNameSet[processingFileInfo.scriptInfo.fileName] = processingFileInfo.scriptInfo;
}
const result: string[] = [];
fileNameSet.forEach((scriptInfo, fileName) => {
if (shouldEmitFile(scriptInfo)) {
for (const fileName in fileNameSet) {
if (shouldEmitFile(fileNameSet[fileName])) {
result.push(fileName);
}
});
}
return result;
}
}

View File

@@ -15,7 +15,7 @@ namespace ts.server {
export class SessionClient implements LanguageService {
private sequence: number = 0;
private lineMaps = ts.createMap<string, number[]>();
private lineMaps: ts.Map<number[]> = ts.createMap<number[]>();
private messages: string[] = [];
private lastRenameEntry: RenameEntry;
@@ -31,10 +31,10 @@ namespace ts.server {
}
private getLineMap(fileName: string): number[] {
let lineMap = this.lineMaps.get(fileName);
let lineMap = this.lineMaps[fileName];
if (!lineMap) {
const scriptSnapshot = this.host.getScriptSnapshot(fileName);
lineMap = setAndReturn(this.lineMaps, fileName, ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength())));
lineMap = this.lineMaps[fileName] = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()));
}
return lineMap;
}
@@ -140,7 +140,7 @@ namespace ts.server {
changeFile(fileName: string, start: number, end: number, newText: string): void {
// clear the line map after an edit
this.lineMaps.set(fileName, undefined);
this.lineMaps[fileName] = undefined;
const lineOffset = this.positionToOneBasedLineOffset(fileName, start);
const endLineOffset = this.positionToOneBasedLineOffset(fileName, end);

View File

@@ -17,23 +17,23 @@ namespace ts.server {
(event: ProjectServiceEvent): void;
}
function prepareConvertersForEnumLikeCompilerOptions(commandLineOptions: CommandLineOption[]): Map<string, Map<string, number>> {
const map = createMap<string, Map<string, number>>();
function prepareConvertersForEnumLikeCompilerOptions(commandLineOptions: CommandLineOption[]): Map<Map<number>> {
const map: Map<Map<number>> = createMap<Map<number>>();
for (const option of commandLineOptions) {
if (typeof option.type === "object") {
const optionMap = <Map<string, number>>option.type;
const optionMap = <Map<number>>option.type;
// verify that map contains only numbers
optionMap.forEach(value => {
Debug.assert(typeof value === "number");
});
map.set(option.name, optionMap);
for (const id in optionMap) {
Debug.assert(typeof optionMap[id] === "number");
}
map[option.name] = optionMap;
}
}
return map;
}
const compilerOptionConverters = prepareConvertersForEnumLikeCompilerOptions(optionDeclarations);
const indentStyle = mapOfMapLike({
const indentStyle = createMap({
"none": IndentStyle.None,
"block": IndentStyle.Block,
"smart": IndentStyle.Smart
@@ -41,20 +41,20 @@ namespace ts.server {
export function convertFormatOptions(protocolOptions: protocol.FormatCodeSettings): FormatCodeSettings {
if (typeof protocolOptions.indentStyle === "string") {
protocolOptions.indentStyle = indentStyle.get(protocolOptions.indentStyle.toLowerCase());
protocolOptions.indentStyle = indentStyle[protocolOptions.indentStyle.toLowerCase()];
Debug.assert(protocolOptions.indentStyle !== undefined);
}
return <any>protocolOptions;
}
export function convertCompilerOptions(protocolOptions: protocol.ExternalProjectCompilerOptions): CompilerOptions & protocol.CompileOnSaveMixin {
forEachKeyInMap(compilerOptionConverters, id => {
for (const id in compilerOptionConverters) {
const propertyValue = protocolOptions[id];
if (typeof propertyValue === "string") {
const mappedValues = compilerOptionConverters.get(id);
protocolOptions[id] = mappedValues.get(propertyValue.toLowerCase());
const mappedValues = compilerOptionConverters[id];
protocolOptions[id] = mappedValues[propertyValue.toLowerCase()];
}
});
}
return <any>protocolOptions;
}
@@ -159,24 +159,23 @@ namespace ts.server {
/**
* a path to directory watcher map that detects added tsconfig files
**/
private readonly directoryWatchersForTsconfig = createMap<string, FileWatcher>();
private readonly directoryWatchersForTsconfig: Map<FileWatcher> = createMap<FileWatcher>();
/**
* count of how many projects are using the directory watcher.
* If the number becomes 0 for a watcher, then we should close it.
**/
private readonly directoryWatchersRefCount = createMap<string, number>();
private readonly directoryWatchersRefCount: Map<number> = createMap<number>();
constructor(private readonly projectService: ProjectService) {
}
stopWatchingDirectory(directory: string) {
// if the ref count for this directory watcher drops to 0, it's time to close it
const refCount = this.directoryWatchersRefCount.get(directory) - 1;
this.directoryWatchersRefCount.set(directory, refCount);
if (refCount === 0) {
this.directoryWatchersRefCount[directory]--;
if (this.directoryWatchersRefCount[directory] === 0) {
this.projectService.logger.info(`Close directory watcher for: ${directory}`);
this.directoryWatchersForTsconfig.get(directory).close();
this.directoryWatchersForTsconfig.delete(directory);
this.directoryWatchersForTsconfig[directory].close();
delete this.directoryWatchersForTsconfig[directory];
}
}
@@ -184,13 +183,13 @@ namespace ts.server {
let currentPath = getDirectoryPath(fileName);
let parentPath = getDirectoryPath(currentPath);
while (currentPath != parentPath) {
if (!this.directoryWatchersForTsconfig.get(currentPath)) {
if (!this.directoryWatchersForTsconfig[currentPath]) {
this.projectService.logger.info(`Add watcher for: ${currentPath}`);
this.directoryWatchersForTsconfig.set(currentPath, this.projectService.host.watchDirectory(currentPath, callback));
this.directoryWatchersRefCount.set(currentPath, 1);
this.directoryWatchersForTsconfig[currentPath] = this.projectService.host.watchDirectory(currentPath, callback);
this.directoryWatchersRefCount[currentPath] = 1;
}
else {
modifyValue(this.directoryWatchersRefCount, currentPath, count => count + 1);
this.directoryWatchersRefCount[currentPath] += 1;
}
project.directoriesWatchedForTsconfig.push(currentPath);
currentPath = parentPath;
@@ -212,7 +211,7 @@ namespace ts.server {
/**
* maps external project file name to list of config files that were the part of this project
*/
private readonly externalProjectToConfiguredProjectMap = createMap<string, NormalizedPath[]>();
private readonly externalProjectToConfiguredProjectMap: Map<NormalizedPath[]> = createMap<NormalizedPath[]>();
/**
* external projects (configuration and list of root files is not controlled by tsserver)
@@ -393,7 +392,7 @@ namespace ts.server {
}
else {
if (info && (!info.isOpen)) {
// file has been changed which might affect the set of referenced files in projects that include
// file has been changed which might affect the set of referenced files in projects that include
// this file and set of inferred projects
info.reloadFromFile();
this.updateProjectGraphs(info.containingProjects);
@@ -412,7 +411,7 @@ namespace ts.server {
this.filenameToScriptInfo.remove(info.path);
this.lastDeletedFile = info;
// capture list of projects since detachAllProjects will wipe out original list
// capture list of projects since detachAllProjects will wipe out original list
const containingProjects = info.containingProjects.slice();
info.detachAllProjects();
@@ -563,7 +562,7 @@ namespace ts.server {
const inferredProject = this.createInferredProjectWithRootFileIfNecessary(info);
if (!this.useSingleInferredProject) {
// if useOneInferredProject is not set then try to fixup ownership of open files
// check 'defaultProject !== inferredProject' is necessary to handle cases
// check 'defaultProject !== inferredProject' is necessary to handle cases
// when creation inferred project for some file has added other open files into this project (i.e. as referenced files)
// we definitely don't want to delete the project that was just created
for (const f of this.openFiles) {
@@ -573,7 +572,7 @@ namespace ts.server {
}
const defaultProject = f.getDefaultProject();
if (isRootFileInInferredProject(info) && defaultProject !== inferredProject && inferredProject.containsScriptInfo(f)) {
// open file used to be root in inferred project,
// open file used to be root in inferred project,
// this inferred project is different from the one we've just created for current file
// and new inferred project references this open file.
// We should delete old inferred project and attach open file to the new one
@@ -785,7 +784,7 @@ namespace ts.server {
files: parsedCommandLine.fileNames,
compilerOptions: parsedCommandLine.options,
configHasFilesProperty: config["files"] !== undefined,
wildcardDirectories: parsedCommandLine.wildcardDirectories,
wildcardDirectories: createMap(parsedCommandLine.wildcardDirectories),
typingOptions: parsedCommandLine.typingOptions,
compileOnSave: parsedCommandLine.compileOnSave
};
@@ -844,7 +843,7 @@ namespace ts.server {
this.documentRegistry,
projectOptions.configHasFilesProperty,
projectOptions.compilerOptions,
mapOfMapLike(projectOptions.wildcardDirectories),
projectOptions.wildcardDirectories,
/*languageServiceEnabled*/ !sizeLimitExceeded,
projectOptions.compileOnSave === undefined ? false : projectOptions.compileOnSave);
@@ -902,7 +901,7 @@ namespace ts.server {
private updateNonInferredProject<T>(project: ExternalProject | ConfiguredProject, newUncheckedFiles: T[], propertyReader: FilePropertyReader<T>, newOptions: CompilerOptions, newTypingOptions: TypingOptions, compileOnSave: boolean, configFileErrors: Diagnostic[]) {
const oldRootScriptInfos = project.getRootScriptInfos();
const newRootScriptInfos: ScriptInfo[] = [];
const newRootScriptInfoMap: Map<NormalizedPath, ScriptInfo> = createMap<string, ScriptInfo>();
const newRootScriptInfoMap: NormalizedPathMap<ScriptInfo> = createNormalizedPathMap<ScriptInfo>();
let projectErrors: Diagnostic[];
let rootFilesChanged = false;
@@ -930,7 +929,7 @@ namespace ts.server {
let toAdd: ScriptInfo[];
let toRemove: ScriptInfo[];
for (const oldFile of oldRootScriptInfos) {
if (!newRootScriptInfoMap.has(oldFile.fileName)) {
if (!newRootScriptInfoMap.contains(oldFile.fileName)) {
(toRemove || (toRemove = [])).push(oldFile);
}
}
@@ -947,7 +946,7 @@ namespace ts.server {
if (toAdd) {
for (const f of toAdd) {
if (f.isOpen && isRootFileInInferredProject(f)) {
// if file is already root in some inferred project
// if file is already root in some inferred project
// - remove the file from that project and delete the project if necessary
const inferredProject = f.containingProjects[0];
inferredProject.removeFile(f);
@@ -1096,7 +1095,7 @@ namespace ts.server {
this.logger.info(`Host information ${args.hostInfo}`);
}
if (args.formatOptions) {
mergeMapLikes(this.hostConfiguration.formatCodeOptions, convertFormatOptions(args.formatOptions));
mergeMaps(this.hostConfiguration.formatCodeOptions, convertFormatOptions(args.formatOptions));
this.logger.info("Format host information updated");
}
}
@@ -1218,7 +1217,7 @@ namespace ts.server {
for (const file of changedFiles) {
const scriptInfo = this.getScriptInfo(file.fileName);
Debug.assert(!!scriptInfo);
// apply changes in reverse order
// apply changes in reverse order
for (let i = file.changes.length - 1; i >= 0; i--) {
const change = file.changes[i];
scriptInfo.editContent(change.span.start, change.span.start + change.span.length, change.newText);
@@ -1255,7 +1254,7 @@ namespace ts.server {
closeExternalProject(uncheckedFileName: string, suppressRefresh = false): void {
const fileName = toNormalizedPath(uncheckedFileName);
const configFiles = this.externalProjectToConfiguredProjectMap.get(fileName);
const configFiles = this.externalProjectToConfiguredProjectMap[fileName];
if (configFiles) {
let shouldRefreshInferredProjects = false;
for (const configFile of configFiles) {
@@ -1263,7 +1262,7 @@ namespace ts.server {
shouldRefreshInferredProjects = true;
}
}
this.externalProjectToConfiguredProjectMap.delete(fileName);
delete this.externalProjectToConfiguredProjectMap[fileName];
if (shouldRefreshInferredProjects && !suppressRefresh) {
this.refreshInferredProjects();
}
@@ -1310,46 +1309,43 @@ namespace ts.server {
// close existing project and later we'll open a set of configured projects for these files
this.closeExternalProject(proj.projectFileName, /*suppressRefresh*/ true);
}
else {
const oldConfigFiles = this.externalProjectToConfiguredProjectMap.get(proj.projectFileName);
if (oldConfigFiles) {
// this project used to include config files
if (!tsConfigFiles) {
// config files were removed from the project - close existing external project which in turn will close configured projects
this.closeExternalProject(proj.projectFileName, /*suppressRefresh*/ true);
else if (this.externalProjectToConfiguredProjectMap[proj.projectFileName]) {
// this project used to include config files
if (!tsConfigFiles) {
// config files were removed from the project - close existing external project which in turn will close configured projects
this.closeExternalProject(proj.projectFileName, /*suppressRefresh*/ true);
}
else {
// project previously had some config files - compare them with new set of files and close all configured projects that correspond to unused files
const oldConfigFiles = this.externalProjectToConfiguredProjectMap[proj.projectFileName];
let iNew = 0;
let iOld = 0;
while (iNew < tsConfigFiles.length && iOld < oldConfigFiles.length) {
const newConfig = tsConfigFiles[iNew];
const oldConfig = oldConfigFiles[iOld];
if (oldConfig < newConfig) {
this.closeConfiguredProject(oldConfig);
iOld++;
}
else if (oldConfig > newConfig) {
iNew++;
}
else {
// record existing config files so avoid extra add-refs
(exisingConfigFiles || (exisingConfigFiles = [])).push(oldConfig);
iOld++;
iNew++;
}
}
else {
// project previously had some config files - compare them with new set of files and close all configured projects that correspond to unused files
let iNew = 0;
let iOld = 0;
while (iNew < tsConfigFiles.length && iOld < oldConfigFiles.length) {
const newConfig = tsConfigFiles[iNew];
const oldConfig = oldConfigFiles[iOld];
if (oldConfig < newConfig) {
this.closeConfiguredProject(oldConfig);
iOld++;
}
else if (oldConfig > newConfig) {
iNew++;
}
else {
// record existing config files so avoid extra add-refs
(exisingConfigFiles || (exisingConfigFiles = [])).push(oldConfig);
iOld++;
iNew++;
}
}
for (let i = iOld; i < oldConfigFiles.length; i++) {
// projects for all remaining old config files should be closed
this.closeConfiguredProject(oldConfigFiles[i]);
}
for (let i = iOld; i < oldConfigFiles.length; i++) {
// projects for all remaining old config files should be closed
this.closeConfiguredProject(oldConfigFiles[i]);
}
}
}
if (tsConfigFiles) {
// store the list of tsconfig files that belong to the external project
this.externalProjectToConfiguredProjectMap.set(proj.projectFileName, tsConfigFiles);
this.externalProjectToConfiguredProjectMap[proj.projectFileName] = tsConfigFiles;
for (const tsconfigFile of tsConfigFiles) {
let project = this.findConfiguredProjectByProjectName(tsconfigFile);
if (!project) {
@@ -1365,7 +1361,7 @@ namespace ts.server {
}
else {
// no config files - remove the item from the collection
this.externalProjectToConfiguredProjectMap.delete(proj.projectFileName);
delete this.externalProjectToConfiguredProjectMap[proj.projectFileName];
this.createAndAddExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typingOptions);
}
this.refreshInferredProjects();

View File

@@ -5,8 +5,8 @@
namespace ts.server {
export class LSHost implements ts.LanguageServiceHost, ModuleResolutionHost, ServerLanguageServiceHost {
private compilationSettings: ts.CompilerOptions;
private readonly resolvedModuleNames = createFileMap<Map<string, ResolvedModuleWithFailedLookupLocations>>();
private readonly resolvedTypeReferenceDirectives = createFileMap<Map<string, ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
private readonly resolvedModuleNames= createFileMap<Map<ResolvedModuleWithFailedLookupLocations>>();
private readonly resolvedTypeReferenceDirectives = createFileMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
private readonly getCanonicalFileName: (fileName: string) => string;
private filesWithChangedSetOfUnresolvedImports: Path[];
@@ -54,7 +54,7 @@ namespace ts.server {
private resolveNamesWithLocalCache<T extends { failedLookupLocations: string[] }, R>(
names: string[],
containingFile: string,
cache: ts.FileMap<Map<string, T>>,
cache: ts.FileMap<Map<T>>,
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => T,
getResult: (s: T) => R,
getResultFileName: (result: R) => string | undefined,
@@ -63,22 +63,22 @@ namespace ts.server {
const path = toPath(containingFile, this.host.getCurrentDirectory(), this.getCanonicalFileName);
const currentResolutionsInFile = cache.get(path);
const newResolutions = createMap<string, T>();
const newResolutions: Map<T> = createMap<T>();
const resolvedModules: R[] = [];
const compilerOptions = this.getCompilationSettings();
const lastDeletedFileName = this.project.projectService.lastDeletedFile && this.project.projectService.lastDeletedFile.fileName;
for (const name of names) {
// check if this is a duplicate entry in the list
let resolution = newResolutions.get(name);
let resolution = newResolutions[name];
if (!resolution) {
const existingResolution = currentResolutionsInFile && currentResolutionsInFile.get(name);
const existingResolution = currentResolutionsInFile && currentResolutionsInFile[name];
if (moduleResolutionIsValid(existingResolution)) {
// ok, it is safe to use existing name resolution results
resolution = existingResolution;
}
else {
newResolutions.set(name, resolution = loader(name, containingFile, compilerOptions, this));
newResolutions[name] = resolution = loader(name, containingFile, compilerOptions, this);
}
if (logChanges && this.filesWithChangedSetOfUnresolvedImports && !resolutionIsEqualTo(existingResolution, resolution)) {
this.filesWithChangedSetOfUnresolvedImports.push(path);

View File

@@ -104,7 +104,7 @@ namespace ts.server {
/**
* Set of files that was returned from the last call to getChangesSinceVersion.
*/
private lastReportedFileNames: Map<string, string>;
private lastReportedFileNames: Map<string>;
/**
* Last version that was reported.
*/
@@ -385,9 +385,9 @@ namespace ts.server {
}
let unresolvedImports: string[];
if (file.resolvedModules) {
file.resolvedModules.forEach((resolvedModule, name) => {
for (const name in file.resolvedModules) {
// pick unresolved non-relative names
if (!resolvedModule && !isExternalModuleNameRelative(name)) {
if (!file.resolvedModules[name] && !isExternalModuleNameRelative(name)) {
// for non-scoped names extract part up-to the first slash
// for scoped names - extract up to the second slash
let trimmed = name.trim();
@@ -401,7 +401,7 @@ namespace ts.server {
(unresolvedImports || (unresolvedImports = [])).push(trimmed);
result.push(trimmed);
}
});
}
}
this.cachedUnresolvedImportsPerFile.set(file.path, unresolvedImports || emptyArray);
}
@@ -427,7 +427,7 @@ namespace ts.server {
}
// 1. no changes in structure, no changes in unresolved imports - do nothing
// 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files
// 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files
// (can reuse cached imports for files that were not changed)
// 3. new files were added/removed, but compilation settings stays the same - collect unresolved imports for all new/modified files
// (can reuse cached imports for files that were not changed)
@@ -568,16 +568,16 @@ namespace ts.server {
const added: string[] = [];
const removed: string[] = [];
forEachKeyInMap(currentFiles, id => {
if (!lastReportedFileNames.has(id)) {
for (const id in currentFiles) {
if (!hasProperty(lastReportedFileNames, id)) {
added.push(id);
}
});
forEachKeyInMap(lastReportedFileNames, id => {
if (!currentFiles.has(id)) {
}
for (const id in lastReportedFileNames) {
if (!hasProperty(currentFiles, id)) {
removed.push(id);
}
});
}
this.lastReportedFileNames = currentFiles;
this.lastReportedVersion = this.projectStructureVersion;
return { info, changes: { added, removed }, projectErrors: this.projectErrors };
@@ -603,7 +603,7 @@ namespace ts.server {
// We need to use a set here since the code can contain the same import twice,
// but that will only be one dependency.
// To avoid invernal conversion, the key of the referencedFiles map must be of type Path
const referencedFiles = createSet();
const referencedFiles = createMap<boolean>();
if (sourceFile.imports && sourceFile.imports.length > 0) {
const checker: TypeChecker = this.program.getTypeChecker();
for (const importName of sourceFile.imports) {
@@ -611,7 +611,7 @@ namespace ts.server {
if (symbol && symbol.declarations && symbol.declarations[0]) {
const declarationSourceFile = symbol.declarations[0].getSourceFile();
if (declarationSourceFile) {
referencedFiles.add(declarationSourceFile.path);
referencedFiles[declarationSourceFile.path] = true;
}
}
}
@@ -623,24 +623,26 @@ namespace ts.server {
if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) {
for (const referencedFile of sourceFile.referencedFiles) {
const referencedPath = toPath(referencedFile.fileName, currentDirectory, getCanonicalFileName);
referencedFiles.add(referencedPath);
referencedFiles[referencedPath] = true;
}
}
// Handle type reference directives
if (sourceFile.resolvedTypeReferenceDirectiveNames) {
sourceFile.resolvedTypeReferenceDirectiveNames.forEach(resolvedTypeReferenceDirective => {
for (const typeName in sourceFile.resolvedTypeReferenceDirectiveNames) {
const resolvedTypeReferenceDirective = sourceFile.resolvedTypeReferenceDirectiveNames[typeName];
if (!resolvedTypeReferenceDirective) {
return;
continue;
}
const fileName = resolvedTypeReferenceDirective.resolvedFileName;
const typeFilePath = toPath(fileName, currentDirectory, getCanonicalFileName);
referencedFiles.add(typeFilePath);
});
referencedFiles[typeFilePath] = true;
}
}
return filterSetToArray(referencedFiles, file => this.projectService.host.fileExists(file)) as Path[];
const allFileNames = map(Object.keys(referencedFiles), key => <Path>key);
return filter(allFileNames, file => this.projectService.host.fileExists(file));
}
// remove a root file from project
@@ -711,7 +713,7 @@ namespace ts.server {
private typingOptions: TypingOptions;
private projectFileWatcher: FileWatcher;
private directoryWatcher: FileWatcher;
private directoriesWatchedForWildcards: Map<string, FileWatcher>;
private directoriesWatchedForWildcards: Map<FileWatcher>;
private typeRootsWatchers: FileWatcher[];
/** Used for configured projects which may have multiple open roots */
@@ -722,7 +724,7 @@ namespace ts.server {
documentRegistry: ts.DocumentRegistry,
hasExplicitListOfFiles: boolean,
compilerOptions: CompilerOptions,
private wildcardDirectories: Map<string, WatchDirectoryFlags>,
private wildcardDirectories: Map<WatchDirectoryFlags>,
languageServiceEnabled: boolean,
public compileOnSaveEnabled: boolean) {
super(ProjectKind.Configured, projectService, documentRegistry, hasExplicitListOfFiles, languageServiceEnabled, compilerOptions, compileOnSaveEnabled);
@@ -777,19 +779,18 @@ namespace ts.server {
return;
}
const configDirectoryPath = getDirectoryPath(this.configFileName);
this.directoriesWatchedForWildcards = createMap<string, FileWatcher>();
this.wildcardDirectories.forEach((flag, directory) => {
this.directoriesWatchedForWildcards = reduceProperties(this.wildcardDirectories, (watchers, flag, directory) => {
if (comparePaths(configDirectoryPath, directory, ".", !this.projectService.host.useCaseSensitiveFileNames) !== Comparison.EqualTo) {
const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0;
this.projectService.logger.info(`Add ${recursive ? "recursive " : ""}watcher for: ${directory}`);
this.directoriesWatchedForWildcards.set(directory, this.projectService.host.watchDirectory(
watchers[directory] = this.projectService.host.watchDirectory(
directory,
path => callback(this, path),
recursive
));
);
}
});
return watchers;
}, <Map<FileWatcher>>{});
}
stopWatchingDirectory() {
@@ -813,7 +814,9 @@ namespace ts.server {
this.typeRootsWatchers = undefined;
}
this.directoriesWatchedForWildcards.forEach(watcher => { watcher.close(); });
for (const id in this.directoriesWatchedForWildcards) {
this.directoriesWatchedForWildcards[id].close();
}
this.directoriesWatchedForWildcards = undefined;
this.stopWatchingDirectory();

View File

@@ -95,7 +95,7 @@ namespace ts.server {
if (!this.formatCodeSettings) {
this.formatCodeSettings = getDefaultFormatCodeSettings(this.host);
}
mergeMapLikes(this.formatCodeSettings, formatSettings);
mergeMaps(this.formatCodeSettings, formatSettings);
}
}

View File

@@ -1351,7 +1351,7 @@ namespace ts.server {
return { response, responseRequired: true };
}
private handlers = mapOfMapLike<(request: protocol.Request) => { response?: any, responseRequired?: boolean }>({
private handlers = createMap<(request: protocol.Request) => { response?: any, responseRequired?: boolean }>({
[CommandNames.OpenExternalProject]: (request: protocol.OpenExternalProjectRequest) => {
this.projectService.openExternalProject(request.arguments);
// TODO: report errors
@@ -1597,14 +1597,14 @@ namespace ts.server {
});
public addProtocolHandler(command: string, handler: (request: protocol.Request) => { response?: any, responseRequired: boolean }) {
if (this.handlers.has(command)) {
if (command in this.handlers) {
throw new Error(`Protocol handler already exists for command "${command}"`);
}
this.handlers.set(command, handler);
this.handlers[command] = handler;
}
public executeCommand(request: protocol.Request): { response?: any, responseRequired?: boolean } {
const handler = this.handlers.get(request.command);
const handler = this.handlers[request.command];
if (handler) {
return handler(request);
}

View File

@@ -18,7 +18,7 @@
"utilities.ts",
"scriptVersionCache.ts",
"scriptInfo.ts",
"lsHost.ts",
"lshost.ts",
"typingsCache.ts",
"project.ts",
"editorServices.ts",

View File

@@ -31,22 +31,21 @@ namespace ts.server {
if ((arr1 || emptyArray).length === 0 && (arr2 || emptyArray).length === 0) {
return true;
}
const set = createMap<string, boolean>();
const set: Map<boolean> = createMap<boolean>();
let unique = 0;
for (const v of arr1) {
if (set.get(v) !== true) {
set.set(v, true);
if (set[v] !== true) {
set[v] = true;
unique++;
}
}
for (const v of arr2) {
const isSet = set.get(v);
if (isSet === undefined) {
if (!hasProperty(set, v)) {
return false;
}
if (isSet === true) {
set.set(v, false);
if (set[v] === true) {
set[v] = false;
unique--;
}
}
@@ -72,7 +71,7 @@ namespace ts.server {
}
export class TypingsCache {
private readonly perProjectCache = createMap<string, TypingsCacheEntry>();
private readonly perProjectCache: Map<TypingsCacheEntry> = createMap<TypingsCacheEntry>();
constructor(private readonly installer: ITypingsInstaller) {
}
@@ -84,7 +83,7 @@ namespace ts.server {
return <any>emptyArray;
}
const entry = this.perProjectCache.get(project.getProjectName());
const entry = this.perProjectCache[project.getProjectName()];
const result: SortedReadonlyArray<string> = entry ? entry.typings : <any>emptyArray;
if (forceRefresh ||
!entry ||
@@ -93,13 +92,13 @@ namespace ts.server {
unresolvedImportsChanged(unresolvedImports, entry.unresolvedImports)) {
// Note: entry is now poisoned since it does not really contain typings for a given combination of compiler options\typings options.
// instead it acts as a placeholder to prevent issuing multiple requests
this.perProjectCache.set(project.getProjectName(), {
this.perProjectCache[project.getProjectName()] = {
compilerOptions: project.getCompilerOptions(),
typingOptions,
typings: result,
unresolvedImports,
poisoned: true
});
};
// something has been changed, issue a request to update typings
this.installer.enqueueInstallTypingsRequest(project, typingOptions, unresolvedImports);
}
@@ -107,21 +106,21 @@ namespace ts.server {
}
updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typingOptions: TypingOptions, unresolvedImports: SortedReadonlyArray<string>, newTypings: string[]) {
this.perProjectCache.set(projectName, {
this.perProjectCache[projectName] = {
compilerOptions,
typingOptions,
typings: toSortedReadonlyArray(newTypings),
unresolvedImports,
poisoned: false
});
};
}
deleteTypingsForProject(projectName: string) {
this.perProjectCache.delete(projectName);
delete this.perProjectCache[projectName];
}
onProjectClosed(project: Project) {
this.perProjectCache.delete(project.getProjectName());
delete this.perProjectCache[project.getProjectName()];
this.installer.onProjectClosed(project);
}
}

View File

@@ -78,10 +78,10 @@ namespace ts.server.typingsInstaller {
};
export abstract class TypingsInstaller {
private readonly packageNameToTypingLocation = createMap<string, string>();
private readonly missingTypingsSet = createSet();
private readonly knownCachesSet = createSet();
private readonly projectWatchers = createMap<string, FileWatcher[]>();
private readonly packageNameToTypingLocation: Map<string> = createMap<string>();
private readonly missingTypingsSet: Map<true> = createMap<true>();
private readonly knownCachesSet: Map<true> = createMap<true>();
private readonly projectWatchers: Map<FileWatcher[]> = createMap<FileWatcher[]>();
readonly pendingRunRequests: PendingRequest[] = [];
private installRunCount = 1;
@@ -111,7 +111,7 @@ namespace ts.server.typingsInstaller {
if (this.log.isEnabled()) {
this.log.writeLine(`Closing file watchers for project '${projectName}'`);
}
const watchers = this.projectWatchers.get(projectName);
const watchers = this.projectWatchers[projectName];
if (!watchers) {
if (this.log.isEnabled()) {
this.log.writeLine(`No watchers are registered for project '${projectName}'`);
@@ -122,7 +122,7 @@ namespace ts.server.typingsInstaller {
w.close();
}
this.projectWatchers.delete(projectName);
delete this.projectWatchers[projectName];
if (this.log.isEnabled()) {
this.log.writeLine(`Closing file watchers for project '${projectName}' - done.`);
@@ -176,7 +176,7 @@ namespace ts.server.typingsInstaller {
if (this.log.isEnabled()) {
this.log.writeLine(`Processing cache location '${cacheLocation}'`);
}
if (this.knownCachesSet.has(cacheLocation)) {
if (this.knownCachesSet[cacheLocation]) {
if (this.log.isEnabled()) {
this.log.writeLine(`Cache location was already processed...`);
}
@@ -202,7 +202,7 @@ namespace ts.server.typingsInstaller {
if (!typingFile) {
continue;
}
const existingTypingFile = this.packageNameToTypingLocation.get(packageName);
const existingTypingFile = this.packageNameToTypingLocation[packageName];
if (existingTypingFile === typingFile) {
continue;
}
@@ -214,14 +214,14 @@ namespace ts.server.typingsInstaller {
if (this.log.isEnabled()) {
this.log.writeLine(`Adding entry into typings cache: '${packageName}' => '${typingFile}'`);
}
this.packageNameToTypingLocation.set(packageName, typingFile);
this.packageNameToTypingLocation[packageName] = typingFile;
}
}
}
if (this.log.isEnabled()) {
this.log.writeLine(`Finished processing cache location '${cacheLocation}'`);
}
this.knownCachesSet.add(cacheLocation);
this.knownCachesSet[cacheLocation] = true;
}
private filterTypings(typingsToInstall: string[]) {
@@ -230,7 +230,7 @@ namespace ts.server.typingsInstaller {
}
const result: string[] = [];
for (const typing of typingsToInstall) {
if (this.missingTypingsSet.has(typing)) {
if (this.missingTypingsSet[typing]) {
continue;
}
const validationResult = validatePackageName(typing);
@@ -239,7 +239,7 @@ namespace ts.server.typingsInstaller {
}
else {
// add typing name to missing set so we won't process it again
this.missingTypingsSet.add(typing);
this.missingTypingsSet[typing] = true;
if (this.log.isEnabled()) {
switch (validationResult) {
case PackageNameValidationResult.EmptyName:
@@ -296,20 +296,20 @@ namespace ts.server.typingsInstaller {
if (this.log.isEnabled()) {
this.log.writeLine(`Requested to install typings ${JSON.stringify(typingsToInstall)}, installed typings ${JSON.stringify(installedTypings)}`);
}
const installedPackages = createSet();
const installedPackages: Map<true> = createMap<true>();
const installedTypingFiles: string[] = [];
for (const t of installedTypings) {
const packageName = getBaseFileName(t);
if (!packageName) {
continue;
}
installedPackages.add(packageName);
installedPackages[packageName] = true;
const typingFile = typingToFileName(cachePath, packageName, this.installTypingHost);
if (!typingFile) {
continue;
}
if (!this.packageNameToTypingLocation.get(packageName)) {
this.packageNameToTypingLocation.set(packageName, typingFile);
if (!this.packageNameToTypingLocation[packageName]) {
this.packageNameToTypingLocation[packageName] = typingFile;
}
installedTypingFiles.push(typingFile);
}
@@ -317,11 +317,11 @@ namespace ts.server.typingsInstaller {
this.log.writeLine(`Installed typing files ${JSON.stringify(installedTypingFiles)}`);
}
for (const toInstall of typingsToInstall) {
if (!installedPackages.has(toInstall)) {
if (!installedPackages[toInstall]) {
if (this.log.isEnabled()) {
this.log.writeLine(`New missing typing package '${toInstall}'`);
}
this.missingTypingsSet.add(toInstall);
this.missingTypingsSet[toInstall] = true;
}
}
@@ -395,7 +395,7 @@ namespace ts.server.typingsInstaller {
});
watchers.push(w);
}
this.projectWatchers.set(projectName, watchers);
this.projectWatchers[projectName] = watchers;
}
private createSetTypings(request: DiscoverTypings, typings: string[]): SetTypings {

View File

@@ -91,7 +91,7 @@ namespace ts.server {
};
}
export function mergeMapLikes(target: MapLike<any>, source: MapLike <any>): void {
export function mergeMaps(target: MapLike<any>, source: MapLike <any>): void {
for (const key in source) {
if (hasProperty(source, key)) {
target[key] = source[key];
@@ -132,6 +132,32 @@ namespace ts.server {
return <NormalizedPath>fileName;
}
export interface NormalizedPathMap<T> {
get(path: NormalizedPath): T;
set(path: NormalizedPath, value: T): void;
contains(path: NormalizedPath): boolean;
remove(path: NormalizedPath): void;
}
export function createNormalizedPathMap<T>(): NormalizedPathMap<T> {
/* tslint:disable:no-null-keyword */
const map: Map<T> = Object.create(null);
/* tslint:enable:no-null-keyword */
return {
get(path) {
return map[path];
},
set(path, value) {
map[path] = value;
},
contains(path) {
return hasProperty(map, path);
},
remove(path) {
delete map[path];
}
};
}
function throwLanguageServiceIsDisabledError(): never {
throw new Error("LanguageService is disabled");
}
@@ -204,7 +230,7 @@ namespace ts.server {
* these fields can be present in the project file
**/
files?: string[];
wildcardDirectories?: MapLike<WatchDirectoryFlags>;
wildcardDirectories?: Map<WatchDirectoryFlags>;
compilerOptions?: CompilerOptions;
typingOptions?: TypingOptions;
compileOnSave?: boolean;
@@ -225,22 +251,21 @@ namespace ts.server {
}
export class ThrottledOperations {
private pendingTimeouts = createMap<string, any>();
private pendingTimeouts: Map<any> = createMap<any>();
constructor(private readonly host: ServerHost) {
}
public schedule(operationId: string, delay: number, cb: () => void) {
const pendingTimeout = this.pendingTimeouts.get(operationId);
if (pendingTimeout !== undefined) {
if (hasProperty(this.pendingTimeouts, operationId)) {
// another operation was already scheduled for this id - cancel it
this.host.clearTimeout(pendingTimeout);
this.host.clearTimeout(this.pendingTimeouts[operationId]);
}
// schedule new operation, pass arguments
this.pendingTimeouts.set(operationId, this.host.setTimeout(ThrottledOperations.run, delay, this, operationId, cb));
this.pendingTimeouts[operationId] = this.host.setTimeout(ThrottledOperations.run, delay, this, operationId, cb);
}
private static run(self: ThrottledOperations, operationId: string, cb: () => void) {
self.pendingTimeouts.delete(operationId);
delete self.pendingTimeouts[operationId];
cb();
}
}