@@ -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 adjustBlockParameterPosOpt (pos : SourcePosition ): Option [ 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 )
@@ -126,16 +128,51 @@ class PcInlayHintsProvider(
126
128
val endsWithBrace = text.lift(end).contains('}' )
127
129
128
130
if startsWithBrace && endsWithBrace then
129
- adjusted.withStart(start + 1 )
131
+ Some ( adjusted.withStart(start + 1 ) )
130
132
else
131
- adjusted
133
+ None
134
+
135
+ def adjustParameterPos (pos : SourcePosition ): SourcePosition =
136
+ adjustBlockParameterPosOpt(pos) match
137
+ case Some (adjusted) => adjusted
138
+ case None => adjustPos(pos)
139
+
140
+ def isNamedParameter (pos : SourcePosition ): Boolean =
141
+ val adjusted = adjustPos(pos)
142
+ val start = text.indexWhere(! _.isWhitespace, adjusted.start)
143
+ val end = text.lastIndexWhere(! _.isWhitespace, adjusted.end - 1 )
144
+ text.slice(start, end).contains('=' )
145
+
146
+ val namedParams = NamedParameters .unapply(tree).getOrElse(Nil ).collect {
147
+ // We don't want to show parameter names for block parameters or named parameters
148
+ case (name, pos) if adjustBlockParameterPosOpt(pos).isEmpty && ! isNamedParameter(pos) => (name, pos)
149
+ }
150
+ val byNameParams = ByNameParameters .unapply(tree).getOrElse(Nil )
151
+
152
+ val namedAndByNameInlayHints =
153
+ namedParams.collect {
154
+ case (name, pos) if byNameParams.contains(pos) =>
155
+ (name.toString() + " = => " , adjustParameterPos(pos))
156
+ }
157
+
158
+ val namedInlayHints =
159
+ namedParams.collect {
160
+ case (name, pos) if ! byNameParams.contains(pos) =>
161
+ (name.toString() + " = " , adjustParameterPos(pos))
162
+ }
163
+
164
+ val namedParamsPos = namedParams.map(_._2)
165
+ val byNameInlayHints =
166
+ byNameParams.collect {
167
+ case pos if ! namedParamsPos.contains(pos) =>
168
+ (" => " , adjustParameterPos(pos))
169
+ }
132
170
133
- byNameParams.foldLeft(inlayHints) {
134
- case (ih, pos) =>
135
- val adjusted = adjustByNameParameterPos(pos)
171
+ (namedAndByNameInlayHints ++ namedInlayHints ++ byNameInlayHints).foldLeft(inlayHints) {
172
+ case (ih, (labelStr, pos)) =>
136
173
ih.add(
137
- adjusted .startPos.toLsp,
138
- List (LabelPart (" => " )),
174
+ pos .startPos.toLsp,
175
+ List (LabelPart (labelStr )),
139
176
InlayHintKind .Parameter
140
177
)
141
178
}
@@ -414,16 +451,11 @@ end InferredType
414
451
415
452
object ByNameParameters :
416
453
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()){
454
+ if (params.byNameParameters()) {
421
455
tree match
422
- case Apply (TypeApply (sel : Select , _), _) if shouldSkipSelect(sel) =>
423
- None
424
- case Apply (sel : Select , _) if shouldSkipSelect(sel) =>
456
+ case FlattenApplies (sel : Select , args) if SkipRules .shouldSkipSelect(sel, args) =>
425
457
None
426
- case Apply (fun, args) =>
458
+ case Apply (fun, args) if ! SkipRules .shouldSkipInfix(fun, args) =>
427
459
val funTp = fun.typeOpt.widenTermRefExpr
428
460
val params = funTp.paramInfoss.flatten
429
461
Some (
@@ -436,3 +468,51 @@ object ByNameParameters:
436
468
case _ => None
437
469
} else None
438
470
end ByNameParameters
471
+
472
+ object NamedParameters :
473
+ def unapply (tree : Tree )(using params : InlayHintsParams , ctx : Context ): Option [List [(Name , SourcePosition )]] =
474
+ def isRealApply (tree : Tree ) =
475
+ ! tree.symbol.isOneOf(Flags .GivenOrImplicit ) && ! tree.span.isZeroExtent
476
+ if (params.namedParameters()){
477
+ tree match
478
+ case FlattenApplies (sel : Select , args) if SkipRules .shouldSkipSelect(sel, args) =>
479
+ None
480
+ case Apply (fun, args) if isRealApply(fun) && ! SkipRules .shouldSkipInfix(fun, args) =>
481
+ val funTp = fun.typeOpt.widenTermRefExpr
482
+ val params = funTp.paramNamess.flatten
483
+ Some (
484
+ args
485
+ .zip(params)
486
+ .collect {
487
+ case (arg, param) if ! arg.span.isZeroExtent => (param.fieldName, arg.sourcePos)
488
+ }
489
+ )
490
+ case _ => None
491
+ } else None
492
+ end NamedParameters
493
+
494
+ private object SkipRules :
495
+ def shouldSkipSelect (sel : Select , args : List [Tree ])(using Context ): Boolean =
496
+ isForComprehensionMethod(sel) || sel.symbol.name == nme.unapply || sel.isInfix
497
+
498
+ def shouldSkipInfix (fun : Tree , args : List [Tree ])(using Context ): Boolean =
499
+ val source = fun.source
500
+ if args.isEmpty then false
501
+ else
502
+ val isInfixExtensionMethod =
503
+ ! (fun.span.end until args.head.span.start)
504
+ .map(source.apply)
505
+ .contains('.' )
506
+ isInfixExtensionMethod && fun.symbol.is(Flags .ExtensionMethod )
507
+
508
+ private object FlattenApplies :
509
+ /*
510
+ * Extractor that strips away a (possibly nested) chain of `Apply` / `TypeApply`
511
+ * wrappers and returns the underlying function tree.
512
+ */
513
+ def unapply (tree : Tree ): (Tree , List [Tree ]) =
514
+ tree match
515
+ case Apply (FlattenApplies (fun, argss), args) => (fun, argss ++: args)
516
+ case TypeApply (FlattenApplies (fun, argss), args) => (fun, argss ++: args)
517
+ case t => (t, Nil )
518
+ end FlattenApplies
0 commit comments