mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 20:25:23 -06:00
Adding import completions for typings
This commit is contained in:
parent
0b16180174
commit
dbf19f18af
@ -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);
|
||||
|
||||
@ -1968,7 +1968,7 @@ namespace ts {
|
||||
* for completions.
|
||||
* For example, this matches /// <reference path="fragment
|
||||
*/
|
||||
const tripleSlashDirectiveFragmentRegex = /^\/\/\/\s*<reference\s+path\s*=\s*(?:'|")([^'"]+)$/;
|
||||
const tripleSlashDirectiveFragmentRegex = /^\/\/\/\s*<reference\s+(path|types)\s*=\s*(?:'|")([^'"]+)$/;
|
||||
|
||||
let commandLineOptionsStringToEnum: CommandLineOptionOfCustomType[];
|
||||
|
||||
@ -4501,7 +4501,7 @@ namespace ts {
|
||||
result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/false);
|
||||
|
||||
if (paths) {
|
||||
for (var path in paths) {
|
||||
for (const path in paths) {
|
||||
if (paths.hasOwnProperty(path)) {
|
||||
if (path === "*") {
|
||||
if (paths[path]) {
|
||||
@ -4526,7 +4526,7 @@ namespace ts {
|
||||
result = [];
|
||||
}
|
||||
|
||||
|
||||
getCompletionEntriesFromTypings(host, options, scriptPath, result);
|
||||
|
||||
forEach(enumeratePotentialNonRelativeModules(fragment, scriptPath), moduleName => {
|
||||
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 {
|
||||
|
||||
@ -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"];
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @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);
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @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);
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @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);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user