@@ -17,6 +17,7 @@ import scala.meta.pc.SymbolSearch
17
17
import dotty .tools .dotc .ast .tpd .*
18
18
import dotty .tools .dotc .core .Contexts .Context
19
19
import dotty .tools .dotc .core .Flags
20
+ import dotty .tools .dotc .core .Names .Name
20
21
import dotty .tools .dotc .core .StdNames .*
21
22
import dotty .tools .dotc .core .Symbols .*
22
23
import dotty .tools .dotc .core .Types .*
@@ -29,6 +30,7 @@ import dotty.tools.dotc.util.Spans.Span
29
30
import org .eclipse .lsp4j .InlayHint
30
31
import org .eclipse .lsp4j .InlayHintKind
31
32
import org .eclipse .{lsp4j as l }
33
+ import dotty .tools .dotc .core .NameOps .fieldName
32
34
33
35
class PcInlayHintsProvider (
34
36
driver : InteractiveDriver ,
@@ -116,8 +118,8 @@ class PcInlayHintsProvider(
116
118
InlayHintKind .Type ,
117
119
)
118
120
.addDefinition(adjustedPos.start)
119
- case ByNameParameters (byNameParams ) =>
120
- def adjustByNameParameterPos (pos : SourcePosition ): SourcePosition =
121
+ case NamedParameters (_) | ByNameParameters (_ ) =>
122
+ def adjustBlockParameterPos (pos : SourcePosition ): SourcePosition =
121
123
val adjusted = adjustPos(pos)
122
124
val start = text.indexWhere(! _.isWhitespace, adjusted.start)
123
125
val end = text.lastIndexWhere(! _.isWhitespace, adjusted.end - 1 )
@@ -130,12 +132,33 @@ class PcInlayHintsProvider(
130
132
else
131
133
adjusted
132
134
133
- byNameParams.foldLeft(inlayHints) {
134
- case (ih, pos) =>
135
- val adjusted = adjustByNameParameterPos(pos)
135
+ val namedParams = NamedParameters .unapply(tree).getOrElse(Nil )
136
+ val byNameParams = ByNameParameters .unapply(tree).getOrElse(Nil )
137
+
138
+ val namedAndByNameInlayHints =
139
+ namedParams.collect {
140
+ case (name, pos) if byNameParams.contains(pos) =>
141
+ (name.toString() + " = => " , adjustBlockParameterPos(pos))
142
+ }
143
+
144
+ val namedInlayHints =
145
+ namedParams.collect {
146
+ case (name, pos) if ! byNameParams.contains(pos) =>
147
+ (name.toString() + " = " , adjustBlockParameterPos(pos))
148
+ }
149
+
150
+ val namedParamsPos = namedParams.map(_._2)
151
+ val byNameInlayHints =
152
+ byNameParams.collect {
153
+ case pos if ! namedParamsPos.contains(pos) =>
154
+ (" => " , adjustBlockParameterPos(pos))
155
+ }
156
+
157
+ (namedAndByNameInlayHints ++ namedInlayHints ++ byNameInlayHints).foldLeft(inlayHints) {
158
+ case (ih, (labelStr, pos)) =>
136
159
ih.add(
137
- adjusted .startPos.toLsp,
138
- List (LabelPart (" => " )),
160
+ pos .startPos.toLsp,
161
+ List (LabelPart (labelStr )),
139
162
InlayHintKind .Parameter
140
163
)
141
164
}
@@ -414,16 +437,11 @@ end InferredType
414
437
415
438
object ByNameParameters :
416
439
def unapply (tree : Tree )(using params : InlayHintsParams , ctx : Context ): Option [List [SourcePosition ]] =
417
- def shouldSkipSelect (sel : Select ) =
418
- isForComprehensionMethod(sel) || sel.symbol.name == nme.unapply
419
-
420
- if (params.byNameParameters()){
440
+ if (params.byNameParameters()) {
421
441
tree match
422
- case Apply (TypeApply (sel : Select , _), _) if shouldSkipSelect(sel) =>
423
- None
424
- case Apply (sel : Select , _) if shouldSkipSelect(sel) =>
442
+ case FlattenApplies (sel : Select , args) if SkipRules .shouldSkipSelect(sel, args) =>
425
443
None
426
- case Apply (fun, args) =>
444
+ case Apply (fun, args) if ! SkipRules .shouldSkipInfix(fun, args) =>
427
445
val funTp = fun.typeOpt.widenTermRefExpr
428
446
val params = funTp.paramInfoss.flatten
429
447
Some (
@@ -436,3 +454,51 @@ object ByNameParameters:
436
454
case _ => None
437
455
} else None
438
456
end ByNameParameters
457
+
458
+ object NamedParameters :
459
+ def unapply (tree : Tree )(using params : InlayHintsParams , ctx : Context ): Option [List [(Name , SourcePosition )]] =
460
+ def isRealApply (tree : Tree ) =
461
+ ! tree.symbol.isOneOf(Flags .GivenOrImplicit ) && ! tree.span.isZeroExtent
462
+ if (params.namedParameters()){
463
+ tree match
464
+ case FlattenApplies (sel : Select , args) if SkipRules .shouldSkipSelect(sel, args) =>
465
+ None
466
+ case Apply (fun, args) if isRealApply(fun) && ! SkipRules .shouldSkipInfix(fun, args) =>
467
+ val funTp = fun.typeOpt.widenTermRefExpr
468
+ val params = funTp.paramNamess.flatten
469
+ Some (
470
+ args
471
+ .zip(params)
472
+ .collect {
473
+ case (tree, paramName) if ! tree.span.isZeroExtent => (paramName.fieldName, tree.sourcePos)
474
+ }
475
+ )
476
+ case _ => None
477
+ } else None
478
+ end NamedParameters
479
+
480
+ private object SkipRules :
481
+ def shouldSkipSelect (sel : Select , args : List [Tree ])(using Context ): Boolean =
482
+ isForComprehensionMethod(sel) || sel.symbol.name == nme.unapply || sel.isInfix
483
+
484
+ def shouldSkipInfix (fun : Tree , args : List [Tree ])(using Context ): Boolean =
485
+ val source = fun.source
486
+ if args.isEmpty then false
487
+ else
488
+ val isInfixExtensionMethod =
489
+ ! (fun.span.end until args.head.span.start)
490
+ .map(source.apply)
491
+ .contains('.' )
492
+ isInfixExtensionMethod && fun.symbol.is(Flags .ExtensionMethod )
493
+
494
+ private object FlattenApplies :
495
+ /*
496
+ * Extractor that strips away a (possibly nested) chain of `Apply` / `TypeApply`
497
+ * wrappers and returns the underlying function tree.
498
+ */
499
+ def unapply (tree : Tree ): (Tree , List [Tree ]) =
500
+ tree match
501
+ case Apply (FlattenApplies (fun, argss), args) => (fun, argss ++: args)
502
+ case TypeApply (FlattenApplies (fun, argss), args) => (fun, argss ++: args)
503
+ case t => (t, Nil )
504
+ end FlattenApplies
0 commit comments