Skip to content

Commit b38743c

Browse files
Merge pull request #2307 from Microsoft/sigHelpIndex
Compute consistent argument indices and counts for signature help.
2 parents d2ecfa7 + 05c2a3e commit b38743c

File tree

3 files changed

+63
-20
lines changed

3 files changed

+63
-20
lines changed

src/services/signatureHelp.ts

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ module ts.SignatureHelp {
238238
invocation: callExpression,
239239
argumentsSpan: getApplicableSpanForArguments(list),
240240
argumentIndex: 0,
241-
argumentCount: getCommaBasedArgCount(list)
241+
argumentCount: getArgumentCount(list)
242242
};
243243
}
244244

@@ -253,20 +253,11 @@ module ts.SignatureHelp {
253253
var list = listItemInfo.list;
254254
var isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos;
255255

256-
// The listItemIndex we got back includes commas. Our goal is to return the index of the proper
257-
// item (not including commas). Here are some examples:
258-
// 1. foo(a, b, c #) -> the listItemIndex is 4, we want to return 2
259-
// 2. foo(a, b, # c) -> listItemIndex is 3, we want to return 2
260-
// 3. foo(#a) -> listItemIndex is 0, we want to return 0
261-
//
262-
// In general, we want to subtract the number of commas before the current index.
263-
// But if we are on a comma, we also want to pretend we are on the argument *following*
264-
// the comma. That amounts to taking the ceiling of half the index.
265-
var argumentIndex = (listItemInfo.listItemIndex + 1) >> 1;
256+
var argumentIndex = getArgumentIndex(list, node);
257+
var argumentCount = getArgumentCount(list);
266258

267-
var argumentCount = getCommaBasedArgCount(list);
268-
269-
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
259+
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount,
260+
`argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
270261

271262
return {
272263
kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments,
@@ -313,12 +304,53 @@ module ts.SignatureHelp {
313304
return undefined;
314305
}
315306

316-
function getCommaBasedArgCount(argumentsList: Node) {
317-
// The number of arguments is the number of commas plus one, unless the list
318-
// is completely empty, in which case there are 0 arguments.
319-
return argumentsList.getChildCount() === 0
320-
? 0
321-
: 1 + countWhere(argumentsList.getChildren(), arg => arg.kind === SyntaxKind.CommaToken);
307+
function getArgumentIndex(argumentsList: Node, node: Node) {
308+
// The list we got back can include commas. In the presence of errors it may
309+
// also just have nodes without commas. For example "Foo(a b c)" will have 3
310+
// args without commas. We want to find what index we're at. So we count
311+
// forward until we hit ourselves, only incrementing the index if it isn't a
312+
// comma.
313+
//
314+
// Note: the subtlety around trailing commas (in getArgumentCount) does not apply
315+
// here. That's because we're only walking forward until we hit the node we're
316+
// on. In that case, even if we're after the trailing comma, we'll still see
317+
// that trailing comma in the list, and we'll have generated the appropriate
318+
// arg index.
319+
var argumentIndex = 0;
320+
var listChildren = argumentsList.getChildren();
321+
for (var i = 0, n = listChildren.length; i < n; i++) {
322+
var child = listChildren[i];
323+
if (child === node) {
324+
break;
325+
}
326+
if (child.kind !== SyntaxKind.CommaToken) {
327+
argumentIndex++;
328+
}
329+
}
330+
331+
return argumentIndex;
332+
}
333+
334+
function getArgumentCount(argumentsList: Node) {
335+
// The argument count for a list is normally the number of non-comma children it has.
336+
// For example, if you have "Foo(a,b)" then there will be three children of the arg
337+
// list 'a' '<comma>' 'b'. So, in this case the arg count will be 2. However, there
338+
// is a small subtlety. If you have "Foo(a,)", then the child list will just have
339+
// 'a' '<comma>'. So, in the case where the last child is a comma, we increase the
340+
// arg count by one to compensate.
341+
//
342+
// Note: this subtlety only applies to the last comma. If you had "Foo(a,," then
343+
// we'll have: 'a' '<comma>' '<missing>'
344+
// That will give us 2 non-commas. We then add one for the last comma, givin us an
345+
// arg count of 3.
346+
var listChildren = argumentsList.getChildren();
347+
348+
var argumentCount = countWhere(listChildren, arg => arg.kind !== SyntaxKind.CommaToken);
349+
if (listChildren.length > 0 && lastOrUndefined(listChildren).kind === SyntaxKind.CommaToken) {
350+
argumentCount++;
351+
}
352+
353+
return argumentCount;
322354
}
323355

324356
// spanIndex is either the index for a given template span.

src/services/utilities.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ module ts {
9595
}
9696
});
9797

98+
// Either we didn't find an appropriate list, or the list must contain us.
99+
Debug.assert(!syntaxList || contains(syntaxList.getChildren(), node));
98100
return syntaxList;
99101
}
100102

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function foo(a) { }
4+
////foo(hello my name /**/is
5+
6+
goTo.marker();
7+
verify.signatureHelpPresent();
8+
verify.signatureHelpCountIs(1);
9+

0 commit comments

Comments
 (0)