Improve isPossiblyTypeArgumentPosition (#25043)

* Improve isPossiblyTypeArgumentPosition

* Update API (#24966)

* Handle new Type<Type<

* Add test for `new C < C<`
This commit is contained in:
Andy 2018-07-11 11:15:04 -07:00 committed by GitHub
parent 97f10bc3a2
commit 52486ae362
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 59 additions and 9 deletions

View File

@ -4767,7 +4767,7 @@ namespace FourSlashInterface {
}
export interface VerifyCompletionsOptions {
readonly marker?: ArrayOrSingle<string>;
readonly marker?: ArrayOrSingle<string | FourSlash.Marker>;
readonly isNewIdentifierLocation?: boolean;
readonly exact?: ArrayOrSingle<ExpectedCompletionEntry>;
readonly includes?: ArrayOrSingle<ExpectedCompletionEntry>;

View File

@ -1060,7 +1060,7 @@ namespace ts.Completions {
const isImportType = isLiteralImportTypeNode(node);
const isTypeLocation = insideJsDocTagTypeExpression || (isImportType && !(node as ImportTypeNode).isTypeOf) || isPartOfTypeNode(node.parent);
const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration(node);
const allowTypeOrValue = isRhsOfImportDeclaration || (!isTypeLocation && isPossiblyTypeArgumentPosition(contextToken, sourceFile));
const allowTypeOrValue = isRhsOfImportDeclaration || (!isTypeLocation && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker));
if (isEntityName(node) || isImportType) {
let symbol = typeChecker.getSymbolAtLocation(node);
if (symbol) {
@ -1275,7 +1275,7 @@ namespace ts.Completions {
function filterGlobalCompletion(symbols: Symbol[]): void {
const isTypeOnlyCompletion = insideJsDocTagTypeExpression || !isContextTokenValueLocation(contextToken) && (isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken));
const allowTypes = isTypeOnlyCompletion || !isContextTokenValueLocation(contextToken) && isPossiblyTypeArgumentPosition(contextToken, sourceFile);
const allowTypes = isTypeOnlyCompletion || !isContextTokenValueLocation(contextToken) && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker);
if (isTypeOnlyCompletion) keywordFilters = KeywordCompletionFilters.TypeKeywords;
filterMutate(symbols, symbol => {

View File

@ -103,9 +103,7 @@ namespace ts.SignatureHelp {
if (onlyUseSyntacticOwners && !lessThanFollowsCalledExpression(startingToken, sourceFile, invocation.called)) {
return undefined;
}
const type = checker.getTypeAtLocation(invocation.called)!; // TODO: GH#18217
const signatures = isNewExpression(invocation.called.parent) ? type.getConstructSignatures() : type.getCallSignatures();
const candidates = signatures.filter(candidate => !!candidate.typeParameters && candidate.typeParameters.length >= argumentInfo.argumentCount);
const candidates = getPossibleGenericSignatures(invocation.called, argumentInfo.argumentCount, checker);
return candidates.length === 0 ? undefined : { candidates, resolvedSignature: first(candidates) };
}
else {
@ -261,7 +259,7 @@ namespace ts.SignatureHelp {
};
}
else {
const typeArgInfo = isPossiblyTypeArgumentPosition(node, sourceFile);
const typeArgInfo = getPossibleTypeArgumentsInfo(node, sourceFile);
if (typeArgInfo) {
const { called, nTypeArguments } = typeArgInfo;
const invocation: Invocation = { kind: InvocationKind.TypeArgs, called };

View File

@ -917,11 +917,25 @@ namespace ts {
}
}
export function isPossiblyTypeArgumentPosition(token: Node, sourceFile: SourceFile, checker: TypeChecker): boolean {
const info = getPossibleTypeArgumentsInfo(token, sourceFile);
return info !== undefined && (isPartOfTypeNode(info.called) ||
getPossibleGenericSignatures(info.called, info.nTypeArguments, checker).length !== 0 ||
isPossiblyTypeArgumentPosition(info.called, sourceFile, checker));
}
export function getPossibleGenericSignatures(called: Expression, typeArgumentCount: number, checker: TypeChecker): ReadonlyArray<Signature> {
const type = checker.getTypeAtLocation(called)!; // TODO: GH#18217
const signatures = isNewExpression(called.parent) ? type.getConstructSignatures() : type.getCallSignatures();
return signatures.filter(candidate => !!candidate.typeParameters && candidate.typeParameters.length >= typeArgumentCount);
}
export interface PossibleTypeArgumentInfo {
readonly called: Identifier;
readonly nTypeArguments: number;
}
export function isPossiblyTypeArgumentPosition(tokenIn: Node, sourceFile: SourceFile): PossibleTypeArgumentInfo | undefined {
// Get info for an expression like `f <` that may be the start of type arguments.
export function getPossibleTypeArgumentsInfo(tokenIn: Node, sourceFile: SourceFile): PossibleTypeArgumentInfo | undefined {
let token: Node | undefined = tokenIn;
// This function determines if the node could be type argument position
// Since during editing, when type argument list is not complete,

View File

@ -0,0 +1,38 @@
/// <reference path="fourslash.ts" />
////const x = 0;
////type T = number;
////function f(x: number) {}
////function g<T>(x: T) {}
////class C<T> {}
////x + {| "valueOnly": true |}
////x < {| "valueOnly": true |}
////f < {| "valueOnly": true |}
////g < {| "valueOnly": false |}
////const something: C<{| "valueOnly": false |};
////const something2: C<C<{| "valueOnly": false |};
////new C<{| "valueOnly": false |};
////new C<C<{| "valueOnly": false |};
////
////declare const callAndConstruct: { new<T>(): callAndConstruct<T>; <T>(): string; };
////interface callAndConstruct<T> {}
////new callAndConstruct<callAndConstruct</*callAndConstruct*/
for (const marker of test.markers()) {
if (marker.data && marker.data.valueOnly) {
verify.completions({ marker, includes: "x", excludes: "T" });
}
else {
verify.completions({ marker, includes: ["x", "T"] });
}
}
verify.signatureHelp({
marker: "callAndConstruct",
text: "callAndConstruct<T>(): string",
parameterName: "T",
parameterSpan: "T",
parameterCount: 1,
argumentCount: 1,
});

View File

@ -200,7 +200,7 @@ declare namespace FourSlashInterface {
assertHasRanges(ranges: Range[]): void;
caretAtMarker(markerName?: string): void;
completions(...options: {
readonly marker?: ArrayOrSingle<string>,
readonly marker?: ArrayOrSingle<string | Marker>,
readonly isNewIdentifierLocation?: boolean;
readonly exact?: ArrayOrSingle<ExpectedCompletionEntry>;
readonly includes?: ArrayOrSingle<ExpectedCompletionEntry>;