Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class PcInlayHintsProvider(
val pos = driver.sourcePosition(params)

def provide(): List[InlayHint] =
val deepFolder = DeepFolder[InlayHints](collectDecorations)
val deepFolder = PcCollector.DeepFolderWithParent[InlayHints](collectDecorations)
Interactive
.pathTo(driver.openedTrees(uri), pos)(using driver.currentCtx)
.headOption
Expand All @@ -68,11 +68,23 @@ class PcInlayHintsProvider(
def collectDecorations(
inlayHints: InlayHints,
tree: Tree,
parent: Option[Tree]
): InlayHints =
// XRay hints are not mutually exclusive with other hints, so they must be matched separately
val firstPassHints = (tree, parent) match {
case XRayModeHint(tpe, pos) =>
inlayHints.addToBlock(
adjustPos(pos).toLsp,
LabelPart(": ") :: toLabelParts(tpe, pos),
InlayHintKind.Type
)
case _ => inlayHints
}

tree match
case ImplicitConversion(symbol, range) =>
val adjusted = adjustPos(range)
inlayHints
firstPassHints
.add(
adjusted.startPos.toLsp,
labelPart(symbol, symbol.decodedName) :: LabelPart("(") :: Nil,
Expand All @@ -84,35 +96,35 @@ class PcInlayHintsProvider(
InlayHintKind.Parameter,
)
case ImplicitParameters(trees, pos) =>
inlayHints.add(
firstPassHints.add(
adjustPos(pos).toLsp,
ImplicitParameters.partsFromImplicitArgs(trees).map((label, maybeSymbol) =>
maybeSymbol match
case Some(symbol) => labelPart(symbol, label)
case None => LabelPart(label)
),
InlayHintKind.Parameter
InlayHintKind.Parameter,
)
case ValueOf(label, pos) =>
inlayHints.add(
firstPassHints.add(
adjustPos(pos).toLsp,
LabelPart("(") :: LabelPart(label) :: List(LabelPart(")")),
InlayHintKind.Parameter,
)
case TypeParameters(tpes, pos, sel)
if !syntheticTupleApply(sel) =>
val label = tpes.map(toLabelParts(_, pos)).separated("[", ", ", "]")
inlayHints.add(
firstPassHints.add(
adjustPos(pos).endPos.toLsp,
label,
InlayHintKind.Type,
)
case InferredType(tpe, pos, defTree)
if !isErrorTpe(tpe) =>
val adjustedPos = adjustPos(pos).endPos
if inlayHints.containsDef(adjustedPos.start) then inlayHints
if firstPassHints.containsDef(adjustedPos.start) then firstPassHints
else
inlayHints
firstPassHints
.add(
adjustedPos.toLsp,
LabelPart(": ") :: toLabelParts(tpe, pos),
Expand All @@ -138,7 +150,7 @@ class PcInlayHintsProvider(
pos.withStart(pos.start + 1)


args.foldLeft(inlayHints) {
args.foldLeft(firstPassHints) {
case (ih, (name, pos0, isByName)) =>
val pos = adjustPos(pos0)
val isBlock = isBlockParam(pos)
Expand All @@ -158,7 +170,7 @@ class PcInlayHintsProvider(
)
else ih
}
case _ => inlayHints
case _ => firstPassHints

private def toLabelParts(
tpe: Type,
Expand Down Expand Up @@ -491,3 +503,55 @@ object Parameters:
case _ => None
else None
end Parameters

object XRayModeHint:
def unapply(trees: (Tree, Option[Tree]))(using params: InlayHintsParams, ctx: Context): Option[(Type, SourcePosition)] =
if params.hintsXRayMode() then
val (tree, parent) = trees
val isParentApply = parent match
case Some(_: Apply) => true
case _ => false
val isParentOnSameLine = parent match
case Some(sel: Select) if sel.isForComprehensionMethod => false
case Some(par) if par.sourcePos.exists && par.sourcePos.line == tree.sourcePos.line => true
case _ => false

tree match
/*
anotherTree
.innerSelect()
*/
case a @ Apply(inner, _)
if inner.sourcePos.exists && !isParentOnSameLine && !isParentApply &&
endsInSimpleSelect(a) && isEndOfLine(tree.sourcePos) =>
Some((a.tpe.widen.deepDealiasAndSimplify, tree.sourcePos))
/*
innerTree
.select
*/
case select @ Select(innerTree, _)
if innerTree.sourcePos.exists && !isParentOnSameLine && !isParentApply &&
isEndOfLine(tree.sourcePos) =>
Some((select.tpe.widen.deepDealiasAndSimplify, tree.sourcePos))
case _ => None
else None

@tailrec
private def endsInSimpleSelect(ap: Tree)(using ctx: Context): Boolean =
ap match
case Apply(sel: Select, _) =>
sel.name != nme.apply && !isInfix(sel)
case Apply(TypeApply(sel: Select, _), _) =>
sel.name != nme.apply && !isInfix(sel)
case Apply(innerTree @ Apply(_, _), _) =>
endsInSimpleSelect(innerTree)
case _ => false

private def isEndOfLine(pos: SourcePosition): Boolean =
if pos.exists then
val source = pos.source
val end = pos.end
end >= source.length || source(end) == '\n' || source(end) == '\r'
else false

end XRayModeHint
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ case class CompletionAffix(
private def loopPrefix(prefixes: List[PrefixKind]): String =
prefixes match
case PrefixKind.New :: tail => "new " + loopPrefix(tail)
case PrefixKind.Using :: tail => "using " + loopPrefix(tail)
case _ => ""

/**
Expand Down Expand Up @@ -87,7 +88,7 @@ enum SuffixKind:
case Brace, Bracket, Template, NoSuffix

enum PrefixKind:
case New
case New, Using

type Suffix = Affix[SuffixKind]
type Prefix = Affix[PrefixKind]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,15 @@ class Completions(
case tpe :: (appl: AppliedTypeTree) :: _ if appl.tpt == tpe => false
case sel :: (funSel @ Select(fun, name)) :: (appl: GenericApply) :: _
if appl.fun == funSel && sel == fun => false
case _ => true)
case _ => true) &&
(adjustedPath match
/* In case of `class X derives TC@@` we shouldn't add `[]`
*/
case Ident(_) :: (templ: untpd.DerivingTemplate) :: _ =>
val pos = completionPos.toSourcePosition
!templ.derived.exists(_.sourcePos.contains(pos))
case _ => true
)

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

Expand Down Expand Up @@ -193,13 +201,24 @@ class Completions(
)
end isAbstractType

private def findSuffix(symbol: Symbol): CompletionAffix =
private def findSuffix(symbol: Symbol, adjustedPath: List[untpd.Tree]): CompletionAffix =
CompletionAffix.empty
.chain { suffix => // for [] suffix
if shouldAddSuffix && symbol.info.typeParams.nonEmpty then
suffix.withNewSuffixSnippet(Affix(SuffixKind.Bracket))
else suffix
}
.chain{ suffix =>
adjustedPath match
case (ident: Ident) :: (app@Apply(_, List(arg))) :: _ =>
app.symbol.info match
case mt@MethodType(termNames) if app.symbol.paramSymss.last.exists(_.is(Given)) &&
!text.substring(app.fun.span.start, arg.span.end).nn.contains("using") =>
suffix.withNewPrefix(Affix(PrefixKind.Using))
case _ => suffix
case _ => suffix

}
.chain { suffix => // for () suffix
if shouldAddSuffix && symbol.is(Flags.Method) then
val paramss = getParams(symbol)
Expand Down Expand Up @@ -273,7 +292,7 @@ class Completions(
val existsApply = extraMethodDenots.exists(_.symbol.name == nme.apply)

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

if skipOriginalDenot then extraCompletionValues
else
val suffix = findSuffix(denot.symbol)
val suffix = findSuffix(denot.symbol, adjustedPath)
val name = undoBacktick(label)
val denotCompletionValue = toCompletionValue(name, denot, suffix)
denotCompletionValue :: extraCompletionValues
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class BaseInlayHintsSuite extends BasePCSuite {
inferredTypes = true,
typeParameters = true,
implicitParameters = true,
hintsXRayMode = true,
byNameParameters = true,
implicitConversions = true,
namedParameters = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,65 @@ class CompletionArgSuite extends BaseCompletionSuite:
""
)

@Test def `using` =
checkEdit(
s"""|def hello(using String): Unit = ???
|@main def main1(): Unit =
| val str = "hello"
| hello(st@@)
|""".stripMargin,
s"""|def hello(using String): Unit = ???
|@main def main1(): Unit =
| val str = "hello"
| hello(using str)
|""".stripMargin,
assertSingleItem = false)

@Test def `using2` =
checkEdit(
s"""|def hello(using String): Unit = ???
|@main def main1(): Unit =
| val str = "hello"
| hello(using st@@)
|""".stripMargin,
s"""|def hello(using String): Unit = ???
|@main def main1(): Unit =
| val str = "hello"
| hello(using str)
|""".stripMargin,
assertSingleItem = false)

@Test def `using3` =
checkEdit(
s"""|def hello(using String, Int): Unit = ???
|@main def main1(): Unit =
| val str = "hello"
| val int = 4
| hello(str, in@@)
|""".stripMargin,
s"""|def hello(using String, Int): Unit = ???
|@main def main1(): Unit =
| val str = "hello"
| val int = 4
| hello(str, int)
|""".stripMargin,
assertSingleItem = false)

@Test def `using4` =
checkEdit(
s"""|def hello(name: String)(using String): Unit = ???
|@main def main1(): Unit =
| val str = "hello"
| hello("name")(str@@)
|""".stripMargin,
s"""|def hello(name: String)(using String): Unit = ???
|@main def main1(): Unit =
| val str = "hello"
| hello("name")(using str)
|""".stripMargin,
assertSingleItem = false
)

@Test def `default-args` =
check(
s"""|object Main {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2241,3 +2241,11 @@ class CompletionSuite extends BaseCompletionSuite:
|""".stripMargin,
"asTerm: Term"
)

@Test def `derives-no-square-brackets` =
check(
"""
|case class Miau(y: Int) derives Ordering, CanEqu@@
|""".stripMargin,
"CanEqual scala"
)
Loading
Loading