mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-30 01:04:49 -05:00
Support signature help for partially-filled-in type arguments f< (#24138)
* Support signature help for partially-filled-in type arguments `f<` * Use `isPossiblyTypeArgumentPosition` and support new expressions
This commit is contained in:
@@ -422,7 +422,7 @@ namespace ts.Completions {
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.NewExpression:
|
||||
if (!isRequireCall(node.parent, /*checkArgumentIsStringLiteralLike*/ false) && !isImportCall(node.parent)) {
|
||||
const argumentInfo = SignatureHelp.getImmediatelyContainingArgumentInfo(node, position, sourceFile);
|
||||
const argumentInfo = SignatureHelp.getArgumentInfoForCompletions(node, position, sourceFile);
|
||||
// Get string literal completions from specialized signatures of the target
|
||||
// i.e. declare function f(a: 'A');
|
||||
// f("/*completion position*/")
|
||||
@@ -452,7 +452,7 @@ namespace ts.Completions {
|
||||
}
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionsFromSignature(argumentInfo: SignatureHelp.ArgumentListInfo, checker: TypeChecker): StringLiteralCompletionsFromTypes {
|
||||
function getStringLiteralCompletionsFromSignature(argumentInfo: SignatureHelp.ArgumentInfoForCompletions, checker: TypeChecker): StringLiteralCompletionsFromTypes {
|
||||
let isNewIdentifier = false;
|
||||
|
||||
const uniques = createMap<true>();
|
||||
@@ -460,7 +460,7 @@ namespace ts.Completions {
|
||||
checker.getResolvedSignature(argumentInfo.invocation, candidates, argumentInfo.argumentCount);
|
||||
const types = flatMap(candidates, candidate => {
|
||||
if (!candidate.hasRestParameter && argumentInfo.argumentCount > candidate.parameters.length) return;
|
||||
const type = checker.getParameterType(candidate, argumentInfo.argumentIndex!); // TODO: GH#18217
|
||||
const type = checker.getParameterType(candidate, argumentInfo.argumentIndex);
|
||||
isNewIdentifier = isNewIdentifier || !!(type.flags & TypeFlags.String);
|
||||
return getStringLiteralTypes(type, checker, uniques);
|
||||
});
|
||||
@@ -720,10 +720,10 @@ namespace ts.Completions {
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
return isJsxExpression(parent) && parent.parent.kind !== SyntaxKind.JsxElement ? checker.getContextualTypeForJsxAttribute(parent.parent) : undefined;
|
||||
default:
|
||||
const argInfo = SignatureHelp.getImmediatelyContainingArgumentInfo(currentToken, position, sourceFile);
|
||||
const argInfo = SignatureHelp.getArgumentInfoForCompletions(currentToken, position, sourceFile);
|
||||
return argInfo
|
||||
// At `,`, treat this as the next argument after the comma.
|
||||
? checker.getContextualTypeForArgumentAtIndex(argInfo.invocation, argInfo.argumentIndex! + (currentToken.kind === SyntaxKind.CommaToken ? 1 : 0)) // TODO: GH#18217
|
||||
? checker.getContextualTypeForArgumentAtIndex(argInfo.invocation, argInfo.argumentIndex + (currentToken.kind === SyntaxKind.CommaToken ? 1 : 0))
|
||||
: isEqualityOperatorKind(currentToken.kind) && isBinaryExpression(parent) && isEqualityOperatorKind(parent.operatorToken.kind)
|
||||
// completion at `x ===/**/` should be for the right side
|
||||
? checker.getTypeAtLocation(parent.left)
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
/* @internal */
|
||||
namespace ts.SignatureHelp {
|
||||
export const enum ArgumentListKind {
|
||||
const enum ArgumentListKind {
|
||||
TypeArguments,
|
||||
CallArguments,
|
||||
TaggedTemplateArguments,
|
||||
JSXAttributesArguments
|
||||
}
|
||||
|
||||
export interface ArgumentListInfo {
|
||||
const enum InvocationKind { Call, TypeArgs }
|
||||
type Invocation = { kind: InvocationKind.Call, node: CallLikeExpression } | { kind: InvocationKind.TypeArgs, called: Expression };
|
||||
|
||||
interface ArgumentListInfo {
|
||||
kind: ArgumentListKind;
|
||||
invocation: CallLikeExpression;
|
||||
invocation: Invocation;
|
||||
argumentsSpan: TextSpan;
|
||||
argumentIndex?: number;
|
||||
argumentIndex: number;
|
||||
/** argumentCount is the *apparent* number of arguments. */
|
||||
argumentCount: number;
|
||||
}
|
||||
@@ -32,32 +35,39 @@ namespace ts.SignatureHelp {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
// Semantic filtering of signature help
|
||||
const call = argumentInfo.invocation;
|
||||
const candidates: Signature[] = [];
|
||||
const resolvedSignature = typeChecker.getResolvedSignature(call, candidates, argumentInfo.argumentCount);
|
||||
const candidateInfo = getCandidateInfo(argumentInfo, typeChecker);
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
if (!candidates.length) {
|
||||
if (!candidateInfo) {
|
||||
// We didn't have any sig help items produced by the TS compiler. If this is a JS
|
||||
// file, then see if we can figure out anything better.
|
||||
if (isSourceFileJavaScript(sourceFile)) {
|
||||
return createJavaScriptSignatureHelpItems(argumentInfo, program, cancellationToken);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return typeChecker.runWithCancellationToken(cancellationToken, typeChecker => createSignatureHelpItems(candidates, resolvedSignature!, argumentInfo, typeChecker));
|
||||
return typeChecker.runWithCancellationToken(cancellationToken, typeChecker => createSignatureHelpItems(candidateInfo.candidates, candidateInfo.resolvedSignature, argumentInfo, sourceFile, typeChecker));
|
||||
}
|
||||
|
||||
function getCandidateInfo(argumentInfo: ArgumentListInfo, checker: TypeChecker): { readonly candidates: ReadonlyArray<Signature>, readonly resolvedSignature: Signature } | undefined {
|
||||
const { invocation } = argumentInfo;
|
||||
if (invocation.kind === InvocationKind.Call) {
|
||||
const candidates: Signature[] = [];
|
||||
const resolvedSignature = checker.getResolvedSignature(invocation.node, candidates, argumentInfo.argumentCount)!; // TODO: GH#18217
|
||||
return candidates.length === 0 ? undefined : { candidates, resolvedSignature };
|
||||
}
|
||||
else {
|
||||
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);
|
||||
return candidates.length === 0 ? undefined : { candidates, resolvedSignature: first(candidates) };
|
||||
}
|
||||
}
|
||||
|
||||
function createJavaScriptSignatureHelpItems(argumentInfo: ArgumentListInfo, program: Program, cancellationToken: CancellationToken): SignatureHelpItems | undefined {
|
||||
if (argumentInfo.invocation.kind !== SyntaxKind.CallExpression) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// See if we can find some symbol with the call expression name that has call signatures.
|
||||
const callExpression = argumentInfo.invocation;
|
||||
const expression = callExpression.expression;
|
||||
const expression = getExpressionFromInvocation(argumentInfo.invocation);
|
||||
const name = isIdentifier(expression) ? expression : isPropertyAccessExpression(expression) ? expression.name : undefined;
|
||||
if (!name || !name.escapedText) {
|
||||
return undefined;
|
||||
@@ -76,7 +86,7 @@ namespace ts.SignatureHelp {
|
||||
if (type) {
|
||||
const callSignatures = type.getCallSignatures();
|
||||
if (callSignatures && callSignatures.length) {
|
||||
return typeChecker.runWithCancellationToken(cancellationToken, typeChecker => createSignatureHelpItems(callSignatures, callSignatures[0], argumentInfo, typeChecker));
|
||||
return typeChecker.runWithCancellationToken(cancellationToken, typeChecker => createSignatureHelpItems(callSignatures, callSignatures[0], argumentInfo, sourceFile, typeChecker));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,13 +95,25 @@ namespace ts.SignatureHelp {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ArgumentInfoForCompletions {
|
||||
readonly invocation: CallLikeExpression;
|
||||
readonly argumentIndex: number;
|
||||
readonly argumentCount: number;
|
||||
}
|
||||
export function getArgumentInfoForCompletions(node: Node, position: number, sourceFile: SourceFile): ArgumentInfoForCompletions | undefined {
|
||||
const info = getImmediatelyContainingArgumentInfo(node, position, sourceFile);
|
||||
return !info || info.kind === ArgumentListKind.TypeArguments || info.invocation.kind === InvocationKind.TypeArgs ? undefined
|
||||
: { invocation: info.invocation.node, argumentCount: info.argumentCount, argumentIndex: info.argumentIndex };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns relevant information for the argument list and the current argument if we are
|
||||
* in the argument of an invocation; returns undefined otherwise.
|
||||
*/
|
||||
export function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo | undefined {
|
||||
function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo | undefined {
|
||||
const { parent } = node;
|
||||
if (isCallOrNewExpression(parent)) {
|
||||
const invocation = parent;
|
||||
let list: Node | undefined;
|
||||
let argumentIndex: number;
|
||||
|
||||
@@ -134,56 +156,63 @@ namespace ts.SignatureHelp {
|
||||
Debug.assertLessThan(argumentIndex, argumentCount);
|
||||
}
|
||||
const argumentsSpan = getApplicableSpanForArguments(list, sourceFile);
|
||||
return { kind, invocation: parent, argumentsSpan, argumentIndex, argumentCount };
|
||||
return { kind, invocation: { kind: InvocationKind.Call, node: invocation }, argumentsSpan, argumentIndex, argumentCount };
|
||||
}
|
||||
else if (node.kind === SyntaxKind.NoSubstitutionTemplateLiteral && parent.kind === SyntaxKind.TaggedTemplateExpression) {
|
||||
else if (isNoSubstitutionTemplateLiteral(node) && isTaggedTemplateExpression(parent)) {
|
||||
// Check if we're actually inside the template;
|
||||
// otherwise we'll fall out and return undefined.
|
||||
if (isInsideTemplateLiteral(<LiteralExpression>node, position)) {
|
||||
return getArgumentListInfoForTemplate(<TaggedTemplateExpression>node.parent, /*argumentIndex*/ 0, sourceFile);
|
||||
if (isInsideTemplateLiteral(node, position)) {
|
||||
return getArgumentListInfoForTemplate(parent, /*argumentIndex*/ 0, sourceFile);
|
||||
}
|
||||
}
|
||||
else if (node.kind === SyntaxKind.TemplateHead && parent.parent.kind === SyntaxKind.TaggedTemplateExpression) {
|
||||
const templateExpression = <TemplateExpression>node.parent;
|
||||
else if (isTemplateHead(node) && parent.parent.kind === SyntaxKind.TaggedTemplateExpression) {
|
||||
const templateExpression = <TemplateExpression>parent;
|
||||
const tagExpression = <TaggedTemplateExpression>templateExpression.parent;
|
||||
Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression);
|
||||
|
||||
const argumentIndex = isInsideTemplateLiteral(<LiteralExpression>node, position) ? 0 : 1;
|
||||
const argumentIndex = isInsideTemplateLiteral(node, position) ? 0 : 1;
|
||||
|
||||
return getArgumentListInfoForTemplate(tagExpression, argumentIndex, sourceFile);
|
||||
}
|
||||
else if (parent.kind === SyntaxKind.TemplateSpan && parent.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) {
|
||||
const templateSpan = <TemplateSpan>node.parent;
|
||||
const templateExpression = templateSpan.parent;
|
||||
const tagExpression = <TaggedTemplateExpression>templateExpression.parent;
|
||||
Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression);
|
||||
else if (isTemplateSpan(parent) && isTaggedTemplateExpression(parent.parent.parent)) {
|
||||
const templateSpan = parent;
|
||||
const tagExpression = parent.parent.parent;
|
||||
|
||||
// If we're just after a template tail, don't show signature help.
|
||||
if (node.kind === SyntaxKind.TemplateTail && !isInsideTemplateLiteral(<LiteralExpression>node, position)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const spanIndex = templateExpression.templateSpans.indexOf(templateSpan);
|
||||
const spanIndex = templateSpan.parent.templateSpans.indexOf(templateSpan);
|
||||
const argumentIndex = getArgumentIndexForTemplatePiece(spanIndex, node, position);
|
||||
|
||||
return getArgumentListInfoForTemplate(tagExpression, argumentIndex, sourceFile);
|
||||
}
|
||||
else if (node.parent && isJsxOpeningLikeElement(node.parent)) {
|
||||
else if (isJsxOpeningLikeElement(parent)) {
|
||||
// Provide a signature help for JSX opening element or JSX self-closing element.
|
||||
// This is not guarantee that JSX tag-name is resolved into stateless function component. (that is done in "getSignatureHelpItems")
|
||||
// i.e
|
||||
// export function MainButton(props: ButtonProps, context: any): JSX.Element { ... }
|
||||
// <MainButton /*signatureHelp*/
|
||||
const attributeSpanStart = node.parent.attributes.getFullStart();
|
||||
const attributeSpanEnd = skipTrivia(sourceFile.text, node.parent.attributes.getEnd(), /*stopAfterLineBreak*/ false);
|
||||
const attributeSpanStart = parent.attributes.pos;
|
||||
const attributeSpanEnd = skipTrivia(sourceFile.text, parent.attributes.end, /*stopAfterLineBreak*/ false);
|
||||
return {
|
||||
kind: ArgumentListKind.JSXAttributesArguments,
|
||||
invocation: node.parent,
|
||||
invocation: { kind: InvocationKind.Call, node: parent },
|
||||
argumentsSpan: createTextSpan(attributeSpanStart, attributeSpanEnd - attributeSpanStart),
|
||||
argumentIndex: 0,
|
||||
argumentCount: 1
|
||||
};
|
||||
}
|
||||
else {
|
||||
const typeArgInfo = isPossiblyTypeArgumentPosition(node, sourceFile);
|
||||
if (typeArgInfo) {
|
||||
const { called, nTypeArguments } = typeArgInfo;
|
||||
const invocation: Invocation = { kind: InvocationKind.TypeArgs, called };
|
||||
const argumentsSpan = createTextSpanFromBounds(called.getStart(sourceFile), node.end);
|
||||
return { kind: ArgumentListKind.TypeArguments, invocation, argumentsSpan, argumentIndex: nTypeArguments, argumentCount: nTypeArguments + 1 };
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
@@ -269,7 +298,7 @@ namespace ts.SignatureHelp {
|
||||
}
|
||||
return {
|
||||
kind: ArgumentListKind.TaggedTemplateArguments,
|
||||
invocation: tagExpression,
|
||||
invocation: { kind: InvocationKind.Call, node: tagExpression },
|
||||
argumentsSpan: getApplicableSpanForTaggedTemplate(tagExpression, sourceFile),
|
||||
argumentIndex,
|
||||
argumentCount
|
||||
@@ -313,25 +342,15 @@ namespace ts.SignatureHelp {
|
||||
return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart);
|
||||
}
|
||||
|
||||
export function getContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo | undefined {
|
||||
for (let n = node; n.kind !== SyntaxKind.SourceFile; n = n.parent) {
|
||||
if (isFunctionBlock(n)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo | undefined {
|
||||
for (let n = node; !isBlock(n) && !isSourceFile(n); n = n.parent) {
|
||||
// If the node is not a subspan of its parent, this is a big problem.
|
||||
// There have been crashes that might be caused by this violation.
|
||||
if (n.pos < n.parent.pos || n.end > n.parent.end) {
|
||||
Debug.fail("Node of kind " + n.kind + " is not a subspan of its parent of kind " + n.parent.kind);
|
||||
}
|
||||
|
||||
Debug.assert(rangeContainsRange(n.parent, n), "Not a subspan", () => `Child: ${Debug.showSyntaxKind(n)}, parent: ${Debug.showSyntaxKind(n.parent)}`);
|
||||
const argumentInfo = getImmediatelyContainingArgumentInfo(n, position, sourceFile);
|
||||
if (argumentInfo) {
|
||||
return argumentInfo;
|
||||
}
|
||||
|
||||
|
||||
// TODO: Handle generic call with incomplete syntax
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -343,16 +362,20 @@ namespace ts.SignatureHelp {
|
||||
return children[indexOfOpenerToken + 1];
|
||||
}
|
||||
|
||||
function getExpressionFromInvocation(invocation: Invocation): Expression {
|
||||
return invocation.kind === InvocationKind.Call ? getInvokedExpression(invocation.node) : invocation.called;
|
||||
}
|
||||
|
||||
const signatureHelpNodeBuilderFlags = NodeBuilderFlags.OmitParameterModifiers | NodeBuilderFlags.IgnoreErrors;
|
||||
function createSignatureHelpItems(candidates: Signature[], resolvedSignature: Signature, argumentListInfo: ArgumentListInfo, typeChecker: TypeChecker): SignatureHelpItems {
|
||||
function createSignatureHelpItems(candidates: ReadonlyArray<Signature>, resolvedSignature: Signature, argumentListInfo: ArgumentListInfo, sourceFile: SourceFile, typeChecker: TypeChecker): SignatureHelpItems {
|
||||
const { argumentCount, argumentsSpan: applicableSpan, invocation, argumentIndex } = argumentListInfo;
|
||||
const isTypeParameterList = argumentListInfo.kind === ArgumentListKind.TypeArguments;
|
||||
|
||||
const callTarget = getInvokedExpression(invocation);
|
||||
const callTargetSymbol = typeChecker.getSymbolAtLocation(callTarget);
|
||||
const enclosingDeclaration = invocation.kind === InvocationKind.Call ? invocation.node : invocation.called;
|
||||
const callTargetSymbol = typeChecker.getSymbolAtLocation(getExpressionFromInvocation(invocation));
|
||||
const callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined);
|
||||
const printer = createPrinter({ removeComments: true });
|
||||
const items: SignatureHelpItem[] = map(candidates, candidateSignature => {
|
||||
const items = candidates.map<SignatureHelpItem>(candidateSignature => {
|
||||
let signatureHelpParameters: SignatureHelpParameter[];
|
||||
const prefixDisplayParts: SymbolDisplayPart[] = [];
|
||||
const suffixDisplayParts: SymbolDisplayPart[] = [];
|
||||
@@ -369,9 +392,9 @@ namespace ts.SignatureHelp {
|
||||
signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray;
|
||||
suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken));
|
||||
const parameterParts = mapToDisplayParts(writer => {
|
||||
const thisParameter = candidateSignature.thisParameter ? [typeChecker.symbolToParameterDeclaration(candidateSignature.thisParameter, invocation, signatureHelpNodeBuilderFlags)!] : [];
|
||||
const params = createNodeArray([...thisParameter, ...map(candidateSignature.parameters, param => typeChecker.symbolToParameterDeclaration(param, invocation, signatureHelpNodeBuilderFlags)!)]);
|
||||
printer.writeList(ListFormat.CallExpressionArguments, params, getSourceFileOfNode(getParseTreeNode(invocation)), writer);
|
||||
const thisParameter = candidateSignature.thisParameter ? [typeChecker.symbolToParameterDeclaration(candidateSignature.thisParameter, enclosingDeclaration, signatureHelpNodeBuilderFlags)!] : [];
|
||||
const params = createNodeArray([...thisParameter, ...candidateSignature.parameters.map(param => typeChecker.symbolToParameterDeclaration(param, enclosingDeclaration, signatureHelpNodeBuilderFlags)!)]);
|
||||
printer.writeList(ListFormat.CallExpressionArguments, params, sourceFile, writer);
|
||||
});
|
||||
addRange(suffixDisplayParts, parameterParts);
|
||||
}
|
||||
@@ -379,8 +402,8 @@ namespace ts.SignatureHelp {
|
||||
isVariadic = candidateSignature.hasRestParameter;
|
||||
const typeParameterParts = mapToDisplayParts(writer => {
|
||||
if (candidateSignature.typeParameters && candidateSignature.typeParameters.length) {
|
||||
const args = createNodeArray(map(candidateSignature.typeParameters, p => typeChecker.typeParameterToDeclaration(p, invocation)!));
|
||||
printer.writeList(ListFormat.TypeParameters, args, getSourceFileOfNode(getParseTreeNode(invocation)), writer);
|
||||
const args = createNodeArray(candidateSignature.typeParameters.map(p => typeChecker.typeParameterToDeclaration(p, enclosingDeclaration)!));
|
||||
printer.writeList(ListFormat.TypeParameters, args, sourceFile, writer);
|
||||
}
|
||||
});
|
||||
addRange(prefixDisplayParts, typeParameterParts);
|
||||
@@ -395,10 +418,10 @@ namespace ts.SignatureHelp {
|
||||
writer.writeSpace(" ");
|
||||
const predicate = typeChecker.getTypePredicateOfSignature(candidateSignature);
|
||||
if (predicate) {
|
||||
typeChecker.writeTypePredicate(predicate, invocation, /*flags*/ undefined, writer);
|
||||
typeChecker.writeTypePredicate(predicate, enclosingDeclaration, /*flags*/ undefined, writer);
|
||||
}
|
||||
else {
|
||||
typeChecker.writeType(typeChecker.getReturnTypeOfSignature(candidateSignature), invocation, /*flags*/ undefined, writer);
|
||||
typeChecker.writeType(typeChecker.getReturnTypeOfSignature(candidateSignature), enclosingDeclaration, /*flags*/ undefined, writer);
|
||||
}
|
||||
});
|
||||
addRange(suffixDisplayParts, returnTypeParts);
|
||||
@@ -415,18 +438,18 @@ namespace ts.SignatureHelp {
|
||||
});
|
||||
|
||||
if (argumentIndex !== 0) {
|
||||
Debug.assertLessThan(argumentIndex!, argumentCount); // TODO: GH#18217
|
||||
Debug.assertLessThan(argumentIndex, argumentCount);
|
||||
}
|
||||
|
||||
const selectedItemIndex = candidates.indexOf(resolvedSignature);
|
||||
Debug.assert(selectedItemIndex !== -1); // If candidates is non-empty it should always include bestSignature. We check for an empty candidates before calling this function.
|
||||
|
||||
return { items, applicableSpan, selectedItemIndex, argumentIndex: argumentIndex!, argumentCount }; // TODO: GH#18217
|
||||
return { items, applicableSpan, selectedItemIndex, argumentIndex, argumentCount };
|
||||
|
||||
function createSignatureHelpParameterForParameter(parameter: Symbol): SignatureHelpParameter {
|
||||
const displayParts = mapToDisplayParts(writer => {
|
||||
const param = typeChecker.symbolToParameterDeclaration(parameter, invocation, signatureHelpNodeBuilderFlags)!;
|
||||
printer.writeNode(EmitHint.Unspecified, param, getSourceFileOfNode(getParseTreeNode(invocation)), writer);
|
||||
const param = typeChecker.symbolToParameterDeclaration(parameter, enclosingDeclaration, signatureHelpNodeBuilderFlags)!;
|
||||
printer.writeNode(EmitHint.Unspecified, param, sourceFile, writer);
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -439,8 +462,8 @@ namespace ts.SignatureHelp {
|
||||
|
||||
function createSignatureHelpParameterForTypeParameter(typeParameter: TypeParameter): SignatureHelpParameter {
|
||||
const displayParts = mapToDisplayParts(writer => {
|
||||
const param = typeChecker.typeParameterToDeclaration(typeParameter, invocation)!;
|
||||
printer.writeNode(EmitHint.Unspecified, param, getSourceFileOfNode(getParseTreeNode(invocation)), writer);
|
||||
const param = typeChecker.typeParameterToDeclaration(typeParameter, enclosingDeclaration)!;
|
||||
printer.writeNode(EmitHint.Unspecified, param, sourceFile, writer);
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -920,7 +920,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function isPossiblyTypeArgumentPosition(tokenIn: Node, sourceFile: SourceFile): boolean {
|
||||
export interface PossibleTypeArgumentInfo {
|
||||
readonly called: Identifier;
|
||||
readonly nTypeArguments: number;
|
||||
}
|
||||
export function isPossiblyTypeArgumentPosition(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,
|
||||
@@ -928,15 +932,15 @@ namespace ts {
|
||||
// scanning of the previous identifier followed by "<" before current node would give us better result
|
||||
// Note that we also balance out the already provided type arguments, arrays, object literals while doing so
|
||||
let remainingLessThanTokens = 0;
|
||||
let nTypeArguments = 0;
|
||||
while (token) {
|
||||
switch (token.kind) {
|
||||
case SyntaxKind.LessThanToken:
|
||||
// Found the beginning of the generic argument expression
|
||||
token = findPrecedingToken(token.getFullStart(), sourceFile);
|
||||
if (!token) return false;
|
||||
const tokenIsIdentifier = isIdentifier(token);
|
||||
if (!remainingLessThanTokens || !tokenIsIdentifier) {
|
||||
return tokenIsIdentifier;
|
||||
if (!token || !isIdentifier(token)) return undefined;
|
||||
if (!remainingLessThanTokens) {
|
||||
return { called: token, nTypeArguments };
|
||||
}
|
||||
remainingLessThanTokens--;
|
||||
break;
|
||||
@@ -957,25 +961,28 @@ namespace ts {
|
||||
// This can be object type, skip until we find the matching open brace token
|
||||
// Skip until the matching open brace token
|
||||
token = findPrecedingMatchingToken(token, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
if (!token) return false;
|
||||
if (!token) return undefined;
|
||||
break;
|
||||
|
||||
case SyntaxKind.CloseParenToken:
|
||||
// This can be object type, skip until we find the matching open brace token
|
||||
// Skip until the matching open brace token
|
||||
token = findPrecedingMatchingToken(token, SyntaxKind.OpenParenToken, sourceFile);
|
||||
if (!token) return false;
|
||||
if (!token) return undefined;
|
||||
break;
|
||||
|
||||
case SyntaxKind.CloseBracketToken:
|
||||
// This can be object type, skip until we find the matching open brace token
|
||||
// Skip until the matching open brace token
|
||||
token = findPrecedingMatchingToken(token, SyntaxKind.OpenBracketToken, sourceFile);
|
||||
if (!token) return false;
|
||||
if (!token) return undefined;
|
||||
break;
|
||||
|
||||
// Valid tokens in a type name. Skip.
|
||||
case SyntaxKind.CommaToken:
|
||||
nTypeArguments++;
|
||||
break;
|
||||
|
||||
case SyntaxKind.EqualsGreaterThanToken:
|
||||
|
||||
case SyntaxKind.Identifier:
|
||||
@@ -999,13 +1006,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Invalid token in type
|
||||
return false;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
token = findPrecedingToken(token.getFullStart(), sourceFile);
|
||||
}
|
||||
|
||||
return false;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1086,7 +1093,7 @@ namespace ts {
|
||||
return SyntaxKind.FirstPunctuation <= kind && kind <= SyntaxKind.LastPunctuation;
|
||||
}
|
||||
|
||||
export function isInsideTemplateLiteral(node: LiteralExpression, position: number) {
|
||||
export function isInsideTemplateLiteral(node: LiteralExpression | TemplateHead, position: number) {
|
||||
return isTemplateLiteralKind(node.kind)
|
||||
&& (node.getStart() < position && position < node.getEnd()) || (!!node.isUnterminated && position === node.getEnd());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user