Skip to content

Commit

Permalink
improvement: Add separate option for decorations for evidence params
Browse files Browse the repository at this point in the history
  • Loading branch information
jkciesluk committed Mar 15, 2024
1 parent d8475d9 commit 07c82e5
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 53 deletions.
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 @@ -94,6 +94,7 @@ class InlayHintsBench extends PcBenchmark {
true,
true,
true,
true,
)
pc.inlayHints(pcParams).get().asScala.toList
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ class Compilers(
userConfig().showInferredType.contains("true"),
implicitParameters = userConfig().showImplicitArguments,
implicitConversions = userConfig().showImplicitConversionsAndClasses,
contextBounds = userConfig().showEvidenceParams,
)

pc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ case class UserConfiguration(
showInferredType: Option[String] = None,
showImplicitArguments: Boolean = false,
showImplicitConversionsAndClasses: Boolean = false,
showEvidenceParams: Boolean = false,
enableStripMarginOnTypeFormatting: Boolean = true,
enableIndentOnPaste: Boolean = false,
enableSemanticHighlighting: Boolean = true,
Expand Down Expand Up @@ -260,6 +261,16 @@ object UserConfiguration {
|shown in the hover.
|""".stripMargin,
),
UserConfigurationOption(
"show-evidence-params",
"false",
"false",
"Should display context bounds evidence parameters at usage sites",
"""|When this option is enabled, each place where a context bound is used has it
|displayed either as additional decorations if they are supported by the editor or
|shown in the hover.
|""".stripMargin,
),
UserConfigurationOption(
"enable-semantic-highlighting",
"true",
Expand Down Expand Up @@ -538,6 +549,8 @@ object UserConfiguration {
getBooleanKey("show-implicit-arguments").getOrElse(false)
val showImplicitConversionsAndClasses =
getBooleanKey("show-implicit-conversions-and-classes").getOrElse(false)
val showEvidenceParams =
getBooleanKey("show-evidence-params").getOrElse(false)
val enableStripMarginOnTypeFormatting =
getBooleanKey("enable-strip-margin-on-type-formatting").getOrElse(true)
val enableIndentOnPaste =
Expand Down Expand Up @@ -609,6 +622,7 @@ object UserConfiguration {
showInferredType,
showImplicitArguments,
showImplicitConversionsAndClasses,
showEvidenceParams,
enableStripMarginOnTypeFormatting,
enableIndentOnPaste,
enableSemanticHighlighting,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,8 @@ public interface InlayHintsParams extends RangeParams {
*/
boolean implicitConversions();

default boolean contextBounds() {
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ case class CompilerInlayHintsParams(
inferredTypes: Boolean,
typeParameters: Boolean,
implicitParameters: Boolean,
implicitConversions: Boolean
implicitConversions: Boolean,
override val contextBounds: Boolean
) extends InlayHintsParams {
override def uri(): URI = rangeParams.uri
override def text(): String = rangeParams.text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,24 @@ final class PcInlayHintsProvider(
LabelPart(")") :: Nil,
InlayHintKind.Parameter
)
case ImplicitParameters(symbols, pos, allImplicit)
if params.implicitParameters() =>
val labelParts = symbols.map(s => List(labelPart(s, s.decodedName)))
val label =
if (allImplicit) labelParts.separated("(", ", ", ")")
else labelParts.separated(", ")
inlayHints.add(
adjustPos(pos).focusEnd.toLsp,
label,
InlayHintKind.Parameter
)
case ImplicitParameters(args, pos, allImplicit)
if params.implicitParameters() || params.contextBounds() =>
val symbols = args.collect {
case Param(sym) if params.implicitParameters() => sym
case ContextBound(sym) if params.contextBounds() => sym
}
if (symbols.isEmpty) inlayHints
else {
val labelParts = symbols.map(s => List(labelPart(s, s.decodedName)))
val label =
if (allImplicit) labelParts.separated("(", ", ", ")")
else labelParts.separated(", ")
inlayHints.add(
adjustPos(pos).focusEnd.toLsp,
label,
InlayHintKind.Parameter
)
}
case ValueOf(label, pos) if params.implicitParameters() =>
inlayHints.add(
adjustPos(pos).focusEnd.toLsp,
Expand Down Expand Up @@ -160,24 +167,42 @@ final class PcInlayHintsProvider(
fun.pos.isOffset && fun.symbol != null && fun.symbol.isImplicit
}
object ImplicitParameters {
def unapply(tree: Tree): Option[(List[Symbol], Position, Boolean)] =
def unapply(
tree: Tree
): Option[(List[ImplicitParamSym], Position, Boolean)] =
tree match {
case Apply(_, args)
case Apply(fun, args)
if args.exists(isSyntheticArg) && !tree.pos.isOffset =>
val (implicitArgs, providedArgs) = args.partition(isSyntheticArg)
val allImplicit = providedArgs.isEmpty
val pos = providedArgs.lastOption.fold(tree.pos)(_.pos)
Some(
implicitArgs.map(_.symbol),
pos,
allImplicit
)
fun.tpe.widen match {
case mt: MethodType if mt.params.nonEmpty =>
val (implicitArgs0, providedArgs) =
args.zip(mt.params).partition { case (arg, _) =>
isSyntheticArg(arg)
}
val implicitArgs = implicitArgs0.map(ImplicitParamSym(_))
val allImplicit = providedArgs.isEmpty
val pos = providedArgs.lastOption.fold(tree.pos)(_._1.pos)
Some((implicitArgs, pos, allImplicit))
case _ => None
}

case _ => None
}

private def isSyntheticArg(arg: Tree): Boolean =
arg.pos.isOffset && arg.symbol != null && arg.symbol.isImplicit
object ImplicitParamSym {
def apply(argWithName: (Tree, Symbol)): ImplicitParamSym = {
val (arg, name) = argWithName
if (isContextBoundParam(name)) ContextBound(arg.symbol)
else Param(arg.symbol)
}
}
private def isContextBoundParam(sym: Symbol) =
sym.name.toString.startsWith(nme.EVIDENCE_PARAM_PREFIX)
}
sealed trait ImplicitParamSym
case class Param(sym: Symbol) extends ImplicitParamSym
case class ContextBound(sym: Symbol) extends ImplicitParamSym

object ValueOf {
def unapply(tree: Tree): Option[(String, Position)] = tree match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import java.nio.file.Paths

import scala.meta.internal.metals.ReportContext
import scala.meta.internal.mtags.MtagsEnrichments.*
import scala.meta.internal.pc.ImplicitParameters.ImplicitParamSym.*
import scala.meta.internal.pc.printer.MetalsPrinter
import scala.meta.internal.pc.printer.ShortenedNames
import scala.meta.pc.InlayHintsParams
Expand All @@ -13,6 +14,7 @@ import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.ast.tpd.*
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.Flags
import dotty.tools.dotc.core.Names.Name
import dotty.tools.dotc.core.StdNames.*
import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.core.Types.*
Expand Down Expand Up @@ -73,17 +75,23 @@ class PcInlayHintsProvider(
LabelPart(")") :: Nil,
InlayHintKind.Parameter,
)
case ImplicitParameters(symbols, pos, allImplicit)
if params.implicitParameters() =>
val labelParts = symbols.map(s => List(labelPart(s, s.decodedName)))
val label =
if allImplicit then labelParts.separated("(using ", ", ", ")")
else labelParts.separated(", ")
inlayHints.add(
adjustPos(pos).toLsp,
label,
InlayHintKind.Parameter,
)
case ImplicitParameters(args, pos, allImplicit)
if params.implicitParameters() || params.contextBounds() =>
val symbols = args.collect {
case Param(sym) if params.implicitParameters() => sym
case ContextBound(sym) if params.contextBounds() => sym
}
if symbols.isEmpty then inlayHints
else
val labelParts = symbols.map(s => List(labelPart(s, s.decodedName)))
val label =
if allImplicit then labelParts.separated("(using ", ", ", ")")
else labelParts.separated(", ")
inlayHints.add(
adjustPos(pos).toLsp,
label,
InlayHintKind.Parameter,
)
case ValueOf(label, pos) if params.implicitParameters() =>
inlayHints.add(
adjustPos(pos).toLsp,
Expand Down Expand Up @@ -210,23 +218,41 @@ object ImplicitConversion:
end ImplicitConversion

object ImplicitParameters:
def unapply(tree: Tree)(using Context) =
def unapply(tree: Tree)(using Context): Option[(List[ImplicitParamSym], SourcePosition, Boolean)] =
tree match
case Apply(fun, args)
if args.exists(isSyntheticArg) && !tree.sourcePos.span.isZeroExtent =>
val (implicitArgs, providedArgs) = args.partition(isSyntheticArg)
val allImplicit = providedArgs.isEmpty || providedArgs.forall {
case Ident(name) => name == nme.MISSING
case _ => false
fun.typeOpt.widen.paramNamess.headOption.map {paramNames =>
val (implicitArgs0, providedArgs) = args.zip(paramNames).partition((arg, _) => isSyntheticArg(arg))
val firstImplicitPos = implicitArgs0.head._1.sourcePos
val implicitArgs = implicitArgs0.map(ImplicitParamSym(_))
val allImplicit = providedArgs.isEmpty || providedArgs.forall {
case (Ident(name), _) => name == nme.MISSING
case _ => false
}
(implicitArgs, firstImplicitPos, allImplicit)
}
val pos = implicitArgs.head.sourcePos
Some(implicitArgs.map(_.symbol), pos, allImplicit)
case _ => None

private def isSyntheticArg(tree: Tree)(using Context) = tree match
case tree: Ident =>
tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit)
case _ => false

enum ImplicitParamSym:
case Param(sym: Symbol)
case ContextBound(sym: Symbol)

object ImplicitParamSym:
def apply(argWithName: (Tree, Name))(using Context) =
val (arg, name) = argWithName
if isContextBoundParam(name) then ContextBound(arg.symbol)
else Param(arg.symbol)
private def isContextBoundParam(name: Name) =
// In 3.1.3 NameKinds.ContextBoundParamName.separator is not available
name.toString.startsWith("evidence$")


end ImplicitParameters

object ValueOf:
Expand Down
16 changes: 11 additions & 5 deletions tests/cross/src/main/scala/tests/BaseInlayHintsSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ class BaseInlayHintsSuite extends BasePCSuite {
name: TestOptions,
base: String,
expected: String,
compat: Map[String, String] = Map.empty
compat: Map[String, String] = Map.empty,
showInferredType: Boolean = true,
showTypeArguments: Boolean = true,
showImplicitArguments: Boolean = true,
showImplicitConversions: Boolean = true,
showContextBounds: Boolean = true
)(implicit location: Location): Unit =
test(name) {
def pkgWrap(text: String) =
Expand All @@ -31,10 +36,11 @@ class BaseInlayHintsSuite extends BasePCSuite {
)
val pcParams = CompilerInlayHintsParams(
rangeParams,
true,
true,
true,
true
showInferredType,
showTypeArguments,
showImplicitArguments,
showImplicitConversions,
showContextBounds
)

val inlayHints = presentationCompiler
Expand Down
Loading

0 comments on commit 07c82e5

Please sign in to comment.