Merge branch 'master' into inferredTypeParameterConstraints

This commit is contained in:
Anders Hejlsberg
2018-02-06 17:22:07 -08:00
8 changed files with 505 additions and 204 deletions
+133 -174
View File
@@ -36,7 +36,7 @@ namespace ts.Completions {
): CompletionInfo | undefined {
if (isInReferenceComment(sourceFile, position)) {
const entries = PathCompletions.getTripleSlashReferenceCompletion(sourceFile, position, compilerOptions, host);
return entries && pathCompletionsInfo(entries);
return entries && convertPathCompletions(entries);
}
const contextToken = findPrecedingToken(position, sourceFile);
@@ -44,7 +44,7 @@ namespace ts.Completions {
if (isInString(sourceFile, position, contextToken)) {
return !contextToken || !isStringLiteral(contextToken) && !isNoSubstitutionTemplateLiteral(contextToken)
? undefined
: getStringLiteralCompletionEntries(sourceFile, contextToken, position, typeChecker, compilerOptions, host, log);
: convertStringLiteralCompletions(getStringLiteralCompletionEntries(sourceFile, contextToken, position, typeChecker, compilerOptions, host), sourceFile, typeChecker, log);
}
if (contextToken && isBreakOrContinueStatement(contextToken.parent)
@@ -73,6 +73,34 @@ namespace ts.Completions {
}
}
function convertStringLiteralCompletions(completion: StringLiteralCompletion | undefined, sourceFile: SourceFile, checker: TypeChecker, log: Log): CompletionInfo | undefined {
if (completion === undefined) {
return undefined;
}
switch (completion.kind) {
case StringLiteralCompletionKind.Paths:
return convertPathCompletions(completion.paths);
case StringLiteralCompletionKind.Properties: {
const entries: CompletionEntry[] = [];
getCompletionEntriesFromSymbols(completion.symbols, entries, sourceFile, sourceFile, checker, ScriptTarget.ESNext, log, CompletionKind.String); // Target will not be used, so arbitrary
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries };
}
case StringLiteralCompletionKind.Types: {
const entries = completion.types.map(type => ({ name: type.value, kindModifiers: ScriptElementKindModifier.none, kind: ScriptElementKind.variableElement, sortText: "0" }));
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: true, entries };
}
default:
return Debug.assertNever(completion);
}
}
function convertPathCompletions(pathCompletions: ReadonlyArray<PathCompletions.PathCompletion>): CompletionInfo {
const isGlobalCompletion = false; // We don't want the editor to offer any other completions, such as snippets, inside a comment.
const isNewIdentifierLocation = true; // 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.
const entries = pathCompletions.map(({ name, kind, span }) => ({ name, kind, kindModifiers: ScriptElementKindModifier.none, sortText: "0", replacementSpan: span }));
return { isGlobalCompletion, isMemberCompletion: false, isNewIdentifierLocation, entries };
}
function jsdocCompletionInfo(entries: CompletionEntry[]): CompletionInfo {
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries };
}
@@ -294,156 +322,6 @@ namespace ts.Completions {
}
}
function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringLiteralLike, position: number, typeChecker: TypeChecker, compilerOptions: CompilerOptions, host: LanguageServiceHost, log: Log): CompletionInfo | undefined {
switch (node.parent.kind) {
case SyntaxKind.LiteralType:
switch (node.parent.parent.kind) {
case SyntaxKind.TypeReference:
// TODO: GH#21168
return undefined;
case SyntaxKind.IndexedAccessType:
// Get all apparent property names
// i.e. interface Foo {
// foo: string;
// bar: string;
// }
// let x: Foo["/*completion position*/"]
const type = typeChecker.getTypeFromTypeNode((node.parent.parent as IndexedAccessTypeNode).objectType);
return getStringLiteralCompletionEntriesFromElementAccessOrIndexedAccess(node, sourceFile, type, typeChecker, compilerOptions.target, log);
default:
return undefined;
}
case SyntaxKind.PropertyAssignment:
if (node.parent.parent.kind === SyntaxKind.ObjectLiteralExpression &&
(<PropertyAssignment>node.parent).name === node) {
// Get quoted name of properties of the object literal expression
// i.e. interface ConfigFiles {
// 'jspm:dev': string
// }
// let files: ConfigFiles = {
// '/*completion position*/'
// }
//
// function foo(c: ConfigFiles) {}
// foo({
// '/*completion position*/'
// });
return getStringLiteralCompletionEntriesFromPropertyAssignment(<PropertyAssignment>node.parent, sourceFile, typeChecker, compilerOptions.target, log);
}
return fromContextualType();
case SyntaxKind.ElementAccessExpression: {
const { expression, argumentExpression } = node.parent as ElementAccessExpression;
if (node === argumentExpression) {
// Get all names of properties on the expression
// i.e. interface A {
// 'prop1': string
// }
// let a: A;
// a['/*completion position*/']
const type = typeChecker.getTypeAtLocation(expression);
return getStringLiteralCompletionEntriesFromElementAccessOrIndexedAccess(node, sourceFile, type, typeChecker, compilerOptions.target, log);
}
break;
}
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
if (!isRequireCall(node.parent, /*checkArgumentIsStringLiteral*/ false) && !isImportCall(node.parent)) {
const argumentInfo = SignatureHelp.getImmediatelyContainingArgumentInfo(node, position, sourceFile);
// Get string literal completions from specialized signatures of the target
// i.e. declare function f(a: 'A');
// f("/*completion position*/")
return argumentInfo ? getStringLiteralCompletionEntriesFromCallExpression(argumentInfo, typeChecker) : fromContextualType();
}
// falls through
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ExportDeclaration:
case SyntaxKind.ExternalModuleReference:
// Get all known external module names or complete a path to a module
// i.e. import * as ns from "/*completion position*/";
// var y = import("/*completion position*/");
// import x = require("/*completion position*/");
// var y = require("/*completion position*/");
// export * from "/*completion position*/";
return pathCompletionsInfo(PathCompletions.getStringLiteralCompletionsFromModuleNames(sourceFile, node, compilerOptions, host, typeChecker));
default:
return fromContextualType();
}
function fromContextualType(): CompletionInfo {
// Get completion for string literal from string literal type
// i.e. var x: "hi" | "hello" = "/*completion position*/"
return getStringLiteralCompletionEntriesFromType(getContextualTypeFromParent(node, typeChecker), typeChecker);
}
}
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, sourceFile: SourceFile, typeChecker: TypeChecker, target: ScriptTarget, log: Log): CompletionInfo | undefined {
const type = typeChecker.getContextualType((<ObjectLiteralExpression>element.parent));
const entries: CompletionEntry[] = [];
if (type) {
getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, element, sourceFile, typeChecker, target, log, CompletionKind.String);
if (entries.length) {
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries };
}
}
}
function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo, typeChecker: TypeChecker): CompletionInfo | undefined {
const candidates: Signature[] = [];
const entries: CompletionEntry[] = [];
const uniques = createMap<true>();
typeChecker.getResolvedSignature(argumentInfo.invocation, candidates, argumentInfo.argumentCount);
for (const candidate of candidates) {
addStringLiteralCompletionsFromType(typeChecker.getParameterType(candidate, argumentInfo.argumentIndex), entries, typeChecker, uniques);
}
if (entries.length) {
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: true, entries };
}
return undefined;
}
function getStringLiteralCompletionEntriesFromElementAccessOrIndexedAccess(stringLiteralNode: StringLiteral | NoSubstitutionTemplateLiteral, sourceFile: SourceFile, type: Type, typeChecker: TypeChecker, target: ScriptTarget, log: Log): CompletionInfo | undefined {
const entries: CompletionEntry[] = [];
if (type) {
getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, stringLiteralNode, sourceFile, typeChecker, target, log, CompletionKind.String);
if (entries.length) {
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries };
}
}
return undefined;
}
function getStringLiteralCompletionEntriesFromType(type: Type, typeChecker: TypeChecker): CompletionInfo | undefined {
if (type) {
const entries: CompletionEntry[] = [];
addStringLiteralCompletionsFromType(type, entries, typeChecker);
if (entries.length) {
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries };
}
}
return undefined;
}
function getLabelStatementCompletions(node: Node): CompletionEntry[] {
const entries: CompletionEntry[] = [];
const uniques = createMap<true>();
@@ -470,30 +348,111 @@ namespace ts.Completions {
return entries;
}
function addStringLiteralCompletionsFromType(type: Type, result: Push<CompletionEntry>, typeChecker: TypeChecker, uniques = createMap<true>()): void {
const enum StringLiteralCompletionKind { Paths, Properties, Types }
type StringLiteralCompletion =
| { readonly kind: StringLiteralCompletionKind.Paths, readonly paths: ReadonlyArray<PathCompletions.PathCompletion> }
| { readonly kind: StringLiteralCompletionKind.Properties, readonly symbols: ReadonlyArray<Symbol> }
| { readonly kind: StringLiteralCompletionKind.Types, readonly types: ReadonlyArray<StringLiteralType> };
function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringLiteralLike, position: number, typeChecker: TypeChecker, compilerOptions: CompilerOptions, host: LanguageServiceHost): StringLiteralCompletion | undefined {
switch (node.parent.kind) {
case SyntaxKind.LiteralType:
switch (node.parent.parent.kind) {
case SyntaxKind.TypeReference:
// TODO: GH#21168
return undefined;
case SyntaxKind.IndexedAccessType:
// Get all apparent property names
// i.e. interface Foo {
// foo: string;
// bar: string;
// }
// let x: Foo["/*completion position*/"]
return { kind: StringLiteralCompletionKind.Properties, symbols: typeChecker.getTypeFromTypeNode((node.parent.parent as IndexedAccessTypeNode).objectType).getApparentProperties() };
default:
return undefined;
}
case SyntaxKind.PropertyAssignment:
if (isObjectLiteralExpression(node.parent.parent) && (<PropertyAssignment>node.parent).name === node) {
// Get quoted name of properties of the object literal expression
// i.e. interface ConfigFiles {
// 'jspm:dev': string
// }
// let files: ConfigFiles = {
// '/*completion position*/'
// }
//
// function foo(c: ConfigFiles) {}
// foo({
// '/*completion position*/'
// });
const type = typeChecker.getContextualType(node.parent.parent);
return { kind: StringLiteralCompletionKind.Properties, symbols: type && type.getApparentProperties() };
}
return fromContextualType();
case SyntaxKind.ElementAccessExpression: {
const { expression, argumentExpression } = node.parent as ElementAccessExpression;
if (node === argumentExpression) {
// Get all names of properties on the expression
// i.e. interface A {
// 'prop1': string
// }
// let a: A;
// a['/*completion position*/']
return { kind: StringLiteralCompletionKind.Properties, symbols: typeChecker.getTypeAtLocation(expression).getApparentProperties() };
}
return undefined;
}
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
if (!isRequireCall(node.parent, /*checkArgumentIsStringLiteral*/ false) && !isImportCall(node.parent)) {
const argumentInfo = SignatureHelp.getImmediatelyContainingArgumentInfo(node, position, sourceFile);
// Get string literal completions from specialized signatures of the target
// i.e. declare function f(a: 'A');
// f("/*completion position*/")
if (argumentInfo) {
const candidates: Signature[] = [];
typeChecker.getResolvedSignature(argumentInfo.invocation, candidates, argumentInfo.argumentCount);
const uniques = createMap<true>();
return { kind: StringLiteralCompletionKind.Types, types: flatMap(candidates, candidate => getStringLiteralTypes(typeChecker.getParameterType(candidate, argumentInfo.argumentIndex), typeChecker, uniques)) };
}
return fromContextualType();
}
// falls through (is `require("")` or `import("")`)
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ExportDeclaration:
case SyntaxKind.ExternalModuleReference:
// Get all known external module names or complete a path to a module
// i.e. import * as ns from "/*completion position*/";
// var y = import("/*completion position*/");
// import x = require("/*completion position*/");
// var y = require("/*completion position*/");
// export * from "/*completion position*/";
return { kind: StringLiteralCompletionKind.Paths, paths: PathCompletions.getStringLiteralCompletionsFromModuleNames(sourceFile, node, compilerOptions, host, typeChecker) };
default:
return fromContextualType();
}
function fromContextualType(): StringLiteralCompletion {
// Get completion for string literal from string literal type
// i.e. var x: "hi" | "hello" = "/*completion position*/"
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker), typeChecker) };
}
}
function getStringLiteralTypes(type: Type, typeChecker: TypeChecker, uniques = createMap<true>()): ReadonlyArray<StringLiteralType> {
if (type && type.flags & TypeFlags.TypeParameter) {
type = typeChecker.getBaseConstraintOfType(type);
}
if (!type) {
return;
}
if (type.flags & TypeFlags.Union) {
for (const t of (<UnionType>type).types) {
addStringLiteralCompletionsFromType(t, result, typeChecker, uniques);
}
}
else if (type.flags & TypeFlags.StringLiteral && !(type.flags & TypeFlags.EnumLiteral)) {
const name = (<StringLiteralType>type).value;
if (!uniques.has(name)) {
uniques.set(name, true);
result.push({
name,
kindModifiers: ScriptElementKindModifier.none,
kind: ScriptElementKind.variableElement,
sortText: "0"
});
}
type = type.getConstraint();
}
return type && type.flags & TypeFlags.Union
? flatMap((<UnionType>type).types, t => getStringLiteralTypes(t, typeChecker, uniques))
: type && type.flags & TypeFlags.StringLiteral && !(type.flags & TypeFlags.EnumLiteral) && addToSeen(uniques, (type as StringLiteralType).value)
? [type as StringLiteralType]
: emptyArray;
}
interface SymbolCompletion {
+37 -30
View File
@@ -1,6 +1,17 @@
/* @internal */
namespace ts.Completions.PathCompletions {
export function getStringLiteralCompletionsFromModuleNames(sourceFile: SourceFile, node: LiteralExpression, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): CompletionEntry[] {
export interface NameAndKind {
readonly name: string;
readonly kind: ScriptElementKind.scriptElement | ScriptElementKind.directory | ScriptElementKind.externalModuleName;
}
export interface PathCompletion extends NameAndKind {
readonly span: TextSpan;
}
function createPathCompletion(name: string, kind: PathCompletion["kind"], span: TextSpan): PathCompletion {
return { name, kind, span };
}
export function getStringLiteralCompletionsFromModuleNames(sourceFile: SourceFile, node: LiteralExpression, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): PathCompletion[] {
const literalValue = normalizeSlashes(node.text);
const scriptPath = node.getSourceFile().path;
@@ -43,12 +54,12 @@ namespace ts.Completions.PathCompletions {
compareStringsCaseSensitive);
}
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): CompletionEntry[] {
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): PathCompletion[] {
const basePath = compilerOptions.project || host.getCurrentDirectory();
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptPath, ignoreCase);
const result: CompletionEntry[] = [];
const result: PathCompletion[] = [];
for (const baseDirectory of baseDirectories) {
getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, span, host, exclude, result);
@@ -60,7 +71,7 @@ namespace ts.Completions.PathCompletions {
/**
* Given a path ending at a directory, gets the completions for the path, and filters for those entries containing the basename.
*/
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, host: LanguageServiceHost, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] {
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, host: LanguageServiceHost, exclude?: string, result: PathCompletion[] = []): PathCompletion[] {
if (fragment === undefined) {
fragment = "";
}
@@ -109,7 +120,7 @@ namespace ts.Completions.PathCompletions {
}
forEachKey(foundFiles, foundFile => {
result.push(createCompletionEntryForModule(foundFile, ScriptElementKind.scriptElement, span));
result.push(createPathCompletion(foundFile, ScriptElementKind.scriptElement, span));
});
}
@@ -120,7 +131,7 @@ namespace ts.Completions.PathCompletions {
for (const directory of directories) {
const directoryName = getBaseFileName(normalizePath(directory));
result.push(createCompletionEntryForModule(directoryName, ScriptElementKind.directory, span));
result.push(createPathCompletion(directoryName, ScriptElementKind.directory, span));
}
}
}
@@ -135,10 +146,10 @@ namespace ts.Completions.PathCompletions {
* 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, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): CompletionEntry[] {
function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): PathCompletion[] {
const { baseUrl, paths } = compilerOptions;
const result: CompletionEntry[] = [];
const result: PathCompletion[] = [];
const fileExtensions = getSupportedExtensions(compilerOptions);
if (baseUrl) {
@@ -149,10 +160,10 @@ namespace ts.Completions.PathCompletions {
for (const path in paths) {
const patterns = paths[path];
if (paths.hasOwnProperty(path) && patterns) {
for (const pathCompletion of getCompletionsForPathMapping(path, patterns, fragment, baseUrl, fileExtensions, host)) {
for (const { name, kind } of getCompletionsForPathMapping(path, patterns, fragment, baseUrl, fileExtensions, host)) {
// Path mappings may provide a duplicate way to get to something we've already added, so don't add again.
if (!result.some(entry => entry.name === pathCompletion)) {
result.push(createCompletionEntryForModule(pathCompletion, ScriptElementKind.externalModuleName, span));
if (!result.some(entry => entry.name === name)) {
result.push(createPathCompletion(name, kind, span));
}
}
}
@@ -171,7 +182,7 @@ namespace ts.Completions.PathCompletions {
getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, span, result);
for (const moduleName of enumeratePotentialNonRelativeModules(fragment, scriptPath, compilerOptions, typeChecker, host)) {
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span));
result.push(createPathCompletion(moduleName, ScriptElementKind.externalModuleName, span));
}
return result;
@@ -179,22 +190,22 @@ namespace ts.Completions.PathCompletions {
function getCompletionsForPathMapping(
path: string, patterns: ReadonlyArray<string>, fragment: string, baseUrl: string, fileExtensions: ReadonlyArray<string>, host: LanguageServiceHost,
): ReadonlyArray<string> {
): ReadonlyArray<NameAndKind> {
if (!endsWith(path, "*")) {
// For a path mapping "foo": ["/x/y/z.ts"], add "foo" itself as a completion.
return !stringContains(path, "*") && startsWith(path, fragment) ? [path] : emptyArray;
return !stringContains(path, "*") && startsWith(path, fragment) ? [{ name: path, kind: ScriptElementKind.directory }] : emptyArray;
}
const pathPrefix = path.slice(0, path.length - 1);
if (!startsWith(fragment, pathPrefix)) {
return [pathPrefix];
return [{ name: pathPrefix, kind: ScriptElementKind.directory }];
}
const remainingFragment = fragment.slice(pathPrefix.length);
return flatMap(patterns, pattern => getModulesForPathsPattern(remainingFragment, baseUrl, pattern, fileExtensions, host));
}
function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: ReadonlyArray<string>, host: LanguageServiceHost): string[] | undefined {
function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: ReadonlyArray<string>, host: LanguageServiceHost): ReadonlyArray<NameAndKind> | undefined {
if (!host.readDirectory) {
return undefined;
}
@@ -225,14 +236,14 @@ namespace ts.Completions.PathCompletions {
// doesn't support. For now, this is safer but slower
const includeGlob = normalizedSuffix ? "**/*" : "./*";
const matches = tryReadDirectory(host, baseDirectory, fileExtensions, /*exclude*/ undefined, [includeGlob]);
const directories = tryGetDirectories(host, baseDirectory).map(d => combinePaths(baseDirectory, d));
const matches = tryReadDirectory(host, baseDirectory, fileExtensions, /*exclude*/ undefined, [includeGlob]).map<NameAndKind>(name => ({ name, kind: ScriptElementKind.scriptElement }));
const directories = tryGetDirectories(host, baseDirectory).map(d => combinePaths(baseDirectory, d)).map<NameAndKind>(name => ({ name, kind: ScriptElementKind.directory }));
// Trim away prefix and suffix
return mapDefined(concatenate(matches, directories), match => {
const normalizedMatch = normalizePath(match);
return mapDefined<NameAndKind, NameAndKind>(concatenate(matches, directories), ({ name, kind }) => {
const normalizedMatch = normalizePath(name);
const inner = withoutStartAndEnd(normalizedMatch, completePrefix, normalizedSuffix);
return inner !== undefined ? removeLeadingDirectorySeparator(removeFileExtension(inner)) : undefined;
return inner !== undefined ? { name: removeLeadingDirectorySeparator(removeFileExtension(inner)), kind } : undefined;
});
}
@@ -285,7 +296,7 @@ namespace ts.Completions.PathCompletions {
return deduplicate(nonRelativeModuleNames, equateStringsCaseSensitive, compareStringsCaseSensitive);
}
export function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): CompletionEntry[] | undefined {
export function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): PathCompletion[] | undefined {
const token = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false);
const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
const range = commentRanges && find(commentRanges, commentRange => position >= commentRange.pos && position <= commentRange.end);
@@ -316,7 +327,7 @@ namespace ts.Completions.PathCompletions {
}
}
function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, span: TextSpan, result: CompletionEntry[] = []): CompletionEntry[] {
function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, span: TextSpan, result: PathCompletion[] = []): PathCompletion[] {
// Check for typings specified in compiler options
const seen = createMap<true>();
if (options.types) {
@@ -364,7 +375,7 @@ namespace ts.Completions.PathCompletions {
function pushResult(moduleName: string) {
if (!seen.has(moduleName)) {
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span));
result.push(createPathCompletion(moduleName, ScriptElementKind.externalModuleName, span));
seen.set(moduleName, true);
}
}
@@ -433,10 +444,6 @@ namespace ts.Completions.PathCompletions {
}
}
function createCompletionEntryForModule(name: string, kind: ScriptElementKind, replacementSpan: TextSpan): CompletionEntry {
return { name, kind, kindModifiers: ScriptElementKindModifier.none, sortText: name, replacementSpan };
}
// Replace everything after the last directory seperator that appears
function getDirectoryFragmentTextSpan(text: string, textStart: number): TextSpan {
const index = text.lastIndexOf(directorySeparator);
@@ -483,8 +490,8 @@ namespace ts.Completions.PathCompletions {
return tryIOAndConsumeErrors(host, host.getDirectories, directoryName) || [];
}
function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>): string[] | undefined | undefined {
return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include);
function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>): ReadonlyArray<string> {
return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include) || emptyArray;
}
function tryReadFile(host: LanguageServiceHost, path: string): string | undefined {
@@ -108,4 +108,36 @@ tests/cases/conformance/types/conditional/inferTypes1.ts(72,43): error TS4081: E
!!! error TS2304: Cannot find name 'U'.
~
!!! error TS4081: Exported type alias 'T62' has or is using private name 'U'.
// Example from #21496
type JsonifiedObject<T extends object> = { [K in keyof T]: Jsonified<T[K]> };
type Jsonified<T> =
T extends string | number | boolean | null ? T
: T extends undefined | Function ? never // undefined and functions are removed
: T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date)
: T extends object ? JsonifiedObject<T>
: "what is this";
type Example = {
str: "literalstring",
fn: () => void,
date: Date,
customClass: MyClass,
obj: {
prop: "property",
clz: MyClass,
nested: { attr: Date }
},
}
declare class MyClass {
toJSON(): "correct";
}
type JsonifiedExample = Jsonified<Example>;
declare let ex: JsonifiedExample;
const z1: "correct" = ex.customClass;
const z2: string = ex.obj.nested.attr;
+34
View File
@@ -71,6 +71,38 @@ type T54 = X3<{ a: (x: number) => void, b: () => void }>; // number
type T60 = infer U; // Error
type T61<T> = infer A extends infer B ? infer C : infer D; // Error
type T62<T> = U extends (infer U)[] ? U : U; // Error
// Example from #21496
type JsonifiedObject<T extends object> = { [K in keyof T]: Jsonified<T[K]> };
type Jsonified<T> =
T extends string | number | boolean | null ? T
: T extends undefined | Function ? never // undefined and functions are removed
: T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date)
: T extends object ? JsonifiedObject<T>
: "what is this";
type Example = {
str: "literalstring",
fn: () => void,
date: Date,
customClass: MyClass,
obj: {
prop: "property",
clz: MyClass,
nested: { attr: Date }
},
}
declare class MyClass {
toJSON(): "correct";
}
type JsonifiedExample = Jsonified<Example>;
declare let ex: JsonifiedExample;
const z1: "correct" = ex.customClass;
const z2: string = ex.obj.nested.attr;
//// [inferTypes1.js]
@@ -85,3 +117,5 @@ var C = /** @class */ (function () {
}
return C;
}());
var z1 = ex.customClass;
var z2 = ex.obj.nested.attr;
@@ -315,3 +315,106 @@ type T62<T> = U extends (infer U)[] ? U : U; // Error
>U : Symbol(U, Decl(inferTypes1.ts, 71, 30))
>U : Symbol(U, Decl(inferTypes1.ts, 71, 30))
// Example from #21496
type JsonifiedObject<T extends object> = { [K in keyof T]: Jsonified<T[K]> };
>JsonifiedObject : Symbol(JsonifiedObject, Decl(inferTypes1.ts, 71, 44))
>T : Symbol(T, Decl(inferTypes1.ts, 75, 21))
>K : Symbol(K, Decl(inferTypes1.ts, 75, 44))
>T : Symbol(T, Decl(inferTypes1.ts, 75, 21))
>Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 75, 77))
>T : Symbol(T, Decl(inferTypes1.ts, 75, 21))
>K : Symbol(K, Decl(inferTypes1.ts, 75, 44))
type Jsonified<T> =
>Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 75, 77))
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
T extends string | number | boolean | null ? T
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
: T extends undefined | Function ? never // undefined and functions are removed
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
: T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date)
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
>toJSON : Symbol(toJSON, Decl(inferTypes1.ts, 80, 17))
>R : Symbol(R, Decl(inferTypes1.ts, 80, 33))
>R : Symbol(R, Decl(inferTypes1.ts, 80, 33))
: T extends object ? JsonifiedObject<T>
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
>JsonifiedObject : Symbol(JsonifiedObject, Decl(inferTypes1.ts, 71, 44))
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
: "what is this";
type Example = {
>Example : Symbol(Example, Decl(inferTypes1.ts, 82, 21))
str: "literalstring",
>str : Symbol(str, Decl(inferTypes1.ts, 84, 16))
fn: () => void,
>fn : Symbol(fn, Decl(inferTypes1.ts, 85, 25))
date: Date,
>date : Symbol(date, Decl(inferTypes1.ts, 86, 19))
>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
customClass: MyClass,
>customClass : Symbol(customClass, Decl(inferTypes1.ts, 87, 15))
>MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 94, 1))
obj: {
>obj : Symbol(obj, Decl(inferTypes1.ts, 88, 25))
prop: "property",
>prop : Symbol(prop, Decl(inferTypes1.ts, 89, 10))
clz: MyClass,
>clz : Symbol(clz, Decl(inferTypes1.ts, 90, 25))
>MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 94, 1))
nested: { attr: Date }
>nested : Symbol(nested, Decl(inferTypes1.ts, 91, 21))
>attr : Symbol(attr, Decl(inferTypes1.ts, 92, 17))
>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
},
}
declare class MyClass {
>MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 94, 1))
toJSON(): "correct";
>toJSON : Symbol(MyClass.toJSON, Decl(inferTypes1.ts, 96, 23))
}
type JsonifiedExample = Jsonified<Example>;
>JsonifiedExample : Symbol(JsonifiedExample, Decl(inferTypes1.ts, 98, 1))
>Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 75, 77))
>Example : Symbol(Example, Decl(inferTypes1.ts, 82, 21))
declare let ex: JsonifiedExample;
>ex : Symbol(ex, Decl(inferTypes1.ts, 101, 11))
>JsonifiedExample : Symbol(JsonifiedExample, Decl(inferTypes1.ts, 98, 1))
const z1: "correct" = ex.customClass;
>z1 : Symbol(z1, Decl(inferTypes1.ts, 102, 5))
>ex.customClass : Symbol(customClass, Decl(inferTypes1.ts, 87, 15))
>ex : Symbol(ex, Decl(inferTypes1.ts, 101, 11))
>customClass : Symbol(customClass, Decl(inferTypes1.ts, 87, 15))
const z2: string = ex.obj.nested.attr;
>z2 : Symbol(z2, Decl(inferTypes1.ts, 103, 5))
>ex.obj.nested.attr : Symbol(attr, Decl(inferTypes1.ts, 92, 17))
>ex.obj.nested : Symbol(nested, Decl(inferTypes1.ts, 91, 21))
>ex.obj : Symbol(obj, Decl(inferTypes1.ts, 88, 25))
>ex : Symbol(ex, Decl(inferTypes1.ts, 101, 11))
>obj : Symbol(obj, Decl(inferTypes1.ts, 88, 25))
>nested : Symbol(nested, Decl(inferTypes1.ts, 91, 21))
>attr : Symbol(attr, Decl(inferTypes1.ts, 92, 17))
+104
View File
@@ -321,3 +321,107 @@ type T62<T> = U extends (infer U)[] ? U : U; // Error
>U : U
>U : No type information available!
// Example from #21496
type JsonifiedObject<T extends object> = { [K in keyof T]: Jsonified<T[K]> };
>JsonifiedObject : JsonifiedObject<T>
>T : T
>K : K
>T : T
>Jsonified : Jsonified<T>
>T : T
>K : K
type Jsonified<T> =
>Jsonified : Jsonified<T>
>T : T
T extends string | number | boolean | null ? T
>T : T
>null : null
>T : T
: T extends undefined | Function ? never // undefined and functions are removed
>T : T
>Function : Function
: T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date)
>T : T
>toJSON : () => R
>R : R
>R : R
: T extends object ? JsonifiedObject<T>
>T : T
>JsonifiedObject : JsonifiedObject<T>
>T : T
: "what is this";
type Example = {
>Example : Example
str: "literalstring",
>str : "literalstring"
fn: () => void,
>fn : () => void
date: Date,
>date : Date
>Date : Date
customClass: MyClass,
>customClass : MyClass
>MyClass : MyClass
obj: {
>obj : { prop: "property"; clz: MyClass; nested: { attr: Date; }; }
prop: "property",
>prop : "property"
clz: MyClass,
>clz : MyClass
>MyClass : MyClass
nested: { attr: Date }
>nested : { attr: Date; }
>attr : Date
>Date : Date
},
}
declare class MyClass {
>MyClass : MyClass
toJSON(): "correct";
>toJSON : () => "correct"
}
type JsonifiedExample = Jsonified<Example>;
>JsonifiedExample : JsonifiedObject<Example>
>Jsonified : Jsonified<T>
>Example : Example
declare let ex: JsonifiedExample;
>ex : JsonifiedObject<Example>
>JsonifiedExample : JsonifiedObject<Example>
const z1: "correct" = ex.customClass;
>z1 : "correct"
>ex.customClass : "correct"
>ex : JsonifiedObject<Example>
>customClass : "correct"
const z2: string = ex.obj.nested.attr;
>z2 : string
>ex.obj.nested.attr : string
>ex.obj.nested : JsonifiedObject<{ attr: Date; }>
>ex.obj : JsonifiedObject<{ prop: "property"; clz: MyClass; nested: { attr: Date; }; }>
>ex : JsonifiedObject<Example>
>obj : JsonifiedObject<{ prop: "property"; clz: MyClass; nested: { attr: Date; }; }>
>nested : JsonifiedObject<{ attr: Date; }>
>attr : string
@@ -73,3 +73,35 @@ type T54 = X3<{ a: (x: number) => void, b: () => void }>; // number
type T60 = infer U; // Error
type T61<T> = infer A extends infer B ? infer C : infer D; // Error
type T62<T> = U extends (infer U)[] ? U : U; // Error
// Example from #21496
type JsonifiedObject<T extends object> = { [K in keyof T]: Jsonified<T[K]> };
type Jsonified<T> =
T extends string | number | boolean | null ? T
: T extends undefined | Function ? never // undefined and functions are removed
: T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date)
: T extends object ? JsonifiedObject<T>
: "what is this";
type Example = {
str: "literalstring",
fn: () => void,
date: Date,
customClass: MyClass,
obj: {
prop: "property",
clz: MyClass,
nested: { attr: Date }
},
}
declare class MyClass {
toJSON(): "correct";
}
type JsonifiedExample = Jsonified<Example>;
declare let ex: JsonifiedExample;
const z1: "correct" = ex.customClass;
const z2: string = ex.obj.nested.attr;
@@ -0,0 +1,30 @@
/// <reference path="fourslash.ts" />
// @Filename: /src/b.ts
////not read
// @Filename: /src/dir/x.ts
////not read
// @Filename: /src/a.ts
////import {} from "./[|/*0*/|]";
////import {} from "./[|/*1*/|]";
// @Filename: /tsconfig.json
////{
//// "compilerOptions": {
//// "baseUrl": ".",
//// "paths": {
//// "foo/*": ["src/*"]
//// }
//// }
////}
goTo.marker("0");
verify.completionListContains("dir", undefined, undefined, "directory");
verify.completionListContains("b", undefined, undefined, "script");
goTo.marker("1");
verify.completionListContains("dir", undefined, undefined, "directory");
verify.completionListContains("b", undefined, undefined, "script");