Skip to content

Commit 4c141e6

Browse files
authored
Backport latest Scala Presentation Improvements to 3.3.7 (#23945)
2 parents aa8ff7f + c072f7d commit 4c141e6

File tree

8 files changed

+502
-16
lines changed

8 files changed

+502
-16
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/src/main/dotty/tools/pc/completions/CompletionAffix.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ case class CompletionAffix(
5656
private def loopPrefix(prefixes: List[PrefixKind]): String =
5757
prefixes match
5858
case PrefixKind.New :: tail => "new " + loopPrefix(tail)
59+
case PrefixKind.Using :: tail => "using " + loopPrefix(tail)
5960
case _ => ""
6061

6162
/**
@@ -87,7 +88,7 @@ enum SuffixKind:
8788
case Brace, Bracket, Template, NoSuffix
8889

8990
enum PrefixKind:
90-
case New
91+
case New, Using
9192

9293
type Suffix = Affix[SuffixKind]
9394
type Prefix = Affix[PrefixKind]

presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,15 @@ class Completions(
7474
case tpe :: (appl: AppliedTypeTree) :: _ if appl.tpt == tpe => false
7575
case sel :: (funSel @ Select(fun, name)) :: (appl: GenericApply) :: _
7676
if appl.fun == funSel && sel == fun => false
77-
case _ => true)
77+
case _ => true) &&
78+
(adjustedPath match
79+
/* In case of `class X derives TC@@` we shouldn't add `[]`
80+
*/
81+
case Ident(_) :: (templ: untpd.DerivingTemplate) :: _ =>
82+
val pos = completionPos.toSourcePosition
83+
!templ.derived.exists(_.sourcePos.contains(pos))
84+
case _ => true
85+
)
7886

7987
private lazy val isNew: Boolean = Completion.isInNewContext(adjustedPath)
8088

@@ -193,13 +201,24 @@ class Completions(
193201
)
194202
end isAbstractType
195203

196-
private def findSuffix(symbol: Symbol): CompletionAffix =
204+
private def findSuffix(symbol: Symbol, adjustedPath: List[untpd.Tree]): CompletionAffix =
197205
CompletionAffix.empty
198206
.chain { suffix => // for [] suffix
199207
if shouldAddSuffix && symbol.info.typeParams.nonEmpty then
200208
suffix.withNewSuffixSnippet(Affix(SuffixKind.Bracket))
201209
else suffix
202210
}
211+
.chain{ suffix =>
212+
adjustedPath match
213+
case (ident: Ident) :: (app@Apply(_, List(arg))) :: _ =>
214+
app.symbol.info match
215+
case mt@MethodType(termNames) if app.symbol.paramSymss.last.exists(_.is(Given)) &&
216+
!text.substring(app.fun.span.start, arg.span.end).nn.contains("using") =>
217+
suffix.withNewPrefix(Affix(PrefixKind.Using))
218+
case _ => suffix
219+
case _ => suffix
220+
221+
}
203222
.chain { suffix => // for () suffix
204223
if shouldAddSuffix && symbol.is(Flags.Method) then
205224
val paramss = getParams(symbol)
@@ -273,7 +292,7 @@ class Completions(
273292
val existsApply = extraMethodDenots.exists(_.symbol.name == nme.apply)
274293

275294
extraMethodDenots.map { methodDenot =>
276-
val suffix = findSuffix(methodDenot.symbol)
295+
val suffix = findSuffix(methodDenot.symbol, adjustedPath)
277296
val affix = if methodDenot.symbol.isConstructor && existsApply then
278297
adjustedPath match
279298
case (select @ Select(qual, _)) :: _ =>
@@ -295,7 +314,7 @@ class Completions(
295314

296315
if skipOriginalDenot then extraCompletionValues
297316
else
298-
val suffix = findSuffix(denot.symbol)
317+
val suffix = findSuffix(denot.symbol, adjustedPath)
299318
val name = undoBacktick(label)
300319
val denotCompletionValue = toCompletionValue(name, denot, suffix)
301320
denotCompletionValue :: extraCompletionValues

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,

presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,65 @@ class CompletionArgSuite extends BaseCompletionSuite:
238238
""
239239
)
240240

241+
@Test def `using` =
242+
checkEdit(
243+
s"""|def hello(using String): Unit = ???
244+
|@main def main1(): Unit =
245+
| val str = "hello"
246+
| hello(st@@)
247+
|""".stripMargin,
248+
s"""|def hello(using String): Unit = ???
249+
|@main def main1(): Unit =
250+
| val str = "hello"
251+
| hello(using str)
252+
|""".stripMargin,
253+
assertSingleItem = false)
254+
255+
@Test def `using2` =
256+
checkEdit(
257+
s"""|def hello(using String): Unit = ???
258+
|@main def main1(): Unit =
259+
| val str = "hello"
260+
| hello(using st@@)
261+
|""".stripMargin,
262+
s"""|def hello(using String): Unit = ???
263+
|@main def main1(): Unit =
264+
| val str = "hello"
265+
| hello(using str)
266+
|""".stripMargin,
267+
assertSingleItem = false)
268+
269+
@Test def `using3` =
270+
checkEdit(
271+
s"""|def hello(using String, Int): Unit = ???
272+
|@main def main1(): Unit =
273+
| val str = "hello"
274+
| val int = 4
275+
| hello(str, in@@)
276+
|""".stripMargin,
277+
s"""|def hello(using String, Int): Unit = ???
278+
|@main def main1(): Unit =
279+
| val str = "hello"
280+
| val int = 4
281+
| hello(str, int)
282+
|""".stripMargin,
283+
assertSingleItem = false)
284+
285+
@Test def `using4` =
286+
checkEdit(
287+
s"""|def hello(name: String)(using String): Unit = ???
288+
|@main def main1(): Unit =
289+
| val str = "hello"
290+
| hello("name")(str@@)
291+
|""".stripMargin,
292+
s"""|def hello(name: String)(using String): Unit = ???
293+
|@main def main1(): Unit =
294+
| val str = "hello"
295+
| hello("name")(using str)
296+
|""".stripMargin,
297+
assertSingleItem = false
298+
)
299+
241300
@Test def `default-args` =
242301
check(
243302
s"""|object Main {

presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2241,3 +2241,11 @@ class CompletionSuite extends BaseCompletionSuite:
22412241
|""".stripMargin,
22422242
"asTerm: Term"
22432243
)
2244+
2245+
@Test def `derives-no-square-brackets` =
2246+
check(
2247+
"""
2248+
|case class Miau(y: Int) derives Ordering, CanEqu@@
2249+
|""".stripMargin,
2250+
"CanEqual scala"
2251+
)

0 commit comments

Comments
 (0)