2014-09-22 23:49:33 +02:00
///<reference path='services.ts' />
2014-07-19 01:55:11 +02:00
2014-09-22 20:38:01 +02:00
module ts.SignatureHelp {
// A partially written generic type expression is not guaranteed to have the correct syntax tree. the expression could be parsed as less than/greater than expression or a comma expression
// or some other combination depending on what the user has typed so far. For the purposes of signature help we need to consider any location after "<" as a possible generic type reference.
// To do this, the method will back parse the expression starting at the position required. it will try to parse the current expression as a generic type expression, if it did succeed it
// will return the generic identifier that started the expression (e.g. "foo" in "foo<any, |"). It is then up to the caller to ensure that this is a valid generic expression through
// looking up the type. The method will also keep track of the parameter index inside the expression.
//public static isInPartiallyWrittenTypeArgumentList(syntaxTree: TypeScript.SyntaxTree, position: number): any {
// var token = Syntax.findTokenOnLeft(syntaxTree.sourceUnit(), position, /*includeSkippedTokens*/ true);
// if (token && TypeScript.Syntax.hasAncestorOfKind(token, TypeScript.SyntaxKind.TypeParameterList)) {
// // We are in the wrong generic list. bail out
// return null;
// }
// var stack = 0;
// var argumentIndex = 0;
// whileLoop:
// while (token) {
// switch (token.kind()) {
// case TypeScript.SyntaxKind.LessThanToken:
// if (stack === 0) {
// // Found the beginning of the generic argument expression
// var lessThanToken = token;
// token = previousToken(token, /*includeSkippedTokens*/ true);
// if (!token || token.kind() !== TypeScript.SyntaxKind.IdentifierName) {
// break whileLoop;
// }
// // Found the name, return the data
// return {
// genericIdentifer: token,
// lessThanToken: lessThanToken,
// argumentIndex: argumentIndex
// };
// }
// else if (stack < 0) {
// // Seen one too many less than tokens, bail out
// break whileLoop;
// }
// else {
// stack--;
// }
// break;
// case TypeScript.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
// stack++;
// // Intentaion fall through
// case TypeScript.SyntaxKind.GreaterThanToken:
// stack++;
// break;
// case TypeScript.SyntaxKind.CommaToken:
// if (stack == 0) {
// argumentIndex++;
// }
// break;
// case TypeScript.SyntaxKind.CloseBraceToken:
// // This can be object type, skip untill we find the matching open brace token
// var unmatchedOpenBraceTokens = 0;
// // Skip untill the matching open brace token
// token = SignatureInfoHelpers.moveBackUpTillMatchingTokenKind(token, TypeScript.SyntaxKind.CloseBraceToken, TypeScript.SyntaxKind.OpenBraceToken);
// if (!token) {
// // No matching token was found. bail out
// break whileLoop;
// }
// break;
// case TypeScript.SyntaxKind.EqualsGreaterThanToken:
// // This can be a function type or a constructor type. In either case, we want to skip the function defintion
// token = previousToken(token, /*includeSkippedTokens*/ true);
// if (token && token.kind() === TypeScript.SyntaxKind.CloseParenToken) {
// // Skip untill the matching open paren token
// token = SignatureInfoHelpers.moveBackUpTillMatchingTokenKind(token, TypeScript.SyntaxKind.CloseParenToken, TypeScript.SyntaxKind.OpenParenToken);
// if (token && token.kind() === TypeScript.SyntaxKind.GreaterThanToken) {
// // Another generic type argument list, skip it\
// token = SignatureInfoHelpers.moveBackUpTillMatchingTokenKind(token, TypeScript.SyntaxKind.GreaterThanToken, TypeScript.SyntaxKind.LessThanToken);
// }
// if (token && token.kind() === TypeScript.SyntaxKind.NewKeyword) {
// // In case this was a constructor type, skip the new keyword
// token = previousToken(token, /*includeSkippedTokens*/ true);
// }
// if (!token) {
// // No matching token was found. bail out
// break whileLoop;
// }
// }
// else {
// // This is not a funtion type. exit the main loop
// break whileLoop;
// }
// break;
// case TypeScript.SyntaxKind.IdentifierName:
// case TypeScript.SyntaxKind.AnyKeyword:
// case TypeScript.SyntaxKind.NumberKeyword:
// case TypeScript.SyntaxKind.StringKeyword:
// case TypeScript.SyntaxKind.VoidKeyword:
// case TypeScript.SyntaxKind.BooleanKeyword:
// case TypeScript.SyntaxKind.DotToken:
// case TypeScript.SyntaxKind.OpenBracketToken:
// case TypeScript.SyntaxKind.CloseBracketToken:
// // Valid tokens in a type name. Skip.
// break;
// default:
// break whileLoop;
// }
// token = previousToken(token, /*includeSkippedTokens*/ true);
// }
// return null;
2014-09-04 19:24:11 +02:00
//}
2014-07-19 01:55:11 +02:00
2014-09-22 20:38:01 +02:00
//private static moveBackUpTillMatchingTokenKind(token: TypeScript.ISyntaxToken, tokenKind: TypeScript.SyntaxKind, matchingTokenKind: TypeScript.SyntaxKind): TypeScript.ISyntaxToken {
// if (!token || token.kind() !== tokenKind) {
// throw TypeScript.Errors.invalidOperation();
// }
// // Skip the current token
// token = previousToken(token, /*includeSkippedTokens*/ true);
// var stack = 0;
// while (token) {
// if (token.kind() === matchingTokenKind) {
// if (stack === 0) {
// // Found the matching token, return
// return token;
// }
// else if (stack < 0) {
// // tokens overlapped.. bail out.
// break;
// }
// else {
// stack--;
// }
// }
// else if (token.kind() === tokenKind) {
// stack++;
// }
// // Move back
// token = previousToken(token, /*includeSkippedTokens*/ true);
// }
// // Did not find matching token
// return null;
2014-09-04 19:24:11 +02:00
//}
2014-09-22 20:38:01 +02:00
var emptyArray : any [ ] = [ ] ;
2014-11-18 22:40:42 +01:00
const enum ArgumentListKind {
2014-11-11 22:58:50 +01:00
TypeArguments ,
CallArguments ,
TaggedTemplateArguments
}
2014-11-18 22:40:42 +01:00
interface ArgumentListInfo {
2014-11-11 22:58:50 +01:00
kind : ArgumentListKind ;
invocation : CallLikeExpression ;
2014-11-18 03:41:50 +01:00
argumentsSpan : TextSpan ;
2014-11-13 01:30:25 +01:00
argumentIndex? : number ;
2014-11-11 22:58:50 +01:00
argumentCount : number ;
}
2014-09-24 01:18:47 +02:00
export function getSignatureHelpItems ( sourceFile : SourceFile , position : number , typeInfoResolver : TypeChecker , cancellationToken : CancellationTokenObject ) : SignatureHelpItems {
2014-09-22 20:38:01 +02:00
// Decide whether to show signature help
2014-09-24 03:38:13 +02:00
var startingToken = findTokenOnLeftOfPosition ( sourceFile , position ) ;
if ( ! startingToken ) {
// We are at the beginning of the file
return undefined ;
}
2014-11-25 01:24:15 +01:00
var argumentInfo = getContainingArgumentInfo ( startingToken ) ;
2014-09-22 22:49:48 +02:00
cancellationToken . throwIfCancellationRequested ( ) ;
2014-09-22 20:38:01 +02:00
// Semantic filtering of signature help
2014-09-30 22:46:07 +02:00
if ( ! argumentInfo ) {
2014-09-24 00:34:42 +02:00
return undefined ;
2014-09-22 20:38:01 +02:00
}
2014-11-13 01:30:25 +01:00
var call = argumentInfo . invocation ;
2014-09-24 00:34:42 +02:00
var candidates = < Signature [ ] > [ ] ;
var resolvedSignature = typeInfoResolver . getResolvedSignature ( call , candidates ) ;
cancellationToken . throwIfCancellationRequested ( ) ;
if ( ! candidates . length ) {
return undefined ;
}
2014-09-22 20:38:01 +02:00
2014-09-30 22:46:07 +02:00
return createSignatureHelpItems ( candidates , resolvedSignature , argumentInfo ) ;
2014-09-22 20:38:01 +02:00
2014-09-24 04:00:45 +02:00
/ * *
2014-11-21 02:00:01 +01:00
* Returns relevant information for the argument list and the current argument if we are
* in the argument of an invocation ; returns undefined otherwise .
2014-09-24 04:00:45 +02:00
* /
2014-11-25 01:24:15 +01:00
function getImmediatelyContainingArgumentInfo ( node : Node ) : ArgumentListInfo {
2014-11-11 22:58:50 +01:00
if ( node . parent . kind === SyntaxKind . CallExpression || node . parent . kind === SyntaxKind . NewExpression ) {
var callExpression = < CallExpression > node . parent ;
// There are 3 cases to handle:
// 1. The token introduces a list, and should begin a sig help session
// 2. The token is either not associated with a list, or ends a list, so the session should end
// 3. The token is buried inside a list, and should give sig help
//
// The following are examples of each:
//
// Case 1:
// foo<#T, U>(#a, b) -> The token introduces a list, and should begin a sig help session
// Case 2:
// fo#o<T, U>#(a, b)# -> The token is either not associated with a list, or ends a list, so the session should end
// Case 3:
// foo<T#, U#>(a#, #b#) -> The token is buried inside a list, and should give sig help
// Find out if 'node' is an argument, a type argument, or neither
if ( node . kind === SyntaxKind . LessThanToken ||
node . kind === SyntaxKind . OpenParenToken ) {
// Find the list that starts right *after* the < or ( token.
// If the user has just opened a list, consider this item 0.
var list = getChildListThatStartsWithOpenerToken ( callExpression , node , sourceFile ) ;
var isTypeArgList = callExpression . typeArguments && callExpression . typeArguments . pos === list . pos ;
Debug . assert ( list !== undefined ) ;
return {
kind : isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments ,
invocation : callExpression ,
2014-11-18 00:13:58 +01:00
argumentsSpan : getApplicableSpanForArguments ( list ) ,
2014-11-11 22:58:50 +01:00
argumentIndex : 0 ,
2015-03-11 23:05:31 +01:00
argumentCount : getArgumentCount ( list )
2014-11-11 22:58:50 +01:00
} ;
}
// findListItemInfo can return undefined if we are not in parent's argument list
// or type argument list. This includes cases where the cursor is:
// - To the right of the closing paren, non-substitution template, or template tail.
// - Between the type arguments and the arguments (greater than token)
// - On the target of the call (parent.func)
// - On the 'new' keyword in a 'new' expression
var listItemInfo = findListItemInfo ( node ) ;
if ( listItemInfo ) {
var list = listItemInfo . list ;
var isTypeArgList = callExpression . typeArguments && callExpression . typeArguments . pos === list . pos ;
2014-11-13 01:30:25 +01:00
2015-03-11 23:05:31 +01:00
var argumentIndex = getArgumentIndex ( list , node ) ;
var argumentCount = getArgumentCount ( list ) ;
2014-11-13 01:30:25 +01:00
2015-03-11 23:05:31 +01:00
Debug . assert ( argumentIndex === 0 || argumentIndex < argumentCount ,
` argumentCount < argumentIndex, ${ argumentCount } < ${ argumentIndex } ` ) ;
2015-03-06 23:29:23 +01:00
2014-11-11 22:58:50 +01:00
return {
kind : isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments ,
invocation : callExpression ,
2014-11-18 00:13:58 +01:00
argumentsSpan : getApplicableSpanForArguments ( list ) ,
2014-11-13 01:30:25 +01:00
argumentIndex : argumentIndex ,
2015-03-06 23:29:23 +01:00
argumentCount : argumentCount
2014-11-11 22:58:50 +01:00
} ;
}
2014-09-22 20:38:01 +02:00
}
2014-11-15 02:30:19 +01:00
else if ( node . kind === SyntaxKind . NoSubstitutionTemplateLiteral && node . parent . kind === SyntaxKind . TaggedTemplateExpression ) {
2014-11-18 00:13:58 +01:00
// 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 ) ;
}
2014-11-15 02:30:19 +01:00
}
2014-11-18 00:13:58 +01:00
else if ( node . kind === SyntaxKind . TemplateHead && node . parent . parent . kind === SyntaxKind . TaggedTemplateExpression ) {
2014-11-11 22:58:50 +01:00
var templateExpression = < TemplateExpression > node . parent ;
var tagExpression = < TaggedTemplateExpression > templateExpression . parent ;
2014-11-25 01:24:15 +01:00
Debug . assert ( templateExpression . kind === SyntaxKind . TemplateExpression ) ;
var argumentIndex = isInsideTemplateLiteral ( < LiteralExpression > node , position ) ? 0 : 1 ;
2014-11-11 22:58:50 +01:00
2014-11-18 00:13:58 +01:00
return getArgumentListInfoForTemplate ( tagExpression , argumentIndex ) ;
2014-09-22 20:38:01 +02:00
}
2014-11-11 22:58:50 +01:00
else if ( node . parent . kind === SyntaxKind . TemplateSpan && node . parent . parent . parent . kind === SyntaxKind . TaggedTemplateExpression ) {
var templateSpan = < TemplateSpan > node . parent ;
var templateExpression = < TemplateExpression > templateSpan . parent ;
var tagExpression = < TaggedTemplateExpression > templateExpression . parent ;
Debug . assert ( templateExpression . kind === SyntaxKind . TemplateExpression ) ;
2015-03-02 23:46:15 +01:00
// If we're just after a template tail, don't show signature help.
if ( node . kind === SyntaxKind . TemplateTail && ! isInsideTemplateLiteral ( < LiteralExpression > node , position ) ) {
2014-11-18 00:13:58 +01:00
return undefined ;
}
2014-11-11 22:58:50 +01:00
var spanIndex = templateExpression . templateSpans . indexOf ( templateSpan ) ;
2014-11-25 01:24:15 +01:00
var argumentIndex = getArgumentIndexForTemplatePiece ( spanIndex , node ) ;
2014-11-11 22:58:50 +01:00
2014-11-18 00:13:58 +01:00
return getArgumentListInfoForTemplate ( tagExpression , argumentIndex ) ;
2014-11-11 22:58:50 +01:00
}
return undefined ;
}
2015-03-11 23:05:31 +01:00
function getArgumentIndex ( argumentsList : Node , node : Node ) {
// The list we got back can include commas. In the presence of errors it may
// also just have nodes without commas. For example "Foo(a b c)" will have 3
// args without commas. We want to find what index we're at. So we count
// forward until we hit ourselves, only incrementing the index if it isn't a
// comma.
2015-03-11 23:30:33 +01:00
//
// Note: the subtlety around trailing commas (in getArgumentCount) does not apply
// here. That's because we're only walking forward until we hit the node we're
// on. In that case, even if we're after the trailing comma, we'll still see
// that trailing comma in the list, and we'll have generated the appropriate
// arg index.
2015-03-11 23:05:31 +01:00
var argumentIndex = 0 ;
var listChildren = argumentsList . getChildren ( ) ;
for ( var i = 0 , n = listChildren . length ; i < n ; i ++ ) {
var child = listChildren [ i ] ;
if ( child === node ) {
break ;
}
if ( child . kind !== SyntaxKind . CommaToken ) {
argumentIndex ++ ;
}
}
return argumentIndex ;
}
function getArgumentCount ( argumentsList : Node ) {
// The argument count for a list is normally the number of non-comma children it has.
// For example, if you have "Foo(a,b)" then there will be three children of the arg
// list 'a' '<comma>' 'b'. So, in this case the arg count will be 2. However, there
// is a small subtlety. If you have "Foo(a,)", then the child list will just have
// 'a' '<comma>'. So, in the case where the last child is a comma, we increase the
// arg count by one to compensate.
2015-03-11 23:30:33 +01:00
//
// Note: this subtlety only applies to the last comma. If you had "Foo(a,," then
// we'll have: 'a' '<comma>' '<missing>'
// That will give us 2 non-commas. We then add one for the last comma, givin us an
// arg count of 3.
2015-03-11 23:05:31 +01:00
var listChildren = argumentsList . getChildren ( ) ;
var argumentCount = countWhere ( listChildren , arg = > arg . kind !== SyntaxKind . CommaToken ) ;
if ( listChildren . length > 0 && lastOrUndefined ( listChildren ) . kind === SyntaxKind . CommaToken ) {
argumentCount ++ ;
}
return argumentCount ;
2014-11-18 00:13:58 +01:00
}
2014-11-25 01:24:15 +01:00
// spanIndex is either the index for a given template span.
2014-11-18 00:13:58 +01:00
// This does not give appropriate results for a NoSubstitutionTemplateLiteral
2014-11-25 01:24:15 +01:00
function getArgumentIndexForTemplatePiece ( spanIndex : number , node : Node ) : number {
2014-11-21 02:00:01 +01:00
// Because the TemplateStringsArray is the first argument, we have to offset each substitution expression by 1.
2014-11-18 00:13:58 +01:00
// There are three cases we can encounter:
2014-11-21 02:00:01 +01:00
// 1. We are precisely in the template literal (argIndex = 0).
// 2. We are in or to the right of the substitution expression (argIndex = spanIndex + 1).
// 3. We are directly to the right of the template literal, but because we look for the token on the left,
// not enough to put us in the substitution expression; we should consider ourselves part of
// the *next* span's expression by offsetting the index (argIndex = (spanIndex + 1) + 1).
2014-11-18 00:13:58 +01:00
//
// Example: f `# abcd $#{# 1 + 1# }# efghi ${ #"#hello"# } # `
// ^ ^ ^ ^ ^ ^ ^ ^ ^
2014-11-18 22:40:42 +01:00
// Case: 1 1 3 2 1 3 2 2 1
2014-11-18 00:13:58 +01:00
Debug . assert ( position >= node . getStart ( ) , "Assumed 'position' could not occur before node." ) ;
if ( isTemplateLiteralKind ( node . kind ) ) {
if ( isInsideTemplateLiteral ( < LiteralExpression > node , position ) ) {
return 0 ;
}
return spanIndex + 2 ;
}
return spanIndex + 1 ;
}
2014-11-15 02:13:00 +01:00
function getArgumentListInfoForTemplate ( tagExpression : TaggedTemplateExpression , argumentIndex : number ) : ArgumentListInfo {
2014-11-21 02:00:01 +01:00
// argumentCount is either 1 or (numSpans + 1) to account for the template strings array argument.
2014-11-11 22:58:50 +01:00
var argumentCount = tagExpression . template . kind === SyntaxKind . NoSubstitutionTemplateLiteral
? 1
: ( < TemplateExpression > tagExpression . template ) . templateSpans . length + 1 ;
2015-03-07 00:09:19 +01:00
Debug . assert ( argumentIndex === 0 || argumentIndex < argumentCount , ` argumentCount < argumentIndex, ${ argumentCount } < ${ argumentIndex } ` ) ;
2015-03-06 23:29:23 +01:00
2014-11-11 22:58:50 +01:00
return {
kind : ArgumentListKind.TaggedTemplateArguments ,
invocation : tagExpression ,
2014-11-25 01:24:15 +01:00
argumentsSpan : getApplicableSpanForTaggedTemplate ( tagExpression ) ,
2014-11-11 22:58:50 +01:00
argumentIndex : argumentIndex ,
argumentCount : argumentCount
} ;
}
2014-09-22 20:38:01 +02:00
2014-11-18 03:41:50 +01:00
function getApplicableSpanForArguments ( argumentsList : Node ) : TextSpan {
2014-11-18 00:13:58 +01:00
// We use full start and skip trivia on the end because we want to include trivia on
// both sides. For example,
//
// foo( /*comment */ a, b, c /*comment*/ )
// | |
//
// The applicable span is from the first bar to the second bar (inclusive,
// but not including parentheses)
2014-11-18 03:41:50 +01:00
var applicableSpanStart = argumentsList . getFullStart ( ) ;
var applicableSpanEnd = skipTrivia ( sourceFile . text , argumentsList . getEnd ( ) , /*stopAfterLineBreak*/ false ) ;
2014-12-10 22:45:08 +01:00
return createTextSpan ( applicableSpanStart , applicableSpanEnd - applicableSpanStart ) ;
2014-11-18 00:13:58 +01:00
}
2014-11-25 01:24:15 +01:00
function getApplicableSpanForTaggedTemplate ( taggedTemplate : TaggedTemplateExpression ) : TextSpan {
var template = taggedTemplate . template ;
2014-11-18 00:13:58 +01:00
var applicableSpanStart = template . getStart ( ) ;
var applicableSpanEnd = template . getEnd ( ) ;
2014-11-25 01:24:15 +01:00
// We need to adjust the end position for the case where the template does not have a tail.
// Otherwise, we will not show signature help past the expression.
// For example,
//
// ` ${ 1 + 1 foo(10)
// | |
//
// This is because a Missing node has no width. However, what we actually want is to include trivia
// leading up to the next token in case the user is about to type in a TemplateMiddle or TemplateTail.
2014-11-18 00:13:58 +01:00
if ( template . kind === SyntaxKind . TemplateExpression ) {
var lastSpan = lastOrUndefined ( ( < TemplateExpression > template ) . templateSpans ) ;
2014-12-02 01:17:04 +01:00
if ( lastSpan . literal . getFullWidth ( ) === 0 ) {
2014-11-25 01:24:15 +01:00
applicableSpanEnd = skipTrivia ( sourceFile . text , applicableSpanEnd , /*stopAfterLineBreak*/ false ) ;
2014-11-18 00:13:58 +01:00
}
}
2014-12-10 22:45:08 +01:00
return createTextSpan ( applicableSpanStart , applicableSpanEnd - applicableSpanStart ) ;
2014-09-22 20:38:01 +02:00
}
2014-11-25 01:24:15 +01:00
function getContainingArgumentInfo ( node : Node ) : ArgumentListInfo {
2014-09-22 20:38:01 +02:00
for ( var n = node ; n . kind !== SyntaxKind . SourceFile ; n = n . parent ) {
2014-12-03 07:52:21 +01:00
if ( isFunctionBlock ( n ) ) {
2014-09-22 20:38:01 +02:00
return undefined ;
}
2014-10-07 22:37:57 +02:00
// 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 ) {
2014-11-04 08:31:38 +01:00
Debug . fail ( "Node of kind " + n . kind + " is not a subspan of its parent of kind " + n . parent . kind ) ;
2014-10-07 22:37:57 +02:00
}
2014-11-25 01:24:15 +01:00
var argumentInfo = getImmediatelyContainingArgumentInfo ( n ) ;
2014-09-30 22:46:07 +02:00
if ( argumentInfo ) {
return argumentInfo ;
2014-09-22 20:38:01 +02:00
}
// TODO: Handle generic call with incomplete syntax
}
return undefined ;
}
2014-10-02 02:09:50 +02:00
function getChildListThatStartsWithOpenerToken ( parent : Node , openerToken : Node , sourceFile : SourceFile ) : Node {
var children = parent . getChildren ( sourceFile ) ;
var indexOfOpenerToken = children . indexOf ( openerToken ) ;
2014-10-20 20:51:45 +02:00
Debug . assert ( indexOfOpenerToken >= 0 && children . length > indexOfOpenerToken + 1 ) ;
2014-10-02 02:09:50 +02:00
return children [ indexOfOpenerToken + 1 ] ;
}
2014-10-01 02:13:27 +02:00
/ * *
* The selectedItemIndex could be negative for several reasons .
* 1 . There are too many arguments for all of the overloads
* 2 . None of the overloads were type compatible
* The solution here is to try to pick the best overload by picking
* either the first one that has an appropriate number of parameters ,
* or the one with the most parameters .
* /
function selectBestInvalidOverloadIndex ( candidates : Signature [ ] , argumentCount : number ) : number {
var maxParamsSignatureIndex = - 1 ;
var maxParams = - 1 ;
for ( var i = 0 ; i < candidates . length ; i ++ ) {
var candidate = candidates [ i ] ;
if ( candidate . hasRestParameter || candidate . parameters . length >= argumentCount ) {
return i ;
}
if ( candidate . parameters . length > maxParams ) {
maxParams = candidate . parameters . length ;
maxParamsSignatureIndex = i ;
}
}
return maxParamsSignatureIndex ;
}
2014-11-11 22:58:50 +01:00
function createSignatureHelpItems ( candidates : Signature [ ] , bestSignature : Signature , argumentListInfo : ArgumentListInfo ) : SignatureHelpItems {
2014-11-18 00:13:58 +01:00
var applicableSpan = argumentListInfo . argumentsSpan ;
2014-11-11 22:58:50 +01:00
var isTypeParameterList = argumentListInfo . kind === ArgumentListKind . TypeArguments ;
2014-09-25 08:44:13 +02:00
2014-11-11 22:58:50 +01:00
var invocation = argumentListInfo . invocation ;
2014-11-25 01:24:15 +01:00
var callTarget = getInvokedExpression ( invocation )
2014-12-04 21:55:54 +01:00
var callTargetSymbol = typeInfoResolver . getSymbolAtLocation ( callTarget ) ;
2014-11-25 01:24:15 +01:00
var callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts ( typeInfoResolver , callTargetSymbol , /*enclosingDeclaration*/ undefined , /*meaning*/ undefined ) ;
2014-10-16 19:35:34 +02:00
var items : SignatureHelpItem [ ] = map ( candidates , candidateSignature = > {
var signatureHelpParameters : SignatureHelpParameter [ ] ;
2014-11-18 23:08:42 +01:00
var prefixDisplayParts : SymbolDisplayPart [ ] = [ ] ;
var suffixDisplayParts : SymbolDisplayPart [ ] = [ ] ;
2014-09-25 08:44:13 +02:00
2014-11-25 01:24:15 +01:00
if ( callTargetDisplayParts ) {
2014-11-18 23:08:42 +01:00
prefixDisplayParts . push . apply ( prefixDisplayParts , callTargetDisplayParts ) ;
2014-10-16 19:35:34 +02:00
}
2014-09-25 08:44:13 +02:00
2014-11-11 22:58:50 +01:00
if ( isTypeParameterList ) {
2014-11-18 23:08:42 +01:00
prefixDisplayParts . push ( punctuationPart ( SyntaxKind . LessThanToken ) ) ;
2014-10-16 19:35:34 +02:00
var typeParameters = candidateSignature . typeParameters ;
signatureHelpParameters = typeParameters && typeParameters . length > 0 ? map ( typeParameters , createSignatureHelpParameterForTypeParameter ) : emptyArray ;
2014-11-18 23:08:42 +01:00
suffixDisplayParts . push ( punctuationPart ( SyntaxKind . GreaterThanToken ) ) ;
2014-10-16 19:35:34 +02:00
var parameterParts = mapToDisplayParts ( writer = >
2014-11-11 22:58:50 +01:00
typeInfoResolver . getSymbolDisplayBuilder ( ) . buildDisplayForParametersAndDelimiters ( candidateSignature . parameters , writer , invocation ) ) ;
2014-11-18 23:08:42 +01:00
suffixDisplayParts . push . apply ( suffixDisplayParts , parameterParts ) ;
2014-10-16 19:35:34 +02:00
}
else {
var typeParameterParts = mapToDisplayParts ( writer = >
2014-11-11 22:58:50 +01:00
typeInfoResolver . getSymbolDisplayBuilder ( ) . buildDisplayForTypeParametersAndDelimiters ( candidateSignature . typeParameters , writer , invocation ) ) ;
2014-11-18 23:08:42 +01:00
prefixDisplayParts . push . apply ( prefixDisplayParts , typeParameterParts ) ;
prefixDisplayParts . push ( punctuationPart ( SyntaxKind . OpenParenToken ) ) ;
2014-11-25 01:34:00 +01:00
2014-10-16 19:35:34 +02:00
var parameters = candidateSignature . parameters ;
signatureHelpParameters = parameters . length > 0 ? map ( parameters , createSignatureHelpParameterForParameter ) : emptyArray ;
2014-11-18 23:08:42 +01:00
suffixDisplayParts . push ( punctuationPart ( SyntaxKind . CloseParenToken ) ) ;
2014-10-16 19:35:34 +02:00
}
2014-09-25 08:44:13 +02:00
2014-10-16 00:25:29 +02:00
var returnTypeParts = mapToDisplayParts ( writer = >
2014-11-11 22:58:50 +01:00
typeInfoResolver . getSymbolDisplayBuilder ( ) . buildReturnTypeDisplay ( candidateSignature , writer , invocation ) ) ;
2014-11-18 23:08:42 +01:00
suffixDisplayParts . push . apply ( suffixDisplayParts , returnTypeParts ) ;
2014-09-25 08:44:13 +02:00
2014-09-24 22:58:03 +02:00
return {
isVariadic : candidateSignature.hasRestParameter ,
2014-11-18 23:08:42 +01:00
prefixDisplayParts ,
suffixDisplayParts ,
2014-10-16 00:25:29 +02:00
separatorDisplayParts : [ punctuationPart ( SyntaxKind . CommaToken ) , spacePart ( ) ] ,
2014-10-16 19:35:34 +02:00
parameters : signatureHelpParameters ,
2014-09-27 00:40:55 +02:00
documentation : candidateSignature.getDocumentationComment ( )
2014-09-24 22:58:03 +02:00
} ;
2014-09-22 20:38:01 +02:00
} ) ;
2014-09-25 08:44:13 +02:00
2014-11-11 22:58:50 +01:00
var argumentIndex = argumentListInfo . argumentIndex ;
2014-10-01 02:13:27 +02:00
2014-11-11 22:58:50 +01:00
// argumentCount is the *apparent* number of arguments.
var argumentCount = argumentListInfo . argumentCount ;
2014-10-01 02:13:27 +02:00
var selectedItemIndex = candidates . indexOf ( bestSignature ) ;
if ( selectedItemIndex < 0 ) {
selectedItemIndex = selectBestInvalidOverloadIndex ( candidates , argumentCount ) ;
}
2015-03-07 00:09:19 +01:00
Debug . assert ( argumentIndex === 0 || argumentIndex < argumentCount , ` argumentCount < argumentIndex, ${ argumentCount } < ${ argumentIndex } ` ) ;
2015-03-06 23:29:23 +01:00
2014-09-24 22:58:03 +02:00
return {
2014-11-18 23:08:42 +01:00
items ,
applicableSpan ,
selectedItemIndex ,
argumentIndex ,
argumentCount
2014-09-24 22:58:03 +02:00
} ;
2014-10-16 19:35:34 +02:00
function createSignatureHelpParameterForParameter ( parameter : Symbol ) : SignatureHelpParameter {
var displayParts = mapToDisplayParts ( writer = >
2014-11-11 22:58:50 +01:00
typeInfoResolver . getSymbolDisplayBuilder ( ) . buildParameterDisplay ( parameter , writer , invocation ) ) ;
2014-10-16 19:35:34 +02:00
2014-12-02 07:03:41 +01:00
var isOptional = hasQuestionToken ( parameter . valueDeclaration ) ;
2014-10-16 19:35:34 +02:00
return {
name : parameter.name ,
documentation : parameter.getDocumentationComment ( ) ,
2014-11-18 23:08:42 +01:00
displayParts ,
isOptional
2014-10-16 19:35:34 +02:00
} ;
}
function createSignatureHelpParameterForTypeParameter ( typeParameter : TypeParameter ) : SignatureHelpParameter {
var displayParts = mapToDisplayParts ( writer = >
2014-11-11 22:58:50 +01:00
typeInfoResolver . getSymbolDisplayBuilder ( ) . buildTypeParameterDisplay ( typeParameter , writer , invocation ) ) ;
2014-10-16 19:35:34 +02:00
return {
name : typeParameter.symbol.name ,
documentation : emptyArray ,
2014-11-18 23:08:42 +01:00
displayParts ,
2014-10-16 19:35:34 +02:00
isOptional : false
} ;
}
2014-09-22 20:38:01 +02:00
}
}
2014-07-19 01:55:11 +02:00
}