From 102da2d80a83d340a8bbcdea3814c7f1b87dd944 Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Mon, 22 Sep 2014 11:38:01 -0700 Subject: [PATCH] Fix errors in signatureHelp.ts --- Jakefile | 2 +- src/services/services.ts | 2 +- src/services/signatureHelp.ts | 775 ++++++++++++++++++++-------------- 3 files changed, 465 insertions(+), 314 deletions(-) diff --git a/Jakefile b/Jakefile index bb481fbcd1e..04ca4ae386f 100644 --- a/Jakefile +++ b/Jakefile @@ -54,7 +54,7 @@ var servicesSources = [ }).concat([ "services.ts", "shims.ts", - "signatureInfoHelpers.ts" + "signatureHelp.ts" ].map(function (f) { return path.join(servicesDirectory, f); })); diff --git a/src/services/services.ts b/src/services/services.ts index cf542d00e9d..eb3793199e1 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -10,7 +10,7 @@ /// /// /// -/// +/// /// /// /// diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 352cdb02950..145bc58d806 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -3,347 +3,498 @@ /// -module ts { +module ts.SignatureHelp { - //export interface IPartiallyWrittenTypeArgumentListInformation { - // genericIdentifer: TypeScript.ISyntaxToken; - // lessThanToken: TypeScript.ISyntaxToken; - // argumentIndex: number; + // 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 1; + + //// for (var i = 0, n = signatures.length; i < n; i++) { + //// var signature = signatures[i]; + + //// // filter out the definition signature if there are overloads + //// if (hasOverloads && signature.isDefinition()) { + //// continue; + //// } + + //// var signatureGroupInfo = new FormalSignatureItemInfo(); + //// var paramIndexInfo: number[] = []; + //// var functionName = signature.getScopedNameEx(enclosingScopeSymbol).toString(); + //// if (!functionName && (!symbol.isType() || (symbol).isNamedTypeSymbol())) { + //// functionName = symbol.getScopedNameEx(enclosingScopeSymbol).toString(); + //// } + + //// var signatureMemberName = signature.getSignatureTypeNameEx(functionName, /*shortform*/ false, /*brackets*/ false, enclosingScopeSymbol, /*getParamMarkerInfo*/ true, /*getTypeParameterMarkerInfo*/ true); + //// signatureGroupInfo.signatureInfo = TypeScript.MemberName.memberNameToString(signatureMemberName, paramIndexInfo); + //// signatureGroupInfo.docComment = signature.docComments(); + + //// var parameterMarkerIndex = 0; + + //// if (signature.isGeneric()) { + //// var typeParameters = signature.getTypeParameters(); + //// for (var j = 0, m = typeParameters.length; j < m; j++) { + //// var typeParameter = typeParameters[j]; + //// var signatureTypeParameterInfo = new FormalTypeParameterInfo(); + //// signatureTypeParameterInfo.name = typeParameter.getDisplayName(); + //// signatureTypeParameterInfo.docComment = typeParameter.docComments(); + //// signatureTypeParameterInfo.minChar = paramIndexInfo[2 * parameterMarkerIndex]; + //// signatureTypeParameterInfo.limChar = paramIndexInfo[2 * parameterMarkerIndex + 1]; + //// parameterMarkerIndex++; + //// signatureGroupInfo.typeParameters.push(signatureTypeParameterInfo); + //// } + //// } + + //// var parameters = signature.parameters; + //// for (var j = 0, m = parameters.length; j < m; j++) { + //// var parameter = parameters[j]; + //// var signatureParameterInfo = new FormalParameterInfo(); + //// signatureParameterInfo.isVariable = signature.hasVarArgs && (j === parameters.length - 1); + //// signatureParameterInfo.name = parameter.getDisplayName(); + //// signatureParameterInfo.docComment = parameter.docComments(); + //// signatureParameterInfo.minChar = paramIndexInfo[2 * parameterMarkerIndex]; + //// signatureParameterInfo.limChar = paramIndexInfo[2 * parameterMarkerIndex + 1]; + //// parameterMarkerIndex++; + //// signatureGroupInfo.parameters.push(signatureParameterInfo); + //// } + + //// signatureGroup.push(signatureGroupInfo); + //// } + + //// return signatureGroup; + ////} + + ////public static getSignatureInfoFromGenericSymbol(symbol: TypeScript.PullSymbol, enclosingScopeSymbol: TypeScript.PullSymbol, compilerState: LanguageServiceCompiler) { + //// var signatureGroupInfo = new FormalSignatureItemInfo(); + + //// var paramIndexInfo: number[] = []; + //// var symbolName = symbol.getScopedNameEx(enclosingScopeSymbol, /*skipTypeParametersInName*/ false, /*useConstaintInName*/ true, /*getPrettyTypeName*/ false, /*getTypeParamMarkerInfo*/ true); + + //// signatureGroupInfo.signatureInfo = TypeScript.MemberName.memberNameToString(symbolName, paramIndexInfo); + //// signatureGroupInfo.docComment = symbol.docComments(); + + //// var parameterMarkerIndex = 0; + + //// var typeSymbol = symbol.type; + + //// var typeParameters = typeSymbol.getTypeParameters(); + //// for (var i = 0, n = typeParameters.length; i < n; i++) { + //// var typeParameter = typeParameters[i]; + //// var signatureTypeParameterInfo = new FormalTypeParameterInfo(); + //// signatureTypeParameterInfo.name = typeParameter.getDisplayName(); + //// signatureTypeParameterInfo.docComment = typeParameter.docComments(); + //// signatureTypeParameterInfo.minChar = paramIndexInfo[2 * i]; + //// signatureTypeParameterInfo.limChar = paramIndexInfo[2 * i + 1]; + //// signatureGroupInfo.typeParameters.push(signatureTypeParameterInfo); + //// } + + //// return [signatureGroupInfo]; + ////} + + ////public static getActualSignatureInfoFromCallExpression(ast: IExpressionWithArgumentListSyntax, caretPosition: number, typeParameterInformation: IPartiallyWrittenTypeArgumentListInformation): ActualSignatureInfo { + //// if (!ast) { + //// return null; + //// } + + //// var result = new ActualSignatureInfo(); + + //// // The expression is not guaranteed to be complete, we need to populate the min and lim with the most accurate information we have about + //// // type argument and argument lists + //// var parameterMinChar = caretPosition; + //// var parameterLimChar = caretPosition; + + //// if (ast.argumentList.typeArgumentList) { + //// parameterMinChar = Math.min(start(ast.argumentList.typeArgumentList)); + //// parameterLimChar = Math.max(Math.max(start(ast.argumentList.typeArgumentList), end(ast.argumentList.typeArgumentList) + trailingTriviaWidth(ast.argumentList.typeArgumentList))); + //// } + + //// if (ast.argumentList.arguments) { + //// parameterMinChar = Math.min(parameterMinChar, end(ast.argumentList.openParenToken)); + //// parameterLimChar = Math.max(parameterLimChar, + //// ast.argumentList.closeParenToken.fullWidth() > 0 ? start(ast.argumentList.closeParenToken) : fullEnd(ast.argumentList)); + //// } + + //// result.parameterMinChar = parameterMinChar; + //// result.parameterLimChar = parameterLimChar; + //// result.currentParameterIsTypeParameter = false; + //// result.currentParameter = -1; + + //// if (typeParameterInformation) { + //// result.currentParameterIsTypeParameter = true; + //// result.currentParameter = typeParameterInformation.argumentIndex; + //// } + //// else if (ast.argumentList.arguments && ast.argumentList.arguments.length > 0) { + //// result.currentParameter = 0; + //// for (var index = 0; index < ast.argumentList.arguments.length; index++) { + //// if (caretPosition > end(ast.argumentList.arguments[index]) + lastToken(ast.argumentList.arguments[index]).trailingTriviaWidth()) { + //// result.currentParameter++; + //// } + //// } + //// } + + //// return result; + ////} + + ////public static getActualSignatureInfoFromPartiallyWritenGenericExpression(caretPosition: number, typeParameterInformation: IPartiallyWrittenTypeArgumentListInformation): ActualSignatureInfo { + //// var result = new ActualSignatureInfo(); + + //// result.parameterMinChar = start(typeParameterInformation.lessThanToken); + //// result.parameterLimChar = Math.max(fullEnd(typeParameterInformation.lessThanToken), caretPosition); + //// result.currentParameterIsTypeParameter = true; + //// result.currentParameter = typeParameterInformation.argumentIndex; + + //// return result; + ////} + + ////public static isSignatureHelpBlocker(sourceUnit: TypeScript.SourceUnitSyntax, position: number): boolean { + //// // We shouldn't be getting a possition that is outside the file because + //// // isEntirelyInsideComment can't handle when the position is out of bounds, + //// // callers should be fixed, however we should be resiliant to bad inputs + //// // so we return true (this position is a blocker for getting signature help) + //// if (position < 0 || position > fullWidth(sourceUnit)) { + //// return true; + //// } + + //// return TypeScript.Syntax.isEntirelyInsideComment(sourceUnit, position); + ////} + + ////public static isTargetOfObjectCreationExpression(positionedToken: TypeScript.ISyntaxToken): boolean { + //// var positionedParent = TypeScript.Syntax.getAncestorOfKind(positionedToken, TypeScript.SyntaxKind.ObjectCreationExpression); + //// if (positionedParent) { + //// var objectCreationExpression = positionedParent; + //// var expressionRelativeStart = objectCreationExpression.newKeyword.fullWidth(); + //// var tokenRelativeStart = positionedToken.fullStart() - fullStart(positionedParent); + //// return tokenRelativeStart >= expressionRelativeStart && + //// tokenRelativeStart <= (expressionRelativeStart + fullWidth(objectCreationExpression.expression)); + //// } + + //// return false; + ////} + + //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; //} + var emptyArray: any[] = []; - export module SignatureInfoHelpers { + export function getSignatureHelpItems(sourceFile: SourceFile, position: number, startingNode: Node, typeInfoResolver: TypeChecker): SignatureHelpItems { + // Decide whether to show signature help + var signatureHelpContext = getSignatureHelpArgumentContext(startingNode); - // 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 "foosignatureHelpContext.list.parent; + var candidates = []; + var resolvedSignature = typeInfoResolver.getResolvedSignature(call, candidates); + return candidates.length + ? createSignatureHelpItems(candidates, resolvedSignature, signatureHelpContext.list) + : undefined; + } - // if (token && TypeScript.Syntax.hasAncestorOfKind(token, TypeScript.SyntaxKind.TypeParameterList)) { - // // We are in the wrong generic list. bail out - // return null; - // } + return undefined; - // 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; - // } + // If node is an argument, returns its index in the argument list + // If not, returns -1 + function getArgumentIndexInfo(node: Node): ServicesSyntaxUtilities.ListItemInfo { + if (node.parent.kind !== SyntaxKind.CallExpression && node.parent.kind !== SyntaxKind.NewExpression) { + return undefined; + } - // // 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--; - // } + var parent = node.parent; + // 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 + var list = getChildListThatStartsWithOpenerToken(parent, node, sourceFile); + Debug.assert(list); + // Treat the open paren / angle bracket of a call as the introduction of parameter slot 0 + return { + listItemIndex: 0, + list: list + }; + } - // break; + if (node.kind === SyntaxKind.GreaterThanToken + || node.kind === SyntaxKind.CloseParenToken + || node === parent.func) { + return undefined; + } - // case TypeScript.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: - // stack++; + return ServicesSyntaxUtilities.findListItemInfo(node); + } - // // Intentaion fall through - // case TypeScript.SyntaxKind.GreaterThanToken: - // stack++; - // break; + function getSignatureHelpArgumentContext(node: Node): ServicesSyntaxUtilities.ListItemInfo { + // We only want this node if it is a token and it strictly contains the current position. + // Otherwise we want the previous token + var isToken = node.kind < SyntaxKind.Missing; + if (!isToken || position <= node.getStart() || position >= node.getEnd()) { + node = ServicesSyntaxUtilities.findPrecedingToken(position, sourceFile); - // case TypeScript.SyntaxKind.CommaToken: - // if (stack == 0) { - // argumentIndex++; - // } + if (!node) { + return undefined; + } + } - // break; + var signatureHelpAvailable = false; + for (var n = node; n.kind !== SyntaxKind.SourceFile; n = n.parent) { + if (n.kind === SyntaxKind.FunctionBlock) { + return undefined; + } - // case TypeScript.SyntaxKind.CloseBraceToken: - // // This can be object type, skip untill we find the matching open brace token - // var unmatchedOpenBraceTokens = 0; + var argumentInfo = getArgumentIndexInfo(n); + if (argumentInfo) { + return argumentInfo; + } - // // 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; + // TODO: Handle generic call with incomplete syntax + } + return undefined; + } - // 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); + function createSignatureHelpItems(candidates: Signature[], bestSignature: Signature, argumentListOrTypeArgumentList: Node): SignatureHelpItems { + var items = map(candidates, candidateSignature => { + var parameters = candidateSignature.parameters; + var parameterHelpItems = parameters.length === 0 ? emptyArray : map(parameters, p => { + var display = p.name; + if (candidateSignature.hasRestParameter && parameters[parameters.length - 1] === p) { + display = "..." + display; + } + var isOptional = !!(p.valueDeclaration.flags & NodeFlags.QuestionMark); + if (isOptional) { + display += "?"; + } + display += ": " + typeInfoResolver.typeToString(typeInfoResolver.getTypeOfSymbol(p), argumentListOrTypeArgumentList); + return new SignatureHelpParameter(p.name, "", display, /*isOptional*/ false); + }); + var callTargetNode = (argumentListOrTypeArgumentList.parent).func; + var callTargetSymbol = typeInfoResolver.getSymbolInfo(callTargetNode); + var signatureName = callTargetSymbol ? typeInfoResolver.symbolToString(callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined) : ""; + var prefix = signatureName; + // TODO(jfreeman): Constraints? + if (candidateSignature.typeParameters && candidateSignature.typeParameters.length) { + prefix += "<" + map(candidateSignature.typeParameters, tp => tp.symbol.name).join(", ") + ">"; + } + prefix += "("; + var suffix = "): " + typeInfoResolver.typeToString(candidateSignature.getReturnType(), argumentListOrTypeArgumentList); + return new SignatureHelpItem(candidateSignature.hasRestParameter, prefix, suffix, ", ", parameterHelpItems, ""); + }); + var selectedItemIndex = candidates.indexOf(bestSignature); + if (selectedItemIndex < 0) { + selectedItemIndex = 0; + } - // if (token && token.kind() === TypeScript.SyntaxKind.CloseParenToken) { - // // Skip untill the matching open paren token - // token = SignatureInfoHelpers.moveBackUpTillMatchingTokenKind(token, TypeScript.SyntaxKind.CloseParenToken, TypeScript.SyntaxKind.OpenParenToken); + var applicableSpanStart = argumentListOrTypeArgumentList.getFullStart(); + var applicableSpanEnd = skipTrivia(sourceFile.text, argumentListOrTypeArgumentList.end, /*stopAfterLineBreak*/ false); + var applicableSpan = new TypeScript.TextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart); + return new SignatureHelpItems(items, applicableSpan, selectedItemIndex); + } + } - // if (token && token.kind() === TypeScript.SyntaxKind.GreaterThanToken) { - // // Another generic type argument list, skip it\ - // token = SignatureInfoHelpers.moveBackUpTillMatchingTokenKind(token, TypeScript.SyntaxKind.GreaterThanToken, TypeScript.SyntaxKind.LessThanToken); - // } + export function getSignatureHelpCurrentArgumentState(sourceFile: SourceFile, position: number, applicableSpanStart: number): SignatureHelpState { + var tokenPrecedingSpanStart = ServicesSyntaxUtilities.findPrecedingToken(applicableSpanStart, sourceFile); + if (tokenPrecedingSpanStart.kind !== SyntaxKind.OpenParenToken && tokenPrecedingSpanStart.kind !== SyntaxKind.LessThanToken) { + // The span start must have moved backward in the file (for example if the open paren was backspaced) + return undefined; + } - // if (token && token.kind() === TypeScript.SyntaxKind.NewKeyword) { - // // In case this was a constructor type, skip the new keyword - // token = previousToken(token, /*includeSkippedTokens*/ true); - // } + var tokenPrecedingCurrentPosition = ServicesSyntaxUtilities.findPrecedingToken(position, sourceFile); + var call = tokenPrecedingSpanStart.parent; + if (tokenPrecedingCurrentPosition.kind === SyntaxKind.CloseParenToken || tokenPrecedingCurrentPosition.kind === SyntaxKind.GreaterThanToken) { + if (tokenPrecedingCurrentPosition.parent === call) { + // This call expression is complete. Stop signature help. + return undefined; + } + } - // if (!token) { - // // No matching token was found. bail out - // break whileLoop; - // } - // } - // else { - // // This is not a funtion type. exit the main loop - // break whileLoop; - // } + Debug.assert(call.kind === SyntaxKind.CallExpression || call.kind === SyntaxKind.NewExpression, "wrong call kind " + SyntaxKind[call.kind]); - // break; + var argumentListOrTypeArgumentList = getChildListThatStartsWithOpenerToken(call, tokenPrecedingSpanStart, sourceFile); + // Debug.assert(argumentListOrTypeArgumentList.getChildCount() === 0 || argumentListOrTypeArgumentList.getChildCount() % 2 === 1, "Even number of children"); - // 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; + // The call might be finished, but incorrectly. Check if we are still within the bounds of the call + if (position > skipTrivia(sourceFile.text, argumentListOrTypeArgumentList.end, /*stopAfterLineBreak*/ false)) { + return undefined; + } - // default: - // break whileLoop; - // } + var numberOfCommas = countWhere(argumentListOrTypeArgumentList.getChildren(), arg => arg.kind === SyntaxKind.CommaToken); + var argumentCount = numberOfCommas + 1; - // token = previousToken(token, /*includeSkippedTokens*/ true); - // } - // return null; - //} + if (argumentCount <= 1) { + return new SignatureHelpState(/*argumentIndex*/ 0, argumentCount); + } - ////public static getSignatureInfoFromSignatureSymbol(symbol: TypeScript.PullSymbol, signatures: TypeScript.PullSignatureSymbol[], enclosingScopeSymbol: TypeScript.PullSymbol, compilerState: LanguageServiceCompiler) { - //// var signatureGroup: FormalSignatureItemInfo[] = []; - - //// var hasOverloads = signatures.length > 1; - - //// for (var i = 0, n = signatures.length; i < n; i++) { - //// var signature = signatures[i]; - - //// // filter out the definition signature if there are overloads - //// if (hasOverloads && signature.isDefinition()) { - //// continue; - //// } - - //// var signatureGroupInfo = new FormalSignatureItemInfo(); - //// var paramIndexInfo: number[] = []; - //// var functionName = signature.getScopedNameEx(enclosingScopeSymbol).toString(); - //// if (!functionName && (!symbol.isType() || (symbol).isNamedTypeSymbol())) { - //// functionName = symbol.getScopedNameEx(enclosingScopeSymbol).toString(); - //// } - - //// var signatureMemberName = signature.getSignatureTypeNameEx(functionName, /*shortform*/ false, /*brackets*/ false, enclosingScopeSymbol, /*getParamMarkerInfo*/ true, /*getTypeParameterMarkerInfo*/ true); - //// signatureGroupInfo.signatureInfo = TypeScript.MemberName.memberNameToString(signatureMemberName, paramIndexInfo); - //// signatureGroupInfo.docComment = signature.docComments(); - - //// var parameterMarkerIndex = 0; - - //// if (signature.isGeneric()) { - //// var typeParameters = signature.getTypeParameters(); - //// for (var j = 0, m = typeParameters.length; j < m; j++) { - //// var typeParameter = typeParameters[j]; - //// var signatureTypeParameterInfo = new FormalTypeParameterInfo(); - //// signatureTypeParameterInfo.name = typeParameter.getDisplayName(); - //// signatureTypeParameterInfo.docComment = typeParameter.docComments(); - //// signatureTypeParameterInfo.minChar = paramIndexInfo[2 * parameterMarkerIndex]; - //// signatureTypeParameterInfo.limChar = paramIndexInfo[2 * parameterMarkerIndex + 1]; - //// parameterMarkerIndex++; - //// signatureGroupInfo.typeParameters.push(signatureTypeParameterInfo); - //// } - //// } - - //// var parameters = signature.parameters; - //// for (var j = 0, m = parameters.length; j < m; j++) { - //// var parameter = parameters[j]; - //// var signatureParameterInfo = new FormalParameterInfo(); - //// signatureParameterInfo.isVariable = signature.hasVarArgs && (j === parameters.length - 1); - //// signatureParameterInfo.name = parameter.getDisplayName(); - //// signatureParameterInfo.docComment = parameter.docComments(); - //// signatureParameterInfo.minChar = paramIndexInfo[2 * parameterMarkerIndex]; - //// signatureParameterInfo.limChar = paramIndexInfo[2 * parameterMarkerIndex + 1]; - //// parameterMarkerIndex++; - //// signatureGroupInfo.parameters.push(signatureParameterInfo); - //// } - - //// signatureGroup.push(signatureGroupInfo); - //// } - - //// return signatureGroup; - ////} - - ////public static getSignatureInfoFromGenericSymbol(symbol: TypeScript.PullSymbol, enclosingScopeSymbol: TypeScript.PullSymbol, compilerState: LanguageServiceCompiler) { - //// var signatureGroupInfo = new FormalSignatureItemInfo(); - - //// var paramIndexInfo: number[] = []; - //// var symbolName = symbol.getScopedNameEx(enclosingScopeSymbol, /*skipTypeParametersInName*/ false, /*useConstaintInName*/ true, /*getPrettyTypeName*/ false, /*getTypeParamMarkerInfo*/ true); - - //// signatureGroupInfo.signatureInfo = TypeScript.MemberName.memberNameToString(symbolName, paramIndexInfo); - //// signatureGroupInfo.docComment = symbol.docComments(); - - //// var parameterMarkerIndex = 0; - - //// var typeSymbol = symbol.type; - - //// var typeParameters = typeSymbol.getTypeParameters(); - //// for (var i = 0, n = typeParameters.length; i < n; i++) { - //// var typeParameter = typeParameters[i]; - //// var signatureTypeParameterInfo = new FormalTypeParameterInfo(); - //// signatureTypeParameterInfo.name = typeParameter.getDisplayName(); - //// signatureTypeParameterInfo.docComment = typeParameter.docComments(); - //// signatureTypeParameterInfo.minChar = paramIndexInfo[2 * i]; - //// signatureTypeParameterInfo.limChar = paramIndexInfo[2 * i + 1]; - //// signatureGroupInfo.typeParameters.push(signatureTypeParameterInfo); - //// } - - //// return [signatureGroupInfo]; - ////} - - ////public static getActualSignatureInfoFromCallExpression(ast: IExpressionWithArgumentListSyntax, caretPosition: number, typeParameterInformation: IPartiallyWrittenTypeArgumentListInformation): ActualSignatureInfo { - //// if (!ast) { - //// return null; - //// } - - //// var result = new ActualSignatureInfo(); - - //// // The expression is not guaranteed to be complete, we need to populate the min and lim with the most accurate information we have about - //// // type argument and argument lists - //// var parameterMinChar = caretPosition; - //// var parameterLimChar = caretPosition; - - //// if (ast.argumentList.typeArgumentList) { - //// parameterMinChar = Math.min(start(ast.argumentList.typeArgumentList)); - //// parameterLimChar = Math.max(Math.max(start(ast.argumentList.typeArgumentList), end(ast.argumentList.typeArgumentList) + trailingTriviaWidth(ast.argumentList.typeArgumentList))); - //// } - - //// if (ast.argumentList.arguments) { - //// parameterMinChar = Math.min(parameterMinChar, end(ast.argumentList.openParenToken)); - //// parameterLimChar = Math.max(parameterLimChar, - //// ast.argumentList.closeParenToken.fullWidth() > 0 ? start(ast.argumentList.closeParenToken) : fullEnd(ast.argumentList)); - //// } - - //// result.parameterMinChar = parameterMinChar; - //// result.parameterLimChar = parameterLimChar; - //// result.currentParameterIsTypeParameter = false; - //// result.currentParameter = -1; - - //// if (typeParameterInformation) { - //// result.currentParameterIsTypeParameter = true; - //// result.currentParameter = typeParameterInformation.argumentIndex; - //// } - //// else if (ast.argumentList.arguments && ast.argumentList.arguments.length > 0) { - //// result.currentParameter = 0; - //// for (var index = 0; index < ast.argumentList.arguments.length; index++) { - //// if (caretPosition > end(ast.argumentList.arguments[index]) + lastToken(ast.argumentList.arguments[index]).trailingTriviaWidth()) { - //// result.currentParameter++; - //// } - //// } - //// } - - //// return result; - ////} - - ////public static getActualSignatureInfoFromPartiallyWritenGenericExpression(caretPosition: number, typeParameterInformation: IPartiallyWrittenTypeArgumentListInformation): ActualSignatureInfo { - //// var result = new ActualSignatureInfo(); - - //// result.parameterMinChar = start(typeParameterInformation.lessThanToken); - //// result.parameterLimChar = Math.max(fullEnd(typeParameterInformation.lessThanToken), caretPosition); - //// result.currentParameterIsTypeParameter = true; - //// result.currentParameter = typeParameterInformation.argumentIndex; - - //// return result; - ////} - - ////public static isSignatureHelpBlocker(sourceUnit: TypeScript.SourceUnitSyntax, position: number): boolean { - //// // We shouldn't be getting a possition that is outside the file because - //// // isEntirelyInsideComment can't handle when the position is out of bounds, - //// // callers should be fixed, however we should be resiliant to bad inputs - //// // so we return true (this position is a blocker for getting signature help) - //// if (position < 0 || position > fullWidth(sourceUnit)) { - //// return true; - //// } - - //// return TypeScript.Syntax.isEntirelyInsideComment(sourceUnit, position); - ////} - - ////public static isTargetOfObjectCreationExpression(positionedToken: TypeScript.ISyntaxToken): boolean { - //// var positionedParent = TypeScript.Syntax.getAncestorOfKind(positionedToken, TypeScript.SyntaxKind.ObjectCreationExpression); - //// if (positionedParent) { - //// var objectCreationExpression = positionedParent; - //// var expressionRelativeStart = objectCreationExpression.newKeyword.fullWidth(); - //// var tokenRelativeStart = positionedToken.fullStart() - fullStart(positionedParent); - //// return tokenRelativeStart >= expressionRelativeStart && - //// tokenRelativeStart <= (expressionRelativeStart + fullWidth(objectCreationExpression.expression)); - //// } - - //// return false; - ////} - - //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; - //} + var indexOfNodeContainingPosition = ServicesSyntaxUtilities.findListItemIndexContainingPosition(argumentListOrTypeArgumentList, position); + // indexOfNodeContainingPosition checks that position is between pos and end of each child, so it is + // possible that we are to the right of all children. Assume that we are still within + // the applicable span and that we are typing the last argument + // Alternatively, we could be in range of one of the arguments, in which case we need to divide + // by 2 to exclude commas + var argumentIndex = indexOfNodeContainingPosition < 0 ? argumentCount - 1 : indexOfNodeContainingPosition / 2; + return new SignatureHelpState(argumentIndex, argumentCount); + } + + function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node, sourceFile: SourceFile): Node { + var children = parent.getChildren(sourceFile); + var indexOfOpenerToken = children.indexOf(openerToken); + return children[indexOfOpenerToken + 1]; } } \ No newline at end of file