Skip to content

Commit 1315eda

Browse files
Porting XRayModeHints (#23891)
Porting scalameta/metals#7639 Co-authored-by: Henry Parker <[email protected]>
1 parent e227128 commit 1315eda

File tree

4 files changed

+410
-11
lines changed

4 files changed

+410
-11
lines changed

presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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
493505
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

presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class BaseInlayHintsSuite extends BasePCSuite {
3434
inferredTypes = true,
3535
typeParameters = true,
3636
implicitParameters = true,
37+
hintsXRayMode = true,
3738
byNameParameters = true,
3839
implicitConversions = true,
3940
namedParameters = true,

0 commit comments

Comments
 (0)