@@ -9,6 +9,7 @@ import core.Denotations.{SingleDenotation, Denotation}
9
9
import core .Flags
10
10
import core .NameOps .isUnapplyName
11
11
import core .Names ._
12
+ import core .NameKinds
12
13
import core .Types ._
13
14
import core .Symbols .NoSymbol
14
15
import interactive .Interactive
@@ -54,7 +55,8 @@ object Signatures {
54
55
*
55
56
* @param path The path to the function application
56
57
* @param span The position of the cursor
57
- * @return A triple containing the index of the parameter being edited, the index of functeon
58
+ *
59
+ * @return A triple containing the index of the parameter being edited, the index of functeon
58
60
* being called, the list of overloads of this function).
59
61
*/
60
62
def signatureHelp (path : List [tpd.Tree ], pos : Span )(using Context ): (Int , Int , List [Signature ]) =
@@ -65,7 +67,8 @@ object Signatures {
65
67
*
66
68
* @param path The path to the function application
67
69
* @param span The position of the cursor
68
- * @return A triple containing the index of the parameter being edited, the index of the function
70
+ *
71
+ * @return A triple containing the index of the parameter being edited, the index of the function
69
72
* being called, the list of overloads of this function).
70
73
*/
71
74
@ deprecated(
@@ -82,49 +85,76 @@ object Signatures {
82
85
*
83
86
* @param path The path to the function application
84
87
* @param span The position of the cursor
88
+ *
85
89
* @return A triple containing the index of the parameter being edited, the index of the function
86
90
* being called, the list of overloads of this function).
87
91
*/
88
92
def computeSignatureHelp (path : List [tpd.Tree ], span : Span )(using Context ): (Int , Int , List [Signature ]) =
89
93
findEnclosingApply(path, span) match
90
- case tpd.EmptyTree => (0 , 0 , Nil )
91
94
case Apply (fun, params) => applyCallInfo(span, params, fun)
92
95
case UnApply (fun, _, patterns) => unapplyCallInfo(span, fun, patterns)
96
+ case appliedTypeTree @ AppliedTypeTree (_, types) => appliedTypeTreeCallInfo(appliedTypeTree, types)
97
+ case tp @ TypeApply (fun, types) => applyCallInfo(span, types, fun, true )
98
+ case _ => (0 , 0 , Nil )
93
99
94
100
/**
95
101
* Finds enclosing application from given `path` for `span`.
96
102
*
97
103
* @param path The path to the function application
98
104
* @param span The position of the cursor
105
+ *
99
106
* @return Tree which encloses closest application containing span.
100
107
* In case if cursor is pointing on closing parenthesis and
101
108
* next subsequent application exists, it returns the latter
102
109
*/
103
110
private def findEnclosingApply (path : List [tpd.Tree ], span : Span )(using Context ): tpd.Tree =
104
111
path.filterNot {
105
- case apply @ Apply (fun, _) => fun.span.contains(span) || isTuple(apply)
106
- case unapply @ UnApply (fun, _, _) => fun.span.contains(span) || isTuple(unapply)
112
+ case apply @ Apply (fun, _) => fun.span.contains(span) || isValid(apply)
113
+ case unapply @ UnApply (fun, _, _) => fun.span.contains(span) || isValid(unapply)
114
+ case typeTree @ AppliedTypeTree (fun, _) => fun.span.contains(span) || isValid(typeTree)
115
+ case typeApply @ TypeApply (fun, _) => fun.span.contains(span) || isValid(typeApply)
107
116
case _ => true
108
117
} match {
109
118
case Nil => tpd.EmptyTree
110
- case direct :: enclosing :: _ if direct.source(span.end - 1 ) == ')' => enclosing
119
+ case direct :: enclosing :: _ if isClosingSymbol( direct.source(span.end - 1 )) => enclosing
111
120
case direct :: _ => direct
112
121
}
113
122
123
+ private def isClosingSymbol (ch : Char ) = ch == ')' || ch == ']'
124
+
114
125
/**
115
- * Extracts call information for a function application.
126
+ * Extracts call information for applied type tree:
116
127
*
117
- * @param span The position of the cursor
118
- * @param params Current function parameters
119
- * @param fun Function tree which is being applied
128
+ * @param types Currently applied function type parameters
129
+ * @param fun Function tree which is being applied
130
+ */
131
+ private def appliedTypeTreeCallInfo (
132
+ fun : tpd.Tree ,
133
+ types : List [tpd.Tree ]
134
+ )(using Context ): (Int , Int , List [Signature ]) =
135
+ val typeName = fun.symbol.name.show
136
+ val typeParams = fun.symbol.typeRef.typeParams.map(_.paramName.show)
137
+ val denot = fun.denot.asSingleDenotation
138
+ val activeParameter = (types.length - 1 ) max 0
139
+
140
+ val signature = Signature (typeName, typeParams, Nil , Some (typeName) , None , Some (denot))
141
+ (activeParameter, 0 , List (signature))
142
+
143
+ /**
144
+ * Extracts call information for a function application and type application.
120
145
*
146
+ * @param span The position of the cursor
147
+ * @param params Current function parameters
148
+ * @param fun Function tree which is being applied
149
+ * @param isTypeApply Is a type application
121
150
* @return A triple containing the index of the parameter being edited, the index of the function
122
151
* being called, the list of overloads of this function).
123
152
*/
124
153
private def applyCallInfo (
125
154
span : Span ,
126
155
params : List [tpd.Tree ],
127
- fun : tpd.Tree
156
+ fun : tpd.Tree ,
157
+ isTypeApply : Boolean = false
128
158
)(using Context ): (Int , Int , List [Signature ]) =
129
159
def treeQualifier (tree : tpd.Tree ): tpd.Tree = tree match
130
160
case Apply (qual, _) => treeQualifier(qual)
@@ -154,10 +184,11 @@ object Signatures {
154
184
155
185
if alternativeIndex < alternatives.length then
156
186
val curriedArguments = countParams(fun, alternatives(alternativeIndex))
157
- val paramIndex = params.indexWhere(_.span.contains(span)) match {
158
- case - 1 => (params.length - 1 max 0 ) + curriedArguments
159
- case n => n + curriedArguments
160
- }
187
+ // We have to shift all arguments by number of type parameters to properly show activeParameter index
188
+ val typeParamsShift = if ! isTypeApply then fun.symbol.denot.paramSymss.flatten.filter(_.isType).length else 0
189
+ val paramIndex = params.indexWhere(_.span.contains(span)) match
190
+ case - 1 => (params.length - 1 max 0 ) + curriedArguments + typeParamsShift
191
+ case n => n + curriedArguments + typeParamsShift
161
192
162
193
val pre = treeQualifier(fun)
163
194
val alternativesWithTypes = alternatives.map(_.asSeenFrom(pre.tpe.widenTermRefExpr))
@@ -170,9 +201,9 @@ object Signatures {
170
201
/**
171
202
* Extracts call informatioin for function in unapply context.
172
203
*
173
- * @param span The position of the cursor
204
+ * @param span The position of the cursor
174
205
* @param params Current function parameters
175
- * @param fun Unapply function tree
206
+ * @param fun Unapply function tree
176
207
*
177
208
* @return A triple containing the index of the parameter being edited, the index of the function
178
209
* being called, the list of overloads of this function).
@@ -202,7 +233,8 @@ object Signatures {
202
233
* Extract parameter names from `resultType` only if `resultType` is case class and `denot` is synthetic.
203
234
*
204
235
* @param resultType Function result type
205
- * @param denot Function denotation
236
+ * @param denot Function denotation
237
+ *
206
238
* @return List of lists of names of parameters if `resultType` is case class without overriden unapply
207
239
*/
208
240
private def extractParamNamess (resultType : Type , denot : Denotation )(using Context ): List [List [Name ]] =
@@ -214,9 +246,10 @@ object Signatures {
214
246
/**
215
247
* Extract parameter types from `resultType` in unapply context.
216
248
*
217
- * @param resultType Function result type
218
- * @param denot Function denotation
249
+ * @param resultType Function result type
250
+ * @param denot Function denotation
219
251
* @param patternsSize Number of pattern trees present in function tree
252
+ *
220
253
* @return List of lists of types present in unapply clause
221
254
*/
222
255
private def extractParamTypess (
@@ -263,13 +296,19 @@ object Signatures {
263
296
case (- 1 , pos) => - 1 // there are patterns, we must be outside range so we set no active parameter
264
297
case _ => (maximumParams - 1 ) min patternPosition max 0 // handle unapplySeq to always highlight Seq[A] on elements
265
298
266
- private def isTuple (tree : tpd.Tree )(using Context ): Boolean =
267
- tree.symbol != NoSymbol && ctx.definitions.isTupleClass(tree.symbol.owner.companionClass)
299
+ /**
300
+ * Checks if tree is valid for signatureHelp. Skipped trees are either tuple type or function type
301
+ *
302
+ * @param tree tree to validate
303
+ */
304
+ private def isValid (tree : tpd.Tree )(using Context ): Boolean =
305
+ ctx.definitions.isTupleNType(tree.tpe) || ctx.definitions.isFunctionType(tree.tpe)
268
306
269
307
/**
270
308
* Get unapply method result type omiting unknown types and another method calls.
271
309
*
272
310
* @param fun Unapply tree
311
+ *
273
312
* @return Proper unapply method type after extracting result from method types and omiting unknown types.
274
313
*/
275
314
private def unapplyMethodResult (fun : tpd.Tree )(using Context ): Type =
@@ -292,9 +331,10 @@ object Signatures {
292
331
*
293
332
* @see [[https://docs.scala-lang.org/scala3/reference/changed-features/pattern-matching.html ]]
294
333
*
295
- * @param resultType Final result type for unapply
334
+ * @param resultType Final result type for unapply
296
335
* @param patternCount Currently applied patterns to unapply function
297
336
* @param isUnapplySeq true if unapply name equals "unapplySeq", false otherwise
337
+ *
298
338
* @return List of List of types dependent on option less extractor type.
299
339
*/
300
340
private def mapOptionLessUnapply (
@@ -333,40 +373,63 @@ object Signatures {
333
373
* Creates signature for apply method.
334
374
*
335
375
* @param denot Function denotation for which apply signature is returned.
376
+ *
336
377
* @return Signature if denot is a function, None otherwise
337
378
*/
338
379
private def toApplySignature (denot : SingleDenotation )(using Context ): Option [Signature ] = {
339
380
val symbol = denot.symbol
340
381
val docComment = ParsedComment .docOf(symbol)
341
382
342
- def toParamss (tp : MethodType )(using Context ): List [List [Param ]] = {
343
- val rest = tp.resType match {
344
- case res : MethodType =>
345
- // Hide parameter lists consisting only of DummyImplicit,
346
- if (res.resultType.isParameterless &&
347
- res.isImplicitMethod &&
348
- res.paramInfos.forall(info =>
349
- info.classSymbol.derivesFrom(ctx.definitions.DummyImplicitClass )))
350
- Nil
351
- else
352
- toParamss(res)
383
+ def isDummyImplicit (res : MethodType ): Boolean =
384
+ res.resultType.isParameterless &&
385
+ res.isImplicitMethod &&
386
+ res.paramInfos.forall(info =>
387
+ info.classSymbol.derivesFrom(ctx.definitions.DummyImplicitClass ))
388
+
389
+ def toParamss (tp : Type )(using Context ): List [List [Param ]] =
390
+ val rest = tp.resultType match
391
+ case res : MethodType => if isDummyImplicit(res) then Nil else toParamss(res)
392
+ case _ => Nil
393
+
394
+ val currentParams = (tp.paramNamess, tp.paramInfoss) match
395
+ case (params :: _, infos :: _) => params zip infos
353
396
case _ => Nil
354
- }
355
- val params = tp.paramNames.zip(tp.paramInfos).map { case (name, info) =>
356
- Signatures .Param (
357
- name.show,
358
- info.widenTermRefExpr.show,
359
- docComment.flatMap(_.paramDoc(name)),
360
- isImplicit = tp.isImplicitMethod)
361
- }
362
- params :: rest
363
- }
397
+
398
+ val params = currentParams.map { (name, info) =>
399
+ Signatures .Param (
400
+ name.show,
401
+ info.widenTermRefExpr.show,
402
+ docComment.flatMap(_.paramDoc(name)),
403
+ isImplicit = tp.isImplicitMethod
404
+ )
405
+ }
406
+
407
+ (params :: rest)
408
+
409
+ def isSyntheticEvidence (name : String ) =
410
+ if ! name.startsWith(NameKinds .EvidenceParamName .separator) then false else
411
+ symbol.paramSymss.flatten.find(_.name.show == name).exists(_.flags.is(Flags .Implicit ))
364
412
365
413
denot.info.stripPoly match
366
- case tpe : MethodType =>
367
- val paramss = toParamss(tpe)
414
+ case tpe : (MethodType | AppliedType | TypeRef | TypeParamRef ) =>
415
+ val paramss = toParamss(tpe).map(_.filterNot(param => isSyntheticEvidence(param.name)))
416
+ val evidenceParams = (tpe.paramNamess.flatten zip tpe.paramInfoss.flatten).flatMap {
417
+ case (name, AppliedType (tpe, (ref : TypeParamRef ) :: _)) if isSyntheticEvidence(name.show) =>
418
+ Some (ref.paramName -> tpe)
419
+ case _ => None
420
+ }
421
+
368
422
val typeParams = denot.info match
369
- case poly : PolyType => poly.paramNames.zip(poly.paramInfos).map { case (x, y) => x.show + y.show }
423
+ case poly : PolyType =>
424
+ val tparams = poly.paramNames.zip(poly.paramInfos)
425
+ tparams.map { (name, info) =>
426
+ evidenceParams.find((evidenceName : TypeName , _ : Type ) => name == evidenceName).flatMap {
427
+ case (_, tparam) => tparam.show.split('.' ).lastOption
428
+ } match {
429
+ case Some (evidenceTypeName) => s " ${name.show}: ${evidenceTypeName}"
430
+ case None => name.show + info.show
431
+ }
432
+ }
370
433
case _ => Nil
371
434
val (name, returnType) =
372
435
if (symbol.isConstructor) then
@@ -400,10 +463,11 @@ object Signatures {
400
463
* where _$n is only present for synthetic product extractors such as case classes.
401
464
* In rest of cases signature skips them resulting in pattern (T1, T2, T3, Tn)
402
465
*
403
- * @param denot Unapply denotation
466
+ * @param denot Unapply denotation
404
467
* @param paramNames Parameter names for unapply final result type.
405
- * It non empty only when unapply returns synthetic product as for case classes.
468
+ * It non empty only when unapply returns synthetic product as for case classes.
406
469
* @param paramTypes Parameter types for unapply final result type.
470
+ *
407
471
* @return Signature if paramTypes is non empty, None otherwise
408
472
*/
409
473
private def toUnapplySignature (denot : SingleDenotation , paramNames : List [Name ], paramTypes : List [Type ])(using Context ): Option [Signature ] =
@@ -425,17 +489,17 @@ object Signatures {
425
489
* `countParams` should be 3. It also takes into considerations unapplied arguments so for `foo(1)(3)`
426
490
* we will still get 3, as first application `foo(1)` takes 2 parameters with currently only 1 applied.
427
491
*
428
- * @param tree The tree to inspect.
429
- * @param denot Denotation of function we are trying to apply
492
+ * @param tree The tree to inspect.
493
+ * @param denot Denotation of function we are trying to apply
430
494
* @param alreadyCurried Number of subsequent Apply trees before current tree
495
+ *
431
496
* @return The number of parameters that are passed.
432
497
*/
433
498
private def countParams (tree : tpd.Tree , denot : SingleDenotation , alreadyCurried : Int = 0 )(using Context ): Int =
434
- tree match {
499
+ tree match
435
500
case Apply (fun, params) =>
436
501
countParams(fun, denot, alreadyCurried + 1 ) + denot.symbol.paramSymss(alreadyCurried).length
437
502
case _ => 0
438
- }
439
503
440
504
/**
441
505
* Inspect `err` to determine, if it is an error related to application of an overloaded
@@ -447,6 +511,7 @@ object Signatures {
447
511
*
448
512
* @param err The error message to inspect.
449
513
* @param params The parameters that were given at the call site.
514
+ *
450
515
* @return A pair composed of the index of the best alternative (0 if no alternatives
451
516
* were found), and the list of alternatives.
452
517
*/
@@ -460,20 +525,18 @@ object Signatures {
460
525
// If the user writes `foo(bar, <cursor>)`, the typer will insert a synthetic
461
526
// `null` parameter: `foo(bar, null)`. This may influence what's the "best"
462
527
// alternative, so we discard it.
463
- val userParams = params match {
528
+ val userParams = params match
464
529
case xs :+ (nul @ Literal (Constant (null ))) if nul.span.isZeroExtent => xs
465
530
case _ => params
466
- }
467
531
val userParamsTypes = userParams.map(_.tpe)
468
532
469
533
// Assign a score to each alternative (how many parameters are correct so far), and
470
534
// use that to determine what is the current active signature.
471
535
val alternativesScores = alternatives.map { alt =>
472
- alt.info.stripPoly match {
536
+ alt.info.stripPoly match
473
537
case tpe : MethodType =>
474
538
userParamsTypes.zip(tpe.paramInfos).takeWhile{ case (t0, t1) => t0 <:< t1 }.size
475
539
case _ => 0
476
- }
477
540
}
478
541
val bestAlternative =
479
542
if (alternativesScores.isEmpty) 0
0 commit comments