Skip to content

Commit ff24963

Browse files
committed
Initial draft of getSignatureHelpCurrentArgumentState
1 parent ab5ce84 commit ff24963

File tree

4 files changed

+93
-32
lines changed

4 files changed

+93
-32
lines changed

src/compiler/core.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ module ts {
4141
return -1;
4242
}
4343

44+
export function countWhere<T>(array: T[], predicate: (x: T) => boolean): number {
45+
var count = 0;
46+
if (array) {
47+
for (var i = 0, len = array.length; i < len; i++) {
48+
if (predicate(array[i])) {
49+
count++;
50+
}
51+
}
52+
}
53+
return count;
54+
}
55+
4456
export function filter<T>(array: T[], f: (x: T) => boolean): T[] {
4557
if (array) {
4658
var result: T[] = [];

src/services/services.ts

Lines changed: 66 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3479,8 +3479,36 @@ module ts {
34793479
return emitOutput;
34803480
}
34813481

3482+
function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node, sourceFile: SourceFile): Node {
3483+
var children = parent.getChildren(sourceFile);
3484+
var indexOfOpenerToken = children.indexOf(openerToken);
3485+
return children[indexOfOpenerToken + 1];
3486+
}
3487+
34823488
// Signature help
34833489
function getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems {
3490+
synchronizeHostData();
3491+
3492+
// Decide whether to show signature help
3493+
fileName = TypeScript.switchToForwardSlashes(fileName);
3494+
var sourceFile = getSourceFile(fileName);
3495+
var node = getNodeAtPosition(sourceFile, position);
3496+
3497+
3498+
// Semantic filtering of signature help
3499+
var signatureHelpContext = getSignatureHelpArgumentContext(node);
3500+
if (signatureHelpContext) {
3501+
var call = <CallExpression>signatureHelpContext.list.parent;
3502+
var candidates = <Signature[]>[];
3503+
var resolvedSignature = typeInfoResolver.getResolvedSignature(call, candidates);
3504+
return candidates.length
3505+
? createSignatureHelpItems(candidates, resolvedSignature, signatureHelpContext.list)
3506+
: undefined;
3507+
}
3508+
3509+
return undefined;
3510+
3511+
34843512
// If node is an argument, returns its index in the argument list
34853513
// If not, returns -1
34863514
function getArgumentIndexInfo(node: Node): ServicesSyntaxUtilities.ListItemInfo {
@@ -3492,16 +3520,7 @@ module ts {
34923520
// Find out if 'node' is an argument, a type argument, or neither
34933521
if (node.kind === SyntaxKind.LessThanToken || node.kind === SyntaxKind.OpenParenToken) {
34943522
// Find the list that starts right *after* the < or ( token
3495-
var seenRelevantOpenerToken = false;
3496-
var list = forEach(parent.getChildren(), c => {
3497-
if (seenRelevantOpenerToken) {
3498-
Debug.assert(c.kind === SyntaxKind.SyntaxList);
3499-
return c;
3500-
}
3501-
if (c.kind === node.kind /*node is the relevant opener token we are looking for*/) {
3502-
seenRelevantOpenerToken = true;
3503-
}
3504-
});
3523+
var list = getChildListThatStartsWithOpenerToken(parent, node, sourceFile);
35053524
Debug.assert(list);
35063525
// Treat the open paren / angle bracket of a call as the introduction of parameter slot 0
35073526
return {
@@ -3549,7 +3568,7 @@ module ts {
35493568
return undefined;
35503569
}
35513570

3552-
function getSignatureHelpItemsFromCandidateInfo(candidates: Signature[], bestSignature: Signature, argumentListOrTypeArgumentList: Node): SignatureHelpItems {
3571+
function createSignatureHelpItems(candidates: Signature[], bestSignature: Signature, argumentListOrTypeArgumentList: Node): SignatureHelpItems {
35533572
var items = map(candidates, candidateSignature => {
35543573
var parameters = candidateSignature.parameters;
35553574
var parameterHelpItems = parameters.length === 0 ? emptyArray : map(parameters, p => {
@@ -3586,32 +3605,49 @@ module ts {
35863605
var applicableSpan = new TypeScript.TextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart);
35873606
return new SignatureHelpItems(items, applicableSpan, selectedItemIndex);
35883607
}
3608+
}
35893609

3590-
synchronizeHostData();
3591-
3592-
// Decide whether to show signature help
3610+
function getSignatureHelpCurrentArgumentState(fileName: string, position: number, applicableSpanStart: number): SignatureHelpState {
35933611
fileName = TypeScript.switchToForwardSlashes(fileName);
3594-
var sourceFile = getSourceFile(fileName);
3595-
var node = getNodeAtPosition(sourceFile, position);
3612+
var sourceFile = getCurrentSourceFile(fileName);
3613+
var tokenPrecedingSpanStart = ServicesSyntaxUtilities.findPrecedingToken(applicableSpanStart, sourceFile);
3614+
if (tokenPrecedingSpanStart.kind !== SyntaxKind.OpenParenToken && tokenPrecedingSpanStart.kind !== SyntaxKind.LessThanToken) {
3615+
// The span start must have moved backward in the file (for example if the open paren was backspaced)
3616+
return undefined;
3617+
}
35963618

3619+
var tokenPrecedingCurrentPosition = ServicesSyntaxUtilities.findPrecedingToken(position, sourceFile);
3620+
var call = <CallExpression>tokenPrecedingSpanStart.parent;
3621+
if (tokenPrecedingCurrentPosition.kind === SyntaxKind.CloseParenToken || tokenPrecedingCurrentPosition.kind === SyntaxKind.GreaterThanToken) {
3622+
if (tokenPrecedingCurrentPosition.parent === call) {
3623+
// This call expression is complete. Stop signature help.
3624+
return undefined;
3625+
}
3626+
// TODO(jfreeman): handle other (incorrect) ways that a call expression can end
3627+
}
35973628

3598-
// Semantic filtering of signature help
3599-
var signatureHelpContext = getSignatureHelpArgumentContext(node);
3600-
if (signatureHelpContext) {
3601-
var call = <CallExpression>signatureHelpContext.list.parent;
3602-
var candidates = <Signature[]>[];
3603-
var resolvedSignature = typeInfoResolver.getResolvedSignature(call, candidates);
3604-
return candidates.length
3605-
? getSignatureHelpItemsFromCandidateInfo(candidates, resolvedSignature, signatureHelpContext.list)
3606-
: undefined;
3629+
Debug.assert(call.kind === SyntaxKind.CallExpression || call.kind === SyntaxKind.NewExpression, "wrong call kind " + SyntaxKind[call.kind]);
3630+
3631+
var argumentListOrTypeArgumentList = getChildListThatStartsWithOpenerToken(call, tokenPrecedingSpanStart, sourceFile);
3632+
// Debug.assert(argumentListOrTypeArgumentList.getChildCount() === 0 || argumentListOrTypeArgumentList.getChildCount() % 2 === 1, "Even number of children");
3633+
3634+
var numberOfCommas = countWhere(argumentListOrTypeArgumentList.getChildren(), arg => arg.kind === SyntaxKind.CommaToken);
3635+
var argumentCount = numberOfCommas + 1;
3636+
3637+
3638+
if (argumentCount <= 1) {
3639+
return new SignatureHelpState(/*argumentIndex*/ 0, argumentCount);
36073640
}
36083641

3609-
return undefined;
3610-
}
3642+
var indexOfNodeContainingPosition = ServicesSyntaxUtilities.findListItemIndexContainingPosition(argumentListOrTypeArgumentList, position);
36113643

3612-
function getSignatureHelpCurrentArgumentState(fileName: string, position: number, applicableSpanStart: number): SignatureHelpState {
3613-
synchronizeHostData();
3614-
return null;
3644+
// indexOfNodeContainingPosition checks that position is between pos and end of each child, so it is
3645+
// possible that we are to the right of all children. Assume that we are still within
3646+
// the applicable span and that we are typing the last argument
3647+
// Alternatively, we could be in range of one of the arguments, in which case we need to divide
3648+
// by 2 to exclude commas
3649+
var argumentIndex = indexOfNodeContainingPosition < 0 ? argumentCount - 1 : indexOfNodeContainingPosition / 2;
3650+
return new SignatureHelpState(argumentIndex, argumentCount);
36153651
}
36163652

36173653
/// Syntactic features

src/services/servicesSyntaxUtilities.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ module ts.ServicesSyntaxUtilities {
2626
};
2727
}
2828

29+
// Includes the start position of each child, but excludes the end
30+
export function findListItemIndexContainingPosition(list: Node, position: number): number {
31+
Debug.assert(list.kind === SyntaxKind.SyntaxList);
32+
var children = list.getChildren();
33+
for (var i = 0; i < children.length; i++) {
34+
if (children[i].pos <= position && children[i].end > position) {
35+
return i;
36+
}
37+
}
38+
39+
return -1;
40+
}
41+
2942
export function findNextToken(previousToken: Node, parent: Node): Node {
3043
return find(parent);
3144

src/services/shims.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,8 @@ module ts {
587587
return this.forwardJSONCall(
588588
"getSignatureHelpCurrentArgumentState('" + fileName + "', " + position + ", " + applicableSpanStart + ")",
589589
() => {
590-
var signatureInfo = this.languageService.getSignatureHelpItems(fileName, position);
591-
return signatureInfo;
590+
var signatureHelpState = this.languageService.getSignatureHelpCurrentArgumentState(fileName, position, applicableSpanStart);
591+
return signatureHelpState;
592592
});
593593
}
594594

0 commit comments

Comments
 (0)