mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-24 04:30:53 -06:00
Minor cleanups in pathCompletions.ts (#19685)
* Minor cleanups in pathCompletions.ts * Update name
This commit is contained in:
parent
749e151c23
commit
1d7f449a87
@ -7,18 +7,10 @@ namespace ts {
|
||||
const ignoreDiagnosticCommentRegEx = /(^\s*$)|(^\s*\/\/\/?\s*(@ts-ignore)?)/;
|
||||
|
||||
export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "tsconfig.json"): string {
|
||||
while (true) {
|
||||
const fileName = combinePaths(searchPath, configName);
|
||||
if (fileExists(fileName)) {
|
||||
return fileName;
|
||||
}
|
||||
const parentPath = getDirectoryPath(searchPath);
|
||||
if (parentPath === searchPath) {
|
||||
break;
|
||||
}
|
||||
searchPath = parentPath;
|
||||
}
|
||||
return undefined;
|
||||
return forEachAncestorDirectory(searchPath, ancestor => {
|
||||
const fileName = combinePaths(ancestor, configName);
|
||||
return fileExists(fileName) ? fileName : undefined;
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveTripleslashReference(moduleName: string, containingFile: string): string {
|
||||
|
||||
@ -3592,7 +3592,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
/** Calls `callback` on `directory` and every ancestor directory it has, returning the first defined result. */
|
||||
export function forEachAncestorDirectory<T>(directory: string, callback: (directory: string) => T): T {
|
||||
export function forEachAncestorDirectory<T>(directory: string, callback: (directory: string) => T | undefined): T | undefined {
|
||||
while (true) {
|
||||
const result = callback(directory);
|
||||
if (result !== undefined) {
|
||||
|
||||
@ -30,7 +30,8 @@ namespace ts.Completions {
|
||||
allSourceFiles: ReadonlyArray<SourceFile>,
|
||||
): CompletionInfo | undefined {
|
||||
if (isInReferenceComment(sourceFile, position)) {
|
||||
return PathCompletions.getTripleSlashReferenceCompletion(sourceFile, position, compilerOptions, host);
|
||||
const entries = PathCompletions.getTripleSlashReferenceCompletion(sourceFile, position, compilerOptions, host);
|
||||
return entries && pathCompletionsInfo(entries);
|
||||
}
|
||||
|
||||
if (isInString(sourceFile, position)) {
|
||||
@ -250,7 +251,8 @@ namespace ts.Completions {
|
||||
// import x = require("/*completion position*/");
|
||||
// var y = require("/*completion position*/");
|
||||
// export * from "/*completion position*/";
|
||||
return PathCompletions.getStringLiteralCompletionEntriesFromModuleNames(<StringLiteral>node, compilerOptions, host, typeChecker);
|
||||
const entries = PathCompletions.getStringLiteralCompletionsFromModuleNames(<StringLiteral>node, compilerOptions, host, typeChecker);
|
||||
return pathCompletionsInfo(entries);
|
||||
}
|
||||
else if (isEqualityExpression(node.parent)) {
|
||||
// Get completions from the type of the other operand
|
||||
@ -279,6 +281,18 @@ namespace ts.Completions {
|
||||
}
|
||||
}
|
||||
|
||||
function pathCompletionsInfo(entries: CompletionEntry[]): CompletionInfo {
|
||||
return {
|
||||
// We don't want the editor to offer any other completions, such as snippets, inside a comment.
|
||||
isGlobalCompletion: false,
|
||||
isMemberCompletion: false,
|
||||
// The user may type in a path that doesn't yet exist, creating a "new identifier"
|
||||
// with respect to the collection of identifiers the server is aware of.
|
||||
isNewIdentifierLocation: true,
|
||||
entries,
|
||||
};
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionEntriesFromPropertyAssignment(element: ObjectLiteralElement, typeChecker: TypeChecker, target: ScriptTarget, log: Log): CompletionInfo | undefined {
|
||||
const type = typeChecker.getContextualType((<ObjectLiteralExpression>element.parent));
|
||||
const entries: CompletionEntry[] = [];
|
||||
|
||||
@ -1,34 +1,27 @@
|
||||
/* @internal */
|
||||
namespace ts.Completions.PathCompletions {
|
||||
export function getStringLiteralCompletionEntriesFromModuleNames(node: StringLiteral, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): CompletionInfo {
|
||||
export function getStringLiteralCompletionsFromModuleNames(node: StringLiteral, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): CompletionEntry[] {
|
||||
const literalValue = normalizeSlashes(node.text);
|
||||
|
||||
const scriptPath = node.getSourceFile().path;
|
||||
const scriptDirectory = getDirectoryPath(scriptPath);
|
||||
|
||||
const span = getDirectoryFragmentTextSpan((<StringLiteral>node).text, node.getStart() + 1);
|
||||
let entries: CompletionEntry[];
|
||||
if (isPathRelativeToScript(literalValue) || isRootedDiskPath(literalValue)) {
|
||||
const extensions = getSupportedExtensions(compilerOptions);
|
||||
if (compilerOptions.rootDirs) {
|
||||
entries = getCompletionEntriesForDirectoryFragmentWithRootDirs(
|
||||
return getCompletionEntriesForDirectoryFragmentWithRootDirs(
|
||||
compilerOptions.rootDirs, literalValue, scriptDirectory, extensions, /*includeExtensions*/ false, span, compilerOptions, host, scriptPath);
|
||||
}
|
||||
else {
|
||||
entries = getCompletionEntriesForDirectoryFragment(
|
||||
return getCompletionEntriesForDirectoryFragment(
|
||||
literalValue, scriptDirectory, extensions, /*includeExtensions*/ false, span, host, scriptPath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Check for node modules
|
||||
entries = getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, span, compilerOptions, host, typeChecker);
|
||||
return getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, span, compilerOptions, host, typeChecker);
|
||||
}
|
||||
return {
|
||||
isGlobalCompletion: false,
|
||||
isMemberCompletion: false,
|
||||
isNewIdentifierLocation: true,
|
||||
entries
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,14 +30,14 @@ namespace ts.Completions.PathCompletions {
|
||||
*/
|
||||
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)));
|
||||
rootDirs = rootDirs.map(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
|
||||
const relativeDirectory = forEach(rootDirs, rootDirectory =>
|
||||
const relativeDirectory = firstDefined(rootDirs, rootDirectory =>
|
||||
containsPath(rootDirectory, scriptPath, basePath, ignoreCase) ? scriptPath.substr(rootDirectory.length) : undefined);
|
||||
|
||||
// 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)));
|
||||
return deduplicate(rootDirs.map(rootDirectory => combinePaths(rootDirectory, relativeDirectory)));
|
||||
}
|
||||
|
||||
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): CompletionEntry[] {
|
||||
@ -283,61 +276,35 @@ namespace ts.Completions.PathCompletions {
|
||||
return deduplicate(nonRelativeModuleNames);
|
||||
}
|
||||
|
||||
export function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): CompletionInfo {
|
||||
export function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): CompletionEntry[] | undefined {
|
||||
const token = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false);
|
||||
if (!token) {
|
||||
return undefined;
|
||||
}
|
||||
const commentRanges: CommentRange[] = getLeadingCommentRanges(sourceFile.text, token.pos);
|
||||
|
||||
if (!commentRanges || !commentRanges.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const range = forEach(commentRanges, commentRange => position >= commentRange.pos && position <= commentRange.end && commentRange);
|
||||
|
||||
const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
|
||||
const range = commentRanges && find(commentRanges, commentRange => position >= commentRange.pos && position <= commentRange.end);
|
||||
if (!range) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const completionInfo: CompletionInfo = {
|
||||
/**
|
||||
* We don't want the editor to offer any other completions, such as snippets, inside a comment.
|
||||
*/
|
||||
isGlobalCompletion: false,
|
||||
isMemberCompletion: false,
|
||||
/**
|
||||
* The user may type in a path that doesn't yet exist, creating a "new identifier"
|
||||
* with respect to the collection of identifiers the server is aware of.
|
||||
*/
|
||||
isNewIdentifierLocation: true,
|
||||
|
||||
entries: []
|
||||
};
|
||||
|
||||
const text = sourceFile.text.substr(range.pos, position - range.pos);
|
||||
|
||||
const text = sourceFile.text.slice(range.pos, position);
|
||||
const match = tripleSlashDirectiveFragmentRegex.exec(text);
|
||||
|
||||
if (match) {
|
||||
const prefix = match[1];
|
||||
const kind = match[2];
|
||||
const toComplete = match[3];
|
||||
|
||||
const scriptPath = getDirectoryPath(sourceFile.path);
|
||||
if (kind === "path") {
|
||||
// Give completions for a relative path
|
||||
const span: TextSpan = getDirectoryFragmentTextSpan(toComplete, range.pos + prefix.length);
|
||||
completionInfo.entries = getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(compilerOptions), /*includeExtensions*/ true, span, host, sourceFile.path);
|
||||
}
|
||||
else {
|
||||
// Give completions based on the typings available
|
||||
const span: TextSpan = { start: range.pos + prefix.length, length: match[0].length - prefix.length };
|
||||
completionInfo.entries = getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, span);
|
||||
}
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return completionInfo;
|
||||
const [, prefix, kind, toComplete] = match;
|
||||
const scriptPath = getDirectoryPath(sourceFile.path);
|
||||
switch (kind) {
|
||||
case "path": {
|
||||
// Give completions for a relative path
|
||||
const span = getDirectoryFragmentTextSpan(toComplete, range.pos + prefix.length);
|
||||
return getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(compilerOptions), /*includeExtensions*/ true, span, host, sourceFile.path);
|
||||
}
|
||||
case "types": {
|
||||
// Give completions based on the typings available
|
||||
const span = createTextSpan(range.pos + prefix.length, match[0].length - prefix.length);
|
||||
return getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, span);
|
||||
}
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, span: TextSpan, result: CompletionEntry[] = []): CompletionEntry[] {
|
||||
@ -385,26 +352,15 @@ namespace ts.Completions.PathCompletions {
|
||||
}
|
||||
}
|
||||
|
||||
function findPackageJsons(currentDir: string, host: LanguageServiceHost): string[] {
|
||||
function findPackageJsons(directory: string, host: LanguageServiceHost): string[] {
|
||||
const paths: string[] = [];
|
||||
let currentConfigPath: string;
|
||||
while (true) {
|
||||
currentConfigPath = findConfigFile(currentDir, (f) => tryFileExists(host, f), "package.json");
|
||||
if (currentConfigPath) {
|
||||
paths.push(currentConfigPath);
|
||||
|
||||
currentDir = getDirectoryPath(currentConfigPath);
|
||||
const parent = getDirectoryPath(currentDir);
|
||||
if (currentDir === parent) {
|
||||
break;
|
||||
}
|
||||
currentDir = parent;
|
||||
forEachAncestorDirectory(directory, ancestor => {
|
||||
const currentConfigPath = findConfigFile(ancestor, (f) => tryFileExists(host, f), "package.json");
|
||||
if (!currentConfigPath) {
|
||||
return true; // break out
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
paths.push(currentConfigPath);
|
||||
});
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user