@@ -172,15 +172,15 @@ module ts.SignatureHelp {
172
172
return undefined ;
173
173
}
174
174
175
- var argumentList = getContainingArgumentList ( startingToken ) ;
175
+ var argumentInfo = getContainingArgumentInfo ( startingToken ) ;
176
176
cancellationToken . throwIfCancellationRequested ( ) ;
177
177
178
178
// Semantic filtering of signature help
179
- if ( ! argumentList ) {
179
+ if ( ! argumentInfo ) {
180
180
return undefined ;
181
181
}
182
182
183
- var call = < CallExpression > argumentList . parent ;
183
+ var call = < CallExpression > argumentInfo . list . parent ;
184
184
var candidates = < Signature [ ] > [ ] ;
185
185
var resolvedSignature = typeInfoResolver . getResolvedSignature ( call , candidates ) ;
186
186
cancellationToken . throwIfCancellationRequested ( ) ;
@@ -189,13 +189,13 @@ module ts.SignatureHelp {
189
189
return undefined ;
190
190
}
191
191
192
- return createSignatureHelpItems ( candidates , resolvedSignature , argumentList ) ;
192
+ return createSignatureHelpItems ( candidates , resolvedSignature , argumentInfo ) ;
193
193
194
194
/**
195
195
* If node is an argument, returns its index in the argument list.
196
196
* If not, returns -1.
197
197
*/
198
- function getImmediatelyContainingArgumentList ( node : Node ) : Node {
198
+ function getImmediatelyContainingArgumentInfo ( node : Node ) : ListItemInfo {
199
199
if ( node . parent . kind !== SyntaxKind . CallExpression && node . parent . kind !== SyntaxKind . NewExpression ) {
200
200
return undefined ;
201
201
}
@@ -216,10 +216,14 @@ module ts.SignatureHelp {
216
216
var parent = < CallExpression > node . parent ;
217
217
// Find out if 'node' is an argument, a type argument, or neither
218
218
if ( node . kind === SyntaxKind . LessThanToken || node . kind === SyntaxKind . OpenParenToken ) {
219
- // Find the list that starts right *after* the < or ( token
219
+ // Find the list that starts right *after* the < or ( token.
220
+ // If the user has just opened a list, consider this item 0.
220
221
var list = getChildListThatStartsWithOpenerToken ( parent , node , sourceFile ) ;
221
222
Debug . assert ( list ) ;
222
- return list ;
223
+ return {
224
+ list : list ,
225
+ listItemIndex : 0
226
+ } ;
223
227
}
224
228
225
229
if ( node . kind === SyntaxKind . GreaterThanToken
@@ -228,18 +232,18 @@ module ts.SignatureHelp {
228
232
return undefined ;
229
233
}
230
234
231
- return findContainingList ( node ) ;
235
+ return findListItemInfo ( node ) ;
232
236
}
233
237
234
- function getContainingArgumentList ( node : Node ) : Node {
238
+ function getContainingArgumentInfo ( node : Node ) : ListItemInfo {
235
239
for ( var n = node ; n . kind !== SyntaxKind . SourceFile ; n = n . parent ) {
236
240
if ( n . kind === SyntaxKind . FunctionBlock ) {
237
241
return undefined ;
238
242
}
239
243
240
- var argumentList = getImmediatelyContainingArgumentList ( n ) ;
241
- if ( argumentList ) {
242
- return argumentList ;
244
+ var argumentInfo = getImmediatelyContainingArgumentInfo ( n ) ;
245
+ if ( argumentInfo ) {
246
+ return argumentInfo ;
243
247
}
244
248
245
249
@@ -248,7 +252,35 @@ module ts.SignatureHelp {
248
252
return undefined ;
249
253
}
250
254
251
- function createSignatureHelpItems ( candidates : Signature [ ] , bestSignature : Signature , argumentListOrTypeArgumentList : Node ) : SignatureHelpItems {
255
+ /**
256
+ * The selectedItemIndex could be negative for several reasons.
257
+ * 1. There are too many arguments for all of the overloads
258
+ * 2. None of the overloads were type compatible
259
+ * The solution here is to try to pick the best overload by picking
260
+ * either the first one that has an appropriate number of parameters,
261
+ * or the one with the most parameters.
262
+ */
263
+ function selectBestInvalidOverloadIndex ( candidates : Signature [ ] , argumentCount : number ) : number {
264
+ var maxParamsSignatureIndex = - 1 ;
265
+ var maxParams = - 1 ;
266
+ for ( var i = 0 ; i < candidates . length ; i ++ ) {
267
+ var candidate = candidates [ i ] ;
268
+
269
+ if ( candidate . hasRestParameter || candidate . parameters . length >= argumentCount ) {
270
+ return i ;
271
+ }
272
+
273
+ if ( candidate . parameters . length > maxParams ) {
274
+ maxParams = candidate . parameters . length ;
275
+ maxParamsSignatureIndex = i ;
276
+ }
277
+ }
278
+
279
+ return maxParamsSignatureIndex ;
280
+ }
281
+
282
+ function createSignatureHelpItems ( candidates : Signature [ ] , bestSignature : Signature , argumentInfoOrTypeArgumentInfo : ListItemInfo ) : SignatureHelpItems {
283
+ var argumentListOrTypeArgumentList = argumentInfoOrTypeArgumentInfo . list ;
252
284
var items : SignatureHelpItem [ ] = map ( candidates , candidateSignature => {
253
285
var parameters = candidateSignature . parameters ;
254
286
var parameterHelpItems : SignatureHelpParameter [ ] = parameters . length === 0 ? emptyArray : map ( parameters , p => {
@@ -321,11 +353,6 @@ module ts.SignatureHelp {
321
353
} ;
322
354
} ) ;
323
355
324
- var selectedItemIndex = candidates . indexOf ( bestSignature ) ;
325
- if ( selectedItemIndex < 0 ) {
326
- selectedItemIndex = 0 ;
327
- }
328
-
329
356
// We use full start and skip trivia on the end because we want to include trivia on
330
357
// both sides. For example,
331
358
//
@@ -338,63 +365,38 @@ module ts.SignatureHelp {
338
365
var applicableSpanEnd = skipTrivia ( sourceFile . text , argumentListOrTypeArgumentList . end , /*stopAfterLineBreak*/ false ) ;
339
366
var applicableSpan = new TypeScript . TextSpan ( applicableSpanStart , applicableSpanEnd - applicableSpanStart ) ;
340
367
341
- var state = getSignatureHelpCurrentArgumentState ( sourceFile , position , applicableSpanStart ) ;
368
+ // The listItemIndex we got back includes commas. Our goal is to return the index of the proper
369
+ // item (not including commas). Here are some examples:
370
+ // 1. foo(a, b, c $) -> the listItemIndex is 4, we want to return 2
371
+ // 2. foo(a, b, $ c) -> listItemIndex is 3, we want to return 2
372
+ // 3. foo($a) -> listItemIndex is 0, we want to return 0
373
+ //
374
+ // In general, we want to subtract the number of commas before the current index.
375
+ // But if we are on a comma, we also want to pretend we are on the argument *following*
376
+ // the comma. That amounts to taking the ceiling of half the index.
377
+ var argumentIndex = ( argumentInfoOrTypeArgumentInfo . listItemIndex + 1 ) >> 1 ;
378
+
379
+ // argumentCount is the number of commas plus one, unless the list is completely empty,
380
+ // in which case there are 0.
381
+ var argumentCount = argumentListOrTypeArgumentList . getChildCount ( ) === 0
382
+ ? 0
383
+ : 1 + countWhere ( argumentListOrTypeArgumentList . getChildren ( ) , arg => arg . kind === SyntaxKind . CommaToken ) ;
384
+
385
+ var selectedItemIndex = candidates . indexOf ( bestSignature ) ;
386
+ if ( selectedItemIndex < 0 ) {
387
+ selectedItemIndex = selectBestInvalidOverloadIndex ( candidates , argumentCount ) ;
388
+ }
389
+
342
390
return {
343
391
items : items ,
344
392
applicableSpan : applicableSpan ,
345
393
selectedItemIndex : selectedItemIndex ,
346
- argumentIndex : state . argumentIndex ,
347
- argumentCount : state . argumentCount
394
+ argumentIndex : argumentIndex ,
395
+ argumentCount : argumentCount
348
396
} ;
349
397
}
350
398
}
351
399
352
- function getSignatureHelpCurrentArgumentState ( sourceFile : SourceFile , position : number , applicableSpanStart : number ) : { argumentIndex : number ; argumentCount : number } {
353
- var tokenPrecedingSpanStart = findPrecedingToken ( applicableSpanStart , sourceFile ) ;
354
- if ( ! tokenPrecedingSpanStart ) {
355
- return undefined ;
356
- }
357
-
358
- if ( tokenPrecedingSpanStart . kind !== SyntaxKind . OpenParenToken && tokenPrecedingSpanStart . kind !== SyntaxKind . LessThanToken ) {
359
- // The span start must have moved backward in the file (for example if the open paren was backspaced)
360
- return undefined ;
361
- }
362
-
363
- var tokenPrecedingCurrentPosition = findPrecedingToken ( position , sourceFile ) ;
364
- var call = < CallExpression > tokenPrecedingSpanStart . parent ;
365
- Debug . assert ( call . kind === SyntaxKind . CallExpression || call . kind === SyntaxKind . NewExpression , "wrong call kind " + SyntaxKind [ call . kind ] ) ;
366
- if ( tokenPrecedingCurrentPosition . kind === SyntaxKind . CloseParenToken || tokenPrecedingCurrentPosition . kind === SyntaxKind . GreaterThanToken ) {
367
- if ( tokenPrecedingCurrentPosition . parent === call ) {
368
- // This call expression is complete. Stop signature help.
369
- return undefined ;
370
- }
371
- }
372
-
373
- var argumentListOrTypeArgumentList = getChildListThatStartsWithOpenerToken ( call , tokenPrecedingSpanStart , sourceFile ) ;
374
- // Debug.assert(argumentListOrTypeArgumentList.getChildCount() === 0 || argumentListOrTypeArgumentList.getChildCount() % 2 === 1, "Even number of children");
375
-
376
- // The call might be finished, but incorrectly. Check if we are still within the bounds of the call
377
- if ( position > skipTrivia ( sourceFile . text , argumentListOrTypeArgumentList . end , /*stopAfterLineBreak*/ false ) ) {
378
- return undefined ;
379
- }
380
-
381
- var numberOfCommas = countWhere ( argumentListOrTypeArgumentList . getChildren ( ) , arg => arg . kind === SyntaxKind . CommaToken ) ;
382
- var argumentCount = numberOfCommas + 1 ;
383
- if ( argumentCount <= 1 ) {
384
- return { argumentIndex : 0 , argumentCount : argumentCount } ;
385
- }
386
-
387
- var indexOfNodeContainingPosition = findListItemIndexContainingPosition ( argumentListOrTypeArgumentList , position ) ;
388
-
389
- // indexOfNodeContainingPosition checks that position is between pos and end of each child, so it is
390
- // possible that we are to the right of all children. Assume that we are still within
391
- // the applicable span and that we are typing the last argument
392
- // Alternatively, we could be in range of one of the arguments, in which case we need to divide
393
- // by 2 to exclude commas. Use bit shifting in order to take the floor of the division.
394
- var argumentIndex = indexOfNodeContainingPosition < 0 ? argumentCount - 1 : indexOfNodeContainingPosition >> 1 ;
395
- return { argumentIndex : argumentIndex , argumentCount : argumentCount } ;
396
- }
397
-
398
400
function getChildListThatStartsWithOpenerToken ( parent : Node , openerToken : Node , sourceFile : SourceFile ) : Node {
399
401
var children = parent . getChildren ( sourceFile ) ;
400
402
var indexOfOpenerToken = children . indexOf ( openerToken ) ;
0 commit comments