mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-17 00:55:32 -05:00
introduce FileMap to store mappings with filenames as keys
This commit is contained in:
@@ -15,6 +15,38 @@ module ts {
|
||||
True = -1
|
||||
}
|
||||
|
||||
export class FileMap<T> {
|
||||
private files: Map<T> = {};
|
||||
|
||||
constructor(private getCanonicalFileName: (fileName: string) => string) {
|
||||
}
|
||||
|
||||
public set(fileName: string, value: T) {
|
||||
this.files[this.normalizeKey(fileName)] = value;
|
||||
}
|
||||
|
||||
public get(fileName: string) {
|
||||
return this.files[this.normalizeKey(fileName)];
|
||||
}
|
||||
|
||||
public contains(fileName: string) {
|
||||
return hasProperty(this.files, this.normalizeKey(fileName));
|
||||
}
|
||||
|
||||
public delete(fileName: string) {
|
||||
let key = this.normalizeKey(fileName);
|
||||
delete this.files[key];
|
||||
}
|
||||
|
||||
public forEachValue(f: (value: T) => void) {
|
||||
forEachValue(this.files, f);
|
||||
}
|
||||
|
||||
private normalizeKey(key: string) {
|
||||
return this.getCanonicalFileName(normalizeSlashes(key));
|
||||
}
|
||||
}
|
||||
|
||||
export const enum Comparison {
|
||||
LessThan = -1,
|
||||
EqualTo = 0,
|
||||
|
||||
@@ -149,7 +149,6 @@ module ts {
|
||||
export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost): Program {
|
||||
let program: Program;
|
||||
let files: SourceFile[] = [];
|
||||
let filesByName: Map<SourceFile> = {};
|
||||
let diagnostics = createDiagnosticCollection();
|
||||
let seenNoDefaultLib = options.noLib;
|
||||
let commonSourceDirectory: string;
|
||||
@@ -159,6 +158,8 @@ module ts {
|
||||
let start = new Date().getTime();
|
||||
|
||||
host = host || createCompilerHost(options);
|
||||
let filesByName: FileMap<SourceFile> = new FileMap<SourceFile>(host.getCanonicalFileName);
|
||||
|
||||
forEach(rootNames, name => processRootFile(name, false));
|
||||
if (!seenNoDefaultLib) {
|
||||
processRootFile(host.getDefaultLibFileName(options), true);
|
||||
@@ -238,8 +239,7 @@ module ts {
|
||||
}
|
||||
|
||||
function getSourceFile(fileName: string) {
|
||||
fileName = host.getCanonicalFileName(normalizeSlashes(fileName));
|
||||
return hasProperty(filesByName, fileName) ? filesByName[fileName] : undefined;
|
||||
return filesByName.get(fileName);
|
||||
}
|
||||
|
||||
function getDiagnosticsHelper(sourceFile: SourceFile, getDiagnostics: (sourceFile: SourceFile) => Diagnostic[]): Diagnostic[] {
|
||||
@@ -358,19 +358,19 @@ module ts {
|
||||
// Get source file from normalized fileName
|
||||
function findSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refStart?: number, refLength?: number): SourceFile {
|
||||
let canonicalName = host.getCanonicalFileName(normalizeSlashes(fileName));
|
||||
if (hasProperty(filesByName, canonicalName)) {
|
||||
if (filesByName.contains(canonicalName)) {
|
||||
// We've already looked for this file, use cached result
|
||||
return getSourceFileFromCache(fileName, canonicalName, /*useAbsolutePath*/ false);
|
||||
}
|
||||
else {
|
||||
let normalizedAbsolutePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory());
|
||||
let canonicalAbsolutePath = host.getCanonicalFileName(normalizedAbsolutePath);
|
||||
if (hasProperty(filesByName, canonicalAbsolutePath)) {
|
||||
if (filesByName.contains(canonicalAbsolutePath)) {
|
||||
return getSourceFileFromCache(normalizedAbsolutePath, canonicalAbsolutePath, /*useAbsolutePath*/ true);
|
||||
}
|
||||
|
||||
// We haven't looked for this file, do so now and cache result
|
||||
let file = filesByName[canonicalName] = host.getSourceFile(fileName, options.target, hostErrorMessage => {
|
||||
let file = host.getSourceFile(fileName, options.target, hostErrorMessage => {
|
||||
if (refFile) {
|
||||
diagnostics.add(createFileDiagnostic(refFile, refStart, refLength,
|
||||
Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
|
||||
@@ -379,11 +379,12 @@ module ts {
|
||||
diagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
|
||||
}
|
||||
});
|
||||
filesByName.set(canonicalName, file);
|
||||
if (file) {
|
||||
seenNoDefaultLib = seenNoDefaultLib || file.hasNoDefaultLib;
|
||||
|
||||
// Set the source file for normalized absolute path
|
||||
filesByName[canonicalAbsolutePath] = file;
|
||||
filesByName.set(canonicalAbsolutePath, file);
|
||||
|
||||
if (!options.noResolve) {
|
||||
let basePath = getDirectoryPath(fileName);
|
||||
@@ -402,7 +403,7 @@ module ts {
|
||||
}
|
||||
|
||||
function getSourceFileFromCache(fileName: string, canonicalName: string, useAbsolutePath: boolean): SourceFile {
|
||||
let file = filesByName[canonicalName];
|
||||
let file = filesByName.get(canonicalName);
|
||||
if (file && host.useCaseSensitiveFileNames()) {
|
||||
let sourceFileName = useAbsolutePath ? getNormalizedAbsolutePath(file.fileName, host.getCurrentDirectory()) : file.fileName;
|
||||
if (canonicalName !== sourceFileName) {
|
||||
|
||||
@@ -960,6 +960,7 @@ module ts {
|
||||
log? (s: string): void;
|
||||
trace? (s: string): void;
|
||||
error? (s: string): void;
|
||||
useCaseSensitiveFileNames? (): boolean;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1632,12 +1633,12 @@ module ts {
|
||||
// at each language service public entry point, since we don't know when
|
||||
// set of scripts handled by the host changes.
|
||||
class HostCache {
|
||||
private fileNameToEntry: Map<HostFileInformation>;
|
||||
private fileNameToEntry: FileMap<HostFileInformation>;
|
||||
private _compilationSettings: CompilerOptions;
|
||||
|
||||
constructor(private host: LanguageServiceHost, private getCanonicalFileName: (fileName: string) => string) {
|
||||
constructor(private host: LanguageServiceHost, getCanonicalFileName: (fileName: string) => string) {
|
||||
// script id => script index
|
||||
this.fileNameToEntry = {};
|
||||
this.fileNameToEntry = new FileMap<HostFileInformation>(getCanonicalFileName);
|
||||
|
||||
// Initialize the list with the root file names
|
||||
let rootFileNames = host.getScriptFileNames();
|
||||
@@ -1653,10 +1654,6 @@ module ts {
|
||||
return this._compilationSettings;
|
||||
}
|
||||
|
||||
private normalizeFileName(fileName: string): string {
|
||||
return this.getCanonicalFileName(normalizeSlashes(fileName));
|
||||
}
|
||||
|
||||
private createEntry(fileName: string) {
|
||||
let entry: HostFileInformation;
|
||||
let scriptSnapshot = this.host.getScriptSnapshot(fileName);
|
||||
@@ -1668,15 +1665,16 @@ module ts {
|
||||
};
|
||||
}
|
||||
|
||||
return this.fileNameToEntry[this.normalizeFileName(fileName)] = entry;
|
||||
this.fileNameToEntry.set(fileName, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
private getEntry(fileName: string): HostFileInformation {
|
||||
return lookUp(this.fileNameToEntry, this.normalizeFileName(fileName));
|
||||
return this.fileNameToEntry.get(fileName);
|
||||
}
|
||||
|
||||
private contains(fileName: string): boolean {
|
||||
return hasProperty(this.fileNameToEntry, this.normalizeFileName(fileName));
|
||||
return this.fileNameToEntry.contains(fileName);
|
||||
}
|
||||
|
||||
public getOrCreateEntry(fileName: string): HostFileInformation {
|
||||
@@ -1690,10 +1688,9 @@ module ts {
|
||||
public getRootFileNames(): string[] {
|
||||
let fileNames: string[] = [];
|
||||
|
||||
forEachKey(this.fileNameToEntry, key => {
|
||||
let entry = this.getEntry(key);
|
||||
if (entry) {
|
||||
fileNames.push(entry.hostFileName);
|
||||
this.fileNameToEntry.forEachValue(value => {
|
||||
if (value) {
|
||||
fileNames.push(value.hostFileName);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1873,20 +1870,28 @@ module ts {
|
||||
return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents:*/ true);
|
||||
}
|
||||
|
||||
export function createDocumentRegistry(): DocumentRegistry {
|
||||
function createGetCanonicalFileName(useCaseSensitivefileNames: boolean): (fileName: string) => string {
|
||||
return useCaseSensitivefileNames
|
||||
? ((fileName) => fileName)
|
||||
: ((fileName) => fileName.toLowerCase());
|
||||
}
|
||||
|
||||
|
||||
export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean): DocumentRegistry {
|
||||
// Maps from compiler setting target (ES3, ES5, etc.) to all the cached documents we have
|
||||
// for those settings.
|
||||
let buckets: Map<Map<DocumentRegistryEntry>> = {};
|
||||
let buckets: Map<FileMap<DocumentRegistryEntry>> = {};
|
||||
let getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames || false);
|
||||
|
||||
function getKeyFromCompilationSettings(settings: CompilerOptions): string {
|
||||
return "_" + settings.target; // + "|" + settings.propagateEnumConstantoString()
|
||||
}
|
||||
|
||||
function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): Map<DocumentRegistryEntry> {
|
||||
function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
|
||||
let key = getKeyFromCompilationSettings(settings);
|
||||
let bucket = lookUp(buckets, key);
|
||||
if (!bucket && createIfMissing) {
|
||||
buckets[key] = bucket = {};
|
||||
buckets[key] = bucket = new FileMap<DocumentRegistryEntry>(getCanonicalFileName);
|
||||
}
|
||||
return bucket;
|
||||
}
|
||||
@@ -1896,7 +1901,7 @@ module ts {
|
||||
let entries = lookUp(buckets, name);
|
||||
let sourceFiles: { name: string; refCount: number; references: string[]; }[] = [];
|
||||
for (let i in entries) {
|
||||
let entry = entries[i];
|
||||
let entry = entries.get(i);
|
||||
sourceFiles.push({
|
||||
name: i,
|
||||
refCount: entry.languageServiceRefCount,
|
||||
@@ -1928,18 +1933,19 @@ module ts {
|
||||
acquiring: boolean): SourceFile {
|
||||
|
||||
let bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true);
|
||||
let entry = lookUp(bucket, fileName);
|
||||
let entry = bucket.get(fileName);
|
||||
if (!entry) {
|
||||
Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?");
|
||||
|
||||
// Have never seen this file with these settings. Create a new source file for it.
|
||||
let sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents:*/ false);
|
||||
|
||||
bucket[fileName] = entry = {
|
||||
entry = {
|
||||
sourceFile: sourceFile,
|
||||
languageServiceRefCount: 0,
|
||||
owners: []
|
||||
};
|
||||
bucket.set(fileName, entry);
|
||||
}
|
||||
else {
|
||||
// We have an entry for this file. However, it may be for a different version of
|
||||
@@ -1967,12 +1973,12 @@ module ts {
|
||||
let bucket = getBucketForCompilationSettings(compilationSettings, false);
|
||||
Debug.assert(bucket !== undefined);
|
||||
|
||||
let entry = lookUp(bucket, fileName);
|
||||
let entry = bucket.get(fileName);
|
||||
entry.languageServiceRefCount--;
|
||||
|
||||
Debug.assert(entry.languageServiceRefCount >= 0);
|
||||
if (entry.languageServiceRefCount === 0) {
|
||||
delete bucket[fileName];
|
||||
bucket.delete(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2400,9 +2406,7 @@ module ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getCanonicalFileName(fileName: string) {
|
||||
return useCaseSensitivefileNames ? fileName : fileName.toLowerCase();
|
||||
}
|
||||
let getCanonicalFileName = createGetCanonicalFileName(useCaseSensitivefileNames);
|
||||
|
||||
function getValidSourceFile(fileName: string): SourceFile {
|
||||
fileName = normalizeSlashes(fileName);
|
||||
|
||||
@@ -56,6 +56,7 @@ module ts {
|
||||
getDefaultLibFileName(options: string): string;
|
||||
getNewLine?(): string;
|
||||
getProjectVersion?(): string;
|
||||
useCaseSensitiveFileNames?(): boolean;
|
||||
}
|
||||
|
||||
/** Public interface of the the of a config service shim instance.*/
|
||||
@@ -270,6 +271,10 @@ module ts {
|
||||
return this.shimHost.getProjectVersion();
|
||||
}
|
||||
|
||||
public useCaseSensitiveFileNames(): boolean {
|
||||
return this.shimHost.useCaseSensitiveFileNames && this.useCaseSensitiveFileNames();
|
||||
}
|
||||
|
||||
public getCompilationSettings(): CompilerOptions {
|
||||
var settingsJson = this.shimHost.getCompilationSettings();
|
||||
if (settingsJson == null || settingsJson == "") {
|
||||
@@ -909,7 +914,7 @@ module ts {
|
||||
|
||||
export class TypeScriptServicesFactory implements ShimFactory {
|
||||
private _shims: Shim[] = [];
|
||||
private documentRegistry: DocumentRegistry = createDocumentRegistry();
|
||||
private documentRegistry: DocumentRegistry;
|
||||
|
||||
/*
|
||||
* Returns script API version.
|
||||
@@ -920,6 +925,9 @@ module ts {
|
||||
|
||||
public createLanguageServiceShim(host: LanguageServiceShimHost): LanguageServiceShim {
|
||||
try {
|
||||
if (this.documentRegistry === undefined) {
|
||||
this.documentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
|
||||
}
|
||||
var hostAdapter = new LanguageServiceShimHostAdapter(host);
|
||||
var languageService = createLanguageService(hostAdapter, this.documentRegistry);
|
||||
return new LanguageServiceShimObject(this, host, languageService);
|
||||
|
||||
Reference in New Issue
Block a user