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
1 change: 1 addition & 0 deletions metals-bench/src/main/scala/bench/InlayHintsBench.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class InlayHintsBench extends PcBenchmark {
true,
true,
true,
true,
false,
)
pc.inlayHints(pcParams).get().asScala.toList
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,7 @@ class Compilers(
typeParameters = options.typeParameters,
byNameParameters = options.byNameParameters,
hintsInPatternMatch = options.hintsInPatternMatch,
namedParameters = options.namedParameters,
)

pc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ case class InlayHintsOptions(options: Map[InlayHintsOption, Boolean])
options.getOrElse(InlayHintsOption.TypeParameters, false)
def byNameParameters: Boolean =
options.getOrElse(InlayHintsOption.ByNameParameters, false)
def namedParameters: Boolean =
options.getOrElse(InlayHintsOption.NamedParameters, false)
def hintsInPatternMatch: Boolean =
options.getOrElse(InlayHintsOption.HintsInPatternMatch, false)
def areSyntheticsEnabled: Boolean = options.exists(_._2)
Expand All @@ -37,6 +39,7 @@ object InlayHintsOption {
case object ImplicitArguments extends InlayHintsOption
case object TypeParameters extends InlayHintsOption
case object ByNameParameters extends InlayHintsOption
case object NamedParameters extends InlayHintsOption
case object HintsInPatternMatch extends InlayHintsOption
def unapply(value: String): Option[InlayHintsOption] =
StringCase.kebabToCamel(value) match {
Expand All @@ -45,6 +48,7 @@ object InlayHintsOption {
case "implicitArguments" => Some(ImplicitArguments)
case "typeParameters" => Some(TypeParameters)
case "byNameParameters" => Some(ByNameParameters)
case "namedParameters" => Some(NamedParameters)
case "hintsInPatternMatch" => Some(HintsInPatternMatch)
case _ => None
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,26 @@ object UserConfiguration {
|shown in the hover.
|""".stripMargin,
),
UserConfigurationOption(
"inlay-hints.named-parameters.enable",
"false",
"false",
"Should display parameter names next to arguments",
"""|When this option is enabled, each method has an added parameter name next to its arguments
|displayed either as additional decorations if they are supported by the editor or
|shown in the hover.
|""".stripMargin,
),
UserConfigurationOption(
"inlay-hints.by-name-parameters.enable",
"false",
"false",
"Should display if a parameter is by-name at usage sites",
"""|When this option is enabled, each method that has by-name parameters has them
|displayed either as additional '=>' decorations if they are supported by the editor or
|shown in the hover.
|""".stripMargin,
),
UserConfigurationOption(
"inlay-hints.implicit-arguments.enable",
"false",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ default boolean byNameParameters() {
*/
boolean implicitConversions();

/**
* Response should contain decorations for named parameters.
*/
default boolean namedParameters(){
return false;
}

/**
* Response should contain decorations in pattern matches.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ case class CompilerInlayHintsParams(
implicitParameters: Boolean,
override val byNameParameters: Boolean,
implicitConversions: Boolean,
override val namedParameters: Boolean,
override val hintsInPatternMatch: Boolean
) extends InlayHintsParams {
override def uri(): URI = rangeParams.uri
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,16 @@ final class PcInlayHintsProvider(
def collectDecorations(
tree: Tree,
inlayHints: InlayHints
): InlayHints =
): InlayHints = {
tree match {
case NamedParameters(params) =>
params.foldLeft(inlayHints) { case (acc, (prefix, name, pos)) =>
acc.add(
adjustPos(pos).focusStart.toLsp,
LabelPart(name.toString() + " = " + prefix) :: Nil,
InlayHintKind.Parameter
)
}
case ImplicitConversion(symbol, range) =>
val adjusted = adjustPos(range)
inlayHints
Expand Down Expand Up @@ -96,6 +104,7 @@ final class PcInlayHintsProvider(
.addDefinition(adjustedPos.start)
case _ => inlayHints
}
}

def traverse(
acc: InlayHints,
Expand Down Expand Up @@ -150,6 +159,68 @@ final class PcInlayHintsProvider(
} else {
LabelPart(label, symbol = semanticdbSymbol(symbol))
}

object NamedParameters {
def unapply(tree: Tree): Option[List[(String, Name, Position)]] =
if (params.namedParameters()) {
tree match {
case Apply(fun: Select, args) if isNotInterestingApply(fun) =>
None
case Apply(TypeApply(fun: Select, _), args)
if isNotInterestingApply(fun) =>
None
case Apply(fun, args)
if isRealApply(fun) &&
/* We don't want to show named parameters for unapplies*/
args.exists(arg => arg.pos.isRange && arg.pos != fun.pos) &&
/* It's most likely a block argument, even if it's not it's
doubtful that anyone wants to see the named parameters */
!isSingleBlock(args) =>
val applyParams = fun.tpe.params
Some(args.zip(applyParams).collect {
case (arg, param)
if !isNamedArg(arg) && !isDefaultArgument(arg) =>
val prefix =
if (param.isByNameParam && params.byNameParameters()) "=> "
else ""
(prefix, param.name, arg.pos)
})
case _ => None
}
} else None

private def isDefaultArgument(arg: Tree) = {
arg.symbol != null && arg.symbol.isDefaultGetter
}
private def isNamedArg(arg: Tree) = {
def loop(i: Int): Boolean = {
if (text(i) == '=') true
else if (text(i).isWhitespace)
loop(i - 1)
else false
}
loop(arg.pos.start - 1)
}

private def isNotInterestingApply(sel: Select) =
isForComprehensionMethod(sel) || syntheticTupleApply(sel) ||
isInfix(sel, textStr) || sel.symbol.isSetter || sel.symbol.isJavaDefined

private def isSingleBlock(args: List[Tree]): Boolean =
args.size == 1 && args.forall {
case _: Block => true
case Typed(_: Block, _) => true
case _ => false
}

private def isNotStringContextApply(fun: Tree) =
fun.symbol.owner != definitions.StringContextClass && !fun.symbol.isMacro

private def isRealApply(fun: Tree) =
fun.pos.isRange && fun.symbol != null && !fun.symbol.isImplicit &&
isNotStringContextApply(fun) && fun.symbol.paramss.nonEmpty
}

object ImplicitConversion {
def unapply(tree: Tree): Option[(Symbol, Position)] =
if (params.implicitConversions())
Expand Down
4 changes: 3 additions & 1 deletion tests/cross/src/main/scala/tests/BaseInlayHintsSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class BaseInlayHintsSuite extends BasePCSuite {
base: String,
expected: String,
compat: Map[String, String] = Map.empty,
hintsInPatternMatch: Boolean = false
hintsInPatternMatch: Boolean = false,
namedParameters: Boolean = true
)(implicit location: Location): Unit =
test(name) {
def pkgWrap(text: String) =
Expand All @@ -37,6 +38,7 @@ class BaseInlayHintsSuite extends BasePCSuite {
true,
true,
true,
namedParameters,
hintsInPatternMatch
)

Expand Down
Loading