@@ -54,7 +54,7 @@ class PcInlayHintsProvider(
5454 val pos = driver.sourcePosition(params)
5555
5656 def provide (): List [InlayHint ] =
57- val deepFolder = DeepFolder [InlayHints ](collectDecorations)
57+ val deepFolder = PcCollector . DeepFolderWithParent [InlayHints ](collectDecorations)
5858 Interactive
5959 .pathTo(driver.openedTrees(uri), pos)(using driver.currentCtx)
6060 .headOption
@@ -68,11 +68,23 @@ class PcInlayHintsProvider(
6868 def collectDecorations (
6969 inlayHints : InlayHints ,
7070 tree : Tree ,
71+ parent : Option [Tree ]
7172 ): 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+ 7284 tree match
7385 case ImplicitConversion (symbol, range) =>
7486 val adjusted = adjustPos(range)
75- inlayHints
87+ firstPassHints
7688 .add(
7789 adjusted.startPos.toLsp,
7890 labelPart(symbol, symbol.decodedName) :: LabelPart (" (" ) :: Nil ,
@@ -84,35 +96,35 @@ class PcInlayHintsProvider(
8496 InlayHintKind .Parameter ,
8597 )
8698 case ImplicitParameters (trees, pos) =>
87- inlayHints .add(
99+ firstPassHints .add(
88100 adjustPos(pos).toLsp,
89101 ImplicitParameters .partsFromImplicitArgs(trees).map((label, maybeSymbol) =>
90102 maybeSymbol match
91103 case Some (symbol) => labelPart(symbol, label)
92104 case None => LabelPart (label)
93105 ),
94- InlayHintKind .Parameter
106+ InlayHintKind .Parameter ,
95107 )
96108 case ValueOf (label, pos) =>
97- inlayHints .add(
109+ firstPassHints .add(
98110 adjustPos(pos).toLsp,
99111 LabelPart (" (" ) :: LabelPart (label) :: List (LabelPart (" )" )),
100112 InlayHintKind .Parameter ,
101113 )
102114 case TypeParameters (tpes, pos, sel)
103115 if ! syntheticTupleApply(sel) =>
104116 val label = tpes.map(toLabelParts(_, pos)).separated(" [" , " , " , " ]" )
105- inlayHints .add(
117+ firstPassHints .add(
106118 adjustPos(pos).endPos.toLsp,
107119 label,
108120 InlayHintKind .Type ,
109121 )
110122 case InferredType (tpe, pos, defTree)
111123 if ! isErrorTpe(tpe) =>
112124 val adjustedPos = adjustPos(pos).endPos
113- if inlayHints .containsDef(adjustedPos.start) then inlayHints
125+ if firstPassHints .containsDef(adjustedPos.start) then firstPassHints
114126 else
115- inlayHints
127+ firstPassHints
116128 .add(
117129 adjustedPos.toLsp,
118130 LabelPart (" : " ) :: toLabelParts(tpe, pos),
@@ -138,7 +150,7 @@ class PcInlayHintsProvider(
138150 pos.withStart(pos.start + 1 )
139151
140152
141- args.foldLeft(inlayHints ) {
153+ args.foldLeft(firstPassHints ) {
142154 case (ih, (name, pos0, isByName)) =>
143155 val pos = adjustPos(pos0)
144156 val isBlock = isBlockParam(pos)
@@ -158,7 +170,7 @@ class PcInlayHintsProvider(
158170 )
159171 else ih
160172 }
161- case _ => inlayHints
173+ case _ => firstPassHints
162174
163175 private def toLabelParts (
164176 tpe : Type ,
@@ -491,3 +503,55 @@ object Parameters:
491503 case _ => None
492504 else None
493505end 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