Skip to content

Commit

Permalink
improvement: New inlay hints options
Browse files Browse the repository at this point in the history
Adds new user configuration for inlay hints, with a fallback to old settings.
Also adds separate option for inlay hints inside pattern match.
  • Loading branch information
jkciesluk committed Mar 20, 2024
1 parent d8475d9 commit 082c6bb
Show file tree
Hide file tree
Showing 25 changed files with 562 additions and 289 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,
false,
)
pc.inlayHints(pcParams).get().asScala.toList
}
Expand Down
11 changes: 6 additions & 5 deletions metals/src/main/scala/scala/meta/internal/metals/Compilers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -609,13 +609,14 @@ class Compilers(

val rangeParams =
CompilerRangeParamsUtils.fromPos(pos, token)
val options = userConfig().inlayHintsOptions
val pcParams = CompilerInlayHintsParams(
rangeParams,
typeParameters = userConfig().showInferredType.contains("true"),
inferredTypes = userConfig().showInferredType.contains("minimal") ||
userConfig().showInferredType.contains("true"),
implicitParameters = userConfig().showImplicitArguments,
implicitConversions = userConfig().showImplicitConversionsAndClasses,
inferredTypes = options.inferredType,
implicitParameters = options.implicitArguments,
implicitConversions = options.implicitConversions,
typeParameters = options.typeParameters,
hintsInPatternMatch = options.hintsInPatternMatch,
)

pc
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package scala.meta.internal.metals

case class InlayHintsOptions(options: Map[InlayHintsOption, Boolean])
extends AnyVal {
def inferredType: Boolean =
options.getOrElse(InlayHintsOption.InferredType, false)
def implicitConversions: Boolean =
options.getOrElse(InlayHintsOption.ImplicitConversions, false)
def implicitArguments: Boolean =
options.getOrElse(InlayHintsOption.ImplicitArguments, false)
def typeParameters: Boolean =
options.getOrElse(InlayHintsOption.TypeParameters, false)
def hintsInPatternMatch: Boolean =
options.getOrElse(InlayHintsOption.HintsInPatternMatch, false)
def areSyntheticsEnabled: Boolean = options.exists(_._2)
}

object InlayHintsOptions {
def all: InlayHintsOptions = InlayHintsOptions(
Map(
InlayHintsOption.InferredType -> true,
InlayHintsOption.ImplicitConversions -> true,
InlayHintsOption.ImplicitArguments -> true,
InlayHintsOption.TypeParameters -> true,
InlayHintsOption.HintsInPatternMatch -> true,
)
)
}

sealed trait InlayHintsOption
object InlayHintsOption {
case object InferredType extends InlayHintsOption
case object ImplicitConversions extends InlayHintsOption
case object ImplicitArguments extends InlayHintsOption
case object TypeParameters extends InlayHintsOption
case object HintsInPatternMatch extends InlayHintsOption
def unapply(value: String): Option[InlayHintsOption] = value match {
case "inferredTypes" => Some(InferredType)
case "implicitConversions" => Some(ImplicitConversions)
case "implicitArguments" => Some(ImplicitArguments)
case "typeParameters" => Some(TypeParameters)
case "hintsInPatternMatch" => Some(HintsInPatternMatch)
case _ => None
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -975,11 +975,7 @@ class MetalsLspService(
} else Future.successful(())

val resetDecorations =
if (
userConfig.showImplicitArguments != old.showImplicitArguments ||
userConfig.showImplicitConversionsAndClasses != old.showImplicitConversionsAndClasses ||
userConfig.showInferredType != old.showInferredType
) {
if (userConfig.inlayHintsOptions != old.inlayHintsOptions) {
languageClient.refreshInlayHints().asScala
} else Future.successful(())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import scala.util.Success
import scala.util.Try

import scala.meta.internal.jdk.CollectionConverters._
import scala.meta.internal.metals.JsonParser.XtensionSerializedAsOption
import scala.meta.internal.mtags.Symbol
import scala.meta.io.AbsolutePath
import scala.meta.pc.PresentationCompilerConfig
Expand Down Expand Up @@ -41,9 +42,7 @@ case class UserConfiguration(
bloopJvmProperties: Option[List[String]] = None,
ammoniteJvmProperties: Option[List[String]] = None,
superMethodLensesEnabled: Boolean = false,
showInferredType: Option[String] = None,
showImplicitArguments: Boolean = false,
showImplicitConversionsAndClasses: Boolean = false,
inlayHintsOptions: InlayHintsOptions = InlayHintsOptions(Map.empty),
enableStripMarginOnTypeFormatting: Boolean = true,
enableIndentOnPaste: Boolean = false,
enableSemanticHighlighting: Boolean = true,
Expand All @@ -67,12 +66,7 @@ case class UserConfiguration(

def usedJavaBinary(): Option[AbsolutePath] = JavaBinary.path(javaHome)

def areSyntheticsEnabled(): Boolean = {
val showInferredType = !this.showInferredType.contains(
"false"
) && this.showInferredType.nonEmpty
showImplicitArguments || showInferredType || showImplicitConversionsAndClasses
}
def areSyntheticsEnabled(): Boolean = inlayHintsOptions.areSyntheticsEnabled

def getCustomProjectRoot(workspace: AbsolutePath): Option[AbsolutePath] =
customProjectRoot
Expand Down Expand Up @@ -231,7 +225,7 @@ object UserConfiguration {
|""".stripMargin,
),
UserConfigurationOption(
"show-inferred-type",
"inferred-types",
"false",
"false",
"Should display type annotations for inferred types",
Expand All @@ -241,7 +235,7 @@ object UserConfiguration {
|""".stripMargin,
),
UserConfigurationOption(
"show-implicit-arguments",
"implicit-arguments",
"false",
"false",
"Should display implicit parameter at usage sites",
Expand All @@ -251,7 +245,7 @@ object UserConfiguration {
|""".stripMargin,
),
UserConfigurationOption(
"show-implicit-conversions-and-classes",
"implicit-conversions",
"false",
"false",
"Should display implicit conversion at usage sites",
Expand All @@ -260,6 +254,26 @@ object UserConfiguration {
|shown in the hover.
|""".stripMargin,
),
UserConfigurationOption(
"type-parameters",
"false",
"false",
"Should display type annotations for type parameters",
"""|When this option is enabled, each place when a type parameter is applied has it
|displayed either as additional decorations if they are supported by the editor or
|shown in the hover.
|""".stripMargin,
),
UserConfigurationOption(
"hints-in-pattern-match",
"false",
"false",
"Should display type annotations in pattern matches",
"""|When this option is enabled, each place when a type is inferred in a pattern match 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 @@ -496,6 +510,31 @@ object UserConfiguration {
},
)

def getInlayHints =
getKey(
"inlay-hints",
json,
{ value =>
Try {
for {
entry <- value.getAsJsonObject.entrySet().asScala.iterator
enable <- entry
.getValue()
.getAsJsonObject()
.getBooleanOption("enable")
} yield {
entry.getKey -> enable
}
}.fold(
_ => {
errors += s"json error: key 'inlayHints' should have be object with Boolean values but obtained $value"
None
},
entries => Some(entries.toMap),
).filter(_.nonEmpty)
},
)

val javaHome =
getStringKey("java-home")
val scalafmtConfigPath =
Expand Down Expand Up @@ -532,12 +571,35 @@ object UserConfiguration {
val bloopJvmProperties = getStringListKey("bloop-jvm-properties")
val superMethodLensesEnabled =
getBooleanKey("super-method-lenses-enabled").getOrElse(false)
val showInferredType =
getStringKey("show-inferred-type")
val showImplicitArguments =
getBooleanKey("show-implicit-arguments").getOrElse(false)
val showImplicitConversionsAndClasses =
getBooleanKey("show-implicit-conversions-and-classes").getOrElse(false)

// For old inlay hints settings
def inlayHintsOptionsFallback: Map[InlayHintsOption, Boolean] = {
val showInferredType =
getStringKey("show-inferred-type")
val inferredType = showInferredType.contains("true") ||
showInferredType.contains("minimal")
val typeParameters = showInferredType.contains("true")
val implicitArguments =
getBooleanKey("show-implicit-arguments").getOrElse(false)
val implicitConversionsAndClasses =
getBooleanKey("show-implicit-conversions-and-classes")
.getOrElse(false)
Map(
InlayHintsOption.InferredType -> inferredType,
InlayHintsOption.TypeParameters -> typeParameters,
InlayHintsOption.ImplicitArguments -> implicitArguments,
InlayHintsOption.ImplicitConversions -> implicitConversionsAndClasses,
)
}
val inlayHintsOptions =
InlayHintsOptions(getInlayHints match {
case Some(options) =>
options.collect { case (InlayHintsOption(key), value) =>
key -> value
}
case _ => inlayHintsOptionsFallback
})

val enableStripMarginOnTypeFormatting =
getBooleanKey("enable-strip-margin-on-type-formatting").getOrElse(true)
val enableIndentOnPaste =
Expand Down Expand Up @@ -584,7 +646,6 @@ object UserConfiguration {
}

val scalaCliLauncher = getStringKey("scala-cli-launcher")

val defaultBspToBuildTool =
getBooleanKey("default-bsp-to-build-tool").getOrElse(false)

Expand All @@ -606,9 +667,7 @@ object UserConfiguration {
bloopJvmProperties,
ammoniteProperties,
superMethodLensesEnabled,
showInferredType,
showImplicitArguments,
showImplicitConversionsAndClasses,
inlayHintsOptions,
enableStripMarginOnTypeFormatting,
enableIndentOnPaste,
enableSemanticHighlighting,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,11 @@ public interface InlayHintsParams extends RangeParams {
*/
boolean implicitConversions();

/**
* Response should contain decorations in pattern matches.
*/
default boolean hintsInPatternMatch() {
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 hintsInPatternMatch: Boolean
) extends InlayHintsParams {
override def uri(): URI = rangeParams.uri
override def text(): String = rangeParams.text
Expand Down
10 changes: 6 additions & 4 deletions mtags/src/main/scala-2/scala/meta/internal/pc/MetalsGlobal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1053,10 +1053,12 @@ class MetalsGlobal(
case Select(_, name: TermName) if infixNames(name) => false
case Select(This(_), _) => false
// is a select statement without a dot `qual.name`
case Select(qual, _) => {
val pos = qual.pos.end
pos < text.length() && text(pos) != '.'
}
case sel: Select if !sel.qualifier.pos.isOffset =>
val qualEnd = sel.qualifier.pos.end
val qualStart = sel.qualifier.pos.start
val nameStart = sel.namePosition.start
qualStart != nameStart && nameStart < text.length() &&
!text.slice(qualEnd, nameStart).contains(".")
case _ => false
}

Expand Down
Loading

0 comments on commit 082c6bb

Please sign in to comment.