@@ -54,7 +54,7 @@ class PcInlayHintsProvider(
54
54
val pos = driver.sourcePosition(params)
55
55
56
56
def provide (): List [InlayHint ] =
57
- val deepFolder = DeepFolder [InlayHints ](collectDecorations)
57
+ val deepFolder = PcCollector . DeepFolderWithParent [InlayHints ](collectDecorations)
58
58
Interactive
59
59
.pathTo(driver.openedTrees(uri), pos)(using driver.currentCtx)
60
60
.headOption
@@ -68,11 +68,23 @@ class PcInlayHintsProvider(
68
68
def collectDecorations (
69
69
inlayHints : InlayHints ,
70
70
tree : Tree ,
71
+ parent : Option [Tree ]
71
72
): InlayHints =
73
+ // XRay hints are not mutually exclusive with other hints, so they must be matched separately
74
+ val firstPassHints = (tree, parent) match {
75
+ case XRayModeHint (tpe, pos) =>
76
+ inlayHints.addToBlock(
77
+ adjustPos(pos).toLsp,
78
+ LabelPart (" : " ) :: toLabelParts(tpe, pos),
79
+ InlayHintKind .Type
80
+ )
81
+ case _ => inlayHints
82
+ }
83
+
72
84
tree match
73
85
case ImplicitConversion (symbol, range) =>
74
86
val adjusted = adjustPos(range)
75
- inlayHints
87
+ firstPassHints
76
88
.add(
77
89
adjusted.startPos.toLsp,
78
90
labelPart(symbol, symbol.decodedName) :: LabelPart (" (" ) :: Nil ,
@@ -84,35 +96,35 @@ class PcInlayHintsProvider(
84
96
InlayHintKind .Parameter ,
85
97
)
86
98
case ImplicitParameters (trees, pos) =>
87
- inlayHints .add(
99
+ firstPassHints .add(
88
100
adjustPos(pos).toLsp,
89
101
ImplicitParameters .partsFromImplicitArgs(trees).map((label, maybeSymbol) =>
90
102
maybeSymbol match
91
103
case Some (symbol) => labelPart(symbol, label)
92
104
case None => LabelPart (label)
93
105
),
94
- InlayHintKind .Parameter
106
+ InlayHintKind .Parameter ,
95
107
)
96
108
case ValueOf (label, pos) =>
97
- inlayHints .add(
109
+ firstPassHints .add(
98
110
adjustPos(pos).toLsp,
99
111
LabelPart (" (" ) :: LabelPart (label) :: List (LabelPart (" )" )),
100
112
InlayHintKind .Parameter ,
101
113
)
102
114
case TypeParameters (tpes, pos, sel)
103
115
if ! syntheticTupleApply(sel) =>
104
116
val label = tpes.map(toLabelParts(_, pos)).separated(" [" , " , " , " ]" )
105
- inlayHints .add(
117
+ firstPassHints .add(
106
118
adjustPos(pos).endPos.toLsp,
107
119
label,
108
120
InlayHintKind .Type ,
109
121
)
110
122
case InferredType (tpe, pos, defTree)
111
123
if ! isErrorTpe(tpe) =>
112
124
val adjustedPos = adjustPos(pos).endPos
113
- if inlayHints .containsDef(adjustedPos.start) then inlayHints
125
+ if firstPassHints .containsDef(adjustedPos.start) then firstPassHints
114
126
else
115
- inlayHints
127
+ firstPassHints
116
128
.add(
117
129
adjustedPos.toLsp,
118
130
LabelPart (" : " ) :: toLabelParts(tpe, pos),
@@ -138,7 +150,7 @@ class PcInlayHintsProvider(
138
150
pos.withStart(pos.start + 1 )
139
151
140
152
141
- args.foldLeft(inlayHints ) {
153
+ args.foldLeft(firstPassHints ) {
142
154
case (ih, (name, pos0, isByName)) =>
143
155
val pos = adjustPos(pos0)
144
156
val isBlock = isBlockParam(pos)
@@ -158,7 +170,7 @@ class PcInlayHintsProvider(
158
170
)
159
171
else ih
160
172
}
161
- case _ => inlayHints
173
+ case _ => firstPassHints
162
174
163
175
private def toLabelParts (
164
176
tpe : Type ,
@@ -491,3 +503,55 @@ object Parameters:
491
503
case _ => None
492
504
else None
493
505
end Parameters
506
+
507
+ object XRayModeHint :
508
+ def unapply (trees : (Tree , Option [Tree ]))(using params : InlayHintsParams , ctx : Context ): Option [(Type , SourcePosition )] =
509
+ if params.hintsXRayMode() then
510
+ val (tree, parent) = trees
511
+ val isParentApply = parent match
512
+ case Some (_ : Apply ) => true
513
+ case _ => false
514
+ val isParentOnSameLine = parent match
515
+ case Some (sel : Select ) if sel.isForComprehensionMethod => false
516
+ case Some (par) if par.sourcePos.exists && par.sourcePos.line == tree.sourcePos.line => true
517
+ case _ => false
518
+
519
+ tree match
520
+ /*
521
+ anotherTree
522
+ .innerSelect()
523
+ */
524
+ case a @ Apply (inner, _)
525
+ if inner.sourcePos.exists && ! isParentOnSameLine && ! isParentApply &&
526
+ endsInSimpleSelect(a) && isEndOfLine(tree.sourcePos) =>
527
+ Some ((a.tpe.widen.deepDealiasAndSimplify, tree.sourcePos))
528
+ /*
529
+ innerTree
530
+ .select
531
+ */
532
+ case select @ Select (innerTree, _)
533
+ if innerTree.sourcePos.exists && ! isParentOnSameLine && ! isParentApply &&
534
+ isEndOfLine(tree.sourcePos) =>
535
+ Some ((select.tpe.widen.deepDealiasAndSimplify, tree.sourcePos))
536
+ case _ => None
537
+ else None
538
+
539
+ @ tailrec
540
+ private def endsInSimpleSelect (ap : Tree )(using ctx : Context ): Boolean =
541
+ ap match
542
+ case Apply (sel : Select , _) =>
543
+ sel.name != nme.apply && ! isInfix(sel)
544
+ case Apply (TypeApply (sel : Select , _), _) =>
545
+ sel.name != nme.apply && ! isInfix(sel)
546
+ case Apply (innerTree @ Apply (_, _), _) =>
547
+ endsInSimpleSelect(innerTree)
548
+ case _ => false
549
+
550
+ private def isEndOfLine (pos : SourcePosition ): Boolean =
551
+ if pos.exists then
552
+ val source = pos.source
553
+ val end = pos.end
554
+ end >= source.length || source(end) == '\n ' || source(end) == '\r '
555
+ else false
556
+
557
+ end XRayModeHint
0 commit comments