Skip to content

Commit c762f7b

Browse files
committed
improvement: New inlay hints options
Adds new user configuration for inlay hints, with a fallback to old settings. Also adds separate option for inlay hints inside pattern match.
1 parent d8475d9 commit c762f7b

File tree

25 files changed

+561
-289
lines changed

25 files changed

+561
-289
lines changed

metals-bench/src/main/scala/bench/InlayHintsBench.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class InlayHintsBench extends PcBenchmark {
9494
true,
9595
true,
9696
true,
97+
false,
9798
)
9899
pc.inlayHints(pcParams).get().asScala.toList
99100
}

metals/src/main/scala/scala/meta/internal/metals/Compilers.scala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -609,13 +609,14 @@ class Compilers(
609609

610610
val rangeParams =
611611
CompilerRangeParamsUtils.fromPos(pos, token)
612+
val options = userConfig().inlayHintsOptions
612613
val pcParams = CompilerInlayHintsParams(
613614
rangeParams,
614-
typeParameters = userConfig().showInferredType.contains("true"),
615-
inferredTypes = userConfig().showInferredType.contains("minimal") ||
616-
userConfig().showInferredType.contains("true"),
617-
implicitParameters = userConfig().showImplicitArguments,
618-
implicitConversions = userConfig().showImplicitConversionsAndClasses,
615+
inferredTypes = options.inferredType,
616+
implicitParameters = options.implicitArguments,
617+
implicitConversions = options.implicitConversions,
618+
typeParameters = options.typeParameters,
619+
hintsInPatternMatch = options.hintsInPatternMatch,
619620
)
620621

621622
pc
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package scala.meta.internal.metals
2+
3+
case class InlayHintsOptions(options: Map[InlayHintsOption, Boolean])
4+
extends AnyVal {
5+
def inferredType: Boolean =
6+
options.getOrElse(InlayHintsOption.InferredType, false)
7+
def implicitConversions: Boolean =
8+
options.getOrElse(InlayHintsOption.ImplicitConversions, false)
9+
def implicitArguments: Boolean =
10+
options.getOrElse(InlayHintsOption.ImplicitArguments, false)
11+
def typeParameters: Boolean =
12+
options.getOrElse(InlayHintsOption.TypeParameters, false)
13+
def hintsInPatternMatch: Boolean =
14+
options.getOrElse(InlayHintsOption.HintsInPatternMatch, false)
15+
def areSyntheticsEnabled: Boolean = options.exists(_._2)
16+
}
17+
18+
object InlayHintsOptions {
19+
def all: InlayHintsOptions = InlayHintsOptions(
20+
Map(
21+
InlayHintsOption.InferredType -> true,
22+
InlayHintsOption.ImplicitConversions -> true,
23+
InlayHintsOption.ImplicitArguments -> true,
24+
InlayHintsOption.TypeParameters -> true,
25+
InlayHintsOption.HintsInPatternMatch -> true,
26+
)
27+
)
28+
}
29+
30+
sealed trait InlayHintsOption
31+
object InlayHintsOption {
32+
case object InferredType extends InlayHintsOption
33+
case object ImplicitConversions extends InlayHintsOption
34+
case object ImplicitArguments extends InlayHintsOption
35+
case object TypeParameters extends InlayHintsOption
36+
case object HintsInPatternMatch extends InlayHintsOption
37+
def unapply(value: String): Option[InlayHintsOption] = value match {
38+
case "inferredTypes" => Some(InferredType)
39+
case "implicitConversions" => Some(ImplicitConversions)
40+
case "implicitArguments" => Some(ImplicitArguments)
41+
case "typeParameters" => Some(TypeParameters)
42+
case "hintsInPatternMatch" => Some(HintsInPatternMatch)
43+
case _ => None
44+
}
45+
46+
}

metals/src/main/scala/scala/meta/internal/metals/MetalsLspService.scala

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -975,11 +975,7 @@ class MetalsLspService(
975975
} else Future.successful(())
976976

977977
val resetDecorations =
978-
if (
979-
userConfig.showImplicitArguments != old.showImplicitArguments ||
980-
userConfig.showImplicitConversionsAndClasses != old.showImplicitConversionsAndClasses ||
981-
userConfig.showInferredType != old.showInferredType
982-
) {
978+
if (userConfig.inlayHintsOptions != old.inlayHintsOptions) {
983979
languageClient.refreshInlayHints().asScala
984980
} else Future.successful(())
985981

metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala

Lines changed: 81 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import scala.util.Success
88
import scala.util.Try
99

1010
import scala.meta.internal.jdk.CollectionConverters._
11+
import scala.meta.internal.metals.JsonParser.XtensionSerializedAsOption
1112
import scala.meta.internal.mtags.Symbol
1213
import scala.meta.io.AbsolutePath
1314
import scala.meta.pc.PresentationCompilerConfig
@@ -41,9 +42,7 @@ case class UserConfiguration(
4142
bloopJvmProperties: Option[List[String]] = None,
4243
ammoniteJvmProperties: Option[List[String]] = None,
4344
superMethodLensesEnabled: Boolean = false,
44-
showInferredType: Option[String] = None,
45-
showImplicitArguments: Boolean = false,
46-
showImplicitConversionsAndClasses: Boolean = false,
45+
inlayHintsOptions: InlayHintsOptions = InlayHintsOptions(Map.empty),
4746
enableStripMarginOnTypeFormatting: Boolean = true,
4847
enableIndentOnPaste: Boolean = false,
4948
enableSemanticHighlighting: Boolean = true,
@@ -67,12 +66,7 @@ case class UserConfiguration(
6766

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

70-
def areSyntheticsEnabled(): Boolean = {
71-
val showInferredType = !this.showInferredType.contains(
72-
"false"
73-
) && this.showInferredType.nonEmpty
74-
showImplicitArguments || showInferredType || showImplicitConversionsAndClasses
75-
}
69+
def areSyntheticsEnabled(): Boolean = inlayHintsOptions.areSyntheticsEnabled
7670

7771
def getCustomProjectRoot(workspace: AbsolutePath): Option[AbsolutePath] =
7872
customProjectRoot
@@ -231,7 +225,7 @@ object UserConfiguration {
231225
|""".stripMargin,
232226
),
233227
UserConfigurationOption(
234-
"show-inferred-type",
228+
"inferred-types",
235229
"false",
236230
"false",
237231
"Should display type annotations for inferred types",
@@ -241,7 +235,7 @@ object UserConfiguration {
241235
|""".stripMargin,
242236
),
243237
UserConfigurationOption(
244-
"show-implicit-arguments",
238+
"implicit-arguments",
245239
"false",
246240
"false",
247241
"Should display implicit parameter at usage sites",
@@ -251,7 +245,7 @@ object UserConfiguration {
251245
|""".stripMargin,
252246
),
253247
UserConfigurationOption(
254-
"show-implicit-conversions-and-classes",
248+
"implicit-conversions",
255249
"false",
256250
"false",
257251
"Should display implicit conversion at usage sites",
@@ -260,6 +254,26 @@ object UserConfiguration {
260254
|shown in the hover.
261255
|""".stripMargin,
262256
),
257+
UserConfigurationOption(
258+
"type-parameters",
259+
"false",
260+
"false",
261+
"Should display type annotations for type parameters",
262+
"""|When this option is enabled, each place when a type parameter is applied has it
263+
|displayed either as additional decorations if they are supported by the editor or
264+
|shown in the hover.
265+
|""".stripMargin,
266+
),
267+
UserConfigurationOption(
268+
"hints-in-pattern-match",
269+
"false",
270+
"false",
271+
"Should display type annotations in pattern matches",
272+
"""|When this option is enabled, each place when a type is inferred in a pattern match has it
273+
|displayed either as additional decorations if they are supported by the editor or
274+
|shown in the hover.
275+
|""".stripMargin,
276+
),
263277
UserConfigurationOption(
264278
"enable-semantic-highlighting",
265279
"true",
@@ -496,6 +510,31 @@ object UserConfiguration {
496510
},
497511
)
498512

513+
def getInlayHints =
514+
getKey(
515+
"inlay-hints",
516+
json,
517+
{ value =>
518+
Try {
519+
for {
520+
entry <- value.getAsJsonObject.entrySet().asScala.iterator
521+
enable <- entry
522+
.getValue()
523+
.getAsJsonObject()
524+
.getBooleanOption("enable")
525+
} yield {
526+
entry.getKey -> enable
527+
}
528+
}.fold(
529+
_ => {
530+
errors += s"json error: key 'inlayHints' should have be object with Boolean values but obtained $value"
531+
None
532+
},
533+
entries => Some(entries.toMap),
534+
).filter(_.nonEmpty)
535+
},
536+
)
537+
499538
val javaHome =
500539
getStringKey("java-home")
501540
val scalafmtConfigPath =
@@ -532,12 +571,35 @@ object UserConfiguration {
532571
val bloopJvmProperties = getStringListKey("bloop-jvm-properties")
533572
val superMethodLensesEnabled =
534573
getBooleanKey("super-method-lenses-enabled").getOrElse(false)
535-
val showInferredType =
536-
getStringKey("show-inferred-type")
537-
val showImplicitArguments =
538-
getBooleanKey("show-implicit-arguments").getOrElse(false)
539-
val showImplicitConversionsAndClasses =
540-
getBooleanKey("show-implicit-conversions-and-classes").getOrElse(false)
574+
575+
// For old inlay hints settings
576+
def inlayHintsOptionsFallback: Map[InlayHintsOption, Boolean] = {
577+
val showInferredType =
578+
getStringKey("show-inferred-type")
579+
val inferredType = showInferredType.contains("true") ||
580+
showInferredType.contains("minimal")
581+
val typeParameters = showInferredType.contains("true")
582+
val implicitArguments =
583+
getBooleanKey("show-implicit-arguments").getOrElse(false)
584+
val implicitConversionsAndClasses =
585+
getBooleanKey("show-implicit-conversions-and-classes")
586+
.getOrElse(false)
587+
Map(
588+
InlayHintsOption.InferredType -> inferredType,
589+
InlayHintsOption.TypeParameters -> typeParameters,
590+
InlayHintsOption.ImplicitArguments -> implicitArguments,
591+
InlayHintsOption.ImplicitConversions -> implicitConversionsAndClasses,
592+
)
593+
}
594+
val inlayHintsOptions =
595+
InlayHintsOptions(getInlayHints match {
596+
case Some(options) =>
597+
options.collect { case (InlayHintsOption(key), value) =>
598+
key -> value
599+
}
600+
case _ => inlayHintsOptionsFallback
601+
})
602+
541603
val enableStripMarginOnTypeFormatting =
542604
getBooleanKey("enable-strip-margin-on-type-formatting").getOrElse(true)
543605
val enableIndentOnPaste =
@@ -584,7 +646,6 @@ object UserConfiguration {
584646
}
585647

586648
val scalaCliLauncher = getStringKey("scala-cli-launcher")
587-
588649
val defaultBspToBuildTool =
589650
getBooleanKey("default-bsp-to-build-tool").getOrElse(false)
590651

@@ -606,9 +667,7 @@ object UserConfiguration {
606667
bloopJvmProperties,
607668
ammoniteProperties,
608669
superMethodLensesEnabled,
609-
showInferredType,
610-
showImplicitArguments,
611-
showImplicitConversionsAndClasses,
670+
inlayHintsOptions,
612671
enableStripMarginOnTypeFormatting,
613672
enableIndentOnPaste,
614673
enableSemanticHighlighting,

mtags-interfaces/src/main/java/scala/meta/pc/InlayHintsParams.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,11 @@ public interface InlayHintsParams extends RangeParams {
2929
*/
3030
boolean implicitConversions();
3131

32+
/**
33+
* Response should contain decorations in pattern matches.
34+
*/
35+
default boolean hintsInPatternMatch() {
36+
return false;
37+
}
38+
3239
}

mtags-shared/src/main/scala/scala/meta/internal/metals/CompilerInlayHintsParams.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ case class CompilerInlayHintsParams(
1010
inferredTypes: Boolean,
1111
typeParameters: Boolean,
1212
implicitParameters: Boolean,
13-
implicitConversions: Boolean
13+
implicitConversions: Boolean,
14+
override val hintsInPatternMatch: Boolean
1415
) extends InlayHintsParams {
1516
override def uri(): URI = rangeParams.uri
1617
override def text(): String = rangeParams.text

mtags/src/main/scala-2/scala/meta/internal/pc/MetalsGlobal.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,10 +1053,12 @@ class MetalsGlobal(
10531053
case Select(_, name: TermName) if infixNames(name) => false
10541054
case Select(This(_), _) => false
10551055
// is a select statement without a dot `qual.name`
1056-
case Select(qual, _) => {
1057-
val pos = qual.pos.end
1058-
pos < text.length() && text(pos) != '.'
1059-
}
1056+
case sel: Select if !sel.qualifier.pos.isOffset =>
1057+
val qualEnd = sel.qualifier.pos.end
1058+
val qualStart = sel.qualifier.pos.start
1059+
val nameStart = sel.namePosition.start
1060+
qualStart != nameStart && nameStart < text.length() &&
1061+
!text.slice(qualEnd, nameStart).contains(".")
10601062
case _ => false
10611063
}
10621064

0 commit comments

Comments
 (0)