Signature help present with completed signatures

This commit is contained in:
Jason Freeman 2014-09-04 10:24:11 -07:00
parent 927bab6565
commit 49fdb98081
4 changed files with 508 additions and 295 deletions

View File

@ -54,6 +54,7 @@ var servicesSources = [
}).concat([
"services.ts",
"shims.ts",
"signatureInfoHelpers.ts"
].map(function (f) {
return path.join(servicesDirectory, f);
}));

View File

@ -10,6 +10,7 @@
/// <reference path='braceMatcher.ts' />
/// <reference path='breakpoints.ts' />
/// <reference path='indentation.ts' />
/// <reference path='signatureInfoHelpers.ts' />
/// <reference path='formatting\formatting.ts' />
/// <reference path='formatting\smartIndenter.ts' />
@ -95,6 +96,7 @@ module ts {
public flags: NodeFlags;
public parent: Node;
private _children: Node[];
private _syntheticParent: Node;
public getSourceFile(): SourceFile {
var node: Node = this;
@ -150,6 +152,9 @@ module ts {
if (pos < node.pos) {
pos = this.addSyntheticNodes(list._children, pos, node.pos);
}
else {
(<NodeObject>node)._syntheticParent = list;
}
list._children.push(node);
pos = node.end;
}
@ -202,8 +207,13 @@ module ts {
return this._children;
}
public getIndexOfChild(child: Node): number {
if (!this._children) this.createChildren();
return this._children.indexOf(child);
}
public getFirstToken(sourceFile?: SourceFile): Node {
var children = this.getChildren(sourceFile);
var children = this.getChildren();
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child.kind < SyntaxKind.Missing) return child;
@ -219,6 +229,10 @@ module ts {
if (child.kind > SyntaxKind.Missing) return child.getLastToken(sourceFile);
}
}
public getSyntheticParentOrParent(): Node {
return this._syntheticParent || this.parent;
}
}
class SymbolObject implements Symbol {
@ -3474,7 +3488,105 @@ module ts {
// Reset writer back to undefined to make sure that we produce an error message if CompilerHost.writeFile method is called when we are not in getEmitOutput
writer = undefined;
return emitOutput;
return emitOutput;
}
// Signature help
function getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems {
// If node is an argument, returns its index in the argument list
// If not, returns -1
function getArgumentIndex(node: Node): number {
// Treat the open paren / angle bracket of a call as the introduction of parameter slot 0
var parent = (<NodeObject>node).getSyntheticParentOrParent();
if (parent.kind === SyntaxKind.SyntaxList) {
var grandparent = parent.parent;
if (grandparent.kind === SyntaxKind.CallExpression || grandparent.kind === SyntaxKind.NewExpression) {
var index = (<NodeObject>parent).getIndexOfChild(node);
Debug.assert(index >= 0);
return index;
}
}
if (node.kind === SyntaxKind.LessThanToken || node.kind === SyntaxKind.OpenParenToken) {
return parent.kind === SyntaxKind.CallExpression || parent.kind === SyntaxKind.NewExpression
? 0
: -1;
}
// TODO: Handle close paren or close angle bracket on nonempty list
return -1;
}
//// Technically signature help should only be triggered on these characters
//if (node.kind !== SyntaxKind.CommaToken && node.kind !== SyntaxKind.OpenParenToken && node.kind !== SyntaxKind.LessThanToken) {
// return false;
//}
//if (node.kind === SyntaxKind.CommaToken) {
// if (node.parent.kind !== SyntaxKind.SyntaxList) {
// return false;
// }
// // node becomes the containing SyntaxList
// node = node.parent;
//}
//// node is open paren, less than, or a syntax list containing a comma
//if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) {
// return true;
//}
synchronizeHostData();
// Decide whether to show signature help
var sourceFile = getSourceFile(fileName);
var node = getNodeAtPosition(sourceFile, position);
// 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()) {
// This is a temporary hack until we figure out our token story.
// The correct solution is to get the previous token
node = SignatureInfoHelpers.findClosestRightmostSiblingFromLeft(position, sourceFile);
if (!node) {
return undefined;
}
if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) {
if (node === (<CallExpression>node.parent).func) {
node = node.parent.getChildAt(1);
}
}
}
var signatureHelpAvailable = false;
for (var n = node; n.kind !== SyntaxKind.SourceFile; n = n.parent) {
if (n.kind === SyntaxKind.FunctionBlock) {
break;
}
var index = getArgumentIndex(n);
if (index >= 0) {
signatureHelpAvailable = true;
break;
}
// TODO: Handle previous token logic
// TODO: Handle generic call with incomplete
}
return signatureHelpAvailable
? new SignatureHelpItems(undefined, undefined, undefined)
: undefined;
}
function getSignatureHelpCurrentArgumentState(fileName: string, position: number, applicableSpanStart: number): SignatureHelpState {
synchronizeHostData();
return null;
}
/// Syntactic features
@ -4026,8 +4138,8 @@ module ts {
getCompletionsAtPosition: getCompletionsAtPosition,
getCompletionEntryDetails: getCompletionEntryDetails,
getTypeAtPosition: getTypeAtPosition,
getSignatureHelpItems: (filename, position): SignatureHelpItems => null,
getSignatureHelpCurrentArgumentState: (fileName, position, applicableSpanStart): SignatureHelpState => null,
getSignatureHelpItems: getSignatureHelpItems,
getSignatureHelpCurrentArgumentState: getSignatureHelpCurrentArgumentState,
getDefinitionAtPosition: getDefinitionAtPosition,
getReferencesAtPosition: getReferencesAtPosition,
getOccurrencesAtPosition: getOccurrencesAtPosition,

View File

@ -3,344 +3,424 @@
///<reference path='references.ts' />
module TypeScript.Services {
module ts {
export interface IPartiallyWrittenTypeArgumentListInformation {
genericIdentifer: TypeScript.ISyntaxToken;
lessThanToken: TypeScript.ISyntaxToken;
argumentIndex: number;
}
//export interface IPartiallyWrittenTypeArgumentListInformation {
// genericIdentifer: TypeScript.ISyntaxToken;
// lessThanToken: TypeScript.ISyntaxToken;
// argumentIndex: number;
//}
export interface IExpressionWithArgumentListSyntax extends IExpressionSyntax {
expression: IExpressionSyntax;
argumentList: ArgumentListSyntax;
}
//export interface IExpressionWithArgumentListSyntax extends IExpressionSyntax {
// expression: IExpressionSyntax;
// argumentList: ArgumentListSyntax;
//}
export class SignatureInfoHelpers {
export module SignatureInfoHelpers {
// 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): IPartiallyWrittenTypeArgumentListInformation {
var token = Syntax.findTokenOnLeft(syntaxTree.sourceUnit(), position, /*includeSkippedTokens*/ true);
//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;
}
// 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;
// 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;
}
// 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--;
}
// // 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;
// break;
case TypeScript.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
stack++;
// case TypeScript.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
// stack++;
// Intentaion fall through
case TypeScript.SyntaxKind.GreaterThanToken:
stack++;
break;
// // Intentaion fall through
// case TypeScript.SyntaxKind.GreaterThanToken:
// stack++;
// break;
case TypeScript.SyntaxKind.CommaToken:
if (stack == 0) {
argumentIndex++;
}
// case TypeScript.SyntaxKind.CommaToken:
// if (stack == 0) {
// argumentIndex++;
// }
break;
// break;
case TypeScript.SyntaxKind.CloseBraceToken:
// This can be object type, skip untill we find the matching open brace token
var unmatchedOpenBraceTokens = 0;
// 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;
}
// // 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;
// 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);
// 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.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.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 && 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;
}
// 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;
// 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;
// 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;
}
// default:
// break whileLoop;
// }
token = previousToken(token, /*includeSkippedTokens*/ true);
}
// token = previousToken(token, /*includeSkippedTokens*/ true);
// }
return null;
}
// return null;
//}
public static getSignatureInfoFromSignatureSymbol(symbol: TypeScript.PullSymbol, signatures: TypeScript.PullSignatureSymbol[], enclosingScopeSymbol: TypeScript.PullSymbol, compilerState: LanguageServiceCompiler) {
var signatureGroup: FormalSignatureItemInfo[] = [];
////public static getSignatureInfoFromSignatureSymbol(symbol: TypeScript.PullSymbol, signatures: TypeScript.PullSignatureSymbol[], enclosingScopeSymbol: TypeScript.PullSymbol, compilerState: LanguageServiceCompiler) {
//// var signatureGroup: FormalSignatureItemInfo[] = [];
var hasOverloads = signatures.length > 1;
//// var hasOverloads = signatures.length > 1;
for (var i = 0, n = signatures.length; i < n; i++) {
var signature = signatures[i];
//// 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;
}
//// // 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() || (<TypeScript.PullTypeSymbol>symbol).isNamedTypeSymbol())) {
functionName = symbol.getScopedNameEx(enclosingScopeSymbol).toString();
}
//// var signatureGroupInfo = new FormalSignatureItemInfo();
//// var paramIndexInfo: number[] = [];
//// var functionName = signature.getScopedNameEx(enclosingScopeSymbol).toString();
//// if (!functionName && (!symbol.isType() || (<TypeScript.PullTypeSymbol>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 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;
//// 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);
//// 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 = <TypeScript.ObjectCreationExpressionSyntax> 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;
//}
export function findClosestRightmostSiblingFromLeft(position: number, sourceFile: SourceFile): Node {
return search(sourceFile);
function search(n: Node): Node {
var childCandidate: Node;
forEachChild(n, c => {
if (c.kind === SyntaxKind.OmittedExpression || c.kind === SyntaxKind.Missing) {
return;
}
}
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 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++;
if (c.end <= position || c.getStart() < position) {
childCandidate = c;
}
}
}
return c.end > position;
});
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 = <TypeScript.ObjectCreationExpressionSyntax> 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;
if (childCandidate) {
if (childCandidate.end > position || !isCompletedNode(childCandidate, position, sourceFile)) {
return search(childCandidate) || childCandidate;
}
else {
stack--;
return childCandidate;
}
}
else if (token.kind() === tokenKind) {
stack++;
}
// Move back
token = previousToken(token, /*includeSkippedTokens*/ true);
}
// Did not find matching token
return null;
}
function isCompletedNode(n: Node, position: number, sourceFile: SourceFile): boolean {
switch (n.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ObjectLiteral:
case SyntaxKind.Block:
case SyntaxKind.CatchBlock:
case SyntaxKind.FinallyBlock:
case SyntaxKind.FunctionBlock:
case SyntaxKind.ModuleBlock:
return isNodeEndWith(n, sourceFile, CharacterCodes.closeBrace);
case SyntaxKind.ParenExpression:
case SyntaxKind.CallSignature:
case SyntaxKind.CallExpression:
return isNodeEndWith(n, sourceFile, CharacterCodes.closeParen);
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.Method:
case SyntaxKind.ArrowFunction:
return !(<FunctionDeclaration>n).body || isCompletedNode((<FunctionDeclaration>n).body, position, sourceFile);
case SyntaxKind.ModuleDeclaration:
return (<ModuleDeclaration>n).body && isCompletedNode((<ModuleDeclaration>n).body, position, sourceFile);
case SyntaxKind.IfStatement:
if ((<IfStatement>n).thenStatement && (<IfStatement>n).thenStatement.end > position) {
return isCompletedNode((<IfStatement>n).thenStatement, position, sourceFile);
}
else if ((<IfStatement>n).elseStatement) {
return isCompletedNode((<IfStatement>n).elseStatement, position, sourceFile);
}
else {
return true;
}
break;
case SyntaxKind.ExpressionStatement:
return isCompletedNode((<ExpressionStatement>n).expression, position, sourceFile);
case SyntaxKind.ArrayLiteral:
return isNodeEndWith(n, sourceFile, CharacterCodes.closeBracket);
case SyntaxKind.Missing:
return false;
default:
return true;
}
}
function isNodeEndWith(n: Node, sourceFile: SourceFile, charCode: CharacterCodes): boolean {
var pos = n.end - 1; // Node.end is exclusive
return pos < sourceFile.text.length && sourceFile.text.charCodeAt(pos) === charCode;
}
}
}

View File

@ -0,0 +1,20 @@
/// <reference path='fourslash.ts' />
////function Foo(arg1: string, arg2: string) {
////}
////
////Foo(/*1*/);
////function Bar<T>(arg1: string, arg2: string) { }
////Bar</*2*/>();
goTo.marker('1');
verify.signatureHelpPresent();
verify.signatureHelpCountIs(1);
verify.currentSignatureHelpIs("Foo(arg1: string, arg2: string): void");
verify.currentSignatureParamterCountIs(2);
verify.currentParameterHelpArgumentNameIs("arg1");
verify.currentParameterSpanIs("arg1: string");
goTo.marker('2');
verify.signatureHelpPresent();