diff --git a/src/compiler/core.ts b/src/compiler/core.ts index effe24a253e..3669ece5104 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -989,43 +989,43 @@ namespace ts { return result; } - /** - * Adds the value to an array of values associated with the key, and returns the array. - * Creates the array if it does not already exist. - */ - export function multiMapAdd(map: Map, key: string, value: V): V[] { - let values = map.get(key); + export interface MultiMap extends Map { + /** + * Adds the value to an array of values associated with the key, and returns the array. + * Creates the array if it does not already exist. + */ + add(key: string, value: T): T[]; + /** + * Removes a value from an array of values associated with the key. + * Does not preserve the order of those values. + * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`. + */ + remove(key: string, value: T): void; + } + + export function createMultiMap(): MultiMap { + const map = createMap() as MultiMap; + map.add = multiMapAdd; + map.remove = multiMapRemove; + return map; + } + function multiMapAdd(this: MultiMap, key: string, value: T) { + let values = this.get(key); if (values) { values.push(value); } else { - map.set(key, values = [value]); + this.set(key, values = [value]); } return values; - } - export function multiMapSparseArrayAdd(map: SparseArray, key: number, value: V): V[] { - let values = map[key]; - if (values) { - values.push(value); - } - else { - map[key] = values = [value]; - } - return values; } - - /** - * Removes a value from an array of values associated with the key. - * Does not preserve the order of those values. - * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`. - */ - export function multiMapRemove(map: Map, key: string, value: V): void { - const values = map.get(key); + function multiMapRemove(this: MultiMap, key: string, value: T) { + const values = this.get(key); if (values) { unorderedRemoveItem(values, value); if (!values.length) { - map.delete(key); + this.delete(key); } } } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 38e28fc2de0..06108f2d0b2 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -3298,7 +3298,7 @@ namespace ts { export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo { const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; - const exportSpecifiers = createMap(); + const exportSpecifiers = createMultiMap(); const exportedBindings: SparseArray = []; const uniqueExports = createMap(); let exportedNames: Identifier[]; @@ -3352,7 +3352,7 @@ namespace ts { for (const specifier of (node).exportClause.elements) { if (!uniqueExports.get(specifier.name.text)) { const name = specifier.propertyName || specifier.name; - multiMapAdd(exportSpecifiers, name.text, specifier); + exportSpecifiers.add(name.text, specifier); const decl = resolver.getReferencedImportDeclaration(name) || resolver.getReferencedValueDeclaration(name); @@ -3446,4 +3446,16 @@ namespace ts { } return exportedNames; } + + /** Use a sparse array as a multi-map. */ + function multiMapSparseArrayAdd(map: SparseArray, key: number, value: V): V[] { + let values = map[key]; + if (values) { + values.push(value); + } + else { + map[key] = values = [value]; + } + return values; + } } diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index b10d746e435..3c28efd0de8 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -1,4 +1,4 @@ -/// +/// namespace ts { export type FileWatcherCallback = (fileName: string, removed?: boolean) => void; @@ -243,7 +243,7 @@ namespace ts { function createWatchedFileSet() { const dirWatchers = createMap(); // One file can have multiple watchers - const fileWatcherCallbacks = createMap(); + const fileWatcherCallbacks = createMultiMap(); return { addFile, removeFile }; function reduceDirWatcherRefCountForFile(fileName: string) { @@ -275,7 +275,7 @@ namespace ts { } function addFileWatcherCallback(filePath: string, callback: FileWatcherCallback): void { - multiMapAdd(fileWatcherCallbacks, filePath, callback); + fileWatcherCallbacks.add(filePath, callback); } function addFile(fileName: string, callback: FileWatcherCallback): WatchedFile { @@ -291,7 +291,7 @@ namespace ts { } function removeFileWatcherCallback(filePath: string, callback: FileWatcherCallback) { - multiMapRemove(fileWatcherCallbacks, filePath, callback); + fileWatcherCallbacks.remove(filePath, callback); } function fileEventHandler(eventName: string, relativeFileName: string, baseDirPath: string) { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 04fd55b110e..8b127afa081 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1760,10 +1760,10 @@ namespace FourSlash { } public rangesByText(): ts.Map { - const result = ts.createMap(); + const result = ts.createMultiMap(); for (const range of this.getRanges()) { const text = this.rangeText(range); - ts.multiMapAdd(result, text, range); + result.add(text, range); } return result; } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index caa51cd5566..c8729d49497 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -334,8 +334,8 @@ namespace ts.projectSystem { private timeoutCallbacks = new Callbacks(); private immediateCallbacks = new Callbacks(); - readonly watchedDirectories = createMap<{ cb: DirectoryWatcherCallback, recursive: boolean }[]>(); - readonly watchedFiles = createMap(); + readonly watchedDirectories = createMultiMap<{ cb: DirectoryWatcherCallback, recursive: boolean }>(); + readonly watchedFiles = createMultiMap(); private filesOrFolders: FileOrFolder[]; @@ -421,11 +421,11 @@ namespace ts.projectSystem { watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher { const path = this.toPath(directoryName); const cbWithRecursive = { cb: callback, recursive }; - multiMapAdd(this.watchedDirectories, path, cbWithRecursive); + this.watchedDirectories.add(path, cbWithRecursive); return { referenceCount: 0, directoryName, - close: () => multiMapRemove(this.watchedDirectories, path, cbWithRecursive) + close: () => this.watchedDirectories.remove(path, cbWithRecursive) }; } @@ -455,8 +455,8 @@ namespace ts.projectSystem { watchFile(fileName: string, callback: FileWatcherCallback) { const path = this.toPath(fileName); - multiMapAdd(this.watchedFiles, path, callback); - return { close: () => multiMapRemove(this.watchedFiles, path, callback) }; + this.watchedFiles.add(path, callback); + return { close: () => this.watchedFiles.remove(path, callback) }; } // TOOD: record and invoke callbacks to simulate timer events diff --git a/src/services/services.ts b/src/services/services.ts index 7cd2dce196d..86fbf22b71a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -518,7 +518,7 @@ namespace ts { } private computeNamedDeclarations(): Map { - const result = createMap(); + const result = createMultiMap(); forEachChild(this, visit); @@ -527,7 +527,7 @@ namespace ts { function addDeclaration(declaration: Declaration) { const name = getDeclarationName(declaration); if (name) { - multiMapAdd(result, name, declaration); + result.add(name, declaration); } }