diff --git a/Jakefile b/Jakefile
index 1307601fb81..bb481fbcd1e 100644
--- a/Jakefile
+++ b/Jakefile
@@ -54,6 +54,7 @@ var servicesSources = [
}).concat([
"services.ts",
"shims.ts",
+ "signatureInfoHelpers.ts"
].map(function (f) {
return path.join(servicesDirectory, f);
}));
diff --git a/src/services/services.ts b/src/services/services.ts
index 04e63f6dbe1..d763785ae1e 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -10,6 +10,7 @@
///
///
///
+///
///
///
@@ -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 {
+ (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 = (node).getSyntheticParentOrParent();
+ if (parent.kind === SyntaxKind.SyntaxList) {
+ var grandparent = parent.parent;
+ if (grandparent.kind === SyntaxKind.CallExpression || grandparent.kind === SyntaxKind.NewExpression) {
+ var index = (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 === (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,
diff --git a/src/services/signatureInfoHelpers.ts b/src/services/signatureInfoHelpers.ts
index 8594a5dd320..875aa3cfcd7 100644
--- a/src/services/signatureInfoHelpers.ts
+++ b/src/services/signatureInfoHelpers.ts
@@ -3,344 +3,424 @@
///
-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 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() || (symbol).isNamedTypeSymbol())) {
- functionName = symbol.getScopedNameEx(enclosingScopeSymbol).toString();
- }
+ //// 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 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 = 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 = 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 !(n).body || isCompletedNode((n).body, position, sourceFile);
+ case SyntaxKind.ModuleDeclaration:
+ return (n).body && isCompletedNode((n).body, position, sourceFile);
+ case SyntaxKind.IfStatement:
+ if ((n).thenStatement && (n).thenStatement.end > position) {
+ return isCompletedNode((n).thenStatement, position, sourceFile);
+ }
+ else if ((n).elseStatement) {
+ return isCompletedNode((n).elseStatement, position, sourceFile);
+ }
+ else {
+ return true;
+ }
+ break;
+ case SyntaxKind.ExpressionStatement:
+ return isCompletedNode((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;
+ }
+
}
}
\ No newline at end of file
diff --git a/tests/cases/fourslash/signatureHelpEmptyList.ts b/tests/cases/fourslash/signatureHelpEmptyList.ts
new file mode 100644
index 00000000000..6ea70159dcc
--- /dev/null
+++ b/tests/cases/fourslash/signatureHelpEmptyList.ts
@@ -0,0 +1,20 @@
+///
+
+////function Foo(arg1: string, arg2: string) {
+////}
+////
+////Foo(/*1*/);
+////function Bar(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();
\ No newline at end of file