From c06b02ac3426a06959e49f50636c9f86c2d620ef Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Fri, 24 Jun 2016 13:56:45 -0700 Subject: [PATCH 01/73] Adding completions for import and reference directives --- src/compiler/checker.ts | 13 + src/compiler/program.ts | 4 +- src/compiler/types.ts | 1 + src/harness/harness.ts | 4 +- src/harness/harnessLanguageService.ts | 56 +++- src/harness/virtualFileSystem.ts | 101 ++++--- src/server/editorServices.ts | 8 + src/services/services.ts | 278 +++++++++++++++++- src/services/shims.ts | 26 ++ ...etionForStringLiteralNonrelativeImport1.ts | 53 ++++ ...etionForStringLiteralNonrelativeImport2.ts | 32 ++ ...etionForStringLiteralNonrelativeImport3.ts | 30 ++ ...etionForStringLiteralNonrelativeImport4.ts | 45 +++ ...etionForStringLiteralNonrelativeImport5.ts | 28 ++ ...etionForStringLiteralNonrelativeImport6.ts | 57 ++++ ...mpletionForStringLiteralRelativeImport1.ts | 77 +++++ ...mpletionForStringLiteralRelativeImport2.ts | 43 +++ ...mpletionForStringLiteralRelativeImport3.ts | 41 +++ .../completionForTripleSlashReference1.ts | 79 +++++ .../completionForTripleSlashReference2.ts | 47 +++ .../completionForTripleSlashReference3.ts | 41 +++ 21 files changed, 1009 insertions(+), 55 deletions(-) create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts create mode 100644 tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts create mode 100644 tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts create mode 100644 tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts create mode 100644 tests/cases/fourslash/completionForTripleSlashReference1.ts create mode 100644 tests/cases/fourslash/completionForTripleSlashReference2.ts create mode 100644 tests/cases/fourslash/completionForTripleSlashReference3.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0ca0fd907fb..14cab98db0d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2,6 +2,8 @@ /* @internal */ namespace ts { + const ambientModuleSymbolRegex = /^".+"$/; + let nextSymbolId = 1; let nextNodeId = 1; let nextMergeId = 1; @@ -101,6 +103,7 @@ namespace ts { getAliasedSymbol: resolveAlias, getEmitResolver, getExportsOfModule: getExportsOfModuleAsArray, + getAmbientModules, getJsxElementAttributesType, getJsxIntrinsicTagNames, @@ -19104,5 +19107,15 @@ namespace ts { return true; } } + + function getAmbientModules(): Symbol[] { + const result: Symbol[] = []; + for (const sym in globals) { + if (globals.hasOwnProperty(sym) && ambientModuleSymbolRegex.test(sym)) { + result.push(globals[sym]); + } + } + return result; + } } } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index a01f3a4c5b2..2e5a5950098 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -15,9 +15,9 @@ namespace ts { const defaultTypeRoots = ["node_modules/@types"]; - export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean): string { + export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName="tsconfig.json"): string { while (true) { - const fileName = combinePaths(searchPath, "tsconfig.json"); + const fileName = combinePaths(searchPath, configName); if (fileExists(fileName)) { return fileName; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cac04172a5c..c2cf3a8527f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1854,6 +1854,7 @@ namespace ts { getJsxElementAttributesType(elementNode: JsxOpeningLikeElement): Type; getJsxIntrinsicTagNames(): Symbol[]; isOptionalParameter(node: ParameterDeclaration): boolean; + getAmbientModules(): Symbol[]; // Should not be called directly. Should only be accessed through the Program instance. /* @internal */ getDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 1977d95492c..ace211f2553 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -742,14 +742,14 @@ namespace Harness { } export function readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]) { - const fs = new Utils.VirtualFileSystem(path, useCaseSensitiveFileNames()); + const fs = new Utils.VirtualFileSystem(path, useCaseSensitiveFileNames()); for (const file in listFiles(path)) { fs.addFile(file); } return ts.matchFiles(path, extension, exclude, include, useCaseSensitiveFileNames(), getCurrentDirectory(), path => { const entry = fs.traversePath(path); if (entry && entry.isDirectory()) { - const directory = entry; + const directory = >entry; return { files: ts.map(directory.getFiles(), f => f.name), directories: ts.map(directory.getDirectories(), d => d.name) diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 8567a9109de..682496442e9 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -123,7 +123,7 @@ namespace Harness.LanguageService { } export class LanguageServiceAdapterHost { - protected fileNameToScript: ts.Map = {}; + protected virtualFileSystem: Utils.VirtualFileSystem = new Utils.VirtualFileSystem(/*root*/"c:", /*useCaseSensitiveFilenames*/false); constructor(protected cancellationToken = DefaultHostCancellationToken.Instance, protected settings = ts.getDefaultCompilerOptions()) { @@ -135,7 +135,8 @@ namespace Harness.LanguageService { public getFilenames(): string[] { const fileNames: string[] = []; - ts.forEachValue(this.fileNameToScript, (scriptInfo) => { + this.virtualFileSystem.getAllFileEntries().forEach((virtualEntry) => { + const scriptInfo = virtualEntry.content; if (scriptInfo.isRootFile) { // only include root files here // usually it means that we won't include lib.d.ts in the list of root files so it won't mess the computation of compilation root dir. @@ -146,11 +147,12 @@ namespace Harness.LanguageService { } public getScriptInfo(fileName: string): ScriptInfo { - return ts.lookUp(this.fileNameToScript, fileName); + const fileEntry = this.virtualFileSystem.traversePath(fileName); + return fileEntry && fileEntry.isFile() ? (>fileEntry).content : undefined; } public addScript(fileName: string, content: string, isRootFile: boolean): void { - this.fileNameToScript[fileName] = new ScriptInfo(fileName, content, isRootFile); + this.virtualFileSystem.addFile(fileName, new ScriptInfo(fileName, content, isRootFile)); } public editScript(fileName: string, start: number, end: number, newText: string) { @@ -171,7 +173,7 @@ namespace Harness.LanguageService { * @param col 0 based index */ public positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter { - const script: ScriptInfo = this.fileNameToScript[fileName]; + const script: ScriptInfo = this.getScriptInfo(fileName); assert.isOk(script); return ts.computeLineAndCharacterOfPosition(script.getLineMap(), position); @@ -182,7 +184,13 @@ namespace Harness.LanguageService { class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceHost { getCompilationSettings() { return this.settings; } getCancellationToken() { return this.cancellationToken; } - getDirectories(path: string): string[] { return []; } + getDirectories(path: string): string[] { + const dir = this.virtualFileSystem.traversePath(path); + if (dir && dir.isDirectory()) { + return ts.map((>dir).getDirectories(), (d) => ts.combinePaths(path, d.name)); + } + return []; + } getCurrentDirectory(): string { return ""; } getDefaultLibFileName(): string { return Harness.Compiler.defaultLibFileName; } getScriptFileNames(): string[] { return this.getFilenames(); } @@ -196,6 +204,39 @@ namespace Harness.LanguageService { return script ? script.version.toString() : undefined; } + fileExists(fileName: string): boolean { + const script = this.getScriptSnapshot(fileName); + return script !== undefined; + } + readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] { + return ts.matchFiles(path, extensions, exclude, include, + /*useCaseSensitiveFileNames*/false, + /*currentDirectory*/"/", + (p) => this.virtualFileSystem.getAccessibleFileSystemEntries(p)); + } + readFile(path: string, encoding?: string): string { + const snapshot = this.getScriptSnapshot(path); + return snapshot.getText(0, snapshot.getLength()); + } + resolvePath(path: string): string { + // Reduce away "." and ".." + const parts = path.split("/"); + const res: string[] = []; + for (let i = 0; i < parts.length; i++) { + if (parts[i] === ".") { + continue; + } + else if (parts[i] === ".." && res.length > 0) { + res.splice(res.length - 1, 1); + } + else { + res.push(parts[i]); + } + } + return res.join("/"); + } + + log(s: string): void { } trace(s: string): void { } error(s: string): void { } @@ -299,6 +340,9 @@ namespace Harness.LanguageService { const snapshot = this.nativeHost.getScriptSnapshot(fileName); return snapshot && snapshot.getText(0, snapshot.getLength()); } + resolvePath(path: string): string { + return this.nativeHost.resolvePath(path); + } log(s: string): void { this.nativeHost.log(s); } trace(s: string): void { this.nativeHost.trace(s); } error(s: string): void { this.nativeHost.error(s); } diff --git a/src/harness/virtualFileSystem.ts b/src/harness/virtualFileSystem.ts index 30192b8b8ec..5a89efea7fa 100644 --- a/src/harness/virtualFileSystem.ts +++ b/src/harness/virtualFileSystem.ts @@ -1,11 +1,11 @@ /// /// namespace Utils { - export class VirtualFileSystemEntry { - fileSystem: VirtualFileSystem; + export class VirtualFileSystemEntry { + fileSystem: VirtualFileSystem; name: string; - constructor(fileSystem: VirtualFileSystem, name: string) { + constructor(fileSystem: VirtualFileSystem, name: string) { this.fileSystem = fileSystem; this.name = name; } @@ -15,15 +15,15 @@ namespace Utils { isFileSystem() { return false; } } - export class VirtualFile extends VirtualFileSystemEntry { - content: string; + export class VirtualFile extends VirtualFileSystemEntry { + content: T; isFile() { return true; } } - export abstract class VirtualFileSystemContainer extends VirtualFileSystemEntry { - abstract getFileSystemEntries(): VirtualFileSystemEntry[]; + export abstract class VirtualFileSystemContainer extends VirtualFileSystemEntry { + abstract getFileSystemEntries(): VirtualFileSystemEntry[]; - getFileSystemEntry(name: string): VirtualFileSystemEntry { + getFileSystemEntry(name: string): VirtualFileSystemEntry { for (const entry of this.getFileSystemEntries()) { if (this.fileSystem.sameName(entry.name, name)) { return entry; @@ -32,57 +32,57 @@ namespace Utils { return undefined; } - getDirectories(): VirtualDirectory[] { - return ts.filter(this.getFileSystemEntries(), entry => entry.isDirectory()); + getDirectories(): VirtualDirectory[] { + return []>ts.filter(this.getFileSystemEntries(), entry => entry.isDirectory()); } - getFiles(): VirtualFile[] { - return ts.filter(this.getFileSystemEntries(), entry => entry.isFile()); + getFiles(): VirtualFile[] { + return []>ts.filter(this.getFileSystemEntries(), entry => entry.isFile()); } - getDirectory(name: string): VirtualDirectory { + getDirectory(name: string): VirtualDirectory { const entry = this.getFileSystemEntry(name); - return entry.isDirectory() ? entry : undefined; + return entry.isDirectory() ? >entry : undefined; } - getFile(name: string): VirtualFile { + getFile(name: string): VirtualFile { const entry = this.getFileSystemEntry(name); - return entry.isFile() ? entry : undefined; + return entry.isFile() ? >entry : undefined; } } - export class VirtualDirectory extends VirtualFileSystemContainer { - private entries: VirtualFileSystemEntry[] = []; + export class VirtualDirectory extends VirtualFileSystemContainer { + private entries: VirtualFileSystemEntry[] = []; isDirectory() { return true; } getFileSystemEntries() { return this.entries.slice(); } - addDirectory(name: string): VirtualDirectory { + addDirectory(name: string): VirtualDirectory { const entry = this.getFileSystemEntry(name); if (entry === undefined) { - const directory = new VirtualDirectory(this.fileSystem, name); + const directory = new VirtualDirectory(this.fileSystem, name); this.entries.push(directory); return directory; } else if (entry.isDirectory()) { - return entry; + return >entry; } else { return undefined; } } - addFile(name: string, content?: string): VirtualFile { + addFile(name: string, content?: T): VirtualFile { const entry = this.getFileSystemEntry(name); if (entry === undefined) { - const file = new VirtualFile(this.fileSystem, name); + const file = new VirtualFile(this.fileSystem, name); file.content = content; this.entries.push(file); return file; } else if (entry.isFile()) { - const file = entry; + const file = >entry; file.content = content; return file; } @@ -92,8 +92,8 @@ namespace Utils { } } - export class VirtualFileSystem extends VirtualFileSystemContainer { - private root: VirtualDirectory; + export class VirtualFileSystem extends VirtualFileSystemContainer { + private root: VirtualDirectory; currentDirectory: string; useCaseSensitiveFileNames: boolean; @@ -101,7 +101,7 @@ namespace Utils { constructor(currentDirectory: string, useCaseSensitiveFileNames: boolean) { super(undefined, ""); this.fileSystem = this; - this.root = new VirtualDirectory(this, ""); + this.root = new VirtualDirectory(this, ""); this.currentDirectory = currentDirectory; this.useCaseSensitiveFileNames = useCaseSensitiveFileNames; } @@ -112,7 +112,7 @@ namespace Utils { addDirectory(path: string) { const components = ts.getNormalizedPathComponents(path, this.currentDirectory); - let directory: VirtualDirectory = this.root; + let directory: VirtualDirectory = this.root; for (const component of components) { directory = directory.addDirectory(component); if (directory === undefined) { @@ -123,7 +123,7 @@ namespace Utils { return directory; } - addFile(path: string, content?: string) { + addFile(path: string, content?: T) { const absolutePath = ts.getNormalizedAbsolutePath(path, this.currentDirectory); const fileName = ts.getBaseFileName(path); const directoryPath = ts.getDirectoryPath(absolutePath); @@ -141,14 +141,14 @@ namespace Utils { } traversePath(path: string) { - let directory: VirtualDirectory = this.root; + let directory: VirtualDirectory = this.root; for (const component of ts.getNormalizedPathComponents(path, this.currentDirectory)) { const entry = directory.getFileSystemEntry(component); if (entry === undefined) { return undefined; } else if (entry.isDirectory()) { - directory = entry; + directory = >entry; } else { return entry; @@ -157,9 +157,34 @@ namespace Utils { return directory; } + + getAccessibleFileSystemEntries(path: string) { + const entry = this.traversePath(path); + if (entry && entry.isDirectory()) { + const directory = >entry; + return { + files: ts.map(directory.getFiles(), f => f.name), + directories: ts.map(directory.getDirectories(), d => d.name) + }; + } + return { files: [], directories: [] }; + } + + getAllFileEntries() { + const fileEntries: VirtualFile[] = []; + getFilesRecursive(this.root, fileEntries); + return fileEntries; + + function getFilesRecursive(dir: VirtualDirectory, result: VirtualFile[]) { + dir.getFiles().forEach((e) => result.push(e)); + dir.getDirectories().forEach((subDir) => getFilesRecursive(subDir, result)); + } + } + + } - export class MockParseConfigHost extends VirtualFileSystem implements ts.ParseConfigHost { + export class MockParseConfigHost extends VirtualFileSystem implements ts.ParseConfigHost { constructor(currentDirectory: string, ignoreCase: boolean, files: string[]) { super(currentDirectory, ignoreCase); for (const file of files) { @@ -170,17 +195,5 @@ namespace Utils { readDirectory(path: string, extensions: string[], excludes: string[], includes: string[]) { return ts.matchFiles(path, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, (path: string) => this.getAccessibleFileSystemEntries(path)); } - - getAccessibleFileSystemEntries(path: string) { - const entry = this.traversePath(path); - if (entry && entry.isDirectory()) { - const directory = entry; - return { - files: ts.map(directory.getFiles(), f => f.name), - directories: ts.map(directory.getDirectories(), d => d.name) - }; - } - return { files: [], directories: [] }; - } } } \ No newline at end of file diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 9c0662e5534..b94c366e7c9 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -321,6 +321,14 @@ namespace ts.server { return this.host.getDirectories(path); } + readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] { + return this.host.readDirectory(path, extensions, exclude, include); + } + + readFile(path: string, encoding?: string): string { + return this.host.readFile(path, encoding); + } + /** * @param line 1 based index */ diff --git a/src/services/services.ts b/src/services/services.ts index 492be6f1719..c6c8a7a2e83 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1070,6 +1070,11 @@ namespace ts { error?(s: string): void; useCaseSensitiveFileNames?(): boolean; + readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[]; + resolvePath(path: string): string; + readFile(path: string, encoding?: string): string; + fileExists(path: string): boolean; + /* * LS host can optionally implement this method if it wants to be completely in charge of module name resolution. * if implementation is omitted then language service will use built-in module resolution logic and get answers to @@ -1943,6 +1948,9 @@ namespace ts { } + const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions); + const tripleSlashDirectivePrefixRegex = /^\/\/\/\s*node); + } else { // Otherwise, get the completions from the contextual type if one exists return getStringLiteralCompletionEntriesFromContextualType(node); @@ -4314,6 +4339,258 @@ namespace ts { } } } + + function getStringLiteralCompletionEntriesFromModuleNames(node: StringLiteral) { + const literalValue = node.text; + let result: CompletionEntry[]; + + const isRelativePath = startsWith(literalValue, "."); + const scriptDir = getDirectoryPath(node.getSourceFile().path); + if (isRelativePath || isRootedDiskPath(literalValue)) { + result = getCompletionEntriesForDirectoryFragment(literalValue, scriptDir, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false); + } + else { + // Check for node modules + result = getCompletionEntriesForNonRelativeModules(literalValue, scriptDir); + } + + return { + isMemberCompletion: false, + isNewIdentifierLocation: false, + entries: result + }; + } + + function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean): CompletionEntry[] { + // Complete the path by looking for source files and directories + const result: CompletionEntry[] = []; + + const toComplete = getBaseFileName(fragment); + const absolutePath = normalizeSlashes(host.resolvePath(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment))); + const baseDir = getDirectoryPath(absolutePath); + + + if (directoryProbablyExists(baseDir, host)) { + // Enumerate the available files + const files = host.readDirectory(baseDir, extensions, /*exclude*/undefined, /*include*/["./*"]); + files.forEach((f) => { + const fName = includeExtensions ? getBaseFileName(f) : removeFileExtension(getBaseFileName(f)); + + if (startsWith(fName, toComplete)) { + result.push({ + name: fName, + kind: ScriptElementKind.unknown, + kindModifiers: ScriptElementKindModifier.none, + sortText: fName + }); + } + }); + + // If possible, get folder completion as well + if (host.getDirectories) { + const directories = host.getDirectories(baseDir); + directories.forEach((d) => { + const dName = getBaseFileName(removeTrailingDirectorySeparator(d)); + + if (startsWith(dName, toComplete)) { + result.push({ + name: ensureTrailingDirectorySeparator(dName), + kind: ScriptElementKind.unknown, + kindModifiers: ScriptElementKindModifier.none, + sortText: dName + }); + } + }); + } + } + + return includeExtensions ? result : deduplicate(result, (a, b) => a.name === b.name); + } + + /** + * Check all of the declared modules and those in node modules. Possible sources of modules: + * Modules declared in the program + * Modules from node_modules (i.e. those listed in package.json) + * This includes all files that are found in node_modules/moduleName/ and node_modules/@types/moduleName/ + * with acceptable file extensions + */ + function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string): CompletionEntry[] { + return ts.map(enumeratePotentialNonRelativeModules(fragment, scriptPath), (moduleName) => { + return { + name: moduleName, + kind: ScriptElementKind.unknown, + kindModifiers: ScriptElementKindModifier.none, + sortText: moduleName + }; + }); + } + + function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string) { + const ambientSymbolNameRegex = /^"(.+?)"$/; + + // If this is a nested module, get the module name + const firstSeparator = fragment.indexOf(directorySeparator); + const moduleNameFragment = firstSeparator !== -1 ? fragment.substr(0, firstSeparator) : fragment; + const isNestedModule = fragment !== moduleNameFragment; + + // Get modules that the type checker picked up + const ambientModules = ts.map(program.getTypeChecker().getAmbientModules(), (sym) => { + const match = ambientSymbolNameRegex.exec(sym.name); + if (match) { + return match[1]; + } + // This should never happen + return sym.name; + }); + let nonRelativeModules = ts.filter(ambientModules, (moduleName) => startsWith(moduleName, fragment)); + + // Nested modules of the form "module-name/sub" need to be adjusted to only return the string + // after the last '/' that appears in the fragment because editors insert the completion + // only after that character + nonRelativeModules = ts.map(nonRelativeModules, (moduleName) => { + if (moduleName.indexOf(directorySeparator) !== -1) { + if (isNestedModule) { + return moduleName.substr(fragment.lastIndexOf(directorySeparator) + 1); + } + } + return moduleName; + }); + + // Check for node_modules. We only offer completions for modules that are listed in the + // package.json for a project for efficiency and to ensure that the completion list is + // not polluted with sub-dependencies + findPackageJsons(scriptPath).forEach((packageJson) => { + const package = tryReadingPackageJson(packageJson); + if (!package) { + return; + } + + const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules"); + const foundModuleNames: string[] = []; + + if (package.dependencies) { + addPotentialPackageNames(package.dependencies, moduleNameFragment, foundModuleNames); + } + if (package.devDependencies) { + addPotentialPackageNames(package.devDependencies, moduleNameFragment, foundModuleNames); + } + + foundModuleNames.forEach((moduleName) => { + if (isNestedModule && moduleName === moduleNameFragment) { + const moduleDir = combinePaths(nodeModulesDir, moduleName); + if (directoryProbablyExists(moduleDir, host)) { + // Get all possible nested module names from files with all extensions + const nestedFiles = host.readDirectory(moduleDir, allSupportedExtensions, /*exclude*/undefined, /*include*/["./*"]); + + // Add those with typings to the completion list + nestedFiles.forEach((f) => { + const nestedModule = removeFileExtension(getBaseFileName(f)); + if (hasTypeScriptFileExtension(f)) { + nonRelativeModules.push(nestedModule); + } + }); + } + } + else if (startsWith(moduleName, fragment)) { + if (moduleCanBeImported(combinePaths(nodeModulesDir, moduleName))) { + nonRelativeModules.push(moduleName); + } + else { + nonRelativeModules.push(ensureTrailingDirectorySeparator(moduleName)); + } + } + }); + }); + + return deduplicate(nonRelativeModules); + } + + function findPackageJsons(currentDir: string): string[] { + const paths: string[] = []; + let currentConfigPath: string; + while (true) { + currentConfigPath = findConfigFile(currentDir, (f) => host.fileExists(f), "package.json"); + if (currentConfigPath) { + paths.push(currentConfigPath); + + currentDir = getDirectoryPath(currentConfigPath); + const parent = getDirectoryPath(currentDir); + if (currentDir === parent) { + break; + } + currentDir = parent; + } + else { + break; + } + } + + return paths; + } + + function tryReadingPackageJson(filePath: string) { + try { + const fileText = host.readFile(filePath); + return JSON.parse(fileText); + } + catch (e) { + return undefined; + } + } + + function addPotentialPackageNames(dependencies: any, prefix: string, result: string[]) { + for (const dep in dependencies) { + if (dependencies.hasOwnProperty(dep) && startsWith(dep, prefix)) { + result.push(dep); + } + } + } + + /* + * A module can be imported by name alone if one of the following is true: + * It defines the "typings" property in its package.json + * The module has a "main" export and an index.d.ts file + * The module has an index.ts + */ + function moduleCanBeImported(modulePath: string): boolean { + const packagePath = combinePaths(modulePath, "package.json"); + + let hasMainExport = false; + if (host.fileExists(packagePath)) { + const package = tryReadingPackageJson(packagePath); + if (package) { + if (package.typings) { + return true; + } + hasMainExport = !!package.main; + } + } + + hasMainExport = hasMainExport || host.fileExists(combinePaths(modulePath, "index.js")); + + return (hasMainExport && host.fileExists(combinePaths(modulePath, "index.d.ts"))) || host.fileExists(combinePaths(modulePath, "index.ts")); + } + + function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number) { + const node = getTokenAtPosition(sourceFile, position); + if (!node) { + return undefined; + } + + const text = sourceFile.text.substr(node.pos, position); + const match = tripleSlashDirectiveFragmentRegex.exec(text); + if (match) { + const fragment = match[1]; + const scriptPath = getDirectoryPath(sourceFile.path); + return { + isMemberCompletion: false, + isNewIdentifierLocation: false, + entries: getCompletionEntriesForDirectoryFragment(fragment, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true) + }; + } + + return undefined; + } } function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails { @@ -6189,7 +6466,6 @@ namespace ts { symbolToIndex: number[]): void { const sourceFile = container.getSourceFile(); - const tripleSlashDirectivePrefixRegex = /^\/\/\/\s* + +// @Filename: tests/test0.ts +//// import * as foo from "f/*0*/ + +// @Filename: tests/test1.ts +//// import * as foo from "fake-module//*1*/ + +// @Filename: tests/test2.ts +//// import * as foo from "fake-module/*2*/ + +// @Filename: package.json +//// { "dependencies": { "fake-module": "latest" }, "devDependencies": { "fake-module-dev": "latest" } } + +// @Filename: node_modules/fake-module/index.js +//// /*fake-module*/ +// @Filename: node_modules/fake-module/index.d.ts +//// /*fakemodule-d-ts*/ +// @Filename: node_modules/fake-module/ts.ts +//// /*ts*/ +// @Filename: node_modules/fake-module/dts.d.ts +//// /*dts*/ +// @Filename: node_modules/fake-module/tsx.tsx +//// /*tsx*/ +// @Filename: node_modules/fake-module/js.js +//// /*js*/ +// @Filename: node_modules/fake-module/jsx.jsx +//// /*jsx*/ + +// @Filename: node_modules/fake-module-dev/index.js +//// /*fakemodule-dev*/ +// @Filename: node_modules/fake-module-dev/index.d.ts +//// /*fakemodule-dev-d-ts*/ + +// @Filename: node_modules/unlisted-module/index.ts +//// /*unlisted-module*/ + +goTo.marker("0"); +verify.completionListContains("fake-module"); +verify.completionListContains("fake-module-dev"); +verify.not.completionListItemsCountIsGreaterThan(2); + +goTo.marker("1"); +verify.completionListContains("index"); +verify.completionListContains("ts"); +verify.completionListContains("dts"); +verify.completionListContains("tsx"); +verify.not.completionListItemsCountIsGreaterThan(4); + +goTo.marker("2"); +verify.completionListContains("fake-module"); +verify.completionListContains("fake-module-dev"); +verify.not.completionListItemsCountIsGreaterThan(2); \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts new file mode 100644 index 00000000000..c3e6f2e9062 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts @@ -0,0 +1,32 @@ +/// + +// @Filename: tests/test0.ts +//// import * as foo from "fake-module//*0*/ + +// @Filename: package.json +//// { "dependencies": { "fake-module": "latest" }, "devDependencies": { "fake-module-dev": "latest" } } + +// @Filename: node_modules/fake-module/repeated.ts +//// /*repeatedts*/ +// @Filename: node_modules/fake-module/repeated.tsx +//// /*repeatedtsx*/ +// @Filename: node_modules/fake-module/repeated.d.ts +//// /*repeateddts*/ +// @Filename: node_modules/fake-module/other.js +//// /*other*/ +// @Filename: node_modules/fake-module/other2.js +//// /*other2*/ + +// @Filename: node_modules/unlisted-module/index.js +//// /*unlisted-module*/ + +// @Filename: node_modules/@types/fake-module/other.d.ts +//// declare module "fake-module/other" {} + +// @Filename: node_modules/@types/unlisted-module/index.d.ts +//// /*unlisted-types*/ + +goTo.marker("0"); +verify.completionListContains("repeated"); +verify.completionListContains("other"); +verify.not.completionListItemsCountIsGreaterThan(2); diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts new file mode 100644 index 00000000000..7be26cf76d2 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts @@ -0,0 +1,30 @@ +/// +// @allowJs: true + +// @Filename: tests/test0.ts +//// import * as foo from "fake-module//*0*/ + +// @Filename: package.json +//// { "dependencies": { "fake-module": "latest" } } + +// @Filename: node_modules/fake-module/ts.ts +//// /*ts*/ +// @Filename: node_modules/fake-module/tsx.tsx +//// /*tsx*/ +// @Filename: node_modules/fake-module/dts.d.ts +//// /*dts*/ +// @Filename: node_modules/fake-module/js.js +//// /*js*/ +// @Filename: node_modules/fake-module/jsx.jsx +//// /*jsx*/ +// @Filename: node_modules/fake-module/repeated.js +//// /*repeatedjs*/ +// @Filename: node_modules/fake-module/repeated.jsx +//// /*repeatedjsx*/ + +goTo.marker("0"); + +verify.completionListContains("ts"); +verify.completionListContains("tsx"); +verify.completionListContains("dts"); +verify.not.completionListItemsCountIsGreaterThan(3); diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts new file mode 100644 index 00000000000..baa13638569 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts @@ -0,0 +1,45 @@ +/// + +// @Filename: dir1/dir2/dir3/dir4/test0.ts +//// import * as foo from "f/*0*/ + +// @Filename: dir1/dir2/dir3/dir4/test1.ts +//// import * as foo from "a/*1*/ + +// @Filename: dir1/dir2/dir3/dir4/test2.ts +//// import * as foo from "fake-module/*2*/ + +// @Filename: package.json +//// { "dependencies": { "fake-module": "latest" } } +// @Filename: node_modules/fake-module/ts.ts +//// /*module1*/ + +// @Filename: dir1/package.json +//// { "dependencies": { "fake-module2": "latest" } } +// @Filename: dir1/node_modules/@types/fake-module2/js.d.ts +//// declare module "ambient-module-test" {} + +// @Filename: dir1/dir2/dir3/package.json +//// { "dependencies": { "fake-module3": "latest" } } +// @Filename: dir1/dir2/dir3/node_modules/fake-module3/ts.ts +//// /*module3*/ + + +goTo.marker("0"); + +verify.completionListContains("fake-module/"); +verify.completionListContains("fake-module2/"); +verify.completionListContains("fake-module3/"); +verify.not.completionListItemsCountIsGreaterThan(3); + +goTo.marker("1"); + +verify.completionListContains("ambient-module-test"); +verify.not.completionListItemsCountIsGreaterThan(1); + +goTo.marker("2"); + +verify.completionListContains("fake-module/"); +verify.completionListContains("fake-module2/"); +verify.completionListContains("fake-module3/"); +verify.not.completionListItemsCountIsGreaterThan(3); diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts new file mode 100644 index 00000000000..a5bd602bbbf --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts @@ -0,0 +1,28 @@ +/// + +// @Filename: test0.ts +//// import * as foo from "/*0*/ + +// @Filename: test1.ts +//// import * as foo from "a/*1*/ + +// @Filename: ambientModules.d.ts +//// declare module "ambientModule" {} +//// declare module "otherAmbientModule" {} + +// @Filename: ambientModules2.d.ts +//// declare module "otherOtherAmbientModule" {} + + +goTo.marker("0"); + +verify.completionListContains("ambientModule"); +verify.completionListContains("otherAmbientModule"); +verify.completionListContains("otherOtherAmbientModule"); +verify.not.completionListItemsCountIsGreaterThan(3); + +goTo.marker("1"); + +verify.completionListContains("ambientModule"); +verify.not.completionListItemsCountIsGreaterThan(1); + diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts new file mode 100644 index 00000000000..ed588f35802 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts @@ -0,0 +1,57 @@ +/// + +// @Filename: tests/test0.ts +//// import * as foo from "module-/*0*/ + +// @Filename: package.json +//// { "dependencies": { +//// "module-no-main": "latest", +//// "module-no-main-index-d-ts": "latest", +//// "module-index-ts": "latest", +//// "module-index-d-ts-explicit-main": "latest", +//// "module-index-d-ts-default-main": "latest", +//// "module-typings": "latest" +//// } } + +// @Filename: node_modules/module-no-main/package.json +//// { } + +// @Filename: node_modules/module-no-main-index-d-ts/package.json +//// { } +// @Filename: node_modules/module-no-main-index-d-ts/index.d.ts +//// /*module-no-main-index-d-ts*/ + +// @Filename: node_modules/module-index-ts/package.json +//// { } +// @Filename: node_modules/module-index-ts/index.ts +//// /*module-index-ts*/ + +// @Filename: node_modules/module-index-d-ts-explicit-main/package.json +//// { "main":"./notIndex.js" } +// @Filename: node_modules/module-index-d-ts-explicit-main/notIndex.js +//// /*module-index-d-ts-explicit-main*/ +// @Filename: node_modules/module-index-d-ts-explicit-main/index.d.ts +//// /*module-index-d-ts-explicit-main2*/ + +// @Filename: node_modules/module-index-d-ts-default-main/package.json +//// { } +// @Filename: node_modules/module-index-d-ts-default-main/index.js +//// /*module-index-d-ts-default-main*/ +// @Filename: node_modules/module-index-d-ts-default-main/index.d.ts +//// /*module-index-d-ts-default-main2*/ + +// @Filename: node_modules/module-typings/package.json +//// { "typings":"./types.d.ts" } +// @Filename: node_modules/module-typings/types.d.ts +//// /*module-typings*/ + + +goTo.marker("0"); + +verify.completionListContains("module-no-main/"); +verify.completionListContains("module-no-main-index-d-ts/"); +verify.completionListContains("module-index-ts"); +verify.completionListContains("module-index-d-ts-explicit-main"); +verify.completionListContains("module-index-d-ts-default-main"); +verify.completionListContains("module-typings"); +verify.not.completionListItemsCountIsGreaterThan(6); diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts new file mode 100644 index 00000000000..26a6645b3fa --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts @@ -0,0 +1,77 @@ +/// + +// @Filename: test0.ts +//// import * as foo from "./*0*/ + +// @Filename: test1.ts +//// import * as foo from ".//*1*/ + +// @Filename: test2.ts +//// import * as foo from "./f/*2*/ + +// @Filename: test3.ts +//// import * as foo from "./folder//*3*/ + +// @Filename: test4.ts +//// import * as foo from "./folder/h/*4*/ + +// @Filename: parentTest/sub/test5.ts +//// import * as foo from "../g/*5*/ + +// @Filename: f1.ts +//// /*f1*/ +// @Filename: f1.js +//// /*f1j*/ +// @Filename: f1.d.ts +//// /*f1d*/ +// @Filename: f2.tsx +//// /f2*/ +// @Filename: f3.js +//// /*f3*/ +// @Filename: f4.jsx +//// /*f4*/ +// @Filename: e1.ts +//// /*e1*/ +// @Filename: folder/f1.ts +//// /*subf1*/ +// @Filename: folder/h1.ts +//// /*subh1*/ +// @Filename: parentTest/f1.ts +//// /*parentf1*/ +// @Filename: parentTest/g1.ts +//// /*parentg1*/ + +goTo.marker("0"); +verify.completionListIsEmpty(); + +goTo.marker("1"); +verify.completionListContains("f1"); +verify.completionListContains("f2"); +verify.completionListContains("e1"); +verify.completionListContains("test0"); +verify.completionListContains("test1"); +verify.completionListContains("test2"); +verify.completionListContains("test3"); +verify.completionListContains("test4"); +verify.completionListContains("folder/"); +verify.completionListContains("parentTest/"); +verify.not.completionListItemsCountIsGreaterThan(10); + +goTo.marker("2"); +verify.completionListContains("f1"); +verify.completionListContains("f2"); +verify.completionListContains("folder/"); +verify.not.completionListItemsCountIsGreaterThan(3); + +goTo.marker("3"); +verify.completionListContains("f1"); +verify.completionListContains("h1"); +verify.not.completionListItemsCountIsGreaterThan(2); + +goTo.marker("4"); +verify.completionListContains("h1"); +verify.not.completionListItemsCountIsGreaterThan(1); + +goTo.marker("5"); +verify.completionListContains("g1"); +verify.not.completionListItemsCountIsGreaterThan(1); \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts new file mode 100644 index 00000000000..1b4abe79119 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts @@ -0,0 +1,43 @@ +/// +// @allowJs: true + +// @Filename: test0.ts +//// import * as foo from ".//*0*/ + +// @Filename: test1.ts +//// import * as foo from "./f/*1*/ + +// @Filename: f1.ts +//// /f1*/ +// @Filename: f1.js +//// /*f1j*/ +// @Filename: f1.d.ts +//// /*f1d*/ +// @Filename: f2.tsx +//// /*f2*/ +// @Filename: f3.js +//// /*f3*/ +// @Filename: f4.jsx +//// /*f4*/ +// @Filename: e1.ts +//// /*e1*/ +// @Filename: e2.js +//// /*e2*/ + +goTo.marker("0"); +verify.completionListContains("f1"); +verify.completionListContains("f2"); +verify.completionListContains("f3"); +verify.completionListContains("f4"); +verify.completionListContains("e1"); +verify.completionListContains("e2"); +verify.completionListContains("test0"); +verify.completionListContains("test1"); +verify.not.completionListItemsCountIsGreaterThan(8); + +goTo.marker("1"); +verify.completionListContains("f1"); +verify.completionListContains("f2"); +verify.completionListContains("f3"); +verify.completionListContains("f4"); +verify.not.completionListItemsCountIsGreaterThan(4); \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts new file mode 100644 index 00000000000..1b10ffff5b6 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts @@ -0,0 +1,41 @@ +/// + +// @Filename: tests/test0.ts +//// import * as foo from "c:/tests/cases/f/*0*/ + +// @Filename: tests/test1.ts +//// import * as foo from "c:/tests/cases/fourslash/*1*/ + +// @Filename: tests/test2.ts +//// import * as foo from "c:/tests/cases/fourslash//*2*/ + +// @Filename: f1.ts +//// /*f1*/ +// @Filename: f2.tsx +//// /*f2*/ +// @Filename: folder/f1.ts +//// /*subf1*/ +// @Filename: f3.js +//// /*f3*/ +// @Filename: f4.jsx +//// /*f4*/ +// @Filename: e1.ts +//// /*e1*/ +// @Filename: e2.js +//// /*e2*/ + +goTo.marker("0"); +verify.completionListContains("fourslash/"); +verify.not.completionListItemsCountIsGreaterThan(1); + +goTo.marker("1"); +verify.completionListContains("fourslash/"); +verify.not.completionListItemsCountIsGreaterThan(1); + +goTo.marker("2"); +verify.completionListContains("f1"); +verify.completionListContains("f2"); +verify.completionListContains("e1"); +verify.completionListContains("folder/"); +verify.completionListContains("tests/"); +verify.not.completionListItemsCountIsGreaterThan(5); \ No newline at end of file diff --git a/tests/cases/fourslash/completionForTripleSlashReference1.ts b/tests/cases/fourslash/completionForTripleSlashReference1.ts new file mode 100644 index 00000000000..96582be1730 --- /dev/null +++ b/tests/cases/fourslash/completionForTripleSlashReference1.ts @@ -0,0 +1,79 @@ +/// + +// @Filename: test0.ts +//// /// + +// @Filename: test3.ts +//// /// +// @allowJs: true + +// @Filename: test0.ts +//// /// + +// @Filename: tests/test0.ts +//// /// Date: Fri, 24 Jun 2016 14:51:30 -0700 Subject: [PATCH 02/73] Minor fix --- src/services/services.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index c6c8a7a2e83..650d0c4ce1a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1948,7 +1948,6 @@ namespace ts { } - const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions); const tripleSlashDirectivePrefixRegex = /^\/\/\/\s* { @@ -4479,15 +4477,11 @@ namespace ts { if (isNestedModule && moduleName === moduleNameFragment) { const moduleDir = combinePaths(nodeModulesDir, moduleName); if (directoryProbablyExists(moduleDir, host)) { - // Get all possible nested module names from files with all extensions - const nestedFiles = host.readDirectory(moduleDir, allSupportedExtensions, /*exclude*/undefined, /*include*/["./*"]); + const nestedFiles = host.readDirectory(moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]); - // Add those with typings to the completion list nestedFiles.forEach((f) => { const nestedModule = removeFileExtension(getBaseFileName(f)); - if (hasTypeScriptFileExtension(f)) { - nonRelativeModules.push(nestedModule); - } + nonRelativeModules.push(nestedModule); }); } } From dbdd9893f54a5a5e3165bc1e21010d9e51740ae8 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Mon, 27 Jun 2016 13:06:40 -0700 Subject: [PATCH 03/73] Import completions for require calls --- src/services/services.ts | 26 ++++-- ...etionForStringLiteralNonrelativeImport1.ts | 46 ++++++---- ...etionForStringLiteralNonrelativeImport2.ts | 16 ++-- ...etionForStringLiteralNonrelativeImport3.ts | 17 ++-- ...etionForStringLiteralNonrelativeImport4.ts | 43 +++++---- ...etionForStringLiteralNonrelativeImport5.ts | 29 +++--- ...etionForStringLiteralNonrelativeImport6.ts | 23 +++-- ...mpletionForStringLiteralRelativeImport1.ts | 89 ++++++++++--------- ...mpletionForStringLiteralRelativeImport2.ts | 44 +++++---- ...mpletionForStringLiteralRelativeImport3.ts | 44 +++++---- 10 files changed, 225 insertions(+), 152 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 650d0c4ce1a..04c435f1339 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2775,13 +2775,17 @@ namespace ts { function isNameOfExternalModuleImportOrDeclaration(node: Node): boolean { if (node.kind === SyntaxKind.StringLiteral) { - return isNameOfModuleDeclaration(node) || - (isExternalModuleImportEqualsDeclaration(node.parent.parent) && getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node); + return isNameOfModuleDeclaration(node) || isExpressionOfExternalModuleImportEqualsDeclaration(node); } return false; } + function isExpressionOfExternalModuleImportEqualsDeclaration(node: Node) { + return isExternalModuleImportEqualsDeclaration(node.parent.parent) && + getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node; + } + /** Returns true if the position is within a comment */ function isInsideComment(sourceFile: SourceFile, token: Node, position: number): boolean { // The position has to be: 1. in the leading trivia (before token.getStart()), and 2. within a comment @@ -4256,15 +4260,25 @@ namespace ts { const argumentInfo = SignatureHelp.getContainingArgumentInfo(node, position, sourceFile); if (argumentInfo) { - // Get string literal completions from specialized signatures of the target - return getStringLiteralCompletionEntriesFromCallExpression(argumentInfo); + // Try to get string literal completions from specialized signatures of the target + const callExpressionCompletionEntries = getStringLiteralCompletionEntriesFromCallExpression(argumentInfo); + if (callExpressionCompletionEntries) { + return callExpressionCompletionEntries; + } + else if (isRequireCall(node.parent, false)) { + // If that failed but this call mataches the signature of a require call, treat the literal as an external module name + return getStringLiteralCompletionEntriesFromModuleNames(node); + } + else { + return undefined; + } } else if (isElementAccessExpression(node.parent) && node.parent.argumentExpression === node) { // Get all names of properties on the expression return getStringLiteralCompletionEntriesFromElementAccess(node.parent); } - else if (node.parent.kind === SyntaxKind.ImportDeclaration) { - // Get all known module names + else if (node.parent.kind === SyntaxKind.ImportDeclaration || isExpressionOfExternalModuleImportEqualsDeclaration(node)) { + // Get all known external module names or complete a path to a module return getStringLiteralCompletionEntriesFromModuleNames(node); } else { diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts index 681a6fce03c..5c7b2016a75 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts @@ -1,13 +1,17 @@ /// // @Filename: tests/test0.ts -//// import * as foo from "f/*0*/ +//// import * as foo1 from "f/*import_as0*/ +//// import * as foo2 from "fake-module//*import_as1*/ +//// import * as foo3 from "fake-module/*import_as2*/ -// @Filename: tests/test1.ts -//// import * as foo from "fake-module//*1*/ +//// import foo4 = require("f/*import_equals0*/ +//// import foo5 = require("fake-module//*import_equals1*/ +//// import foo6 = require("fake-module/*import_equals2*/ -// @Filename: tests/test2.ts -//// import * as foo from "fake-module/*2*/ +//// var foo7 = require("f/*require0*/ +//// var foo8 = require("fake-module//*require1*/ +//// var foo9 = require("fake-module/*require2*/ // @Filename: package.json //// { "dependencies": { "fake-module": "latest" }, "devDependencies": { "fake-module-dev": "latest" } } @@ -35,19 +39,23 @@ // @Filename: node_modules/unlisted-module/index.ts //// /*unlisted-module*/ -goTo.marker("0"); -verify.completionListContains("fake-module"); -verify.completionListContains("fake-module-dev"); -verify.not.completionListItemsCountIsGreaterThan(2); +const kinds = ["import_as", "import_equals", "require"]; -goTo.marker("1"); -verify.completionListContains("index"); -verify.completionListContains("ts"); -verify.completionListContains("dts"); -verify.completionListContains("tsx"); -verify.not.completionListItemsCountIsGreaterThan(4); +for (const kind of kinds) { + goTo.marker(kind + "0"); + verify.completionListContains("fake-module"); + verify.completionListContains("fake-module-dev"); + verify.not.completionListItemsCountIsGreaterThan(2); -goTo.marker("2"); -verify.completionListContains("fake-module"); -verify.completionListContains("fake-module-dev"); -verify.not.completionListItemsCountIsGreaterThan(2); \ No newline at end of file + goTo.marker(kind + "1"); + verify.completionListContains("index"); + verify.completionListContains("ts"); + verify.completionListContains("dts"); + verify.completionListContains("tsx"); + verify.not.completionListItemsCountIsGreaterThan(4); + + goTo.marker(kind + "2"); + verify.completionListContains("fake-module"); + verify.completionListContains("fake-module-dev"); + verify.not.completionListItemsCountIsGreaterThan(2); +} \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts index c3e6f2e9062..2b5f57412de 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts @@ -1,7 +1,9 @@ /// // @Filename: tests/test0.ts -//// import * as foo from "fake-module//*0*/ +//// import * as foo1 from "fake-module//*import_as0*/ +//// import foo2 = require("fake-module//*import_equals0*/ +//// var foo3 = require("fake-module//*require0*/ // @Filename: package.json //// { "dependencies": { "fake-module": "latest" }, "devDependencies": { "fake-module-dev": "latest" } } @@ -26,7 +28,11 @@ // @Filename: node_modules/@types/unlisted-module/index.d.ts //// /*unlisted-types*/ -goTo.marker("0"); -verify.completionListContains("repeated"); -verify.completionListContains("other"); -verify.not.completionListItemsCountIsGreaterThan(2); +const kinds = ["import_as", "import_equals", "require"]; + +for (const kind of kinds) { + goTo.marker(kind + "0"); + verify.completionListContains("repeated"); + verify.completionListContains("other"); + verify.not.completionListItemsCountIsGreaterThan(2); +} diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts index 7be26cf76d2..aec9f8411bc 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts @@ -2,7 +2,9 @@ // @allowJs: true // @Filename: tests/test0.ts -//// import * as foo from "fake-module//*0*/ +//// import * as foo1 from "fake-module//*import_as0*/ +//// import foo2 = require("fake-module//*import_equals0*/ +//// var foo3 = require("fake-module//*require0*/ // @Filename: package.json //// { "dependencies": { "fake-module": "latest" } } @@ -22,9 +24,12 @@ // @Filename: node_modules/fake-module/repeated.jsx //// /*repeatedjsx*/ -goTo.marker("0"); +const kinds = ["import_as", "import_equals", "require"]; -verify.completionListContains("ts"); -verify.completionListContains("tsx"); -verify.completionListContains("dts"); -verify.not.completionListItemsCountIsGreaterThan(3); +for (const kind of kinds) { + goTo.marker(kind + "0"); + verify.completionListContains("ts"); + verify.completionListContains("tsx"); + verify.completionListContains("dts"); + verify.not.completionListItemsCountIsGreaterThan(3); +} diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts index baa13638569..64e3cbad48e 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts @@ -1,13 +1,17 @@ /// // @Filename: dir1/dir2/dir3/dir4/test0.ts -//// import * as foo from "f/*0*/ +//// import * as foo1 from "f/*import_as0*/ +//// import * as foo2 from "a/*import_as1*/ +//// import * as foo3 from "fake-module/*import_as2*/ -// @Filename: dir1/dir2/dir3/dir4/test1.ts -//// import * as foo from "a/*1*/ +//// import foo4 = require("f/*import_equals0*/ +//// import foo5 = require("a/*import_equals1*/ +//// import foo6 = require("fake-module/*import_equals2*/ -// @Filename: dir1/dir2/dir3/dir4/test2.ts -//// import * as foo from "fake-module/*2*/ +//// var foo7 = require("f/*require0*/ +//// var foo8 = require("a/*require1*/ +//// var foo9 = require("fake-module/*require2*/ // @Filename: package.json //// { "dependencies": { "fake-module": "latest" } } @@ -24,22 +28,25 @@ // @Filename: dir1/dir2/dir3/node_modules/fake-module3/ts.ts //// /*module3*/ +const kinds = ["import_as", "import_equals", "require"]; -goTo.marker("0"); +for (const kind of kinds) { + goTo.marker(kind + "0"); -verify.completionListContains("fake-module/"); -verify.completionListContains("fake-module2/"); -verify.completionListContains("fake-module3/"); -verify.not.completionListItemsCountIsGreaterThan(3); + verify.completionListContains("fake-module/"); + verify.completionListContains("fake-module2/"); + verify.completionListContains("fake-module3/"); + verify.not.completionListItemsCountIsGreaterThan(3); -goTo.marker("1"); + goTo.marker(kind + "1"); -verify.completionListContains("ambient-module-test"); -verify.not.completionListItemsCountIsGreaterThan(1); + verify.completionListContains("ambient-module-test"); + verify.not.completionListItemsCountIsGreaterThan(1); -goTo.marker("2"); + goTo.marker(kind + "2"); -verify.completionListContains("fake-module/"); -verify.completionListContains("fake-module2/"); -verify.completionListContains("fake-module3/"); -verify.not.completionListItemsCountIsGreaterThan(3); + verify.completionListContains("fake-module/"); + verify.completionListContains("fake-module2/"); + verify.completionListContains("fake-module3/"); + verify.not.completionListItemsCountIsGreaterThan(3); +} \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts index a5bd602bbbf..4a8b9e2f614 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts @@ -1,10 +1,14 @@ /// // @Filename: test0.ts -//// import * as foo from "/*0*/ +//// import * as foo1 from "/*import_as0*/ +//// import * as foo2 from "a/*import_as1*/ -// @Filename: test1.ts -//// import * as foo from "a/*1*/ +//// import foo3 = require("/*import_equals0*/ +//// import foo4 = require("a/*import_equals1*/ + +//// var foo5 = require("/*require0*/ +//// var foo6 = require("a/*require1*/ // @Filename: ambientModules.d.ts //// declare module "ambientModule" {} @@ -13,16 +17,19 @@ // @Filename: ambientModules2.d.ts //// declare module "otherOtherAmbientModule" {} +const kinds = ["import_as", "import_equals", "require"]; -goTo.marker("0"); +for (const kind of kinds) { + goTo.marker(kind + "0"); -verify.completionListContains("ambientModule"); -verify.completionListContains("otherAmbientModule"); -verify.completionListContains("otherOtherAmbientModule"); -verify.not.completionListItemsCountIsGreaterThan(3); + verify.completionListContains("ambientModule"); + verify.completionListContains("otherAmbientModule"); + verify.completionListContains("otherOtherAmbientModule"); + verify.not.completionListItemsCountIsGreaterThan(3); -goTo.marker("1"); + goTo.marker(kind + "1"); -verify.completionListContains("ambientModule"); -verify.not.completionListItemsCountIsGreaterThan(1); + verify.completionListContains("ambientModule"); + verify.not.completionListItemsCountIsGreaterThan(1); +} diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts index ed588f35802..bfd0539e149 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts @@ -1,7 +1,9 @@ /// // @Filename: tests/test0.ts -//// import * as foo from "module-/*0*/ +//// import * as foo1 from "module-/*import_as0*/ +//// import foo2 = require("module-/*import_equals0*/ +//// var foo3 = require("module-/*require0*/ // @Filename: package.json //// { "dependencies": { @@ -45,13 +47,16 @@ // @Filename: node_modules/module-typings/types.d.ts //// /*module-typings*/ +const kinds = ["import_as", "import_equals", "require"]; -goTo.marker("0"); +for (const kind of kinds) { + goTo.marker(kind + "0"); -verify.completionListContains("module-no-main/"); -verify.completionListContains("module-no-main-index-d-ts/"); -verify.completionListContains("module-index-ts"); -verify.completionListContains("module-index-d-ts-explicit-main"); -verify.completionListContains("module-index-d-ts-default-main"); -verify.completionListContains("module-typings"); -verify.not.completionListItemsCountIsGreaterThan(6); + verify.completionListContains("module-no-main/"); + verify.completionListContains("module-no-main-index-d-ts/"); + verify.completionListContains("module-index-ts"); + verify.completionListContains("module-index-d-ts-explicit-main"); + verify.completionListContains("module-index-d-ts-default-main"); + verify.completionListContains("module-typings"); + verify.not.completionListItemsCountIsGreaterThan(6); +} diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts index 26a6645b3fa..bb9d6a59c7b 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts @@ -1,22 +1,30 @@ /// // @Filename: test0.ts -//// import * as foo from "./*0*/ +//// import * as foo1 from "./*import_as0*/ +//// import * as foo2 from ".//*import_as1*/ +//// import * as foo3 from "./f/*import_as2*/ +//// import * as foo4 from "./folder//*import_as3*/ +//// import * as foo5 from "./folder/h/*import_as4*/ -// @Filename: test1.ts -//// import * as foo from ".//*1*/ +//// import foo6 = require("./*import_equals0*/ +//// import foo7 = require(".//*import_equals1*/ +//// import foo8 = require("./f/*import_equals2*/ +//// import foo9 = require("./folder//*import_equals3*/ +//// import foo10 = require("./folder/h/*import_equals4*/ -// @Filename: test2.ts -//// import * as foo from "./f/*2*/ - -// @Filename: test3.ts -//// import * as foo from "./folder//*3*/ - -// @Filename: test4.ts -//// import * as foo from "./folder/h/*4*/ +//// var foo11 = require("./*require0*/ +//// var foo12 = require(".//*require1*/ +//// var foo13 = require("./f/*require2*/ +//// var foo14 = require("./folder//*require3*/ +//// var foo15 = require("./folder/h/*require4*/ // @Filename: parentTest/sub/test5.ts -//// import * as foo from "../g/*5*/ +//// import * as foo16 from "../g/*import_as5*/ + +//// import foo17 = require("../g/*import_equals5*/ + +//// var foo18 = require("../g/*require5*/ // @Filename: f1.ts //// /*f1*/ @@ -40,38 +48,37 @@ //// /*parentf1*/ // @Filename: parentTest/g1.ts //// /*parentg1*/ +const kinds = ["import_as", "import_equals", "require"]; -goTo.marker("0"); -verify.completionListIsEmpty(); +for (const kind of kinds) { + goTo.marker(kind + "0"); + verify.completionListIsEmpty(); -goTo.marker("1"); -verify.completionListContains("f1"); -verify.completionListContains("f2"); -verify.completionListContains("e1"); -verify.completionListContains("test0"); -verify.completionListContains("test1"); -verify.completionListContains("test2"); -verify.completionListContains("test3"); -verify.completionListContains("test4"); -verify.completionListContains("folder/"); -verify.completionListContains("parentTest/"); -verify.not.completionListItemsCountIsGreaterThan(10); + goTo.marker(kind + "1"); + verify.completionListContains("f1"); + verify.completionListContains("f2"); + verify.completionListContains("e1"); + verify.completionListContains("test0"); + verify.completionListContains("folder/"); + verify.completionListContains("parentTest/"); + verify.not.completionListItemsCountIsGreaterThan(6); -goTo.marker("2"); -verify.completionListContains("f1"); -verify.completionListContains("f2"); -verify.completionListContains("folder/"); -verify.not.completionListItemsCountIsGreaterThan(3); + goTo.marker(kind + "2"); + verify.completionListContains("f1"); + verify.completionListContains("f2"); + verify.completionListContains("folder/"); + verify.not.completionListItemsCountIsGreaterThan(3); -goTo.marker("3"); -verify.completionListContains("f1"); -verify.completionListContains("h1"); -verify.not.completionListItemsCountIsGreaterThan(2); + goTo.marker(kind + "3"); + verify.completionListContains("f1"); + verify.completionListContains("h1"); + verify.not.completionListItemsCountIsGreaterThan(2); -goTo.marker("4"); -verify.completionListContains("h1"); -verify.not.completionListItemsCountIsGreaterThan(1); + goTo.marker(kind + "4"); + verify.completionListContains("h1"); + verify.not.completionListItemsCountIsGreaterThan(1); -goTo.marker("5"); -verify.completionListContains("g1"); -verify.not.completionListItemsCountIsGreaterThan(1); \ No newline at end of file + goTo.marker(kind + "5"); + verify.completionListContains("g1"); + verify.not.completionListItemsCountIsGreaterThan(1); +} \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts index 1b4abe79119..4e879148469 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts @@ -2,10 +2,14 @@ // @allowJs: true // @Filename: test0.ts -//// import * as foo from ".//*0*/ +//// import * as foo1 from ".//*import_as0*/ +//// import * as foo2 from "./f/*import_as1*/ -// @Filename: test1.ts -//// import * as foo from "./f/*1*/ +//// import foo3 = require(".//*import_equals0*/ +//// import foo4 = require("./f/*import_equals1*/ + +//// var foo5 = require(".//*require0*/ +//// var foo6 = require("./f/*require1*/ // @Filename: f1.ts //// /f1*/ @@ -23,21 +27,23 @@ //// /*e1*/ // @Filename: e2.js //// /*e2*/ +const kinds = ["import_as", "import_equals", "require"]; -goTo.marker("0"); -verify.completionListContains("f1"); -verify.completionListContains("f2"); -verify.completionListContains("f3"); -verify.completionListContains("f4"); -verify.completionListContains("e1"); -verify.completionListContains("e2"); -verify.completionListContains("test0"); -verify.completionListContains("test1"); -verify.not.completionListItemsCountIsGreaterThan(8); +for (const kind of kinds) { + goTo.marker(kind + "0"); + verify.completionListContains("f1"); + verify.completionListContains("f2"); + verify.completionListContains("f3"); + verify.completionListContains("f4"); + verify.completionListContains("e1"); + verify.completionListContains("e2"); + verify.completionListContains("test0"); + verify.not.completionListItemsCountIsGreaterThan(7); -goTo.marker("1"); -verify.completionListContains("f1"); -verify.completionListContains("f2"); -verify.completionListContains("f3"); -verify.completionListContains("f4"); -verify.not.completionListItemsCountIsGreaterThan(4); \ No newline at end of file + goTo.marker(kind + "1"); + verify.completionListContains("f1"); + verify.completionListContains("f2"); + verify.completionListContains("f3"); + verify.completionListContains("f4"); + verify.not.completionListItemsCountIsGreaterThan(4); +} \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts index 1b10ffff5b6..426eb3c2318 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts @@ -1,13 +1,17 @@ /// // @Filename: tests/test0.ts -//// import * as foo from "c:/tests/cases/f/*0*/ +//// import * as foo1 from "c:/tests/cases/f/*import_as0*/ +//// import * as foo2 from "c:/tests/cases/fourslash/*import_as1*/ +//// import * as foo3 from "c:/tests/cases/fourslash//*import_as2*/ -// @Filename: tests/test1.ts -//// import * as foo from "c:/tests/cases/fourslash/*1*/ +//// import foo4 = require("c:/tests/cases/f/*import_equals0*/ +//// import foo5 = require("c:/tests/cases/fourslash/*import_equals1*/ +//// import foo6 = require("c:/tests/cases/fourslash//*import_equals2*/ -// @Filename: tests/test2.ts -//// import * as foo from "c:/tests/cases/fourslash//*2*/ +//// var foo7 = require("c:/tests/cases/f/*require0*/ +//// var foo8 = require("c:/tests/cases/fourslash/*require1*/ +//// var foo9 = require("c:/tests/cases/fourslash//*require2*/ // @Filename: f1.ts //// /*f1*/ @@ -24,18 +28,22 @@ // @Filename: e2.js //// /*e2*/ -goTo.marker("0"); -verify.completionListContains("fourslash/"); -verify.not.completionListItemsCountIsGreaterThan(1); +const kinds = ["import_as", "import_equals", "require"]; -goTo.marker("1"); -verify.completionListContains("fourslash/"); -verify.not.completionListItemsCountIsGreaterThan(1); +for (const kind of kinds) { + goTo.marker(kind + "0"); + verify.completionListContains("fourslash/"); + verify.not.completionListItemsCountIsGreaterThan(1); -goTo.marker("2"); -verify.completionListContains("f1"); -verify.completionListContains("f2"); -verify.completionListContains("e1"); -verify.completionListContains("folder/"); -verify.completionListContains("tests/"); -verify.not.completionListItemsCountIsGreaterThan(5); \ No newline at end of file + goTo.marker(kind + "1"); + verify.completionListContains("fourslash/"); + verify.not.completionListItemsCountIsGreaterThan(1); + + goTo.marker(kind + "2"); + verify.completionListContains("f1"); + verify.completionListContains("f2"); + verify.completionListContains("e1"); + verify.completionListContains("folder/"); + verify.completionListContains("tests/"); + verify.not.completionListItemsCountIsGreaterThan(5); +} \ No newline at end of file From 801b49360202fd8c2c4f46b2aeedb49f5034b6a7 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Thu, 30 Jun 2016 10:30:23 -0700 Subject: [PATCH 04/73] PR feedback --- src/harness/harnessLanguageService.ts | 2 +- src/harness/virtualFileSystem.ts | 9 +++++++-- src/services/services.ts | 19 +++++++++++++------ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 682496442e9..08c74e695be 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -135,7 +135,7 @@ namespace Harness.LanguageService { public getFilenames(): string[] { const fileNames: string[] = []; - this.virtualFileSystem.getAllFileEntries().forEach((virtualEntry) => { + ts.forEach(this.virtualFileSystem.getAllFileEntries(), (virtualEntry) => { const scriptInfo = virtualEntry.content; if (scriptInfo.isRootFile) { // only include root files here diff --git a/src/harness/virtualFileSystem.ts b/src/harness/virtualFileSystem.ts index 5a89efea7fa..2a46bf4c3fe 100644 --- a/src/harness/virtualFileSystem.ts +++ b/src/harness/virtualFileSystem.ts @@ -158,6 +158,11 @@ namespace Utils { return directory; } + /** + * Reads the directory at the given path and retrieves a list of file names and a list + * of directory names within it. Suitable for use with ts.matchFiles() + * @param path The path to the directory to be read + */ getAccessibleFileSystemEntries(path: string) { const entry = this.traversePath(path); if (entry && entry.isDirectory()) { @@ -176,8 +181,8 @@ namespace Utils { return fileEntries; function getFilesRecursive(dir: VirtualDirectory, result: VirtualFile[]) { - dir.getFiles().forEach((e) => result.push(e)); - dir.getDirectories().forEach((subDir) => getFilesRecursive(subDir, result)); + ts.forEach(dir.getFiles(), (e) => result.push(e)); + ts.forEach(dir.getDirectories(), (subDir) => getFilesRecursive(subDir, result)); } } diff --git a/src/services/services.ts b/src/services/services.ts index 04c435f1339..b7e79473fcd 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1947,8 +1947,15 @@ namespace ts { sourceMapText?: string; } - + // Matches the beginning of a triple slash directive const tripleSlashDirectivePrefixRegex = /^\/\/\/\s* { + ts.forEach(files, (f) => { const fName = includeExtensions ? getBaseFileName(f) : removeFileExtension(getBaseFileName(f)); if (startsWith(fName, toComplete)) { @@ -4402,7 +4409,7 @@ namespace ts { // If possible, get folder completion as well if (host.getDirectories) { const directories = host.getDirectories(baseDir); - directories.forEach((d) => { + ts.forEach(directories, (d) => { const dName = getBaseFileName(removeTrailingDirectorySeparator(d)); if (startsWith(dName, toComplete)) { @@ -4471,7 +4478,7 @@ namespace ts { // Check for node_modules. We only offer completions for modules that are listed in the // package.json for a project for efficiency and to ensure that the completion list is // not polluted with sub-dependencies - findPackageJsons(scriptPath).forEach((packageJson) => { + ts.forEach(findPackageJsons(scriptPath), (packageJson) => { const package = tryReadingPackageJson(packageJson); if (!package) { return; @@ -4487,13 +4494,13 @@ namespace ts { addPotentialPackageNames(package.devDependencies, moduleNameFragment, foundModuleNames); } - foundModuleNames.forEach((moduleName) => { + ts.forEach(foundModuleNames, (moduleName) => { if (isNestedModule && moduleName === moduleNameFragment) { const moduleDir = combinePaths(nodeModulesDir, moduleName); if (directoryProbablyExists(moduleDir, host)) { const nestedFiles = host.readDirectory(moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]); - nestedFiles.forEach((f) => { + ts.forEach(nestedFiles, (f) => { const nestedModule = removeFileExtension(getBaseFileName(f)); nonRelativeModules.push(nestedModule); }); From 5c24b3528d75220843334aac1ae56e48743d7c39 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Tue, 5 Jul 2016 17:01:26 -0700 Subject: [PATCH 05/73] Refactoring node_modules enumeration code --- src/services/services.ts | 112 ++---------------- src/services/utilities.ts | 103 ++++++++++++++++ ...etionForStringLiteralNonrelativeImport5.ts | 6 +- 3 files changed, 116 insertions(+), 105 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 167f8c9edf7..704a12fda21 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4477,117 +4477,23 @@ namespace ts { return moduleName; }); - // Check for node_modules. We only offer completions for modules that are listed in the - // package.json for a project for efficiency and to ensure that the completion list is - // not polluted with sub-dependencies - ts.forEach(findPackageJsons(scriptPath), (packageJson) => { - const package = tryReadingPackageJson(packageJson); - if (!package) { - return; + forEach(enumerateNodeModulesVisibleToScript(host, scriptPath, moduleNameFragment), visibleModule => { + if (!isNestedModule) { + nonRelativeModules.push(visibleModule.canBeImported ? visibleModule.moduleName : ensureTrailingDirectorySeparator(visibleModule.moduleName)); } + else { + const nestedFiles = host.readDirectory(visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]); - const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules"); - const foundModuleNames: string[] = []; - - if (package.dependencies) { - addPotentialPackageNames(package.dependencies, moduleNameFragment, foundModuleNames); + forEach(nestedFiles, (f) => { + const nestedModule = removeFileExtension(getBaseFileName(f)); + nonRelativeModules.push(nestedModule); + }); } - if (package.devDependencies) { - addPotentialPackageNames(package.devDependencies, moduleNameFragment, foundModuleNames); - } - - ts.forEach(foundModuleNames, (moduleName) => { - if (isNestedModule && moduleName === moduleNameFragment) { - const moduleDir = combinePaths(nodeModulesDir, moduleName); - if (directoryProbablyExists(moduleDir, host)) { - const nestedFiles = host.readDirectory(moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]); - - ts.forEach(nestedFiles, (f) => { - const nestedModule = removeFileExtension(getBaseFileName(f)); - nonRelativeModules.push(nestedModule); - }); - } - } - else if (startsWith(moduleName, fragment)) { - if (moduleCanBeImported(combinePaths(nodeModulesDir, moduleName))) { - nonRelativeModules.push(moduleName); - } - else { - nonRelativeModules.push(ensureTrailingDirectorySeparator(moduleName)); - } - } - }); }); return deduplicate(nonRelativeModules); } - function findPackageJsons(currentDir: string): string[] { - const paths: string[] = []; - let currentConfigPath: string; - while (true) { - currentConfigPath = findConfigFile(currentDir, (f) => host.fileExists(f), "package.json"); - if (currentConfigPath) { - paths.push(currentConfigPath); - - currentDir = getDirectoryPath(currentConfigPath); - const parent = getDirectoryPath(currentDir); - if (currentDir === parent) { - break; - } - currentDir = parent; - } - else { - break; - } - } - - return paths; - } - - function tryReadingPackageJson(filePath: string) { - try { - const fileText = host.readFile(filePath); - return JSON.parse(fileText); - } - catch (e) { - return undefined; - } - } - - function addPotentialPackageNames(dependencies: any, prefix: string, result: string[]) { - for (const dep in dependencies) { - if (dependencies.hasOwnProperty(dep) && startsWith(dep, prefix)) { - result.push(dep); - } - } - } - - /* - * A module can be imported by name alone if one of the following is true: - * It defines the "typings" property in its package.json - * The module has a "main" export and an index.d.ts file - * The module has an index.ts - */ - function moduleCanBeImported(modulePath: string): boolean { - const packagePath = combinePaths(modulePath, "package.json"); - - let hasMainExport = false; - if (host.fileExists(packagePath)) { - const package = tryReadingPackageJson(packagePath); - if (package) { - if (package.typings) { - return true; - } - hasMainExport = !!package.main; - } - } - - hasMainExport = hasMainExport || host.fileExists(combinePaths(modulePath, "index.js")); - - return (hasMainExport && host.fileExists(combinePaths(modulePath, "index.d.ts"))) || host.fileExists(combinePaths(modulePath, "index.ts")); - } - function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number) { const node = getTokenAtPosition(sourceFile, position); if (!node) { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 0c4c0fd1dea..44423860d4a 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -6,6 +6,12 @@ namespace ts { list: Node; } + export interface VisibleModuleInfo { + moduleName: string; + moduleDir: string; + canBeImported: boolean; + } + export function getLineStartPositionForPosition(position: number, sourceFile: SourceFile): number { const lineStarts = sourceFile.getLineStarts(); const line = sourceFile.getLineAndCharacterOfPosition(position).line; @@ -927,4 +933,101 @@ namespace ts { } return ensureScriptKind(fileName, scriptKind); } + + export function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string, modulePrefix?: string) { + const result: VisibleModuleInfo[] = []; + findPackageJsons(scriptPath).forEach((packageJson) => { + const package = tryReadingPackageJson(packageJson); + if (!package) { + return; + } + + const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules"); + const foundModuleNames: string[] = []; + + if (package.dependencies) { + addPotentialPackageNames(package.dependencies, modulePrefix, foundModuleNames); + } + if (package.devDependencies) { + addPotentialPackageNames(package.devDependencies, modulePrefix, foundModuleNames); + } + + forEach(foundModuleNames, (moduleName) => { + const moduleDir = combinePaths(nodeModulesDir, moduleName); + result.push({ + moduleName, + moduleDir, + canBeImported: moduleCanBeImported(moduleDir) + }); + }); + }); + + return result; + + function findPackageJsons(currentDir: string): string[] { + const paths: string[] = []; + let currentConfigPath: string; + while (true) { + currentConfigPath = findConfigFile(currentDir, (f) => host.fileExists(f), "package.json"); + if (currentConfigPath) { + paths.push(currentConfigPath); + + currentDir = getDirectoryPath(currentConfigPath); + const parent = getDirectoryPath(currentDir); + if (currentDir === parent) { + break; + } + currentDir = parent; + } + else { + break; + } + } + + return paths; + } + + function tryReadingPackageJson(filePath: string) { + try { + const fileText = host.readFile(filePath); + return JSON.parse(fileText); + } + catch (e) { + return undefined; + } + } + + function addPotentialPackageNames(dependencies: any, prefix: string, result: string[]) { + for (const dep in dependencies) { + if (dependencies.hasOwnProperty(dep) && (!prefix || startsWith(dep, prefix))) { + result.push(dep); + } + } + } + + /* + * A module can be imported by name alone if one of the following is true: + * It defines the "typings" property in its package.json + * The module has a "main" export and an index.d.ts file + * The module has an index.ts + */ + function moduleCanBeImported(modulePath: string): boolean { + const packagePath = combinePaths(modulePath, "package.json"); + + let hasMainExport = false; + if (host.fileExists(packagePath)) { + const package = tryReadingPackageJson(packagePath); + if (package) { + if (package.typings) { + return true; + } + hasMainExport = !!package.main; + } + } + + hasMainExport = hasMainExport || host.fileExists(combinePaths(modulePath, "index.js")); + + return (hasMainExport && host.fileExists(combinePaths(modulePath, "index.d.ts"))) || host.fileExists(combinePaths(modulePath, "index.ts")); + } + } } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts index 4a8b9e2f614..2e172e3578e 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts @@ -1,6 +1,8 @@ /// // @Filename: test0.ts +//// /// +//// /// //// import * as foo1 from "/*import_as0*/ //// import * as foo2 from "a/*import_as1*/ @@ -12,10 +14,10 @@ // @Filename: ambientModules.d.ts //// declare module "ambientModule" {} -//// declare module "otherAmbientModule" {} +//// declare module "otherAmbientModule" {} /*dummy0*/ // @Filename: ambientModules2.d.ts -//// declare module "otherOtherAmbientModule" {} +//// declare module "otherOtherAmbientModule" {} /*dummy1*/ const kinds = ["import_as", "import_equals", "require"]; From ffc165ee3657c4cabb5ad06e67eb9410c340ad0b Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Tue, 5 Jul 2016 17:38:52 -0700 Subject: [PATCH 06/73] Fixing behavior of resolvePath --- src/harness/harnessLanguageService.ts | 16 +--------------- src/services/services.ts | 6 +++--- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index e6d529bcde9..3cb83faf610 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -219,21 +219,7 @@ namespace Harness.LanguageService { return snapshot.getText(0, snapshot.getLength()); } resolvePath(path: string): string { - // Reduce away "." and ".." - const parts = path.split("/"); - const res: string[] = []; - for (let i = 0; i < parts.length; i++) { - if (parts[i] === ".") { - continue; - } - else if (parts[i] === ".." && res.length > 0) { - res.splice(res.length - 1, 1); - } - else { - res.push(parts[i]); - } - } - return res.join("/"); + return ts.normalizePath(ts.isRootedDiskPath(path) ? path : ts.combinePaths(this.getCurrentDirectory(), path)); } diff --git a/src/services/services.ts b/src/services/services.ts index 704a12fda21..d440f194b70 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4389,13 +4389,13 @@ namespace ts { const toComplete = getBaseFileName(fragment); const absolutePath = normalizeSlashes(host.resolvePath(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment))); - const baseDir = getDirectoryPath(absolutePath); + const baseDir = toComplete ? getDirectoryPath(absolutePath) : absolutePath; if (directoryProbablyExists(baseDir, host)) { // Enumerate the available files const files = host.readDirectory(baseDir, extensions, /*exclude*/undefined, /*include*/["./*"]); - ts.forEach(files, (f) => { + forEach(files, (f) => { const fName = includeExtensions ? getBaseFileName(f) : removeFileExtension(getBaseFileName(f)); if (startsWith(fName, toComplete)) { @@ -4411,7 +4411,7 @@ namespace ts { // If possible, get folder completion as well if (host.getDirectories) { const directories = host.getDirectories(baseDir); - ts.forEach(directories, (d) => { + forEach(directories, (d) => { const dName = getBaseFileName(removeTrailingDirectorySeparator(d)); if (startsWith(dName, toComplete)) { From 5c87c5a4bcd59e4012b22bbde8ab795a8f3c4522 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Wed, 6 Jul 2016 13:05:12 -0700 Subject: [PATCH 07/73] Removing forEach reference --- src/harness/virtualFileSystem.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/harness/virtualFileSystem.ts b/src/harness/virtualFileSystem.ts index 2a46bf4c3fe..c2502bbc80f 100644 --- a/src/harness/virtualFileSystem.ts +++ b/src/harness/virtualFileSystem.ts @@ -181,8 +181,14 @@ namespace Utils { return fileEntries; function getFilesRecursive(dir: VirtualDirectory, result: VirtualFile[]) { - ts.forEach(dir.getFiles(), (e) => result.push(e)); - ts.forEach(dir.getDirectories(), (subDir) => getFilesRecursive(subDir, result)); + const files = dir.getFiles(); + const dirs = dir.getDirectories(); + for (const file of files) { + result.push(file); + } + for (const subDir of dirs) { + getFilesRecursive(subDir, result); + } } } From 84a10e439ea895afcbdaa5494305f3f2533db7fd Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Mon, 25 Jul 2016 12:57:17 -0700 Subject: [PATCH 08/73] Some PR feedback --- src/services/services.ts | 61 +++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index d440f194b70..7024dc7211b 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1665,6 +1665,10 @@ namespace ts { export const constElement = "const"; export const letElement = "let"; + + export const directory = "directory"; + + export const externalModuleName = "external module name"; } export namespace ScriptElementKindModifier { @@ -4378,7 +4382,7 @@ namespace ts { return { isMemberCompletion: false, - isNewIdentifierLocation: false, + isNewIdentifierLocation: true, entries: result }; } @@ -4389,44 +4393,46 @@ namespace ts { const toComplete = getBaseFileName(fragment); const absolutePath = normalizeSlashes(host.resolvePath(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment))); - const baseDir = toComplete ? getDirectoryPath(absolutePath) : absolutePath; + const baseDirectory = toComplete ? getDirectoryPath(absolutePath) : absolutePath; - if (directoryProbablyExists(baseDir, host)) { + if (directoryProbablyExists(baseDirectory, host)) { // Enumerate the available files - const files = host.readDirectory(baseDir, extensions, /*exclude*/undefined, /*include*/["./*"]); - forEach(files, (f) => { - const fName = includeExtensions ? getBaseFileName(f) : removeFileExtension(getBaseFileName(f)); + const files = host.readDirectory(baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]); + forEach(files, f => { + const fileName = includeExtensions ? getBaseFileName(f) : removeFileExtension(getBaseFileName(f)); - if (startsWith(fName, toComplete)) { + const duplicate = includeExtensions ? false : forEach(result, entry => entry.name === fileName); + + if (startsWith(fileName, toComplete) && !duplicate) { result.push({ - name: fName, - kind: ScriptElementKind.unknown, + name: fileName, + kind: ScriptElementKind.directory, kindModifiers: ScriptElementKindModifier.none, - sortText: fName + sortText: fileName }); } }); // If possible, get folder completion as well if (host.getDirectories) { - const directories = host.getDirectories(baseDir); - forEach(directories, (d) => { - const dName = getBaseFileName(removeTrailingDirectorySeparator(d)); + const directories = host.getDirectories(baseDirectory); + forEach(directories, d => { + const directoryName = getBaseFileName(removeTrailingDirectorySeparator(d)); - if (startsWith(dName, toComplete)) { + if (startsWith(directoryName, toComplete)) { result.push({ - name: ensureTrailingDirectorySeparator(dName), - kind: ScriptElementKind.unknown, + name: ensureTrailingDirectorySeparator(directoryName), + kind: ScriptElementKind.directory, kindModifiers: ScriptElementKindModifier.none, - sortText: dName + sortText: directoryName }); } }); } } - return includeExtensions ? result : deduplicate(result, (a, b) => a.name === b.name); + return result; } /** @@ -4439,36 +4445,27 @@ namespace ts { return ts.map(enumeratePotentialNonRelativeModules(fragment, scriptPath), (moduleName) => { return { name: moduleName, - kind: ScriptElementKind.unknown, + kind: ScriptElementKind.externalModuleName, kindModifiers: ScriptElementKindModifier.none, sortText: moduleName }; }); } - function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string) { - const ambientSymbolNameRegex = /^"(.+?)"$/; - + function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string): string[] { // If this is a nested module, get the module name const firstSeparator = fragment.indexOf(directorySeparator); const moduleNameFragment = firstSeparator !== -1 ? fragment.substr(0, firstSeparator) : fragment; const isNestedModule = fragment !== moduleNameFragment; // Get modules that the type checker picked up - const ambientModules = ts.map(program.getTypeChecker().getAmbientModules(), (sym) => { - const match = ambientSymbolNameRegex.exec(sym.name); - if (match) { - return match[1]; - } - // This should never happen - return sym.name; - }); - let nonRelativeModules = ts.filter(ambientModules, (moduleName) => startsWith(moduleName, fragment)); + const ambientModules = ts.map(program.getTypeChecker().getAmbientModules(), sym => stripQuotes(sym.name)); + let nonRelativeModules = ts.filter(ambientModules, moduleName => startsWith(moduleName, fragment)); // Nested modules of the form "module-name/sub" need to be adjusted to only return the string // after the last '/' that appears in the fragment because editors insert the completion // only after that character - nonRelativeModules = ts.map(nonRelativeModules, (moduleName) => { + nonRelativeModules = ts.map(nonRelativeModules, moduleName => { if (moduleName.indexOf(directorySeparator) !== -1) { if (isNestedModule) { return moduleName.substr(fragment.lastIndexOf(directorySeparator) + 1); From ed2da32776449eef338fe535b5f0c662b82a7944 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Tue, 26 Jul 2016 13:43:29 -0700 Subject: [PATCH 09/73] Handling more compiler options and minor refactor --- src/harness/fourslash.ts | 34 ++- src/services/services.ts | 208 +++++++++++++++++- src/services/utilities.ts | 103 --------- ...etionForStringLiteralNonrelativeImport7.ts | 27 +++ ...etionForStringLiteralNonrelativeImport8.ts | 53 +++++ ...etionForStringLiteralNonrelativeImport9.ts | 34 +++ 6 files changed, 341 insertions(+), 118 deletions(-) create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index a42abbbc609..87b537417ea 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -245,14 +245,7 @@ namespace FourSlash { constructor(private basePath: string, private testType: FourSlashTestType, public testData: FourSlashData) { // Create a new Services Adapter this.cancellationToken = new TestCancellationToken(); - const compilationOptions = convertGlobalOptionsToCompilerOptions(this.testData.globalOptions); - if (compilationOptions.typeRoots) { - compilationOptions.typeRoots = compilationOptions.typeRoots.map(p => ts.getNormalizedAbsolutePath(p, this.basePath)); - } - - const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions); - this.languageServiceAdapterHost = languageServiceAdapter.getHost(); - this.languageService = languageServiceAdapter.getLanguageService(); + let compilationOptions = convertGlobalOptionsToCompilerOptions(this.testData.globalOptions); // Initialize the language service with all the scripts let startResolveFileRef: FourSlashFile; @@ -260,6 +253,22 @@ namespace FourSlash { ts.forEach(testData.files, file => { // Create map between fileName and its content for easily looking up when resolveReference flag is specified this.inputFiles[file.fileName] = file.content; + + if (ts.getBaseFileName(file.fileName).toLowerCase() === "tsconfig.json") { + const configJson = ts.parseConfigFileTextToJson(file.fileName, file.content); + assert.isTrue(configJson.config !== undefined); + + // Extend our existing compiler options so that we can also support tsconfig only options + if (configJson.config.compilerOptions) { + let baseDir = ts.normalizePath(ts.getDirectoryPath(file.fileName)); + let tsConfig = ts.convertCompilerOptionsFromJson(configJson.config.compilerOptions, baseDir, file.fileName); + + if (!tsConfig.errors || !tsConfig.errors.length) { + compilationOptions = ts.extend(compilationOptions, tsConfig.options); + } + } + } + if (!startResolveFileRef && file.fileOptions[metadataOptionNames.resolveReference] === "true") { startResolveFileRef = file; } @@ -269,6 +278,15 @@ namespace FourSlash { } }); + + if (compilationOptions.typeRoots) { + compilationOptions.typeRoots = compilationOptions.typeRoots.map(p => ts.getNormalizedAbsolutePath(p, this.basePath)); + } + + const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions); + this.languageServiceAdapterHost = languageServiceAdapter.getHost(); + this.languageService = languageServiceAdapter.getLanguageService(); + if (startResolveFileRef) { // Add the entry-point file itself into the languageServiceShimHost this.languageServiceAdapterHost.addScript(startResolveFileRef.fileName, startResolveFileRef.content, /*isRootFile*/ true); diff --git a/src/services/services.ts b/src/services/services.ts index 7024dc7211b..275c41732c1 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1757,6 +1757,12 @@ namespace ts { owners: string[]; } + interface VisibleModuleInfo { + moduleName: string; + moduleDir: string; + canBeImported: boolean; + } + export interface DisplayPartsSymbolWriter extends SymbolWriter { displayParts(): SymbolDisplayPart[]; } @@ -4438,18 +4444,100 @@ namespace ts { /** * Check all of the declared modules and those in node modules. Possible sources of modules: * Modules that are found by the type checker + * Modules found relative to "baseUrl" compliler options (including patterns from "paths" compiler option) * Modules from node_modules (i.e. those listed in package.json) * This includes all files that are found in node_modules/moduleName/ with acceptable file extensions */ function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string): CompletionEntry[] { - return ts.map(enumeratePotentialNonRelativeModules(fragment, scriptPath), (moduleName) => { - return { - name: moduleName, - kind: ScriptElementKind.externalModuleName, - kindModifiers: ScriptElementKindModifier.none, - sortText: moduleName - }; + const options = program.getCompilerOptions(); + const { baseUrl, paths } = options; + + let result: CompletionEntry[]; + + if (baseUrl) { + const fileExtensions = getSupportedExtensions(options); + const absolute = isRootedDiskPath(baseUrl) ? baseUrl : combinePaths(host.getCurrentDirectory(), baseUrl) + result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false); + + if (paths) { + for (var path in paths) { + if (paths.hasOwnProperty(path)) { + if (path === "*") { + if (paths[path]) { + forEach(paths[path], pattern => { + forEach(getModulesForPathsPattern(fragment, baseUrl, pattern, fileExtensions), match => { + result.push(createCompletionEntryForModule(match, ScriptElementKind.externalModuleName)); + }); + }); + } + } + else if (startsWith(path, fragment)) { + const entry = paths[path] && paths[path].length === 1 && paths[path][0]; + if (entry) { + result.push(createCompletionEntryForModule(path, ScriptElementKind.externalModuleName)); + } + } + } + } + } + } + else { + result = []; + } + + + + forEach(enumeratePotentialNonRelativeModules(fragment, scriptPath), moduleName => { + result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); }); + + return result; + } + + function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: string[]): string[] { + const parsed = hasZeroOrOneAsteriskCharacter(pattern) ? tryParsePattern(pattern) : undefined; + if (parsed) { + const hasTrailingSlash = parsed.prefix.charAt(parsed.prefix.length - 1) === "/" || parsed.prefix.charAt(parsed.prefix.length - 1) === "\\"; + + // The prefix has two effective parts: the directory path and the base component after the filepath that is not a + // full directory component. For example: directory/path/of/prefix/base* + const normalizedPrefix = hasTrailingSlash ? ensureTrailingDirectorySeparator(normalizePath(parsed.prefix)) : normalizePath(parsed.prefix); + const normalizedPrefixDirectory = getDirectoryPath(normalizedPrefix); + const normalizedPrefixBase = getBaseFileName(normalizedPrefix); + + const fragmentHasPath = fragment.indexOf(directorySeparator) !== -1; + + // Try and expand the prefix to include any path from the fragment so that we can limit the readDirectory call + const expandedPrefixDir = fragmentHasPath ? combinePaths(normalizedPrefixDirectory, normalizedPrefixBase + getDirectoryPath(fragment)) : normalizedPrefixDirectory; + + const normalizedSuffix = normalizePath(parsed.suffix); + const baseDirectory = combinePaths(baseUrl, expandedPrefixDir); + const completePrefix = fragmentHasPath ? baseDirectory : ensureTrailingDirectorySeparator(baseDirectory) + normalizedPrefixBase; + + // If we have a suffix, then we need to read the directory all the way down. We could create a glob + // that encodes the suffix, but we would have to escape the character "?" which readDirectory + // doesn't support. For now, this is safer but slower + const includeGlob = normalizedSuffix ? "**/*" : "./*" + + const matches = host.readDirectory(baseDirectory, fileExtensions, undefined, [includeGlob]); + const result: string[] = []; + + // Trim away prefix and suffix + forEach(matches, match => { + const normalizedMatch = normalizePath(match); + if (!endsWith(normalizedMatch, normalizedSuffix) || !startsWith(normalizedMatch, completePrefix)) { + return; + } + + const start = completePrefix.length; + const length = normalizedMatch.length - start - normalizedSuffix.length; + + result.push(removeFileExtension(normalizedMatch.substr(start, length))); + }); + return result; + } + + return undefined; } function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string): string[] { @@ -4513,6 +4601,112 @@ namespace ts { } } + function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string, modulePrefix?: string) { + const result: VisibleModuleInfo[] = []; + findPackageJsons(scriptPath).forEach((packageJson) => { + const package = tryReadingPackageJson(packageJson); + if (!package) { + return; + } + + const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules"); + const foundModuleNames: string[] = []; + + if (package.dependencies) { + addPotentialPackageNames(package.dependencies, modulePrefix, foundModuleNames); + } + if (package.devDependencies) { + addPotentialPackageNames(package.devDependencies, modulePrefix, foundModuleNames); + } + + forEach(foundModuleNames, (moduleName) => { + const moduleDir = combinePaths(nodeModulesDir, moduleName); + result.push({ + moduleName, + moduleDir, + canBeImported: moduleCanBeImported(moduleDir) + }); + }); + }); + + return result; + + function findPackageJsons(currentDir: string): string[] { + const paths: string[] = []; + let currentConfigPath: string; + while (true) { + currentConfigPath = findConfigFile(currentDir, (f) => host.fileExists(f), "package.json"); + if (currentConfigPath) { + paths.push(currentConfigPath); + + currentDir = getDirectoryPath(currentConfigPath); + const parent = getDirectoryPath(currentDir); + if (currentDir === parent) { + break; + } + currentDir = parent; + } + else { + break; + } + } + + return paths; + } + + function tryReadingPackageJson(filePath: string) { + try { + const fileText = host.readFile(filePath); + return JSON.parse(fileText); + } + catch (e) { + return undefined; + } + } + + function addPotentialPackageNames(dependencies: any, prefix: string, result: string[]) { + for (const dep in dependencies) { + if (dependencies.hasOwnProperty(dep) && (!prefix || startsWith(dep, prefix))) { + result.push(dep); + } + } + } + + /* + * A module can be imported by name alone if one of the following is true: + * It defines the "typings" property in its package.json + * The module has a "main" export and an index.d.ts file + * The module has an index.ts + */ + function moduleCanBeImported(modulePath: string): boolean { + const packagePath = combinePaths(modulePath, "package.json"); + + let hasMainExport = false; + if (host.fileExists(packagePath)) { + const package = tryReadingPackageJson(packagePath); + if (package) { + if (package.typings) { + return true; + } + hasMainExport = !!package.main; + } + } + + hasMainExport = hasMainExport || host.fileExists(combinePaths(modulePath, "index.js")); + + return (hasMainExport && host.fileExists(combinePaths(modulePath, "index.d.ts"))) || host.fileExists(combinePaths(modulePath, "index.ts")); + } + } + + function createCompletionEntryForModule(name: string, kind: string): CompletionEntry { + return { + name, + kind, + kindModifiers: ScriptElementKindModifier.none, + sortText: name + } + } + function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails { synchronizeHostData(); diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 44423860d4a..0c4c0fd1dea 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -6,12 +6,6 @@ namespace ts { list: Node; } - export interface VisibleModuleInfo { - moduleName: string; - moduleDir: string; - canBeImported: boolean; - } - export function getLineStartPositionForPosition(position: number, sourceFile: SourceFile): number { const lineStarts = sourceFile.getLineStarts(); const line = sourceFile.getLineAndCharacterOfPosition(position).line; @@ -933,101 +927,4 @@ namespace ts { } return ensureScriptKind(fileName, scriptKind); } - - export function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string, modulePrefix?: string) { - const result: VisibleModuleInfo[] = []; - findPackageJsons(scriptPath).forEach((packageJson) => { - const package = tryReadingPackageJson(packageJson); - if (!package) { - return; - } - - const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules"); - const foundModuleNames: string[] = []; - - if (package.dependencies) { - addPotentialPackageNames(package.dependencies, modulePrefix, foundModuleNames); - } - if (package.devDependencies) { - addPotentialPackageNames(package.devDependencies, modulePrefix, foundModuleNames); - } - - forEach(foundModuleNames, (moduleName) => { - const moduleDir = combinePaths(nodeModulesDir, moduleName); - result.push({ - moduleName, - moduleDir, - canBeImported: moduleCanBeImported(moduleDir) - }); - }); - }); - - return result; - - function findPackageJsons(currentDir: string): string[] { - const paths: string[] = []; - let currentConfigPath: string; - while (true) { - currentConfigPath = findConfigFile(currentDir, (f) => host.fileExists(f), "package.json"); - if (currentConfigPath) { - paths.push(currentConfigPath); - - currentDir = getDirectoryPath(currentConfigPath); - const parent = getDirectoryPath(currentDir); - if (currentDir === parent) { - break; - } - currentDir = parent; - } - else { - break; - } - } - - return paths; - } - - function tryReadingPackageJson(filePath: string) { - try { - const fileText = host.readFile(filePath); - return JSON.parse(fileText); - } - catch (e) { - return undefined; - } - } - - function addPotentialPackageNames(dependencies: any, prefix: string, result: string[]) { - for (const dep in dependencies) { - if (dependencies.hasOwnProperty(dep) && (!prefix || startsWith(dep, prefix))) { - result.push(dep); - } - } - } - - /* - * A module can be imported by name alone if one of the following is true: - * It defines the "typings" property in its package.json - * The module has a "main" export and an index.d.ts file - * The module has an index.ts - */ - function moduleCanBeImported(modulePath: string): boolean { - const packagePath = combinePaths(modulePath, "package.json"); - - let hasMainExport = false; - if (host.fileExists(packagePath)) { - const package = tryReadingPackageJson(packagePath); - if (package) { - if (package.typings) { - return true; - } - hasMainExport = !!package.main; - } - } - - hasMainExport = hasMainExport || host.fileExists(combinePaths(modulePath, "index.js")); - - return (hasMainExport && host.fileExists(combinePaths(modulePath, "index.d.ts"))) || host.fileExists(combinePaths(modulePath, "index.ts")); - } - } } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts new file mode 100644 index 00000000000..35e144c7184 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts @@ -0,0 +1,27 @@ +/// +// @baseUrl: tests/cases/fourslash/modules + +// @Filename: tests/test0.ts +//// import * as foo1 from "mod/*import_as0*/ +//// import foo2 = require("mod/*import_equals0*/ +//// var foo3 = require("mod/*require0*/ + +// @Filename: modules/module.ts +//// export var x = 5; + +// @Filename: package.json +//// { "dependencies": { "module-from-node": "latest" } } +// @Filename: node_modules/module-from-node/index.ts +//// /*module1*/ + + + +const kinds = ["import_as", "import_equals", "require"]; + +for (const kind of kinds) { + goTo.marker(kind + "0"); + + verify.completionListContains("module"); + verify.completionListContains("module-from-node"); + verify.not.completionListItemsCountIsGreaterThan(2); +} diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts new file mode 100644 index 00000000000..0e8da4b02bb --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts @@ -0,0 +1,53 @@ +/// + +// @Filename: tsconfig.json +//// { +//// "compilerOptions": { +//// "baseUrl": "./modules", +//// "paths": { +//// "*": [ +//// "prefix/0*/suffix.ts", +//// "prefix-only/*", +//// "*/suffix-only.ts" +//// ] +//// } +//// } +//// } + + +// @Filename: tests/test0.ts +//// import * as foo1 from "0/*import_as0*/ +//// import foo2 = require("0/*import_equals0*/ +//// var foo3 = require("0/*require0*/ + +//// import * as foo1 from "1/*import_as1*/ +//// import foo2 = require("1/*import_equals1*/ +//// var foo3 = require("1/*require1*/ + +//// import * as foo1 from "2/*import_as2*/ +//// import foo2 = require("2/*import_equals2*/ +//// var foo3 = require("2/*require2*/ + + +// @Filename: modules/prefix/00test/suffix.ts +//// export var x = 5; + +// @Filename: modules/prefix-only/1test.ts +//// export var y = 5; + +// @Filename: modules/2test/suffix-only.ts +//// export var z = 5; + + +const kinds = ["import_as", "import_equals", "require"]; + +for (const kind of kinds) { + goTo.marker(kind + "0"); + verify.completionListContains("0test"); + + goTo.marker(kind + "1"); + verify.completionListContains("1test"); + + goTo.marker(kind + "2"); + verify.completionListContains("2test"); +} diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts new file mode 100644 index 00000000000..3c32ec2670d --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts @@ -0,0 +1,34 @@ +/// + +// @Filename: tsconfig.json +//// { +//// "compilerOptions": { +//// "baseUrl": "./modules", +//// "paths": { +//// "module1": ["some/path/whatever.ts"], +//// "module2": ["some/other/path.ts"] +//// } +//// } +//// } + + +// @Filename: tests/test0.ts +//// import * as foo1 from "m/*import_as0*/ +//// import foo2 = require("m/*import_equals0*/ +//// var foo3 = require("m/*require0*/ + +// @Filename: some/path/whatever.ts +//// export var x = 9; + +// @Filename: some/other/path.ts +//// export var y = 10; + + +const kinds = ["import_as", "import_equals", "require"]; + +for (const kind of kinds) { + goTo.marker(kind + "0"); + verify.completionListContains("module1"); + verify.completionListContains("module2"); + verify.not.completionListItemsCountIsGreaterThan(2); +} From 0b16180174ab6ccab7b58ef15b08f470090b1159 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Wed, 27 Jul 2016 11:41:45 -0700 Subject: [PATCH 10/73] Import completions with rootdirs compiler option --- src/harness/harnessLanguageService.ts | 17 ++++++- src/services/services.ts | 49 +++++++++++++++++-- ...mpletionForStringLiteralRelativeImport4.ts | 48 ++++++++++++++++++ 3 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 3cb83faf610..a488146ba69 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -219,7 +219,22 @@ namespace Harness.LanguageService { return snapshot.getText(0, snapshot.getLength()); } resolvePath(path: string): string { - return ts.normalizePath(ts.isRootedDiskPath(path) ? path : ts.combinePaths(this.getCurrentDirectory(), path)); + if (!ts.isRootedDiskPath(path)) { + // An "absolute" path for fourslash is one that is contained within the tests directory + const components = ts.getNormalizedPathComponents(path, this.getCurrentDirectory()); + if (components.length) { + // If this is still a relative path after normalization (i.e. currentDirectory is relative), the root will be the empty string + if (!components[0]) { + components.splice(0, 1); + if (components[0] !== "tests") { + // If not contained within test, assume its relative to the directory containing the test files + return ts.normalizePath(ts.combinePaths("tests/cases/fourslash", components.join(ts.directorySeparator))); + } + } + return ts.normalizePath(components.join(ts.directorySeparator)); + } + } + return ts.normalizePath(path); } diff --git a/src/services/services.ts b/src/services/services.ts index 275c41732c1..43f9552730d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4379,7 +4379,15 @@ namespace ts { const isRelativePath = startsWith(literalValue, "."); const scriptDir = getDirectoryPath(node.getSourceFile().path); if (isRelativePath || isRootedDiskPath(literalValue)) { - result = getCompletionEntriesForDirectoryFragment(literalValue, scriptDir, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false); + const compilerOptions = program.getCompilerOptions(); + if (compilerOptions.rootDirs) { + result = getCompletionEntriesForDirectoryFragmentWithRootDirs( + compilerOptions.rootDirs, literalValue, scriptDir, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false); + } + else { + result = getCompletionEntriesForDirectoryFragment( + literalValue, scriptDir, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false); + } } else { // Check for node modules @@ -4393,10 +4401,42 @@ namespace ts { }; } - function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean): CompletionEntry[] { - // Complete the path by looking for source files and directories + /** + * Takes a script path and returns paths for all potential folders that could be merged with its + * containing folder via the "rootDirs" compiler option + */ + function getBaseDirectoriesFromRootDirs(rootDirs: string[], basePath: string, scriptPath: string, ignoreCase: boolean) { + // Make all paths absolute/normalized if they are not already + rootDirs = map(rootDirs, rootDirectory => normalizePath(isRootedDiskPath(rootDirectory) ? rootDirectory : combinePaths(basePath, rootDirectory))); + + // Determine the path to the directory containing the script relative to the root directory it is contained within + let relativeDirectory: string; + forEach(rootDirs, rootDirectory => { + if (containsPath(rootDirectory, scriptPath, basePath, ignoreCase)) { + relativeDirectory = scriptPath.substr(rootDirectory.length); + return true; + } + }); + + // Now find a path for each potential directory that is to be merged with the one containing the script + return deduplicate(map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory))); + } + + function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean): CompletionEntry[] { + const basePath = program.getCompilerOptions().project || host.getCurrentDirectory(); + + const baseDirectories = getBaseDirectoriesFromRootDirs( + rootDirs, basePath, scriptPath, host.useCaseSensitiveFileNames && !host.useCaseSensitiveFileNames()); const result: CompletionEntry[] = []; + for (const baseDirectory of baseDirectories) { + getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, result); + } + + return result; + } + + function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, result: CompletionEntry[] = []): CompletionEntry[] { const toComplete = getBaseFileName(fragment); const absolutePath = normalizeSlashes(host.resolvePath(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment))); const baseDirectory = toComplete ? getDirectoryPath(absolutePath) : absolutePath; @@ -4456,7 +4496,8 @@ namespace ts { if (baseUrl) { const fileExtensions = getSupportedExtensions(options); - const absolute = isRootedDiskPath(baseUrl) ? baseUrl : combinePaths(host.getCurrentDirectory(), baseUrl) + const projectDir = options.project || host.getCurrentDirectory(); + const absolute = isRootedDiskPath(baseUrl) ? baseUrl : combinePaths(projectDir, baseUrl); result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false); if (paths) { diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts new file mode 100644 index 00000000000..1895fff8514 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts @@ -0,0 +1,48 @@ +/// + +// @rootDirs: sub/src1,src2 + +// @Filename: src2/test0.ts +//// import * as foo1 from "./mo/*import_as0*/ +//// import foo2 = require("./mo/*import_equals0*/ +//// var foo3 = require("./mo/*require0*/ + +// @Filename: src2/module0.ts +//// export var w = 0; + +// @Filename: sub/src1/module1.ts +//// export var x = 0; + +// @Filename: sub/src1/module2.ts +//// export var y = 0; + +// @Filename: sub/src1/more/module3.ts +//// export var z = 0; + + +// @Filename: f1.ts +//// /*f1*/ +// @Filename: f2.tsx +//// /*f2*/ +// @Filename: folder/f1.ts +//// /*subf1*/ +// @Filename: f3.js +//// /*f3*/ +// @Filename: f4.jsx +//// /*f4*/ +// @Filename: e1.ts +//// /*e1*/ +// @Filename: e2.js +//// /*e2*/ + +const kinds = ["import_as", "import_equals", "require"]; + +for (const kind of kinds) { + goTo.marker(kind + "0"); + + verify.completionListContains("module0"); + verify.completionListContains("module1"); + verify.completionListContains("module2"); + verify.completionListContains("more/"); + verify.not.completionListItemsCountIsGreaterThan(4); +} \ No newline at end of file From dbf19f18af35afc21640ecf9b80a6c745992f5a7 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Thu, 28 Jul 2016 11:52:15 -0700 Subject: [PATCH 11/73] Adding import completions for typings --- src/harness/fourslash.ts | 4 +- src/services/services.ts | 125 +++++++++++++----- ...etionForStringLiteralNonrelativeImport2.ts | 7 +- ...etionForStringLiteralNonrelativeImport4.ts | 6 +- ...rStringLiteralNonrelativeImportTypings1.ts | 32 +++++ ...rStringLiteralNonrelativeImportTypings2.ts | 29 ++++ ...rStringLiteralNonrelativeImportTypings3.ts | 25 ++++ 7 files changed, 183 insertions(+), 45 deletions(-) create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 87b537417ea..8a8cb348a73 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -260,8 +260,8 @@ namespace FourSlash { // Extend our existing compiler options so that we can also support tsconfig only options if (configJson.config.compilerOptions) { - let baseDir = ts.normalizePath(ts.getDirectoryPath(file.fileName)); - let tsConfig = ts.convertCompilerOptionsFromJson(configJson.config.compilerOptions, baseDir, file.fileName); + const baseDirectory = ts.normalizePath(ts.getDirectoryPath(file.fileName)); + const tsConfig = ts.convertCompilerOptionsFromJson(configJson.config.compilerOptions, baseDirectory, file.fileName); if (!tsConfig.errors || !tsConfig.errors.length) { compilationOptions = ts.extend(compilationOptions, tsConfig.options); diff --git a/src/services/services.ts b/src/services/services.ts index 43f9552730d..76aed2ffe01 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1968,7 +1968,7 @@ namespace ts { * for completions. * For example, this matches /// { result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); @@ -4558,7 +4558,7 @@ namespace ts { // If we have a suffix, then we need to read the directory all the way down. We could create a glob // that encodes the suffix, but we would have to escape the character "?" which readDirectory // doesn't support. For now, this is safer but slower - const includeGlob = normalizedSuffix ? "**/*" : "./*" + const includeGlob = normalizedSuffix ? "**/*" : "./*"; const matches = host.readDirectory(baseDirectory, fileExtensions, undefined, [includeGlob]); const result: string[] = []; @@ -4629,19 +4629,97 @@ namespace ts { const text = sourceFile.text.substr(node.pos, position); const match = tripleSlashDirectiveFragmentRegex.exec(text); if (match) { - const fragment = match[1]; - const scriptPath = getDirectoryPath(sourceFile.path); - return { - isMemberCompletion: false, - isNewIdentifierLocation: false, - entries: getCompletionEntriesForDirectoryFragment(fragment, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true) - }; + const kind= match[1]; + const fragment = match[2]; + if (kind === "path") { + // Give completions for a relative path + const scriptPath = getDirectoryPath(sourceFile.path); + return { + isMemberCompletion: false, + isNewIdentifierLocation: false, + entries: getCompletionEntriesForDirectoryFragment(fragment, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true) + }; + } + else { + // Give completions based on what is available in the types directory + } } return undefined; } } + function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, result: CompletionEntry[]): CompletionEntry[] { + // Check for typings specified in compiler options + if (options.types) { + forEach(options.types, moduleName => { + result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); + }); + } + else if (options.typeRoots) { + const absoluteRoots = map(options.typeRoots, rootDirectory => getAbsoluteProjectPath(rootDirectory, host, options.project)); + forEach(absoluteRoots, absoluteRoot => getCompletionEntriesFromDirectory(host, options, absoluteRoot, result)); + } + + // Also get all @types typings installed in visible node_modules directories + forEach(findPackageJsons(scriptPath), package => { + const typesDir = combinePaths(getDirectoryPath(package), "node_modules/@types"); + getCompletionEntriesFromDirectory(host, options, typesDir, result); + }); + + return result; + } + + function getAbsoluteProjectPath(path: string, host: LanguageServiceHost, projectDir?: string) { + if (isRootedDiskPath(path)) { + return normalizePath(path); + } + + if (projectDir) { + return normalizePath(combinePaths(projectDir, path)); + } + + return normalizePath(host.resolvePath(path)); + } + + function getCompletionEntriesFromDirectory(host: LanguageServiceHost, options: CompilerOptions, directory: string, result: CompletionEntry[]) { + if (directoryProbablyExists(directory, host)) { + const typeDirectories = host.readDirectory(directory, getSupportedExtensions(options), /*exclude*/undefined, /*include*/["./*/*"]); + const seen: {[index: string]: boolean} = {}; + forEach(typeDirectories, typeFile => { + const typeDirectory = getDirectoryPath(typeFile); + if (!hasProperty(seen, typeDirectory)) { + seen[typeDirectory] = true; + result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName)); + } + }); + } + } + + function findPackageJsons(currentDir: string): string[] { + const paths: string[] = []; + let currentConfigPath: string; + while (true) { + currentConfigPath = findConfigFile(currentDir, (f) => host.fileExists(f), "package.json"); + if (currentConfigPath) { + paths.push(currentConfigPath); + + currentDir = getDirectoryPath(currentConfigPath); + const parent = getDirectoryPath(currentDir); + if (currentDir === parent) { + break; + } + currentDir = parent; + } + else { + break; + } + } + + return paths; + } + + function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string, modulePrefix?: string) { const result: VisibleModuleInfo[] = []; findPackageJsons(scriptPath).forEach((packageJson) => { @@ -4672,29 +4750,6 @@ namespace ts { return result; - function findPackageJsons(currentDir: string): string[] { - const paths: string[] = []; - let currentConfigPath: string; - while (true) { - currentConfigPath = findConfigFile(currentDir, (f) => host.fileExists(f), "package.json"); - if (currentConfigPath) { - paths.push(currentConfigPath); - - currentDir = getDirectoryPath(currentConfigPath); - const parent = getDirectoryPath(currentDir); - if (currentDir === parent) { - break; - } - currentDir = parent; - } - else { - break; - } - } - - return paths; - } - function tryReadingPackageJson(filePath: string) { try { const fileText = host.readFile(filePath); @@ -4745,7 +4800,7 @@ namespace ts { kind, kindModifiers: ScriptElementKindModifier.none, sortText: name - } + }; } function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails { diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts index 2b5f57412de..11420fe4f37 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts @@ -22,11 +22,8 @@ // @Filename: node_modules/unlisted-module/index.js //// /*unlisted-module*/ -// @Filename: node_modules/@types/fake-module/other.d.ts -//// declare module "fake-module/other" {} - -// @Filename: node_modules/@types/unlisted-module/index.d.ts -//// /*unlisted-types*/ +// @Filename: ambient.ts +//// declare module "fake-module/other" const kinds = ["import_as", "import_equals", "require"]; diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts index 64e3cbad48e..5a96e4ee63e 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts @@ -20,7 +20,7 @@ // @Filename: dir1/package.json //// { "dependencies": { "fake-module2": "latest" } } -// @Filename: dir1/node_modules/@types/fake-module2/js.d.ts +// @Filename: dir1/node_modules/fake-module2/index.ts //// declare module "ambient-module-test" {} // @Filename: dir1/dir2/dir3/package.json @@ -34,7 +34,7 @@ for (const kind of kinds) { goTo.marker(kind + "0"); verify.completionListContains("fake-module/"); - verify.completionListContains("fake-module2/"); + verify.completionListContains("fake-module2"); verify.completionListContains("fake-module3/"); verify.not.completionListItemsCountIsGreaterThan(3); @@ -46,7 +46,7 @@ for (const kind of kinds) { goTo.marker(kind + "2"); verify.completionListContains("fake-module/"); - verify.completionListContains("fake-module2/"); + verify.completionListContains("fake-module2"); verify.completionListContains("fake-module3/"); verify.not.completionListItemsCountIsGreaterThan(3); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts new file mode 100644 index 00000000000..53dc12033c9 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts @@ -0,0 +1,32 @@ +/// + +// @typeRoots: my_typings,my_other_typings + + +// @Filename: tests/test0.ts +//// import * as foo1 from "m/*import_as0*/ +//// import foo2 = require("m/*import_equals0*/ +//// var foo3 = require("m/*require0*/ + +// @Filename: my_typings/module-x/index.d.ts +//// export var x = 9; + +// @Filename: my_typings/module-x/whatever.d.ts +//// export var w = 9; + +// @Filename: my_typings/module-y/index.d.ts +//// export var y = 9; + +// @Filename: my_other_typings/module-z/index.d.ts +//// export var z = 9; + + +const kinds = ["import_as", "import_equals", "require"]; + +for (const kind of kinds) { + goTo.marker(kind + "0"); + verify.completionListContains("module-x"); + verify.completionListContains("module-y"); + verify.completionListContains("module-z"); + verify.not.completionListItemsCountIsGreaterThan(3); +} diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts new file mode 100644 index 00000000000..01e6f0ed420 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts @@ -0,0 +1,29 @@ +/// + +// @typeRoots: my_typings,my_other_typings +// @types: module-x,module-z + + +// @Filename: tests/test0.ts +//// import * as foo1 from "m/*import_as0*/ +//// import foo2 = require("m/*import_equals0*/ +//// var foo3 = require("m/*require0*/ + +// @Filename: my_typings/module-x/index.d.ts +//// export var x = 9; + +// @Filename: my_typings/module-y/index.d.ts +//// export var y = 9; + +// @Filename: my_other_typings/module-z/index.d.ts +//// export var z = 9; + + +const kinds = ["import_as", "import_equals", "require"]; + +for (const kind of kinds) { + goTo.marker(kind + "0"); + verify.completionListContains("module-x"); + verify.completionListContains("module-z"); + verify.not.completionListItemsCountIsGreaterThan(2); +} diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts new file mode 100644 index 00000000000..9710ce05cd5 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts @@ -0,0 +1,25 @@ +/// + +// @Filename: subdirectory/test0.ts +//// import * as foo1 from "m/*import_as0*/ +//// import foo2 = require("m/*import_equals0*/ +//// var foo3 = require("m/*require0*/ + +// @Filename: subdirectory/node_modules/@types/module-x/index.d.ts +//// export var x = 9; +// @Filename: subdirectory/package.json +//// { "dependencies": { "@types/module-x": "latest" } } + +// @Filename: node_modules/@types/module-y/index.d.ts +//// export var y = 9; +// @Filename: package.json +//// { "dependencies": { "@types/module-y": "latest" } } + +const kinds = ["import_as", "import_equals", "require"]; + +for (const kind of kinds) { + goTo.marker(kind + "0"); + verify.completionListContains("module-x"); + verify.completionListContains("module-y"); + verify.not.completionListItemsCountIsGreaterThan(2); +} From fdbc23e9acc1c2bbc04c6b8440a4bfe3577d6f78 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Thu, 28 Jul 2016 13:12:53 -0700 Subject: [PATCH 12/73] Add completions for types triple slash directives --- src/services/services.ts | 23 ++++++++++++++----- src/services/utilities.ts | 5 ++++ ...rStringLiteralNonrelativeImportTypings1.ts | 3 ++- ...rStringLiteralNonrelativeImportTypings2.ts | 3 ++- ...rStringLiteralNonrelativeImportTypings3.ts | 3 ++- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 76aed2ffe01..a4a1a444592 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4538,11 +4538,10 @@ namespace ts { function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: string[]): string[] { const parsed = hasZeroOrOneAsteriskCharacter(pattern) ? tryParsePattern(pattern) : undefined; if (parsed) { - const hasTrailingSlash = parsed.prefix.charAt(parsed.prefix.length - 1) === "/" || parsed.prefix.charAt(parsed.prefix.length - 1) === "\\"; - // The prefix has two effective parts: the directory path and the base component after the filepath that is not a // full directory component. For example: directory/path/of/prefix/base* - const normalizedPrefix = hasTrailingSlash ? ensureTrailingDirectorySeparator(normalizePath(parsed.prefix)) : normalizePath(parsed.prefix); + const normalizedPrefix = hasTrailingDirectorySeparator(parsed.prefix) ? + ensureTrailingDirectorySeparator(normalizePath(parsed.prefix)) : normalizePath(parsed.prefix); const normalizedPrefixDirectory = getDirectoryPath(normalizedPrefix); const normalizedPrefixBase = getBaseFileName(normalizedPrefix); @@ -4582,6 +4581,13 @@ namespace ts { } function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string): string[] { + const trailingSeperator = hasTrailingDirectorySeparator(fragment); + fragment = normalizePath(fragment); + + if (trailingSeperator) { + fragment = ensureTrailingDirectorySeparator(fragment); + } + // If this is a nested module, get the module name const firstSeparator = fragment.indexOf(directorySeparator); const moduleNameFragment = firstSeparator !== -1 ? fragment.substr(0, firstSeparator) : fragment; @@ -4631,9 +4637,9 @@ namespace ts { if (match) { const kind= match[1]; const fragment = match[2]; + const scriptPath = getDirectoryPath(sourceFile.path); if (kind === "path") { // Give completions for a relative path - const scriptPath = getDirectoryPath(sourceFile.path); return { isMemberCompletion: false, isNewIdentifierLocation: false, @@ -4641,7 +4647,12 @@ namespace ts { }; } else { - // Give completions based on what is available in the types directory + // Give completions based on the typings available + return { + isMemberCompletion: false, + isNewIdentifierLocation: false, + entries: getCompletionEntriesFromTypings(host, program.getCompilerOptions(), scriptPath) + }; } } @@ -4649,7 +4660,7 @@ namespace ts { } } - function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, result: CompletionEntry[]): CompletionEntry[] { + function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, result: CompletionEntry[] = []): CompletionEntry[] { // Check for typings specified in compiler options if (options.types) { forEach(options.types, moduleName => { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 0c4c0fd1dea..3a9b56584c2 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -927,4 +927,9 @@ namespace ts { } return ensureScriptKind(fileName, scriptKind); } + + export function hasTrailingDirectorySeparator(path: string) { + const lastCharacter = path.charAt(path.length - 1); + return lastCharacter === "/" || lastCharacter === "\\"; + } } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts index 53dc12033c9..edce0ce7e9f 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts @@ -4,6 +4,7 @@ // @Filename: tests/test0.ts +//// /// //// import * as foo1 from "m/*import_as0*/ //// import foo2 = require("m/*import_equals0*/ //// var foo3 = require("m/*require0*/ @@ -21,7 +22,7 @@ //// export var z = 9; -const kinds = ["import_as", "import_equals", "require"]; +const kinds = ["types_ref", "import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts index 01e6f0ed420..541bb88c073 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts @@ -5,6 +5,7 @@ // @Filename: tests/test0.ts +//// /// //// import * as foo1 from "m/*import_as0*/ //// import foo2 = require("m/*import_equals0*/ //// var foo3 = require("m/*require0*/ @@ -19,7 +20,7 @@ //// export var z = 9; -const kinds = ["import_as", "import_equals", "require"]; +const kinds = ["types_ref", "import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts index 9710ce05cd5..7a7d4e00df9 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts @@ -1,6 +1,7 @@ /// // @Filename: subdirectory/test0.ts +//// /// //// import * as foo1 from "m/*import_as0*/ //// import foo2 = require("m/*import_equals0*/ //// var foo3 = require("m/*require0*/ @@ -15,7 +16,7 @@ // @Filename: package.json //// { "dependencies": { "@types/module-y": "latest" } } -const kinds = ["import_as", "import_equals", "require"]; +const kinds = ["types_ref", "import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); From 9e797b4c787b3352cdda0a39ad7ac1704c8b596e Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Thu, 28 Jul 2016 16:44:24 -0700 Subject: [PATCH 13/73] Use getDirectories and condition node modules resolution on moduleResolution flag --- src/services/services.ts | 65 ++++++++++--------- ...tionForStringLiteralNonrelativeImport10.ts | 33 ++++++++++ 2 files changed, 67 insertions(+), 31 deletions(-) create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts diff --git a/src/services/services.ts b/src/services/services.ts index a14090e26c9..025afcd1548 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1171,6 +1171,11 @@ namespace ts { resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[]; resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; directoryExists?(directoryName: string): boolean; + + /** + * getDirectories is also required for full import and type reference completions. Without it defined, certain + * completions will not be provided + */ getDirectories?(directoryName: string): string[]; } @@ -4652,7 +4657,7 @@ namespace ts { getCompletionEntriesFromTypings(host, options, scriptPath, result); - forEach(enumeratePotentialNonRelativeModules(fragment, scriptPath), moduleName => { + forEach(enumeratePotentialNonRelativeModules(fragment, scriptPath, options), moduleName => { result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); }); @@ -4704,7 +4709,7 @@ namespace ts { return undefined; } - function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string): string[] { + function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string, options: CompilerOptions): string[] { const trailingSeperator = hasTrailingDirectorySeparator(fragment); fragment = normalizePath(fragment); @@ -4733,19 +4738,21 @@ namespace ts { return moduleName; }); - forEach(enumerateNodeModulesVisibleToScript(host, scriptPath, moduleNameFragment), visibleModule => { - if (!isNestedModule) { - nonRelativeModules.push(visibleModule.canBeImported ? visibleModule.moduleName : ensureTrailingDirectorySeparator(visibleModule.moduleName)); - } - else { - const nestedFiles = host.readDirectory(visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]); + if (!options.moduleResolution || options.moduleResolution === ModuleResolutionKind.NodeJs) { + forEach(enumerateNodeModulesVisibleToScript(host, scriptPath, moduleNameFragment), visibleModule => { + if (!isNestedModule) { + nonRelativeModules.push(visibleModule.canBeImported ? visibleModule.moduleName : ensureTrailingDirectorySeparator(visibleModule.moduleName)); + } + else { + const nestedFiles = host.readDirectory(visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]); - forEach(nestedFiles, (f) => { - const nestedModule = removeFileExtension(getBaseFileName(f)); - nonRelativeModules.push(nestedModule); - }); - } - }); + forEach(nestedFiles, (f) => { + const nestedModule = removeFileExtension(getBaseFileName(f)); + nonRelativeModules.push(nestedModule); + }); + } + }); + } return deduplicate(nonRelativeModules); } @@ -4791,16 +4798,18 @@ namespace ts { result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); }); } - else if (options.typeRoots) { + else if (host.getDirectories && options.typeRoots) { const absoluteRoots = map(options.typeRoots, rootDirectory => getAbsoluteProjectPath(rootDirectory, host, options.project)); - forEach(absoluteRoots, absoluteRoot => getCompletionEntriesFromDirectory(host, options, absoluteRoot, result)); + forEach(absoluteRoots, absoluteRoot => getCompletionEntriesFromDirectories(host, options, absoluteRoot, result)); } - // Also get all @types typings installed in visible node_modules directories - forEach(findPackageJsons(scriptPath), package => { - const typesDir = combinePaths(getDirectoryPath(package), "node_modules/@types"); - getCompletionEntriesFromDirectory(host, options, typesDir, result); - }); + if (host.getDirectories) { + // Also get all @types typings installed in visible node_modules directories + forEach(findPackageJsons(scriptPath), package => { + const typesDir = combinePaths(getDirectoryPath(package), "node_modules/@types"); + getCompletionEntriesFromDirectories(host, options, typesDir, result); + }); + } return result; } @@ -4817,16 +4826,10 @@ namespace ts { return normalizePath(host.resolvePath(path)); } - function getCompletionEntriesFromDirectory(host: LanguageServiceHost, options: CompilerOptions, directory: string, result: CompletionEntry[]) { - if (directoryProbablyExists(directory, host)) { - const typeDirectories = host.readDirectory(directory, getSupportedExtensions(options), /*exclude*/undefined, /*include*/["./*/*"]); - const seen: {[index: string]: boolean} = {}; - forEach(typeDirectories, typeFile => { - const typeDirectory = getDirectoryPath(typeFile); - if (!hasProperty(seen, typeDirectory)) { - seen[typeDirectory] = true; - result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName)); - } + function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, result: CompletionEntry[]) { + if (host.getDirectories && directoryProbablyExists(directory, host)) { + forEach(host.getDirectories(directory), typeDirectory => { + result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName)); }); } } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts new file mode 100644 index 00000000000..cb1f67f6349 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts @@ -0,0 +1,33 @@ +/// + +// @moduleResolution: classic + +// @Filename: dir1/dir2/dir3/dir4/test0.ts +//// import * as foo1 from "f/*import_as0*/ +//// import * as foo3 from "fake-module/*import_as1*/ + +//// import foo4 = require("f/*import_equals0*/ +//// import foo6 = require("fake-module/*import_equals1*/ + +//// var foo7 = require("f/*require0*/ +//// var foo9 = require("fake-module/*require1*/ + +// @Filename: package.json +//// { "dependencies": { "fake-module": "latest" } } +// @Filename: node_modules/fake-module/ts.ts +//// /*module1*/ + +// @Filename: dir1/dir2/dir3/package.json +//// { "dependencies": { "fake-module3": "latest" } } +// @Filename: dir1/dir2/dir3/node_modules/fake-module3/ts.ts +//// /*module3*/ + +const kinds = ["import_as", "import_equals", "require"]; + +for (const kind of kinds) { + goTo.marker(kind + "0"); + verify.completionListIsEmpty(); + + goTo.marker(kind + "1"); + verify.completionListIsEmpty(); +} \ No newline at end of file From 4ec8b2b134c9f6c91251c12ec42974bf0b3b403f Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Mon, 1 Aug 2016 14:29:10 -0700 Subject: [PATCH 14/73] Refactoring import completions into their own api --- src/harness/fourslash.ts | 75 ++++ src/harness/harnessLanguageService.ts | 3 + src/server/client.ts | 15 + src/server/protocol.d.ts | 14 + src/server/session.ts | 1 + src/services/services.ts | 350 +++++++++--------- src/services/shims.ts | 8 + ...etionForStringLiteralNonrelativeImport1.ts | 22 +- ...tionForStringLiteralNonrelativeImport10.ts | 4 +- ...etionForStringLiteralNonrelativeImport2.ts | 6 +- ...etionForStringLiteralNonrelativeImport3.ts | 8 +- ...etionForStringLiteralNonrelativeImport4.ts | 20 +- ...etionForStringLiteralNonrelativeImport5.ts | 12 +- ...etionForStringLiteralNonrelativeImport6.ts | 14 +- ...etionForStringLiteralNonrelativeImport7.ts | 6 +- ...etionForStringLiteralNonrelativeImport8.ts | 6 +- ...etionForStringLiteralNonrelativeImport9.ts | 6 +- ...rStringLiteralNonrelativeImportTypings1.ts | 8 +- ...rStringLiteralNonrelativeImportTypings2.ts | 6 +- ...rStringLiteralNonrelativeImportTypings3.ts | 6 +- ...mpletionForStringLiteralRelativeImport1.ts | 38 +- ...mpletionForStringLiteralRelativeImport2.ts | 26 +- ...mpletionForStringLiteralRelativeImport3.ts | 20 +- ...mpletionForStringLiteralRelativeImport4.ts | 10 +- .../completionForTripleSlashReference1.ts | 50 +-- .../completionForTripleSlashReference2.ts | 36 +- .../completionForTripleSlashReference3.ts | 20 +- tests/cases/fourslash/fourslash.ts | 3 + 28 files changed, 456 insertions(+), 337 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 8a8cb348a73..8b0968664e0 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -597,6 +597,38 @@ namespace FourSlash { } } + public verifyImportModuleCompletionListItemsCountIsGreaterThan(count: number, negative: boolean) { + const completions = this.getImportModuleCompletionListAtCaret(); + const itemsCount = completions.length; + + if (negative) { + if (itemsCount > count) { + this.raiseError(`Expected import module completion list items count to not be greater than ${count}, but is actually ${itemsCount}`); + } + } + else { + if (itemsCount <= count) { + this.raiseError(`Expected import module completion list items count to be greater than ${count}, but is actually ${itemsCount}`); + } + } + } + + public verifyImportModuleCompletionListIsEmpty(negative: boolean) { + const completions = this.getImportModuleCompletionListAtCaret(); + if ((!completions || completions.length === 0) && negative) { + this.raiseError("Completion list is empty at caret at position " + this.activeFile.fileName + " " + this.currentCaretPosition); + } + else if (completions && completions.length !== 0 && !negative) { + let errorMsg = "\n" + "Completion List contains: [" + completions[0].name; + for (let i = 1; i < completions.length; i++) { + errorMsg += ", " + completions[i].name; + } + errorMsg += "]\n"; + + this.raiseError("Completion list is not empty at caret at position " + this.activeFile.fileName + " " + this.currentCaretPosition + errorMsg); + } + } + public verifyCompletionListStartsWithItemsInOrder(items: string[]): void { if (items.length === 0) { return; @@ -734,6 +766,28 @@ namespace FourSlash { } } + public verifyImportModuleCompletionListContains(symbol: string) { + const completions = this.getImportModuleCompletionListAtCaret(); + if (completions) { + if (!ts.forEach(completions, completion => completion.name === symbol)) { + const itemsString = completions.map(item => stringify({ name: item.name, span: item.span })).join(",\n"); + this.raiseError(`Expected "${symbol}" to be in list [${itemsString}]`); + } + } + else { + this.raiseError(`No import module completions at position '${this.currentCaretPosition}' when looking for '${symbol}'.`); + } + } + + public verifyImportModuleCompletionListDoesNotContain(symbol: string) { + const completions = this.getImportModuleCompletionListAtCaret(); + if (completions) { + if (ts.forEach(completions, completion => completion.name === symbol)) { + this.raiseError(`Import module completion list did contain ${symbol}`); + } + } + } + public verifyCompletionEntryDetails(entryName: string, expectedText: string, expectedDocumentation?: string, kind?: string) { const details = this.getCompletionEntryDetails(entryName); @@ -820,6 +874,10 @@ namespace FourSlash { return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition); } + private getImportModuleCompletionListAtCaret() { + return this.languageService.getImportModuleCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition); + } + private getCompletionEntryDetails(entryName: string) { return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName); } @@ -2885,6 +2943,23 @@ namespace FourSlashInterface { this.state.verifyCompletionListItemsCountIsGreaterThan(count, this.negative); } + public importModuleCompletionListContains(symbol: string): void { + if (this.negative) { + this.state.verifyImportModuleCompletionListDoesNotContain(symbol); + } + else { + this.state.verifyImportModuleCompletionListContains(symbol); + } + } + + public importModuleCompletionListItemsCountIsGreaterThan(count: number): void { + this.state.verifyImportModuleCompletionListItemsCountIsGreaterThan(count, this.negative); + } + + public importModuleCompletionListIsEmpty(): void { + this.state.verifyImportModuleCompletionListIsEmpty(this.negative); + } + public assertHasRanges(ranges: FourSlash.Range[]) { assert(ranges.length !== 0, "Array of ranges is expected to be non-empty"); } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index d5491d55acd..1972ab85b9d 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -426,6 +426,9 @@ namespace Harness.LanguageService { getCompletionsAtPosition(fileName: string, position: number): ts.CompletionInfo { return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position)); } + getImportModuleCompletionsAtPosition(fileName: string, position: number): ts.ImportCompletionEntry[] { + return unwrapJSONCallResult(this.shim.getImportModuleCompletionsAtPosition(fileName, position)); + } getCompletionEntryDetails(fileName: string, position: number, entryName: string): ts.CompletionEntryDetails { return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName)); } diff --git a/src/server/client.ts b/src/server/client.ts index f04dbd8dc02..04784a77f10 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -220,6 +220,21 @@ namespace ts.server { }; } + getImportModuleCompletionsAtPosition(fileName: string, position: number): ImportCompletionEntry[] { + const lineOffset = this.positionToOneBasedLineOffset(fileName, position); + const args: protocol.CompletionsRequestArgs = { + file: fileName, + line: lineOffset.line, + offset: lineOffset.offset, + prefix: undefined + }; + + const request = this.processRequest(CommandNames.ImportModuleCompletions, args); + const response = this.processResponse(request); + + return response.body; + } + getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails { const lineOffset = this.positionToOneBasedLineOffset(fileName, position); const args: protocol.CompletionDetailsRequestArgs = { diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts index 6442848abbe..803edce0d70 100644 --- a/src/server/protocol.d.ts +++ b/src/server/protocol.d.ts @@ -717,6 +717,16 @@ declare namespace ts.server.protocol { arguments: CompletionsRequestArgs; } + /** + * Import Module Completions request; value of command field is + * "importModuleCompletions". Given a file location (file, line, + * col) return the possible completions for external module + * specifiers or paths given that position refers to a module + * import declaration, require call, or triple slash reference. + */ + export interface ImportModuleCompletionsRequest extends FileLocationRequest { + } + /** * Arguments for completion details request. */ @@ -806,6 +816,10 @@ declare namespace ts.server.protocol { body?: CompletionEntry[]; } + export interface ImportModuleCompletionsResponse extends Response { + body?: ImportCompletionEntry[]; + } + export interface CompletionDetailsResponse extends Response { body?: CompletionEntryDetails[]; } diff --git a/src/server/session.ts b/src/server/session.ts index 7e1ca81e2af..74f6e6bb233 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -103,6 +103,7 @@ namespace ts.server { export const Change = "change"; export const Close = "close"; export const Completions = "completions"; + export const ImportModuleCompletions = "importModuleCompletions"; export const CompletionDetails = "completionEntryDetails"; export const Configure = "configure"; export const Definition = "definition"; diff --git a/src/services/services.ts b/src/services/services.ts index 025afcd1548..11eec28a5f6 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1208,6 +1208,7 @@ namespace ts { getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; getCompletionsAtPosition(fileName: string, position: number): CompletionInfo; + getImportModuleCompletionsAtPosition(fileName: string, position: number): ImportCompletionEntry[]; getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; @@ -1480,6 +1481,13 @@ namespace ts { sortText: string; } + export interface ImportCompletionEntry { + name: string; + kind: string; // see ScriptElementKind + span: TextSpan; + sortText: string; + } + export interface CompletionEntryDetails { name: string; kind: string; // see ScriptElementKind @@ -4225,10 +4233,6 @@ namespace ts { return getStringLiteralCompletionEntries(sourceFile, position); } - if (isInReferenceComment(sourceFile, position)) { - return getTripleSlashReferenceCompletion(sourceFile, position); - } - const completionData = getCompletionData(fileName, position); if (!completionData) { return undefined; @@ -4394,27 +4398,13 @@ namespace ts { // a['/*completion position*/'] return getStringLiteralCompletionEntriesFromElementAccess(node.parent); } - else if (node.parent.kind === SyntaxKind.ImportDeclaration || isExpressionOfExternalModuleImportEqualsDeclaration(node)) { - // Get all known external module names or complete a path to a module - return getStringLiteralCompletionEntriesFromModuleNames(node); - } else { const argumentInfo = SignatureHelp.getContainingArgumentInfo(node, position, sourceFile); if (argumentInfo) { // Get string literal completions from specialized signatures of the target // i.e. declare function f(a: 'A'); // f("/*completion position*/") - const callExpressionCompletionEntries = getStringLiteralCompletionEntriesFromCallExpression(argumentInfo, node); - if (callExpressionCompletionEntries) { - return callExpressionCompletionEntries; - } - else if (isRequireCall(node.parent, false)) { - // If that failed but this call mataches the signature of a require call, treat the literal as an external module name - return getStringLiteralCompletionEntriesFromModuleNames(node); - } - else { - return undefined; - } + return getStringLiteralCompletionEntriesFromCallExpression(argumentInfo, node); } // Get completion for string literal from string literal type @@ -4500,10 +4490,35 @@ namespace ts { } } } + } + + function getImportModuleCompletionsAtPosition(fileName: string, position: number): ImportCompletionEntry[] { + synchronizeHostData(); + + const sourceFile = getValidSourceFile(fileName); + if (isInReferenceComment(sourceFile, position)) { + return getTripleSlashReferenceCompletion(sourceFile, position); + } + else if (isInString(sourceFile, position)) { + const node = findPrecedingToken(position, sourceFile); + if (!node || node.kind !== SyntaxKind.StringLiteral) { + return undefined; + } + + if (node.parent.kind === SyntaxKind.ImportDeclaration || isExpressionOfExternalModuleImportEqualsDeclaration(node) || isRequireCall(node.parent, false)) { + // Get all known external module names or complete a path to a module + return getStringLiteralCompletionEntriesFromModuleNames(node); + } + } + + return undefined; function getStringLiteralCompletionEntriesFromModuleNames(node: StringLiteral) { const literalValue = node.text; - let result: CompletionEntry[]; + let result: ImportCompletionEntry[]; + + const nodeStart = node.getStart(); + const span: TextSpan = { start: nodeStart, length: nodeStart - node.getEnd() }; const isRelativePath = startsWith(literalValue, "."); const scriptDir = getDirectoryPath(node.getSourceFile().path); @@ -4511,30 +4526,26 @@ namespace ts { const compilerOptions = program.getCompilerOptions(); if (compilerOptions.rootDirs) { result = getCompletionEntriesForDirectoryFragmentWithRootDirs( - compilerOptions.rootDirs, literalValue, scriptDir, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false); + compilerOptions.rootDirs, literalValue, scriptDir, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, span); } else { result = getCompletionEntriesForDirectoryFragment( - literalValue, scriptDir, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false); + literalValue, scriptDir, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, span); } } else { // Check for node modules - result = getCompletionEntriesForNonRelativeModules(literalValue, scriptDir); + result = getCompletionEntriesForNonRelativeModules(literalValue, scriptDir, span); } - return { - isMemberCompletion: false, - isNewIdentifierLocation: true, - entries: result - }; + return result; } /** * Takes a script path and returns paths for all potential folders that could be merged with its * containing folder via the "rootDirs" compiler option */ - function getBaseDirectoriesFromRootDirs(rootDirs: string[], basePath: string, scriptPath: string, ignoreCase: boolean) { + function getBaseDirectoriesFromRootDirs(rootDirs: string[], basePath: string, scriptPath: string, ignoreCase: boolean): string[] { // Make all paths absolute/normalized if they are not already rootDirs = map(rootDirs, rootDirectory => normalizePath(isRootedDiskPath(rootDirectory) ? rootDirectory : combinePaths(basePath, rootDirectory))); @@ -4551,26 +4562,25 @@ namespace ts { return deduplicate(map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory))); } - function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean): CompletionEntry[] { + function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan): ImportCompletionEntry[] { const basePath = program.getCompilerOptions().project || host.getCurrentDirectory(); const baseDirectories = getBaseDirectoriesFromRootDirs( rootDirs, basePath, scriptPath, host.useCaseSensitiveFileNames && !host.useCaseSensitiveFileNames()); - const result: CompletionEntry[] = []; + const result: ImportCompletionEntry[] = []; for (const baseDirectory of baseDirectories) { - getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, result); + getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, span, result); } return result; } - function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, result: CompletionEntry[] = []): CompletionEntry[] { + function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, result: ImportCompletionEntry[] = []): ImportCompletionEntry[] { const toComplete = getBaseFileName(fragment); const absolutePath = normalizeSlashes(host.resolvePath(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment))); const baseDirectory = toComplete ? getDirectoryPath(absolutePath) : absolutePath; - if (directoryProbablyExists(baseDirectory, host)) { // Enumerate the available files const files = host.readDirectory(baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]); @@ -4583,8 +4593,8 @@ namespace ts { result.push({ name: fileName, kind: ScriptElementKind.directory, - kindModifiers: ScriptElementKindModifier.none, - sortText: fileName + sortText: fileName, + span }); } }); @@ -4599,8 +4609,8 @@ namespace ts { result.push({ name: ensureTrailingDirectorySeparator(directoryName), kind: ScriptElementKind.directory, - kindModifiers: ScriptElementKindModifier.none, - sortText: directoryName + sortText: directoryName, + span }); } }); @@ -4617,17 +4627,17 @@ namespace ts { * Modules from node_modules (i.e. those listed in package.json) * This includes all files that are found in node_modules/moduleName/ with acceptable file extensions */ - function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string): CompletionEntry[] { + function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string, span: TextSpan): ImportCompletionEntry[] { const options = program.getCompilerOptions(); const { baseUrl, paths } = options; - let result: CompletionEntry[]; + let result: ImportCompletionEntry[]; if (baseUrl) { const fileExtensions = getSupportedExtensions(options); const projectDir = options.project || host.getCurrentDirectory(); const absolute = isRootedDiskPath(baseUrl) ? baseUrl : combinePaths(projectDir, baseUrl); - result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false); + result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false, span); if (paths) { for (const path in paths) { @@ -4636,7 +4646,7 @@ namespace ts { if (paths[path]) { forEach(paths[path], pattern => { forEach(getModulesForPathsPattern(fragment, baseUrl, pattern, fileExtensions), match => { - result.push(createCompletionEntryForModule(match, ScriptElementKind.externalModuleName)); + result.push(createCompletionEntryForModule(match, ScriptElementKind.externalModuleName, span)); }); }); } @@ -4644,7 +4654,7 @@ namespace ts { else if (startsWith(path, fragment)) { const entry = paths[path] && paths[path].length === 1 && paths[path][0]; if (entry) { - result.push(createCompletionEntryForModule(path, ScriptElementKind.externalModuleName)); + result.push(createCompletionEntryForModule(path, ScriptElementKind.externalModuleName, span)); } } } @@ -4655,10 +4665,10 @@ namespace ts { result = []; } - getCompletionEntriesFromTypings(host, options, scriptPath, result); + getCompletionEntriesFromTypings(host, options, scriptPath, span, result); forEach(enumeratePotentialNonRelativeModules(fragment, scriptPath, options), moduleName => { - result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); + result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span)); }); return result; @@ -4757,12 +4767,14 @@ namespace ts { return deduplicate(nonRelativeModules); } - function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number) { + function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number): ImportCompletionEntry[] { const node = getTokenAtPosition(sourceFile, position); if (!node) { return undefined; } + const span: TextSpan = undefined; + const text = sourceFile.text.substr(node.pos, position); const match = tripleSlashDirectiveFragmentRegex.exec(text); if (match) { @@ -4771,174 +4783,161 @@ namespace ts { const scriptPath = getDirectoryPath(sourceFile.path); if (kind === "path") { // Give completions for a relative path - return { - isMemberCompletion: false, - isNewIdentifierLocation: false, - entries: getCompletionEntriesForDirectoryFragment(fragment, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true) - }; + return getCompletionEntriesForDirectoryFragment(fragment, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, span); } else { // Give completions based on the typings available - return { - isMemberCompletion: false, - isNewIdentifierLocation: false, - entries: getCompletionEntriesFromTypings(host, program.getCompilerOptions(), scriptPath) - }; + return getCompletionEntriesFromTypings(host, program.getCompilerOptions(), scriptPath, span); } } return undefined; } - } - function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, result: CompletionEntry[] = []): CompletionEntry[] { - // Check for typings specified in compiler options - if (options.types) { - forEach(options.types, moduleName => { - result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); - }); - } - else if (host.getDirectories && options.typeRoots) { - const absoluteRoots = map(options.typeRoots, rootDirectory => getAbsoluteProjectPath(rootDirectory, host, options.project)); - forEach(absoluteRoots, absoluteRoot => getCompletionEntriesFromDirectories(host, options, absoluteRoot, result)); + function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, span: TextSpan, result: ImportCompletionEntry[] = []): ImportCompletionEntry[] { + // Check for typings specified in compiler options + if (options.types) { + forEach(options.types, moduleName => { + result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span)); + }); + } + else if (host.getDirectories && options.typeRoots) { + const absoluteRoots = map(options.typeRoots, rootDirectory => getAbsoluteProjectPath(rootDirectory, host, options.project)); + forEach(absoluteRoots, absoluteRoot => getCompletionEntriesFromDirectories(host, options, absoluteRoot, span, result)); + } + + if (host.getDirectories) { + // Also get all @types typings installed in visible node_modules directories + forEach(findPackageJsons(scriptPath), package => { + const typesDir = combinePaths(getDirectoryPath(package), "node_modules/@types"); + getCompletionEntriesFromDirectories(host, options, typesDir, span, result); + }); + } + + return result; } - if (host.getDirectories) { - // Also get all @types typings installed in visible node_modules directories - forEach(findPackageJsons(scriptPath), package => { - const typesDir = combinePaths(getDirectoryPath(package), "node_modules/@types"); - getCompletionEntriesFromDirectories(host, options, typesDir, result); - }); + function getAbsoluteProjectPath(path: string, host: LanguageServiceHost, projectDir?: string) { + if (isRootedDiskPath(path)) { + return normalizePath(path); + } + + if (projectDir) { + return normalizePath(combinePaths(projectDir, path)); + } + + return normalizePath(host.resolvePath(path)); } - return result; - } - - function getAbsoluteProjectPath(path: string, host: LanguageServiceHost, projectDir?: string) { - if (isRootedDiskPath(path)) { - return normalizePath(path); + function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, span: TextSpan, result: ImportCompletionEntry[]) { + if (host.getDirectories && directoryProbablyExists(directory, host)) { + forEach(host.getDirectories(directory), typeDirectory => { + result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName, span)); + }); + } } - if (projectDir) { - return normalizePath(combinePaths(projectDir, path)); - } + function findPackageJsons(currentDir: string): string[] { + const paths: string[] = []; + let currentConfigPath: string; + while (true) { + currentConfigPath = findConfigFile(currentDir, (f) => host.fileExists(f), "package.json"); + if (currentConfigPath) { + paths.push(currentConfigPath); - return normalizePath(host.resolvePath(path)); - } - - function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, result: CompletionEntry[]) { - if (host.getDirectories && directoryProbablyExists(directory, host)) { - forEach(host.getDirectories(directory), typeDirectory => { - result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName)); - }); - } - } - - function findPackageJsons(currentDir: string): string[] { - const paths: string[] = []; - let currentConfigPath: string; - while (true) { - currentConfigPath = findConfigFile(currentDir, (f) => host.fileExists(f), "package.json"); - if (currentConfigPath) { - paths.push(currentConfigPath); - - currentDir = getDirectoryPath(currentConfigPath); - const parent = getDirectoryPath(currentDir); - if (currentDir === parent) { + currentDir = getDirectoryPath(currentConfigPath); + const parent = getDirectoryPath(currentDir); + if (currentDir === parent) { + break; + } + currentDir = parent; + } + else { break; } - currentDir = parent; - } - else { - break; } + + return paths; } - return paths; - } + function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string, modulePrefix?: string) { + const result: VisibleModuleInfo[] = []; + findPackageJsons(scriptPath).forEach((packageJson) => { + const package = tryReadingPackageJson(packageJson); + if (!package) { + return; + } - function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string, modulePrefix?: string) { - const result: VisibleModuleInfo[] = []; - findPackageJsons(scriptPath).forEach((packageJson) => { - const package = tryReadingPackageJson(packageJson); - if (!package) { - return; - } + const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules"); + const foundModuleNames: string[] = []; - const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules"); - const foundModuleNames: string[] = []; + if (package.dependencies) { + addPotentialPackageNames(package.dependencies, modulePrefix, foundModuleNames); + } + if (package.devDependencies) { + addPotentialPackageNames(package.devDependencies, modulePrefix, foundModuleNames); + } - if (package.dependencies) { - addPotentialPackageNames(package.dependencies, modulePrefix, foundModuleNames); - } - if (package.devDependencies) { - addPotentialPackageNames(package.devDependencies, modulePrefix, foundModuleNames); - } - - forEach(foundModuleNames, (moduleName) => { - const moduleDir = combinePaths(nodeModulesDir, moduleName); - result.push({ - moduleName, - moduleDir, - canBeImported: moduleCanBeImported(moduleDir) + forEach(foundModuleNames, (moduleName) => { + const moduleDir = combinePaths(nodeModulesDir, moduleName); + result.push({ + moduleName, + moduleDir, + canBeImported: moduleCanBeImported(moduleDir) + }); }); }); - }); - return result; + return result; - function tryReadingPackageJson(filePath: string) { - try { - const fileText = host.readFile(filePath); - return JSON.parse(fileText); - } - catch (e) { - return undefined; - } - } - - function addPotentialPackageNames(dependencies: any, prefix: string, result: string[]) { - for (const dep in dependencies) { - if (dependencies.hasOwnProperty(dep) && (!prefix || startsWith(dep, prefix))) { - result.push(dep); + function tryReadingPackageJson(filePath: string) { + try { + const fileText = host.readFile(filePath); + return JSON.parse(fileText); + } + catch (e) { + return undefined; } } - } - /* - * A module can be imported by name alone if one of the following is true: - * It defines the "typings" property in its package.json - * The module has a "main" export and an index.d.ts file - * The module has an index.ts - */ - function moduleCanBeImported(modulePath: string): boolean { - const packagePath = combinePaths(modulePath, "package.json"); - - let hasMainExport = false; - if (host.fileExists(packagePath)) { - const package = tryReadingPackageJson(packagePath); - if (package) { - if (package.typings) { - return true; + function addPotentialPackageNames(dependencies: any, prefix: string, result: string[]) { + for (const dep in dependencies) { + if (dependencies.hasOwnProperty(dep) && (!prefix || startsWith(dep, prefix))) { + result.push(dep); } - hasMainExport = !!package.main; } } - hasMainExport = hasMainExport || host.fileExists(combinePaths(modulePath, "index.js")); + /* + * A module can be imported by name alone if one of the following is true: + * It defines the "typings" property in its package.json + * The module has a "main" export and an index.d.ts file + * The module has an index.ts + */ + function moduleCanBeImported(modulePath: string): boolean { + const packagePath = combinePaths(modulePath, "package.json"); - return (hasMainExport && host.fileExists(combinePaths(modulePath, "index.d.ts"))) || host.fileExists(combinePaths(modulePath, "index.ts")); + let hasMainExport = false; + if (host.fileExists(packagePath)) { + const package = tryReadingPackageJson(packagePath); + if (package) { + if (package.typings) { + return true; + } + hasMainExport = !!package.main; + } + } + + hasMainExport = hasMainExport || host.fileExists(combinePaths(modulePath, "index.js")); + + return (hasMainExport && host.fileExists(combinePaths(modulePath, "index.d.ts"))) || host.fileExists(combinePaths(modulePath, "index.ts")); + } } - } - function createCompletionEntryForModule(name: string, kind: string): CompletionEntry { - return { - name, - kind, - kindModifiers: ScriptElementKindModifier.none, - sortText: name - }; + function createCompletionEntryForModule(name: string, kind: string, span: TextSpan): ImportCompletionEntry { + return { name, kind, sortText: name, span }; + } } function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails { @@ -8752,6 +8751,7 @@ namespace ts { getEncodedSyntacticClassifications, getEncodedSemanticClassifications, getCompletionsAtPosition, + getImportModuleCompletionsAtPosition, getCompletionEntryDetails, getSignatureHelpItems, getQuickInfoAtPosition, diff --git a/src/services/shims.ts b/src/services/shims.ts index ca12733e0a2..eb6c5e55b7b 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -131,6 +131,7 @@ namespace ts { getEncodedSemanticClassifications(fileName: string, start: number, length: number): string; getCompletionsAtPosition(fileName: string, position: number): string; + getImportModuleCompletionsAtPosition(fileName: string, position: number): string; getCompletionEntryDetails(fileName: string, position: number, entryName: string): string; getQuickInfoAtPosition(fileName: string, position: number): string; @@ -870,6 +871,13 @@ namespace ts { ); } + getImportModuleCompletionsAtPosition(fileName: string, position: number): string { + return this.forwardJSONCall( + `getImportModuleCompletionsAtPosition('${fileName}', ${position})`, + () => this.languageService.getCompletionsAtPosition(fileName, position) + ); + } + /** Get a string based representation of a completion list entry details */ public getCompletionEntryDetails(fileName: string, position: number, entryName: string) { return this.forwardJSONCall( diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts index 5c7b2016a75..f8ea68a271c 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts @@ -43,19 +43,19 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("fake-module"); - verify.completionListContains("fake-module-dev"); - verify.not.completionListItemsCountIsGreaterThan(2); + verify.importModuleCompletionListContains("fake-module"); + verify.importModuleCompletionListContains("fake-module-dev"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); goTo.marker(kind + "1"); - verify.completionListContains("index"); - verify.completionListContains("ts"); - verify.completionListContains("dts"); - verify.completionListContains("tsx"); - verify.not.completionListItemsCountIsGreaterThan(4); + verify.importModuleCompletionListContains("index"); + verify.importModuleCompletionListContains("ts"); + verify.importModuleCompletionListContains("dts"); + verify.importModuleCompletionListContains("tsx"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(4); goTo.marker(kind + "2"); - verify.completionListContains("fake-module"); - verify.completionListContains("fake-module-dev"); - verify.not.completionListItemsCountIsGreaterThan(2); + verify.importModuleCompletionListContains("fake-module"); + verify.importModuleCompletionListContains("fake-module-dev"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts index cb1f67f6349..393ffed0c52 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts @@ -26,8 +26,8 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListIsEmpty(); + verify.importModuleCompletionListIsEmpty(); goTo.marker(kind + "1"); - verify.completionListIsEmpty(); + verify.importModuleCompletionListIsEmpty(); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts index 11420fe4f37..08b92b05295 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts @@ -29,7 +29,7 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("repeated"); - verify.completionListContains("other"); - verify.not.completionListItemsCountIsGreaterThan(2); + verify.importModuleCompletionListContains("repeated"); + verify.importModuleCompletionListContains("other"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts index aec9f8411bc..9d9beac16e7 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts @@ -28,8 +28,8 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("ts"); - verify.completionListContains("tsx"); - verify.completionListContains("dts"); - verify.not.completionListItemsCountIsGreaterThan(3); + verify.importModuleCompletionListContains("ts"); + verify.importModuleCompletionListContains("tsx"); + verify.importModuleCompletionListContains("dts"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts index 5a96e4ee63e..28170754456 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts @@ -33,20 +33,20 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("fake-module/"); - verify.completionListContains("fake-module2"); - verify.completionListContains("fake-module3/"); - verify.not.completionListItemsCountIsGreaterThan(3); + verify.importModuleCompletionListContains("fake-module/"); + verify.importModuleCompletionListContains("fake-module2"); + verify.importModuleCompletionListContains("fake-module3/"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); goTo.marker(kind + "1"); - verify.completionListContains("ambient-module-test"); - verify.not.completionListItemsCountIsGreaterThan(1); + verify.importModuleCompletionListContains("ambient-module-test"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); goTo.marker(kind + "2"); - verify.completionListContains("fake-module/"); - verify.completionListContains("fake-module2"); - verify.completionListContains("fake-module3/"); - verify.not.completionListItemsCountIsGreaterThan(3); + verify.importModuleCompletionListContains("fake-module/"); + verify.importModuleCompletionListContains("fake-module2"); + verify.importModuleCompletionListContains("fake-module3/"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts index 2e172e3578e..9e0c756a919 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts @@ -24,14 +24,14 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("ambientModule"); - verify.completionListContains("otherAmbientModule"); - verify.completionListContains("otherOtherAmbientModule"); - verify.not.completionListItemsCountIsGreaterThan(3); + verify.importModuleCompletionListContains("ambientModule"); + verify.importModuleCompletionListContains("otherAmbientModule"); + verify.importModuleCompletionListContains("otherOtherAmbientModule"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); goTo.marker(kind + "1"); - verify.completionListContains("ambientModule"); - verify.not.completionListItemsCountIsGreaterThan(1); + verify.importModuleCompletionListContains("ambientModule"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts index bfd0539e149..263b57d9d29 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts @@ -52,11 +52,11 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("module-no-main/"); - verify.completionListContains("module-no-main-index-d-ts/"); - verify.completionListContains("module-index-ts"); - verify.completionListContains("module-index-d-ts-explicit-main"); - verify.completionListContains("module-index-d-ts-default-main"); - verify.completionListContains("module-typings"); - verify.not.completionListItemsCountIsGreaterThan(6); + verify.importModuleCompletionListContains("module-no-main/"); + verify.importModuleCompletionListContains("module-no-main-index-d-ts/"); + verify.importModuleCompletionListContains("module-index-ts"); + verify.importModuleCompletionListContains("module-index-d-ts-explicit-main"); + verify.importModuleCompletionListContains("module-index-d-ts-default-main"); + verify.importModuleCompletionListContains("module-typings"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(6); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts index 35e144c7184..d08ebd17b73 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts @@ -21,7 +21,7 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("module"); - verify.completionListContains("module-from-node"); - verify.not.completionListItemsCountIsGreaterThan(2); + verify.importModuleCompletionListContains("module"); + verify.importModuleCompletionListContains("module-from-node"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts index 0e8da4b02bb..b7e745f814e 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts @@ -43,11 +43,11 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("0test"); + verify.importModuleCompletionListContains("0test"); goTo.marker(kind + "1"); - verify.completionListContains("1test"); + verify.importModuleCompletionListContains("1test"); goTo.marker(kind + "2"); - verify.completionListContains("2test"); + verify.importModuleCompletionListContains("2test"); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts index 3c32ec2670d..31984df9081 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts @@ -28,7 +28,7 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("module1"); - verify.completionListContains("module2"); - verify.not.completionListItemsCountIsGreaterThan(2); + verify.importModuleCompletionListContains("module1"); + verify.importModuleCompletionListContains("module2"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts index edce0ce7e9f..1b77c57f5ca 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts @@ -26,8 +26,8 @@ const kinds = ["types_ref", "import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("module-x"); - verify.completionListContains("module-y"); - verify.completionListContains("module-z"); - verify.not.completionListItemsCountIsGreaterThan(3); + verify.importModuleCompletionListContains("module-x"); + verify.importModuleCompletionListContains("module-y"); + verify.importModuleCompletionListContains("module-z"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts index 541bb88c073..3e991b3d4b4 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts @@ -24,7 +24,7 @@ const kinds = ["types_ref", "import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("module-x"); - verify.completionListContains("module-z"); - verify.not.completionListItemsCountIsGreaterThan(2); + verify.importModuleCompletionListContains("module-x"); + verify.importModuleCompletionListContains("module-z"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts index 7a7d4e00df9..add1238cefc 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts @@ -20,7 +20,7 @@ const kinds = ["types_ref", "import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("module-x"); - verify.completionListContains("module-y"); - verify.not.completionListItemsCountIsGreaterThan(2); + verify.importModuleCompletionListContains("module-x"); + verify.importModuleCompletionListContains("module-y"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); } diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts index bb9d6a59c7b..f3607d70c28 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts @@ -52,33 +52,33 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListIsEmpty(); + verify.importModuleCompletionListIsEmpty(); goTo.marker(kind + "1"); - verify.completionListContains("f1"); - verify.completionListContains("f2"); - verify.completionListContains("e1"); - verify.completionListContains("test0"); - verify.completionListContains("folder/"); - verify.completionListContains("parentTest/"); - verify.not.completionListItemsCountIsGreaterThan(6); + verify.importModuleCompletionListContains("f1"); + verify.importModuleCompletionListContains("f2"); + verify.importModuleCompletionListContains("e1"); + verify.importModuleCompletionListContains("test0"); + verify.importModuleCompletionListContains("folder/"); + verify.importModuleCompletionListContains("parentTest/"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(6); goTo.marker(kind + "2"); - verify.completionListContains("f1"); - verify.completionListContains("f2"); - verify.completionListContains("folder/"); - verify.not.completionListItemsCountIsGreaterThan(3); + verify.importModuleCompletionListContains("f1"); + verify.importModuleCompletionListContains("f2"); + verify.importModuleCompletionListContains("folder/"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); goTo.marker(kind + "3"); - verify.completionListContains("f1"); - verify.completionListContains("h1"); - verify.not.completionListItemsCountIsGreaterThan(2); + verify.importModuleCompletionListContains("f1"); + verify.importModuleCompletionListContains("h1"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); goTo.marker(kind + "4"); - verify.completionListContains("h1"); - verify.not.completionListItemsCountIsGreaterThan(1); + verify.importModuleCompletionListContains("h1"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); goTo.marker(kind + "5"); - verify.completionListContains("g1"); - verify.not.completionListItemsCountIsGreaterThan(1); + verify.importModuleCompletionListContains("g1"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts index 4e879148469..eeb24d591f0 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts @@ -31,19 +31,19 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("f1"); - verify.completionListContains("f2"); - verify.completionListContains("f3"); - verify.completionListContains("f4"); - verify.completionListContains("e1"); - verify.completionListContains("e2"); - verify.completionListContains("test0"); - verify.not.completionListItemsCountIsGreaterThan(7); + verify.importModuleCompletionListContains("f1"); + verify.importModuleCompletionListContains("f2"); + verify.importModuleCompletionListContains("f3"); + verify.importModuleCompletionListContains("f4"); + verify.importModuleCompletionListContains("e1"); + verify.importModuleCompletionListContains("e2"); + verify.importModuleCompletionListContains("test0"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(7); goTo.marker(kind + "1"); - verify.completionListContains("f1"); - verify.completionListContains("f2"); - verify.completionListContains("f3"); - verify.completionListContains("f4"); - verify.not.completionListItemsCountIsGreaterThan(4); + verify.importModuleCompletionListContains("f1"); + verify.importModuleCompletionListContains("f2"); + verify.importModuleCompletionListContains("f3"); + verify.importModuleCompletionListContains("f4"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(4); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts index 426eb3c2318..4f35612c5c6 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts @@ -32,18 +32,18 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("fourslash/"); - verify.not.completionListItemsCountIsGreaterThan(1); + verify.importModuleCompletionListContains("fourslash/"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); goTo.marker(kind + "1"); - verify.completionListContains("fourslash/"); - verify.not.completionListItemsCountIsGreaterThan(1); + verify.importModuleCompletionListContains("fourslash/"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); goTo.marker(kind + "2"); - verify.completionListContains("f1"); - verify.completionListContains("f2"); - verify.completionListContains("e1"); - verify.completionListContains("folder/"); - verify.completionListContains("tests/"); - verify.not.completionListItemsCountIsGreaterThan(5); + verify.importModuleCompletionListContains("f1"); + verify.importModuleCompletionListContains("f2"); + verify.importModuleCompletionListContains("e1"); + verify.importModuleCompletionListContains("folder/"); + verify.importModuleCompletionListContains("tests/"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(5); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts index 1895fff8514..5f4d7bb7fc0 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts @@ -40,9 +40,9 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.completionListContains("module0"); - verify.completionListContains("module1"); - verify.completionListContains("module2"); - verify.completionListContains("more/"); - verify.not.completionListItemsCountIsGreaterThan(4); + verify.importModuleCompletionListContains("module0"); + verify.importModuleCompletionListContains("module1"); + verify.importModuleCompletionListContains("module2"); + verify.importModuleCompletionListContains("more/"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(4); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForTripleSlashReference1.ts b/tests/cases/fourslash/completionForTripleSlashReference1.ts index 96582be1730..284d5ff66a4 100644 --- a/tests/cases/fourslash/completionForTripleSlashReference1.ts +++ b/tests/cases/fourslash/completionForTripleSlashReference1.ts @@ -42,38 +42,38 @@ //// /*parentg1*/ goTo.marker("0"); -verify.completionListIsEmpty(); +verify.importModuleCompletionListIsEmpty(); goTo.marker("1"); -verify.completionListContains("f1.ts"); -verify.completionListContains("f1.d.ts"); -verify.completionListContains("f2.tsx"); -verify.completionListContains("e1.ts"); -verify.completionListContains("test0.ts"); -verify.completionListContains("test1.ts"); -verify.completionListContains("test2.ts"); -verify.completionListContains("test3.ts"); -verify.completionListContains("test4.ts"); -verify.completionListContains("folder/"); -verify.completionListContains("parentTest/"); -verify.not.completionListItemsCountIsGreaterThan(11); +verify.importModuleCompletionListContains("f1.ts"); +verify.importModuleCompletionListContains("f1.d.ts"); +verify.importModuleCompletionListContains("f2.tsx"); +verify.importModuleCompletionListContains("e1.ts"); +verify.importModuleCompletionListContains("test0.ts"); +verify.importModuleCompletionListContains("test1.ts"); +verify.importModuleCompletionListContains("test2.ts"); +verify.importModuleCompletionListContains("test3.ts"); +verify.importModuleCompletionListContains("test4.ts"); +verify.importModuleCompletionListContains("folder/"); +verify.importModuleCompletionListContains("parentTest/"); +verify.not.importModuleCompletionListItemsCountIsGreaterThan(11); goTo.marker("2"); -verify.completionListContains("f1.ts"); -verify.completionListContains("f1.d.ts"); -verify.completionListContains("f2.tsx"); -verify.completionListContains("folder/"); -verify.not.completionListItemsCountIsGreaterThan(4); +verify.importModuleCompletionListContains("f1.ts"); +verify.importModuleCompletionListContains("f1.d.ts"); +verify.importModuleCompletionListContains("f2.tsx"); +verify.importModuleCompletionListContains("folder/"); +verify.not.importModuleCompletionListItemsCountIsGreaterThan(4); goTo.marker("3"); -verify.completionListContains("f1.ts"); -verify.completionListContains("h1.ts"); -verify.not.completionListItemsCountIsGreaterThan(2); +verify.importModuleCompletionListContains("f1.ts"); +verify.importModuleCompletionListContains("h1.ts"); +verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); goTo.marker("4"); -verify.completionListContains("h1.ts"); -verify.not.completionListItemsCountIsGreaterThan(1); +verify.importModuleCompletionListContains("h1.ts"); +verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); goTo.marker("5"); -verify.completionListContains("g1.ts"); -verify.not.completionListItemsCountIsGreaterThan(1); \ No newline at end of file +verify.importModuleCompletionListContains("g1.ts"); +verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); \ No newline at end of file diff --git a/tests/cases/fourslash/completionForTripleSlashReference2.ts b/tests/cases/fourslash/completionForTripleSlashReference2.ts index 69a9e98810a..8296b851ac2 100644 --- a/tests/cases/fourslash/completionForTripleSlashReference2.ts +++ b/tests/cases/fourslash/completionForTripleSlashReference2.ts @@ -25,23 +25,23 @@ //// /*e2*/ goTo.marker("0"); -verify.completionListContains("f1.ts"); -verify.completionListContains("f1.js"); -verify.completionListContains("f1.d.ts"); -verify.completionListContains("f2.tsx"); -verify.completionListContains("f3.js"); -verify.completionListContains("f4.jsx"); -verify.completionListContains("e1.ts"); -verify.completionListContains("e2.js"); -verify.completionListContains("test0.ts"); -verify.completionListContains("test1.ts"); -verify.not.completionListItemsCountIsGreaterThan(10); +verify.importModuleCompletionListContains("f1.ts"); +verify.importModuleCompletionListContains("f1.js"); +verify.importModuleCompletionListContains("f1.d.ts"); +verify.importModuleCompletionListContains("f2.tsx"); +verify.importModuleCompletionListContains("f3.js"); +verify.importModuleCompletionListContains("f4.jsx"); +verify.importModuleCompletionListContains("e1.ts"); +verify.importModuleCompletionListContains("e2.js"); +verify.importModuleCompletionListContains("test0.ts"); +verify.importModuleCompletionListContains("test1.ts"); +verify.not.importModuleCompletionListItemsCountIsGreaterThan(10); goTo.marker("1"); -verify.completionListContains("f1.ts"); -verify.completionListContains("f1.js"); -verify.completionListContains("f1.d.ts"); -verify.completionListContains("f2.tsx"); -verify.completionListContains("f3.js"); -verify.completionListContains("f4.jsx"); -verify.not.completionListItemsCountIsGreaterThan(6); \ No newline at end of file +verify.importModuleCompletionListContains("f1.ts"); +verify.importModuleCompletionListContains("f1.js"); +verify.importModuleCompletionListContains("f1.d.ts"); +verify.importModuleCompletionListContains("f2.tsx"); +verify.importModuleCompletionListContains("f3.js"); +verify.importModuleCompletionListContains("f4.jsx"); +verify.not.importModuleCompletionListItemsCountIsGreaterThan(6); \ No newline at end of file diff --git a/tests/cases/fourslash/completionForTripleSlashReference3.ts b/tests/cases/fourslash/completionForTripleSlashReference3.ts index e7aca7399f0..dda6ae92b41 100644 --- a/tests/cases/fourslash/completionForTripleSlashReference3.ts +++ b/tests/cases/fourslash/completionForTripleSlashReference3.ts @@ -25,17 +25,17 @@ //// /*e2*/ goTo.marker("0"); -verify.completionListContains("fourslash/"); -verify.not.completionListItemsCountIsGreaterThan(1); +verify.importModuleCompletionListContains("fourslash/"); +verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); goTo.marker("1"); -verify.completionListContains("fourslash/"); -verify.not.completionListItemsCountIsGreaterThan(1); +verify.importModuleCompletionListContains("fourslash/"); +verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); goTo.marker("2"); -verify.completionListContains("f1.ts"); -verify.completionListContains("f2.tsx"); -verify.completionListContains("e1.ts"); -verify.completionListContains("folder/"); -verify.completionListContains("tests/"); -verify.not.completionListItemsCountIsGreaterThan(5); \ No newline at end of file +verify.importModuleCompletionListContains("f1.ts"); +verify.importModuleCompletionListContains("f2.tsx"); +verify.importModuleCompletionListContains("e1.ts"); +verify.importModuleCompletionListContains("folder/"); +verify.importModuleCompletionListContains("tests/"); +verify.not.importModuleCompletionListItemsCountIsGreaterThan(5); \ No newline at end of file diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 9ce3197a46f..e011dde4b68 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -125,6 +125,9 @@ declare namespace FourSlashInterface { completionListItemsCountIsGreaterThan(count: number): void; completionListIsEmpty(): void; completionListAllowsNewIdentifier(): void; + importModuleCompletionListContains(symbol: string): void; + importModuleCompletionListItemsCountIsGreaterThan(count: number): void; + importModuleCompletionListIsEmpty(): void; memberListIsEmpty(): void; signatureHelpPresent(): void; errorExistsBetweenMarkers(startMarker: string, endMarker: string): void; From 98a162be2a510a7771e7998f63370252c799741f Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Mon, 1 Aug 2016 16:58:33 -0700 Subject: [PATCH 15/73] Replacement spans for import completions --- src/harness/fourslash.ts | 24 ++++++++++--- src/services/services.ts | 34 +++++++++++++------ .../completionForStringLiteralImport1.ts | 19 +++++++++++ .../completionForStringLiteralImport2.ts | 28 +++++++++++++++ tests/cases/fourslash/fourslash.ts | 2 +- 5 files changed, 92 insertions(+), 15 deletions(-) create mode 100644 tests/cases/fourslash/completionForStringLiteralImport1.ts create mode 100644 tests/cases/fourslash/completionForStringLiteralImport2.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 8b0968664e0..9ad5f25e005 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -766,13 +766,29 @@ namespace FourSlash { } } - public verifyImportModuleCompletionListContains(symbol: string) { + public verifyImportModuleCompletionListContains(symbol: string, rangeIndex?: number) { const completions = this.getImportModuleCompletionListAtCaret(); if (completions) { - if (!ts.forEach(completions, completion => completion.name === symbol)) { + const completion = ts.forEach(completions, completion => completion.name === symbol ? completion : undefined); + if (!completion) { const itemsString = completions.map(item => stringify({ name: item.name, span: item.span })).join(",\n"); this.raiseError(`Expected "${symbol}" to be in list [${itemsString}]`); } + else if (rangeIndex !== undefined) { + const ranges = this.getRanges(); + if (ranges && ranges.length > rangeIndex) { + const range = ranges[rangeIndex]; + + const start = completion.span.start; + const end = start + completion.span.length; + if (range.start !== start || range.end !== end) { + this.raiseError(`Expected completion span for '${symbol}', ${stringify(completion.span)}, to cover range ${stringify(range)}`); + } + } + else { + this.raiseError(`Expected completion span for '${symbol}' to cover range at index ${rangeIndex}, but no range was found at that index`); + } + } } else { this.raiseError(`No import module completions at position '${this.currentCaretPosition}' when looking for '${symbol}'.`); @@ -2943,12 +2959,12 @@ namespace FourSlashInterface { this.state.verifyCompletionListItemsCountIsGreaterThan(count, this.negative); } - public importModuleCompletionListContains(symbol: string): void { + public importModuleCompletionListContains(symbol: string, rangeIndex?: number): void { if (this.negative) { this.state.verifyImportModuleCompletionListDoesNotContain(symbol); } else { - this.state.verifyImportModuleCompletionListContains(symbol); + this.state.verifyImportModuleCompletionListContains(symbol, rangeIndex); } } diff --git a/src/services/services.ts b/src/services/services.ts index 11eec28a5f6..61d624c29df 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2069,7 +2069,7 @@ namespace ts { * for completions. * For example, this matches /// position >= commentRange.pos && position <= commentRange.end && commentRange); + + if (!range) { + return undefined; + } + + const text = sourceFile.text.substr(range.pos, position - range.pos); - const text = sourceFile.text.substr(node.pos, position); const match = tripleSlashDirectiveFragmentRegex.exec(text); if (match) { - const kind= match[1]; - const fragment = match[2]; + const prefix = match[1]; + const kind = match[2]; + const toComplete = match[3]; + + const span: TextSpan = { start: range.pos + prefix.length, length: match[0].length - prefix.length }; + const scriptPath = getDirectoryPath(sourceFile.path); if (kind === "path") { // Give completions for a relative path - return getCompletionEntriesForDirectoryFragment(fragment, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, span); + return getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, span); } else { // Give completions based on the typings available diff --git a/tests/cases/fourslash/completionForStringLiteralImport1.ts b/tests/cases/fourslash/completionForStringLiteralImport1.ts new file mode 100644 index 00000000000..42c2dbaf3d4 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralImport1.ts @@ -0,0 +1,19 @@ +/// + +// @Filename: test.ts +//// import * as foo0 from "[|./some|]/*0*/ +//// import foo1 = require( "[|./some|]/*1*/ +//// var foo2 = require( "[|./some|]/*2*/ + +//// import * as foo3 from "[|./some|]/*3*/"; +//// import foo4 = require( "[|./some|]/*4*/"; +//// var foo5 = require( "[|./some|]/*5*/"; + + +// @Filename: someFile.ts +//// /*someFile*/ + +for (let i = 0; i < 6; i++) { + goTo.marker("" + i); + verify.importModuleCompletionListContains("someFile", i); +} \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralImport2.ts b/tests/cases/fourslash/completionForStringLiteralImport2.ts new file mode 100644 index 00000000000..a0618c344a7 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralImport2.ts @@ -0,0 +1,28 @@ +/// + +// @typeRoots: my_typings + +// @Filename: test.ts +//// /// +//// /// + +// @Filename: someFile.ts +//// /*someFile*/ + +// @Filename: my_typings/some-module/index.d.ts +//// export var x = 9; + +goTo.marker("0"); +verify.importModuleCompletionListContains("someFile.ts", 0); + +goTo.marker("1"); +verify.importModuleCompletionListContains("some-module", 1); + +goTo.marker("2"); +verify.importModuleCompletionListContains("someFile.ts", 2); + +goTo.marker("3"); +verify.importModuleCompletionListContains("some-module", 3); \ No newline at end of file diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index e011dde4b68..948477b78dd 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -125,7 +125,7 @@ declare namespace FourSlashInterface { completionListItemsCountIsGreaterThan(count: number): void; completionListIsEmpty(): void; completionListAllowsNewIdentifier(): void; - importModuleCompletionListContains(symbol: string): void; + importModuleCompletionListContains(symbol: string, rangeIndex?: number): void; importModuleCompletionListItemsCountIsGreaterThan(count: number): void; importModuleCompletionListIsEmpty(): void; memberListIsEmpty(): void; From 35cd480a9c81b1db735587e3dcab3d12d3879f41 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Mon, 1 Aug 2016 17:49:13 -0700 Subject: [PATCH 16/73] Fixing import completion spans to only include the end of the directory fragment --- src/services/services.ts | 15 ++++++-- .../completionForStringLiteralImport1.ts | 38 ++++++++++++------- .../completionForStringLiteralImport2.ts | 9 +++-- 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 61d624c29df..66b894bb428 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4518,7 +4518,7 @@ namespace ts { let result: ImportCompletionEntry[]; // Replace the entire text of the string literal (excluding the quotes) - const span: TextSpan = { start: node.getStart() + 1, length: literalValue.length }; + const span: TextSpan = getDirectoryFragmentTextSpan(literalValue, node.getStart() + 1); const isRelativePath = startsWith(literalValue, "."); const scriptDir = getDirectoryPath(node.getSourceFile().path); @@ -4792,15 +4792,16 @@ namespace ts { const kind = match[2]; const toComplete = match[3]; - const span: TextSpan = { start: range.pos + prefix.length, length: match[0].length - prefix.length }; - const scriptPath = getDirectoryPath(sourceFile.path); if (kind === "path") { // Give completions for a relative path + const span: TextSpan = getDirectoryFragmentTextSpan(toComplete, range.pos + prefix.length); return getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, span); } else { // Give completions based on the typings available + // Replace the entire string + const span: TextSpan = { start: range.pos + prefix.length, length: match[0].length - prefix.length }; return getCompletionEntriesFromTypings(host, program.getCompilerOptions(), scriptPath, span); } } @@ -4952,6 +4953,14 @@ namespace ts { function createCompletionEntryForModule(name: string, kind: string, span: TextSpan): ImportCompletionEntry { return { name, kind, sortText: name, span }; } + + // Replace everything after the last directory seperator that appears + // FIXME: do we care about the other seperator? + function getDirectoryFragmentTextSpan(text: string, textStart: number): TextSpan { + const index = text.lastIndexOf(directorySeparator); + const offset = index !== -1 ? index + 1 : 0; + return { start: textStart + offset, length: text.length - offset } + } } function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails { diff --git a/tests/cases/fourslash/completionForStringLiteralImport1.ts b/tests/cases/fourslash/completionForStringLiteralImport1.ts index 42c2dbaf3d4..239c3be42fc 100644 --- a/tests/cases/fourslash/completionForStringLiteralImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralImport1.ts @@ -1,19 +1,31 @@ /// +// @typeRoots: my_typings + // @Filename: test.ts -//// import * as foo0 from "[|./some|]/*0*/ -//// import foo1 = require( "[|./some|]/*1*/ -//// var foo2 = require( "[|./some|]/*2*/ - -//// import * as foo3 from "[|./some|]/*3*/"; -//// import foo4 = require( "[|./some|]/*4*/"; -//// var foo5 = require( "[|./some|]/*5*/"; +//// import * as foo0 from "./[|some|]/*0*/ +//// import * as foo1 from "./sub/[|some|]/*1*/ +//// import * as foo2 from "[|some-|]/*2*/" +//// import * as foo3 from "../[||]/*3*/"; -// @Filename: someFile.ts -//// /*someFile*/ +// @Filename: someFile1.ts +//// /*someFile1*/ -for (let i = 0; i < 6; i++) { - goTo.marker("" + i); - verify.importModuleCompletionListContains("someFile", i); -} \ No newline at end of file +// @Filename: sub/someFile2.ts +//// /*someFile2*/ + +// @Filename: my_typings/some-module/index.d.ts +//// export var x = 9; + +goTo.marker("0"); +verify.importModuleCompletionListContains("someFile1", 0); + +goTo.marker("1"); +verify.importModuleCompletionListContains("someFile2", 1); + +goTo.marker("2"); +verify.importModuleCompletionListContains("some-module", 2); + +goTo.marker("3"); +verify.importModuleCompletionListContains("fourslash/", 3); \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralImport2.ts b/tests/cases/fourslash/completionForStringLiteralImport2.ts index a0618c344a7..2910d9e1475 100644 --- a/tests/cases/fourslash/completionForStringLiteralImport2.ts +++ b/tests/cases/fourslash/completionForStringLiteralImport2.ts @@ -3,15 +3,18 @@ // @typeRoots: my_typings // @Filename: test.ts -//// /// +//// /// //// /// // @Filename: someFile.ts //// /*someFile*/ +// @Filename: sub/someOtherFile.ts +//// /*someOtherFile*/ + // @Filename: my_typings/some-module/index.d.ts //// export var x = 9; @@ -22,7 +25,7 @@ goTo.marker("1"); verify.importModuleCompletionListContains("some-module", 1); goTo.marker("2"); -verify.importModuleCompletionListContains("someFile.ts", 2); +verify.importModuleCompletionListContains("someOtherFile.ts", 2); goTo.marker("3"); verify.importModuleCompletionListContains("some-module", 3); \ No newline at end of file From a5d73bfc2430091176de535af86a90ce677293a4 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Tue, 2 Aug 2016 15:55:30 -0700 Subject: [PATCH 17/73] No more filtering results --- src/services/services.ts | 122 ++++++++++-------- ...etionForStringLiteralNonrelativeImport4.ts | 22 +--- ...mpletionForStringLiteralRelativeImport1.ts | 44 ++----- ...mpletionForStringLiteralRelativeImport2.ts | 7 +- ...mpletionForStringLiteralRelativeImport4.ts | 2 + .../completionForTripleSlashReference1.ts | 78 ++++------- .../completionForTripleSlashReference2.ts | 49 +++---- 7 files changed, 127 insertions(+), 197 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 66b894bb428..e8ff3847804 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2069,7 +2069,7 @@ namespace ts { * for completions. * For example, this matches /// combinePaths(rootDirectory, relativeDirectory))); } - function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan): ImportCompletionEntry[] { + function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, exclude?: string): ImportCompletionEntry[] { const basePath = program.getCompilerOptions().project || host.getCurrentDirectory(); + const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames()); + const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptPath, ignoreCase); - const baseDirectories = getBaseDirectoriesFromRootDirs( - rootDirs, basePath, scriptPath, host.useCaseSensitiveFileNames && !host.useCaseSensitiveFileNames()); const result: ImportCompletionEntry[] = []; for (const baseDirectory of baseDirectories) { - getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, span, result); + getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, span, exclude, result); } return result; } - function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, result: ImportCompletionEntry[] = []): ImportCompletionEntry[] { - const toComplete = getBaseFileName(fragment); - const absolutePath = normalizeSlashes(host.resolvePath(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment))); - const baseDirectory = toComplete ? getDirectoryPath(absolutePath) : absolutePath; + function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, exclude?: string, result: ImportCompletionEntry[] = []): ImportCompletionEntry[] { + fragment = getDirectoryPath(fragment); + if (!fragment) { + fragment = "./" + } + else { + fragment = ensureTrailingDirectorySeparator(fragment); + } + + const absolutePath = normalizeAndPreserveTrailingSlash(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment)); + const baseDirectory = host.resolvePath(getDirectoryPath(absolutePath)); + const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames()); if (directoryProbablyExists(baseDirectory, host)) { // Enumerate the available files const files = host.readDirectory(baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]); - forEach(files, f => { - const fileName = includeExtensions ? getBaseFileName(f) : removeFileExtension(getBaseFileName(f)); + forEach(files, filePath => { + if (exclude && comparePaths(filePath, exclude, scriptPath, ignoreCase) === Comparison.EqualTo) { + return false; + } - const duplicate = includeExtensions ? false : forEach(result, entry => entry.name === fileName); + const fileName = includeExtensions ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath)); + const duplicate = !includeExtensions && forEach(result, entry => entry.name === fileName); - if (startsWith(fileName, toComplete) && !duplicate) { + if (!duplicate) { result.push({ name: fileName, kind: ScriptElementKind.directory, @@ -4605,14 +4612,12 @@ namespace ts { forEach(directories, d => { const directoryName = getBaseFileName(removeTrailingDirectorySeparator(d)); - if (startsWith(directoryName, toComplete)) { - result.push({ - name: ensureTrailingDirectorySeparator(directoryName), - kind: ScriptElementKind.directory, - sortText: directoryName, - span - }); - } + result.push({ + name: ensureTrailingDirectorySeparator(directoryName), + kind: ScriptElementKind.directory, + sortText: directoryName, + span + }); }); } } @@ -4679,18 +4684,17 @@ namespace ts { if (parsed) { // The prefix has two effective parts: the directory path and the base component after the filepath that is not a // full directory component. For example: directory/path/of/prefix/base* - const normalizedPrefix = hasTrailingDirectorySeparator(parsed.prefix) ? - ensureTrailingDirectorySeparator(normalizePath(parsed.prefix)) : normalizePath(parsed.prefix); + const normalizedPrefix = normalizeAndPreserveTrailingSlash(parsed.prefix); const normalizedPrefixDirectory = getDirectoryPath(normalizedPrefix); const normalizedPrefixBase = getBaseFileName(normalizedPrefix); const fragmentHasPath = fragment.indexOf(directorySeparator) !== -1; // Try and expand the prefix to include any path from the fragment so that we can limit the readDirectory call - const expandedPrefixDir = fragmentHasPath ? combinePaths(normalizedPrefixDirectory, normalizedPrefixBase + getDirectoryPath(fragment)) : normalizedPrefixDirectory; + const expandedPrefixDirectory = fragmentHasPath ? combinePaths(normalizedPrefixDirectory, normalizedPrefixBase + getDirectoryPath(fragment)) : normalizedPrefixDirectory; const normalizedSuffix = normalizePath(parsed.suffix); - const baseDirectory = combinePaths(baseUrl, expandedPrefixDir); + const baseDirectory = combinePaths(baseUrl, expandedPrefixDirectory); const completePrefix = fragmentHasPath ? baseDirectory : ensureTrailingDirectorySeparator(baseDirectory) + normalizedPrefixBase; // If we have a suffix, then we need to read the directory all the way down. We could create a glob @@ -4720,17 +4724,8 @@ namespace ts { } function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string, options: CompilerOptions): string[] { - const trailingSeperator = hasTrailingDirectorySeparator(fragment); - fragment = normalizePath(fragment); - - if (trailingSeperator) { - fragment = ensureTrailingDirectorySeparator(fragment); - } - - // If this is a nested module, get the module name - const firstSeparator = fragment.indexOf(directorySeparator); - const moduleNameFragment = firstSeparator !== -1 ? fragment.substr(0, firstSeparator) : fragment; - const isNestedModule = fragment !== moduleNameFragment; + // Check If this is a nested module + const isNestedModule = fragment.indexOf(directorySeparator) !== -1 ; // Get modules that the type checker picked up const ambientModules = ts.map(program.getTypeChecker().getAmbientModules(), sym => stripQuotes(sym.name)); @@ -4749,7 +4744,7 @@ namespace ts { }); if (!options.moduleResolution || options.moduleResolution === ModuleResolutionKind.NodeJs) { - forEach(enumerateNodeModulesVisibleToScript(host, scriptPath, moduleNameFragment), visibleModule => { + forEach(enumerateNodeModulesVisibleToScript(host, scriptPath), visibleModule => { if (!isNestedModule) { nonRelativeModules.push(visibleModule.canBeImported ? visibleModule.moduleName : ensureTrailingDirectorySeparator(visibleModule.moduleName)); } @@ -4796,7 +4791,7 @@ namespace ts { if (kind === "path") { // Give completions for a relative path const span: TextSpan = getDirectoryFragmentTextSpan(toComplete, range.pos + prefix.length); - return getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, span); + return getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, span, sourceFile.path); } else { // Give completions based on the typings available @@ -4876,7 +4871,7 @@ namespace ts { } - function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string, modulePrefix?: string) { + function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string) { const result: VisibleModuleInfo[] = []; findPackageJsons(scriptPath).forEach((packageJson) => { const package = tryReadingPackageJson(packageJson); @@ -4888,10 +4883,10 @@ namespace ts { const foundModuleNames: string[] = []; if (package.dependencies) { - addPotentialPackageNames(package.dependencies, modulePrefix, foundModuleNames); + addPotentialPackageNames(package.dependencies, foundModuleNames); } if (package.devDependencies) { - addPotentialPackageNames(package.devDependencies, modulePrefix, foundModuleNames); + addPotentialPackageNames(package.devDependencies, foundModuleNames); } forEach(foundModuleNames, (moduleName) => { @@ -4916,9 +4911,10 @@ namespace ts { } } - function addPotentialPackageNames(dependencies: any, prefix: string, result: string[]) { + function addPotentialPackageNames(dependencies: any, result: string[]) { for (const dep in dependencies) { - if (dependencies.hasOwnProperty(dep) && (!prefix || startsWith(dep, prefix))) { + if (dependencies.hasOwnProperty(dep) && !startsWith(dep, "@types") && + dep.charCodeAt(6) !== CharacterCodes.slash && dep.charCodeAt(6) !== CharacterCodes.backslash) { result.push(dep); } } @@ -4961,6 +4957,20 @@ namespace ts { const offset = index !== -1 ? index + 1 : 0; return { start: textStart + offset, length: text.length - offset } } + + // Returns true if the path is explicitly relative to the script (i.e. relative to . or ..) + function isPathRelativeToScript(path: string) { + if (path && path.length >= 2 && path.charCodeAt(0) === CharacterCodes.dot) { + const slashIndex = path.length >= 3 && path.charCodeAt(1) === CharacterCodes.dot ? 2 : 1; + const slashCharCode = path.charCodeAt(slashIndex); + return slashCharCode === CharacterCodes.slash || slashCharCode === CharacterCodes.backslash; + } + return false; + } + + function normalizeAndPreserveTrailingSlash(path: string) { + return hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(normalizePath(path)) : normalizePath(path); + } } function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails { diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts index 28170754456..f4a9ae2d537 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts @@ -2,16 +2,8 @@ // @Filename: dir1/dir2/dir3/dir4/test0.ts //// import * as foo1 from "f/*import_as0*/ -//// import * as foo2 from "a/*import_as1*/ -//// import * as foo3 from "fake-module/*import_as2*/ - //// import foo4 = require("f/*import_equals0*/ -//// import foo5 = require("a/*import_equals1*/ -//// import foo6 = require("fake-module/*import_equals2*/ - //// var foo7 = require("f/*require0*/ -//// var foo8 = require("a/*require1*/ -//// var foo9 = require("fake-module/*require2*/ // @Filename: package.json //// { "dependencies": { "fake-module": "latest" } } @@ -21,7 +13,7 @@ // @Filename: dir1/package.json //// { "dependencies": { "fake-module2": "latest" } } // @Filename: dir1/node_modules/fake-module2/index.ts -//// declare module "ambient-module-test" {} +//// /*module2*/ // @Filename: dir1/dir2/dir3/package.json //// { "dependencies": { "fake-module3": "latest" } } @@ -37,16 +29,4 @@ for (const kind of kinds) { verify.importModuleCompletionListContains("fake-module2"); verify.importModuleCompletionListContains("fake-module3/"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); - - goTo.marker(kind + "1"); - - verify.importModuleCompletionListContains("ambient-module-test"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); - - goTo.marker(kind + "2"); - - verify.importModuleCompletionListContains("fake-module/"); - verify.importModuleCompletionListContains("fake-module2"); - verify.importModuleCompletionListContains("fake-module3/"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts index f3607d70c28..52d62f3b4f8 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts @@ -3,28 +3,21 @@ // @Filename: test0.ts //// import * as foo1 from "./*import_as0*/ //// import * as foo2 from ".//*import_as1*/ -//// import * as foo3 from "./f/*import_as2*/ -//// import * as foo4 from "./folder//*import_as3*/ -//// import * as foo5 from "./folder/h/*import_as4*/ +//// import * as foo4 from "./folder//*import_as2*/ //// import foo6 = require("./*import_equals0*/ //// import foo7 = require(".//*import_equals1*/ -//// import foo8 = require("./f/*import_equals2*/ -//// import foo9 = require("./folder//*import_equals3*/ -//// import foo10 = require("./folder/h/*import_equals4*/ +//// import foo9 = require("./folder//*import_equals2*/ //// var foo11 = require("./*require0*/ //// var foo12 = require(".//*require1*/ -//// var foo13 = require("./f/*require2*/ -//// var foo14 = require("./folder//*require3*/ -//// var foo15 = require("./folder/h/*require4*/ +//// var foo14 = require("./folder//*require2*/ // @Filename: parentTest/sub/test5.ts -//// import * as foo16 from "../g/*import_as5*/ +//// import * as foo16 from "../g/*import_as3*/ +//// import foo17 = require("../g/*import_equals3*/ +//// var foo18 = require("../g/*require3*/ -//// import foo17 = require("../g/*import_equals5*/ - -//// var foo18 = require("../g/*require5*/ // @Filename: f1.ts //// /*f1*/ @@ -40,11 +33,11 @@ //// /*f4*/ // @Filename: e1.ts //// /*e1*/ -// @Filename: folder/f1.ts +// @Filename: folder/f3.ts //// /*subf1*/ // @Filename: folder/h1.ts //// /*subh1*/ -// @Filename: parentTest/f1.ts +// @Filename: parentTest/f4.ts //// /*parentf1*/ // @Filename: parentTest/g1.ts //// /*parentg1*/ @@ -58,27 +51,18 @@ for (const kind of kinds) { verify.importModuleCompletionListContains("f1"); verify.importModuleCompletionListContains("f2"); verify.importModuleCompletionListContains("e1"); - verify.importModuleCompletionListContains("test0"); verify.importModuleCompletionListContains("folder/"); verify.importModuleCompletionListContains("parentTest/"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(6); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(5); goTo.marker(kind + "2"); - verify.importModuleCompletionListContains("f1"); - verify.importModuleCompletionListContains("f2"); - verify.importModuleCompletionListContains("folder/"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); - - goTo.marker(kind + "3"); - verify.importModuleCompletionListContains("f1"); + verify.importModuleCompletionListContains("f3"); verify.importModuleCompletionListContains("h1"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); - goTo.marker(kind + "4"); - verify.importModuleCompletionListContains("h1"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); - - goTo.marker(kind + "5"); + goTo.marker(kind + "3"); + verify.importModuleCompletionListContains("f4"); verify.importModuleCompletionListContains("g1"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); + verify.importModuleCompletionListContains("sub/"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts index eeb24d591f0..72b28d39b87 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts @@ -37,13 +37,14 @@ for (const kind of kinds) { verify.importModuleCompletionListContains("f4"); verify.importModuleCompletionListContains("e1"); verify.importModuleCompletionListContains("e2"); - verify.importModuleCompletionListContains("test0"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(7); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(6); goTo.marker(kind + "1"); verify.importModuleCompletionListContains("f1"); verify.importModuleCompletionListContains("f2"); verify.importModuleCompletionListContains("f3"); verify.importModuleCompletionListContains("f4"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(4); + verify.importModuleCompletionListContains("e1"); + verify.importModuleCompletionListContains("e2"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(6); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts index 5f4d7bb7fc0..773c4d90d75 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts @@ -44,5 +44,7 @@ for (const kind of kinds) { verify.importModuleCompletionListContains("module1"); verify.importModuleCompletionListContains("module2"); verify.importModuleCompletionListContains("more/"); + + // Should not contain itself verify.not.importModuleCompletionListItemsCountIsGreaterThan(4); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForTripleSlashReference1.ts b/tests/cases/fourslash/completionForTripleSlashReference1.ts index 284d5ff66a4..8c6188ac90c 100644 --- a/tests/cases/fourslash/completionForTripleSlashReference1.ts +++ b/tests/cases/fourslash/completionForTripleSlashReference1.ts @@ -1,22 +1,15 @@ /// // @Filename: test0.ts -//// /// +//// /// - -// @Filename: test3.ts -//// /// // @Filename: f1.ts -//// /f1*/ +//// /*f1*/ // @Filename: f1.js //// /*f1j*/ // @Filename: f1.d.ts //// /*f1d*/ // @Filename: f2.tsx -//// /*f2*/ -// @Filename: f3.js -//// /*f3*/ +//// /f2*/ // @Filename: f4.jsx //// /*f4*/ -// @Filename: e1.ts -//// /*e1*/ -// @Filename: e2.js -//// /*e2*/ -goTo.marker("0"); -verify.importModuleCompletionListContains("f1.ts"); -verify.importModuleCompletionListContains("f1.js"); -verify.importModuleCompletionListContains("f1.d.ts"); -verify.importModuleCompletionListContains("f2.tsx"); -verify.importModuleCompletionListContains("f3.js"); -verify.importModuleCompletionListContains("f4.jsx"); -verify.importModuleCompletionListContains("e1.ts"); -verify.importModuleCompletionListContains("e2.js"); -verify.importModuleCompletionListContains("test0.ts"); -verify.importModuleCompletionListContains("test1.ts"); -verify.not.importModuleCompletionListItemsCountIsGreaterThan(10); - -goTo.marker("1"); -verify.importModuleCompletionListContains("f1.ts"); -verify.importModuleCompletionListContains("f1.js"); -verify.importModuleCompletionListContains("f1.d.ts"); -verify.importModuleCompletionListContains("f2.tsx"); -verify.importModuleCompletionListContains("f3.js"); -verify.importModuleCompletionListContains("f4.jsx"); -verify.not.importModuleCompletionListItemsCountIsGreaterThan(6); \ No newline at end of file +for (let i = 0; i < 5; i++) { + goTo.marker("" + i); + verify.importModuleCompletionListContains("f1.ts"); + verify.importModuleCompletionListContains("f1.js"); + verify.importModuleCompletionListContains("f1.d.ts"); + verify.importModuleCompletionListContains("f2.tsx"); + verify.importModuleCompletionListContains("f4.jsx"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(5); +} \ No newline at end of file From 8b5a3d9fd7142a18b2f25c589622a70476306ae9 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Tue, 2 Aug 2016 19:03:36 -0700 Subject: [PATCH 18/73] Refactoring API to remove duplicate spans --- src/harness/fourslash.ts | 24 ++++---- src/harness/harnessLanguageService.ts | 2 +- src/server/client.ts | 10 +++- src/server/protocol.d.ts | 3 +- src/services/services.ts | 84 +++++++++++++++------------ src/services/shims.ts | 2 +- 6 files changed, 70 insertions(+), 55 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 9ad5f25e005..532dccd4f6b 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -599,7 +599,7 @@ namespace FourSlash { public verifyImportModuleCompletionListItemsCountIsGreaterThan(count: number, negative: boolean) { const completions = this.getImportModuleCompletionListAtCaret(); - const itemsCount = completions.length; + const itemsCount = completions.entries.length; if (negative) { if (itemsCount > count) { @@ -615,13 +615,13 @@ namespace FourSlash { public verifyImportModuleCompletionListIsEmpty(negative: boolean) { const completions = this.getImportModuleCompletionListAtCaret(); - if ((!completions || completions.length === 0) && negative) { + if ((!completions || completions.entries.length === 0) && negative) { this.raiseError("Completion list is empty at caret at position " + this.activeFile.fileName + " " + this.currentCaretPosition); } - else if (completions && completions.length !== 0 && !negative) { - let errorMsg = "\n" + "Completion List contains: [" + completions[0].name; - for (let i = 1; i < completions.length; i++) { - errorMsg += ", " + completions[i].name; + else if (completions && completions.entries.length !== 0 && !negative) { + let errorMsg = "\n" + "Completion List contains: [" + completions.entries[0].name; + for (let i = 1; i < completions.entries.length; i++) { + errorMsg += ", " + completions.entries[i].name; } errorMsg += "]\n"; @@ -769,9 +769,9 @@ namespace FourSlash { public verifyImportModuleCompletionListContains(symbol: string, rangeIndex?: number) { const completions = this.getImportModuleCompletionListAtCaret(); if (completions) { - const completion = ts.forEach(completions, completion => completion.name === symbol ? completion : undefined); + const completion = ts.forEach(completions.entries, completion => completion.name === symbol ? completion : undefined); if (!completion) { - const itemsString = completions.map(item => stringify({ name: item.name, span: item.span })).join(",\n"); + const itemsString = completions.entries.map(item => item.name).join(",\n"); this.raiseError(`Expected "${symbol}" to be in list [${itemsString}]`); } else if (rangeIndex !== undefined) { @@ -779,10 +779,10 @@ namespace FourSlash { if (ranges && ranges.length > rangeIndex) { const range = ranges[rangeIndex]; - const start = completion.span.start; - const end = start + completion.span.length; + const start = completions.span.start; + const end = start + completions.span.length; if (range.start !== start || range.end !== end) { - this.raiseError(`Expected completion span for '${symbol}', ${stringify(completion.span)}, to cover range ${stringify(range)}`); + this.raiseError(`Expected completion span for '${symbol}', ${stringify(completions.span)}, to cover range ${stringify(range)}`); } } else { @@ -798,7 +798,7 @@ namespace FourSlash { public verifyImportModuleCompletionListDoesNotContain(symbol: string) { const completions = this.getImportModuleCompletionListAtCaret(); if (completions) { - if (ts.forEach(completions, completion => completion.name === symbol)) { + if (ts.forEach(completions.entries, completion => completion.name === symbol)) { this.raiseError(`Import module completion list did contain ${symbol}`); } } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 1972ab85b9d..35032f31828 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -426,7 +426,7 @@ namespace Harness.LanguageService { getCompletionsAtPosition(fileName: string, position: number): ts.CompletionInfo { return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position)); } - getImportModuleCompletionsAtPosition(fileName: string, position: number): ts.ImportCompletionEntry[] { + getImportModuleCompletionsAtPosition(fileName: string, position: number): ts.ImportCompletionInfo { return unwrapJSONCallResult(this.shim.getImportModuleCompletionsAtPosition(fileName, position)); } getCompletionEntryDetails(fileName: string, position: number, entryName: string): ts.CompletionEntryDetails { diff --git a/src/server/client.ts b/src/server/client.ts index 04784a77f10..03a370411d1 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -220,7 +220,7 @@ namespace ts.server { }; } - getImportModuleCompletionsAtPosition(fileName: string, position: number): ImportCompletionEntry[] { + getImportModuleCompletionsAtPosition(fileName: string, position: number): ImportCompletionInfo { const lineOffset = this.positionToOneBasedLineOffset(fileName, position); const args: protocol.CompletionsRequestArgs = { file: fileName, @@ -232,7 +232,13 @@ namespace ts.server { const request = this.processRequest(CommandNames.ImportModuleCompletions, args); const response = this.processResponse(request); - return response.body; + const startPosition = this.lineOffsetToPosition(fileName, response.span.start); + const endPosition = this.lineOffsetToPosition(fileName, response.span.end); + + return { + span: ts.createTextSpanFromBounds(startPosition, endPosition), + entries: response.body + }; } getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails { diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts index 803edce0d70..20670ae0245 100644 --- a/src/server/protocol.d.ts +++ b/src/server/protocol.d.ts @@ -816,7 +816,8 @@ declare namespace ts.server.protocol { body?: CompletionEntry[]; } - export interface ImportModuleCompletionsResponse extends Response { + export interface ImportModuleCompletionsResponse extends Response { + span: TextSpan; body?: ImportCompletionEntry[]; } diff --git a/src/services/services.ts b/src/services/services.ts index e8ff3847804..fd665908248 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1208,7 +1208,7 @@ namespace ts { getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; getCompletionsAtPosition(fileName: string, position: number): CompletionInfo; - getImportModuleCompletionsAtPosition(fileName: string, position: number): ImportCompletionEntry[]; + getImportModuleCompletionsAtPosition(fileName: string, position: number): ImportCompletionInfo; getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; @@ -1481,10 +1481,14 @@ namespace ts { sortText: string; } + export interface ImportCompletionInfo { + span: TextSpan; + entries: ImportCompletionEntry[]; + } + export interface ImportCompletionEntry { name: string; kind: string; // see ScriptElementKind - span: TextSpan; sortText: string; } @@ -4492,7 +4496,7 @@ namespace ts { } } - function getImportModuleCompletionsAtPosition(fileName: string, position: number): ImportCompletionEntry[] { + function getImportModuleCompletionsAtPosition(fileName: string, position: number): ImportCompletionInfo { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); @@ -4507,7 +4511,10 @@ namespace ts { if (node.parent.kind === SyntaxKind.ImportDeclaration || isExpressionOfExternalModuleImportEqualsDeclaration(node) || isRequireCall(node.parent, false)) { // Get all known external module names or complete a path to a module - return getStringLiteralCompletionEntriesFromModuleNames(node); + return { + entries: getStringLiteralCompletionEntriesFromModuleNames(node), + span: getDirectoryFragmentTextSpan((node).text, node.getStart() + 1) + }; } } @@ -4515,8 +4522,6 @@ namespace ts { function getStringLiteralCompletionEntriesFromModuleNames(node: StringLiteral) { const literalValue = normalizeSlashes(node.text); - // Replace the entire text of the string literal (excluding the quotes) - const span: TextSpan = getDirectoryFragmentTextSpan(literalValue, node.getStart() + 1); const scriptPath = node.getSourceFile().path; const scriptDirectory = getDirectoryPath(scriptPath); @@ -4524,16 +4529,16 @@ namespace ts { const compilerOptions = program.getCompilerOptions(); if (compilerOptions.rootDirs) { return getCompletionEntriesForDirectoryFragmentWithRootDirs( - compilerOptions.rootDirs, literalValue, scriptDirectory, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, span, scriptPath); + compilerOptions.rootDirs, literalValue, scriptDirectory, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, scriptPath); } else { return getCompletionEntriesForDirectoryFragment( - literalValue, scriptDirectory, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, span, scriptPath); + literalValue, scriptDirectory, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, scriptPath); } } else { // Check for node modules - return getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, span); + return getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory); } } @@ -4558,7 +4563,7 @@ namespace ts { return deduplicate(map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory))); } - function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, exclude?: string): ImportCompletionEntry[] { + function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, exclude?: string): ImportCompletionEntry[] { const basePath = program.getCompilerOptions().project || host.getCurrentDirectory(); const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames()); const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptPath, ignoreCase); @@ -4566,16 +4571,16 @@ namespace ts { const result: ImportCompletionEntry[] = []; for (const baseDirectory of baseDirectories) { - getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, span, exclude, result); + getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, exclude, result); } return result; } - function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, exclude?: string, result: ImportCompletionEntry[] = []): ImportCompletionEntry[] { + function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, exclude?: string, result: ImportCompletionEntry[] = []): ImportCompletionEntry[] { fragment = getDirectoryPath(fragment); if (!fragment) { - fragment = "./" + fragment = "./"; } else { fragment = ensureTrailingDirectorySeparator(fragment); @@ -4600,8 +4605,7 @@ namespace ts { result.push({ name: fileName, kind: ScriptElementKind.directory, - sortText: fileName, - span + sortText: fileName }); } }); @@ -4615,8 +4619,7 @@ namespace ts { result.push({ name: ensureTrailingDirectorySeparator(directoryName), kind: ScriptElementKind.directory, - sortText: directoryName, - span + sortText: directoryName }); }); } @@ -4632,7 +4635,7 @@ namespace ts { * Modules from node_modules (i.e. those listed in package.json) * This includes all files that are found in node_modules/moduleName/ with acceptable file extensions */ - function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string, span: TextSpan): ImportCompletionEntry[] { + function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string): ImportCompletionEntry[] { const options = program.getCompilerOptions(); const { baseUrl, paths } = options; @@ -4642,7 +4645,7 @@ namespace ts { const fileExtensions = getSupportedExtensions(options); const projectDir = options.project || host.getCurrentDirectory(); const absolute = isRootedDiskPath(baseUrl) ? baseUrl : combinePaths(projectDir, baseUrl); - result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false, span); + result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false); if (paths) { for (const path in paths) { @@ -4651,7 +4654,7 @@ namespace ts { if (paths[path]) { forEach(paths[path], pattern => { forEach(getModulesForPathsPattern(fragment, baseUrl, pattern, fileExtensions), match => { - result.push(createCompletionEntryForModule(match, ScriptElementKind.externalModuleName, span)); + result.push(createCompletionEntryForModule(match, ScriptElementKind.externalModuleName)); }); }); } @@ -4659,7 +4662,7 @@ namespace ts { else if (startsWith(path, fragment)) { const entry = paths[path] && paths[path].length === 1 && paths[path][0]; if (entry) { - result.push(createCompletionEntryForModule(path, ScriptElementKind.externalModuleName, span)); + result.push(createCompletionEntryForModule(path, ScriptElementKind.externalModuleName)); } } } @@ -4670,10 +4673,10 @@ namespace ts { result = []; } - getCompletionEntriesFromTypings(host, options, scriptPath, span, result); + getCompletionEntriesFromTypings(host, options, scriptPath, result); forEach(enumeratePotentialNonRelativeModules(fragment, scriptPath, options), moduleName => { - result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span)); + result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); }); return result; @@ -4762,7 +4765,7 @@ namespace ts { return deduplicate(nonRelativeModules); } - function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number): ImportCompletionEntry[] { + function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number): ImportCompletionInfo { const token = getTokenAtPosition(sourceFile, position); if (!token) { return undefined; @@ -4791,36 +4794,41 @@ namespace ts { if (kind === "path") { // Give completions for a relative path const span: TextSpan = getDirectoryFragmentTextSpan(toComplete, range.pos + prefix.length); - return getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, span, sourceFile.path); + return { + entries: getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, sourceFile.path), + span + }; } else { // Give completions based on the typings available - // Replace the entire string const span: TextSpan = { start: range.pos + prefix.length, length: match[0].length - prefix.length }; - return getCompletionEntriesFromTypings(host, program.getCompilerOptions(), scriptPath, span); + return { + entries: getCompletionEntriesFromTypings(host, program.getCompilerOptions(), scriptPath), + span + }; } } return undefined; } - function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, span: TextSpan, result: ImportCompletionEntry[] = []): ImportCompletionEntry[] { + function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, result: ImportCompletionEntry[] = []): ImportCompletionEntry[] { // Check for typings specified in compiler options if (options.types) { forEach(options.types, moduleName => { - result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span)); + result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); }); } else if (host.getDirectories && options.typeRoots) { const absoluteRoots = map(options.typeRoots, rootDirectory => getAbsoluteProjectPath(rootDirectory, host, options.project)); - forEach(absoluteRoots, absoluteRoot => getCompletionEntriesFromDirectories(host, options, absoluteRoot, span, result)); + forEach(absoluteRoots, absoluteRoot => getCompletionEntriesFromDirectories(host, options, absoluteRoot, result)); } if (host.getDirectories) { // Also get all @types typings installed in visible node_modules directories forEach(findPackageJsons(scriptPath), package => { const typesDir = combinePaths(getDirectoryPath(package), "node_modules/@types"); - getCompletionEntriesFromDirectories(host, options, typesDir, span, result); + getCompletionEntriesFromDirectories(host, options, typesDir, result); }); } @@ -4839,10 +4847,10 @@ namespace ts { return normalizePath(host.resolvePath(path)); } - function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, span: TextSpan, result: ImportCompletionEntry[]) { + function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, result: ImportCompletionEntry[]) { if (host.getDirectories && directoryProbablyExists(directory, host)) { forEach(host.getDirectories(directory), typeDirectory => { - result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName, span)); + result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName)); }); } } @@ -4911,10 +4919,10 @@ namespace ts { } } + // Add all the package names that are not in the @types scope function addPotentialPackageNames(dependencies: any, result: string[]) { for (const dep in dependencies) { - if (dependencies.hasOwnProperty(dep) && !startsWith(dep, "@types") && - dep.charCodeAt(6) !== CharacterCodes.slash && dep.charCodeAt(6) !== CharacterCodes.backslash) { + if (dependencies.hasOwnProperty(dep) && !startsWith(dep, "@types/")) { result.push(dep); } } @@ -4946,8 +4954,8 @@ namespace ts { } } - function createCompletionEntryForModule(name: string, kind: string, span: TextSpan): ImportCompletionEntry { - return { name, kind, sortText: name, span }; + function createCompletionEntryForModule(name: string, kind: string): ImportCompletionEntry { + return { name, kind, sortText: name }; } // Replace everything after the last directory seperator that appears @@ -4955,7 +4963,7 @@ namespace ts { function getDirectoryFragmentTextSpan(text: string, textStart: number): TextSpan { const index = text.lastIndexOf(directorySeparator); const offset = index !== -1 ? index + 1 : 0; - return { start: textStart + offset, length: text.length - offset } + return { start: textStart + offset, length: text.length - offset }; } // Returns true if the path is explicitly relative to the script (i.e. relative to . or ..) diff --git a/src/services/shims.ts b/src/services/shims.ts index eb6c5e55b7b..9f4d9d24a43 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -874,7 +874,7 @@ namespace ts { getImportModuleCompletionsAtPosition(fileName: string, position: number): string { return this.forwardJSONCall( `getImportModuleCompletionsAtPosition('${fileName}', ${position})`, - () => this.languageService.getCompletionsAtPosition(fileName, position) + () => this.languageService.getImportModuleCompletionsAtPosition(fileName, position) ); } From 293ca60ffd224f8c4a2d4c9c17426b54adee8b1f Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Wed, 3 Aug 2016 11:07:57 -0700 Subject: [PATCH 19/73] Renamed span to textSpan to better follow other language service APIs --- src/harness/fourslash.ts | 6 +++--- src/server/client.ts | 2 +- src/services/services.ts | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 532dccd4f6b..be8f1877c8f 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -779,10 +779,10 @@ namespace FourSlash { if (ranges && ranges.length > rangeIndex) { const range = ranges[rangeIndex]; - const start = completions.span.start; - const end = start + completions.span.length; + const start = completions.textSpan.start; + const end = start + completions.textSpan.length; if (range.start !== start || range.end !== end) { - this.raiseError(`Expected completion span for '${symbol}', ${stringify(completions.span)}, to cover range ${stringify(range)}`); + this.raiseError(`Expected completion span for '${symbol}', ${stringify(completions.textSpan)}, to cover range ${stringify(range)}`); } } else { diff --git a/src/server/client.ts b/src/server/client.ts index 03a370411d1..7af7be16f19 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -236,7 +236,7 @@ namespace ts.server { const endPosition = this.lineOffsetToPosition(fileName, response.span.end); return { - span: ts.createTextSpanFromBounds(startPosition, endPosition), + textSpan: ts.createTextSpanFromBounds(startPosition, endPosition), entries: response.body }; } diff --git a/src/services/services.ts b/src/services/services.ts index fd665908248..1ed91381469 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1482,7 +1482,7 @@ namespace ts { } export interface ImportCompletionInfo { - span: TextSpan; + textSpan: TextSpan; entries: ImportCompletionEntry[]; } @@ -4513,7 +4513,7 @@ namespace ts { // Get all known external module names or complete a path to a module return { entries: getStringLiteralCompletionEntriesFromModuleNames(node), - span: getDirectoryFragmentTextSpan((node).text, node.getStart() + 1) + textSpan: getDirectoryFragmentTextSpan((node).text, node.getStart() + 1) }; } } @@ -4793,18 +4793,18 @@ namespace ts { const scriptPath = getDirectoryPath(sourceFile.path); if (kind === "path") { // Give completions for a relative path - const span: TextSpan = getDirectoryFragmentTextSpan(toComplete, range.pos + prefix.length); + const textSpan: TextSpan = getDirectoryFragmentTextSpan(toComplete, range.pos + prefix.length); return { entries: getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, sourceFile.path), - span + textSpan }; } else { // Give completions based on the typings available - const span: TextSpan = { start: range.pos + prefix.length, length: match[0].length - prefix.length }; + const textSpan: TextSpan = { start: range.pos + prefix.length, length: match[0].length - prefix.length }; return { entries: getCompletionEntriesFromTypings(host, program.getCompilerOptions(), scriptPath), - span + textSpan }; } } From ca288231f7a531778e511643524fa83b03cfc478 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Thu, 4 Aug 2016 11:10:00 -0700 Subject: [PATCH 20/73] Fixing shim and normalizing paths --- src/services/services.ts | 5 ++++- src/services/shims.ts | 15 ++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 1ed91381469..7b523d7e7ab 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4594,6 +4594,7 @@ namespace ts { // Enumerate the available files const files = host.readDirectory(baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]); forEach(files, filePath => { + filePath = normalizePath(filePath); if (exclude && comparePaths(filePath, exclude, scriptPath, ignoreCase) === Comparison.EqualTo) { return false; } @@ -4614,7 +4615,7 @@ namespace ts { if (host.getDirectories) { const directories = host.getDirectories(baseDirectory); forEach(directories, d => { - const directoryName = getBaseFileName(removeTrailingDirectorySeparator(d)); + const directoryName = getBaseFileName(normalizePath(d)); result.push({ name: ensureTrailingDirectorySeparator(directoryName), @@ -4755,6 +4756,7 @@ namespace ts { const nestedFiles = host.readDirectory(visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]); forEach(nestedFiles, (f) => { + f = normalizePath(f); const nestedModule = removeFileExtension(getBaseFileName(f)); nonRelativeModules.push(nestedModule); }); @@ -4850,6 +4852,7 @@ namespace ts { function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, result: ImportCompletionEntry[]) { if (host.getDirectories && directoryProbablyExists(directory, host)) { forEach(host.getDirectories(directory), typeDirectory => { + typeDirectory = normalizePath(typeDirectory); result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName)); }); } diff --git a/src/services/shims.ts b/src/services/shims.ts index 9f4d9d24a43..70e4975ce45 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -67,7 +67,7 @@ namespace ts { getProjectVersion?(): string; useCaseSensitiveFileNames?(): boolean; - readDirectory(path: string, extensions?: string, exclude?: string, include?: string): string; + readDirectory(rootDir: string, extension: string, basePaths?: string, excludeEx?: string, includeFileEx?: string, includeDirEx?: string, depth?: number): string; readFile(path: string, encoding?: string): string; resolvePath(path: string): string; fileExists(path: string): boolean; @@ -417,12 +417,17 @@ namespace ts { return this.shimHost.getDefaultLibFileName(JSON.stringify(options)); } - public readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] { + public readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] { + const pattern = getFileMatcherPatterns(path, extensions, exclude, include, + this.shimHost.useCaseSensitiveFileNames(), this.shimHost.getCurrentDirectory()); return JSON.parse(this.shimHost.readDirectory( path, - extensions ? JSON.stringify(extensions) : undefined, - exclude ? JSON.stringify(exclude) : undefined, - include ? JSON.stringify(include) : undefined + JSON.stringify(extensions), + JSON.stringify(pattern.basePaths), + pattern.excludePattern, + pattern.includeFilePattern, + pattern.includeDirectoryPattern, + depth )); } From 0f22079d9e52f3fd4c26b9999ef20a774896649b Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Thu, 4 Aug 2016 18:17:41 -0700 Subject: [PATCH 21/73] Remove trailing slashes, remove mostly useless IO, fix script element kind for files --- src/services/services.ts | 35 +++---------------- .../completionForStringLiteralImport1.ts | 2 +- ...etionForStringLiteralNonrelativeImport4.ts | 4 +-- ...etionForStringLiteralNonrelativeImport6.ts | 4 +-- ...mpletionForStringLiteralRelativeImport1.ts | 6 ++-- ...mpletionForStringLiteralRelativeImport3.ts | 8 ++--- ...mpletionForStringLiteralRelativeImport4.ts | 2 +- .../completionForTripleSlashReference1.ts | 6 ++-- .../completionForTripleSlashReference3.ts | 8 ++--- 9 files changed, 24 insertions(+), 51 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 7b523d7e7ab..6cb92547998 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1865,7 +1865,6 @@ namespace ts { interface VisibleModuleInfo { moduleName: string; moduleDir: string; - canBeImported: boolean; } export interface DisplayPartsSymbolWriter extends SymbolWriter { @@ -4605,7 +4604,7 @@ namespace ts { if (!duplicate) { result.push({ name: fileName, - kind: ScriptElementKind.directory, + kind: ScriptElementKind.scriptElement, sortText: fileName }); } @@ -4618,7 +4617,7 @@ namespace ts { const directoryName = getBaseFileName(normalizePath(d)); result.push({ - name: ensureTrailingDirectorySeparator(directoryName), + name: directoryName, kind: ScriptElementKind.directory, sortText: directoryName }); @@ -4750,7 +4749,7 @@ namespace ts { if (!options.moduleResolution || options.moduleResolution === ModuleResolutionKind.NodeJs) { forEach(enumerateNodeModulesVisibleToScript(host, scriptPath), visibleModule => { if (!isNestedModule) { - nonRelativeModules.push(visibleModule.canBeImported ? visibleModule.moduleName : ensureTrailingDirectorySeparator(visibleModule.moduleName)); + nonRelativeModules.push(visibleModule.moduleName); } else { const nestedFiles = host.readDirectory(visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]); @@ -4904,8 +4903,7 @@ namespace ts { const moduleDir = combinePaths(nodeModulesDir, moduleName); result.push({ moduleName, - moduleDir, - canBeImported: moduleCanBeImported(moduleDir) + moduleDir }); }); }); @@ -4930,31 +4928,6 @@ namespace ts { } } } - - /* - * A module can be imported by name alone if one of the following is true: - * It defines the "typings" property in its package.json - * The module has a "main" export and an index.d.ts file - * The module has an index.ts - */ - function moduleCanBeImported(modulePath: string): boolean { - const packagePath = combinePaths(modulePath, "package.json"); - - let hasMainExport = false; - if (host.fileExists(packagePath)) { - const package = tryReadingPackageJson(packagePath); - if (package) { - if (package.typings) { - return true; - } - hasMainExport = !!package.main; - } - } - - hasMainExport = hasMainExport || host.fileExists(combinePaths(modulePath, "index.js")); - - return (hasMainExport && host.fileExists(combinePaths(modulePath, "index.d.ts"))) || host.fileExists(combinePaths(modulePath, "index.ts")); - } } function createCompletionEntryForModule(name: string, kind: string): ImportCompletionEntry { diff --git a/tests/cases/fourslash/completionForStringLiteralImport1.ts b/tests/cases/fourslash/completionForStringLiteralImport1.ts index 239c3be42fc..5fcb3e42f1a 100644 --- a/tests/cases/fourslash/completionForStringLiteralImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralImport1.ts @@ -28,4 +28,4 @@ goTo.marker("2"); verify.importModuleCompletionListContains("some-module", 2); goTo.marker("3"); -verify.importModuleCompletionListContains("fourslash/", 3); \ No newline at end of file +verify.importModuleCompletionListContains("fourslash", 3); \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts index f4a9ae2d537..ef926a7c8ad 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts @@ -25,8 +25,8 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("fake-module/"); + verify.importModuleCompletionListContains("fake-module"); verify.importModuleCompletionListContains("fake-module2"); - verify.importModuleCompletionListContains("fake-module3/"); + verify.importModuleCompletionListContains("fake-module3"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts index 263b57d9d29..c430310a217 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts @@ -52,8 +52,8 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("module-no-main/"); - verify.importModuleCompletionListContains("module-no-main-index-d-ts/"); + verify.importModuleCompletionListContains("module-no-main"); + verify.importModuleCompletionListContains("module-no-main-index-d-ts"); verify.importModuleCompletionListContains("module-index-ts"); verify.importModuleCompletionListContains("module-index-d-ts-explicit-main"); verify.importModuleCompletionListContains("module-index-d-ts-default-main"); diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts index 52d62f3b4f8..3257dff40af 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts @@ -51,8 +51,8 @@ for (const kind of kinds) { verify.importModuleCompletionListContains("f1"); verify.importModuleCompletionListContains("f2"); verify.importModuleCompletionListContains("e1"); - verify.importModuleCompletionListContains("folder/"); - verify.importModuleCompletionListContains("parentTest/"); + verify.importModuleCompletionListContains("folder"); + verify.importModuleCompletionListContains("parentTest"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(5); goTo.marker(kind + "2"); @@ -63,6 +63,6 @@ for (const kind of kinds) { goTo.marker(kind + "3"); verify.importModuleCompletionListContains("f4"); verify.importModuleCompletionListContains("g1"); - verify.importModuleCompletionListContains("sub/"); + verify.importModuleCompletionListContains("sub"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts index 4f35612c5c6..95e4e51577a 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts @@ -32,18 +32,18 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("fourslash/"); + verify.importModuleCompletionListContains("fourslash"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); goTo.marker(kind + "1"); - verify.importModuleCompletionListContains("fourslash/"); + verify.importModuleCompletionListContains("fourslash"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); goTo.marker(kind + "2"); verify.importModuleCompletionListContains("f1"); verify.importModuleCompletionListContains("f2"); verify.importModuleCompletionListContains("e1"); - verify.importModuleCompletionListContains("folder/"); - verify.importModuleCompletionListContains("tests/"); + verify.importModuleCompletionListContains("folder"); + verify.importModuleCompletionListContains("tests"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(5); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts index 773c4d90d75..f89eaa567ad 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts @@ -43,7 +43,7 @@ for (const kind of kinds) { verify.importModuleCompletionListContains("module0"); verify.importModuleCompletionListContains("module1"); verify.importModuleCompletionListContains("module2"); - verify.importModuleCompletionListContains("more/"); + verify.importModuleCompletionListContains("more"); // Should not contain itself verify.not.importModuleCompletionListItemsCountIsGreaterThan(4); diff --git a/tests/cases/fourslash/completionForTripleSlashReference1.ts b/tests/cases/fourslash/completionForTripleSlashReference1.ts index 8c6188ac90c..66dfa5f93c2 100644 --- a/tests/cases/fourslash/completionForTripleSlashReference1.ts +++ b/tests/cases/fourslash/completionForTripleSlashReference1.ts @@ -34,16 +34,16 @@ for (let i = 0; i < 5; i++) { verify.importModuleCompletionListContains("f1.d.ts"); verify.importModuleCompletionListContains("f2.tsx"); verify.importModuleCompletionListContains("e1.ts"); - verify.importModuleCompletionListContains("parentTest/"); + verify.importModuleCompletionListContains("parentTest"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(5); } goTo.marker("5"); verify.importModuleCompletionListContains("g1.ts"); -verify.importModuleCompletionListContains("sub/"); +verify.importModuleCompletionListContains("sub"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); goTo.marker("6"); verify.importModuleCompletionListContains("g1.ts"); -verify.importModuleCompletionListContains("sub/"); +verify.importModuleCompletionListContains("sub"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); \ No newline at end of file diff --git a/tests/cases/fourslash/completionForTripleSlashReference3.ts b/tests/cases/fourslash/completionForTripleSlashReference3.ts index dda6ae92b41..af8d49d9df3 100644 --- a/tests/cases/fourslash/completionForTripleSlashReference3.ts +++ b/tests/cases/fourslash/completionForTripleSlashReference3.ts @@ -25,17 +25,17 @@ //// /*e2*/ goTo.marker("0"); -verify.importModuleCompletionListContains("fourslash/"); +verify.importModuleCompletionListContains("fourslash"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); goTo.marker("1"); -verify.importModuleCompletionListContains("fourslash/"); +verify.importModuleCompletionListContains("fourslash"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); goTo.marker("2"); verify.importModuleCompletionListContains("f1.ts"); verify.importModuleCompletionListContains("f2.tsx"); verify.importModuleCompletionListContains("e1.ts"); -verify.importModuleCompletionListContains("folder/"); -verify.importModuleCompletionListContains("tests/"); +verify.importModuleCompletionListContains("folder"); +verify.importModuleCompletionListContains("tests"); verify.not.importModuleCompletionListItemsCountIsGreaterThan(5); \ No newline at end of file From ecdbdb33af5b360427d15635a4826556ce93f472 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Fri, 5 Aug 2016 17:42:52 -0700 Subject: [PATCH 22/73] Fixing the filtering of nested module completions --- src/services/services.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 6cb92547998..1a952491365 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4728,30 +4728,33 @@ namespace ts { function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string, options: CompilerOptions): string[] { // Check If this is a nested module - const isNestedModule = fragment.indexOf(directorySeparator) !== -1 ; + const isNestedModule = fragment.indexOf(directorySeparator) !== -1; + const moduleNameFragment = isNestedModule ? fragment.substr(0, fragment.lastIndexOf(directorySeparator)) : undefined; // Get modules that the type checker picked up const ambientModules = ts.map(program.getTypeChecker().getAmbientModules(), sym => stripQuotes(sym.name)); let nonRelativeModules = ts.filter(ambientModules, moduleName => startsWith(moduleName, fragment)); // Nested modules of the form "module-name/sub" need to be adjusted to only return the string - // after the last '/' that appears in the fragment because editors insert the completion - // only after that character - nonRelativeModules = ts.map(nonRelativeModules, moduleName => { - if (moduleName.indexOf(directorySeparator) !== -1) { - if (isNestedModule) { - return moduleName.substr(fragment.lastIndexOf(directorySeparator) + 1); + // after the last '/' that appears in the fragment because that's where the replacement span + // starts + if (isNestedModule) { + const moduleNameWithSeperator = ensureTrailingDirectorySeparator(moduleNameFragment); + nonRelativeModules = ts.map(nonRelativeModules, moduleName => { + if (startsWith(fragment, moduleNameWithSeperator)) { + return moduleName.substr(moduleNameWithSeperator.length); } - } - return moduleName; - }); + return moduleName; + }); + } + if (!options.moduleResolution || options.moduleResolution === ModuleResolutionKind.NodeJs) { forEach(enumerateNodeModulesVisibleToScript(host, scriptPath), visibleModule => { if (!isNestedModule) { nonRelativeModules.push(visibleModule.moduleName); } - else { + else if (startsWith(visibleModule.moduleName, moduleNameFragment)) { const nestedFiles = host.readDirectory(visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]); forEach(nestedFiles, (f) => { From e11d5e9de632bdd93a26eb2133050d4934aec1c8 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Fri, 5 Aug 2016 17:53:04 -0700 Subject: [PATCH 23/73] Cleaning up test cases and adding a few more --- .../completionForStringLiteralImport1.ts | 2 + .../completionForStringLiteralImport2.ts | 2 + ...etionForStringLiteralNonrelativeImport1.ts | 2 + ...tionForStringLiteralNonrelativeImport10.ts | 2 + ...tionForStringLiteralNonrelativeImport11.ts | 38 ++++++++++++ ...etionForStringLiteralNonrelativeImport2.ts | 2 + ...etionForStringLiteralNonrelativeImport3.ts | 3 + ...etionForStringLiteralNonrelativeImport4.ts | 2 + ...etionForStringLiteralNonrelativeImport5.ts | 2 + ...etionForStringLiteralNonrelativeImport6.ts | 62 ------------------- ...etionForStringLiteralNonrelativeImport7.ts | 3 + ...etionForStringLiteralNonrelativeImport8.ts | 2 + ...etionForStringLiteralNonrelativeImport9.ts | 2 + ...rStringLiteralNonrelativeImportTypings1.ts | 2 + ...rStringLiteralNonrelativeImportTypings2.ts | 2 + ...rStringLiteralNonrelativeImportTypings3.ts | 2 + ...mpletionForStringLiteralRelativeImport1.ts | 2 + ...mpletionForStringLiteralRelativeImport2.ts | 3 + ...mpletionForStringLiteralRelativeImport3.ts | 2 + ...mpletionForStringLiteralRelativeImport4.ts | 2 + .../completionForTripleSlashReference1.ts | 2 + .../completionForTripleSlashReference2.ts | 3 + .../completionForTripleSlashReference3.ts | 2 + .../completionForTripleSlashReference4.ts | 43 +++++++++++++ 24 files changed, 127 insertions(+), 62 deletions(-) create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport11.ts delete mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts create mode 100644 tests/cases/fourslash/completionForTripleSlashReference4.ts diff --git a/tests/cases/fourslash/completionForStringLiteralImport1.ts b/tests/cases/fourslash/completionForStringLiteralImport1.ts index 5fcb3e42f1a..482726f6305 100644 --- a/tests/cases/fourslash/completionForStringLiteralImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralImport1.ts @@ -1,5 +1,7 @@ /// +// Should define spans for replacement that appear after the last directory seperator in import statements + // @typeRoots: my_typings // @Filename: test.ts diff --git a/tests/cases/fourslash/completionForStringLiteralImport2.ts b/tests/cases/fourslash/completionForStringLiteralImport2.ts index 2910d9e1475..43896f1f392 100644 --- a/tests/cases/fourslash/completionForStringLiteralImport2.ts +++ b/tests/cases/fourslash/completionForStringLiteralImport2.ts @@ -1,5 +1,7 @@ /// +// Should define spans for replacement that appear after the last directory seperator in triple slash references + // @typeRoots: my_typings // @Filename: test.ts diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts index f8ea68a271c..d6f38431ce1 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts @@ -1,5 +1,7 @@ /// +// Should give completions for node modules and files within those modules with ts file extensions + // @Filename: tests/test0.ts //// import * as foo1 from "f/*import_as0*/ //// import * as foo2 from "fake-module//*import_as1*/ diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts index 393ffed0c52..aa9fd976644 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts @@ -1,5 +1,7 @@ /// +// Should not give node module completions if classic module resolution is enabled + // @moduleResolution: classic // @Filename: dir1/dir2/dir3/dir4/test0.ts diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport11.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport11.ts new file mode 100644 index 00000000000..70d39182a8a --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport11.ts @@ -0,0 +1,38 @@ +/// + +// Should handle nested files in folders discovered via the baseUrl compiler option + +// @baseUrl: tests/cases/fourslash/modules + +// @Filename: tests/test0.ts +//// import * as foo1 from "subfolder//*import_as0*/ +//// import foo2 = require("subfolder//*import_equals0*/ +//// var foo3 = require("subfolder//*require0*/ + +//// import * as foo1 from "module-from-node//*import_as1*/ +//// import foo2 = require("module-from-node//*import_equals1*/ +//// var foo3 = require("module-from-node//*require1*/ + +// @Filename: modules/subfolder/module.ts +//// export var x = 5; + +// @Filename: package.json +//// { "dependencies": { "module-from-node": "latest" } } +// @Filename: node_modules/module-from-node/index.ts +//// /*module1*/ + + + +const kinds = ["import_as", "import_equals", "require"]; + +for (const kind of kinds) { + goTo.marker(kind + "0"); + + verify.importModuleCompletionListContains("module"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); + + goTo.marker(kind + "1"); + + verify.importModuleCompletionListContains("index"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); +} diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts index 08b92b05295..bc2529e86f9 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts @@ -1,5 +1,7 @@ /// +// Should not give duplicate entries for similarly named files with different extensions + // @Filename: tests/test0.ts //// import * as foo1 from "fake-module//*import_as0*/ //// import foo2 = require("fake-module//*import_equals0*/ diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts index 9d9beac16e7..9b1fd451185 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts @@ -1,4 +1,7 @@ /// + +// Should give completions for js files in node modules when allowJs is set to true + // @allowJs: true // @Filename: tests/test0.ts diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts index ef926a7c8ad..b9be9f90213 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts @@ -1,5 +1,7 @@ /// +// Should give completions for all node modules visible to the script + // @Filename: dir1/dir2/dir3/dir4/test0.ts //// import * as foo1 from "f/*import_as0*/ //// import foo4 = require("f/*import_equals0*/ diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts index 9e0c756a919..4afa5854bf8 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts @@ -1,5 +1,7 @@ /// +// Should give completions for ambiently declared modules + // @Filename: test0.ts //// /// //// /// diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts deleted file mode 100644 index c430310a217..00000000000 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport6.ts +++ /dev/null @@ -1,62 +0,0 @@ -/// - -// @Filename: tests/test0.ts -//// import * as foo1 from "module-/*import_as0*/ -//// import foo2 = require("module-/*import_equals0*/ -//// var foo3 = require("module-/*require0*/ - -// @Filename: package.json -//// { "dependencies": { -//// "module-no-main": "latest", -//// "module-no-main-index-d-ts": "latest", -//// "module-index-ts": "latest", -//// "module-index-d-ts-explicit-main": "latest", -//// "module-index-d-ts-default-main": "latest", -//// "module-typings": "latest" -//// } } - -// @Filename: node_modules/module-no-main/package.json -//// { } - -// @Filename: node_modules/module-no-main-index-d-ts/package.json -//// { } -// @Filename: node_modules/module-no-main-index-d-ts/index.d.ts -//// /*module-no-main-index-d-ts*/ - -// @Filename: node_modules/module-index-ts/package.json -//// { } -// @Filename: node_modules/module-index-ts/index.ts -//// /*module-index-ts*/ - -// @Filename: node_modules/module-index-d-ts-explicit-main/package.json -//// { "main":"./notIndex.js" } -// @Filename: node_modules/module-index-d-ts-explicit-main/notIndex.js -//// /*module-index-d-ts-explicit-main*/ -// @Filename: node_modules/module-index-d-ts-explicit-main/index.d.ts -//// /*module-index-d-ts-explicit-main2*/ - -// @Filename: node_modules/module-index-d-ts-default-main/package.json -//// { } -// @Filename: node_modules/module-index-d-ts-default-main/index.js -//// /*module-index-d-ts-default-main*/ -// @Filename: node_modules/module-index-d-ts-default-main/index.d.ts -//// /*module-index-d-ts-default-main2*/ - -// @Filename: node_modules/module-typings/package.json -//// { "typings":"./types.d.ts" } -// @Filename: node_modules/module-typings/types.d.ts -//// /*module-typings*/ - -const kinds = ["import_as", "import_equals", "require"]; - -for (const kind of kinds) { - goTo.marker(kind + "0"); - - verify.importModuleCompletionListContains("module-no-main"); - verify.importModuleCompletionListContains("module-no-main-index-d-ts"); - verify.importModuleCompletionListContains("module-index-ts"); - verify.importModuleCompletionListContains("module-index-d-ts-explicit-main"); - verify.importModuleCompletionListContains("module-index-d-ts-default-main"); - verify.importModuleCompletionListContains("module-typings"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(6); -} diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts index d08ebd17b73..eca5a3e5412 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts @@ -1,4 +1,7 @@ /// + +// Should give completions for files that are discovered via the baseUrl compiler option + // @baseUrl: tests/cases/fourslash/modules // @Filename: tests/test0.ts diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts index b7e745f814e..54893991432 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts @@ -1,5 +1,7 @@ /// +// Should give completions for modules referenced via baseUrl and paths compiler options with wildcards + // @Filename: tsconfig.json //// { //// "compilerOptions": { diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts index 31984df9081..bcdb731d1f1 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts @@ -1,5 +1,7 @@ /// +// Should give completions for modules referenced via baseUrl and paths compiler options with explicit name mappings + // @Filename: tsconfig.json //// { //// "compilerOptions": { diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts index 1b77c57f5ca..9cf2af9c875 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts @@ -1,5 +1,7 @@ /// +// Should give completions for typings discovered via the typeRoots compiler option + // @typeRoots: my_typings,my_other_typings diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts index 3e991b3d4b4..1452c6c1c61 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts @@ -1,5 +1,7 @@ /// +// Should respect the types compiler option when giving completions + // @typeRoots: my_typings,my_other_typings // @types: module-x,module-z diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts index add1238cefc..f4a4dbaaa99 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts @@ -1,5 +1,7 @@ /// +// Should give completions for typings discovered in all visible @types directories + // @Filename: subdirectory/test0.ts //// /// //// import * as foo1 from "m/*import_as0*/ diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts index 3257dff40af..909e2c6b14d 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts @@ -1,5 +1,7 @@ /// +// Should give completions for ts files when allowJs is false + // @Filename: test0.ts //// import * as foo1 from "./*import_as0*/ //// import * as foo2 from ".//*import_as1*/ diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts index 72b28d39b87..8c8cb405cd3 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts @@ -1,4 +1,7 @@ /// + +// Should give completions for ts and js files when allowJs is true + // @allowJs: true // @Filename: test0.ts diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts index 95e4e51577a..a2035ee7fdb 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts @@ -1,5 +1,7 @@ /// +// Should give completions for absolute paths + // @Filename: tests/test0.ts //// import * as foo1 from "c:/tests/cases/f/*import_as0*/ //// import * as foo2 from "c:/tests/cases/fourslash/*import_as1*/ diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts index f89eaa567ad..df30b925d11 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts @@ -1,5 +1,7 @@ /// +// Should give completions for directories that are merged via the rootDirs compiler option + // @rootDirs: sub/src1,src2 // @Filename: src2/test0.ts diff --git a/tests/cases/fourslash/completionForTripleSlashReference1.ts b/tests/cases/fourslash/completionForTripleSlashReference1.ts index 66dfa5f93c2..f8ea7fc4d44 100644 --- a/tests/cases/fourslash/completionForTripleSlashReference1.ts +++ b/tests/cases/fourslash/completionForTripleSlashReference1.ts @@ -1,5 +1,7 @@ /// +// Should give completions for relative references to ts files when allowJs is false + // @Filename: test0.ts //// /// + +// Should give completions for relative references to js and ts files when allowJs is true + // @allowJs: true // @Filename: test0.ts diff --git a/tests/cases/fourslash/completionForTripleSlashReference3.ts b/tests/cases/fourslash/completionForTripleSlashReference3.ts index af8d49d9df3..2f92de19a21 100644 --- a/tests/cases/fourslash/completionForTripleSlashReference3.ts +++ b/tests/cases/fourslash/completionForTripleSlashReference3.ts @@ -1,5 +1,7 @@ /// +// Should give completions for absolute paths + // @Filename: tests/test0.ts //// /// Date: Mon, 8 Aug 2016 11:04:17 -0700 Subject: [PATCH 24/73] Moving some utility functions around --- src/services/services.ts | 21 --------------------- src/services/utilities.ts | 31 ++++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 1a952491365..ade5cc0b1bf 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2063,9 +2063,6 @@ namespace ts { sourceMapText?: string; } - // Matches the beginning of a triple slash directive - const tripleSlashDirectivePrefixRegex = /^\/\/\/\s* Date: Tue, 16 Aug 2016 14:55:19 -0700 Subject: [PATCH 25/73] Use rooted paths in the fourslash virtual file system --- src/compiler/checker.ts | 2 +- src/harness/fourslash.ts | 10 +++++++--- src/harness/harness.ts | 3 +++ src/harness/harnessLanguageService.ts | 20 ++++--------------- src/harness/virtualFileSystem.ts | 10 +++++++++- src/services/services.ts | 4 +--- ...sWhenEmitBlockingErrorOnOtherFile.baseline | 4 ++-- .../reference/getEmitOutput-pp.baseline | 8 ++++---- .../reference/getEmitOutput.baseline | 8 ++++---- ...etEmitOutputDeclarationMultiFiles.baseline | 8 ++++---- .../reference/getEmitOutputNoErrors.baseline | 2 +- .../getEmitOutputOnlyOneFile.baseline | 2 +- .../reference/getEmitOutputSourceMap.baseline | 4 ++-- .../getEmitOutputSourceRoot.baseline | 4 ++-- ...getEmitOutputSourceRootMultiFiles.baseline | 8 ++++---- .../getEmitOutputTsxFile_Preserve.baseline | 12 +++++------ .../getEmitOutputTsxFile_React.baseline | 12 +++++------ .../getEmitOutputWithDeclarationFile.baseline | 2 +- ...getEmitOutputWithDeclarationFile2.baseline | 4 ++-- ...mitOutputWithEarlySyntacticErrors.baseline | 2 +- .../getEmitOutputWithEmitterErrors.baseline | 2 +- .../getEmitOutputWithEmitterErrors2.baseline | 2 +- .../getEmitOutputWithSemanticErrors.baseline | 2 +- .../getEmitOutputWithSemanticErrors2.baseline | 4 ++-- ...ithSemanticErrorsForMultipleFiles.baseline | 4 ++-- ...thSyntacticErrorsForMultipleFiles.baseline | 2 +- .../getEmitOutputWithSyntaxErrors.baseline | 2 +- ...mpletionForStringLiteralRelativeImport4.ts | 2 +- .../fourslash/getEmitOutputSingleFile2.ts | 2 +- .../fourslash/renameForDefaultExport04.ts | 2 +- .../fourslash/renameForDefaultExport05.ts | 2 +- .../fourslash/renameForDefaultExport06.ts | 2 +- .../fourslash/renameForDefaultExport07.ts | 2 +- .../fourslash/renameForDefaultExport08.ts | 2 +- 34 files changed, 81 insertions(+), 80 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 498e8228e7a..d8504081070 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19892,7 +19892,7 @@ namespace ts { function getAmbientModules(): Symbol[] { const result: Symbol[] = []; for (const sym in globals) { - if (globals.hasOwnProperty(sym) && ambientModuleSymbolRegex.test(sym)) { + if (hasProperty(globals, sym) && ambientModuleSymbolRegex.test(sym)) { result.push(globals[sym]); } } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 61041d1ab45..3ba8f5e3b17 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2352,12 +2352,16 @@ namespace FourSlash { } export function runFourSlashTestContent(basePath: string, testType: FourSlashTestType, content: string, fileName: string): void { + // Give file paths an absolute path for the virtual file system + const absoluteBasePath = ts.combinePaths(Harness.virtualFileSystemRoot, basePath); + const absoluteFileName = ts.combinePaths(Harness.virtualFileSystemRoot, fileName); + // Parse out the files and their metadata - const testData = parseTestData(basePath, content, fileName); - const state = new TestState(basePath, testType, testData); + const testData = parseTestData(absoluteBasePath, content, absoluteFileName); + const state = new TestState(absoluteBasePath, testType, testData); const output = ts.transpileModule(content, { reportDiagnostics: true }); if (output.diagnostics.length > 0) { - throw new Error(`Syntax error in ${basePath}: ${output.diagnostics[0].messageText}`); + throw new Error(`Syntax error in ${absoluteBasePath}: ${output.diagnostics[0].messageText}`); } runCode(output.outputText, state); } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 6efb9400b37..35921f868e8 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -458,6 +458,9 @@ namespace Harness { // harness always uses one kind of new line const harnessNewLine = "\r\n"; + // Roote for file paths that are stored in a virtual file system + export const virtualFileSystemRoot = "/"; + namespace IOImpl { declare class Enumerator { public atEnd(): boolean; diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 50e3d425882..89997aeeab5 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -123,7 +123,7 @@ namespace Harness.LanguageService { } export class LanguageServiceAdapterHost { - protected virtualFileSystem: Utils.VirtualFileSystem = new Utils.VirtualFileSystem(/*root*/"c:", /*useCaseSensitiveFilenames*/false); + protected virtualFileSystem: Utils.VirtualFileSystem = new Utils.VirtualFileSystem(virtualFileSystemRoot, /*useCaseSensitiveFilenames*/false); constructor(protected cancellationToken = DefaultHostCancellationToken.Instance, protected settings = ts.getDefaultCompilerOptions()) { @@ -191,7 +191,7 @@ namespace Harness.LanguageService { } return []; } - getCurrentDirectory(): string { return ""; } + getCurrentDirectory(): string { return virtualFileSystemRoot } getDefaultLibFileName(): string { return Harness.Compiler.defaultLibFileName; } getScriptFileNames(): string[] { return this.getFilenames(); } getScriptSnapshot(fileName: string): ts.IScriptSnapshot { @@ -211,7 +211,7 @@ namespace Harness.LanguageService { readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] { return ts.matchFiles(path, extensions, exclude, include, /*useCaseSensitiveFileNames*/false, - /*currentDirectory*/"/", + this.getCurrentDirectory(), (p) => this.virtualFileSystem.getAccessibleFileSystemEntries(p)); } readFile(path: string, encoding?: string): string { @@ -220,19 +220,7 @@ namespace Harness.LanguageService { } resolvePath(path: string): string { if (!ts.isRootedDiskPath(path)) { - // An "absolute" path for fourslash is one that is contained within the tests directory - const components = ts.getNormalizedPathComponents(path, this.getCurrentDirectory()); - if (components.length) { - // If this is still a relative path after normalization (i.e. currentDirectory is relative), the root will be the empty string - if (!components[0]) { - components.splice(0, 1); - if (components[0] !== "tests") { - // If not contained within test, assume its relative to the directory containing the test files - return ts.normalizePath(ts.combinePaths("tests/cases/fourslash", components.join(ts.directorySeparator))); - } - } - return ts.normalizePath(components.join(ts.directorySeparator)); - } + path = ts.combinePaths(this.getCurrentDirectory(), path); } return ts.normalizePath(path); } diff --git a/src/harness/virtualFileSystem.ts b/src/harness/virtualFileSystem.ts index c2502bbc80f..036965f75de 100644 --- a/src/harness/virtualFileSystem.ts +++ b/src/harness/virtualFileSystem.ts @@ -111,6 +111,7 @@ namespace Utils { getFileSystemEntries() { return this.root.getFileSystemEntries(); } addDirectory(path: string) { + path = this.normalizePathRoot(path); const components = ts.getNormalizedPathComponents(path, this.currentDirectory); let directory: VirtualDirectory = this.root; for (const component of components) { @@ -124,7 +125,7 @@ namespace Utils { } addFile(path: string, content?: T) { - const absolutePath = ts.getNormalizedAbsolutePath(path, this.currentDirectory); + const absolutePath = this.normalizePathRoot(ts.getNormalizedAbsolutePath(path, this.currentDirectory)); const fileName = ts.getBaseFileName(path); const directoryPath = ts.getDirectoryPath(absolutePath); const directory = this.addDirectory(directoryPath); @@ -141,6 +142,7 @@ namespace Utils { } traversePath(path: string) { + path = this.normalizePathRoot(path); let directory: VirtualDirectory = this.root; for (const component of ts.getNormalizedPathComponents(path, this.currentDirectory)) { const entry = directory.getFileSystemEntry(component); @@ -192,7 +194,13 @@ namespace Utils { } } + normalizePathRoot(path: string) { + const components = ts.getNormalizedPathComponents(path, this.currentDirectory); + // Toss the root component + components[0] = ""; + return components.join(ts.directorySeparator); + } } export class MockParseConfigHost extends VirtualFileSystem implements ts.ParseConfigHost { diff --git a/src/services/services.ts b/src/services/services.ts index bbcc9f95040..30500189111 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3154,9 +3154,7 @@ namespace ts { writeFile: (fileName, data, writeByteOrderMark) => { }, getCurrentDirectory: () => currentDirectory, fileExists: (fileName): boolean => { - // stub missing host functionality - Debug.assert(!host.resolveModuleNames || !host.resolveTypeReferenceDirectives); - return hostCache.getOrCreateEntry(fileName) !== undefined; + return host.fileExists(fileName); }, readFile: (fileName): string => { // stub missing host functionality diff --git a/tests/baselines/reference/compileOnSaveWorksWhenEmitBlockingErrorOnOtherFile.baseline b/tests/baselines/reference/compileOnSaveWorksWhenEmitBlockingErrorOnOtherFile.baseline index f9bcca268ec..a088006d145 100644 --- a/tests/baselines/reference/compileOnSaveWorksWhenEmitBlockingErrorOnOtherFile.baseline +++ b/tests/baselines/reference/compileOnSaveWorksWhenEmitBlockingErrorOnOtherFile.baseline @@ -1,8 +1,8 @@ EmitSkipped: true Diagnostics: - Cannot write file 'tests/cases/fourslash/b.js' because it would overwrite input file. + Cannot write file '/tests/cases/fourslash/b.js' because it would overwrite input file. EmitSkipped: false -FileName : tests/cases/fourslash/a.js +FileName : /tests/cases/fourslash/a.js function foo2() { return 30; } // no error - should emit a.js diff --git a/tests/baselines/reference/getEmitOutput-pp.baseline b/tests/baselines/reference/getEmitOutput-pp.baseline index d6952e4537d..683b454a275 100644 --- a/tests/baselines/reference/getEmitOutput-pp.baseline +++ b/tests/baselines/reference/getEmitOutput-pp.baseline @@ -1,12 +1,12 @@ EmitSkipped: false -FileName : tests/cases/fourslash/shims-pp/inputFile1.js +FileName : /tests/cases/fourslash/shims-pp/inputFile1.js var x = 5; var Bar = (function () { function Bar() { } return Bar; }()); -FileName : tests/cases/fourslash/shims-pp/inputFile1.d.ts +FileName : /tests/cases/fourslash/shims-pp/inputFile1.d.ts declare var x: number; declare class Bar { x: string; @@ -14,14 +14,14 @@ declare class Bar { } EmitSkipped: false -FileName : tests/cases/fourslash/shims-pp/inputFile2.js +FileName : /tests/cases/fourslash/shims-pp/inputFile2.js var x1 = "hello world"; var Foo = (function () { function Foo() { } return Foo; }()); -FileName : tests/cases/fourslash/shims-pp/inputFile2.d.ts +FileName : /tests/cases/fourslash/shims-pp/inputFile2.d.ts declare var x1: string; declare class Foo { x: string; diff --git a/tests/baselines/reference/getEmitOutput.baseline b/tests/baselines/reference/getEmitOutput.baseline index 52c7d8a7aae..8251850e1bb 100644 --- a/tests/baselines/reference/getEmitOutput.baseline +++ b/tests/baselines/reference/getEmitOutput.baseline @@ -1,12 +1,12 @@ EmitSkipped: false -FileName : tests/cases/fourslash/shims/inputFile1.js +FileName : /tests/cases/fourslash/shims/inputFile1.js var x = 5; var Bar = (function () { function Bar() { } return Bar; }()); -FileName : tests/cases/fourslash/shims/inputFile1.d.ts +FileName : /tests/cases/fourslash/shims/inputFile1.d.ts declare var x: number; declare class Bar { x: string; @@ -14,14 +14,14 @@ declare class Bar { } EmitSkipped: false -FileName : tests/cases/fourslash/shims/inputFile2.js +FileName : /tests/cases/fourslash/shims/inputFile2.js var x1 = "hello world"; var Foo = (function () { function Foo() { } return Foo; }()); -FileName : tests/cases/fourslash/shims/inputFile2.d.ts +FileName : /tests/cases/fourslash/shims/inputFile2.d.ts declare var x1: string; declare class Foo { x: string; diff --git a/tests/baselines/reference/getEmitOutputDeclarationMultiFiles.baseline b/tests/baselines/reference/getEmitOutputDeclarationMultiFiles.baseline index 5a6fb434b1f..56eb932fb5d 100644 --- a/tests/baselines/reference/getEmitOutputDeclarationMultiFiles.baseline +++ b/tests/baselines/reference/getEmitOutputDeclarationMultiFiles.baseline @@ -1,12 +1,12 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile1.js +FileName : /tests/cases/fourslash/inputFile1.js var x = 5; var Bar = (function () { function Bar() { } return Bar; }()); -FileName : tests/cases/fourslash/inputFile1.d.ts +FileName : /tests/cases/fourslash/inputFile1.d.ts declare var x: number; declare class Bar { x: string; @@ -14,14 +14,14 @@ declare class Bar { } EmitSkipped: false -FileName : tests/cases/fourslash/inputFile2.js +FileName : /tests/cases/fourslash/inputFile2.js var x1 = "hello world"; var Foo = (function () { function Foo() { } return Foo; }()); -FileName : tests/cases/fourslash/inputFile2.d.ts +FileName : /tests/cases/fourslash/inputFile2.d.ts declare var x1: string; declare class Foo { x: string; diff --git a/tests/baselines/reference/getEmitOutputNoErrors.baseline b/tests/baselines/reference/getEmitOutputNoErrors.baseline index 461cba4ea05..6469ab3d1a7 100644 --- a/tests/baselines/reference/getEmitOutputNoErrors.baseline +++ b/tests/baselines/reference/getEmitOutputNoErrors.baseline @@ -1,5 +1,5 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile.js +FileName : /tests/cases/fourslash/inputFile.js var x; var M = (function () { function M() { diff --git a/tests/baselines/reference/getEmitOutputOnlyOneFile.baseline b/tests/baselines/reference/getEmitOutputOnlyOneFile.baseline index 4d1c88e45b4..dbcfe28afaa 100644 --- a/tests/baselines/reference/getEmitOutputOnlyOneFile.baseline +++ b/tests/baselines/reference/getEmitOutputOnlyOneFile.baseline @@ -1,5 +1,5 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile2.js +FileName : /tests/cases/fourslash/inputFile2.js var x; var Foo = (function () { function Foo() { diff --git a/tests/baselines/reference/getEmitOutputSourceMap.baseline b/tests/baselines/reference/getEmitOutputSourceMap.baseline index 65257d63607..25667b8b701 100644 --- a/tests/baselines/reference/getEmitOutputSourceMap.baseline +++ b/tests/baselines/reference/getEmitOutputSourceMap.baseline @@ -1,6 +1,6 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile.js.map -{"version":3,"file":"inputFile.js","sourceRoot":"","sources":["inputFile.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,GAAG,CAAC;AACZ,IAAI,GAAG,GAAG,aAAa,CAAC;AACxB;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile.js +FileName : /tests/cases/fourslash/inputFile.js.map +{"version":3,"file":"inputFile.js","sourceRoot":"","sources":["inputFile.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,GAAG,CAAC;AACZ,IAAI,GAAG,GAAG,aAAa,CAAC;AACxB;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : /tests/cases/fourslash/inputFile.js var x = 109; var foo = "hello world"; var M = (function () { diff --git a/tests/baselines/reference/getEmitOutputSourceRoot.baseline b/tests/baselines/reference/getEmitOutputSourceRoot.baseline index d38078a08e3..5881a61f22a 100644 --- a/tests/baselines/reference/getEmitOutputSourceRoot.baseline +++ b/tests/baselines/reference/getEmitOutputSourceRoot.baseline @@ -1,6 +1,6 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile.js.map -{"version":3,"file":"inputFile.js","sourceRoot":"sourceRootDir/","sources":["inputFile.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,GAAG,CAAC;AACZ,IAAI,GAAG,GAAG,aAAa,CAAC;AACxB;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile.js +FileName : /tests/cases/fourslash/inputFile.js.map +{"version":3,"file":"inputFile.js","sourceRoot":"sourceRootDir/","sources":["inputFile.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,GAAG,CAAC;AACZ,IAAI,GAAG,GAAG,aAAa,CAAC;AACxB;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : /tests/cases/fourslash/inputFile.js var x = 109; var foo = "hello world"; var M = (function () { diff --git a/tests/baselines/reference/getEmitOutputSourceRootMultiFiles.baseline b/tests/baselines/reference/getEmitOutputSourceRootMultiFiles.baseline index 50a6ad68605..62e3f2eb7e5 100644 --- a/tests/baselines/reference/getEmitOutputSourceRootMultiFiles.baseline +++ b/tests/baselines/reference/getEmitOutputSourceRootMultiFiles.baseline @@ -1,6 +1,6 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile1.js.map -{"version":3,"file":"inputFile1.js","sourceRoot":"sourceRootDir/","sources":["inputFile1.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,GAAG,CAAC;AACZ,IAAI,GAAG,GAAG,aAAa,CAAC;AACxB;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile1.js +FileName : /tests/cases/fourslash/inputFile1.js.map +{"version":3,"file":"inputFile1.js","sourceRoot":"sourceRootDir/","sources":["inputFile1.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,GAAG,CAAC;AACZ,IAAI,GAAG,GAAG,aAAa,CAAC;AACxB;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : /tests/cases/fourslash/inputFile1.js var x = 109; var foo = "hello world"; var M = (function () { @@ -10,8 +10,8 @@ var M = (function () { }()); //# sourceMappingURL=inputFile1.js.map EmitSkipped: false -FileName : tests/cases/fourslash/inputFile2.js.map -{"version":3,"file":"inputFile2.js","sourceRoot":"sourceRootDir/","sources":["inputFile2.ts"],"names":[],"mappings":"AAAA,IAAI,GAAG,GAAG,wBAAwB,CAAC;AACnC;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile2.js +FileName : /tests/cases/fourslash/inputFile2.js.map +{"version":3,"file":"inputFile2.js","sourceRoot":"sourceRootDir/","sources":["inputFile2.ts"],"names":[],"mappings":"AAAA,IAAI,GAAG,GAAG,wBAAwB,CAAC;AACnC;IAAA;IAGA,CAAC;IAAD,QAAC;AAAD,CAAC,AAHD,IAGC"}FileName : /tests/cases/fourslash/inputFile2.js var bar = "hello world Typescript"; var C = (function () { function C() { diff --git a/tests/baselines/reference/getEmitOutputTsxFile_Preserve.baseline b/tests/baselines/reference/getEmitOutputTsxFile_Preserve.baseline index bebd1ff1dd1..015ddba07d9 100644 --- a/tests/baselines/reference/getEmitOutputTsxFile_Preserve.baseline +++ b/tests/baselines/reference/getEmitOutputTsxFile_Preserve.baseline @@ -1,6 +1,6 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile1.js.map -{"version":3,"file":"inputFile1.js","sourceRoot":"","sources":["inputFile1.ts"],"names":[],"mappings":"AAAA,kBAAkB;AACjB,IAAI,CAAC,GAAW,CAAC,CAAC;AAClB;IAAA;IAGA,CAAC;IAAD,UAAC;AAAD,CAAC,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile1.js +FileName : /tests/cases/fourslash/inputFile1.js.map +{"version":3,"file":"inputFile1.js","sourceRoot":"","sources":["inputFile1.ts"],"names":[],"mappings":"AAAA,kBAAkB;AACjB,IAAI,CAAC,GAAW,CAAC,CAAC;AAClB;IAAA;IAGA,CAAC;IAAD,UAAC;AAAD,CAAC,AAHD,IAGC"}FileName : /tests/cases/fourslash/inputFile1.js // regular ts file var t = 5; var Bar = (function () { @@ -8,7 +8,7 @@ var Bar = (function () { } return Bar; }()); -//# sourceMappingURL=inputFile1.js.mapFileName : tests/cases/fourslash/inputFile1.d.ts +//# sourceMappingURL=inputFile1.js.mapFileName : /tests/cases/fourslash/inputFile1.d.ts declare var t: number; declare class Bar { x: string; @@ -16,11 +16,11 @@ declare class Bar { } EmitSkipped: false -FileName : tests/cases/fourslash/inputFile2.jsx.map -{"version":3,"file":"inputFile2.jsx","sourceRoot":"","sources":["inputFile2.tsx"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,QAAQ,CAAC;AACjB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,CAAC,CAAC,EAAG,CAAA"}FileName : tests/cases/fourslash/inputFile2.jsx +FileName : /tests/cases/fourslash/inputFile2.jsx.map +{"version":3,"file":"inputFile2.jsx","sourceRoot":"","sources":["inputFile2.tsx"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,QAAQ,CAAC;AACjB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,CAAC,CAAC,EAAG,CAAA"}FileName : /tests/cases/fourslash/inputFile2.jsx var y = "my div"; var x =
; -//# sourceMappingURL=inputFile2.jsx.mapFileName : tests/cases/fourslash/inputFile2.d.ts +//# sourceMappingURL=inputFile2.jsx.mapFileName : /tests/cases/fourslash/inputFile2.d.ts declare var y: string; declare var x: any; diff --git a/tests/baselines/reference/getEmitOutputTsxFile_React.baseline b/tests/baselines/reference/getEmitOutputTsxFile_React.baseline index 5b668fd65e8..b78cbecf6c3 100644 --- a/tests/baselines/reference/getEmitOutputTsxFile_React.baseline +++ b/tests/baselines/reference/getEmitOutputTsxFile_React.baseline @@ -1,6 +1,6 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile1.js.map -{"version":3,"file":"inputFile1.js","sourceRoot":"","sources":["inputFile1.ts"],"names":[],"mappings":"AAAA,kBAAkB;AACjB,IAAI,CAAC,GAAW,CAAC,CAAC;AAClB;IAAA;IAGA,CAAC;IAAD,UAAC;AAAD,CAAC,AAHD,IAGC"}FileName : tests/cases/fourslash/inputFile1.js +FileName : /tests/cases/fourslash/inputFile1.js.map +{"version":3,"file":"inputFile1.js","sourceRoot":"","sources":["inputFile1.ts"],"names":[],"mappings":"AAAA,kBAAkB;AACjB,IAAI,CAAC,GAAW,CAAC,CAAC;AAClB;IAAA;IAGA,CAAC;IAAD,UAAC;AAAD,CAAC,AAHD,IAGC"}FileName : /tests/cases/fourslash/inputFile1.js // regular ts file var t = 5; var Bar = (function () { @@ -8,7 +8,7 @@ var Bar = (function () { } return Bar; }()); -//# sourceMappingURL=inputFile1.js.mapFileName : tests/cases/fourslash/inputFile1.d.ts +//# sourceMappingURL=inputFile1.js.mapFileName : /tests/cases/fourslash/inputFile1.d.ts declare var t: number; declare class Bar { x: string; @@ -16,11 +16,11 @@ declare class Bar { } EmitSkipped: false -FileName : tests/cases/fourslash/inputFile2.js.map -{"version":3,"file":"inputFile2.js","sourceRoot":"","sources":["inputFile2.tsx"],"names":[],"mappings":"AACA,IAAI,CAAC,GAAG,QAAQ,CAAC;AACjB,IAAI,CAAC,GAAG,qBAAC,GAAG,IAAC,IAAI,EAAG,CAAE,EAAG,CAAA"}FileName : tests/cases/fourslash/inputFile2.js +FileName : /tests/cases/fourslash/inputFile2.js.map +{"version":3,"file":"inputFile2.js","sourceRoot":"","sources":["inputFile2.tsx"],"names":[],"mappings":"AACA,IAAI,CAAC,GAAG,QAAQ,CAAC;AACjB,IAAI,CAAC,GAAG,qBAAC,GAAG,IAAC,IAAI,EAAG,CAAE,EAAG,CAAA"}FileName : /tests/cases/fourslash/inputFile2.js var y = "my div"; var x = React.createElement("div", {name: y}); -//# sourceMappingURL=inputFile2.js.mapFileName : tests/cases/fourslash/inputFile2.d.ts +//# sourceMappingURL=inputFile2.js.mapFileName : /tests/cases/fourslash/inputFile2.d.ts declare var React: any; declare var y: string; declare var x: any; diff --git a/tests/baselines/reference/getEmitOutputWithDeclarationFile.baseline b/tests/baselines/reference/getEmitOutputWithDeclarationFile.baseline index af4ccd10580..a10ef5967b0 100644 --- a/tests/baselines/reference/getEmitOutputWithDeclarationFile.baseline +++ b/tests/baselines/reference/getEmitOutputWithDeclarationFile.baseline @@ -1,7 +1,7 @@ EmitSkipped: false EmitSkipped: false -FileName : tests/cases/fourslash/inputFile2.js +FileName : /tests/cases/fourslash/inputFile2.js var x1 = "hello world"; var Foo = (function () { function Foo() { diff --git a/tests/baselines/reference/getEmitOutputWithDeclarationFile2.baseline b/tests/baselines/reference/getEmitOutputWithDeclarationFile2.baseline index 8e43219e005..5c865321691 100644 --- a/tests/baselines/reference/getEmitOutputWithDeclarationFile2.baseline +++ b/tests/baselines/reference/getEmitOutputWithDeclarationFile2.baseline @@ -1,7 +1,7 @@ EmitSkipped: false EmitSkipped: false -FileName : tests/cases/fourslash/inputFile2.js +FileName : /tests/cases/fourslash/inputFile2.js "use strict"; var Foo = (function () { function Foo() { @@ -11,6 +11,6 @@ var Foo = (function () { exports.Foo = Foo; EmitSkipped: false -FileName : tests/cases/fourslash/inputFile3.js +FileName : /tests/cases/fourslash/inputFile3.js var x = "hello"; diff --git a/tests/baselines/reference/getEmitOutputWithEarlySyntacticErrors.baseline b/tests/baselines/reference/getEmitOutputWithEarlySyntacticErrors.baseline index 9e2e9978f46..6609554a299 100644 --- a/tests/baselines/reference/getEmitOutputWithEarlySyntacticErrors.baseline +++ b/tests/baselines/reference/getEmitOutputWithEarlySyntacticErrors.baseline @@ -1,5 +1,5 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile1.js +FileName : /tests/cases/fourslash/inputFile1.js // File contains early errors. All outputs should be skipped. var uninitialized_const_error; diff --git a/tests/baselines/reference/getEmitOutputWithEmitterErrors.baseline b/tests/baselines/reference/getEmitOutputWithEmitterErrors.baseline index 8ba493e04b7..326d547ae1b 100644 --- a/tests/baselines/reference/getEmitOutputWithEmitterErrors.baseline +++ b/tests/baselines/reference/getEmitOutputWithEmitterErrors.baseline @@ -1,7 +1,7 @@ EmitSkipped: true Diagnostics: Exported variable 'foo' has or is using private name 'C'. -FileName : tests/cases/fourslash/inputFile.js +FileName : /tests/cases/fourslash/inputFile.js var M; (function (M) { var C = (function () { diff --git a/tests/baselines/reference/getEmitOutputWithEmitterErrors2.baseline b/tests/baselines/reference/getEmitOutputWithEmitterErrors2.baseline index bf12bd35548..a181526eb27 100644 --- a/tests/baselines/reference/getEmitOutputWithEmitterErrors2.baseline +++ b/tests/baselines/reference/getEmitOutputWithEmitterErrors2.baseline @@ -1,7 +1,7 @@ EmitSkipped: true Diagnostics: Exported variable 'foo' has or is using private name 'C'. -FileName : tests/cases/fourslash/inputFile.js +FileName : /tests/cases/fourslash/inputFile.js define(["require", "exports"], function (require, exports) { "use strict"; var C = (function () { diff --git a/tests/baselines/reference/getEmitOutputWithSemanticErrors.baseline b/tests/baselines/reference/getEmitOutputWithSemanticErrors.baseline index b6249822131..55a58ea041f 100644 --- a/tests/baselines/reference/getEmitOutputWithSemanticErrors.baseline +++ b/tests/baselines/reference/getEmitOutputWithSemanticErrors.baseline @@ -1,4 +1,4 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile.js +FileName : /tests/cases/fourslash/inputFile.js var x = "hello world"; diff --git a/tests/baselines/reference/getEmitOutputWithSemanticErrors2.baseline b/tests/baselines/reference/getEmitOutputWithSemanticErrors2.baseline index 396e220bdb6..b5848da3457 100644 --- a/tests/baselines/reference/getEmitOutputWithSemanticErrors2.baseline +++ b/tests/baselines/reference/getEmitOutputWithSemanticErrors2.baseline @@ -1,6 +1,6 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile.js +FileName : /tests/cases/fourslash/inputFile.js var x = "hello world"; -FileName : tests/cases/fourslash/inputFile.d.ts +FileName : /tests/cases/fourslash/inputFile.d.ts declare var x: number; diff --git a/tests/baselines/reference/getEmitOutputWithSemanticErrorsForMultipleFiles.baseline b/tests/baselines/reference/getEmitOutputWithSemanticErrorsForMultipleFiles.baseline index 7f012b6a546..4c22a7e3f93 100644 --- a/tests/baselines/reference/getEmitOutputWithSemanticErrorsForMultipleFiles.baseline +++ b/tests/baselines/reference/getEmitOutputWithSemanticErrorsForMultipleFiles.baseline @@ -1,8 +1,8 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile1.js +FileName : /tests/cases/fourslash/inputFile1.js // File to emit, does not contain semantic errors // expected to be emitted correctelly regardless of the semantic errors in the other file var noErrors = true; -FileName : tests/cases/fourslash/inputFile1.d.ts +FileName : /tests/cases/fourslash/inputFile1.d.ts declare var noErrors: boolean; diff --git a/tests/baselines/reference/getEmitOutputWithSyntacticErrorsForMultipleFiles.baseline b/tests/baselines/reference/getEmitOutputWithSyntacticErrorsForMultipleFiles.baseline index 701b275eb31..f51782cc75d 100644 --- a/tests/baselines/reference/getEmitOutputWithSyntacticErrorsForMultipleFiles.baseline +++ b/tests/baselines/reference/getEmitOutputWithSyntacticErrorsForMultipleFiles.baseline @@ -1,5 +1,5 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile1.js +FileName : /tests/cases/fourslash/inputFile1.js // File to emit, does not contain syntactic errors // expected to be emitted correctelly regardless of the syntactic errors in the other file var noErrors = true; diff --git a/tests/baselines/reference/getEmitOutputWithSyntaxErrors.baseline b/tests/baselines/reference/getEmitOutputWithSyntaxErrors.baseline index 2c341821fb0..8aa81ba60ce 100644 --- a/tests/baselines/reference/getEmitOutputWithSyntaxErrors.baseline +++ b/tests/baselines/reference/getEmitOutputWithSyntaxErrors.baseline @@ -1,4 +1,4 @@ EmitSkipped: false -FileName : tests/cases/fourslash/inputFile.js +FileName : /tests/cases/fourslash/inputFile.js var x; diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts index df30b925d11..ba9f95a08c2 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts @@ -2,7 +2,7 @@ // Should give completions for directories that are merged via the rootDirs compiler option -// @rootDirs: sub/src1,src2 +// @rootDirs: tests/cases/fourslash/sub/src1,tests/cases/fourslash/src2 // @Filename: src2/test0.ts //// import * as foo1 from "./mo/*import_as0*/ diff --git a/tests/cases/fourslash/getEmitOutputSingleFile2.ts b/tests/cases/fourslash/getEmitOutputSingleFile2.ts index 5dd0276a936..5d616b19e13 100644 --- a/tests/cases/fourslash/getEmitOutputSingleFile2.ts +++ b/tests/cases/fourslash/getEmitOutputSingleFile2.ts @@ -4,7 +4,7 @@ // @module: CommonJS // @declaration: true // @out: declSingleFile.js -// @outDir: tests/cases/fourslash/ +// @outDir: /tests/cases/fourslash/ // @Filename: inputFile1.ts //// var x: number = 5; diff --git a/tests/cases/fourslash/renameForDefaultExport04.ts b/tests/cases/fourslash/renameForDefaultExport04.ts index ba40374e262..b225c47b2b2 100644 --- a/tests/cases/fourslash/renameForDefaultExport04.ts +++ b/tests/cases/fourslash/renameForDefaultExport04.ts @@ -12,4 +12,4 @@ ////var y = new DefaultExportedClass; goTo.marker(); -verify.renameInfoSucceeded("DefaultExportedClass", '"tests/cases/fourslash/foo".DefaultExportedClass'); \ No newline at end of file +verify.renameInfoSucceeded("DefaultExportedClass", '"/tests/cases/fourslash/foo".DefaultExportedClass'); \ No newline at end of file diff --git a/tests/cases/fourslash/renameForDefaultExport05.ts b/tests/cases/fourslash/renameForDefaultExport05.ts index 099f878bda1..2e5fcd847d8 100644 --- a/tests/cases/fourslash/renameForDefaultExport05.ts +++ b/tests/cases/fourslash/renameForDefaultExport05.ts @@ -12,4 +12,4 @@ ////var y = new DefaultExportedClass; goTo.marker(); -verify.renameInfoSucceeded("DefaultExportedClass", '"tests/cases/fourslash/foo".DefaultExportedClass'); \ No newline at end of file +verify.renameInfoSucceeded("DefaultExportedClass", '"/tests/cases/fourslash/foo".DefaultExportedClass'); \ No newline at end of file diff --git a/tests/cases/fourslash/renameForDefaultExport06.ts b/tests/cases/fourslash/renameForDefaultExport06.ts index 3ec067b7029..ba7f8cac7d7 100644 --- a/tests/cases/fourslash/renameForDefaultExport06.ts +++ b/tests/cases/fourslash/renameForDefaultExport06.ts @@ -12,4 +12,4 @@ ////var y = new /**/[|DefaultExportedClass|]; goTo.marker(); -verify.renameInfoSucceeded("DefaultExportedClass", '"tests/cases/fourslash/foo".DefaultExportedClass'); \ No newline at end of file +verify.renameInfoSucceeded("DefaultExportedClass", '"/tests/cases/fourslash/foo".DefaultExportedClass'); \ No newline at end of file diff --git a/tests/cases/fourslash/renameForDefaultExport07.ts b/tests/cases/fourslash/renameForDefaultExport07.ts index b65f348f2a0..9e3b4741b55 100644 --- a/tests/cases/fourslash/renameForDefaultExport07.ts +++ b/tests/cases/fourslash/renameForDefaultExport07.ts @@ -13,4 +13,4 @@ ////var y = DefaultExportedFunction(); goTo.marker(); -verify.renameInfoSucceeded("DefaultExportedFunction", '"tests/cases/fourslash/foo".DefaultExportedFunction'); \ No newline at end of file +verify.renameInfoSucceeded("DefaultExportedFunction", '"/tests/cases/fourslash/foo".DefaultExportedFunction'); \ No newline at end of file diff --git a/tests/cases/fourslash/renameForDefaultExport08.ts b/tests/cases/fourslash/renameForDefaultExport08.ts index 9b3f23db2c1..ad8a2b8242f 100644 --- a/tests/cases/fourslash/renameForDefaultExport08.ts +++ b/tests/cases/fourslash/renameForDefaultExport08.ts @@ -13,4 +13,4 @@ ////var y = DefaultExportedFunction(); goTo.marker(); -verify.renameInfoSucceeded("DefaultExportedFunction", '"tests/cases/fourslash/foo".DefaultExportedFunction'); \ No newline at end of file +verify.renameInfoSucceeded("DefaultExportedFunction", '"/tests/cases/fourslash/foo".DefaultExportedFunction'); \ No newline at end of file From 310bce44591f2f8208a4381d9b15664b20c403f2 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Tue, 16 Aug 2016 15:18:25 -0700 Subject: [PATCH 26/73] Removing resolvePath from language service host --- src/harness/harnessLanguageService.ts | 9 --------- src/server/editorServices.ts | 5 ----- src/services/services.ts | 24 +++++++++--------------- src/services/shims.ts | 5 ----- 4 files changed, 9 insertions(+), 34 deletions(-) diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 89997aeeab5..9a6f9ef77ca 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -218,12 +218,6 @@ namespace Harness.LanguageService { const snapshot = this.getScriptSnapshot(path); return snapshot.getText(0, snapshot.getLength()); } - resolvePath(path: string): string { - if (!ts.isRootedDiskPath(path)) { - path = ts.combinePaths(this.getCurrentDirectory(), path); - } - return ts.normalizePath(path); - } log(s: string): void { } @@ -329,9 +323,6 @@ namespace Harness.LanguageService { const snapshot = this.nativeHost.getScriptSnapshot(fileName); return snapshot && snapshot.getText(0, snapshot.getLength()); } - resolvePath(path: string): string { - return this.nativeHost.resolvePath(path); - } log(s: string): void { this.nativeHost.log(s); } trace(s: string): void { this.nativeHost.trace(s); } error(s: string): void { this.nativeHost.error(s); } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 6b2cdb804c4..3a1a4556e03 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -306,11 +306,6 @@ namespace ts.server { throw new Error("No script with name '" + filename + "'"); } - resolvePath(path: string): string { - const result = this.host.resolvePath(path); - return result; - } - fileExists(path: string): boolean { const result = this.host.fileExists(path); return result; diff --git a/src/services/services.ts b/src/services/services.ts index 30500189111..6108e1e976a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1159,7 +1159,6 @@ namespace ts { useCaseSensitiveFileNames?(): boolean; readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[]; - resolvePath(path: string): string; readFile(path: string, encoding?: string): string; fileExists(path: string): boolean; @@ -4589,7 +4588,7 @@ namespace ts { } const absolutePath = normalizeAndPreserveTrailingSlash(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment)); - const baseDirectory = host.resolvePath(getDirectoryPath(absolutePath)); + const baseDirectory = getDirectoryPath(absolutePath); const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames()); if (directoryProbablyExists(baseDirectory, host)) { @@ -4827,7 +4826,14 @@ namespace ts { }); } else if (host.getDirectories && options.typeRoots) { - const absoluteRoots = map(options.typeRoots, rootDirectory => getAbsoluteProjectPath(rootDirectory, host, options.project)); + const absoluteRoots = map(options.typeRoots, rootDirectory => { + if (isRootedDiskPath(rootDirectory)) { + return normalizePath(rootDirectory); + } + + const basePath = options.project || host.getCurrentDirectory(); + return normalizePath(combinePaths(basePath, rootDirectory)); + }); forEach(absoluteRoots, absoluteRoot => getCompletionEntriesFromDirectories(host, options, absoluteRoot, result)); } @@ -4842,18 +4848,6 @@ namespace ts { return result; } - function getAbsoluteProjectPath(path: string, host: LanguageServiceHost, projectDir?: string) { - if (isRootedDiskPath(path)) { - return normalizePath(path); - } - - if (projectDir) { - return normalizePath(combinePaths(projectDir, path)); - } - - return normalizePath(host.resolvePath(path)); - } - function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, result: ImportCompletionEntry[]) { if (host.getDirectories && directoryProbablyExists(directory, host)) { forEach(host.getDirectories(directory), typeDirectory => { diff --git a/src/services/shims.ts b/src/services/shims.ts index 401137a28e9..b5ae306c71f 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -69,7 +69,6 @@ namespace ts { readDirectory(rootDir: string, extension: string, basePaths?: string, excludeEx?: string, includeFileEx?: string, includeDirEx?: string, depth?: number): string; readFile(path: string, encoding?: string): string; - resolvePath(path: string): string; fileExists(path: string): boolean; getModuleResolutionsForFile?(fileName: string): string; @@ -446,10 +445,6 @@ namespace ts { return this.shimHost.readFile(path, encoding); } - public resolvePath(path: string): string { - return this.shimHost.resolvePath(path); - } - public fileExists(path: string): boolean { return this.shimHost.fileExists(path); } From cf7feb3faace557964447214cae150ae92ec7a80 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Fri, 19 Aug 2016 16:49:55 -0700 Subject: [PATCH 27/73] Responding to PR feedback --- src/compiler/program.ts | 2 +- src/harness/harness.ts | 6 +- src/harness/harnessLanguageService.ts | 8 +- src/harness/virtualFileSystem.ts | 84 +++++++++---------- src/services/services.ts | 2 +- ...mpletionForStringLiteralRelativeImport3.ts | 18 ++-- .../completionForTripleSlashReference3.ts | 6 +- 7 files changed, 59 insertions(+), 67 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index e8b8e0aa359..834cba1358a 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -10,7 +10,7 @@ namespace ts { const defaultTypeRoots = ["node_modules/@types"]; - export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName="tsconfig.json"): string { + export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "tsconfig.json"): string { while (true) { const fileName = combinePaths(searchPath, configName); if (fileExists(fileName)) { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 35921f868e8..41227c9bff2 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -458,7 +458,7 @@ namespace Harness { // harness always uses one kind of new line const harnessNewLine = "\r\n"; - // Roote for file paths that are stored in a virtual file system + // Root for file paths that are stored in a virtual file system export const virtualFileSystemRoot = "/"; namespace IOImpl { @@ -752,14 +752,14 @@ namespace Harness { } export function readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]) { - const fs = new Utils.VirtualFileSystem(path, useCaseSensitiveFileNames()); + const fs = new Utils.VirtualFileSystem(path, useCaseSensitiveFileNames()); for (const file of listFiles(path)) { fs.addFile(file); } return ts.matchFiles(path, extension, exclude, include, useCaseSensitiveFileNames(), getCurrentDirectory(), path => { const entry = fs.traversePath(path); if (entry && entry.isDirectory()) { - const directory = >entry; + const directory = entry; return { files: ts.map(directory.getFiles(), f => f.name), directories: ts.map(directory.getDirectories(), d => d.name) diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 9a6f9ef77ca..a694002d703 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -123,7 +123,7 @@ namespace Harness.LanguageService { } export class LanguageServiceAdapterHost { - protected virtualFileSystem: Utils.VirtualFileSystem = new Utils.VirtualFileSystem(virtualFileSystemRoot, /*useCaseSensitiveFilenames*/false); + protected virtualFileSystem: Utils.VirtualFileSystem = new Utils.VirtualFileSystem(virtualFileSystemRoot, /*useCaseSensitiveFilenames*/false); constructor(protected cancellationToken = DefaultHostCancellationToken.Instance, protected settings = ts.getDefaultCompilerOptions()) { @@ -148,7 +148,7 @@ namespace Harness.LanguageService { public getScriptInfo(fileName: string): ScriptInfo { const fileEntry = this.virtualFileSystem.traversePath(fileName); - return fileEntry && fileEntry.isFile() ? (>fileEntry).content : undefined; + return fileEntry && fileEntry.isFile() ? (fileEntry).content : undefined; } public addScript(fileName: string, content: string, isRootFile: boolean): void { @@ -187,11 +187,11 @@ namespace Harness.LanguageService { getDirectories(path: string): string[] { const dir = this.virtualFileSystem.traversePath(path); if (dir && dir.isDirectory()) { - return ts.map((>dir).getDirectories(), (d) => ts.combinePaths(path, d.name)); + return ts.map((dir).getDirectories(), (d) => ts.combinePaths(path, d.name)); } return []; } - getCurrentDirectory(): string { return virtualFileSystemRoot } + getCurrentDirectory(): string { return virtualFileSystemRoot; } getDefaultLibFileName(): string { return Harness.Compiler.defaultLibFileName; } getScriptFileNames(): string[] { return this.getFilenames(); } getScriptSnapshot(fileName: string): ts.IScriptSnapshot { diff --git a/src/harness/virtualFileSystem.ts b/src/harness/virtualFileSystem.ts index 036965f75de..b6d234acd59 100644 --- a/src/harness/virtualFileSystem.ts +++ b/src/harness/virtualFileSystem.ts @@ -1,11 +1,11 @@ /// /// namespace Utils { - export class VirtualFileSystemEntry { - fileSystem: VirtualFileSystem; + export class VirtualFileSystemEntry { + fileSystem: VirtualFileSystem; name: string; - constructor(fileSystem: VirtualFileSystem, name: string) { + constructor(fileSystem: VirtualFileSystem, name: string) { this.fileSystem = fileSystem; this.name = name; } @@ -15,15 +15,15 @@ namespace Utils { isFileSystem() { return false; } } - export class VirtualFile extends VirtualFileSystemEntry { - content: T; + export class VirtualFile extends VirtualFileSystemEntry { + content?: Harness.LanguageService.ScriptInfo; isFile() { return true; } } - export abstract class VirtualFileSystemContainer extends VirtualFileSystemEntry { - abstract getFileSystemEntries(): VirtualFileSystemEntry[]; + export abstract class VirtualFileSystemContainer extends VirtualFileSystemEntry { + abstract getFileSystemEntries(): VirtualFileSystemEntry[]; - getFileSystemEntry(name: string): VirtualFileSystemEntry { + getFileSystemEntry(name: string): VirtualFileSystemEntry { for (const entry of this.getFileSystemEntries()) { if (this.fileSystem.sameName(entry.name, name)) { return entry; @@ -32,57 +32,57 @@ namespace Utils { return undefined; } - getDirectories(): VirtualDirectory[] { - return []>ts.filter(this.getFileSystemEntries(), entry => entry.isDirectory()); + getDirectories(): VirtualDirectory[] { + return ts.filter(this.getFileSystemEntries(), entry => entry.isDirectory()); } - getFiles(): VirtualFile[] { - return []>ts.filter(this.getFileSystemEntries(), entry => entry.isFile()); + getFiles(): VirtualFile[] { + return ts.filter(this.getFileSystemEntries(), entry => entry.isFile()); } - getDirectory(name: string): VirtualDirectory { + getDirectory(name: string): VirtualDirectory { const entry = this.getFileSystemEntry(name); - return entry.isDirectory() ? >entry : undefined; + return entry.isDirectory() ? entry : undefined; } - getFile(name: string): VirtualFile { + getFile(name: string): VirtualFile { const entry = this.getFileSystemEntry(name); - return entry.isFile() ? >entry : undefined; + return entry.isFile() ? entry : undefined; } } - export class VirtualDirectory extends VirtualFileSystemContainer { - private entries: VirtualFileSystemEntry[] = []; + export class VirtualDirectory extends VirtualFileSystemContainer { + private entries: VirtualFileSystemEntry[] = []; isDirectory() { return true; } getFileSystemEntries() { return this.entries.slice(); } - addDirectory(name: string): VirtualDirectory { + addDirectory(name: string): VirtualDirectory { const entry = this.getFileSystemEntry(name); if (entry === undefined) { - const directory = new VirtualDirectory(this.fileSystem, name); + const directory = new VirtualDirectory(this.fileSystem, name); this.entries.push(directory); return directory; } else if (entry.isDirectory()) { - return >entry; + return entry; } else { return undefined; } } - addFile(name: string, content?: T): VirtualFile { + addFile(name: string, content?: Harness.LanguageService.ScriptInfo): VirtualFile { const entry = this.getFileSystemEntry(name); if (entry === undefined) { - const file = new VirtualFile(this.fileSystem, name); + const file = new VirtualFile(this.fileSystem, name); file.content = content; this.entries.push(file); return file; } else if (entry.isFile()) { - const file = >entry; + const file = entry; file.content = content; return file; } @@ -92,8 +92,8 @@ namespace Utils { } } - export class VirtualFileSystem extends VirtualFileSystemContainer { - private root: VirtualDirectory; + export class VirtualFileSystem extends VirtualFileSystemContainer { + private root: VirtualDirectory; currentDirectory: string; useCaseSensitiveFileNames: boolean; @@ -101,7 +101,7 @@ namespace Utils { constructor(currentDirectory: string, useCaseSensitiveFileNames: boolean) { super(undefined, ""); this.fileSystem = this; - this.root = new VirtualDirectory(this, ""); + this.root = new VirtualDirectory(this, ""); this.currentDirectory = currentDirectory; this.useCaseSensitiveFileNames = useCaseSensitiveFileNames; } @@ -111,9 +111,9 @@ namespace Utils { getFileSystemEntries() { return this.root.getFileSystemEntries(); } addDirectory(path: string) { - path = this.normalizePathRoot(path); + path = ts.normalizePath(path); const components = ts.getNormalizedPathComponents(path, this.currentDirectory); - let directory: VirtualDirectory = this.root; + let directory: VirtualDirectory = this.root; for (const component of components) { directory = directory.addDirectory(component); if (directory === undefined) { @@ -124,8 +124,8 @@ namespace Utils { return directory; } - addFile(path: string, content?: T) { - const absolutePath = this.normalizePathRoot(ts.getNormalizedAbsolutePath(path, this.currentDirectory)); + addFile(path: string, content?: Harness.LanguageService.ScriptInfo) { + const absolutePath = ts.normalizePath(ts.getNormalizedAbsolutePath(path, this.currentDirectory)); const fileName = ts.getBaseFileName(path); const directoryPath = ts.getDirectoryPath(absolutePath); const directory = this.addDirectory(directoryPath); @@ -142,15 +142,15 @@ namespace Utils { } traversePath(path: string) { - path = this.normalizePathRoot(path); - let directory: VirtualDirectory = this.root; + path = ts.normalizePath(path); + let directory: VirtualDirectory = this.root; for (const component of ts.getNormalizedPathComponents(path, this.currentDirectory)) { const entry = directory.getFileSystemEntry(component); if (entry === undefined) { return undefined; } else if (entry.isDirectory()) { - directory = >entry; + directory = entry; } else { return entry; @@ -168,7 +168,7 @@ namespace Utils { getAccessibleFileSystemEntries(path: string) { const entry = this.traversePath(path); if (entry && entry.isDirectory()) { - const directory = >entry; + const directory = entry; return { files: ts.map(directory.getFiles(), f => f.name), directories: ts.map(directory.getDirectories(), d => d.name) @@ -178,11 +178,11 @@ namespace Utils { } getAllFileEntries() { - const fileEntries: VirtualFile[] = []; + const fileEntries: VirtualFile[] = []; getFilesRecursive(this.root, fileEntries); return fileEntries; - function getFilesRecursive(dir: VirtualDirectory, result: VirtualFile[]) { + function getFilesRecursive(dir: VirtualDirectory, result: VirtualFile[]) { const files = dir.getFiles(); const dirs = dir.getDirectories(); for (const file of files) { @@ -193,17 +193,9 @@ namespace Utils { } } } - - normalizePathRoot(path: string) { - const components = ts.getNormalizedPathComponents(path, this.currentDirectory); - - // Toss the root component - components[0] = ""; - return components.join(ts.directorySeparator); - } } - export class MockParseConfigHost extends VirtualFileSystem implements ts.ParseConfigHost { + export class MockParseConfigHost extends VirtualFileSystem implements ts.ParseConfigHost { constructor(currentDirectory: string, ignoreCase: boolean, files: string[]) { super(currentDirectory, ignoreCase); for (const file of files) { diff --git a/src/services/services.ts b/src/services/services.ts index 6108e1e976a..49fc48b6ea8 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1171,7 +1171,7 @@ namespace ts { resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; directoryExists?(directoryName: string): boolean; - /** + /* * getDirectories is also required for full import and type reference completions. Without it defined, certain * completions will not be provided */ diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts index a2035ee7fdb..82c3ea7d1e4 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts @@ -3,17 +3,17 @@ // Should give completions for absolute paths // @Filename: tests/test0.ts -//// import * as foo1 from "c:/tests/cases/f/*import_as0*/ -//// import * as foo2 from "c:/tests/cases/fourslash/*import_as1*/ -//// import * as foo3 from "c:/tests/cases/fourslash//*import_as2*/ +//// import * as foo1 from "/tests/cases/f/*import_as0*/ +//// import * as foo2 from "/tests/cases/fourslash/*import_as1*/ +//// import * as foo3 from "/tests/cases/fourslash//*import_as2*/ -//// import foo4 = require("c:/tests/cases/f/*import_equals0*/ -//// import foo5 = require("c:/tests/cases/fourslash/*import_equals1*/ -//// import foo6 = require("c:/tests/cases/fourslash//*import_equals2*/ +//// import foo4 = require("/tests/cases/f/*import_equals0*/ +//// import foo5 = require("/tests/cases/fourslash/*import_equals1*/ +//// import foo6 = require("/tests/cases/fourslash//*import_equals2*/ -//// var foo7 = require("c:/tests/cases/f/*require0*/ -//// var foo8 = require("c:/tests/cases/fourslash/*require1*/ -//// var foo9 = require("c:/tests/cases/fourslash//*require2*/ +//// var foo7 = require("/tests/cases/f/*require0*/ +//// var foo8 = require("/tests/cases/fourslash/*require1*/ +//// var foo9 = require("/tests/cases/fourslash//*require2*/ // @Filename: f1.ts //// /*f1*/ diff --git a/tests/cases/fourslash/completionForTripleSlashReference3.ts b/tests/cases/fourslash/completionForTripleSlashReference3.ts index 2f92de19a21..d27d0e658c2 100644 --- a/tests/cases/fourslash/completionForTripleSlashReference3.ts +++ b/tests/cases/fourslash/completionForTripleSlashReference3.ts @@ -3,13 +3,13 @@ // Should give completions for absolute paths // @Filename: tests/test0.ts -//// /// Date: Fri, 19 Aug 2016 17:06:59 -0700 Subject: [PATCH 28/73] Removing hasProperty check --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3cf7528abbe..603d9c6dea0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19958,7 +19958,7 @@ namespace ts { function getAmbientModules(): Symbol[] { const result: Symbol[] = []; for (const sym in globals) { - if (hasProperty(globals, sym) && ambientModuleSymbolRegex.test(sym)) { + if (ambientModuleSymbolRegex.test(sym)) { result.push(globals[sym]); } } From 0ebd19618d4b8b5aa64a0c7b2676898cef09308e Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Fri, 19 Aug 2016 17:17:49 -0700 Subject: [PATCH 29/73] Fixing regex for triple slash references --- src/services/services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index a10383ae09f..4c199de1869 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2070,7 +2070,7 @@ namespace ts { * for completions. * For example, this matches /// Date: Mon, 22 Aug 2016 23:10:05 -0700 Subject: [PATCH 30/73] Added tests. --- .../es6/templates/taggedTemplateWithConstructableTag01.ts | 3 +++ .../es6/templates/taggedTemplateWithConstructableTag02.ts | 6 ++++++ 2 files changed, 9 insertions(+) create mode 100644 tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts create mode 100644 tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts diff --git a/tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts b/tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts new file mode 100644 index 00000000000..996f5b80298 --- /dev/null +++ b/tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts @@ -0,0 +1,3 @@ +class CtorTag { } + +CtorTag `Hello world!`; \ No newline at end of file diff --git a/tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts b/tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts new file mode 100644 index 00000000000..7c6f009bf23 --- /dev/null +++ b/tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts @@ -0,0 +1,6 @@ +interface I { + new (...args: any[]): string; + new (): number; +} +var tag: I; +tag `Hello world!`; \ No newline at end of file From e7798c002ef4bb86d29bf7262aadb03b398aadba Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 22 Aug 2016 23:10:16 -0700 Subject: [PATCH 31/73] Accepted baselines. --- .../taggedTemplateWithConstructableTag01.js | 13 +++++++++++++ ...aggedTemplateWithConstructableTag01.symbols | 7 +++++++ .../taggedTemplateWithConstructableTag01.types | 9 +++++++++ .../taggedTemplateWithConstructableTag02.js | 12 ++++++++++++ ...aggedTemplateWithConstructableTag02.symbols | 16 ++++++++++++++++ .../taggedTemplateWithConstructableTag02.types | 18 ++++++++++++++++++ 6 files changed, 75 insertions(+) create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag01.js create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag01.symbols create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag01.types create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag02.js create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag02.symbols create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag02.types diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag01.js b/tests/baselines/reference/taggedTemplateWithConstructableTag01.js new file mode 100644 index 00000000000..dbcb70edf00 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag01.js @@ -0,0 +1,13 @@ +//// [taggedTemplateWithConstructableTag01.ts] +class CtorTag { } + +CtorTag `Hello world!`; + +//// [taggedTemplateWithConstructableTag01.js] +var CtorTag = (function () { + function CtorTag() { + } + return CtorTag; +}()); +(_a = ["Hello world!"], _a.raw = ["Hello world!"], CtorTag(_a)); +var _a; diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag01.symbols b/tests/baselines/reference/taggedTemplateWithConstructableTag01.symbols new file mode 100644 index 00000000000..340d5887808 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag01.symbols @@ -0,0 +1,7 @@ +=== tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts === +class CtorTag { } +>CtorTag : Symbol(CtorTag, Decl(taggedTemplateWithConstructableTag01.ts, 0, 0)) + +CtorTag `Hello world!`; +>CtorTag : Symbol(CtorTag, Decl(taggedTemplateWithConstructableTag01.ts, 0, 0)) + diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag01.types b/tests/baselines/reference/taggedTemplateWithConstructableTag01.types new file mode 100644 index 00000000000..05e5aba7cde --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag01.types @@ -0,0 +1,9 @@ +=== tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts === +class CtorTag { } +>CtorTag : CtorTag + +CtorTag `Hello world!`; +>CtorTag `Hello world!` : any +>CtorTag : typeof CtorTag +>`Hello world!` : string + diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag02.js b/tests/baselines/reference/taggedTemplateWithConstructableTag02.js new file mode 100644 index 00000000000..6c38d508bd2 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag02.js @@ -0,0 +1,12 @@ +//// [taggedTemplateWithConstructableTag02.ts] +interface I { + new (...args: any[]): string; + new (): number; +} +var tag: I; +tag `Hello world!`; + +//// [taggedTemplateWithConstructableTag02.js] +var tag; +(_a = ["Hello world!"], _a.raw = ["Hello world!"], tag(_a)); +var _a; diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag02.symbols b/tests/baselines/reference/taggedTemplateWithConstructableTag02.symbols new file mode 100644 index 00000000000..2b2ccdbcbcf --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag02.symbols @@ -0,0 +1,16 @@ +=== tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts === +interface I { +>I : Symbol(I, Decl(taggedTemplateWithConstructableTag02.ts, 0, 0)) + + new (...args: any[]): string; +>args : Symbol(args, Decl(taggedTemplateWithConstructableTag02.ts, 1, 9)) + + new (): number; +} +var tag: I; +>tag : Symbol(tag, Decl(taggedTemplateWithConstructableTag02.ts, 4, 3)) +>I : Symbol(I, Decl(taggedTemplateWithConstructableTag02.ts, 0, 0)) + +tag `Hello world!`; +>tag : Symbol(tag, Decl(taggedTemplateWithConstructableTag02.ts, 4, 3)) + diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag02.types b/tests/baselines/reference/taggedTemplateWithConstructableTag02.types new file mode 100644 index 00000000000..1b96c3324dd --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag02.types @@ -0,0 +1,18 @@ +=== tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts === +interface I { +>I : I + + new (...args: any[]): string; +>args : any[] + + new (): number; +} +var tag: I; +>tag : I +>I : I + +tag `Hello world!`; +>tag `Hello world!` : any +>tag : I +>`Hello world!` : string + From 3292631b421fd85a870e65aaf82fab8aabf27dfe Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 22 Aug 2016 23:24:13 -0700 Subject: [PATCH 32/73] Added test for untyped tag. --- .../conformance/es6/templates/taggedTemplateUntypedTagCall01.ts | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/cases/conformance/es6/templates/taggedTemplateUntypedTagCall01.ts diff --git a/tests/cases/conformance/es6/templates/taggedTemplateUntypedTagCall01.ts b/tests/cases/conformance/es6/templates/taggedTemplateUntypedTagCall01.ts new file mode 100644 index 00000000000..b77e04b3095 --- /dev/null +++ b/tests/cases/conformance/es6/templates/taggedTemplateUntypedTagCall01.ts @@ -0,0 +1,2 @@ +var tag: Function; +tag `Hello world!`; \ No newline at end of file From 310e9c3a5110fb07bd987f7fcce1b87d7d591180 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 22 Aug 2016 23:25:37 -0700 Subject: [PATCH 33/73] Accepted baselines. --- .../reference/taggedTemplateUntypedTagCall01.js | 8 ++++++++ .../reference/taggedTemplateUntypedTagCall01.symbols | 8 ++++++++ .../reference/taggedTemplateUntypedTagCall01.types | 10 ++++++++++ 3 files changed, 26 insertions(+) create mode 100644 tests/baselines/reference/taggedTemplateUntypedTagCall01.js create mode 100644 tests/baselines/reference/taggedTemplateUntypedTagCall01.symbols create mode 100644 tests/baselines/reference/taggedTemplateUntypedTagCall01.types diff --git a/tests/baselines/reference/taggedTemplateUntypedTagCall01.js b/tests/baselines/reference/taggedTemplateUntypedTagCall01.js new file mode 100644 index 00000000000..e0d16eec39d --- /dev/null +++ b/tests/baselines/reference/taggedTemplateUntypedTagCall01.js @@ -0,0 +1,8 @@ +//// [taggedTemplateUntypedTagCall01.ts] +var tag: Function; +tag `Hello world!`; + +//// [taggedTemplateUntypedTagCall01.js] +var tag; +(_a = ["Hello world!"], _a.raw = ["Hello world!"], tag(_a)); +var _a; diff --git a/tests/baselines/reference/taggedTemplateUntypedTagCall01.symbols b/tests/baselines/reference/taggedTemplateUntypedTagCall01.symbols new file mode 100644 index 00000000000..07af0a996e5 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateUntypedTagCall01.symbols @@ -0,0 +1,8 @@ +=== tests/cases/conformance/es6/templates/taggedTemplateUntypedTagCall01.ts === +var tag: Function; +>tag : Symbol(tag, Decl(taggedTemplateUntypedTagCall01.ts, 0, 3)) +>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +tag `Hello world!`; +>tag : Symbol(tag, Decl(taggedTemplateUntypedTagCall01.ts, 0, 3)) + diff --git a/tests/baselines/reference/taggedTemplateUntypedTagCall01.types b/tests/baselines/reference/taggedTemplateUntypedTagCall01.types new file mode 100644 index 00000000000..3949869550c --- /dev/null +++ b/tests/baselines/reference/taggedTemplateUntypedTagCall01.types @@ -0,0 +1,10 @@ +=== tests/cases/conformance/es6/templates/taggedTemplateUntypedTagCall01.ts === +var tag: Function; +>tag : Function +>Function : Function + +tag `Hello world!`; +>tag `Hello world!` : any +>tag : Function +>`Hello world!` : string + From c21d16a3bbb58b9beac271b1735fa782243d447a Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 23 Aug 2016 11:14:05 -0700 Subject: [PATCH 34/73] Added test for decorators. --- .../decorators/class/constructableDecoratorOnClass01.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts diff --git a/tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts b/tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts new file mode 100644 index 00000000000..a9f9fa1699d --- /dev/null +++ b/tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts @@ -0,0 +1,8 @@ +// @experimentalDecorators: true + +class CtorDtor {} + +@CtorDtor +class C { + +} From d6ec5f29796ff094267df0e11b3b17bc6b95a28a Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 23 Aug 2016 11:14:33 -0700 Subject: [PATCH 35/73] Accepted baselines. --- .../constructableDecoratorOnClass01.js | 30 +++++++++++++++++++ .../constructableDecoratorOnClass01.symbols | 13 ++++++++ .../constructableDecoratorOnClass01.types | 13 ++++++++ 3 files changed, 56 insertions(+) create mode 100644 tests/baselines/reference/constructableDecoratorOnClass01.js create mode 100644 tests/baselines/reference/constructableDecoratorOnClass01.symbols create mode 100644 tests/baselines/reference/constructableDecoratorOnClass01.types diff --git a/tests/baselines/reference/constructableDecoratorOnClass01.js b/tests/baselines/reference/constructableDecoratorOnClass01.js new file mode 100644 index 00000000000..26c5bf4a1c4 --- /dev/null +++ b/tests/baselines/reference/constructableDecoratorOnClass01.js @@ -0,0 +1,30 @@ +//// [constructableDecoratorOnClass01.ts] + +class CtorDtor {} + +@CtorDtor +class C { + +} + + +//// [constructableDecoratorOnClass01.js] +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var CtorDtor = (function () { + function CtorDtor() { + } + return CtorDtor; +}()); +var C = (function () { + function C() { + } + C = __decorate([ + CtorDtor + ], C); + return C; +}()); diff --git a/tests/baselines/reference/constructableDecoratorOnClass01.symbols b/tests/baselines/reference/constructableDecoratorOnClass01.symbols new file mode 100644 index 00000000000..46de420b826 --- /dev/null +++ b/tests/baselines/reference/constructableDecoratorOnClass01.symbols @@ -0,0 +1,13 @@ +=== tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts === + +class CtorDtor {} +>CtorDtor : Symbol(CtorDtor, Decl(constructableDecoratorOnClass01.ts, 0, 0)) + +@CtorDtor +>CtorDtor : Symbol(CtorDtor, Decl(constructableDecoratorOnClass01.ts, 0, 0)) + +class C { +>C : Symbol(C, Decl(constructableDecoratorOnClass01.ts, 1, 17)) + +} + diff --git a/tests/baselines/reference/constructableDecoratorOnClass01.types b/tests/baselines/reference/constructableDecoratorOnClass01.types new file mode 100644 index 00000000000..d66778a27e6 --- /dev/null +++ b/tests/baselines/reference/constructableDecoratorOnClass01.types @@ -0,0 +1,13 @@ +=== tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts === + +class CtorDtor {} +>CtorDtor : CtorDtor + +@CtorDtor +>CtorDtor : typeof CtorDtor + +class C { +>C : C + +} + From 7ecbfb21481fd81464404802bd7a5d713cdf8a09 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 23 Aug 2016 11:30:44 -0700 Subject: [PATCH 36/73] Unify untyped call checking between decorators and template tags. --- src/compiler/checker.ts | 43 +++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 370802478bc..bde11422b95 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11943,18 +11943,12 @@ namespace ts { // Function interface, since they have none by default. This is a bit of a leap of faith // that the user will not add any. const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); - const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct); - // TS 1.0 spec: 4.12 - // If FuncExpr is of type Any, or of an object type that has no call or construct signatures - // but is a subtype of the Function interface, the call is an untyped function call. In an - // untyped function call no TypeArgs are permitted, Args can be any argument list, no contextual + + // TS 1.0 Spec: 4.12 + // In an untyped function call no TypeArgs are permitted, Args can be any argument list, no contextual // types are provided for the argument expressions, and the result is always of type Any. - // We exclude union types because we may have a union of function types that happen to have - // no common signatures. - if (isTypeAny(funcType) || - (isTypeAny(apparentType) && funcType.flags & TypeFlags.TypeParameter) || - (!callSignatures.length && !constructSignatures.length && !(funcType.flags & TypeFlags.Union) && isTypeAssignableTo(funcType, globalFunctionType))) { + if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) { // The unknownType indicates that an error already occurred (and was reported). No // need to report another error in this case. if (funcType !== unknownType && node.typeArguments) { @@ -11977,6 +11971,29 @@ namespace ts { return resolveCall(node, callSignatures, candidatesOutArray); } + /** + * TS 1.0 spec: 4.12 + * If FuncExpr is of type Any, or of an object type that has no call or construct signatures + * but is a subtype of the Function interface, the call is an untyped function call. + */ + function isUntypedFunctionCall(funcType: Type, apparentFuncType: Type, numCallSignatures: number, numConstructSignatures: number) { + if (isTypeAny(funcType)) { + return true; + } + if (isTypeAny(apparentFuncType) && funcType.flags & TypeFlags.TypeParameter) { + return true; + } + if (!numCallSignatures && !numConstructSignatures) { + // We exclude union types because we may have a union of function types that happen to have + // no common signatures. + if (funcType.flags & TypeFlags.Union) { + return false; + } + return isTypeAssignableTo(funcType, globalFunctionType); + } + return false; + } + function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[]): Signature { if (node.arguments && languageVersion < ScriptTarget.ES5) { const spreadIndex = getSpreadArgumentIndex(node.arguments); @@ -12102,8 +12119,9 @@ namespace ts { } const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); + const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct); - if (isTypeAny(tagType) || (!callSignatures.length && !(tagType.flags & TypeFlags.Union) && isTypeAssignableTo(tagType, globalFunctionType))) { + if (isUntypedFunctionCall(tagType, apparentType, callSignatures.length, constructSignatures.length)) { return resolveUntypedCall(node); } @@ -12148,7 +12166,8 @@ namespace ts { } const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); - if (funcType === anyType || (!callSignatures.length && !(funcType.flags & TypeFlags.Union) && isTypeAssignableTo(funcType, globalFunctionType))) { + const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct); + if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) { return resolveUntypedCall(node); } From 5ce285c367c8baf70576ce6b1562015328aa118a Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 23 Aug 2016 11:30:57 -0700 Subject: [PATCH 37/73] Accepted baselines. --- .../constructableDecoratorOnClass01.errors.txt | 16 ++++++++++++++++ ...ggedTemplateWithConstructableTag01.errors.txt | 9 +++++++++ ...ggedTemplateWithConstructableTag02.errors.txt | 12 ++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 tests/baselines/reference/constructableDecoratorOnClass01.errors.txt create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag01.errors.txt create mode 100644 tests/baselines/reference/taggedTemplateWithConstructableTag02.errors.txt diff --git a/tests/baselines/reference/constructableDecoratorOnClass01.errors.txt b/tests/baselines/reference/constructableDecoratorOnClass01.errors.txt new file mode 100644 index 00000000000..f390aaf8550 --- /dev/null +++ b/tests/baselines/reference/constructableDecoratorOnClass01.errors.txt @@ -0,0 +1,16 @@ +tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts(4,1): error TS1238: Unable to resolve signature of class decorator when called as an expression. + Cannot invoke an expression whose type lacks a call signature. + + +==== tests/cases/conformance/decorators/class/constructableDecoratorOnClass01.ts (1 errors) ==== + + class CtorDtor {} + + @CtorDtor + ~~~~~~~~~ +!!! error TS1238: Unable to resolve signature of class decorator when called as an expression. +!!! error TS1238: Cannot invoke an expression whose type lacks a call signature. + class C { + + } + \ No newline at end of file diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag01.errors.txt b/tests/baselines/reference/taggedTemplateWithConstructableTag01.errors.txt new file mode 100644 index 00000000000..b114acc5277 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag01.errors.txt @@ -0,0 +1,9 @@ +tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts(3,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. + + +==== tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag01.ts (1 errors) ==== + class CtorTag { } + + CtorTag `Hello world!`; + ~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. \ No newline at end of file diff --git a/tests/baselines/reference/taggedTemplateWithConstructableTag02.errors.txt b/tests/baselines/reference/taggedTemplateWithConstructableTag02.errors.txt new file mode 100644 index 00000000000..9bc4414b24f --- /dev/null +++ b/tests/baselines/reference/taggedTemplateWithConstructableTag02.errors.txt @@ -0,0 +1,12 @@ +tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts(6,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. + + +==== tests/cases/conformance/es6/templates/taggedTemplateWithConstructableTag02.ts (1 errors) ==== + interface I { + new (...args: any[]): string; + new (): number; + } + var tag: I; + tag `Hello world!`; + ~~~~~~~~~~~~~~~~~~ +!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. \ No newline at end of file From c71c5a876ea357993759ab663a8bc3e098f13afa Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Tue, 23 Aug 2016 16:45:51 -0700 Subject: [PATCH 38/73] Using for..of instead of forEach --- src/harness/harnessLanguageService.ts | 4 +- src/services/services.ts | 74 ++++++++++++++------------- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 5ae6a33463b..880a103a202 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -135,14 +135,14 @@ namespace Harness.LanguageService { public getFilenames(): string[] { const fileNames: string[] = []; - ts.forEach(this.virtualFileSystem.getAllFileEntries(), (virtualEntry) => { + for (const virtualEntry of this.virtualFileSystem.getAllFileEntries()){ const scriptInfo = virtualEntry.content; if (scriptInfo.isRootFile) { // only include root files here // usually it means that we won't include lib.d.ts in the list of root files so it won't mess the computation of compilation root dir. fileNames.push(scriptInfo.fileName); } - }); + } return fileNames; } diff --git a/src/services/services.ts b/src/services/services.ts index 4c199de1869..48cc186f7ec 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4559,12 +4559,12 @@ namespace ts { // Determine the path to the directory containing the script relative to the root directory it is contained within let relativeDirectory: string; - forEach(rootDirs, rootDirectory => { + for (const rootDirectory of rootDirs) { if (containsPath(rootDirectory, scriptPath, basePath, ignoreCase)) { relativeDirectory = scriptPath.substr(rootDirectory.length); - return true; + break; } - }); + } // Now find a path for each potential directory that is to be merged with the one containing the script return deduplicate(map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory))); @@ -4600,10 +4600,10 @@ namespace ts { if (directoryProbablyExists(baseDirectory, host)) { // Enumerate the available files const files = host.readDirectory(baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]); - forEach(files, filePath => { + for (let filePath of files) { filePath = normalizePath(filePath); if (exclude && comparePaths(filePath, exclude, scriptPath, ignoreCase) === Comparison.EqualTo) { - return false; + continue; } const fileName = includeExtensions ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath)); @@ -4616,20 +4616,20 @@ namespace ts { sortText: fileName }); } - }); + } // If possible, get folder completion as well if (host.getDirectories) { const directories = host.getDirectories(baseDirectory); - forEach(directories, d => { - const directoryName = getBaseFileName(normalizePath(d)); + for (const directory of directories) { + const directoryName = getBaseFileName(normalizePath(directory)); result.push({ name: directoryName, kind: ScriptElementKind.directory, sortText: directoryName }); - }); + } } } @@ -4660,11 +4660,11 @@ namespace ts { if (paths.hasOwnProperty(path)) { if (path === "*") { if (paths[path]) { - forEach(paths[path], pattern => { - forEach(getModulesForPathsPattern(fragment, baseUrl, pattern, fileExtensions), match => { + for (const pattern of paths[path]) { + for (const match of getModulesForPathsPattern(fragment, baseUrl, pattern, fileExtensions)) { result.push(createCompletionEntryForModule(match, ScriptElementKind.externalModuleName)); - }); - }); + } + } } } else if (startsWith(path, fragment)) { @@ -4683,9 +4683,9 @@ namespace ts { getCompletionEntriesFromTypings(host, options, scriptPath, result); - forEach(enumeratePotentialNonRelativeModules(fragment, scriptPath, options), moduleName => { + for (const moduleName of enumeratePotentialNonRelativeModules(fragment, scriptPath, options)) { result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); - }); + } return result; } @@ -4717,17 +4717,17 @@ namespace ts { const result: string[] = []; // Trim away prefix and suffix - forEach(matches, match => { + for (const match of matches) { const normalizedMatch = normalizePath(match); if (!endsWith(normalizedMatch, normalizedSuffix) || !startsWith(normalizedMatch, completePrefix)) { - return; + continue; } const start = completePrefix.length; const length = normalizedMatch.length - start - normalizedSuffix.length; result.push(removeFileExtension(normalizedMatch.substr(start, length))); - }); + } return result; } @@ -4740,15 +4740,15 @@ namespace ts { const moduleNameFragment = isNestedModule ? fragment.substr(0, fragment.lastIndexOf(directorySeparator)) : undefined; // Get modules that the type checker picked up - const ambientModules = ts.map(program.getTypeChecker().getAmbientModules(), sym => stripQuotes(sym.name)); - let nonRelativeModules = ts.filter(ambientModules, moduleName => startsWith(moduleName, fragment)); + const ambientModules = map(program.getTypeChecker().getAmbientModules(), sym => stripQuotes(sym.name)); + let nonRelativeModules = filter(ambientModules, moduleName => startsWith(moduleName, fragment)); // Nested modules of the form "module-name/sub" need to be adjusted to only return the string // after the last '/' that appears in the fragment because that's where the replacement span // starts if (isNestedModule) { const moduleNameWithSeperator = ensureTrailingDirectorySeparator(moduleNameFragment); - nonRelativeModules = ts.map(nonRelativeModules, moduleName => { + nonRelativeModules = map(nonRelativeModules, moduleName => { if (startsWith(fragment, moduleNameWithSeperator)) { return moduleName.substr(moduleNameWithSeperator.length); } @@ -4758,20 +4758,20 @@ namespace ts { if (!options.moduleResolution || options.moduleResolution === ModuleResolutionKind.NodeJs) { - forEach(enumerateNodeModulesVisibleToScript(host, scriptPath), visibleModule => { + for (const visibleModule of enumerateNodeModulesVisibleToScript(host, scriptPath)) { if (!isNestedModule) { nonRelativeModules.push(visibleModule.moduleName); } else if (startsWith(visibleModule.moduleName, moduleNameFragment)) { const nestedFiles = host.readDirectory(visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]); - forEach(nestedFiles, (f) => { + for (let f of nestedFiles) { f = normalizePath(f); const nestedModule = removeFileExtension(getBaseFileName(f)); nonRelativeModules.push(nestedModule); - }); + } } - }); + } } return deduplicate(nonRelativeModules); @@ -4827,9 +4827,9 @@ namespace ts { function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, result: ImportCompletionEntry[] = []): ImportCompletionEntry[] { // Check for typings specified in compiler options if (options.types) { - forEach(options.types, moduleName => { + for (const moduleName of options.types){ result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); - }); + } } else if (host.getDirectories && options.typeRoots) { const absoluteRoots = map(options.typeRoots, rootDirectory => { @@ -4840,15 +4840,17 @@ namespace ts { const basePath = options.project || host.getCurrentDirectory(); return normalizePath(combinePaths(basePath, rootDirectory)); }); - forEach(absoluteRoots, absoluteRoot => getCompletionEntriesFromDirectories(host, options, absoluteRoot, result)); + for (const absoluteRoot of absoluteRoots) { + getCompletionEntriesFromDirectories(host, options, absoluteRoot, result); + } } if (host.getDirectories) { // Also get all @types typings installed in visible node_modules directories - forEach(findPackageJsons(scriptPath), package => { + for (const package of findPackageJsons(scriptPath)) { const typesDir = combinePaths(getDirectoryPath(package), "node_modules/@types"); getCompletionEntriesFromDirectories(host, options, typesDir, result); - }); + } } return result; @@ -4856,10 +4858,10 @@ namespace ts { function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, result: ImportCompletionEntry[]) { if (host.getDirectories && directoryProbablyExists(directory, host)) { - forEach(host.getDirectories(directory), typeDirectory => { + for (let typeDirectory of host.getDirectories(directory)) { typeDirectory = normalizePath(typeDirectory); result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName)); - }); + } } } @@ -4889,7 +4891,7 @@ namespace ts { function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string) { const result: VisibleModuleInfo[] = []; - findPackageJsons(scriptPath).forEach((packageJson) => { + for (const packageJson of findPackageJsons(scriptPath)) { const package = tryReadingPackageJson(packageJson); if (!package) { return; @@ -4905,14 +4907,14 @@ namespace ts { addPotentialPackageNames(package.devDependencies, foundModuleNames); } - forEach(foundModuleNames, (moduleName) => { + for (const moduleName of foundModuleNames) { const moduleDir = combinePaths(nodeModulesDir, moduleName); result.push({ moduleName, moduleDir }); - }); - }); + } + } return result; From 73c3961355322515567875286141c4da723fb8ae Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Wed, 24 Aug 2016 18:50:04 -0700 Subject: [PATCH 39/73] Adding display parts to definition items to support FindAllReferences --- src/services/services.ts | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index babb5cf4574..7718be5a3d4 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -300,8 +300,8 @@ namespace ts { } } // For syntactic classifications, all trivia are classcified together, including jsdoc comments. - // For that to work, the jsdoc comments should still be the leading trivia of the first child. - // Restoring the scanner position ensures that. + // For that to work, the jsdoc comments should still be the leading trivia of the first child. + // Restoring the scanner position ensures that. pos = this.pos; forEachChild(this, processNode, processNodes); if (pos < this.end) { @@ -1374,8 +1374,13 @@ namespace ts { containerName: string; } + export interface ReferencedSymbolDefinitionInfo extends DefinitionInfo { + // For more complex definitions where kind and name are insufficient to properly colorize the text + displayParts?: SymbolDisplayPart[]; + } + export interface ReferencedSymbol { - definition: DefinitionInfo; + definition: ReferencedSymbolDefinitionInfo; references: ReferenceEntry[]; } @@ -6108,7 +6113,7 @@ namespace ts { return result; - function getDefinition(symbol: Symbol): DefinitionInfo { + function getDefinition(symbol: Symbol): ReferencedSymbolDefinitionInfo { const info = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, node.getSourceFile(), getContainerNode(node), node); const name = map(info.displayParts, p => p.text).join(""); const declarations = symbol.declarations; @@ -6122,7 +6127,8 @@ namespace ts { name, kind: info.symbolKind, fileName: declarations[0].getSourceFile().fileName, - textSpan: createTextSpan(declarations[0].getStart(), 0) + textSpan: createTextSpan(declarations[0].getStart(), 0), + displayParts: info.displayParts }; } @@ -6317,7 +6323,7 @@ namespace ts { } }); - const definition: DefinitionInfo = { + const definition: ReferencedSymbolDefinitionInfo = { containerKind: "", containerName: "", fileName: targetLabel.getSourceFile().fileName, @@ -6634,6 +6640,11 @@ namespace ts { getReferencesForStringLiteralInFile(sourceFile, type, possiblePositions, references); } + const symbol = typeChecker.getSymbolAtLocation(node); + + const displayParts = symbol ? getSymbolDisplayPartsDocumentationAndSymbolKind( + symbol, node.getSourceFile(), getContainerNode(node), node).displayParts : undefined; + return [{ definition: { containerKind: "", @@ -6641,7 +6652,8 @@ namespace ts { fileName: node.getSourceFile().fileName, kind: ScriptElementKind.variableElement, name: type.text, - textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()) + textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()), + displayParts }, references: references }]; From e0a36840c058210d7d29cea9e9bd039e4cf4210a Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Thu, 25 Aug 2016 11:53:44 -0700 Subject: [PATCH 40/73] PR feedback --- src/services/services.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 7718be5a3d4..6da12696937 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1375,8 +1375,7 @@ namespace ts { } export interface ReferencedSymbolDefinitionInfo extends DefinitionInfo { - // For more complex definitions where kind and name are insufficient to properly colorize the text - displayParts?: SymbolDisplayPart[]; + displayParts: SymbolDisplayPart[]; } export interface ReferencedSymbol { @@ -6640,11 +6639,6 @@ namespace ts { getReferencesForStringLiteralInFile(sourceFile, type, possiblePositions, references); } - const symbol = typeChecker.getSymbolAtLocation(node); - - const displayParts = symbol ? getSymbolDisplayPartsDocumentationAndSymbolKind( - symbol, node.getSourceFile(), getContainerNode(node), node).displayParts : undefined; - return [{ definition: { containerKind: "", @@ -6653,7 +6647,7 @@ namespace ts { kind: ScriptElementKind.variableElement, name: type.text, textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()), - displayParts + displayParts: [displayPart(getTextOfNode(node), SymbolDisplayPartKind.stringLiteral)] }, references: references }]; From 15ff0f79568f6e2dd7f1c685ee11b537ca27b951 Mon Sep 17 00:00:00 2001 From: Yui Date: Mon, 8 Aug 2016 14:45:01 -0700 Subject: [PATCH 41/73] [Release 2.0] fix10179: 2.0 emits optional class properties defined in constructors, which breaks compatibility (#10212) * Do not emit "?" for in property declaration in .d.ts when the property is declared as optional parameter property declaration * Update baselines --- src/compiler/declarationEmitter.ts | 6 ++++-- tests/baselines/reference/declFileConstructors.js | 4 ++-- tests/baselines/reference/optionalMethods.js | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 3642ee7f10a..ab1ca518256 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -1132,8 +1132,10 @@ namespace ts { // it if it's not a well known symbol. In that case, the text of the name will be exactly // what we want, namely the name expression enclosed in brackets. writeTextOfNode(currentText, node.name); - // If optional property emit ? - if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature || node.kind === SyntaxKind.Parameter) && hasQuestionToken(node)) { + // If optional property emit ? but in the case of parameterProperty declaration with "?" indicating optional parameter for the constructor + // we don't want to emit property declaration with "?" + if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature || + (node.kind === SyntaxKind.Parameter && !isParameterPropertyDeclaration(node))) && hasQuestionToken(node)) { write("?"); } if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) && node.parent.kind === SyntaxKind.TypeLiteral) { diff --git a/tests/baselines/reference/declFileConstructors.js b/tests/baselines/reference/declFileConstructors.js index 8e92b1a36ae..44e816712fb 100644 --- a/tests/baselines/reference/declFileConstructors.js +++ b/tests/baselines/reference/declFileConstructors.js @@ -247,7 +247,7 @@ export declare class ConstructorWithPrivateParameterProperty { constructor(x: string); } export declare class ConstructorWithOptionalParameterProperty { - x?: string; + x: string; constructor(x?: string); } export declare class ConstructorWithParameterInitializer { @@ -281,7 +281,7 @@ declare class GlobalConstructorWithPrivateParameterProperty { constructor(x: string); } declare class GlobalConstructorWithOptionalParameterProperty { - x?: string; + x: string; constructor(x?: string); } declare class GlobalConstructorWithParameterInitializer { diff --git a/tests/baselines/reference/optionalMethods.js b/tests/baselines/reference/optionalMethods.js index 42decf512e4..98629f5718b 100644 --- a/tests/baselines/reference/optionalMethods.js +++ b/tests/baselines/reference/optionalMethods.js @@ -126,7 +126,7 @@ interface Foo { } declare function test1(x: Foo): void; declare class Bar { - d?: number; + d: number; e: number; a: number; b?: number; From 34847f0ce05de1c8daccbec99c5181b0988b3b04 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Thu, 25 Aug 2016 14:08:37 -0700 Subject: [PATCH 42/73] Making language service host changes optional --- src/services/services.ts | 150 +++++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 69 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 48cc186f7ec..fde5b300c2f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1158,9 +1158,13 @@ namespace ts { error?(s: string): void; useCaseSensitiveFileNames?(): boolean; - readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[]; - readFile(path: string, encoding?: string): string; - fileExists(path: string): boolean; + /* + * LS host can optionally implement these methods to support getImportModuleCompletionsAtPosition. + * Without these methods, only completions for ambient modules will be provided. + */ + readDirectory?(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[]; + readFile?(path: string, encoding?: string): string; + fileExists?(path: string): boolean; /* * LS host can optionally implement this method if it wants to be completely in charge of module name resolution. @@ -3155,7 +3159,8 @@ namespace ts { writeFile: (fileName, data, writeByteOrderMark) => { }, getCurrentDirectory: () => currentDirectory, fileExists: (fileName): boolean => { - return host.fileExists(fileName); + // stub missing host functionality + return hostCache.getOrCreateEntry(fileName) !== undefined; }, readFile: (fileName): string => { // stub missing host functionality @@ -4598,23 +4603,25 @@ namespace ts { const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames()); if (directoryProbablyExists(baseDirectory, host)) { - // Enumerate the available files - const files = host.readDirectory(baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]); - for (let filePath of files) { - filePath = normalizePath(filePath); - if (exclude && comparePaths(filePath, exclude, scriptPath, ignoreCase) === Comparison.EqualTo) { - continue; - } + if (host.readDirectory) { + // Enumerate the available files if possible + const files = host.readDirectory(baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]); + for (let filePath of files) { + filePath = normalizePath(filePath); + if (exclude && comparePaths(filePath, exclude, scriptPath, ignoreCase) === Comparison.EqualTo) { + continue; + } - const fileName = includeExtensions ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath)); - const duplicate = !includeExtensions && forEach(result, entry => entry.name === fileName); + const fileName = includeExtensions ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath)); + const duplicate = !includeExtensions && forEach(result, entry => entry.name === fileName); - if (!duplicate) { - result.push({ - name: fileName, - kind: ScriptElementKind.scriptElement, - sortText: fileName - }); + if (!duplicate) { + result.push({ + name: fileName, + kind: ScriptElementKind.scriptElement, + sortText: fileName + }); + } } } @@ -4691,44 +4698,46 @@ namespace ts { } function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: string[]): string[] { - const parsed = hasZeroOrOneAsteriskCharacter(pattern) ? tryParsePattern(pattern) : undefined; - if (parsed) { - // The prefix has two effective parts: the directory path and the base component after the filepath that is not a - // full directory component. For example: directory/path/of/prefix/base* - const normalizedPrefix = normalizeAndPreserveTrailingSlash(parsed.prefix); - const normalizedPrefixDirectory = getDirectoryPath(normalizedPrefix); - const normalizedPrefixBase = getBaseFileName(normalizedPrefix); + if (host.readDirectory) { + const parsed = hasZeroOrOneAsteriskCharacter(pattern) ? tryParsePattern(pattern) : undefined; + if (parsed) { + // The prefix has two effective parts: the directory path and the base component after the filepath that is not a + // full directory component. For example: directory/path/of/prefix/base* + const normalizedPrefix = normalizeAndPreserveTrailingSlash(parsed.prefix); + const normalizedPrefixDirectory = getDirectoryPath(normalizedPrefix); + const normalizedPrefixBase = getBaseFileName(normalizedPrefix); - const fragmentHasPath = fragment.indexOf(directorySeparator) !== -1; + const fragmentHasPath = fragment.indexOf(directorySeparator) !== -1; - // Try and expand the prefix to include any path from the fragment so that we can limit the readDirectory call - const expandedPrefixDirectory = fragmentHasPath ? combinePaths(normalizedPrefixDirectory, normalizedPrefixBase + getDirectoryPath(fragment)) : normalizedPrefixDirectory; + // Try and expand the prefix to include any path from the fragment so that we can limit the readDirectory call + const expandedPrefixDirectory = fragmentHasPath ? combinePaths(normalizedPrefixDirectory, normalizedPrefixBase + getDirectoryPath(fragment)) : normalizedPrefixDirectory; - const normalizedSuffix = normalizePath(parsed.suffix); - const baseDirectory = combinePaths(baseUrl, expandedPrefixDirectory); - const completePrefix = fragmentHasPath ? baseDirectory : ensureTrailingDirectorySeparator(baseDirectory) + normalizedPrefixBase; + const normalizedSuffix = normalizePath(parsed.suffix); + const baseDirectory = combinePaths(baseUrl, expandedPrefixDirectory); + const completePrefix = fragmentHasPath ? baseDirectory : ensureTrailingDirectorySeparator(baseDirectory) + normalizedPrefixBase; - // If we have a suffix, then we need to read the directory all the way down. We could create a glob - // that encodes the suffix, but we would have to escape the character "?" which readDirectory - // doesn't support. For now, this is safer but slower - const includeGlob = normalizedSuffix ? "**/*" : "./*"; + // If we have a suffix, then we need to read the directory all the way down. We could create a glob + // that encodes the suffix, but we would have to escape the character "?" which readDirectory + // doesn't support. For now, this is safer but slower + const includeGlob = normalizedSuffix ? "**/*" : "./*"; - const matches = host.readDirectory(baseDirectory, fileExtensions, undefined, [includeGlob]); - const result: string[] = []; + const matches = host.readDirectory(baseDirectory, fileExtensions, undefined, [includeGlob]); + const result: string[] = []; - // Trim away prefix and suffix - for (const match of matches) { - const normalizedMatch = normalizePath(match); - if (!endsWith(normalizedMatch, normalizedSuffix) || !startsWith(normalizedMatch, completePrefix)) { - continue; + // Trim away prefix and suffix + for (const match of matches) { + const normalizedMatch = normalizePath(match); + if (!endsWith(normalizedMatch, normalizedSuffix) || !startsWith(normalizedMatch, completePrefix)) { + continue; + } + + const start = completePrefix.length; + const length = normalizedMatch.length - start - normalizedSuffix.length; + + result.push(removeFileExtension(normalizedMatch.substr(start, length))); } - - const start = completePrefix.length; - const length = normalizedMatch.length - start - normalizedSuffix.length; - - result.push(removeFileExtension(normalizedMatch.substr(start, length))); + return result; } - return result; } return undefined; @@ -4762,7 +4771,7 @@ namespace ts { if (!isNestedModule) { nonRelativeModules.push(visibleModule.moduleName); } - else if (startsWith(visibleModule.moduleName, moduleNameFragment)) { + else if (host.readDirectory && startsWith(visibleModule.moduleName, moduleNameFragment)) { const nestedFiles = host.readDirectory(visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]); for (let f of nestedFiles) { @@ -4891,28 +4900,31 @@ namespace ts { function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string) { const result: VisibleModuleInfo[] = []; - for (const packageJson of findPackageJsons(scriptPath)) { - const package = tryReadingPackageJson(packageJson); - if (!package) { - return; - } - const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules"); - const foundModuleNames: string[] = []; + if (host.readFile && host.fileExists) { + for (const packageJson of findPackageJsons(scriptPath)) { + const package = tryReadingPackageJson(packageJson); + if (!package) { + return; + } - if (package.dependencies) { - addPotentialPackageNames(package.dependencies, foundModuleNames); - } - if (package.devDependencies) { - addPotentialPackageNames(package.devDependencies, foundModuleNames); - } + const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules"); + const foundModuleNames: string[] = []; - for (const moduleName of foundModuleNames) { - const moduleDir = combinePaths(nodeModulesDir, moduleName); - result.push({ - moduleName, - moduleDir - }); + if (package.dependencies) { + addPotentialPackageNames(package.dependencies, foundModuleNames); + } + if (package.devDependencies) { + addPotentialPackageNames(package.devDependencies, foundModuleNames); + } + + for (const moduleName of foundModuleNames) { + const moduleDir = combinePaths(nodeModulesDir, moduleName); + result.push({ + moduleName, + moduleDir + }); + } } } From 276b56dfb03ef5f9a035aa0b0509ef46a469c476 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Thu, 25 Aug 2016 17:39:55 -0700 Subject: [PATCH 43/73] More PR feedback --- src/services/services.ts | 42 ++++++++++--------- ...tionForStringLiteralNonrelativeImport12.ts | 28 +++++++++++++ 2 files changed, 51 insertions(+), 19 deletions(-) create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport12.ts diff --git a/src/services/services.ts b/src/services/services.ts index fde5b300c2f..8bb378e090a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2076,6 +2076,8 @@ namespace ts { */ const tripleSlashDirectiveFragmentRegex = /^(\/\/\/\s*(); for (let filePath of files) { filePath = normalizePath(filePath); if (exclude && comparePaths(filePath, exclude, scriptPath, ignoreCase) === Comparison.EqualTo) { continue; } - const fileName = includeExtensions ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath)); - const duplicate = !includeExtensions && forEach(result, entry => entry.name === fileName); + const foundFileName = includeExtensions ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath)); - if (!duplicate) { - result.push({ - name: fileName, - kind: ScriptElementKind.scriptElement, - sortText: fileName - }); + if (!foundFiles[foundFileName]) { + foundFiles[foundFileName] = true; } } + + for (const foundFile in foundFiles) { + result.push({ + name: foundFile, + kind: ScriptElementKind.scriptElement, + sortText: foundFile + }); + } } // If possible, get folder completion as well @@ -4836,7 +4842,7 @@ namespace ts { function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, result: ImportCompletionEntry[] = []): ImportCompletionEntry[] { // Check for typings specified in compiler options if (options.types) { - for (const moduleName of options.types){ + for (const moduleName of options.types) { result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); } } @@ -4911,11 +4917,9 @@ namespace ts { const nodeModulesDir = combinePaths(getDirectoryPath(packageJson), "node_modules"); const foundModuleNames: string[] = []; - if (package.dependencies) { - addPotentialPackageNames(package.dependencies, foundModuleNames); - } - if (package.devDependencies) { - addPotentialPackageNames(package.devDependencies, foundModuleNames); + // Provide completions for all non @types dependencies + for (const key of nodeModulesDependencyKeys) { + addPotentialPackageNames(package[key], foundModuleNames); } for (const moduleName of foundModuleNames) { @@ -4940,11 +4944,12 @@ namespace ts { } } - // Add all the package names that are not in the @types scope function addPotentialPackageNames(dependencies: any, result: string[]) { - for (const dep in dependencies) { - if (dependencies.hasOwnProperty(dep) && !startsWith(dep, "@types/")) { - result.push(dep); + if (dependencies) { + for (const dep in dependencies) { + if (dependencies.hasOwnProperty(dep) && !startsWith(dep, "@types/")) { + result.push(dep); + } } } } @@ -4955,7 +4960,6 @@ namespace ts { } // Replace everything after the last directory seperator that appears - // FIXME: do we care about the other seperator? function getDirectoryFragmentTextSpan(text: string, textStart: number): TextSpan { const index = text.lastIndexOf(directorySeparator); const offset = index !== -1 ? index + 1 : 0; diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport12.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport12.ts new file mode 100644 index 00000000000..92db8d5a50e --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport12.ts @@ -0,0 +1,28 @@ +/// + +// Should give completions for all dependencies in package.json + +// @Filename: tests/test0.ts +//// import * as foo1 from "m/*import_as0*/ +//// import foo2 = require("m/*import_equals0*/ +//// var foo3 = require("m/*require0*/ + +// @Filename: package.json +//// { +//// "dependencies": { "module": "latest" }, +//// "devDependencies": { "dev-module": "latest" }, +//// "optionalDependencies": { "optional-module": "latest" }, +//// "peerDependencies": { "peer-module": "latest" } +//// } + +const kinds = ["import_as", "import_equals", "require"]; + +for (const kind of kinds) { + goTo.marker(kind + "0"); + + verify.importModuleCompletionListContains("module"); + verify.importModuleCompletionListContains("dev-module"); + verify.importModuleCompletionListContains("optional-module"); + verify.importModuleCompletionListContains("peer-module"); + verify.not.importModuleCompletionListItemsCountIsGreaterThan(4); +} From 0116abdcf2cc57174a839deb29e6c4419c576dfd Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 26 Aug 2016 08:49:43 -0700 Subject: [PATCH 44/73] First in UMD global wins Fixes #9771 --- src/compiler/checker.ts | 8 ++++- .../baselines/reference/umdGlobalConflict.js | 23 +++++++++++++++ .../reference/umdGlobalConflict.symbols | 29 +++++++++++++++++++ .../reference/umdGlobalConflict.types | 29 +++++++++++++++++++ tests/cases/compiler/umdGlobalConflict.ts | 15 ++++++++++ 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/umdGlobalConflict.js create mode 100644 tests/baselines/reference/umdGlobalConflict.symbols create mode 100644 tests/baselines/reference/umdGlobalConflict.types create mode 100644 tests/cases/compiler/umdGlobalConflict.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2341a0f7dfd..8c1e401912d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18763,7 +18763,13 @@ namespace ts { (augmentations || (augmentations = [])).push(file.moduleAugmentations); } if (file.symbol && file.symbol.globalExports) { - mergeSymbolTable(globals, file.symbol.globalExports); + // Merge in UMD exports with first-in-wins semantics (see #9771) + const source = file.symbol.globalExports; + for (const id in source) { + if (!(id in globals)) { + globals[id] = source[id]; + } + } } }); diff --git a/tests/baselines/reference/umdGlobalConflict.js b/tests/baselines/reference/umdGlobalConflict.js new file mode 100644 index 00000000000..e8a820e2333 --- /dev/null +++ b/tests/baselines/reference/umdGlobalConflict.js @@ -0,0 +1,23 @@ +//// [tests/cases/compiler/umdGlobalConflict.ts] //// + +//// [index.d.ts] +export as namespace Alpha; +export var x: string; + +//// [index.d.ts] +export as namespace Alpha; +export var y: number; + +//// [consumer.ts] +import * as v1 from './v1'; +import * as v2 from './v2'; + +//// [global.ts] +// Should be OK, first in wins +const p: string = Alpha.x; + +//// [consumer.js] +"use strict"; +//// [global.js] +// Should be OK, first in wins +var p = Alpha.x; diff --git a/tests/baselines/reference/umdGlobalConflict.symbols b/tests/baselines/reference/umdGlobalConflict.symbols new file mode 100644 index 00000000000..b13fb0f25b1 --- /dev/null +++ b/tests/baselines/reference/umdGlobalConflict.symbols @@ -0,0 +1,29 @@ +=== tests/cases/compiler/v1/index.d.ts === +export as namespace Alpha; +>Alpha : Symbol(Alpha, Decl(index.d.ts, 0, 0)) + +export var x: string; +>x : Symbol(x, Decl(index.d.ts, 1, 10)) + +=== tests/cases/compiler/v2/index.d.ts === +export as namespace Alpha; +>Alpha : Symbol(Alpha, Decl(index.d.ts, 0, 0)) + +export var y: number; +>y : Symbol(y, Decl(index.d.ts, 1, 10)) + +=== tests/cases/compiler/consumer.ts === +import * as v1 from './v1'; +>v1 : Symbol(v1, Decl(consumer.ts, 0, 6)) + +import * as v2 from './v2'; +>v2 : Symbol(v2, Decl(consumer.ts, 1, 6)) + +=== tests/cases/compiler/global.ts === +// Should be OK, first in wins +const p: string = Alpha.x; +>p : Symbol(p, Decl(global.ts, 1, 5)) +>Alpha.x : Symbol(Alpha.x, Decl(index.d.ts, 1, 10)) +>Alpha : Symbol(Alpha, Decl(index.d.ts, 0, 0)) +>x : Symbol(Alpha.x, Decl(index.d.ts, 1, 10)) + diff --git a/tests/baselines/reference/umdGlobalConflict.types b/tests/baselines/reference/umdGlobalConflict.types new file mode 100644 index 00000000000..fa74e26874f --- /dev/null +++ b/tests/baselines/reference/umdGlobalConflict.types @@ -0,0 +1,29 @@ +=== tests/cases/compiler/v1/index.d.ts === +export as namespace Alpha; +>Alpha : typeof Alpha + +export var x: string; +>x : string + +=== tests/cases/compiler/v2/index.d.ts === +export as namespace Alpha; +>Alpha : typeof + +export var y: number; +>y : number + +=== tests/cases/compiler/consumer.ts === +import * as v1 from './v1'; +>v1 : typeof v1 + +import * as v2 from './v2'; +>v2 : typeof v2 + +=== tests/cases/compiler/global.ts === +// Should be OK, first in wins +const p: string = Alpha.x; +>p : string +>Alpha.x : string +>Alpha : typeof Alpha +>x : string + diff --git a/tests/cases/compiler/umdGlobalConflict.ts b/tests/cases/compiler/umdGlobalConflict.ts new file mode 100644 index 00000000000..58b4ea6be02 --- /dev/null +++ b/tests/cases/compiler/umdGlobalConflict.ts @@ -0,0 +1,15 @@ +//@filename: v1/index.d.ts +export as namespace Alpha; +export var x: string; + +//@filename: v2/index.d.ts +export as namespace Alpha; +export var y: number; + +//@filename: consumer.ts +import * as v1 from './v1'; +import * as v2 from './v2'; + +//@filename: global.ts +// Should be OK, first in wins +const p: string = Alpha.x; \ No newline at end of file From 0a985ee28771c1aee386c7a2cb74c36ce8721e81 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 26 Aug 2016 13:25:19 -0700 Subject: [PATCH 45/73] Parse untyped object type members separated by ',' Previously if the first member was untyped and the separator was ',', then parsing would fail. --- src/compiler/parser.ts | 1 + .../reference/parseObjectLiteralsWithoutTypes.js | 10 ++++++++++ .../parseObjectLiteralsWithoutTypes.symbols | 16 ++++++++++++++++ .../parseObjectLiteralsWithoutTypes.types | 16 ++++++++++++++++ .../compiler/parseObjectLiteralsWithoutTypes.ts | 3 +++ 5 files changed, 46 insertions(+) create mode 100644 tests/baselines/reference/parseObjectLiteralsWithoutTypes.js create mode 100644 tests/baselines/reference/parseObjectLiteralsWithoutTypes.symbols create mode 100644 tests/baselines/reference/parseObjectLiteralsWithoutTypes.types create mode 100644 tests/cases/compiler/parseObjectLiteralsWithoutTypes.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b5ba89887a2..b28f13e438f 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2339,6 +2339,7 @@ namespace ts { token() === SyntaxKind.LessThanToken || token() === SyntaxKind.QuestionToken || token() === SyntaxKind.ColonToken || + token() === SyntaxKind.CommaToken || canParseSemicolon(); } return false; diff --git a/tests/baselines/reference/parseObjectLiteralsWithoutTypes.js b/tests/baselines/reference/parseObjectLiteralsWithoutTypes.js new file mode 100644 index 00000000000..4872cb8773b --- /dev/null +++ b/tests/baselines/reference/parseObjectLiteralsWithoutTypes.js @@ -0,0 +1,10 @@ +//// [parseObjectLiteralsWithoutTypes.ts] +let x: { foo, bar } +let y: { foo: number, bar } +let z: { foo, bar: number } + + +//// [parseObjectLiteralsWithoutTypes.js] +var x; +var y; +var z; diff --git a/tests/baselines/reference/parseObjectLiteralsWithoutTypes.symbols b/tests/baselines/reference/parseObjectLiteralsWithoutTypes.symbols new file mode 100644 index 00000000000..1e0bd775d10 --- /dev/null +++ b/tests/baselines/reference/parseObjectLiteralsWithoutTypes.symbols @@ -0,0 +1,16 @@ +=== tests/cases/compiler/parseObjectLiteralsWithoutTypes.ts === +let x: { foo, bar } +>x : Symbol(x, Decl(parseObjectLiteralsWithoutTypes.ts, 0, 3)) +>foo : Symbol(foo, Decl(parseObjectLiteralsWithoutTypes.ts, 0, 8)) +>bar : Symbol(bar, Decl(parseObjectLiteralsWithoutTypes.ts, 0, 13)) + +let y: { foo: number, bar } +>y : Symbol(y, Decl(parseObjectLiteralsWithoutTypes.ts, 1, 3)) +>foo : Symbol(foo, Decl(parseObjectLiteralsWithoutTypes.ts, 1, 8)) +>bar : Symbol(bar, Decl(parseObjectLiteralsWithoutTypes.ts, 1, 21)) + +let z: { foo, bar: number } +>z : Symbol(z, Decl(parseObjectLiteralsWithoutTypes.ts, 2, 3)) +>foo : Symbol(foo, Decl(parseObjectLiteralsWithoutTypes.ts, 2, 8)) +>bar : Symbol(bar, Decl(parseObjectLiteralsWithoutTypes.ts, 2, 13)) + diff --git a/tests/baselines/reference/parseObjectLiteralsWithoutTypes.types b/tests/baselines/reference/parseObjectLiteralsWithoutTypes.types new file mode 100644 index 00000000000..dcd9cb79d19 --- /dev/null +++ b/tests/baselines/reference/parseObjectLiteralsWithoutTypes.types @@ -0,0 +1,16 @@ +=== tests/cases/compiler/parseObjectLiteralsWithoutTypes.ts === +let x: { foo, bar } +>x : { foo: any; bar: any; } +>foo : any +>bar : any + +let y: { foo: number, bar } +>y : { foo: number; bar: any; } +>foo : number +>bar : any + +let z: { foo, bar: number } +>z : { foo: any; bar: number; } +>foo : any +>bar : number + diff --git a/tests/cases/compiler/parseObjectLiteralsWithoutTypes.ts b/tests/cases/compiler/parseObjectLiteralsWithoutTypes.ts new file mode 100644 index 00000000000..7f00e4b15e7 --- /dev/null +++ b/tests/cases/compiler/parseObjectLiteralsWithoutTypes.ts @@ -0,0 +1,3 @@ +let x: { foo, bar } +let y: { foo: number, bar } +let z: { foo, bar: number } From 3f8cd8230163b9b33f5b511bc639398a101b8bc0 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 26 Aug 2016 13:30:59 -0700 Subject: [PATCH 46/73] Update other tests and baselines --- ...ShorthandPropertiesAssignmentError.errors.txt | 13 ++----------- ...tLiteralShorthandPropertiesAssignmentError.js | 5 ++--- ...signmentErrorFromMissingIdentifier.errors.txt | 16 ++++------------ ...ertiesAssignmentErrorFromMissingIdentifier.js | 8 ++++---- ...tLiteralShorthandPropertiesAssignmentError.ts | 2 +- ...ertiesAssignmentErrorFromMissingIdentifier.ts | 4 ++-- 6 files changed, 15 insertions(+), 33 deletions(-) diff --git a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.errors.txt b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.errors.txt index 50839d5e56b..6f405facceb 100644 --- a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.errors.txt +++ b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.errors.txt @@ -1,8 +1,5 @@ tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts(4,43): error TS2322: Type '{ name: string; id: number; }' is not assignable to type '{ b: string; id: number; }'. Object literal may only specify known properties, and 'name' does not exist in type '{ b: string; id: number; }'. -tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts(5,16): error TS1131: Property or signature expected. -tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts(5,22): error TS2403: Subsequent variable declarations must have the same type. Variable 'id' must be of type 'number', but here has type 'any'. -tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts(5,25): error TS1128: Declaration or statement expected. tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts(6,79): error TS2322: Type '{ name: string; id: number; }' is not assignable to type '{ id: string; name: number; }'. Types of property 'id' are incompatible. Type 'number' is not assignable to type 'string'. @@ -11,7 +8,7 @@ tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPr Type 'number' is not assignable to type 'boolean'. -==== tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts (6 errors) ==== +==== tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts (3 errors) ==== var id: number = 10000; var name: string = "my name"; @@ -19,13 +16,7 @@ tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPr ~~~~ !!! error TS2322: Type '{ name: string; id: number; }' is not assignable to type '{ b: string; id: number; }'. !!! error TS2322: Object literal may only specify known properties, and 'name' does not exist in type '{ b: string; id: number; }'. - var person1: { name, id }; // error: can't use short-hand property assignment in type position - ~~~~ -!!! error TS1131: Property or signature expected. - ~~ -!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'id' must be of type 'number', but here has type 'any'. - ~ -!!! error TS1128: Declaration or statement expected. + var person1: { name, id }; // ok function foo(name: string, id: number): { id: string, name: number } { return { name, id }; } // error ~~~~~~~~~~~~ !!! error TS2322: Type '{ name: string; id: number; }' is not assignable to type '{ id: string; name: number; }'. diff --git a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.js b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.js index 8e2d7f80356..4d01f799348 100644 --- a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.js +++ b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentError.js @@ -3,7 +3,7 @@ var id: number = 10000; var name: string = "my name"; var person: { b: string; id: number } = { name, id }; // error -var person1: { name, id }; // error: can't use short-hand property assignment in type position +var person1: { name, id }; // ok function foo(name: string, id: number): { id: string, name: number } { return { name, id }; } // error function bar(obj: { name: string; id: boolean }) { } bar({ name, id }); // error @@ -14,8 +14,7 @@ bar({ name, id }); // error var id = 10000; var name = "my name"; var person = { name: name, id: id }; // error -var person1 = name, id; -; // error: can't use short-hand property assignment in type position +var person1; // ok function foo(name, id) { return { name: name, id: id }; } // error function bar(obj) { } bar({ name: name, id: id }); // error diff --git a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.errors.txt b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.errors.txt index b4f695c835d..92750fd29a0 100644 --- a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.errors.txt +++ b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.errors.txt @@ -3,15 +3,12 @@ tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPr tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts(5,79): error TS2322: Type '{ name: string; id: number; }' is not assignable to type '{ name: number; id: string; }'. Types of property 'name' are incompatible. Type 'string' is not assignable to type 'number'. -tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts(7,16): error TS1131: Property or signature expected. -tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts(7,22): error TS2403: Subsequent variable declarations must have the same type. Variable 'id' must be of type 'number', but here has type 'any'. -tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts(7,25): error TS1128: Declaration or statement expected. tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts(8,5): error TS2322: Type '{ name: number; id: string; }' is not assignable to type '{ name: string; id: number; }'. Types of property 'name' are incompatible. Type 'number' is not assignable to type 'string'. -==== tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts (6 errors) ==== +==== tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts (3 errors) ==== var id: number = 10000; var name: string = "my name"; @@ -25,15 +22,10 @@ tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPr !!! error TS2322: Types of property 'name' are incompatible. !!! error TS2322: Type 'string' is not assignable to type 'number'. function foo(name: string, id: number): { name: string, id: number } { return { name, id }; } // error - var person1: { name, id }; // error : Can't use shorthand in the type position - ~~~~ -!!! error TS1131: Property or signature expected. - ~~ -!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'id' must be of type 'number', but here has type 'any'. - ~ -!!! error TS1128: Declaration or statement expected. + var person1: { name, id }; // ok var person2: { name: string, id: number } = bar("hello", 5); ~~~~~~~ !!! error TS2322: Type '{ name: number; id: string; }' is not assignable to type '{ name: string; id: number; }'. !!! error TS2322: Types of property 'name' are incompatible. -!!! error TS2322: Type 'number' is not assignable to type 'string'. \ No newline at end of file +!!! error TS2322: Type 'number' is not assignable to type 'string'. + \ No newline at end of file diff --git a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.js b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.js index 71449746add..7800cbff53e 100644 --- a/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.js +++ b/tests/baselines/reference/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.js @@ -5,8 +5,9 @@ var name: string = "my name"; var person: { b: string; id: number } = { name, id }; // error function bar(name: string, id: number): { name: number, id: string } { return { name, id }; } // error function foo(name: string, id: number): { name: string, id: number } { return { name, id }; } // error -var person1: { name, id }; // error : Can't use shorthand in the type position -var person2: { name: string, id: number } = bar("hello", 5); +var person1: { name, id }; // ok +var person2: { name: string, id: number } = bar("hello", 5); + //// [objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.js] var id = 10000; @@ -14,6 +15,5 @@ var name = "my name"; var person = { name: name, id: id }; // error function bar(name, id) { return { name: name, id: id }; } // error function foo(name, id) { return { name: name, id: id }; } // error -var person1 = name, id; -; // error : Can't use shorthand in the type position +var person1; // ok var person2 = bar("hello", 5); diff --git a/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts b/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts index b745b93b3ca..55f10aec932 100644 --- a/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts +++ b/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts @@ -2,7 +2,7 @@ var name: string = "my name"; var person: { b: string; id: number } = { name, id }; // error -var person1: { name, id }; // error: can't use short-hand property assignment in type position +var person1: { name, id }; // ok function foo(name: string, id: number): { id: string, name: number } { return { name, id }; } // error function bar(obj: { name: string; id: boolean }) { } bar({ name, id }); // error diff --git a/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts b/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts index 6b0943a8d77..104be41a839 100644 --- a/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts +++ b/tests/cases/conformance/es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts @@ -4,5 +4,5 @@ var name: string = "my name"; var person: { b: string; id: number } = { name, id }; // error function bar(name: string, id: number): { name: number, id: string } { return { name, id }; } // error function foo(name: string, id: number): { name: string, id: number } { return { name, id }; } // error -var person1: { name, id }; // error : Can't use shorthand in the type position -var person2: { name: string, id: number } = bar("hello", 5); \ No newline at end of file +var person1: { name, id }; // ok +var person2: { name: string, id: number } = bar("hello", 5); From 4c847eb257434ac129a8a7b62bdb9a17a272028e Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Fri, 26 Aug 2016 15:04:08 -0700 Subject: [PATCH 47/73] Fixing errors and adding a fourslash test --- src/harness/fourslash.ts | 62 +++++++++++++------ src/services/services.ts | 11 +++- .../findReferencesDefinitionDisplayParts.ts | 23 +++++++ tests/cases/fourslash/fourslash.ts | 1 + 4 files changed, 75 insertions(+), 22 deletions(-) create mode 100644 tests/cases/fourslash/findReferencesDefinitionDisplayParts.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 63731417f48..694dcf6394e 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -206,6 +206,24 @@ namespace FourSlash { private inputFiles = ts.createMap(); // Map between inputFile's fileName and its content for easily looking up when resolving references + private static getDisplayPartsJson(displayParts: ts.SymbolDisplayPart[]) { + let result = ""; + ts.forEach(displayParts, part => { + if (result) { + result += ",\n "; + } + else { + result = "[\n "; + } + result += JSON.stringify(part); + }); + if (result) { + result += "\n]"; + } + + return result; + } + // Add input file which has matched file name with the given reference-file path. // This is necessary when resolveReference flag is specified private addMatchedInputFile(referenceFilePath: string, extensions: string[]) { @@ -777,6 +795,20 @@ namespace FourSlash { ts.forEachProperty(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges)); } + public verifyDisplayPartsOfReferencedSymbol(expected: ts.SymbolDisplayPart[]) { + const referencedSymbols = this.findReferencesAtCaret(); + + if (referencedSymbols.length === 0) { + this.raiseError("No referenced symbols found at current caret position"); + } + else if (referencedSymbols.length > 1) { + this.raiseError("More than one referenced symbol found"); + } + + assert.equal(TestState.getDisplayPartsJson(referencedSymbols[0].definition.displayParts), + TestState.getDisplayPartsJson(expected), this.messageAtLastKnownMarker("referenced symbol definition display parts")); + } + private verifyReferencesWorker(references: ts.ReferenceEntry[], fileName: string, start: number, end: number, isWriteAccess?: boolean, isDefinition?: boolean) { for (let i = 0; i < references.length; i++) { const reference = references[i]; @@ -811,6 +843,10 @@ namespace FourSlash { return this.languageService.getReferencesAtPosition(this.activeFile.fileName, this.currentCaretPosition); } + private findReferencesAtCaret() { + return this.languageService.findReferences(this.activeFile.fileName, this.currentCaretPosition); + } + public getSyntacticDiagnostics(expected: string) { const diagnostics = this.languageService.getSyntacticDiagnostics(this.activeFile.fileName); this.testDiagnostics(expected, diagnostics); @@ -856,30 +892,12 @@ namespace FourSlash { displayParts: ts.SymbolDisplayPart[], documentation: ts.SymbolDisplayPart[]) { - function getDisplayPartsJson(displayParts: ts.SymbolDisplayPart[]) { - let result = ""; - ts.forEach(displayParts, part => { - if (result) { - result += ",\n "; - } - else { - result = "[\n "; - } - result += JSON.stringify(part); - }); - if (result) { - result += "\n]"; - } - - return result; - } - const actualQuickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition); assert.equal(actualQuickInfo.kind, kind, this.messageAtLastKnownMarker("QuickInfo kind")); assert.equal(actualQuickInfo.kindModifiers, kindModifiers, this.messageAtLastKnownMarker("QuickInfo kindModifiers")); assert.equal(JSON.stringify(actualQuickInfo.textSpan), JSON.stringify(textSpan), this.messageAtLastKnownMarker("QuickInfo textSpan")); - assert.equal(getDisplayPartsJson(actualQuickInfo.displayParts), getDisplayPartsJson(displayParts), this.messageAtLastKnownMarker("QuickInfo displayParts")); - assert.equal(getDisplayPartsJson(actualQuickInfo.documentation), getDisplayPartsJson(documentation), this.messageAtLastKnownMarker("QuickInfo documentation")); + assert.equal(TestState.getDisplayPartsJson(actualQuickInfo.displayParts), TestState.getDisplayPartsJson(displayParts), this.messageAtLastKnownMarker("QuickInfo displayParts")); + assert.equal(TestState.getDisplayPartsJson(actualQuickInfo.documentation), TestState.getDisplayPartsJson(documentation), this.messageAtLastKnownMarker("QuickInfo documentation")); } public verifyRenameLocations(findInStrings: boolean, findInComments: boolean, ranges?: Range[]) { @@ -2947,6 +2965,10 @@ namespace FourSlashInterface { this.state.verifyRangesReferenceEachOther(ranges); } + public findReferencesDefinitionDisplayPartsAtCaretAre(expected: ts.SymbolDisplayPart[]) { + this.state.verifyDisplayPartsOfReferencedSymbol(expected); + } + public rangesWithSameTextReferenceEachOther() { this.state.verifyRangesWithSameTextReferenceEachOther(); } diff --git a/src/services/services.ts b/src/services/services.ts index 6da12696937..8794446e83e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -6328,7 +6328,8 @@ namespace ts { fileName: targetLabel.getSourceFile().fileName, kind: ScriptElementKind.label, name: labelName, - textSpan: createTextSpanFromBounds(targetLabel.getStart(), targetLabel.getEnd()) + textSpan: createTextSpanFromBounds(targetLabel.getStart(), targetLabel.getEnd()), + displayParts: [displayPart(labelName, SymbolDisplayPartKind.text)] }; return [{ definition, references }]; @@ -6568,6 +6569,11 @@ namespace ts { getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, references); } + const thisOrSuperSymbol = typeChecker.getSymbolAtLocation(thisOrSuperKeyword); + + const { displayParts } = getSymbolDisplayPartsDocumentationAndSymbolKind( + thisOrSuperSymbol, thisOrSuperKeyword.getSourceFile(), getContainerNode(thisOrSuperKeyword), thisOrSuperKeyword); + return [{ definition: { containerKind: "", @@ -6575,7 +6581,8 @@ namespace ts { fileName: node.getSourceFile().fileName, kind: ScriptElementKind.variableElement, name: "this", - textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()) + textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()), + displayParts }, references: references }]; diff --git a/tests/cases/fourslash/findReferencesDefinitionDisplayParts.ts b/tests/cases/fourslash/findReferencesDefinitionDisplayParts.ts new file mode 100644 index 00000000000..d424bd0c500 --- /dev/null +++ b/tests/cases/fourslash/findReferencesDefinitionDisplayParts.ts @@ -0,0 +1,23 @@ +/// + +//// class Gre/*1*/eter { +//// someFunction() { th/*2*/is; } +//// } +//// +//// type Options = "opt/*3*/ion 1" | "option 2"; +//// let myOption: Options = "option 1"; +//// +//// some/*4*/Label: +//// break someLabel; + +goTo.marker("1"); +verify.findReferencesDefinitionDisplayPartsAtCaretAre([{ text: "class", kind: "keyword" }, { text: " ", kind: "space" }, { text: "Greeter", kind: "className" }]); + +goTo.marker("2"); +verify.findReferencesDefinitionDisplayPartsAtCaretAre([{ text: "this", kind: "keyword" }, { text: ":", kind: "punctuation" }, { text: " ", kind: "space" }, { text: "this", kind: "keyword" }]); + +goTo.marker("3"); +verify.findReferencesDefinitionDisplayPartsAtCaretAre([{ text: "\"option 1\"", kind: "stringLiteral" }]); + +goTo.marker("4"); +verify.findReferencesDefinitionDisplayPartsAtCaretAre([{ text: "someLabel", kind: "text" }]); \ No newline at end of file diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 9ce3197a46f..59037e0de67 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -171,6 +171,7 @@ declare namespace FourSlashInterface { * If `ranges` is omitted, this is `test.ranges()`. */ rangesReferenceEachOther(ranges?: Range[]): void; + findReferencesDefinitionDisplayPartsAtCaretAre(expected: ts.SymbolDisplayPart[]): void; rangesWithSameTextReferenceEachOther(): void; currentParameterHelpArgumentNameIs(name: string): void; currentParameterSpanIs(parameter: string): void; From e62f1181b6a94161f8bfa88a84c2cf4c0f66bc3a Mon Sep 17 00:00:00 2001 From: Yui Date: Fri, 26 Aug 2016 16:46:10 -0700 Subject: [PATCH 48/73] fix typo of missing node.kind (#10569) --- src/compiler/emitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d0f7918dc89..f02cf266744 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -6587,7 +6587,7 @@ const _super = (function (geti, seti) { // import { x, y } from "foo" // import d, * as x from "foo" // import d, { x, y } from "foo" - const isNakedImport = SyntaxKind.ImportDeclaration && !(node).importClause; + const isNakedImport = node.kind === SyntaxKind.ImportDeclaration && !(node).importClause; if (!isNakedImport) { write(varOrConst); write(getGeneratedNameForNode(node)); From d72ed3c0b0fcf1f3c5f2b06d430c5e9c2ea6ca6f Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Fri, 26 Aug 2016 16:52:25 -0700 Subject: [PATCH 49/73] Check for this symbol before getting display parts --- src/services/services.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index dd99c0dba52..c19eb487d75 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -6570,8 +6570,8 @@ namespace ts { const thisOrSuperSymbol = typeChecker.getSymbolAtLocation(thisOrSuperKeyword); - const { displayParts } = getSymbolDisplayPartsDocumentationAndSymbolKind( - thisOrSuperSymbol, thisOrSuperKeyword.getSourceFile(), getContainerNode(thisOrSuperKeyword), thisOrSuperKeyword); + const displayParts = thisOrSuperSymbol && getSymbolDisplayPartsDocumentationAndSymbolKind( + thisOrSuperSymbol, thisOrSuperKeyword.getSourceFile(), getContainerNode(thisOrSuperKeyword), thisOrSuperKeyword).displayParts; return [{ definition: { From fb6ff42b93d46c33ff9439ca392b36511e95093d Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Fri, 26 Aug 2016 18:03:20 -0700 Subject: [PATCH 50/73] Reuse effective type roots code in language service --- src/compiler/program.ts | 11 ++++------- src/services/services.ts | 15 ++++----------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index f48f29785d9..7048496ada1 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -168,22 +168,19 @@ namespace ts { const typeReferenceExtensions = [".d.ts"]; - function getEffectiveTypeRoots(options: CompilerOptions, host: ModuleResolutionHost) { + export function getEffectiveTypeRoots(options: CompilerOptions, currentDirectory: string) { if (options.typeRoots) { return options.typeRoots; } - let currentDirectory: string; if (options.configFilePath) { currentDirectory = getDirectoryPath(options.configFilePath); } - else if (host.getCurrentDirectory) { - currentDirectory = host.getCurrentDirectory(); - } if (!currentDirectory) { return undefined; } + return map(defaultTypeRoots, d => combinePaths(currentDirectory, d)); } @@ -201,7 +198,7 @@ namespace ts { traceEnabled }; - const typeRoots = getEffectiveTypeRoots(options, host); + const typeRoots = getEffectiveTypeRoots(options, host.getCurrentDirectory && host.getCurrentDirectory()); if (traceEnabled) { if (containingFile === undefined) { if (typeRoots === undefined) { @@ -1061,7 +1058,7 @@ namespace ts { // Walk the primary type lookup locations const result: string[] = []; if (host.directoryExists && host.getDirectories) { - const typeRoots = getEffectiveTypeRoots(options, host); + const typeRoots = getEffectiveTypeRoots(options, host.getCurrentDirectory && host.getCurrentDirectory()); if (typeRoots) { for (const root of typeRoots) { if (host.directoryExists(root)) { diff --git a/src/services/services.ts b/src/services/services.ts index 8bb378e090a..519a5abe8cf 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4846,17 +4846,10 @@ namespace ts { result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); } } - else if (host.getDirectories && options.typeRoots) { - const absoluteRoots = map(options.typeRoots, rootDirectory => { - if (isRootedDiskPath(rootDirectory)) { - return normalizePath(rootDirectory); - } - - const basePath = options.project || host.getCurrentDirectory(); - return normalizePath(combinePaths(basePath, rootDirectory)); - }); - for (const absoluteRoot of absoluteRoots) { - getCompletionEntriesFromDirectories(host, options, absoluteRoot, result); + else if (host.getDirectories) { + const typeRoots = getEffectiveTypeRoots(options, host.getCurrentDirectory()); + for (const root of typeRoots) { + getCompletionEntriesFromDirectories(host, options, root, result); } } From d47b3e22e568bdf37c5ed0544713658723e2be8e Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 30 Aug 2016 07:09:18 -0700 Subject: [PATCH 51/73] Remove useless inserts that used to be required to trigger checks --- tests/cases/fourslash/cloduleAsBaseClass.ts | 3 --- tests/cases/fourslash/cloduleAsBaseClass2.ts | 3 --- tests/cases/fourslash/cloduleTypeOf1.ts | 4 ---- tests/cases/fourslash/cloduleWithRecursiveReference.ts | 3 --- tests/cases/fourslash/commentsClass.ts | 3 --- tests/cases/fourslash/commentsExternalModules.ts | 3 --- tests/cases/fourslash/commentsMultiModuleMultiFile.ts | 2 -- tests/cases/fourslash/commentsMultiModuleSingleFile.ts | 3 --- tests/cases/fourslash/completionListAtEOF.ts | 3 --- tests/cases/fourslash/completionListCladule.ts | 3 --- tests/cases/fourslash/completionListErrorRecovery.ts | 3 --- tests/cases/fourslash/completionListErrorRecovery2.ts | 3 --- tests/cases/fourslash/contextualTyping.ts | 3 --- tests/cases/fourslash/extendInterfaceOverloadedMethod.ts | 3 --- tests/cases/fourslash/funduleWithRecursiveReference.ts | 3 --- tests/cases/fourslash/genericCombinators1.ts | 3 --- tests/cases/fourslash/genericCombinators2.ts | 3 --- .../cases/fourslash/goToDefinitionFunctionOverloadsInClass.ts | 3 --- tests/cases/fourslash/goToDefinitionPartialImplementation.ts | 3 --- .../fourslash/mergedDeclarationsWithExportAssignment1.ts | 3 --- tests/cases/fourslash/quickInfoInFunctionTypeReference.ts | 3 --- .../fourslash/quickInfoOnConstructorWithGenericParameter.ts | 3 --- tests/cases/fourslash/signatureHelpForSuperCalls1.ts | 3 --- tests/cases/fourslash/signatureHelpSimpleSuperCall.ts | 3 --- 24 files changed, 72 deletions(-) diff --git a/tests/cases/fourslash/cloduleAsBaseClass.ts b/tests/cases/fourslash/cloduleAsBaseClass.ts index 19bd3bb53e3..89f7b8ce8a7 100644 --- a/tests/cases/fourslash/cloduleAsBaseClass.ts +++ b/tests/cases/fourslash/cloduleAsBaseClass.ts @@ -23,9 +23,6 @@ ////d./*1*/ ////D./*2*/ -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker('1'); verify.completionListContains('foo'); verify.completionListContains('foo2'); diff --git a/tests/cases/fourslash/cloduleAsBaseClass2.ts b/tests/cases/fourslash/cloduleAsBaseClass2.ts index 310ae72d664..995348fc7d1 100644 --- a/tests/cases/fourslash/cloduleAsBaseClass2.ts +++ b/tests/cases/fourslash/cloduleAsBaseClass2.ts @@ -28,9 +28,6 @@ ////d./*1*/ ////D./*2*/ -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker('1'); verify.completionListContains('foo'); verify.completionListContains('foo2'); diff --git a/tests/cases/fourslash/cloduleTypeOf1.ts b/tests/cases/fourslash/cloduleTypeOf1.ts index f34bb04c075..16f49a6ddb3 100644 --- a/tests/cases/fourslash/cloduleTypeOf1.ts +++ b/tests/cases/fourslash/cloduleTypeOf1.ts @@ -14,10 +14,6 @@ //// } ////} -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - - goTo.marker('1'); verify.completionListContains('f'); verify.completionListContains('foo'); diff --git a/tests/cases/fourslash/cloduleWithRecursiveReference.ts b/tests/cases/fourslash/cloduleWithRecursiveReference.ts index 069049907ff..0a22bf7af42 100644 --- a/tests/cases/fourslash/cloduleWithRecursiveReference.ts +++ b/tests/cases/fourslash/cloduleWithRecursiveReference.ts @@ -9,9 +9,6 @@ //// } ////} -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker(); verify.quickInfoIs('var M.C.C: typeof M.C'); verify.numberOfErrorsInCurrentFile(0); \ No newline at end of file diff --git a/tests/cases/fourslash/commentsClass.ts b/tests/cases/fourslash/commentsClass.ts index b9a446efe6e..fb42c01f4c9 100644 --- a/tests/cases/fourslash/commentsClass.ts +++ b/tests/cases/fourslash/commentsClass.ts @@ -58,9 +58,6 @@ ////} ////var myVar = new m.m2.c/*33*/1(); -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker('1'); verify.quickInfoIs("class c2", "This is class c2 without constuctor"); diff --git a/tests/cases/fourslash/commentsExternalModules.ts b/tests/cases/fourslash/commentsExternalModules.ts index c4bba7c444f..c7212fbea61 100644 --- a/tests/cases/fourslash/commentsExternalModules.ts +++ b/tests/cases/fourslash/commentsExternalModules.ts @@ -31,9 +31,6 @@ /////*10*/extMod./*11*/m1./*12*/fooExp/*13q*/ort(/*13*/); ////var new/*14*/Var = new extMod.m1.m2./*15*/c(); -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.file("commentsExternalModules_file0.ts"); goTo.marker('1'); verify.quickInfoIs("namespace m1", "Namespace comment"); diff --git a/tests/cases/fourslash/commentsMultiModuleMultiFile.ts b/tests/cases/fourslash/commentsMultiModuleMultiFile.ts index 9516ddc9abb..2a9e01e42d9 100644 --- a/tests/cases/fourslash/commentsMultiModuleMultiFile.ts +++ b/tests/cases/fourslash/commentsMultiModuleMultiFile.ts @@ -26,8 +26,6 @@ ////} ////new /*7*/mu/*8*/ltiM.d(); -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); goTo.marker('1'); verify.completionListContains("multiM", "namespace multiM", "this is multi declare namespace\nthi is multi namespace 2\nthis is multi namespace 3 comment"); diff --git a/tests/cases/fourslash/commentsMultiModuleSingleFile.ts b/tests/cases/fourslash/commentsMultiModuleSingleFile.ts index c2bd677ce9a..0393338b26c 100644 --- a/tests/cases/fourslash/commentsMultiModuleSingleFile.ts +++ b/tests/cases/fourslash/commentsMultiModuleSingleFile.ts @@ -16,9 +16,6 @@ ////new /*1*/mu/*4*/ltiM.b(); ////new mu/*5*/ltiM.c(); -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker('1'); verify.completionListContains("multiM", "namespace multiM", "this is multi declare namespace\nthi is multi namespace 2"); diff --git a/tests/cases/fourslash/completionListAtEOF.ts b/tests/cases/fourslash/completionListAtEOF.ts index 0f73d15075a..e1b210281cb 100644 --- a/tests/cases/fourslash/completionListAtEOF.ts +++ b/tests/cases/fourslash/completionListAtEOF.ts @@ -2,9 +2,6 @@ ////var a; -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.eof(); verify.completionListContains("a"); diff --git a/tests/cases/fourslash/completionListCladule.ts b/tests/cases/fourslash/completionListCladule.ts index 79c2e8aea6d..d1819e6d187 100644 --- a/tests/cases/fourslash/completionListCladule.ts +++ b/tests/cases/fourslash/completionListCladule.ts @@ -12,9 +12,6 @@ ////var f = new Foo(); ////f/*c3*/; -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker("c1"); edit.insert("."); verify.memberListContains("x"); diff --git a/tests/cases/fourslash/completionListErrorRecovery.ts b/tests/cases/fourslash/completionListErrorRecovery.ts index 7a7b9f81ca1..3e90ff4f22a 100644 --- a/tests/cases/fourslash/completionListErrorRecovery.ts +++ b/tests/cases/fourslash/completionListErrorRecovery.ts @@ -5,9 +5,6 @@ ////Foo./**/; /////*1*/var bar; -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker(); verify.memberListContains("fun"); verify.not.errorExistsAfterMarker("1"); \ No newline at end of file diff --git a/tests/cases/fourslash/completionListErrorRecovery2.ts b/tests/cases/fourslash/completionListErrorRecovery2.ts index 8a368ba4466..9d5ddfdb145 100644 --- a/tests/cases/fourslash/completionListErrorRecovery2.ts +++ b/tests/cases/fourslash/completionListErrorRecovery2.ts @@ -4,9 +4,6 @@ ////var baz = Foo/**/; /////*1*/baz.concat("y"); -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker(); edit.insert(".b"); verify.not.errorExistsAfterMarker("1"); diff --git a/tests/cases/fourslash/contextualTyping.ts b/tests/cases/fourslash/contextualTyping.ts index 6861ca84915..ecd12739271 100644 --- a/tests/cases/fourslash/contextualTyping.ts +++ b/tests/cases/fourslash/contextualTyping.ts @@ -194,9 +194,6 @@ //// } ////}; -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker('1'); verify.quickInfoIs("(property) C1T5.foo: (i: number, s: string) => number"); goTo.marker('2'); diff --git a/tests/cases/fourslash/extendInterfaceOverloadedMethod.ts b/tests/cases/fourslash/extendInterfaceOverloadedMethod.ts index 43a476303f6..332f4e0e5c7 100644 --- a/tests/cases/fourslash/extendInterfaceOverloadedMethod.ts +++ b/tests/cases/fourslash/extendInterfaceOverloadedMethod.ts @@ -11,9 +11,6 @@ ////var b: B; ////var /**/x = b.foo2().foo(5).foo(); // 'x' is of type 'void' -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker(); verify.quickInfoIs('var x: void'); verify.numberOfErrorsInCurrentFile(0); diff --git a/tests/cases/fourslash/funduleWithRecursiveReference.ts b/tests/cases/fourslash/funduleWithRecursiveReference.ts index c93d2cc0e1a..5af02f60dd4 100644 --- a/tests/cases/fourslash/funduleWithRecursiveReference.ts +++ b/tests/cases/fourslash/funduleWithRecursiveReference.ts @@ -7,9 +7,6 @@ //// } ////} -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker(); verify.quickInfoIs('var M.C.C: typeof M.C'); verify.numberOfErrorsInCurrentFile(0); \ No newline at end of file diff --git a/tests/cases/fourslash/genericCombinators1.ts b/tests/cases/fourslash/genericCombinators1.ts index 40f524c9dba..04f64cdcd8c 100644 --- a/tests/cases/fourslash/genericCombinators1.ts +++ b/tests/cases/fourslash/genericCombinators1.ts @@ -50,9 +50,6 @@ ////var /*23*/r8a = _.map(c5, (/*8*/x) => { return x.foo() }); -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker('1'); verify.quickInfoIs('(parameter) x: number'); goTo.marker('2'); diff --git a/tests/cases/fourslash/genericCombinators2.ts b/tests/cases/fourslash/genericCombinators2.ts index bcd96dcd1c6..af7f621bc12 100644 --- a/tests/cases/fourslash/genericCombinators2.ts +++ b/tests/cases/fourslash/genericCombinators2.ts @@ -56,9 +56,6 @@ //// ////var /*23*/r8a = _.map(c5, (/*8a*/x,/*8b*/y) => { return y.foo() }); -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker('2a'); verify.quickInfoIs('(parameter) x: Collection'); goTo.marker('2b'); diff --git a/tests/cases/fourslash/goToDefinitionFunctionOverloadsInClass.ts b/tests/cases/fourslash/goToDefinitionFunctionOverloadsInClass.ts index f368120922d..d5f5a374c39 100644 --- a/tests/cases/fourslash/goToDefinitionFunctionOverloadsInClass.ts +++ b/tests/cases/fourslash/goToDefinitionFunctionOverloadsInClass.ts @@ -11,9 +11,6 @@ //// constructor() { } ////} -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker('staticFunctionOverload'); goTo.definition(); verify.caretAtMarker('staticFunctionOverloadDefinition'); diff --git a/tests/cases/fourslash/goToDefinitionPartialImplementation.ts b/tests/cases/fourslash/goToDefinitionPartialImplementation.ts index eaf75f9a90b..81e417b6c0f 100644 --- a/tests/cases/fourslash/goToDefinitionPartialImplementation.ts +++ b/tests/cases/fourslash/goToDefinitionPartialImplementation.ts @@ -16,9 +16,6 @@ //// var x: /*Part2Use*/IA; ////} -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker('Part2Use'); goTo.definition(); verify.caretAtMarker('Part1Definition'); diff --git a/tests/cases/fourslash/mergedDeclarationsWithExportAssignment1.ts b/tests/cases/fourslash/mergedDeclarationsWithExportAssignment1.ts index 791f519aa28..b3d80b5b588 100644 --- a/tests/cases/fourslash/mergedDeclarationsWithExportAssignment1.ts +++ b/tests/cases/fourslash/mergedDeclarationsWithExportAssignment1.ts @@ -15,9 +15,6 @@ ////var /*3*/z = new /*2*/Foo(); ////var /*5*/r2 = Foo./*4*/x; -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker('1'); verify.quickInfoIs("import Foo = require('./mergedDeclarationsWithExportAssignment1_file0')"); diff --git a/tests/cases/fourslash/quickInfoInFunctionTypeReference.ts b/tests/cases/fourslash/quickInfoInFunctionTypeReference.ts index e8430d3fabe..180590ef4d0 100644 --- a/tests/cases/fourslash/quickInfoInFunctionTypeReference.ts +++ b/tests/cases/fourslash/quickInfoInFunctionTypeReference.ts @@ -4,9 +4,6 @@ ////} ////var x = <{ (fn: (va/*2*/riable2: string) => void, a: string): void; }> () => { }; -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker("1"); verify.quickInfoIs("(parameter) variable1: string", undefined); diff --git a/tests/cases/fourslash/quickInfoOnConstructorWithGenericParameter.ts b/tests/cases/fourslash/quickInfoOnConstructorWithGenericParameter.ts index d2ec8ca4f14..2adc8cf336f 100644 --- a/tests/cases/fourslash/quickInfoOnConstructorWithGenericParameter.ts +++ b/tests/cases/fourslash/quickInfoOnConstructorWithGenericParameter.ts @@ -16,9 +16,6 @@ ////} ////var x = new /*2*/B(/*1*/ -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker("1"); verify.currentSignatureHelpIs("B(a: Foo, b: number): B"); edit.insert("null,"); diff --git a/tests/cases/fourslash/signatureHelpForSuperCalls1.ts b/tests/cases/fourslash/signatureHelpForSuperCalls1.ts index 58e083ea2ed..6857cf76ead 100644 --- a/tests/cases/fourslash/signatureHelpForSuperCalls1.ts +++ b/tests/cases/fourslash/signatureHelpForSuperCalls1.ts @@ -17,9 +17,6 @@ //// } ////} -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker('1'); verify.signatureHelpPresent(); verify.currentSignatureHelpIs('B(): B'); diff --git a/tests/cases/fourslash/signatureHelpSimpleSuperCall.ts b/tests/cases/fourslash/signatureHelpSimpleSuperCall.ts index ff8913a6b04..01d140f29ea 100644 --- a/tests/cases/fourslash/signatureHelpSimpleSuperCall.ts +++ b/tests/cases/fourslash/signatureHelpSimpleSuperCall.ts @@ -10,9 +10,6 @@ //// } ////} -// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - goTo.marker('superCall'); verify.signatureHelpCountIs(1); verify.currentSignatureHelpIs("SuperCallBase(b: boolean): SuperCallBase"); From 0b9573118454ab2420abc168405be1a78b482b57 Mon Sep 17 00:00:00 2001 From: Yui Date: Tue, 30 Aug 2016 16:07:36 -0700 Subject: [PATCH 52/73] Fix 10408 : Better error message for set/get with noImplicitAny error (#10597) * Giving more explicit error message when there is no-implicit-any on get/set accessor * Update error message number * Add new test and baselines * Address PR: assert that getter must existed * Address PR: undo renumbering of error messages --- src/compiler/checker.ts | 8 +++- src/compiler/diagnosticMessages.json | 14 +++--- ...AndSetAccessorWithAnyReturnType.errors.txt | 4 +- ...noImplicitAnyMissingGetAccessor.errors.txt | 27 ++++++++++++ .../noImplicitAnyMissingGetAccessor.js | 44 +++++++++++++++++++ ...noImplicitAnyMissingSetAccessor.errors.txt | 17 +++++++ .../noImplicitAnyMissingSetAccessor.js | 43 ++++++++++++++++++ .../noImplicitAnyMissingGetAccessor.ts | 14 ++++++ .../noImplicitAnyMissingSetAccessor.ts | 13 ++++++ 9 files changed, 176 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/noImplicitAnyMissingGetAccessor.errors.txt create mode 100644 tests/baselines/reference/noImplicitAnyMissingGetAccessor.js create mode 100644 tests/baselines/reference/noImplicitAnyMissingSetAccessor.errors.txt create mode 100644 tests/baselines/reference/noImplicitAnyMissingSetAccessor.js create mode 100644 tests/cases/compiler/noImplicitAnyMissingGetAccessor.ts create mode 100644 tests/cases/compiler/noImplicitAnyMissingSetAccessor.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a98c72c9e10..863c9e7c37a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3346,7 +3346,13 @@ namespace ts { // Otherwise, fall back to 'any'. else { if (compilerOptions.noImplicitAny) { - error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_type_annotation, symbolToString(symbol)); + if (setter) { + error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol)); + } + else { + Debug.assert(!!getter, "there must existed getter as we are current checking either setter or getter in this function"); + error(getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol)); + } } type = anyType; } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index b8978b32571..e6bf1d5a170 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2871,11 +2871,7 @@ "Element implicitly has an 'any' type because index expression is not of type 'number'.": { "category": "Error", "code": 7015 - }, - "Property '{0}' implicitly has type 'any', because its 'set' accessor lacks a type annotation.": { - "category": "Error", - "code": 7016 - }, + }, "Index signature of object type implicitly has an 'any' type.": { "category": "Error", "code": 7017 @@ -2932,6 +2928,14 @@ "category": "Error", "code": 7031 }, + "Property '{0}' implicitly has type 'any', because its set accessor lacks a parameter type annotation.": { + "category": "Error", + "code": 7032 + }, + "Property '{0}' implicitly has type 'any', because its get accessor lacks a return type annotation.": { + "category": "Error", + "code": 7033 + }, "You cannot rename this element.": { "category": "Error", "code": 8000 diff --git a/tests/baselines/reference/implicitAnyGetAndSetAccessorWithAnyReturnType.errors.txt b/tests/baselines/reference/implicitAnyGetAndSetAccessorWithAnyReturnType.errors.txt index 460ff97b10e..1e2de97aae0 100644 --- a/tests/baselines/reference/implicitAnyGetAndSetAccessorWithAnyReturnType.errors.txt +++ b/tests/baselines/reference/implicitAnyGetAndSetAccessorWithAnyReturnType.errors.txt @@ -2,7 +2,7 @@ tests/cases/compiler/implicitAnyGetAndSetAccessorWithAnyReturnType.ts(3,5): erro tests/cases/compiler/implicitAnyGetAndSetAccessorWithAnyReturnType.ts(4,16): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. tests/cases/compiler/implicitAnyGetAndSetAccessorWithAnyReturnType.ts(9,16): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. tests/cases/compiler/implicitAnyGetAndSetAccessorWithAnyReturnType.ts(15,16): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. -tests/cases/compiler/implicitAnyGetAndSetAccessorWithAnyReturnType.ts(15,16): error TS7016: Property 'haveOnlySet' implicitly has type 'any', because its 'set' accessor lacks a type annotation. +tests/cases/compiler/implicitAnyGetAndSetAccessorWithAnyReturnType.ts(15,16): error TS7032: Property 'haveOnlySet' implicitly has type 'any', because its set accessor lacks a parameter type annotation. tests/cases/compiler/implicitAnyGetAndSetAccessorWithAnyReturnType.ts(15,28): error TS7006: Parameter 'newXValue' implicitly has an 'any' type. tests/cases/compiler/implicitAnyGetAndSetAccessorWithAnyReturnType.ts(20,16): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. tests/cases/compiler/implicitAnyGetAndSetAccessorWithAnyReturnType.ts(20,16): error TS7010: 'haveOnlyGet', which lacks return-type annotation, implicitly has an 'any' return type. @@ -33,7 +33,7 @@ tests/cases/compiler/implicitAnyGetAndSetAccessorWithAnyReturnType.ts(20,16): er ~~~~~~~~~~~ !!! error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. ~~~~~~~~~~~ -!!! error TS7016: Property 'haveOnlySet' implicitly has type 'any', because its 'set' accessor lacks a type annotation. +!!! error TS7032: Property 'haveOnlySet' implicitly has type 'any', because its set accessor lacks a parameter type annotation. ~~~~~~~~~ !!! error TS7006: Parameter 'newXValue' implicitly has an 'any' type. } diff --git a/tests/baselines/reference/noImplicitAnyMissingGetAccessor.errors.txt b/tests/baselines/reference/noImplicitAnyMissingGetAccessor.errors.txt new file mode 100644 index 00000000000..ef2a273711f --- /dev/null +++ b/tests/baselines/reference/noImplicitAnyMissingGetAccessor.errors.txt @@ -0,0 +1,27 @@ +tests/cases/compiler/noImplicitAnyMissingGetAccessor.ts(4,25): error TS7032: Property 'message' implicitly has type 'any', because its set accessor lacks a parameter type annotation. +tests/cases/compiler/noImplicitAnyMissingGetAccessor.ts(4,33): error TS7006: Parameter 'str' implicitly has an 'any' type. +tests/cases/compiler/noImplicitAnyMissingGetAccessor.ts(9,16): error TS7032: Property 'message' implicitly has type 'any', because its set accessor lacks a parameter type annotation. +tests/cases/compiler/noImplicitAnyMissingGetAccessor.ts(9,24): error TS7006: Parameter 'str' implicitly has an 'any' type. + + +==== tests/cases/compiler/noImplicitAnyMissingGetAccessor.ts (4 errors) ==== + + abstract class Parent + { + public abstract set message(str); + ~~~~~~~ +!!! error TS7032: Property 'message' implicitly has type 'any', because its set accessor lacks a parameter type annotation. + ~~~ +!!! error TS7006: Parameter 'str' implicitly has an 'any' type. + } + + class Child extends Parent { + _x: any; + public set message(str) { + ~~~~~~~ +!!! error TS7032: Property 'message' implicitly has type 'any', because its set accessor lacks a parameter type annotation. + ~~~ +!!! error TS7006: Parameter 'str' implicitly has an 'any' type. + this._x = str; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/noImplicitAnyMissingGetAccessor.js b/tests/baselines/reference/noImplicitAnyMissingGetAccessor.js new file mode 100644 index 00000000000..38a2a731af4 --- /dev/null +++ b/tests/baselines/reference/noImplicitAnyMissingGetAccessor.js @@ -0,0 +1,44 @@ +//// [noImplicitAnyMissingGetAccessor.ts] + +abstract class Parent +{ + public abstract set message(str); +} + +class Child extends Parent { + _x: any; + public set message(str) { + this._x = str; + } +} + +//// [noImplicitAnyMissingGetAccessor.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var Parent = (function () { + function Parent() { + } + Object.defineProperty(Parent.prototype, "message", { + set: function (str) { }, + enumerable: true, + configurable: true + }); + return Parent; +}()); +var Child = (function (_super) { + __extends(Child, _super); + function Child() { + _super.apply(this, arguments); + } + Object.defineProperty(Child.prototype, "message", { + set: function (str) { + this._x = str; + }, + enumerable: true, + configurable: true + }); + return Child; +}(Parent)); diff --git a/tests/baselines/reference/noImplicitAnyMissingSetAccessor.errors.txt b/tests/baselines/reference/noImplicitAnyMissingSetAccessor.errors.txt new file mode 100644 index 00000000000..ba104209189 --- /dev/null +++ b/tests/baselines/reference/noImplicitAnyMissingSetAccessor.errors.txt @@ -0,0 +1,17 @@ +tests/cases/compiler/noImplicitAnyMissingSetAccessor.ts(4,25): error TS7033: Property 'message' implicitly has type 'any', because its get accessor lacks a return type annotation. + + +==== tests/cases/compiler/noImplicitAnyMissingSetAccessor.ts (1 errors) ==== + + abstract class Parent + { + public abstract get message(); + ~~~~~~~ +!!! error TS7033: Property 'message' implicitly has type 'any', because its get accessor lacks a return type annotation. + } + + class Child extends Parent { + public get message() { + return ""; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/noImplicitAnyMissingSetAccessor.js b/tests/baselines/reference/noImplicitAnyMissingSetAccessor.js new file mode 100644 index 00000000000..d4e0d17186d --- /dev/null +++ b/tests/baselines/reference/noImplicitAnyMissingSetAccessor.js @@ -0,0 +1,43 @@ +//// [noImplicitAnyMissingSetAccessor.ts] + +abstract class Parent +{ + public abstract get message(); +} + +class Child extends Parent { + public get message() { + return ""; + } +} + +//// [noImplicitAnyMissingSetAccessor.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var Parent = (function () { + function Parent() { + } + Object.defineProperty(Parent.prototype, "message", { + get: function () { }, + enumerable: true, + configurable: true + }); + return Parent; +}()); +var Child = (function (_super) { + __extends(Child, _super); + function Child() { + _super.apply(this, arguments); + } + Object.defineProperty(Child.prototype, "message", { + get: function () { + return ""; + }, + enumerable: true, + configurable: true + }); + return Child; +}(Parent)); diff --git a/tests/cases/compiler/noImplicitAnyMissingGetAccessor.ts b/tests/cases/compiler/noImplicitAnyMissingGetAccessor.ts new file mode 100644 index 00000000000..d6c7b012cf9 --- /dev/null +++ b/tests/cases/compiler/noImplicitAnyMissingGetAccessor.ts @@ -0,0 +1,14 @@ +// @noImplicitAny : true +// @target: es5 + +abstract class Parent +{ + public abstract set message(str); +} + +class Child extends Parent { + _x: any; + public set message(str) { + this._x = str; + } +} \ No newline at end of file diff --git a/tests/cases/compiler/noImplicitAnyMissingSetAccessor.ts b/tests/cases/compiler/noImplicitAnyMissingSetAccessor.ts new file mode 100644 index 00000000000..ae18faf1f6f --- /dev/null +++ b/tests/cases/compiler/noImplicitAnyMissingSetAccessor.ts @@ -0,0 +1,13 @@ +// @noImplicitAny: true +// @target: es5 + +abstract class Parent +{ + public abstract get message(); +} + +class Child extends Parent { + public get message() { + return ""; + } +} \ No newline at end of file From 707d61d7fe953b04b3ad117294a33135573ced3a Mon Sep 17 00:00:00 2001 From: Yui Date: Wed, 31 Aug 2016 13:53:14 -0700 Subject: [PATCH 53/73] Fix RWC Runner to report both .types and .symbols errors (#10513) * Correctly append .types or .symbols when calling from rwc runner * Report both errors from generating .types or .symbols * Address PR --- src/harness/harness.ts | 25 +++++++++++++++++++------ src/harness/rwcRunner.ts | 3 ++- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 9e27f500b80..ac0de944213 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1370,23 +1370,31 @@ namespace Harness { // Produce baselines. The first gives the types for all expressions. // The second gives symbols for all identifiers. - let e1: Error, e2: Error; + let typesError: Error, symbolsError: Error; try { checkBaseLines(/*isSymbolBaseLine*/ false); } catch (e) { - e1 = e; + typesError = e; } try { checkBaseLines(/*isSymbolBaseLine*/ true); } catch (e) { - e2 = e; + symbolsError = e; } - if (e1 || e2) { - throw e1 || e2; + if (typesError && symbolsError) { + throw new Error(typesError.message + ts.sys.newLine + symbolsError.message); + } + + if (typesError) { + throw typesError; + } + + if (symbolsError) { + throw symbolsError; } return; @@ -1396,7 +1404,12 @@ namespace Harness { const fullExtension = isSymbolBaseLine ? ".symbols" : ".types"; - Harness.Baseline.runBaseline(baselinePath.replace(/\.tsx?/, fullExtension), () => fullBaseLine, opts); + // When calling this function from rwc-runner, the baselinePath will have no extension. + // As rwc test- file is stored in json which ".json" will get stripped off. + // When calling this function from compiler-runner, the baselinePath will then has either ".ts" or ".tsx" extension + const outputFileName = ts.endsWith(baselinePath, ".ts") || ts.endsWith(baselinePath, ".tsx") ? + baselinePath.replace(/\.tsx?/, fullExtension) : baselinePath.concat(fullExtension); + Harness.Baseline.runBaseline(outputFileName, () => fullBaseLine, opts); } function generateBaseLine(typeWriterResults: ts.Map, isSymbolBaseline: boolean): string { diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index ba1ab71ec19..fff0d6bfb49 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -223,7 +223,8 @@ namespace RWC { }); it("has the expected types", () => { - Harness.Compiler.doTypeAndSymbolBaseline(`${baseName}.types`, compilerResult, inputFiles + // We don't need to pass the extension here because "doTypeAndSymbolBaseline" will append appropriate extension of ".types" or ".symbols" + Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult, inputFiles .concat(otherFiles) .filter(file => !!compilerResult.program.getSourceFile(file.unitName)) .filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts); From b9b79af1b7f93411786849ee53f7cca0b0ab260a Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Wed, 31 Aug 2016 18:11:47 -0700 Subject: [PATCH 54/73] Recombining import completions and regular completion APIs --- src/harness/fourslash.ts | 8 +- src/harness/harnessLanguageService.ts | 3 - src/server/client.ts | 28 ++----- src/server/protocol.d.ts | 20 ++--- src/server/session.ts | 13 ++- src/services/services.ts | 109 ++++++++++++-------------- src/services/shims.ts | 8 -- 7 files changed, 77 insertions(+), 112 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 86317faa174..2b4373d79fa 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -780,10 +780,10 @@ namespace FourSlash { if (ranges && ranges.length > rangeIndex) { const range = ranges[rangeIndex]; - const start = completions.textSpan.start; - const end = start + completions.textSpan.length; + const start = completion.replacementSpan.start; + const end = start + completion.replacementSpan.length; if (range.start !== start || range.end !== end) { - this.raiseError(`Expected completion span for '${symbol}', ${stringify(completions.textSpan)}, to cover range ${stringify(range)}`); + this.raiseError(`Expected completion span for '${symbol}', ${stringify(completion.replacementSpan)}, to cover range ${stringify(range)}`); } } else { @@ -892,7 +892,7 @@ namespace FourSlash { } private getImportModuleCompletionListAtCaret() { - return this.languageService.getImportModuleCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition); + return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition); } private getCompletionEntryDetails(entryName: string) { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 880a103a202..fba1b87610b 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -405,9 +405,6 @@ namespace Harness.LanguageService { getCompletionsAtPosition(fileName: string, position: number): ts.CompletionInfo { return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position)); } - getImportModuleCompletionsAtPosition(fileName: string, position: number): ts.ImportCompletionInfo { - return unwrapJSONCallResult(this.shim.getImportModuleCompletionsAtPosition(fileName, position)); - } getCompletionEntryDetails(fileName: string, position: number, entryName: string): ts.CompletionEntryDetails { return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName)); } diff --git a/src/server/client.ts b/src/server/client.ts index deb7f53801f..d5aed77bd1f 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -216,28 +216,16 @@ namespace ts.server { return { isMemberCompletion: false, isNewIdentifierLocation: false, - entries: response.body - }; - } + entries: response.body.map(({ name, kind, kindModifiers, sortText, replacementSpan }) => { - getImportModuleCompletionsAtPosition(fileName: string, position: number): ImportCompletionInfo { - const lineOffset = this.positionToOneBasedLineOffset(fileName, position); - const args: protocol.CompletionsRequestArgs = { - file: fileName, - line: lineOffset.line, - offset: lineOffset.offset, - prefix: undefined - }; + let convertedSpan: TextSpan; + if (replacementSpan) { + convertedSpan = createTextSpanFromBounds(this.lineOffsetToPosition(fileName, replacementSpan.start), + this.lineOffsetToPosition(fileName, replacementSpan.end)); + } - const request = this.processRequest(CommandNames.ImportModuleCompletions, args); - const response = this.processResponse(request); - - const startPosition = this.lineOffsetToPosition(fileName, response.span.start); - const endPosition = this.lineOffsetToPosition(fileName, response.span.end); - - return { - textSpan: ts.createTextSpanFromBounds(startPosition, endPosition), - entries: response.body + return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan }; + }) }; } diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts index 20670ae0245..2f71c3d5b74 100644 --- a/src/server/protocol.d.ts +++ b/src/server/protocol.d.ts @@ -717,16 +717,6 @@ declare namespace ts.server.protocol { arguments: CompletionsRequestArgs; } - /** - * Import Module Completions request; value of command field is - * "importModuleCompletions". Given a file location (file, line, - * col) return the possible completions for external module - * specifiers or paths given that position refers to a module - * import declaration, require call, or triple slash reference. - */ - export interface ImportModuleCompletionsRequest extends FileLocationRequest { - } - /** * Arguments for completion details request. */ @@ -783,6 +773,11 @@ declare namespace ts.server.protocol { * is often the same as the name but may be different in certain circumstances. */ sortText: string; + /** + * An optional span that indicates the text to be replaced by this completion item. If present, + * this span should be used instead of the default one. + */ + replacementSpan?: TextSpan; } /** @@ -816,11 +811,6 @@ declare namespace ts.server.protocol { body?: CompletionEntry[]; } - export interface ImportModuleCompletionsResponse extends Response { - span: TextSpan; - body?: ImportCompletionEntry[]; - } - export interface CompletionDetailsResponse extends Response { body?: CompletionEntryDetails[]; } diff --git a/src/server/session.ts b/src/server/session.ts index c771d100229..5af4203311a 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -103,7 +103,6 @@ namespace ts.server { export const Change = "change"; export const Close = "close"; export const Completions = "completions"; - export const ImportModuleCompletions = "importModuleCompletions"; export const CompletionDetails = "completionEntryDetails"; export const Configure = "configure"; export const Definition = "definition"; @@ -773,7 +772,17 @@ namespace ts.server { return completions.entries.reduce((result: protocol.CompletionEntry[], entry: ts.CompletionEntry) => { if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) { - result.push(entry); + const { name, kind, kindModifiers, sortText, replacementSpan } = entry; + + let convertedSpan: protocol.TextSpan = undefined; + if (replacementSpan) { + convertedSpan = { + start: compilerService.host.positionToLineOffset(fileName, replacementSpan.start), + end: compilerService.host.positionToLineOffset(fileName, replacementSpan.start + replacementSpan.length) + }; + } + + result.push({ name, kind, kindModifiers, sortText, replacementSpan: convertedSpan }); } return result; }, []).sort((a, b) => a.name.localeCompare(b.name)); diff --git a/src/services/services.ts b/src/services/services.ts index 519a5abe8cf..66e450bf830 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1159,7 +1159,7 @@ namespace ts { useCaseSensitiveFileNames?(): boolean; /* - * LS host can optionally implement these methods to support getImportModuleCompletionsAtPosition. + * LS host can optionally implement these methods to support completions for module specifiers. * Without these methods, only completions for ambient modules will be provided. */ readDirectory?(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[]; @@ -1211,7 +1211,6 @@ namespace ts { getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; getCompletionsAtPosition(fileName: string, position: number): CompletionInfo; - getImportModuleCompletionsAtPosition(fileName: string, position: number): ImportCompletionInfo; getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; @@ -1482,17 +1481,7 @@ namespace ts { kind: string; // see ScriptElementKind kindModifiers: string; // see ScriptElementKindModifier, comma separated sortText: string; - } - - export interface ImportCompletionInfo { - textSpan: TextSpan; - entries: ImportCompletionEntry[]; - } - - export interface ImportCompletionEntry { - name: string; - kind: string; // see ScriptElementKind - sortText: string; + replacementSpan?: TextSpan; } export interface CompletionEntryDetails { @@ -4247,6 +4236,11 @@ namespace ts { const sourceFile = getValidSourceFile(fileName); + const importCompletionInfo = getImportModuleCompletionsAtPosition(fileName, position); + if (importCompletionInfo && importCompletionInfo.entries.length !== 0) { + return importCompletionInfo; + } + if (isInString(sourceFile, position)) { return getStringLiteralCompletionEntries(sourceFile, position); } @@ -4510,7 +4504,7 @@ namespace ts { } } - function getImportModuleCompletionsAtPosition(fileName: string, position: number): ImportCompletionInfo { + function getImportModuleCompletionsAtPosition(fileName: string, position: number): CompletionInfo { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); @@ -4526,8 +4520,9 @@ namespace ts { if (node.parent.kind === SyntaxKind.ImportDeclaration || isExpressionOfExternalModuleImportEqualsDeclaration(node) || isRequireCall(node.parent, false)) { // Get all known external module names or complete a path to a module return { - entries: getStringLiteralCompletionEntriesFromModuleNames(node), - textSpan: getDirectoryFragmentTextSpan((node).text, node.getStart() + 1) + isMemberCompletion: false, + isNewIdentifierLocation: true, + entries: getStringLiteralCompletionEntriesFromModuleNames(node) }; } } @@ -4539,20 +4534,22 @@ namespace ts { const scriptPath = node.getSourceFile().path; const scriptDirectory = getDirectoryPath(scriptPath); + + const span = getDirectoryFragmentTextSpan((node).text, node.getStart() + 1); if (isPathRelativeToScript(literalValue) || isRootedDiskPath(literalValue)) { const compilerOptions = program.getCompilerOptions(); if (compilerOptions.rootDirs) { return getCompletionEntriesForDirectoryFragmentWithRootDirs( - compilerOptions.rootDirs, literalValue, scriptDirectory, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, scriptPath); + compilerOptions.rootDirs, literalValue, scriptDirectory, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, span, scriptPath); } else { return getCompletionEntriesForDirectoryFragment( - literalValue, scriptDirectory, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, scriptPath); + literalValue, scriptDirectory, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, span, scriptPath); } } else { // Check for node modules - return getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory); + return getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, span); } } @@ -4577,21 +4574,21 @@ namespace ts { return deduplicate(map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory))); } - function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, exclude?: string): ImportCompletionEntry[] { + function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, exclude?: string): CompletionEntry[] { const basePath = program.getCompilerOptions().project || host.getCurrentDirectory(); const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames()); const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptPath, ignoreCase); - const result: ImportCompletionEntry[] = []; + const result: CompletionEntry[] = []; for (const baseDirectory of baseDirectories) { - getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, exclude, result); + getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, span, exclude, result); } return result; } - function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, exclude?: string, result: ImportCompletionEntry[] = []): ImportCompletionEntry[] { + function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] { fragment = getDirectoryPath(fragment); if (!fragment) { fragment = "./"; @@ -4623,11 +4620,7 @@ namespace ts { } for (const foundFile in foundFiles) { - result.push({ - name: foundFile, - kind: ScriptElementKind.scriptElement, - sortText: foundFile - }); + result.push(createCompletionEntryForModule(foundFile, ScriptElementKind.scriptElement, span)); } } @@ -4637,11 +4630,7 @@ namespace ts { for (const directory of directories) { const directoryName = getBaseFileName(normalizePath(directory)); - result.push({ - name: directoryName, - kind: ScriptElementKind.directory, - sortText: directoryName - }); + result.push(createCompletionEntryForModule(directoryName, ScriptElementKind.directory, span)); } } } @@ -4656,17 +4645,17 @@ namespace ts { * Modules from node_modules (i.e. those listed in package.json) * This includes all files that are found in node_modules/moduleName/ with acceptable file extensions */ - function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string): ImportCompletionEntry[] { + function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string, span: TextSpan): CompletionEntry[] { const options = program.getCompilerOptions(); const { baseUrl, paths } = options; - let result: ImportCompletionEntry[]; + let result: CompletionEntry[]; if (baseUrl) { const fileExtensions = getSupportedExtensions(options); const projectDir = options.project || host.getCurrentDirectory(); const absolute = isRootedDiskPath(baseUrl) ? baseUrl : combinePaths(projectDir, baseUrl); - result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false); + result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false, span); if (paths) { for (const path in paths) { @@ -4675,7 +4664,7 @@ namespace ts { if (paths[path]) { for (const pattern of paths[path]) { for (const match of getModulesForPathsPattern(fragment, baseUrl, pattern, fileExtensions)) { - result.push(createCompletionEntryForModule(match, ScriptElementKind.externalModuleName)); + result.push(createCompletionEntryForModule(match, ScriptElementKind.externalModuleName, span)); } } } @@ -4683,7 +4672,7 @@ namespace ts { else if (startsWith(path, fragment)) { const entry = paths[path] && paths[path].length === 1 && paths[path][0]; if (entry) { - result.push(createCompletionEntryForModule(path, ScriptElementKind.externalModuleName)); + result.push(createCompletionEntryForModule(path, ScriptElementKind.externalModuleName, span)); } } } @@ -4694,10 +4683,10 @@ namespace ts { result = []; } - getCompletionEntriesFromTypings(host, options, scriptPath, result); + getCompletionEntriesFromTypings(host, options, scriptPath, span, result); for (const moduleName of enumeratePotentialNonRelativeModules(fragment, scriptPath, options)) { - result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); + result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span)); } return result; @@ -4792,7 +4781,7 @@ namespace ts { return deduplicate(nonRelativeModules); } - function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number): ImportCompletionInfo { + function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number): CompletionInfo { const token = getTokenAtPosition(sourceFile, position); if (!token) { return undefined; @@ -4818,38 +4807,39 @@ namespace ts { const toComplete = match[3]; const scriptPath = getDirectoryPath(sourceFile.path); + let entries: CompletionEntry[]; if (kind === "path") { // Give completions for a relative path - const textSpan: TextSpan = getDirectoryFragmentTextSpan(toComplete, range.pos + prefix.length); - return { - entries: getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, sourceFile.path), - textSpan - }; + const span: TextSpan = getDirectoryFragmentTextSpan(toComplete, range.pos + prefix.length); + entries = getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, span, sourceFile.path); } else { // Give completions based on the typings available - const textSpan: TextSpan = { start: range.pos + prefix.length, length: match[0].length - prefix.length }; - return { - entries: getCompletionEntriesFromTypings(host, program.getCompilerOptions(), scriptPath), - textSpan - }; + const span: TextSpan = { start: range.pos + prefix.length, length: match[0].length - prefix.length }; + entries = getCompletionEntriesFromTypings(host, program.getCompilerOptions(), scriptPath, span); } + + return { + isMemberCompletion: false, + isNewIdentifierLocation: true, + entries + }; } return undefined; } - function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, result: ImportCompletionEntry[] = []): ImportCompletionEntry[] { + function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, span: TextSpan, result: CompletionEntry[] = []): CompletionEntry[] { // Check for typings specified in compiler options if (options.types) { for (const moduleName of options.types) { - result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName)); + result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span)); } } else if (host.getDirectories) { const typeRoots = getEffectiveTypeRoots(options, host.getCurrentDirectory()); for (const root of typeRoots) { - getCompletionEntriesFromDirectories(host, options, root, result); + getCompletionEntriesFromDirectories(host, options, root, span, result); } } @@ -4857,18 +4847,18 @@ namespace ts { // Also get all @types typings installed in visible node_modules directories for (const package of findPackageJsons(scriptPath)) { const typesDir = combinePaths(getDirectoryPath(package), "node_modules/@types"); - getCompletionEntriesFromDirectories(host, options, typesDir, result); + getCompletionEntriesFromDirectories(host, options, typesDir, span, result); } } return result; } - function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, result: ImportCompletionEntry[]) { + function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, span: TextSpan, result: CompletionEntry[]) { if (host.getDirectories && directoryProbablyExists(directory, host)) { for (let typeDirectory of host.getDirectories(directory)) { typeDirectory = normalizePath(typeDirectory); - result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName)); + result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName, span)); } } } @@ -4948,8 +4938,8 @@ namespace ts { } } - function createCompletionEntryForModule(name: string, kind: string): ImportCompletionEntry { - return { name, kind, sortText: name }; + function createCompletionEntryForModule(name: string, kind: string, replacementSpan: TextSpan): CompletionEntry { + return { name, kind, kindModifiers: ScriptElementKindModifier.none, sortText: name, replacementSpan }; } // Replace everything after the last directory seperator that appears @@ -8783,7 +8773,6 @@ namespace ts { getEncodedSyntacticClassifications, getEncodedSemanticClassifications, getCompletionsAtPosition, - getImportModuleCompletionsAtPosition, getCompletionEntryDetails, getSignatureHelpItems, getQuickInfoAtPosition, diff --git a/src/services/shims.ts b/src/services/shims.ts index 90d29dfa654..41b366e0857 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -140,7 +140,6 @@ namespace ts { getEncodedSemanticClassifications(fileName: string, start: number, length: number): string; getCompletionsAtPosition(fileName: string, position: number): string; - getImportModuleCompletionsAtPosition(fileName: string, position: number): string; getCompletionEntryDetails(fileName: string, position: number, entryName: string): string; getQuickInfoAtPosition(fileName: string, position: number): string; @@ -886,13 +885,6 @@ namespace ts { ); } - getImportModuleCompletionsAtPosition(fileName: string, position: number): string { - return this.forwardJSONCall( - `getImportModuleCompletionsAtPosition('${fileName}', ${position})`, - () => this.languageService.getImportModuleCompletionsAtPosition(fileName, position) - ); - } - /** Get a string based representation of a completion list entry details */ public getCompletionEntryDetails(fileName: string, position: number, entryName: string) { return this.forwardJSONCall( From 7261866c6cb8a312d2598a1abcd3d39f10ccbb81 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Wed, 31 Aug 2016 19:20:15 -0700 Subject: [PATCH 55/73] Cleaning up the completion code and tests --- src/harness/fourslash.ts | 154 ++++++------------ src/services/services.ts | 52 +++--- .../completionForStringLiteralImport1.ts | 8 +- .../completionForStringLiteralImport2.ts | 8 +- ...etionForStringLiteralNonrelativeImport1.ts | 22 +-- ...tionForStringLiteralNonrelativeImport10.ts | 4 +- ...tionForStringLiteralNonrelativeImport11.ts | 8 +- ...tionForStringLiteralNonrelativeImport12.ts | 10 +- ...etionForStringLiteralNonrelativeImport2.ts | 6 +- ...etionForStringLiteralNonrelativeImport3.ts | 8 +- ...etionForStringLiteralNonrelativeImport4.ts | 8 +- ...etionForStringLiteralNonrelativeImport5.ts | 12 +- ...etionForStringLiteralNonrelativeImport7.ts | 6 +- ...etionForStringLiteralNonrelativeImport8.ts | 6 +- ...etionForStringLiteralNonrelativeImport9.ts | 6 +- ...rStringLiteralNonrelativeImportTypings1.ts | 8 +- ...rStringLiteralNonrelativeImportTypings2.ts | 6 +- ...rStringLiteralNonrelativeImportTypings3.ts | 6 +- ...mpletionForStringLiteralRelativeImport1.ts | 28 ++-- ...mpletionForStringLiteralRelativeImport2.ts | 28 ++-- ...mpletionForStringLiteralRelativeImport3.ts | 20 +-- ...mpletionForStringLiteralRelativeImport4.ts | 10 +- .../completionForTripleSlashReference1.ts | 24 +-- .../completionForTripleSlashReference2.ts | 12 +- .../completionForTripleSlashReference3.ts | 20 +-- .../completionForTripleSlashReference4.ts | 4 +- tests/cases/fourslash/fourslash.ts | 5 +- 27 files changed, 206 insertions(+), 283 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 2b4373d79fa..41471ed9a03 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -598,38 +598,6 @@ namespace FourSlash { } } - public verifyImportModuleCompletionListItemsCountIsGreaterThan(count: number, negative: boolean) { - const completions = this.getImportModuleCompletionListAtCaret(); - const itemsCount = completions.entries.length; - - if (negative) { - if (itemsCount > count) { - this.raiseError(`Expected import module completion list items count to not be greater than ${count}, but is actually ${itemsCount}`); - } - } - else { - if (itemsCount <= count) { - this.raiseError(`Expected import module completion list items count to be greater than ${count}, but is actually ${itemsCount}`); - } - } - } - - public verifyImportModuleCompletionListIsEmpty(negative: boolean) { - const completions = this.getImportModuleCompletionListAtCaret(); - if ((!completions || completions.entries.length === 0) && negative) { - this.raiseError("Completion list is empty at caret at position " + this.activeFile.fileName + " " + this.currentCaretPosition); - } - else if (completions && completions.entries.length !== 0 && !negative) { - let errorMsg = "\n" + "Completion List contains: [" + completions.entries[0].name; - for (let i = 1; i < completions.entries.length; i++) { - errorMsg += ", " + completions.entries[i].name; - } - errorMsg += "]\n"; - - this.raiseError("Completion list is not empty at caret at position " + this.activeFile.fileName + " " + this.currentCaretPosition + errorMsg); - } - } - public verifyCompletionListStartsWithItemsInOrder(items: string[]): void { if (items.length === 0) { return; @@ -701,10 +669,10 @@ namespace FourSlash { } } - public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string) { + public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) { const completions = this.getCompletionListAtCaret(); if (completions) { - this.assertItemInCompletionList(completions.entries, symbol, text, documentation, kind); + this.assertItemInCompletionList(completions.entries, symbol, text, documentation, kind, spanIndex); } else { this.raiseError(`No completions at position '${this.currentCaretPosition}' when looking for '${symbol}'.`); @@ -720,25 +688,32 @@ namespace FourSlash { * @param expectedText the text associated with the symbol * @param expectedDocumentation the documentation text associated with the symbol * @param expectedKind the kind of symbol (see ScriptElementKind) + * @param spanIndex the index of the range that the completion item's replacement text span should match */ - public verifyCompletionListDoesNotContain(symbol: string, expectedText?: string, expectedDocumentation?: string, expectedKind?: string) { + public verifyCompletionListDoesNotContain(symbol: string, expectedText?: string, expectedDocumentation?: string, expectedKind?: string, spanIndex?: number) { const that = this; + let replacementSpan: ts.TextSpan; + if (spanIndex !== undefined) { + replacementSpan = this.getTextSpanForRangeAtIndex(spanIndex); + } + function filterByTextOrDocumentation(entry: ts.CompletionEntry) { const details = that.getCompletionEntryDetails(entry.name); const documentation = ts.displayPartsToString(details.documentation); const text = ts.displayPartsToString(details.displayParts); - if (expectedText && expectedDocumentation) { - return (documentation === expectedDocumentation && text === expectedText) ? true : false; + + // If any of the expected values are undefined, assume that users don't + // care about them. + if (replacementSpan && !TestState.textSpansEqual(replacementSpan, entry.replacementSpan)) { + return false; } - else if (expectedText && !expectedDocumentation) { - return text === expectedText ? true : false; + else if (expectedText && text !== expectedText) { + return false; } - else if (expectedDocumentation && !expectedText) { - return documentation === expectedDocumentation ? true : false; + else if (expectedDocumentation && documentation !== expectedDocumentation) { + return false; } - // Because expectedText and expectedDocumentation are undefined, we assume that - // users don"t care to compare them so we will treat that entry as if the entry has matching text and documentation - // and keep it in the list of filtered entry. + return true; } @@ -762,49 +737,15 @@ namespace FourSlash { if (expectedKind) { error += "Expected kind: " + expectedKind + " to equal: " + filterCompletions[0].kind + "."; } + if (replacementSpan) { + const spanText = filterCompletions[0].replacementSpan ? stringify(filterCompletions[0].replacementSpan) : undefined; + error += "Expected replacement span: " + stringify(replacementSpan) + " to equal: " + spanText + "."; + } this.raiseError(error); } } } - public verifyImportModuleCompletionListContains(symbol: string, rangeIndex?: number) { - const completions = this.getImportModuleCompletionListAtCaret(); - if (completions) { - const completion = ts.forEach(completions.entries, completion => completion.name === symbol ? completion : undefined); - if (!completion) { - const itemsString = completions.entries.map(item => item.name).join(",\n"); - this.raiseError(`Expected "${symbol}" to be in list [${itemsString}]`); - } - else if (rangeIndex !== undefined) { - const ranges = this.getRanges(); - if (ranges && ranges.length > rangeIndex) { - const range = ranges[rangeIndex]; - - const start = completion.replacementSpan.start; - const end = start + completion.replacementSpan.length; - if (range.start !== start || range.end !== end) { - this.raiseError(`Expected completion span for '${symbol}', ${stringify(completion.replacementSpan)}, to cover range ${stringify(range)}`); - } - } - else { - this.raiseError(`Expected completion span for '${symbol}' to cover range at index ${rangeIndex}, but no range was found at that index`); - } - } - } - else { - this.raiseError(`No import module completions at position '${this.currentCaretPosition}' when looking for '${symbol}'.`); - } - } - - public verifyImportModuleCompletionListDoesNotContain(symbol: string) { - const completions = this.getImportModuleCompletionListAtCaret(); - if (completions) { - if (ts.forEach(completions.entries, completion => completion.name === symbol)) { - this.raiseError(`Import module completion list did contain ${symbol}`); - } - } - } - public verifyCompletionEntryDetails(entryName: string, expectedText: string, expectedDocumentation?: string, kind?: string) { const details = this.getCompletionEntryDetails(entryName); @@ -891,10 +832,6 @@ namespace FourSlash { return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition); } - private getImportModuleCompletionListAtCaret() { - return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition); - } - private getCompletionEntryDetails(entryName: string) { return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName); } @@ -2241,7 +2178,7 @@ namespace FourSlash { return text.substring(startPos, endPos); } - private assertItemInCompletionList(items: ts.CompletionEntry[], name: string, text?: string, documentation?: string, kind?: string) { + private assertItemInCompletionList(items: ts.CompletionEntry[], name: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) { for (let i = 0; i < items.length; i++) { const item = items[i]; if (item.name === name) { @@ -2260,6 +2197,11 @@ namespace FourSlash { assert.equal(item.kind, kind, this.assertionMessageAtLastKnownMarker("completion item kind for " + name)); } + if (spanIndex !== undefined) { + const span = this.getTextSpanForRangeAtIndex(spanIndex); + assert.isTrue(TestState.textSpansEqual(span, item.replacementSpan), this.assertionMessageAtLastKnownMarker(stringify(span) + " does not equal " + stringify(item.replacementSpan) + " replacement span for " + name)); + } + return; } } @@ -2316,6 +2258,17 @@ namespace FourSlash { return `line ${(pos.line + 1)}, col ${pos.character}`; } + private getTextSpanForRangeAtIndex(index: number): ts.TextSpan { + const ranges = this.getRanges(); + if (ranges && ranges.length > index) { + const range = ranges[index]; + return { start: range.start, length: range.end - range.start }; + } + else { + this.raiseError("Supplied span index: " + index + " does not exist in range list of size: " + (ranges ? 0 : ranges.length)); + } + } + public getMarkerByName(markerName: string) { const markerPos = this.testData.markerPositions[markerName]; if (markerPos === undefined) { @@ -2339,6 +2292,10 @@ namespace FourSlash { public resetCancelled(): void { this.cancellationToken.resetCancelled(); } + + private static textSpansEqual(a: ts.TextSpan, b: ts.TextSpan) { + return a && b && a.start === b.start && a.length === b.length; + } } export function runFourSlashTest(basePath: string, testType: FourSlashTestType, fileName: string) { @@ -2909,12 +2866,12 @@ namespace FourSlashInterface { // Verifies the completion list contains the specified symbol. The // completion list is brought up if necessary - public completionListContains(symbol: string, text?: string, documentation?: string, kind?: string) { + public completionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) { if (this.negative) { - this.state.verifyCompletionListDoesNotContain(symbol, text, documentation, kind); + this.state.verifyCompletionListDoesNotContain(symbol, text, documentation, kind, spanIndex); } else { - this.state.verifyCompletionListContains(symbol, text, documentation, kind); + this.state.verifyCompletionListContains(symbol, text, documentation, kind, spanIndex); } } @@ -2924,23 +2881,6 @@ namespace FourSlashInterface { this.state.verifyCompletionListItemsCountIsGreaterThan(count, this.negative); } - public importModuleCompletionListContains(symbol: string, rangeIndex?: number): void { - if (this.negative) { - this.state.verifyImportModuleCompletionListDoesNotContain(symbol); - } - else { - this.state.verifyImportModuleCompletionListContains(symbol, rangeIndex); - } - } - - public importModuleCompletionListItemsCountIsGreaterThan(count: number): void { - this.state.verifyImportModuleCompletionListItemsCountIsGreaterThan(count, this.negative); - } - - public importModuleCompletionListIsEmpty(): void { - this.state.verifyImportModuleCompletionListIsEmpty(this.negative); - } - public assertHasRanges(ranges: FourSlash.Range[]) { assert(ranges.length !== 0, "Array of ranges is expected to be non-empty"); } diff --git a/src/services/services.ts b/src/services/services.ts index 66e450bf830..0bbaa9b5c3e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4236,9 +4236,8 @@ namespace ts { const sourceFile = getValidSourceFile(fileName); - const importCompletionInfo = getImportModuleCompletionsAtPosition(fileName, position); - if (importCompletionInfo && importCompletionInfo.entries.length !== 0) { - return importCompletionInfo; + if (isInReferenceComment(sourceFile, position)) { + return getTripleSlashReferenceCompletion(sourceFile, position); } if (isInString(sourceFile, position)) { @@ -4410,6 +4409,13 @@ namespace ts { // a['/*completion position*/'] return getStringLiteralCompletionEntriesFromElementAccess(node.parent); } + else if (node.parent.kind === SyntaxKind.ImportDeclaration || isExpressionOfExternalModuleImportEqualsDeclaration(node) || isRequireCall(node.parent, false)) { + // Get all known external module names or complete a path to a module + // i.e. import * as ns from "/*completion position*/"; + // import x = require("/*completion position*/"); + // var y = require("/*completion position*/"); + return getStringLiteralCompletionEntriesFromModuleNames(node); + } else { const argumentInfo = SignatureHelp.getContainingArgumentInfo(node, position, sourceFile); if (argumentInfo) { @@ -4502,55 +4508,35 @@ namespace ts { } } } - } - function getImportModuleCompletionsAtPosition(fileName: string, position: number): CompletionInfo { - synchronizeHostData(); - - const sourceFile = getValidSourceFile(fileName); - if (isInReferenceComment(sourceFile, position)) { - return getTripleSlashReferenceCompletion(sourceFile, position); - } - else if (isInString(sourceFile, position)) { - const node = findPrecedingToken(position, sourceFile); - if (!node || node.kind !== SyntaxKind.StringLiteral) { - return undefined; - } - - if (node.parent.kind === SyntaxKind.ImportDeclaration || isExpressionOfExternalModuleImportEqualsDeclaration(node) || isRequireCall(node.parent, false)) { - // Get all known external module names or complete a path to a module - return { - isMemberCompletion: false, - isNewIdentifierLocation: true, - entries: getStringLiteralCompletionEntriesFromModuleNames(node) - }; - } - } - - return undefined; - - function getStringLiteralCompletionEntriesFromModuleNames(node: StringLiteral) { + function getStringLiteralCompletionEntriesFromModuleNames(node: StringLiteral): CompletionInfo { const literalValue = normalizeSlashes(node.text); const scriptPath = node.getSourceFile().path; const scriptDirectory = getDirectoryPath(scriptPath); const span = getDirectoryFragmentTextSpan((node).text, node.getStart() + 1); + let entries: CompletionEntry[]; if (isPathRelativeToScript(literalValue) || isRootedDiskPath(literalValue)) { const compilerOptions = program.getCompilerOptions(); if (compilerOptions.rootDirs) { - return getCompletionEntriesForDirectoryFragmentWithRootDirs( + entries = getCompletionEntriesForDirectoryFragmentWithRootDirs( compilerOptions.rootDirs, literalValue, scriptDirectory, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, span, scriptPath); } else { - return getCompletionEntriesForDirectoryFragment( + entries = getCompletionEntriesForDirectoryFragment( literalValue, scriptDirectory, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/false, span, scriptPath); } } else { // Check for node modules - return getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, span); + entries = getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, span); } + return { + isMemberCompletion: false, + isNewIdentifierLocation: true, + entries + }; } /** diff --git a/tests/cases/fourslash/completionForStringLiteralImport1.ts b/tests/cases/fourslash/completionForStringLiteralImport1.ts index 482726f6305..4cdee889538 100644 --- a/tests/cases/fourslash/completionForStringLiteralImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralImport1.ts @@ -21,13 +21,13 @@ //// export var x = 9; goTo.marker("0"); -verify.importModuleCompletionListContains("someFile1", 0); +verify.completionListContains("someFile1", undefined, undefined, undefined, 0); goTo.marker("1"); -verify.importModuleCompletionListContains("someFile2", 1); +verify.completionListContains("someFile2", undefined, undefined, undefined, 1); goTo.marker("2"); -verify.importModuleCompletionListContains("some-module", 2); +verify.completionListContains("some-module", undefined, undefined, undefined, 2); goTo.marker("3"); -verify.importModuleCompletionListContains("fourslash", 3); \ No newline at end of file +verify.completionListContains("fourslash", undefined, undefined, undefined, 3); \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralImport2.ts b/tests/cases/fourslash/completionForStringLiteralImport2.ts index 43896f1f392..25de3d2240a 100644 --- a/tests/cases/fourslash/completionForStringLiteralImport2.ts +++ b/tests/cases/fourslash/completionForStringLiteralImport2.ts @@ -21,13 +21,13 @@ //// export var x = 9; goTo.marker("0"); -verify.importModuleCompletionListContains("someFile.ts", 0); +verify.completionListContains("someFile.ts", undefined, undefined, undefined, 0); goTo.marker("1"); -verify.importModuleCompletionListContains("some-module", 1); +verify.completionListContains("some-module", undefined, undefined, undefined, 1); goTo.marker("2"); -verify.importModuleCompletionListContains("someOtherFile.ts", 2); +verify.completionListContains("someOtherFile.ts", undefined, undefined, undefined, 2); goTo.marker("3"); -verify.importModuleCompletionListContains("some-module", 3); \ No newline at end of file +verify.completionListContains("some-module", undefined, undefined, undefined, 3); \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts index d6f38431ce1..c0e5ebbcd90 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport1.ts @@ -45,19 +45,19 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("fake-module"); - verify.importModuleCompletionListContains("fake-module-dev"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); + verify.completionListContains("fake-module"); + verify.completionListContains("fake-module-dev"); + verify.not.completionListItemsCountIsGreaterThan(2); goTo.marker(kind + "1"); - verify.importModuleCompletionListContains("index"); - verify.importModuleCompletionListContains("ts"); - verify.importModuleCompletionListContains("dts"); - verify.importModuleCompletionListContains("tsx"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(4); + verify.completionListContains("index"); + verify.completionListContains("ts"); + verify.completionListContains("dts"); + verify.completionListContains("tsx"); + verify.not.completionListItemsCountIsGreaterThan(4); goTo.marker(kind + "2"); - verify.importModuleCompletionListContains("fake-module"); - verify.importModuleCompletionListContains("fake-module-dev"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); + verify.completionListContains("fake-module"); + verify.completionListContains("fake-module-dev"); + verify.not.completionListItemsCountIsGreaterThan(2); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts index aa9fd976644..24a323bc6e5 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport10.ts @@ -28,8 +28,8 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListIsEmpty(); + verify.completionListIsEmpty(); goTo.marker(kind + "1"); - verify.importModuleCompletionListIsEmpty(); + verify.completionListIsEmpty(); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport11.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport11.ts index 70d39182a8a..7b81c4da106 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport11.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport11.ts @@ -28,11 +28,11 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("module"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); + verify.completionListContains("module"); + verify.not.completionListItemsCountIsGreaterThan(1); goTo.marker(kind + "1"); - verify.importModuleCompletionListContains("index"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); + verify.completionListContains("index"); + verify.not.completionListItemsCountIsGreaterThan(1); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport12.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport12.ts index 92db8d5a50e..ed202d68ec1 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport12.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport12.ts @@ -20,9 +20,9 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("module"); - verify.importModuleCompletionListContains("dev-module"); - verify.importModuleCompletionListContains("optional-module"); - verify.importModuleCompletionListContains("peer-module"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(4); + verify.completionListContains("module"); + verify.completionListContains("dev-module"); + verify.completionListContains("optional-module"); + verify.completionListContains("peer-module"); + verify.not.completionListItemsCountIsGreaterThan(4); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts index bc2529e86f9..bac5c54ff23 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport2.ts @@ -31,7 +31,7 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("repeated"); - verify.importModuleCompletionListContains("other"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); + verify.completionListContains("repeated"); + verify.completionListContains("other"); + verify.not.completionListItemsCountIsGreaterThan(2); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts index 9b1fd451185..d2103f6f10c 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport3.ts @@ -31,8 +31,8 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("ts"); - verify.importModuleCompletionListContains("tsx"); - verify.importModuleCompletionListContains("dts"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); + verify.completionListContains("ts"); + verify.completionListContains("tsx"); + verify.completionListContains("dts"); + verify.not.completionListItemsCountIsGreaterThan(3); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts index b9be9f90213..b95fd96f380 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport4.ts @@ -27,8 +27,8 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("fake-module"); - verify.importModuleCompletionListContains("fake-module2"); - verify.importModuleCompletionListContains("fake-module3"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); + verify.completionListContains("fake-module"); + verify.completionListContains("fake-module2"); + verify.completionListContains("fake-module3"); + verify.not.completionListItemsCountIsGreaterThan(3); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts index 4afa5854bf8..c8c9a18ff85 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport5.ts @@ -26,14 +26,14 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("ambientModule"); - verify.importModuleCompletionListContains("otherAmbientModule"); - verify.importModuleCompletionListContains("otherOtherAmbientModule"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); + verify.completionListContains("ambientModule"); + verify.completionListContains("otherAmbientModule"); + verify.completionListContains("otherOtherAmbientModule"); + verify.not.completionListItemsCountIsGreaterThan(3); goTo.marker(kind + "1"); - verify.importModuleCompletionListContains("ambientModule"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); + verify.completionListContains("ambientModule"); + verify.not.completionListItemsCountIsGreaterThan(1); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts index eca5a3e5412..57ba6e440cf 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport7.ts @@ -24,7 +24,7 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("module"); - verify.importModuleCompletionListContains("module-from-node"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); + verify.completionListContains("module"); + verify.completionListContains("module-from-node"); + verify.not.completionListItemsCountIsGreaterThan(2); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts index 54893991432..603e8d1104d 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport8.ts @@ -45,11 +45,11 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("0test"); + verify.completionListContains("0test"); goTo.marker(kind + "1"); - verify.importModuleCompletionListContains("1test"); + verify.completionListContains("1test"); goTo.marker(kind + "2"); - verify.importModuleCompletionListContains("2test"); + verify.completionListContains("2test"); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts index bcdb731d1f1..8703c608462 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport9.ts @@ -30,7 +30,7 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("module1"); - verify.importModuleCompletionListContains("module2"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); + verify.completionListContains("module1"); + verify.completionListContains("module2"); + verify.not.completionListItemsCountIsGreaterThan(2); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts index 9cf2af9c875..687103629e2 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings1.ts @@ -28,8 +28,8 @@ const kinds = ["types_ref", "import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("module-x"); - verify.importModuleCompletionListContains("module-y"); - verify.importModuleCompletionListContains("module-z"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); + verify.completionListContains("module-x"); + verify.completionListContains("module-y"); + verify.completionListContains("module-z"); + verify.not.completionListItemsCountIsGreaterThan(3); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts index 1452c6c1c61..12a4fcf243f 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings2.ts @@ -26,7 +26,7 @@ const kinds = ["types_ref", "import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("module-x"); - verify.importModuleCompletionListContains("module-z"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); + verify.completionListContains("module-x"); + verify.completionListContains("module-z"); + verify.not.completionListItemsCountIsGreaterThan(2); } diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts index f4a4dbaaa99..14cf71ad0dc 100644 --- a/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImportTypings3.ts @@ -22,7 +22,7 @@ const kinds = ["types_ref", "import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("module-x"); - verify.importModuleCompletionListContains("module-y"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); + verify.completionListContains("module-x"); + verify.completionListContains("module-y"); + verify.not.completionListItemsCountIsGreaterThan(2); } diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts index 909e2c6b14d..3ba82fb0acf 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport1.ts @@ -47,24 +47,24 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListIsEmpty(); + verify.completionListIsEmpty(); goTo.marker(kind + "1"); - verify.importModuleCompletionListContains("f1"); - verify.importModuleCompletionListContains("f2"); - verify.importModuleCompletionListContains("e1"); - verify.importModuleCompletionListContains("folder"); - verify.importModuleCompletionListContains("parentTest"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(5); + verify.completionListContains("f1"); + verify.completionListContains("f2"); + verify.completionListContains("e1"); + verify.completionListContains("folder"); + verify.completionListContains("parentTest"); + verify.not.completionListItemsCountIsGreaterThan(5); goTo.marker(kind + "2"); - verify.importModuleCompletionListContains("f3"); - verify.importModuleCompletionListContains("h1"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); + verify.completionListContains("f3"); + verify.completionListContains("h1"); + verify.not.completionListItemsCountIsGreaterThan(2); goTo.marker(kind + "3"); - verify.importModuleCompletionListContains("f4"); - verify.importModuleCompletionListContains("g1"); - verify.importModuleCompletionListContains("sub"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(3); + verify.completionListContains("f4"); + verify.completionListContains("g1"); + verify.completionListContains("sub"); + verify.not.completionListItemsCountIsGreaterThan(3); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts index 8c8cb405cd3..18a80ab3481 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport2.ts @@ -34,20 +34,20 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("f1"); - verify.importModuleCompletionListContains("f2"); - verify.importModuleCompletionListContains("f3"); - verify.importModuleCompletionListContains("f4"); - verify.importModuleCompletionListContains("e1"); - verify.importModuleCompletionListContains("e2"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(6); + verify.completionListContains("f1"); + verify.completionListContains("f2"); + verify.completionListContains("f3"); + verify.completionListContains("f4"); + verify.completionListContains("e1"); + verify.completionListContains("e2"); + verify.not.completionListItemsCountIsGreaterThan(6); goTo.marker(kind + "1"); - verify.importModuleCompletionListContains("f1"); - verify.importModuleCompletionListContains("f2"); - verify.importModuleCompletionListContains("f3"); - verify.importModuleCompletionListContains("f4"); - verify.importModuleCompletionListContains("e1"); - verify.importModuleCompletionListContains("e2"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(6); + verify.completionListContains("f1"); + verify.completionListContains("f2"); + verify.completionListContains("f3"); + verify.completionListContains("f4"); + verify.completionListContains("e1"); + verify.completionListContains("e2"); + verify.not.completionListItemsCountIsGreaterThan(6); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts index 82c3ea7d1e4..915cebdf6e7 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport3.ts @@ -34,18 +34,18 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("fourslash"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); + verify.completionListContains("fourslash"); + verify.not.completionListItemsCountIsGreaterThan(1); goTo.marker(kind + "1"); - verify.importModuleCompletionListContains("fourslash"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); + verify.completionListContains("fourslash"); + verify.not.completionListItemsCountIsGreaterThan(1); goTo.marker(kind + "2"); - verify.importModuleCompletionListContains("f1"); - verify.importModuleCompletionListContains("f2"); - verify.importModuleCompletionListContains("e1"); - verify.importModuleCompletionListContains("folder"); - verify.importModuleCompletionListContains("tests"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(5); + verify.completionListContains("f1"); + verify.completionListContains("f2"); + verify.completionListContains("e1"); + verify.completionListContains("folder"); + verify.completionListContains("tests"); + verify.not.completionListItemsCountIsGreaterThan(5); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts index ba9f95a08c2..b9750de9c17 100644 --- a/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts +++ b/tests/cases/fourslash/completionForStringLiteralRelativeImport4.ts @@ -42,11 +42,11 @@ const kinds = ["import_as", "import_equals", "require"]; for (const kind of kinds) { goTo.marker(kind + "0"); - verify.importModuleCompletionListContains("module0"); - verify.importModuleCompletionListContains("module1"); - verify.importModuleCompletionListContains("module2"); - verify.importModuleCompletionListContains("more"); + verify.completionListContains("module0"); + verify.completionListContains("module1"); + verify.completionListContains("module2"); + verify.completionListContains("more"); // Should not contain itself - verify.not.importModuleCompletionListItemsCountIsGreaterThan(4); + verify.not.completionListItemsCountIsGreaterThan(4); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForTripleSlashReference1.ts b/tests/cases/fourslash/completionForTripleSlashReference1.ts index f8ea7fc4d44..2ce4de2e72e 100644 --- a/tests/cases/fourslash/completionForTripleSlashReference1.ts +++ b/tests/cases/fourslash/completionForTripleSlashReference1.ts @@ -32,20 +32,20 @@ for (let i = 0; i < 5; i++) { goTo.marker("" + i); - verify.importModuleCompletionListContains("f1.ts"); - verify.importModuleCompletionListContains("f1.d.ts"); - verify.importModuleCompletionListContains("f2.tsx"); - verify.importModuleCompletionListContains("e1.ts"); - verify.importModuleCompletionListContains("parentTest"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(5); + verify.completionListContains("f1.ts"); + verify.completionListContains("f1.d.ts"); + verify.completionListContains("f2.tsx"); + verify.completionListContains("e1.ts"); + verify.completionListContains("parentTest"); + verify.not.completionListItemsCountIsGreaterThan(5); } goTo.marker("5"); -verify.importModuleCompletionListContains("g1.ts"); -verify.importModuleCompletionListContains("sub"); -verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); +verify.completionListContains("g1.ts"); +verify.completionListContains("sub"); +verify.not.completionListItemsCountIsGreaterThan(2); goTo.marker("6"); -verify.importModuleCompletionListContains("g1.ts"); -verify.importModuleCompletionListContains("sub"); -verify.not.importModuleCompletionListItemsCountIsGreaterThan(2); \ No newline at end of file +verify.completionListContains("g1.ts"); +verify.completionListContains("sub"); +verify.not.completionListItemsCountIsGreaterThan(2); \ No newline at end of file diff --git a/tests/cases/fourslash/completionForTripleSlashReference2.ts b/tests/cases/fourslash/completionForTripleSlashReference2.ts index 0530e5e4093..e6553ce8e73 100644 --- a/tests/cases/fourslash/completionForTripleSlashReference2.ts +++ b/tests/cases/fourslash/completionForTripleSlashReference2.ts @@ -24,10 +24,10 @@ for (let i = 0; i < 5; i++) { goTo.marker("" + i); - verify.importModuleCompletionListContains("f1.ts"); - verify.importModuleCompletionListContains("f1.js"); - verify.importModuleCompletionListContains("f1.d.ts"); - verify.importModuleCompletionListContains("f2.tsx"); - verify.importModuleCompletionListContains("f4.jsx"); - verify.not.importModuleCompletionListItemsCountIsGreaterThan(5); + verify.completionListContains("f1.ts"); + verify.completionListContains("f1.js"); + verify.completionListContains("f1.d.ts"); + verify.completionListContains("f2.tsx"); + verify.completionListContains("f4.jsx"); + verify.not.completionListItemsCountIsGreaterThan(5); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionForTripleSlashReference3.ts b/tests/cases/fourslash/completionForTripleSlashReference3.ts index d27d0e658c2..798e556be58 100644 --- a/tests/cases/fourslash/completionForTripleSlashReference3.ts +++ b/tests/cases/fourslash/completionForTripleSlashReference3.ts @@ -27,17 +27,17 @@ //// /*e2*/ goTo.marker("0"); -verify.importModuleCompletionListContains("fourslash"); -verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); +verify.completionListContains("fourslash"); +verify.not.completionListItemsCountIsGreaterThan(1); goTo.marker("1"); -verify.importModuleCompletionListContains("fourslash"); -verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); +verify.completionListContains("fourslash"); +verify.not.completionListItemsCountIsGreaterThan(1); goTo.marker("2"); -verify.importModuleCompletionListContains("f1.ts"); -verify.importModuleCompletionListContains("f2.tsx"); -verify.importModuleCompletionListContains("e1.ts"); -verify.importModuleCompletionListContains("folder"); -verify.importModuleCompletionListContains("tests"); -verify.not.importModuleCompletionListItemsCountIsGreaterThan(5); \ No newline at end of file +verify.completionListContains("f1.ts"); +verify.completionListContains("f2.tsx"); +verify.completionListContains("e1.ts"); +verify.completionListContains("folder"); +verify.completionListContains("tests"); +verify.not.completionListItemsCountIsGreaterThan(5); \ No newline at end of file diff --git a/tests/cases/fourslash/completionForTripleSlashReference4.ts b/tests/cases/fourslash/completionForTripleSlashReference4.ts index 9cedf4932b2..2eb58646675 100644 --- a/tests/cases/fourslash/completionForTripleSlashReference4.ts +++ b/tests/cases/fourslash/completionForTripleSlashReference4.ts @@ -38,6 +38,6 @@ goTo.marker("0"); -verify.importModuleCompletionListContains("module0.ts"); +verify.completionListContains("module0.ts"); -verify.not.importModuleCompletionListItemsCountIsGreaterThan(1); \ No newline at end of file +verify.not.completionListItemsCountIsGreaterThan(1); \ No newline at end of file diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 948477b78dd..525fcc608f5 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -121,13 +121,10 @@ declare namespace FourSlashInterface { constructor(negative?: boolean); memberListContains(symbol: string, text?: string, documenation?: string, kind?: string): void; memberListCount(expectedCount: number): void; - completionListContains(symbol: string, text?: string, documentation?: string, kind?: string): void; + completionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number): void; completionListItemsCountIsGreaterThan(count: number): void; completionListIsEmpty(): void; completionListAllowsNewIdentifier(): void; - importModuleCompletionListContains(symbol: string, rangeIndex?: number): void; - importModuleCompletionListItemsCountIsGreaterThan(count: number): void; - importModuleCompletionListIsEmpty(): void; memberListIsEmpty(): void; signatureHelpPresent(): void; errorExistsBetweenMarkers(startMarker: string, endMarker: string): void; From ec7e8585a9e7f2a60c3b9cfbcacd787b3e49d8ff Mon Sep 17 00:00:00 2001 From: Rostislav Galimsky Date: Thu, 1 Sep 2016 13:12:05 +0300 Subject: [PATCH 56/73] fix issue --- .../testCode/testCode/formatting/classes.ts | 79 ------------ .../testCode/formatting/classesBaseline.ts | 79 ------------ .../testCode/formatting/colonAndQMark.ts | 4 - .../formatting/colonAndQMarkBaseline.ts | 4 - .../formatting/documentReadyFunction.ts | 3 - .../documentReadyFunctionBaseLine.ts | 3 - .../testCode/formatting/emptyBlock.ts | 1 - .../testCode/formatting/emptyBlockBaseline.ts | 1 - .../formatting/emptyInterfaceLiteral.ts | 10 -- .../emptyInterfaceLiteralBaseLine.ts | 10 -- .../testCode/formatting/fatArrowFunctions.ts | 112 ------------------ .../formatting/fatArrowFunctionsBaseline.ts | 112 ------------------ .../formatting/formatDebuggerStatement.ts | 2 - .../formatDebuggerStatementBaseline.ts | 2 - .../formatvariableDeclarationList.ts | 13 -- .../formatvariableDeclarationListBaseline.ts | 13 -- .../testCode/formatting/implicitModule.ts | 3 - .../formatting/implicitModuleBaseline.ts | 3 - .../testCode/formatting/importDeclaration.ts | 6 - .../formatting/importDeclarationBaseline.ts | 6 - .../testCode/testCode/formatting/main.ts | 95 --------------- .../testCode/formatting/mainBaseline.ts | 98 --------------- .../testCode/formatting/moduleIndentation.ts | 3 - .../formatting/moduleIndentationBaseline.ts | 3 - .../testCode/testCode/formatting/modules.ts | 76 ------------ .../testCode/formatting/modulesBaseline.ts | 76 ------------ .../testCode/formatting/objectLiteral.ts | 27 ----- .../formatting/objectLiteralBaseline.ts | 31 ----- .../testCode/formatting/onClosingBracket.ts | 32 ----- .../formatting/onClosingBracketBaseLine.ts | 28 ----- .../testCode/formatting/onSemiColon.ts | 1 - .../formatting/onSemiColonBaseline.ts | 1 - .../formatting/spaceAfterConstructor.ts | 1 - .../spaceAfterConstructorBaseline.ts | 1 - .../testCode/formatting/tabAfterCloseCurly.ts | 10 -- .../formatting/tabAfterCloseCurlyBaseline.ts | 9 -- .../formatting/typescriptConstructs.ts | 65 ---------- .../typescriptConstructsBaseline.ts | 58 --------- .../testCode/testCode/formatting/various.ts | 17 --- .../testCode/formatting/variousBaseline.ts | 17 --- .../testCode/formatting/withStatement.ts | 9 -- .../formatting/withStatementBaseline.ts | 6 - 42 files changed, 1130 deletions(-) delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/classes.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/classesBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/colonAndQMark.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/colonAndQMarkBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/documentReadyFunction.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/documentReadyFunctionBaseLine.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyBlock.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyBlockBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyInterfaceLiteral.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyInterfaceLiteralBaseLine.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/fatArrowFunctions.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/fatArrowFunctionsBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/formatDebuggerStatement.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/formatDebuggerStatementBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/formatvariableDeclarationList.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/formatvariableDeclarationListBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/implicitModule.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/implicitModuleBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/importDeclaration.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/importDeclarationBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/main.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/mainBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/moduleIndentation.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/moduleIndentationBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/modules.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/modulesBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/objectLiteral.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/objectLiteralBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/onClosingBracket.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/onClosingBracketBaseLine.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/onSemiColon.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/onSemiColonBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/spaceAfterConstructor.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/spaceAfterConstructorBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/tabAfterCloseCurly.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/tabAfterCloseCurlyBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/typescriptConstructs.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/typescriptConstructsBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/various.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/variousBaseline.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/withStatement.ts delete mode 100644 src/harness/unittests/services/formatting/testCode/testCode/formatting/withStatementBaseline.ts diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/classes.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/classes.ts deleted file mode 100644 index e779f69810f..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/classes.ts +++ /dev/null @@ -1,79 +0,0 @@ - class a { - constructor ( n : number ) ; - constructor ( s : string ) ; - constructor ( ns : any ) { - - } - - public pgF ( ) { } ; - - public pv ; - public get d ( ) { - return 30 ; - } - public set d ( ) { - } - - public static get p2 ( ) { - return { x : 30 , y : 40 } ; - } - - private static d2 ( ) { - } - private static get p3 ( ) { - return "string" ; - } - private pv3 ; - - private foo ( n : number ) : string ; - private foo ( s : string ) : string ; - private foo ( ns : any ) { - return ns.toString ( ) ; - } -} - - class b extends a { -} - - class m1b { - -} - - interface m1ib { - - } - class c extends m1b { -} - - class ib2 implements m1ib { -} - - declare class aAmbient { - constructor ( n : number ) ; - constructor ( s : string ) ; - public pgF ( ) : void ; - public pv ; - public d : number ; - static p2 : { x : number ; y : number ; } ; - static d2 ( ) ; - static p3 ; - private pv3 ; - private foo ( s ) ; -} - - class d { - private foo ( n : number ) : string ; - private foo ( ns : any ) { - return ns.toString ( ) ; - } - private foo ( s : string ) : string ; -} - - class e { - private foo ( ns : any ) { - return ns.toString ( ) ; - } - private foo ( s : string ) : string ; - private foo ( n : number ) : string ; -} - diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/classesBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/classesBaseline.ts deleted file mode 100644 index e7e69b44125..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/classesBaseline.ts +++ /dev/null @@ -1,79 +0,0 @@ -class a { - constructor(n: number); - constructor(s: string); - constructor(ns: any) { - - } - - public pgF() { }; - - public pv; - public get d() { - return 30; - } - public set d() { - } - - public static get p2() { - return { x: 30, y: 40 }; - } - - private static d2() { - } - private static get p3() { - return "string"; - } - private pv3; - - private foo(n: number): string; - private foo(s: string): string; - private foo(ns: any) { - return ns.toString(); - } -} - -class b extends a { -} - -class m1b { - -} - -interface m1ib { - -} -class c extends m1b { -} - -class ib2 implements m1ib { -} - -declare class aAmbient { - constructor(n: number); - constructor(s: string); - public pgF(): void; - public pv; - public d: number; - static p2: { x: number; y: number; }; - static d2(); - static p3; - private pv3; - private foo(s); -} - -class d { - private foo(n: number): string; - private foo(ns: any) { - return ns.toString(); - } - private foo(s: string): string; -} - -class e { - private foo(ns: any) { - return ns.toString(); - } - private foo(s: string): string; - private foo(n: number): string; -} - diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/colonAndQMark.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/colonAndQMark.ts deleted file mode 100644 index 5562e142046..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/colonAndQMark.ts +++ /dev/null @@ -1,4 +0,0 @@ -class foo { - constructor (n?: number, m? = 5, o?: string = "") { } - x:number = 1?2:3; -} diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/colonAndQMarkBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/colonAndQMarkBaseline.ts deleted file mode 100644 index 52bbe56251d..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/colonAndQMarkBaseline.ts +++ /dev/null @@ -1,4 +0,0 @@ -class foo { - constructor(n?: number, m? = 5, o?: string = "") { } - x: number = 1 ? 2 : 3; -} diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/documentReadyFunction.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/documentReadyFunction.ts deleted file mode 100644 index 35daa4d895c..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/documentReadyFunction.ts +++ /dev/null @@ -1,3 +0,0 @@ -$ ( document ) . ready ( function ( ) { - alert ( 'i am ready' ) ; - } ); \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/documentReadyFunctionBaseLine.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/documentReadyFunctionBaseLine.ts deleted file mode 100644 index 838ef682207..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/documentReadyFunctionBaseLine.ts +++ /dev/null @@ -1,3 +0,0 @@ -$(document).ready(function() { - alert('i am ready'); -}); \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyBlock.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyBlock.ts deleted file mode 100644 index 9e26dfeeb6e..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyBlock.ts +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyBlockBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyBlockBaseline.ts deleted file mode 100644 index 6f31cf5a2e6..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyBlockBaseline.ts +++ /dev/null @@ -1 +0,0 @@ -{ } \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyInterfaceLiteral.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyInterfaceLiteral.ts deleted file mode 100644 index 1feec453d03..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyInterfaceLiteral.ts +++ /dev/null @@ -1,10 +0,0 @@ - function foo ( x : { } ) { } - -foo ( { } ) ; - - - - interface bar { - x : { } ; - y : ( ) => { } ; - } \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyInterfaceLiteralBaseLine.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyInterfaceLiteralBaseLine.ts deleted file mode 100644 index 04f3c0bc9b9..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/emptyInterfaceLiteralBaseLine.ts +++ /dev/null @@ -1,10 +0,0 @@ -function foo(x: {}) { } - -foo({}); - - - -interface bar { - x: {}; - y: () => {}; -} \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/fatArrowFunctions.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/fatArrowFunctions.ts deleted file mode 100644 index ebbf54557fa..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/fatArrowFunctions.ts +++ /dev/null @@ -1,112 +0,0 @@ -// valid - ( ) => 1 ; - ( arg ) => 2 ; - arg => 2 ; - ( arg = 1 ) => 3 ; - ( arg ? ) => 4 ; - ( arg : number ) => 5 ; - ( arg : number = 0 ) => 6 ; - ( arg ? : number ) => 7 ; - ( ... arg : number [ ] ) => 8 ; - ( arg1 , arg2 ) => 12 ; - ( arg1 = 1 , arg2 =3 ) => 13 ; - ( arg1 ? , arg2 ? ) => 14 ; - ( arg1 : number , arg2 : number ) => 15 ; - ( arg1 : number = 0 , arg2 : number = 1 ) => 16 ; - ( arg1 ? : number , arg2 ? : number ) => 17 ; - ( arg1 , ... arg2 : number [ ] ) => 18 ; - ( arg1 , arg2 ? : number ) => 19 ; - -// in paren - ( ( ) => 21 ) ; - ( ( arg ) => 22 ) ; - ( ( arg = 1 ) => 23 ) ; - ( ( arg ? ) => 24 ) ; - ( ( arg : number ) => 25 ) ; - ( ( arg : number = 0 ) => 26 ) ; - ( ( arg ? : number ) => 27 ) ; - ( ( ... arg : number [ ] ) => 28 ) ; - -// in multiple paren - ( ( ( ( ( arg ) => { return 32 ; } ) ) ) ) ; - -// in ternary exression - false ? ( ) => 41 : null ; - false ? ( arg ) => 42 : null ; - false ? ( arg = 1 ) => 43 : null ; - false ? ( arg ? ) => 44 : null ; - false ? ( arg : number ) => 45 : null ; - false ? ( arg ? : number ) => 46 : null ; - false ? ( arg ? : number = 0 ) => 47 : null ; - false ? ( ... arg : number [ ] ) => 48 : null ; - -// in ternary exression within paren - false ? ( ( ) => 51 ) : null ; - false ? ( ( arg ) => 52 ) : null ; - false ? ( ( arg = 1 ) => 53 ) : null ; - false ? ( ( arg ? ) => 54 ) : null ; - false ? ( ( arg : number ) => 55 ) : null ; - false ? ( ( arg ? : number ) => 56 ) : null ; - false ? ( ( arg ? : number = 0 ) => 57 ) : null ; - false ? ( ( ... arg : number [ ] ) => 58 ) : null ; - -// ternary exression's else clause - false ? null : ( ) => 61 ; - false ? null : ( arg ) => 62 ; - false ? null : ( arg = 1 ) => 63 ; - false ? null : ( arg ? ) => 64 ; - false ? null : ( arg : number ) => 65 ; - false ? null : ( arg ? : number ) => 66 ; - false ? null : ( arg ? : number = 0 ) => 67 ; - false ? null : ( ... arg : number [ ] ) => 68 ; - - -// nested ternary expressions - ( a ? ) => { return a ; } ? ( b ? ) => { return b ; } : ( c ? ) => { return c ; } ; - -//multiple levels - ( a ? ) => { return a ; } ? ( b ) => ( c ) => 81 : ( c ) => ( d ) => 82 ; - - -// In Expressions - ( ( arg ) => 90 ) instanceof Function ; - ( ( arg = 1 ) => 91 ) instanceof Function ; - ( ( arg ? ) => 92 ) instanceof Function ; - ( ( arg : number ) => 93 ) instanceof Function ; - ( ( arg : number = 1 ) => 94 ) instanceof Function ; - ( ( arg ? : number ) => 95 ) instanceof Function ; - ( ( ... arg : number [ ] ) => 96 ) instanceof Function ; - -'' + ( arg ) => 100 ; - ( ( arg ) => 0 ) + '' + ( arg ) => 101 ; - ( ( arg = 1 ) => 0 ) + '' + ( arg = 2 ) => 102 ; - ( ( arg ? ) => 0 ) + '' + ( arg ? ) => 103 ; - ( ( arg : number ) => 0 ) + '' + ( arg : number ) => 104 ; - ( ( arg : number = 1 ) => 0 ) + '' + ( arg : number = 2 ) => 105 ; - ( ( arg ? : number = 1 ) => 0 ) + '' + ( arg ? : number = 2 ) => 106 ; - ( ( ... arg : number [ ] ) => 0 ) + '' + ( ... arg : number [ ] ) => 107 ; - ( ( arg1 , arg2 ? ) => 0 ) + '' + ( arg1 , arg2 ? ) => 108 ; - ( ( arg1 , ... arg2 : number [ ] ) => 0 ) + '' + ( arg1 , ... arg2 : number [ ] ) => 108 ; - - -// Function Parameters -function foo ( ... arg : any [ ] ) { } - -foo ( - ( a ) => 110 , - ( ( a ) => 111 ) , - ( a ) => { - return 112 ; - } , - ( a ? ) => 113 , - ( a , b ? ) => 114 , - ( a : number ) => 115 , - ( a : number = 0 ) => 116 , - ( a = 0 ) => 117 , - ( a ? : number = 0 ) => 118 , - ( a ? , b ? : number = 0 ) => 118 , - ( ... a : number [ ] ) => 119 , - ( a , b ? = 0 , ... c : number [ ] ) => 120 , - ( a ) => ( b ) => ( c ) => 121 , - false ? ( a ) => 0 : ( b ) => 122 - ) ; \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/fatArrowFunctionsBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/fatArrowFunctionsBaseline.ts deleted file mode 100644 index 7a1ef86f5af..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/fatArrowFunctionsBaseline.ts +++ /dev/null @@ -1,112 +0,0 @@ -// valid -() => 1; -(arg) => 2; -arg => 2; -(arg = 1) => 3; -(arg?) => 4; -(arg: number) => 5; -(arg: number = 0) => 6; -(arg?: number) => 7; -(...arg: number[]) => 8; -(arg1, arg2) => 12; -(arg1 = 1, arg2 = 3) => 13; -(arg1?, arg2?) => 14; -(arg1: number, arg2: number) => 15; -(arg1: number = 0, arg2: number = 1) => 16; -(arg1?: number, arg2?: number) => 17; -(arg1, ...arg2: number[]) => 18; -(arg1, arg2?: number) => 19; - -// in paren -(() => 21); -((arg) => 22); -((arg = 1) => 23); -((arg?) => 24); -((arg: number) => 25); -((arg: number = 0) => 26); -((arg?: number) => 27); -((...arg: number[]) => 28); - -// in multiple paren -(((((arg) => { return 32; })))); - -// in ternary exression -false ? () => 41 : null; -false ? (arg) => 42 : null; -false ? (arg = 1) => 43 : null; -false ? (arg?) => 44 : null; -false ? (arg: number) => 45 : null; -false ? (arg?: number) => 46 : null; -false ? (arg?: number = 0) => 47 : null; -false ? (...arg: number[]) => 48 : null; - -// in ternary exression within paren -false ? (() => 51) : null; -false ? ((arg) => 52) : null; -false ? ((arg = 1) => 53) : null; -false ? ((arg?) => 54) : null; -false ? ((arg: number) => 55) : null; -false ? ((arg?: number) => 56) : null; -false ? ((arg?: number = 0) => 57) : null; -false ? ((...arg: number[]) => 58) : null; - -// ternary exression's else clause -false ? null : () => 61; -false ? null : (arg) => 62; -false ? null : (arg = 1) => 63; -false ? null : (arg?) => 64; -false ? null : (arg: number) => 65; -false ? null : (arg?: number) => 66; -false ? null : (arg?: number = 0) => 67; -false ? null : (...arg: number[]) => 68; - - -// nested ternary expressions -(a?) => { return a; } ? (b?) => { return b; } : (c?) => { return c; }; - -//multiple levels -(a?) => { return a; } ? (b) => (c) => 81 : (c) => (d) => 82; - - -// In Expressions -((arg) => 90) instanceof Function; -((arg = 1) => 91) instanceof Function; -((arg?) => 92) instanceof Function; -((arg: number) => 93) instanceof Function; -((arg: number = 1) => 94) instanceof Function; -((arg?: number) => 95) instanceof Function; -((...arg: number[]) => 96) instanceof Function; - -'' + (arg) => 100; -((arg) => 0) + '' + (arg) => 101; -((arg = 1) => 0) + '' + (arg = 2) => 102; -((arg?) => 0) + '' + (arg?) => 103; -((arg: number) => 0) + '' + (arg: number) => 104; -((arg: number = 1) => 0) + '' + (arg: number = 2) => 105; -((arg?: number = 1) => 0) + '' + (arg?: number = 2) => 106; -((...arg: number[]) => 0) + '' + (...arg: number[]) => 107; -((arg1, arg2?) => 0) + '' + (arg1, arg2?) => 108; -((arg1, ...arg2: number[]) => 0) + '' + (arg1, ...arg2: number[]) => 108; - - -// Function Parameters -function foo(...arg: any[]) { } - -foo( - (a) => 110, - ((a) => 111), - (a) => { - return 112; - }, - (a?) => 113, - (a, b?) => 114, - (a: number) => 115, - (a: number = 0) => 116, - (a = 0) => 117, - (a?: number = 0) => 118, - (a?, b?: number = 0) => 118, - (...a: number[]) => 119, - (a, b? = 0, ...c: number[]) => 120, - (a) => (b) => (c) => 121, - false ? (a) => 0 : (b) => 122 - ); \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/formatDebuggerStatement.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/formatDebuggerStatement.ts deleted file mode 100644 index 314cd416a81..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/formatDebuggerStatement.ts +++ /dev/null @@ -1,2 +0,0 @@ -if(false){debugger;} - if ( false ) { debugger ; } \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/formatDebuggerStatementBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/formatDebuggerStatementBaseline.ts deleted file mode 100644 index c03acf91ecf..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/formatDebuggerStatementBaseline.ts +++ /dev/null @@ -1,2 +0,0 @@ -if (false) { debugger; } -if (false) { debugger; } \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/formatvariableDeclarationList.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/formatvariableDeclarationList.ts deleted file mode 100644 index 956309d2c4d..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/formatvariableDeclarationList.ts +++ /dev/null @@ -1,13 +0,0 @@ -var fun1 = function ( ) { - var x = 'foo' , - z = 'bar' ; - return x ; -}, - -fun2 = ( function ( f ) { - var fun = function ( ) { - console . log ( f ( ) ) ; - }, - x = 'Foo' ; - return fun ; -} ( fun1 ) ) ; diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/formatvariableDeclarationListBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/formatvariableDeclarationListBaseline.ts deleted file mode 100644 index f1d32283fd7..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/formatvariableDeclarationListBaseline.ts +++ /dev/null @@ -1,13 +0,0 @@ -var fun1 = function() { - var x = 'foo', - z = 'bar'; - return x; -}, - -fun2 = (function(f) { - var fun = function() { - console.log(f()); - }, - x = 'Foo'; - return fun; -} (fun1)); diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/implicitModule.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/implicitModule.ts deleted file mode 100644 index 352a252593b..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/implicitModule.ts +++ /dev/null @@ -1,3 +0,0 @@ - export class A { - - } diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/implicitModuleBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/implicitModuleBaseline.ts deleted file mode 100644 index df93540466f..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/implicitModuleBaseline.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class A { - -} diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/importDeclaration.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/importDeclaration.ts deleted file mode 100644 index afd010fe8b7..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/importDeclaration.ts +++ /dev/null @@ -1,6 +0,0 @@ -module Foo { -} - -import bar = Foo; - -import bar2=Foo; diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/importDeclarationBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/importDeclarationBaseline.ts deleted file mode 100644 index d0a4e190d95..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/importDeclarationBaseline.ts +++ /dev/null @@ -1,6 +0,0 @@ -module Foo { -} - -import bar = Foo; - -import bar2 = Foo; diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/main.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/main.ts deleted file mode 100644 index 7640013af8b..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/main.ts +++ /dev/null @@ -1,95 +0,0 @@ - -var a;var c , b;var $d -var $e -var f -a++;b++; - -function f ( ) { - for (i = 0; i < 10; i++) { - k = abc + 123 ^ d; - a = XYZ[m (a[b[c][d]])]; - break; - - switch ( variable){ - case 1: abc += 425; -break; -case 404 : a [x--/2]%=3 ; - break ; - case vari : v[--x ] *=++y*( m + n / k[z]); - for (a in b){ - for (a = 0; a < 10; ++a) { - a++;--a; - if (a == b) { - a++;b--; - } -else -if (a == c){ -++a; -(--c)+=d; -$c = $a + --$b; -} -if (a == b) -if (a != b) { - if (a !== b) - if (a === b) - --a; - else - --a; - else { - a--;++b; -a++ - } - } - } - for (x in y) { -m-=m; -k=1+2+3+4; -} -} - break; - - } - } - var a ={b:function(){}}; - return {a:1,b:2} -} - -var z = 1; - for (i = 0; i < 10; i++) - for (j = 0; j < 10; j++) -for (k = 0; k < 10; ++k) { -z++; -} - -for (k = 0; k < 10; k += 2) { -z++; -} - - $(document).ready (); - - - function pageLoad() { - $('#TextBox1' ) . unbind ( ) ; -$('#TextBox1' ) . datepicker ( ) ; -} - - function pageLoad ( ) { - var webclass=[ - { 'student' : - { 'id': '1', 'name': 'Linda Jones', 'legacySkill': 'Access, VB 5.0' } - } , -{ 'student': -{'id':'2','name':'Adam Davidson','legacySkill':'Cobol,MainFrame'} -} , - { 'student': -{ 'id':'3','name':'Charles Boyer' ,'legacySkill':'HTML, XML'} -} - ]; - -$create(Sys.UI.DataView,{data:webclass},null,null,$get('SList')); - -} - -$( document ).ready(function(){ -alert('hello'); - } ) ; diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/mainBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/mainBaseline.ts deleted file mode 100644 index 30756f547ca..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/mainBaseline.ts +++ /dev/null @@ -1,98 +0,0 @@ - -var a; var c, b; var $d -var $e -var f -a++; b++; - -function f() { - for (i = 0; i < 10; i++) { - k = abc + 123 ^ d; - a = XYZ[m(a[b[c][d]])]; - break; - - switch (variable) { - case 1: abc += 425; - break; - case 404: a[x-- / 2] %= 3; - break; - case vari: v[--x] *= ++y * (m + n / k[z]); - for (a in b) { - for (a = 0; a < 10; ++a) { - a++; --a; - if (a == b) { - a++; b--; - } - else - if (a == c) { - ++a; - (--c) += d; - $c = $a + --$b; - } - if (a == b) - if (a != b) { - if (a !== b) - if (a === b) - --a; - else - --a; - else { - a--; ++b; - a++ - } - } - } - for (x in y) { - m -= m; - k = 1 + 2 + 3 + 4; - } - } - break; - - } - } - var a = { b: function() { } }; - return { a: 1, b: 2 } -} - -var z = 1; -for (i = 0; i < 10; i++) - for (j = 0; j < 10; j++) - for (k = 0; k < 10; ++k) { - z++; - } - -for (k = 0; k < 10; k += 2) { - z++; -} - -$(document).ready(); - - -function pageLoad() { - $('#TextBox1').unbind(); - $('#TextBox1').datepicker(); -} - -function pageLoad() { - var webclass = [ - { - 'student': - { 'id': '1', 'name': 'Linda Jones', 'legacySkill': 'Access, VB 5.0' } - }, -{ - 'student': - { 'id': '2', 'name': 'Adam Davidson', 'legacySkill': 'Cobol,MainFrame' } -}, - { - 'student': - { 'id': '3', 'name': 'Charles Boyer', 'legacySkill': 'HTML, XML' } - } - ]; - - $create(Sys.UI.DataView, { data: webclass }, null, null, $get('SList')); - -} - -$(document).ready(function() { - alert('hello'); -}); diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/moduleIndentation.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/moduleIndentation.ts deleted file mode 100644 index 3030a36630a..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/moduleIndentation.ts +++ /dev/null @@ -1,3 +0,0 @@ - module Foo { - export module A . B . C { } - } \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/moduleIndentationBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/moduleIndentationBaseline.ts deleted file mode 100644 index 0013b367dcf..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/moduleIndentationBaseline.ts +++ /dev/null @@ -1,3 +0,0 @@ -module Foo { - export module A.B.C { } -} \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/modules.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/modules.ts deleted file mode 100644 index 5ce0d19b632..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/modules.ts +++ /dev/null @@ -1,76 +0,0 @@ - module mod1 { - export class b { - } - class d { - } - - - export interface ib { - } -} - - module m2 { - - export module m3 { - export class c extends mod1.b { - } - export class ib2 implements mod1.ib { - } - } -} - - class c extends mod1.b { -} - - class ib2 implements mod1.ib { -} - - declare export module "m4" { - export class d { - } ; - var x : d ; - export function foo ( ) : d ; -} - - import m4 = module ( "m4" ) ; - export var x4 = m4.x ; - export var d4 = m4.d ; - export var f4 = m4.foo ( ) ; - - export module m1 { - declare export module "m2" { - export class d { - } ; - var x: d ; - export function foo ( ) : d ; - } - import m2 = module ( "m2" ) ; - import m3 = module ( "m4" ) ; - - export var x2 = m2.x ; - export var d2 = m2.d ; - export var f2 = m2.foo ( ) ; - - export var x3 = m3.x ; - export var d3 = m3.d ; - export var f3 = m3.foo ( ) ; -} - - export var x2 = m1.m2.x ; - export var d2 = m1.m2.d ; - export var f2 = m1.m2.foo ( ) ; - - export var x3 = m1.m3.x ; - export var d3 = m1.m3.d ; - export var f3 = m1.m3.foo ( ) ; - - export module m5 { - export var x2 = m1.m2.x ; - export var d2 = m1.m2.d ; - export var f2 = m1.m2.foo ( ) ; - - export var x3 = m1.m3.x ; - export var d3 = m1.m3.d ; - export var f3 = m1.m3.foo ( ) ; -} - diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/modulesBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/modulesBaseline.ts deleted file mode 100644 index e6f62024fe6..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/modulesBaseline.ts +++ /dev/null @@ -1,76 +0,0 @@ -module mod1 { - export class b { - } - class d { - } - - - export interface ib { - } -} - -module m2 { - - export module m3 { - export class c extends mod1.b { - } - export class ib2 implements mod1.ib { - } - } -} - -class c extends mod1.b { -} - -class ib2 implements mod1.ib { -} - -declare export module "m4" { - export class d { - }; - var x: d; - export function foo(): d; -} - -import m4 = module("m4"); -export var x4 = m4.x; -export var d4 = m4.d; -export var f4 = m4.foo(); - -export module m1 { - declare export module "m2" { - export class d { - }; - var x: d; - export function foo(): d; - } - import m2 = module("m2"); - import m3 = module("m4"); - - export var x2 = m2.x; - export var d2 = m2.d; - export var f2 = m2.foo(); - - export var x3 = m3.x; - export var d3 = m3.d; - export var f3 = m3.foo(); -} - -export var x2 = m1.m2.x; -export var d2 = m1.m2.d; -export var f2 = m1.m2.foo(); - -export var x3 = m1.m3.x; -export var d3 = m1.m3.d; -export var f3 = m1.m3.foo(); - -export module m5 { - export var x2 = m1.m2.x; - export var d2 = m1.m2.d; - export var f2 = m1.m2.foo(); - - export var x3 = m1.m3.x; - export var d3 = m1.m3.d; - export var f3 = m1.m3.foo(); -} - diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/objectLiteral.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/objectLiteral.ts deleted file mode 100644 index dbecc4d4fec..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/objectLiteral.ts +++ /dev/null @@ -1,27 +0,0 @@ -var x = {foo: 1, -bar: "tt", -boo: 1 + 5}; - -var x2 = {foo: 1, -bar: "tt",boo:1+5}; - -function Foo() { -var typeICalc = { -clear: { -"()": [1, 2, 3] -} -} -} - -// Rule for object literal members for the "value" of the memebr to follow the indent -// of the member, i.e. the relative position of the value is maintained when the member -// is indented. -var x2 = { - foo: -3, - 'bar': - { a: 1, b : 2} -}; - -var x={ }; -var y = {}; \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/objectLiteralBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/objectLiteralBaseline.ts deleted file mode 100644 index 3a7fa63d927..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/objectLiteralBaseline.ts +++ /dev/null @@ -1,31 +0,0 @@ -var x = { - foo: 1, - bar: "tt", - boo: 1 + 5 -}; - -var x2 = { - foo: 1, - bar: "tt", boo: 1 + 5 -}; - -function Foo() { - var typeICalc = { - clear: { - "()": [1, 2, 3] - } - } -} - -// Rule for object literal members for the "value" of the memebr to follow the indent -// of the member, i.e. the relative position of the value is maintained when the member -// is indented. -var x2 = { - foo: - 3, - 'bar': - { a: 1, b: 2 } -}; - -var x = {}; -var y = {}; \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/onClosingBracket.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/onClosingBracket.ts deleted file mode 100644 index 0161f04308d..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/onClosingBracket.ts +++ /dev/null @@ -1,32 +0,0 @@ -function f( ) { -var x = 3; - var z = 2 ; - a = z ++ - 2 * x ; - for ( ; ; ) { - a+=(g +g)*a%t; - b -- ; -} - - switch ( a ) - { - case 1 : { - a ++ ; - b--; - if(a===a) - return; - else - { - for(a in b) - if(a!=a) - { - for(a in b) - { -a++; - } - } - } - } - default: - break; - } -} diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/onClosingBracketBaseLine.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/onClosingBracketBaseLine.ts deleted file mode 100644 index 051a4ebd13a..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/onClosingBracketBaseLine.ts +++ /dev/null @@ -1,28 +0,0 @@ -function f() { - var x = 3; - var z = 2; - a = z++ - 2 * x; - for (; ;) { - a += (g + g) * a % t; - b--; - } - - switch (a) { - case 1: { - a++; - b--; - if (a === a) - return; - else { - for (a in b) - if (a != a) { - for (a in b) { - a++; - } - } - } - } - default: - break; - } -} diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/onSemiColon.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/onSemiColon.ts deleted file mode 100644 index 3b5b5456a6f..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/onSemiColon.ts +++ /dev/null @@ -1 +0,0 @@ -var a=b+c^d-e*++f; \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/onSemiColonBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/onSemiColonBaseline.ts deleted file mode 100644 index 2ba96e4f88a..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/onSemiColonBaseline.ts +++ /dev/null @@ -1 +0,0 @@ -var a = b + c ^ d - e * ++f; \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/spaceAfterConstructor.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/spaceAfterConstructor.ts deleted file mode 100644 index 7d98d5a8f43..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/spaceAfterConstructor.ts +++ /dev/null @@ -1 +0,0 @@ -class test { constructor () { } } \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/spaceAfterConstructorBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/spaceAfterConstructorBaseline.ts deleted file mode 100644 index bc124d41baf..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/spaceAfterConstructorBaseline.ts +++ /dev/null @@ -1 +0,0 @@ -class test { constructor() { } } \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/tabAfterCloseCurly.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/tabAfterCloseCurly.ts deleted file mode 100644 index ec093e0e376..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/tabAfterCloseCurly.ts +++ /dev/null @@ -1,10 +0,0 @@ -module Tools { - export enum NodeType { - Error, - Comment, - } - export enum foob - { - Blah=1, Bleah=2 - } -} \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/tabAfterCloseCurlyBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/tabAfterCloseCurlyBaseline.ts deleted file mode 100644 index d0a3db2d51a..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/tabAfterCloseCurlyBaseline.ts +++ /dev/null @@ -1,9 +0,0 @@ -module Tools { - export enum NodeType { - Error, - Comment, - } - export enum foob { - Blah = 1, Bleah = 2 - } -} \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/typescriptConstructs.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/typescriptConstructs.ts deleted file mode 100644 index 43ef3710ef1..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/typescriptConstructs.ts +++ /dev/null @@ -1,65 +0,0 @@ - module MyModule - { - module A.B.C { -module F { -} - } -interface Blah -{ -boo: string; -} - - class Foo - { - -} - -class Foo2 { -public foo():number { -return 5 * 6; -} -public foo2() { -if (1 === 2) - - -{ -var y : number= 76; -return y; -} - - while (2 == 3) { - if ( y == null ) { - -} - } -} - -public foo3() { -if (1 === 2) - -//comment preventing line merging -{ -var y = 76; -return y; -} - -} - } - } - -function foo(a:number, b:number):number -{ -return 0; -} - -function bar(a:number, b:number) :number[] { -return []; -} - -module BugFix3 { -declare var f: { - (): any; - (x: number): string; - foo: number; -}; -} diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/typescriptConstructsBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/typescriptConstructsBaseline.ts deleted file mode 100644 index 929334e4730..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/typescriptConstructsBaseline.ts +++ /dev/null @@ -1,58 +0,0 @@ -module MyModule { - module A.B.C { - module F { - } - } - interface Blah { - boo: string; - } - - class Foo { - - } - - class Foo2 { - public foo(): number { - return 5 * 6; - } - public foo2() { - if (1 === 2) { - var y: number = 76; - return y; - } - - while (2 == 3) { - if (y == null) { - - } - } - } - - public foo3() { - if (1 === 2) - - //comment preventing line merging - { - var y = 76; - return y; - } - - } - } -} - -function foo(a: number, b: number): number { - return 0; -} - -function bar(a: number, b: number): number[] { - return []; -} - -module BugFix3 { - declare var f: { - (): any; - (x: number): string; - foo: number; - }; -} diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/various.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/various.ts deleted file mode 100644 index bd814c2348e..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/various.ts +++ /dev/null @@ -1,17 +0,0 @@ -function f(a,b,c,d){ -for(var i=0;i<10;i++){ -var a=0; -var b=a+a+a*a%a/2-1; -b+=a; -++b; -f(a,b,c,d); -if(1===1){ -var m=function(e,f){ -return e^f; -} -} -} -} - -for (var i = 0 ; i < this.foo(); i++) { -} \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/variousBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/variousBaseline.ts deleted file mode 100644 index a4b5ceeb1c2..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/variousBaseline.ts +++ /dev/null @@ -1,17 +0,0 @@ -function f(a, b, c, d) { - for (var i = 0; i < 10; i++) { - var a = 0; - var b = a + a + a * a % a / 2 - 1; - b += a; - ++b; - f(a, b, c, d); - if (1 === 1) { - var m = function(e, f) { - return e ^ f; - } - } - } -} - -for (var i = 0 ; i < this.foo(); i++) { -} \ No newline at end of file diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/withStatement.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/withStatement.ts deleted file mode 100644 index 66ec4bf546f..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/withStatement.ts +++ /dev/null @@ -1,9 +0,0 @@ -with (foo.bar) - - { - - } - -with (bar.blah) -{ -} diff --git a/src/harness/unittests/services/formatting/testCode/testCode/formatting/withStatementBaseline.ts b/src/harness/unittests/services/formatting/testCode/testCode/formatting/withStatementBaseline.ts deleted file mode 100644 index f81378d7f82..00000000000 --- a/src/harness/unittests/services/formatting/testCode/testCode/formatting/withStatementBaseline.ts +++ /dev/null @@ -1,6 +0,0 @@ -with (foo.bar) { - -} - -with (bar.blah) { -} From 8728b9857d2f5913a2295570861d40fb4b54c8ab Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Fri, 2 Sep 2016 16:44:25 -0700 Subject: [PATCH 57/73] Adding comment and removing unnecessary object creation --- src/server/client.ts | 12 +++++++----- src/services/services.ts | 5 +++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/server/client.ts b/src/server/client.ts index d5aed77bd1f..ca2d517701f 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -216,15 +216,17 @@ namespace ts.server { return { isMemberCompletion: false, isNewIdentifierLocation: false, - entries: response.body.map(({ name, kind, kindModifiers, sortText, replacementSpan }) => { + entries: response.body.map(entry => { - let convertedSpan: TextSpan; - if (replacementSpan) { - convertedSpan = createTextSpanFromBounds(this.lineOffsetToPosition(fileName, replacementSpan.start), + if (entry.replacementSpan !== undefined) { + const { name, kind, kindModifiers, sortText, replacementSpan} = entry; + + const convertedSpan = createTextSpanFromBounds(this.lineOffsetToPosition(fileName, replacementSpan.start), this.lineOffsetToPosition(fileName, replacementSpan.end)); + return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan }; } - return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan }; + return entry as { name: string, kind: string, kindModifiers: string, sortText: string }; }) }; } diff --git a/src/services/services.ts b/src/services/services.ts index b70e860b301..b581836d9c4 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1488,6 +1488,11 @@ namespace ts { kind: string; // see ScriptElementKind kindModifiers: string; // see ScriptElementKindModifier, comma separated sortText: string; + /** + * An optional span that indicates the text to be replaced by this completion item. It will be + * set if the required span differs from the one generated by the default replacement behavior and should + * be used in that case + */ replacementSpan?: TextSpan; } From 8c95c2b474e37ee440a15405dfc9cc717f300cc6 Mon Sep 17 00:00:00 2001 From: Richard Knoll Date: Tue, 13 Sep 2016 11:44:23 -0700 Subject: [PATCH 58/73] Wrap IO for import completions in try catch --- src/services/services.ts | 106 +++++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 33 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index b581836d9c4..b26dea5efb1 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4599,10 +4599,11 @@ namespace ts { const baseDirectory = getDirectoryPath(absolutePath); const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames()); - if (directoryProbablyExists(baseDirectory, host)) { - if (host.readDirectory) { - // Enumerate the available files if possible - const files = host.readDirectory(baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]); + if (tryDirectoryExists(host, baseDirectory)) { + // Enumerate the available files if possible + const files = tryReadDirectory(host, baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]); + + if (files) { const foundFiles = createMap(); for (let filePath of files) { filePath = normalizePath(filePath); @@ -4623,8 +4624,9 @@ namespace ts { } // If possible, get folder completion as well - if (host.getDirectories) { - const directories = host.getDirectories(baseDirectory); + const directories = tryGetDirectories(host, baseDirectory); + + if (directories) { for (const directory of directories) { const directoryName = getBaseFileName(normalizePath(directory)); @@ -4714,22 +4716,24 @@ namespace ts { // doesn't support. For now, this is safer but slower const includeGlob = normalizedSuffix ? "**/*" : "./*"; - const matches = host.readDirectory(baseDirectory, fileExtensions, undefined, [includeGlob]); - const result: string[] = []; + const matches = tryReadDirectory(host, baseDirectory, fileExtensions, undefined, [includeGlob]); + if (matches) { + const result: string[] = []; - // Trim away prefix and suffix - for (const match of matches) { - const normalizedMatch = normalizePath(match); - if (!endsWith(normalizedMatch, normalizedSuffix) || !startsWith(normalizedMatch, completePrefix)) { - continue; + // Trim away prefix and suffix + for (const match of matches) { + const normalizedMatch = normalizePath(match); + if (!endsWith(normalizedMatch, normalizedSuffix) || !startsWith(normalizedMatch, completePrefix)) { + continue; + } + + const start = completePrefix.length; + const length = normalizedMatch.length - start - normalizedSuffix.length; + + result.push(removeFileExtension(normalizedMatch.substr(start, length))); } - - const start = completePrefix.length; - const length = normalizedMatch.length - start - normalizedSuffix.length; - - result.push(removeFileExtension(normalizedMatch.substr(start, length))); + return result; } - return result; } } @@ -4764,13 +4768,14 @@ namespace ts { if (!isNestedModule) { nonRelativeModules.push(visibleModule.moduleName); } - else if (host.readDirectory && startsWith(visibleModule.moduleName, moduleNameFragment)) { - const nestedFiles = host.readDirectory(visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]); - - for (let f of nestedFiles) { - f = normalizePath(f); - const nestedModule = removeFileExtension(getBaseFileName(f)); - nonRelativeModules.push(nestedModule); + else if (startsWith(visibleModule.moduleName, moduleNameFragment)) { + const nestedFiles = tryReadDirectory(host, visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]); + if (nestedFiles) { + for (let f of nestedFiles) { + f = normalizePath(f); + const nestedModule = removeFileExtension(getBaseFileName(f)); + nonRelativeModules.push(nestedModule); + } } } } @@ -4853,10 +4858,13 @@ namespace ts { } function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, span: TextSpan, result: CompletionEntry[]) { - if (host.getDirectories && directoryProbablyExists(directory, host)) { - for (let typeDirectory of host.getDirectories(directory)) { - typeDirectory = normalizePath(typeDirectory); - result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName, span)); + if (host.getDirectories && tryDirectoryExists(host, directory)) { + const directories = tryGetDirectories(host, directory); + if (directories) { + for (let typeDirectory of directories) { + typeDirectory = normalizePath(typeDirectory); + result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName, span)); + } } } } @@ -4865,7 +4873,7 @@ namespace ts { const paths: string[] = []; let currentConfigPath: string; while (true) { - currentConfigPath = findConfigFile(currentDir, (f) => host.fileExists(f), "package.json"); + currentConfigPath = findConfigFile(currentDir, (f) => tryFileExists(host, f), "package.json"); if (currentConfigPath) { paths.push(currentConfigPath); @@ -4917,8 +4925,8 @@ namespace ts { function tryReadingPackageJson(filePath: string) { try { - const fileText = host.readFile(filePath); - return JSON.parse(fileText); + const fileText = tryReadFile(host, filePath); + return fileText ? JSON.parse(fileText) : undefined; } catch (e) { return undefined; @@ -4960,6 +4968,38 @@ namespace ts { function normalizeAndPreserveTrailingSlash(path: string) { return hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(normalizePath(path)) : normalizePath(path); } + + function tryGetDirectories(host: LanguageServiceHost, directoryName: string): string[] { + return tryIOAndConsumeErrors(host, host.getDirectories, directoryName); + } + + function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] { + return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include); + } + + function tryReadFile(host: LanguageServiceHost, path: string): string { + return tryIOAndConsumeErrors(host, host.readFile, path); + } + + function tryFileExists(host: LanguageServiceHost, path: string): boolean { + return tryIOAndConsumeErrors(host, host.fileExists, path); + } + + function tryDirectoryExists(host: LanguageServiceHost, path: string): boolean { + try { + return directoryProbablyExists(path, host); + } + catch (e) {} + return undefined; + } + + function tryIOAndConsumeErrors(host: LanguageServiceHost, toApply: (...a: any[]) => T, ...args: any[]) { + try { + return toApply && toApply.apply(host, args); + } + catch (e) {} + return undefined; + } } function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails { From f0b210cb59003a7c8d4ea9a32c3972a28e99deef Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 8 Sep 2016 11:16:45 -0700 Subject: [PATCH 59/73] Number is assignable to enum, even inside union Previously, numbers were not assignable to enums that were inside a union --- src/compiler/checker.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f492c2bfc08..1c42dbb7c26 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6227,6 +6227,9 @@ namespace ts { if (relation === assignableRelation || relation === comparableRelation) { if (source.flags & TypeFlags.Any) return true; if (source.flags & (TypeFlags.Number | TypeFlags.NumberLiteral) && target.flags & TypeFlags.Enum) return true; + if (source.flags & (TypeFlags.Number | TypeFlags.NumberLiteral) && + target.flags & TypeFlags.Union && + forEach((target as UnionType).types, t => t.flags & TypeFlags.EnumLike)) return true; if (source.flags & TypeFlags.NumberLiteral && target.flags & TypeFlags.EnumLiteral && (source).text === (target).text) return true; } return false; From e32d854f0eacaee638304a8d3e14d3c9637d5389 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 8 Sep 2016 11:21:40 -0700 Subject: [PATCH 60/73] Test that number is assignable to enum in a union --- .../reference/numberAssignableToEnumInsideUnion.js | 14 ++++++++++++++ .../numberAssignableToEnumInsideUnion.symbols | 14 ++++++++++++++ .../numberAssignableToEnumInsideUnion.types | 14 ++++++++++++++ .../compiler/numberAssignableToEnumInsideUnion.ts | 3 +++ 4 files changed, 45 insertions(+) create mode 100644 tests/baselines/reference/numberAssignableToEnumInsideUnion.js create mode 100644 tests/baselines/reference/numberAssignableToEnumInsideUnion.symbols create mode 100644 tests/baselines/reference/numberAssignableToEnumInsideUnion.types create mode 100644 tests/cases/compiler/numberAssignableToEnumInsideUnion.ts diff --git a/tests/baselines/reference/numberAssignableToEnumInsideUnion.js b/tests/baselines/reference/numberAssignableToEnumInsideUnion.js new file mode 100644 index 00000000000..584b395212d --- /dev/null +++ b/tests/baselines/reference/numberAssignableToEnumInsideUnion.js @@ -0,0 +1,14 @@ +//// [numberAssignableToEnumInsideUnion.ts] +enum E { A, B } +let n: number; +let z: E | boolean = n; + + +//// [numberAssignableToEnumInsideUnion.js] +var E; +(function (E) { + E[E["A"] = 0] = "A"; + E[E["B"] = 1] = "B"; +})(E || (E = {})); +var n; +var z = n; diff --git a/tests/baselines/reference/numberAssignableToEnumInsideUnion.symbols b/tests/baselines/reference/numberAssignableToEnumInsideUnion.symbols new file mode 100644 index 00000000000..4d815a99690 --- /dev/null +++ b/tests/baselines/reference/numberAssignableToEnumInsideUnion.symbols @@ -0,0 +1,14 @@ +=== tests/cases/compiler/numberAssignableToEnumInsideUnion.ts === +enum E { A, B } +>E : Symbol(E, Decl(numberAssignableToEnumInsideUnion.ts, 0, 0)) +>A : Symbol(E.A, Decl(numberAssignableToEnumInsideUnion.ts, 0, 8)) +>B : Symbol(E.B, Decl(numberAssignableToEnumInsideUnion.ts, 0, 11)) + +let n: number; +>n : Symbol(n, Decl(numberAssignableToEnumInsideUnion.ts, 1, 3)) + +let z: E | boolean = n; +>z : Symbol(z, Decl(numberAssignableToEnumInsideUnion.ts, 2, 3)) +>E : Symbol(E, Decl(numberAssignableToEnumInsideUnion.ts, 0, 0)) +>n : Symbol(n, Decl(numberAssignableToEnumInsideUnion.ts, 1, 3)) + diff --git a/tests/baselines/reference/numberAssignableToEnumInsideUnion.types b/tests/baselines/reference/numberAssignableToEnumInsideUnion.types new file mode 100644 index 00000000000..fd45ff17622 --- /dev/null +++ b/tests/baselines/reference/numberAssignableToEnumInsideUnion.types @@ -0,0 +1,14 @@ +=== tests/cases/compiler/numberAssignableToEnumInsideUnion.ts === +enum E { A, B } +>E : E +>A : E +>B : E + +let n: number; +>n : number + +let z: E | boolean = n; +>z : boolean | E +>E : E +>n : number + diff --git a/tests/cases/compiler/numberAssignableToEnumInsideUnion.ts b/tests/cases/compiler/numberAssignableToEnumInsideUnion.ts new file mode 100644 index 00000000000..fbefb5ac802 --- /dev/null +++ b/tests/cases/compiler/numberAssignableToEnumInsideUnion.ts @@ -0,0 +1,3 @@ +enum E { A, B } +let n: number; +let z: E | boolean = n; From c47cdb8df88386710215bda03adbc7c4ee1e6e6e Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 8 Sep 2016 14:41:35 -0700 Subject: [PATCH 61/73] Enum literal is assignable to enum, even inside union Previously, only number literals were assignable to enums inside unions. --- src/compiler/checker.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1c42dbb7c26..80e1069f5c7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6230,7 +6230,9 @@ namespace ts { if (source.flags & (TypeFlags.Number | TypeFlags.NumberLiteral) && target.flags & TypeFlags.Union && forEach((target as UnionType).types, t => t.flags & TypeFlags.EnumLike)) return true; - if (source.flags & TypeFlags.NumberLiteral && target.flags & TypeFlags.EnumLiteral && (source).text === (target).text) return true; + if (source.flags & (TypeFlags.NumberLiteral | TypeFlags.EnumLiteral) && + target.flags & TypeFlags.EnumLiteral && + (source).text === (target).text) return true; } return false; } From 083f3426d145837a11f23744f21bd30be861aca4 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 8 Sep 2016 14:43:17 -0700 Subject: [PATCH 62/73] Test that enum literal is assignable to an enum in a union --- .../enumLiteralAssignableToEnumInsideUnion.js | 32 ++++++++++++++++++ ...LiteralAssignableToEnumInsideUnion.symbols | 33 +++++++++++++++++++ ...umLiteralAssignableToEnumInsideUnion.types | 33 +++++++++++++++++++ .../enumLiteralAssignableToEnumInsideUnion.ts | 11 +++++++ 4 files changed, 109 insertions(+) create mode 100644 tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.js create mode 100644 tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.symbols create mode 100644 tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.types create mode 100644 tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts diff --git a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.js b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.js new file mode 100644 index 00000000000..607269e06e3 --- /dev/null +++ b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.js @@ -0,0 +1,32 @@ +//// [enumLiteralAssignableToEnumInsideUnion.ts] +module X { + export enum Foo { + A, B + } +} +module Y { + export enum Foo { + A, B + } +} +const y: X.Foo | boolean = Y.Foo.A; + + +//// [enumLiteralAssignableToEnumInsideUnion.js] +var X; +(function (X) { + (function (Foo) { + Foo[Foo["A"] = 0] = "A"; + Foo[Foo["B"] = 1] = "B"; + })(X.Foo || (X.Foo = {})); + var Foo = X.Foo; +})(X || (X = {})); +var Y; +(function (Y) { + (function (Foo) { + Foo[Foo["A"] = 0] = "A"; + Foo[Foo["B"] = 1] = "B"; + })(Y.Foo || (Y.Foo = {})); + var Foo = Y.Foo; +})(Y || (Y = {})); +var y = Y.Foo.A; diff --git a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.symbols b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.symbols new file mode 100644 index 00000000000..979bd516682 --- /dev/null +++ b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.symbols @@ -0,0 +1,33 @@ +=== tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts === +module X { +>X : Symbol(X, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 0)) + + export enum Foo { +>Foo : Symbol(Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 10)) + + A, B +>A : Symbol(Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 1, 21)) +>B : Symbol(Foo.B, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 2, 10)) + } +} +module Y { +>Y : Symbol(Y, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 4, 1)) + + export enum Foo { +>Foo : Symbol(Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 5, 10)) + + A, B +>A : Symbol(Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 6, 21)) +>B : Symbol(Foo.B, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 7, 10)) + } +} +const y: X.Foo | boolean = Y.Foo.A; +>y : Symbol(y, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 10, 5)) +>X : Symbol(X, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 0)) +>Foo : Symbol(X.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 10)) +>Y.Foo.A : Symbol(Y.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 6, 21)) +>Y.Foo : Symbol(Y.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 5, 10)) +>Y : Symbol(Y, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 4, 1)) +>Foo : Symbol(Y.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 5, 10)) +>A : Symbol(Y.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 6, 21)) + diff --git a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.types b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.types new file mode 100644 index 00000000000..de57b5e92bc --- /dev/null +++ b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.types @@ -0,0 +1,33 @@ +=== tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts === +module X { +>X : typeof X + + export enum Foo { +>Foo : Foo + + A, B +>A : Foo +>B : Foo + } +} +module Y { +>Y : typeof Y + + export enum Foo { +>Foo : Foo + + A, B +>A : Foo +>B : Foo + } +} +const y: X.Foo | boolean = Y.Foo.A; +>y : boolean | X.Foo +>X : any +>Foo : X.Foo +>Y.Foo.A : Y.Foo +>Y.Foo : typeof Y.Foo +>Y : typeof Y +>Foo : typeof Y.Foo +>A : Y.Foo + diff --git a/tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts b/tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts new file mode 100644 index 00000000000..0c10155e48d --- /dev/null +++ b/tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts @@ -0,0 +1,11 @@ +module X { + export enum Foo { + A, B + } +} +module Y { + export enum Foo { + A, B + } +} +const y: X.Foo | boolean = Y.Foo.A; From 90d685b73728dc6c301e2b01b1b10e3e2833cde7 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 8 Sep 2016 15:30:29 -0700 Subject: [PATCH 63/73] Fix both new enum assignability predicates And update error reporting baseline (new error is less elaborate) --- src/compiler/checker.ts | 14 ++++++++------ .../reference/enumAssignmentCompat3.errors.txt | 6 ++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 80e1069f5c7..f2c81970049 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6226,13 +6226,15 @@ namespace ts { if (source.flags & TypeFlags.Null && (!strictNullChecks || target.flags & TypeFlags.Null)) return true; if (relation === assignableRelation || relation === comparableRelation) { if (source.flags & TypeFlags.Any) return true; - if (source.flags & (TypeFlags.Number | TypeFlags.NumberLiteral) && target.flags & TypeFlags.Enum) return true; - if (source.flags & (TypeFlags.Number | TypeFlags.NumberLiteral) && - target.flags & TypeFlags.Union && - forEach((target as UnionType).types, t => t.flags & TypeFlags.EnumLike)) return true; - if (source.flags & (TypeFlags.NumberLiteral | TypeFlags.EnumLiteral) && + if (source.flags & (TypeFlags.Number | TypeFlags.NumberLiteral) && target.flags & TypeFlags.EnumLike) return true; + if (source.flags & TypeFlags.NumberLiteral && target.flags & TypeFlags.EnumLiteral && (source).text === (target).text) return true; + if (source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.EnumLiteral && - (source).text === (target).text) return true; + (source).text === (target).text && + (source).baseType.symbol.name === (target).baseType.symbol.name && + !isConstEnumSymbol((source).baseType.symbol) && !isConstEnumSymbol((target).baseType.symbol)) { + return true; + } } return false; } diff --git a/tests/baselines/reference/enumAssignmentCompat3.errors.txt b/tests/baselines/reference/enumAssignmentCompat3.errors.txt index 02cb4314777..bfdaef96775 100644 --- a/tests/baselines/reference/enumAssignmentCompat3.errors.txt +++ b/tests/baselines/reference/enumAssignmentCompat3.errors.txt @@ -1,7 +1,6 @@ tests/cases/compiler/enumAssignmentCompat3.ts(68,1): error TS2322: Type 'Abcd.E' is not assignable to type 'First.E'. Property 'd' is missing in type 'First.E'. -tests/cases/compiler/enumAssignmentCompat3.ts(70,1): error TS2322: Type 'Cd.E' is not assignable to type 'First.E'. - Property 'd' is missing in type 'First.E'. +tests/cases/compiler/enumAssignmentCompat3.ts(70,1): error TS2324: Property 'd' is missing in type 'First.E'. tests/cases/compiler/enumAssignmentCompat3.ts(71,1): error TS2322: Type 'Nope' is not assignable to type 'E'. tests/cases/compiler/enumAssignmentCompat3.ts(75,1): error TS2322: Type 'First.E' is not assignable to type 'Ab.E'. Property 'c' is missing in type 'Ab.E'. @@ -89,8 +88,7 @@ tests/cases/compiler/enumAssignmentCompat3.ts(86,1): error TS2322: Type 'Merged. abc = secondAb; // ok abc = secondCd; // missing 'd' ~~~ -!!! error TS2322: Type 'Cd.E' is not assignable to type 'First.E'. -!!! error TS2322: Property 'd' is missing in type 'First.E'. +!!! error TS2324: Property 'd' is missing in type 'First.E'. abc = nope; // nope! ~~~ !!! error TS2322: Type 'Nope' is not assignable to type 'E'. From e17797979c85985e08a5a250594b89035d4da3be Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Sep 2016 09:59:23 -0700 Subject: [PATCH 64/73] Address PR comments 1. Cache results of isEnumTypeRelatedTo 2. Make numeric literal assignment stricter again. 3. Use isEnumRelatedTo for comparing enums to each other. This provides the previous semi-structural semantics. 4. Because of the new distinction between computed enums (no union members) and union enums (no computed values => a union of enum literals), some semi-structural code moves out to the body of `isRelatedTo`. --- src/compiler/checker.ts | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f2c81970049..3d590330dd0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -332,6 +332,7 @@ namespace ts { const assignableRelation = createMap(); const comparableRelation = createMap(); const identityRelation = createMap(); + const enumRelation = createMap(); // This is for caching the result of getSymbolDisplayBuilder. Do not access directly. let _displayBuilder: SymbolDisplayBuilder; @@ -6195,7 +6196,12 @@ namespace ts { if (source === target) { return true; } + const id = source.id + "," + target.id; + if (enumRelation[id] !== undefined) { + return enumRelation[id]; + } if (source.symbol.name !== target.symbol.name || !(source.symbol.flags & SymbolFlags.RegularEnum) || !(target.symbol.flags & SymbolFlags.RegularEnum)) { + enumRelation[id] = false; return false; } const targetEnumType = getTypeOfSymbol(target.symbol); @@ -6207,10 +6213,12 @@ namespace ts { errorReporter(Diagnostics.Property_0_is_missing_in_type_1, property.name, typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType)); } + enumRelation[id] = false; return false; } } } + enumRelation[id] = true; return true; } @@ -6226,13 +6234,18 @@ namespace ts { if (source.flags & TypeFlags.Null && (!strictNullChecks || target.flags & TypeFlags.Null)) return true; if (relation === assignableRelation || relation === comparableRelation) { if (source.flags & TypeFlags.Any) return true; - if (source.flags & (TypeFlags.Number | TypeFlags.NumberLiteral) && target.flags & TypeFlags.EnumLike) return true; + if (source.flags & TypeFlags.Number && target.flags & TypeFlags.EnumLike) return true; + if (source.flags & TypeFlags.NumberLiteral && target.flags & TypeFlags.Enum) return true; if (source.flags & TypeFlags.NumberLiteral && target.flags & TypeFlags.EnumLiteral && (source).text === (target).text) return true; if (source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.EnumLiteral && (source).text === (target).text && - (source).baseType.symbol.name === (target).baseType.symbol.name && - !isConstEnumSymbol((source).baseType.symbol) && !isConstEnumSymbol((target).baseType.symbol)) { + isEnumTypeRelatedTo((source).baseType, (target).baseType, errorReporter)) { + return true; + } + if (source.flags & TypeFlags.EnumLiteral && + target.flags & TypeFlags.Enum && + isEnumTypeRelatedTo(target, (source).baseType, errorReporter)) { return true; } } @@ -6410,6 +6423,14 @@ namespace ts { if (result = typeRelatedToSomeType(source, target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive))) { return result; } + if (source.flags & TypeFlags.Enum) { + // 1. look through the target union for a literal whose base type is related to the source + let lit = find((target).types, t => t.flags & TypeFlags.EnumLiteral && isEnumTypeRelatedTo(source, (t).baseType)); + // 2. if found, the base type is only assignable to source if all its literals are in the target union. + if (lit && (result = isRelatedTo((lit as EnumLiteralType).baseType, target, false))) { + return result; + } + } } } From aa3d2501a1cd724b5fc19d68f13446fcd0d19e8a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Sep 2016 10:05:48 -0700 Subject: [PATCH 65/73] More tests of enum assignability 1. Numeric literal <-> enum literal assignability 2. Computed enum <-> union enum assignability 3. Also rebaseline error reporting of existing enum cases. --- .../enumAssignmentCompat3.errors.txt | 24 ++++----- .../enumAssignmentCompat5.errors.txt | 33 ++++++++++++ .../reference/enumAssignmentCompat5.js | 50 +++++++++++++++++++ ...eralAssignableToEnumInsideUnion.errors.txt | 37 ++++++++++++++ .../enumLiteralAssignableToEnumInsideUnion.js | 40 ++++++++++++++- ...LiteralAssignableToEnumInsideUnion.symbols | 35 ++++++++++++- ...umLiteralAssignableToEnumInsideUnion.types | 39 +++++++++++++++ tests/cases/compiler/enumAssignmentCompat5.ts | 23 +++++++++ .../enumLiteralAssignableToEnumInsideUnion.ts | 18 ++++++- 9 files changed, 280 insertions(+), 19 deletions(-) create mode 100644 tests/baselines/reference/enumAssignmentCompat5.errors.txt create mode 100644 tests/baselines/reference/enumAssignmentCompat5.js create mode 100644 tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.errors.txt create mode 100644 tests/cases/compiler/enumAssignmentCompat5.ts diff --git a/tests/baselines/reference/enumAssignmentCompat3.errors.txt b/tests/baselines/reference/enumAssignmentCompat3.errors.txt index bfdaef96775..41fa7a94fca 100644 --- a/tests/baselines/reference/enumAssignmentCompat3.errors.txt +++ b/tests/baselines/reference/enumAssignmentCompat3.errors.txt @@ -1,16 +1,13 @@ -tests/cases/compiler/enumAssignmentCompat3.ts(68,1): error TS2322: Type 'Abcd.E' is not assignable to type 'First.E'. +tests/cases/compiler/enumAssignmentCompat3.ts(68,1): error TS2324: Property 'd' is missing in type 'First.E'. +tests/cases/compiler/enumAssignmentCompat3.ts(70,1): error TS2322: Type 'Cd.E' is not assignable to type 'First.E'. Property 'd' is missing in type 'First.E'. -tests/cases/compiler/enumAssignmentCompat3.ts(70,1): error TS2324: Property 'd' is missing in type 'First.E'. tests/cases/compiler/enumAssignmentCompat3.ts(71,1): error TS2322: Type 'Nope' is not assignable to type 'E'. -tests/cases/compiler/enumAssignmentCompat3.ts(75,1): error TS2322: Type 'First.E' is not assignable to type 'Ab.E'. - Property 'c' is missing in type 'Ab.E'. +tests/cases/compiler/enumAssignmentCompat3.ts(75,1): error TS2324: Property 'c' is missing in type 'Ab.E'. tests/cases/compiler/enumAssignmentCompat3.ts(76,1): error TS2322: Type 'First.E' is not assignable to type 'Cd.E'. - Property 'a' is missing in type 'Cd.E'. tests/cases/compiler/enumAssignmentCompat3.ts(77,1): error TS2322: Type 'E' is not assignable to type 'Nope'. tests/cases/compiler/enumAssignmentCompat3.ts(82,1): error TS2322: Type 'Const.E' is not assignable to type 'First.E'. tests/cases/compiler/enumAssignmentCompat3.ts(83,1): error TS2322: Type 'First.E' is not assignable to type 'Const.E'. -tests/cases/compiler/enumAssignmentCompat3.ts(86,1): error TS2322: Type 'Merged.E' is not assignable to type 'First.E'. - Property 'd' is missing in type 'First.E'. +tests/cases/compiler/enumAssignmentCompat3.ts(86,1): error TS2324: Property 'd' is missing in type 'First.E'. ==== tests/cases/compiler/enumAssignmentCompat3.ts (9 errors) ==== @@ -83,12 +80,12 @@ tests/cases/compiler/enumAssignmentCompat3.ts(86,1): error TS2322: Type 'Merged. abc = secondAbc; // ok abc = secondAbcd; // missing 'd' ~~~ -!!! error TS2322: Type 'Abcd.E' is not assignable to type 'First.E'. -!!! error TS2322: Property 'd' is missing in type 'First.E'. +!!! error TS2324: Property 'd' is missing in type 'First.E'. abc = secondAb; // ok abc = secondCd; // missing 'd' ~~~ -!!! error TS2324: Property 'd' is missing in type 'First.E'. +!!! error TS2322: Type 'Cd.E' is not assignable to type 'First.E'. +!!! error TS2322: Property 'd' is missing in type 'First.E'. abc = nope; // nope! ~~~ !!! error TS2322: Type 'Nope' is not assignable to type 'E'. @@ -97,12 +94,10 @@ tests/cases/compiler/enumAssignmentCompat3.ts(86,1): error TS2322: Type 'Merged. secondAbcd = abc; // ok secondAb = abc; // missing 'c' ~~~~~~~~ -!!! error TS2322: Type 'First.E' is not assignable to type 'Ab.E'. -!!! error TS2322: Property 'c' is missing in type 'Ab.E'. +!!! error TS2324: Property 'c' is missing in type 'Ab.E'. secondCd = abc; // missing 'a' and 'b' ~~~~~~~~ !!! error TS2322: Type 'First.E' is not assignable to type 'Cd.E'. -!!! error TS2322: Property 'a' is missing in type 'Cd.E'. nope = abc; // nope! ~~~~ !!! error TS2322: Type 'E' is not assignable to type 'Nope'. @@ -120,8 +115,7 @@ tests/cases/compiler/enumAssignmentCompat3.ts(86,1): error TS2322: Type 'Merged. // merged enums compare all their members abc = merged; // missing 'd' ~~~ -!!! error TS2322: Type 'Merged.E' is not assignable to type 'First.E'. -!!! error TS2322: Property 'd' is missing in type 'First.E'. +!!! error TS2324: Property 'd' is missing in type 'First.E'. merged = abc; // ok abc = merged2; // ok merged2 = abc; // ok \ No newline at end of file diff --git a/tests/baselines/reference/enumAssignmentCompat5.errors.txt b/tests/baselines/reference/enumAssignmentCompat5.errors.txt new file mode 100644 index 00000000000..98c0aaccfad --- /dev/null +++ b/tests/baselines/reference/enumAssignmentCompat5.errors.txt @@ -0,0 +1,33 @@ +tests/cases/compiler/enumAssignmentCompat5.ts(14,1): error TS2322: Type '2' is not assignable to type 'E.A'. +tests/cases/compiler/enumAssignmentCompat5.ts(20,9): error TS2535: Enum type 'Computed' has members with initializers that are not literals. + + +==== tests/cases/compiler/enumAssignmentCompat5.ts (2 errors) ==== + enum E { + A, B, C + } + enum Computed { + A = 1 << 1, + B = 1 << 2, + C = 1 << 3, + } + let n: number; + let e: E = n; // ok because it's too inconvenient otherwise + e = 0; // ok, in range + e = 4; // ok, out of range, but allowed computed enums don't have all members + let a: E.A = 0; // ok, A === 0 + a = 2; // error, 2 !== 0 + ~ +!!! error TS2322: Type '2' is not assignable to type 'E.A'. + a = n; // ok + + let c: Computed = n; // ok + c = n; // ok + c = 4; // ok + let ca: Computed.A = 1; // error, Computed.A isn't a literal type because Computed has no enum literals + ~~~~~~~~~~ +!!! error TS2535: Enum type 'Computed' has members with initializers that are not literals. + + + + \ No newline at end of file diff --git a/tests/baselines/reference/enumAssignmentCompat5.js b/tests/baselines/reference/enumAssignmentCompat5.js new file mode 100644 index 00000000000..a7c96929f08 --- /dev/null +++ b/tests/baselines/reference/enumAssignmentCompat5.js @@ -0,0 +1,50 @@ +//// [enumAssignmentCompat5.ts] +enum E { + A, B, C +} +enum Computed { + A = 1 << 1, + B = 1 << 2, + C = 1 << 3, +} +let n: number; +let e: E = n; // ok because it's too inconvenient otherwise +e = 0; // ok, in range +e = 4; // ok, out of range, but allowed computed enums don't have all members +let a: E.A = 0; // ok, A === 0 +a = 2; // error, 2 !== 0 +a = n; // ok + +let c: Computed = n; // ok +c = n; // ok +c = 4; // ok +let ca: Computed.A = 1; // error, Computed.A isn't a literal type because Computed has no enum literals + + + + + +//// [enumAssignmentCompat5.js] +var E; +(function (E) { + E[E["A"] = 0] = "A"; + E[E["B"] = 1] = "B"; + E[E["C"] = 2] = "C"; +})(E || (E = {})); +var Computed; +(function (Computed) { + Computed[Computed["A"] = 2] = "A"; + Computed[Computed["B"] = 4] = "B"; + Computed[Computed["C"] = 8] = "C"; +})(Computed || (Computed = {})); +var n; +var e = n; // ok because it's too inconvenient otherwise +e = 0; // ok, in range +e = 4; // ok, out of range, but allowed computed enums don't have all members +var a = 0; // ok, A === 0 +a = 2; // error, 2 !== 0 +a = n; // ok +var c = n; // ok +c = n; // ok +c = 4; // ok +var ca = 1; // error, Computed.A isn't a literal type because Computed has no enum literals diff --git a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.errors.txt b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.errors.txt new file mode 100644 index 00000000000..37bdc99a922 --- /dev/null +++ b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.errors.txt @@ -0,0 +1,37 @@ +tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(25,7): error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo.B'. +tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(26,7): error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo.A'. + + +==== tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts (2 errors) ==== + module X { + export enum Foo { + A, B + } + } + module Y { + export enum Foo { + A, B + } + } + module Z { + export enum Foo { + A = 1 << 1, + B = 1 << 2, + } + } + module Ka { + export enum Foo { + A = 1 << 10, + B = 1 << 11, + } + } + const e1: X.Foo | boolean = Z.Foo.A; // ok + const e2: X.Foo.A | X.Foo.B | boolean = Z.Foo.A; // ok, X.Foo is equivalent to X.Foo.A | X.Foo.B + const e3: X.Foo.B | boolean = Z.Foo.A; // not legal + ~~ +!!! error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo.B'. + const e4: X.Foo.A | boolean = Z.Foo.A; // not legal either because Z.Foo is computed and Z.Foo.A is not necessarily assignable to X.Foo.A + ~~ +!!! error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo.A'. + const e5: Ka.Foo | boolean = Z.Foo.A; // ok + \ No newline at end of file diff --git a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.js b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.js index 607269e06e3..1665e729719 100644 --- a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.js +++ b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.js @@ -9,7 +9,23 @@ module Y { A, B } } -const y: X.Foo | boolean = Y.Foo.A; +module Z { + export enum Foo { + A = 1 << 1, + B = 1 << 2, + } +} +module Ka { + export enum Foo { + A = 1 << 10, + B = 1 << 11, + } +} +const e1: X.Foo | boolean = Z.Foo.A; // ok +const e2: X.Foo.A | X.Foo.B | boolean = Z.Foo.A; // ok, X.Foo is equivalent to X.Foo.A | X.Foo.B +const e3: X.Foo.B | boolean = Z.Foo.A; // not legal +const e4: X.Foo.A | boolean = Z.Foo.A; // not legal either because Z.Foo is computed and Z.Foo.A is not necessarily assignable to X.Foo.A +const e5: Ka.Foo | boolean = Z.Foo.A; // ok //// [enumLiteralAssignableToEnumInsideUnion.js] @@ -29,4 +45,24 @@ var Y; })(Y.Foo || (Y.Foo = {})); var Foo = Y.Foo; })(Y || (Y = {})); -var y = Y.Foo.A; +var Z; +(function (Z) { + (function (Foo) { + Foo[Foo["A"] = 2] = "A"; + Foo[Foo["B"] = 4] = "B"; + })(Z.Foo || (Z.Foo = {})); + var Foo = Z.Foo; +})(Z || (Z = {})); +var Ka; +(function (Ka) { + (function (Foo) { + Foo[Foo["A"] = 1024] = "A"; + Foo[Foo["B"] = 2048] = "B"; + })(Ka.Foo || (Ka.Foo = {})); + var Foo = Ka.Foo; +})(Ka || (Ka = {})); +var e1 = Z.Foo.A; // ok +var e2 = Z.Foo.A; // ok, X.Foo is equivalent to X.Foo.A | X.Foo.B +var e3 = Z.Foo.A; // not legal +var e4 = Z.Foo.A; // not legal either because Z.Foo is computed and Z.Foo.A is not necessarily assignable to X.Foo.A +var e5 = Z.Foo.A; // ok diff --git a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.symbols b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.symbols index 979bd516682..8df8e21b386 100644 --- a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.symbols +++ b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.symbols @@ -21,8 +21,21 @@ module Y { >B : Symbol(Foo.B, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 7, 10)) } } +module Z { +>Z : Symbol(Z, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 9, 1)) + + export enum Foo { +>Foo : Symbol(Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 10, 10)) + + A = 1 << 1, +>A : Symbol(Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 11, 21)) + + B = 1 << 2, +>B : Symbol(Foo.B, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 12, 19)) + } +} const y: X.Foo | boolean = Y.Foo.A; ->y : Symbol(y, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 10, 5)) +>y : Symbol(y, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 16, 5)) >X : Symbol(X, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 0)) >Foo : Symbol(X.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 10)) >Y.Foo.A : Symbol(Y.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 6, 21)) @@ -31,3 +44,23 @@ const y: X.Foo | boolean = Y.Foo.A; >Foo : Symbol(Y.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 5, 10)) >A : Symbol(Y.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 6, 21)) +const z: X.Foo | boolean = Z.Foo.A; +>z : Symbol(z, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 17, 5)) +>X : Symbol(X, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 0)) +>Foo : Symbol(X.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 10)) +>Z.Foo.A : Symbol(Z.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 11, 21)) +>Z.Foo : Symbol(Z.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 10, 10)) +>Z : Symbol(Z, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 9, 1)) +>Foo : Symbol(Z.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 10, 10)) +>A : Symbol(Z.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 11, 21)) + +const x: Z.Foo | boolean = X.Foo.A; +>x : Symbol(x, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 18, 5)) +>Z : Symbol(Z, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 9, 1)) +>Foo : Symbol(Z.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 10, 10)) +>X.Foo.A : Symbol(X.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 1, 21)) +>X.Foo : Symbol(X.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 10)) +>X : Symbol(X, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 0)) +>Foo : Symbol(X.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 10)) +>A : Symbol(X.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 1, 21)) + diff --git a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.types b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.types index de57b5e92bc..5dbb98bcf9c 100644 --- a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.types +++ b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.types @@ -21,6 +21,25 @@ module Y { >B : Foo } } +module Z { +>Z : typeof Z + + export enum Foo { +>Foo : Foo + + A = 1 << 1, +>A : Foo +>1 << 1 : number +>1 : number +>1 : number + + B = 1 << 2, +>B : Foo +>1 << 2 : number +>1 : number +>2 : number + } +} const y: X.Foo | boolean = Y.Foo.A; >y : boolean | X.Foo >X : any @@ -31,3 +50,23 @@ const y: X.Foo | boolean = Y.Foo.A; >Foo : typeof Y.Foo >A : Y.Foo +const z: X.Foo | boolean = Z.Foo.A; +>z : boolean | X.Foo +>X : any +>Foo : X.Foo +>Z.Foo.A : Z.Foo +>Z.Foo : typeof Z.Foo +>Z : typeof Z +>Foo : typeof Z.Foo +>A : Z.Foo + +const x: Z.Foo | boolean = X.Foo.A; +>x : boolean | Z.Foo +>Z : any +>Foo : Z.Foo +>X.Foo.A : X.Foo +>X.Foo : typeof X.Foo +>X : typeof X +>Foo : typeof X.Foo +>A : X.Foo + diff --git a/tests/cases/compiler/enumAssignmentCompat5.ts b/tests/cases/compiler/enumAssignmentCompat5.ts new file mode 100644 index 00000000000..c56db8b04f5 --- /dev/null +++ b/tests/cases/compiler/enumAssignmentCompat5.ts @@ -0,0 +1,23 @@ +enum E { + A, B, C +} +enum Computed { + A = 1 << 1, + B = 1 << 2, + C = 1 << 3, +} +let n: number; +let e: E = n; // ok because it's too inconvenient otherwise +e = 0; // ok, in range +e = 4; // ok, out of range, but allowed computed enums don't have all members +let a: E.A = 0; // ok, A === 0 +a = 2; // error, 2 !== 0 +a = n; // ok + +let c: Computed = n; // ok +c = n; // ok +c = 4; // ok +let ca: Computed.A = 1; // error, Computed.A isn't a literal type because Computed has no enum literals + + + diff --git a/tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts b/tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts index 0c10155e48d..c3029af0f42 100644 --- a/tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts +++ b/tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts @@ -8,4 +8,20 @@ module Y { A, B } } -const y: X.Foo | boolean = Y.Foo.A; +module Z { + export enum Foo { + A = 1 << 1, + B = 1 << 2, + } +} +module Ka { + export enum Foo { + A = 1 << 10, + B = 1 << 11, + } +} +const e1: X.Foo | boolean = Z.Foo.A; // ok +const e2: X.Foo.A | X.Foo.B | boolean = Z.Foo.A; // ok, X.Foo is equivalent to X.Foo.A | X.Foo.B +const e3: X.Foo.B | boolean = Z.Foo.A; // not legal +const e4: X.Foo.A | boolean = Z.Foo.A; // not legal either because Z.Foo is computed and Z.Foo.A is not necessarily assignable to X.Foo.A +const e5: Ka.Foo | boolean = Z.Foo.A; // ok From 3683ec2b084b588cb126f97ef9c60fb90b92335b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Sep 2016 10:07:21 -0700 Subject: [PATCH 66/73] Fix lint --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d590330dd0..dc634d25734 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6425,9 +6425,9 @@ namespace ts { } if (source.flags & TypeFlags.Enum) { // 1. look through the target union for a literal whose base type is related to the source - let lit = find((target).types, t => t.flags & TypeFlags.EnumLiteral && isEnumTypeRelatedTo(source, (t).baseType)); + const lit = find((target).types, t => t.flags & TypeFlags.EnumLiteral && isEnumTypeRelatedTo(source, (t).baseType)); // 2. if found, the base type is only assignable to source if all its literals are in the target union. - if (lit && (result = isRelatedTo((lit as EnumLiteralType).baseType, target, false))) { + if (lit && (result = isRelatedTo((lit as EnumLiteralType).baseType, target, /*reportErrors*/ false))) { return result; } } From e3843171732ca3a32b178fd3989776ed2bd90ccf Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Sep 2016 10:14:48 -0700 Subject: [PATCH 67/73] Update baselines --- .../reference/numberAssignableToEnumInsideUnion.types | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/numberAssignableToEnumInsideUnion.types b/tests/baselines/reference/numberAssignableToEnumInsideUnion.types index fd45ff17622..56d6efb0d1e 100644 --- a/tests/baselines/reference/numberAssignableToEnumInsideUnion.types +++ b/tests/baselines/reference/numberAssignableToEnumInsideUnion.types @@ -1,8 +1,8 @@ === tests/cases/compiler/numberAssignableToEnumInsideUnion.ts === enum E { A, B } >E : E ->A : E ->B : E +>A : E.A +>B : E.B let n: number; >n : number From d0f8be535e32f7225639949ba211f3dbd1c2ab09 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Sep 2016 14:11:47 -0700 Subject: [PATCH 68/73] Enum assignability:loosen numbers+tighten computed 1. All numbers and numeric literals are assignable to all enums and enum literals. 2. Computed enums are no longer assignable to anything except themselves, even if they would otherwise be "semi-structurally" compatible. --- src/compiler/checker.ts | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dc634d25734..28accd8452a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6200,9 +6200,10 @@ namespace ts { if (enumRelation[id] !== undefined) { return enumRelation[id]; } - if (source.symbol.name !== target.symbol.name || !(source.symbol.flags & SymbolFlags.RegularEnum) || !(target.symbol.flags & SymbolFlags.RegularEnum)) { - enumRelation[id] = false; - return false; + if (source.symbol.name !== target.symbol.name || + !(source.symbol.flags & SymbolFlags.RegularEnum) || !(target.symbol.flags & SymbolFlags.RegularEnum) || + !(source.flags & TypeFlags.Union) || !(target.flags & TypeFlags.Union)) { + return (enumRelation[id] = false); } const targetEnumType = getTypeOfSymbol(target.symbol); for (const property of getPropertiesOfType(getTypeOfSymbol(source.symbol))) { @@ -6213,13 +6214,11 @@ namespace ts { errorReporter(Diagnostics.Property_0_is_missing_in_type_1, property.name, typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType)); } - enumRelation[id] = false; - return false; + return (enumRelation[id] = false); } } } - enumRelation[id] = true; - return true; + return (enumRelation[id] = true); } function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { @@ -6234,9 +6233,7 @@ namespace ts { if (source.flags & TypeFlags.Null && (!strictNullChecks || target.flags & TypeFlags.Null)) return true; if (relation === assignableRelation || relation === comparableRelation) { if (source.flags & TypeFlags.Any) return true; - if (source.flags & TypeFlags.Number && target.flags & TypeFlags.EnumLike) return true; - if (source.flags & TypeFlags.NumberLiteral && target.flags & TypeFlags.Enum) return true; - if (source.flags & TypeFlags.NumberLiteral && target.flags & TypeFlags.EnumLiteral && (source).text === (target).text) return true; + if ((source.flags & TypeFlags.Number | source.flags & TypeFlags.NumberLiteral) && target.flags & TypeFlags.EnumLike) return true; if (source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.EnumLiteral && (source).text === (target).text && @@ -6423,14 +6420,6 @@ namespace ts { if (result = typeRelatedToSomeType(source, target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive))) { return result; } - if (source.flags & TypeFlags.Enum) { - // 1. look through the target union for a literal whose base type is related to the source - const lit = find((target).types, t => t.flags & TypeFlags.EnumLiteral && isEnumTypeRelatedTo(source, (t).baseType)); - // 2. if found, the base type is only assignable to source if all its literals are in the target union. - if (lit && (result = isRelatedTo((lit as EnumLiteralType).baseType, target, /*reportErrors*/ false))) { - return result; - } - } } } From bfed54487e3304dd5583c6133d53be45ae42cdfb Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Sep 2016 14:13:17 -0700 Subject: [PATCH 69/73] Update baselines for updated enum assignability --- .../reference/enumAssignmentCompat.errors.txt | 2 +- .../enumAssignmentCompat2.errors.txt | 2 +- .../enumAssignmentCompat3.errors.txt | 8 ++- .../enumAssignmentCompat5.errors.txt | 5 +- ...eralAssignableToEnumInsideUnion.errors.txt | 20 ++++-- .../enumLiteralAssignableToEnumInsideUnion.js | 10 +-- ...LiteralAssignableToEnumInsideUnion.symbols | 66 ----------------- ...umLiteralAssignableToEnumInsideUnion.types | 72 ------------------- .../enumLiteralAssignableToEnumInsideUnion.ts | 5 +- 9 files changed, 34 insertions(+), 156 deletions(-) delete mode 100644 tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.symbols delete mode 100644 tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.types diff --git a/tests/baselines/reference/enumAssignmentCompat.errors.txt b/tests/baselines/reference/enumAssignmentCompat.errors.txt index f1a01247146..7af76d5a102 100644 --- a/tests/baselines/reference/enumAssignmentCompat.errors.txt +++ b/tests/baselines/reference/enumAssignmentCompat.errors.txt @@ -53,4 +53,4 @@ tests/cases/compiler/enumAssignmentCompat.ts(33,5): error TS2322: Type 'number' var i: W = W.a; i = W.a; W.D; - var p: W.D; \ No newline at end of file + var p: W.D; diff --git a/tests/baselines/reference/enumAssignmentCompat2.errors.txt b/tests/baselines/reference/enumAssignmentCompat2.errors.txt index 4e0660eaae3..d1f6a40388f 100644 --- a/tests/baselines/reference/enumAssignmentCompat2.errors.txt +++ b/tests/baselines/reference/enumAssignmentCompat2.errors.txt @@ -52,4 +52,4 @@ tests/cases/compiler/enumAssignmentCompat2.ts(32,5): error TS2322: Type 'number' var i: W = W.a; i = W.a; W.D; - var p: W.D; \ No newline at end of file + var p: W.D; diff --git a/tests/baselines/reference/enumAssignmentCompat3.errors.txt b/tests/baselines/reference/enumAssignmentCompat3.errors.txt index 41fa7a94fca..558540c32c8 100644 --- a/tests/baselines/reference/enumAssignmentCompat3.errors.txt +++ b/tests/baselines/reference/enumAssignmentCompat3.errors.txt @@ -2,15 +2,17 @@ tests/cases/compiler/enumAssignmentCompat3.ts(68,1): error TS2324: Property 'd' tests/cases/compiler/enumAssignmentCompat3.ts(70,1): error TS2322: Type 'Cd.E' is not assignable to type 'First.E'. Property 'd' is missing in type 'First.E'. tests/cases/compiler/enumAssignmentCompat3.ts(71,1): error TS2322: Type 'Nope' is not assignable to type 'E'. +tests/cases/compiler/enumAssignmentCompat3.ts(72,1): error TS2322: Type 'Decl.E' is not assignable to type 'First.E'. tests/cases/compiler/enumAssignmentCompat3.ts(75,1): error TS2324: Property 'c' is missing in type 'Ab.E'. tests/cases/compiler/enumAssignmentCompat3.ts(76,1): error TS2322: Type 'First.E' is not assignable to type 'Cd.E'. tests/cases/compiler/enumAssignmentCompat3.ts(77,1): error TS2322: Type 'E' is not assignable to type 'Nope'. +tests/cases/compiler/enumAssignmentCompat3.ts(78,1): error TS2322: Type 'First.E' is not assignable to type 'Decl.E'. tests/cases/compiler/enumAssignmentCompat3.ts(82,1): error TS2322: Type 'Const.E' is not assignable to type 'First.E'. tests/cases/compiler/enumAssignmentCompat3.ts(83,1): error TS2322: Type 'First.E' is not assignable to type 'Const.E'. tests/cases/compiler/enumAssignmentCompat3.ts(86,1): error TS2324: Property 'd' is missing in type 'First.E'. -==== tests/cases/compiler/enumAssignmentCompat3.ts (9 errors) ==== +==== tests/cases/compiler/enumAssignmentCompat3.ts (11 errors) ==== namespace First { export enum E { a, b, c, @@ -90,6 +92,8 @@ tests/cases/compiler/enumAssignmentCompat3.ts(86,1): error TS2324: Property 'd' ~~~ !!! error TS2322: Type 'Nope' is not assignable to type 'E'. abc = decl; // ok + ~~~ +!!! error TS2322: Type 'Decl.E' is not assignable to type 'First.E'. secondAbc = abc; // ok secondAbcd = abc; // ok secondAb = abc; // missing 'c' @@ -102,6 +106,8 @@ tests/cases/compiler/enumAssignmentCompat3.ts(86,1): error TS2324: Property 'd' ~~~~ !!! error TS2322: Type 'E' is not assignable to type 'Nope'. decl = abc; // ok + ~~~~ +!!! error TS2322: Type 'First.E' is not assignable to type 'Decl.E'. // const is only assignable to itself k = k; diff --git a/tests/baselines/reference/enumAssignmentCompat5.errors.txt b/tests/baselines/reference/enumAssignmentCompat5.errors.txt index 98c0aaccfad..ef703a2ad62 100644 --- a/tests/baselines/reference/enumAssignmentCompat5.errors.txt +++ b/tests/baselines/reference/enumAssignmentCompat5.errors.txt @@ -1,8 +1,7 @@ -tests/cases/compiler/enumAssignmentCompat5.ts(14,1): error TS2322: Type '2' is not assignable to type 'E.A'. tests/cases/compiler/enumAssignmentCompat5.ts(20,9): error TS2535: Enum type 'Computed' has members with initializers that are not literals. -==== tests/cases/compiler/enumAssignmentCompat5.ts (2 errors) ==== +==== tests/cases/compiler/enumAssignmentCompat5.ts (1 errors) ==== enum E { A, B, C } @@ -17,8 +16,6 @@ tests/cases/compiler/enumAssignmentCompat5.ts(20,9): error TS2535: Enum type 'Co e = 4; // ok, out of range, but allowed computed enums don't have all members let a: E.A = 0; // ok, A === 0 a = 2; // error, 2 !== 0 - ~ -!!! error TS2322: Type '2' is not assignable to type 'E.A'. a = n; // ok let c: Computed = n; // ok diff --git a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.errors.txt b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.errors.txt index 37bdc99a922..f79a9d19cd6 100644 --- a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.errors.txt +++ b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.errors.txt @@ -1,8 +1,11 @@ -tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(25,7): error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo.B'. -tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(26,7): error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo.A'. +tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(24,7): error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo'. +tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(25,7): error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo'. +tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(26,7): error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo.B'. +tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(27,7): error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo.A'. +tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(28,7): error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo'. -==== tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts (2 errors) ==== +==== tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts (5 errors) ==== module X { export enum Foo { A, B @@ -25,8 +28,13 @@ tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(26,7): error TS23 B = 1 << 11, } } - const e1: X.Foo | boolean = Z.Foo.A; // ok - const e2: X.Foo.A | X.Foo.B | boolean = Z.Foo.A; // ok, X.Foo is equivalent to X.Foo.A | X.Foo.B + const e0: X.Foo | boolean = Y.Foo.A; // ok + const e1: X.Foo | boolean = Z.Foo.A; // not legal, Z is computed + ~~ +!!! error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo'. + const e2: X.Foo.A | X.Foo.B | boolean = Z.Foo.A; // still not legal + ~~ +!!! error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo'. const e3: X.Foo.B | boolean = Z.Foo.A; // not legal ~~ !!! error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo.B'. @@ -34,4 +42,6 @@ tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(26,7): error TS23 ~~ !!! error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo.A'. const e5: Ka.Foo | boolean = Z.Foo.A; // ok + ~~ +!!! error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo'. \ No newline at end of file diff --git a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.js b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.js index 1665e729719..8786745a504 100644 --- a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.js +++ b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.js @@ -21,8 +21,9 @@ module Ka { B = 1 << 11, } } -const e1: X.Foo | boolean = Z.Foo.A; // ok -const e2: X.Foo.A | X.Foo.B | boolean = Z.Foo.A; // ok, X.Foo is equivalent to X.Foo.A | X.Foo.B +const e0: X.Foo | boolean = Y.Foo.A; // ok +const e1: X.Foo | boolean = Z.Foo.A; // not legal, Z is computed +const e2: X.Foo.A | X.Foo.B | boolean = Z.Foo.A; // still not legal const e3: X.Foo.B | boolean = Z.Foo.A; // not legal const e4: X.Foo.A | boolean = Z.Foo.A; // not legal either because Z.Foo is computed and Z.Foo.A is not necessarily assignable to X.Foo.A const e5: Ka.Foo | boolean = Z.Foo.A; // ok @@ -61,8 +62,9 @@ var Ka; })(Ka.Foo || (Ka.Foo = {})); var Foo = Ka.Foo; })(Ka || (Ka = {})); -var e1 = Z.Foo.A; // ok -var e2 = Z.Foo.A; // ok, X.Foo is equivalent to X.Foo.A | X.Foo.B +var e0 = Y.Foo.A; // ok +var e1 = Z.Foo.A; // not legal, Z is computed +var e2 = Z.Foo.A; // still not legal var e3 = Z.Foo.A; // not legal var e4 = Z.Foo.A; // not legal either because Z.Foo is computed and Z.Foo.A is not necessarily assignable to X.Foo.A var e5 = Z.Foo.A; // ok diff --git a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.symbols b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.symbols deleted file mode 100644 index 8df8e21b386..00000000000 --- a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.symbols +++ /dev/null @@ -1,66 +0,0 @@ -=== tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts === -module X { ->X : Symbol(X, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 0)) - - export enum Foo { ->Foo : Symbol(Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 10)) - - A, B ->A : Symbol(Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 1, 21)) ->B : Symbol(Foo.B, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 2, 10)) - } -} -module Y { ->Y : Symbol(Y, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 4, 1)) - - export enum Foo { ->Foo : Symbol(Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 5, 10)) - - A, B ->A : Symbol(Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 6, 21)) ->B : Symbol(Foo.B, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 7, 10)) - } -} -module Z { ->Z : Symbol(Z, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 9, 1)) - - export enum Foo { ->Foo : Symbol(Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 10, 10)) - - A = 1 << 1, ->A : Symbol(Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 11, 21)) - - B = 1 << 2, ->B : Symbol(Foo.B, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 12, 19)) - } -} -const y: X.Foo | boolean = Y.Foo.A; ->y : Symbol(y, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 16, 5)) ->X : Symbol(X, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 0)) ->Foo : Symbol(X.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 10)) ->Y.Foo.A : Symbol(Y.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 6, 21)) ->Y.Foo : Symbol(Y.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 5, 10)) ->Y : Symbol(Y, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 4, 1)) ->Foo : Symbol(Y.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 5, 10)) ->A : Symbol(Y.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 6, 21)) - -const z: X.Foo | boolean = Z.Foo.A; ->z : Symbol(z, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 17, 5)) ->X : Symbol(X, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 0)) ->Foo : Symbol(X.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 10)) ->Z.Foo.A : Symbol(Z.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 11, 21)) ->Z.Foo : Symbol(Z.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 10, 10)) ->Z : Symbol(Z, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 9, 1)) ->Foo : Symbol(Z.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 10, 10)) ->A : Symbol(Z.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 11, 21)) - -const x: Z.Foo | boolean = X.Foo.A; ->x : Symbol(x, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 18, 5)) ->Z : Symbol(Z, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 9, 1)) ->Foo : Symbol(Z.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 10, 10)) ->X.Foo.A : Symbol(X.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 1, 21)) ->X.Foo : Symbol(X.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 10)) ->X : Symbol(X, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 0)) ->Foo : Symbol(X.Foo, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 0, 10)) ->A : Symbol(X.Foo.A, Decl(enumLiteralAssignableToEnumInsideUnion.ts, 1, 21)) - diff --git a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.types b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.types deleted file mode 100644 index 5dbb98bcf9c..00000000000 --- a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.types +++ /dev/null @@ -1,72 +0,0 @@ -=== tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts === -module X { ->X : typeof X - - export enum Foo { ->Foo : Foo - - A, B ->A : Foo ->B : Foo - } -} -module Y { ->Y : typeof Y - - export enum Foo { ->Foo : Foo - - A, B ->A : Foo ->B : Foo - } -} -module Z { ->Z : typeof Z - - export enum Foo { ->Foo : Foo - - A = 1 << 1, ->A : Foo ->1 << 1 : number ->1 : number ->1 : number - - B = 1 << 2, ->B : Foo ->1 << 2 : number ->1 : number ->2 : number - } -} -const y: X.Foo | boolean = Y.Foo.A; ->y : boolean | X.Foo ->X : any ->Foo : X.Foo ->Y.Foo.A : Y.Foo ->Y.Foo : typeof Y.Foo ->Y : typeof Y ->Foo : typeof Y.Foo ->A : Y.Foo - -const z: X.Foo | boolean = Z.Foo.A; ->z : boolean | X.Foo ->X : any ->Foo : X.Foo ->Z.Foo.A : Z.Foo ->Z.Foo : typeof Z.Foo ->Z : typeof Z ->Foo : typeof Z.Foo ->A : Z.Foo - -const x: Z.Foo | boolean = X.Foo.A; ->x : boolean | Z.Foo ->Z : any ->Foo : Z.Foo ->X.Foo.A : X.Foo ->X.Foo : typeof X.Foo ->X : typeof X ->Foo : typeof X.Foo ->A : X.Foo - diff --git a/tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts b/tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts index c3029af0f42..19cd98fd5e8 100644 --- a/tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts +++ b/tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts @@ -20,8 +20,9 @@ module Ka { B = 1 << 11, } } -const e1: X.Foo | boolean = Z.Foo.A; // ok -const e2: X.Foo.A | X.Foo.B | boolean = Z.Foo.A; // ok, X.Foo is equivalent to X.Foo.A | X.Foo.B +const e0: X.Foo | boolean = Y.Foo.A; // ok +const e1: X.Foo | boolean = Z.Foo.A; // not legal, Z is computed +const e2: X.Foo.A | X.Foo.B | boolean = Z.Foo.A; // still not legal const e3: X.Foo.B | boolean = Z.Foo.A; // not legal const e4: X.Foo.A | boolean = Z.Foo.A; // not legal either because Z.Foo is computed and Z.Foo.A is not necessarily assignable to X.Foo.A const e5: Ka.Foo | boolean = Z.Foo.A; // ok From 267042fdc3e5cf281ef6222003da7c629cef36af Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Sep 2016 14:43:24 -0700 Subject: [PATCH 70/73] Computed enum assignability is semi-structural --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 28accd8452a..f17c34e4a81 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6202,7 +6202,7 @@ namespace ts { } if (source.symbol.name !== target.symbol.name || !(source.symbol.flags & SymbolFlags.RegularEnum) || !(target.symbol.flags & SymbolFlags.RegularEnum) || - !(source.flags & TypeFlags.Union) || !(target.flags & TypeFlags.Union)) { + (source.flags & TypeFlags.Union) !== (target.flags & TypeFlags.Union)) { return (enumRelation[id] = false); } const targetEnumType = getTypeOfSymbol(target.symbol); From 71fa082a0c658d9b3c221370a2285a1d6a6ba5ab Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 12 Sep 2016 14:43:44 -0700 Subject: [PATCH 71/73] Update baselines --- .../enumLiteralAssignableToEnumInsideUnion.errors.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.errors.txt b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.errors.txt index f79a9d19cd6..660e1094ae9 100644 --- a/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.errors.txt +++ b/tests/baselines/reference/enumLiteralAssignableToEnumInsideUnion.errors.txt @@ -2,10 +2,9 @@ tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(24,7): error TS23 tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(25,7): error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo'. tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(26,7): error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo.B'. tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(27,7): error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo.A'. -tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(28,7): error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo'. -==== tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts (5 errors) ==== +==== tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts (4 errors) ==== module X { export enum Foo { A, B @@ -42,6 +41,4 @@ tests/cases/compiler/enumLiteralAssignableToEnumInsideUnion.ts(28,7): error TS23 ~~ !!! error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo.A'. const e5: Ka.Foo | boolean = Z.Foo.A; // ok - ~~ -!!! error TS2322: Type 'Foo' is not assignable to type 'boolean | Foo'. \ No newline at end of file From d81ad4608dddb44d66e28da3441866aff61c346a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 13 Sep 2016 08:48:07 -0700 Subject: [PATCH 72/73] Remove unnecessary parentheses --- src/compiler/checker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f17c34e4a81..b2361c464dd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6203,7 +6203,7 @@ namespace ts { if (source.symbol.name !== target.symbol.name || !(source.symbol.flags & SymbolFlags.RegularEnum) || !(target.symbol.flags & SymbolFlags.RegularEnum) || (source.flags & TypeFlags.Union) !== (target.flags & TypeFlags.Union)) { - return (enumRelation[id] = false); + return enumRelation[id] = false; } const targetEnumType = getTypeOfSymbol(target.symbol); for (const property of getPropertiesOfType(getTypeOfSymbol(source.symbol))) { @@ -6214,11 +6214,11 @@ namespace ts { errorReporter(Diagnostics.Property_0_is_missing_in_type_1, property.name, typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType)); } - return (enumRelation[id] = false); + return enumRelation[id] = false; } } } - return (enumRelation[id] = true); + return enumRelation[id] = true; } function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { From 28ec7d6177b54307ae325317404dffbe8cfa6261 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 13 Sep 2016 13:38:29 -0700 Subject: [PATCH 73/73] Update baselines after cherry-pick to 2.0.5 --- tests/baselines/reference/enumAssignmentCompat.errors.txt | 2 +- tests/baselines/reference/enumAssignmentCompat2.errors.txt | 2 +- .../reference/numberAssignableToEnumInsideUnion.types | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/enumAssignmentCompat.errors.txt b/tests/baselines/reference/enumAssignmentCompat.errors.txt index 7af76d5a102..f1a01247146 100644 --- a/tests/baselines/reference/enumAssignmentCompat.errors.txt +++ b/tests/baselines/reference/enumAssignmentCompat.errors.txt @@ -53,4 +53,4 @@ tests/cases/compiler/enumAssignmentCompat.ts(33,5): error TS2322: Type 'number' var i: W = W.a; i = W.a; W.D; - var p: W.D; + var p: W.D; \ No newline at end of file diff --git a/tests/baselines/reference/enumAssignmentCompat2.errors.txt b/tests/baselines/reference/enumAssignmentCompat2.errors.txt index d1f6a40388f..4e0660eaae3 100644 --- a/tests/baselines/reference/enumAssignmentCompat2.errors.txt +++ b/tests/baselines/reference/enumAssignmentCompat2.errors.txt @@ -52,4 +52,4 @@ tests/cases/compiler/enumAssignmentCompat2.ts(32,5): error TS2322: Type 'number' var i: W = W.a; i = W.a; W.D; - var p: W.D; + var p: W.D; \ No newline at end of file diff --git a/tests/baselines/reference/numberAssignableToEnumInsideUnion.types b/tests/baselines/reference/numberAssignableToEnumInsideUnion.types index 56d6efb0d1e..fd45ff17622 100644 --- a/tests/baselines/reference/numberAssignableToEnumInsideUnion.types +++ b/tests/baselines/reference/numberAssignableToEnumInsideUnion.types @@ -1,8 +1,8 @@ === tests/cases/compiler/numberAssignableToEnumInsideUnion.ts === enum E { A, B } >E : E ->A : E.A ->B : E.B +>A : E +>B : E let n: number; >n : number