From 340ecd4986a3fce3c88ece2968ea8c8b8434f8cc Mon Sep 17 00:00:00 2001 From: Jakub Ciesluk <323892@uwr.edu.pl> Date: Thu, 29 Feb 2024 10:33:14 +0100 Subject: [PATCH] improvement: Better inlay Hints for anonymous implicits --- .../internal/pc/PcInlayHintsProvider.scala | 26 +++- .../test/scala/tests/pc/InlayHintsSuite.scala | 125 ++++++++++++++++-- .../resources/expect/toplevels-scala3.expect | 2 +- .../inlayHints3/example/GivenAlias.scala | 6 +- .../inlayHints3/example/dotted.filename.scala | 2 +- .../example/dotted.filename.scala | 2 +- 6 files changed, 144 insertions(+), 19 deletions(-) diff --git a/mtags/src/main/scala-3/scala/meta/internal/pc/PcInlayHintsProvider.scala b/mtags/src/main/scala-3/scala/meta/internal/pc/PcInlayHintsProvider.scala index 818745746cf..31c26cae741 100644 --- a/mtags/src/main/scala-3/scala/meta/internal/pc/PcInlayHintsProvider.scala +++ b/mtags/src/main/scala-3/scala/meta/internal/pc/PcInlayHintsProvider.scala @@ -23,7 +23,6 @@ import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.util.Spans.Span import org.eclipse.lsp4j.InlayHint import org.eclipse.lsp4j.InlayHintKind -import org.eclipse.{lsp4j as l} class PcInlayHintsProvider( driver: InteractiveDriver, @@ -77,13 +76,24 @@ class PcInlayHintsProvider( if params.implicitParameters() => val labelParts = symbols.map(s => List(labelPart(s, s.decodedName))) val label = - if allImplicit then labelParts.separated("(", ", ", ")") + if allImplicit then labelParts.separated("(using ", ", ", ")") else labelParts.separated(", ") inlayHints.add( adjustPos(pos).toLsp, label, InlayHintKind.Parameter, ) + case AnonymousGivenDef(symbol, pos) if params.implicitParameters() => + val adjustedPos = adjustPos(pos) + if inlayHints.containsDef(adjustedPos.start) then inlayHints + else + inlayHints + .add( + adjustedPos.toLsp, + labelPart(symbol, symbol.decodedName) :: LabelPart(": ") :: Nil, + InlayHintKind.Parameter, + ) + .addDefinition(adjustedPos.start) case ValueOf(label, pos) if params.implicitParameters() => inlayHints.add( adjustPos(pos).toLsp, @@ -226,6 +236,18 @@ object ImplicitParameters: case _ => false end ImplicitParameters +object AnonymousGivenDef: + def unapply(tree: Tree)(using Context) = + tree match + case vd: ValDef + if vd.symbol.is(Flags.Given) && + vd.symbol.isTerm && + vd.namePos.span.isZeroExtent && + !vd.span.isZeroExtent => + Some(vd.symbol, vd.namePos) + case _ => None +end AnonymousGivenDef + object ValueOf: def unapply(tree: Tree)(using Context) = tree match diff --git a/tests/cross/src/test/scala/tests/pc/InlayHintsSuite.scala b/tests/cross/src/test/scala/tests/pc/InlayHintsSuite.scala index 6b3949e6d10..3246c7b46d0 100644 --- a/tests/cross/src/test/scala/tests/pc/InlayHintsSuite.scala +++ b/tests/cross/src/test/scala/tests/pc/InlayHintsSuite.scala @@ -9,7 +9,7 @@ class InlayHintsSuite extends BaseInlayHintsSuite { ) check( - "local", + "local".tag(IgnoreForScala3CompilerPC), """|object Main { | def foo() = { | implicit val imp: Int = 2 @@ -25,7 +25,18 @@ class InlayHintsSuite extends BaseInlayHintsSuite { | val x/*: Int<>*/ = addOne(1)/*(imp<<(3:17)>>)*/ | } |} - |""".stripMargin + |""".stripMargin, + compat = Map( + "3" -> + """|object Main { + | def foo()/*: Unit<>*/ = { + | implicit val imp: Int = 2 + | def addOne(x: Int)(implicit one: Int)/*: Int<>*/ = x + one + | val x/*: Int<>*/ = addOne(1)/*(using imp<<(3:17)>>)*/ + | } + |} + |""".stripMargin + ) ) check( @@ -65,7 +76,7 @@ class InlayHintsSuite extends BaseInlayHintsSuite { ) check( - "implicit-param", + "implicit-param".tag(IgnoreForScala3CompilerPC), """|case class User(name: String) |object Main { | implicit val imp: Int = 2 @@ -79,7 +90,17 @@ class InlayHintsSuite extends BaseInlayHintsSuite { | def addOne(x: Int)(implicit one: Int)/*: Int<>*/ = x + one | val x/*: Int<>*/ = addOne(1)/*(imp<<(3:15)>>)*/ |} - |""".stripMargin + |""".stripMargin, + compat = Map( + "3" -> + """|case class User(name: String) + |object Main { + | implicit val imp: Int = 2 + | def addOne(x: Int)(implicit one: Int)/*: Int<>*/ = x + one + | val x/*: Int<>*/ = addOne(1)/*(using imp<<(3:15)>>)*/ + |} + |""".stripMargin + ) ) check( @@ -99,7 +120,7 @@ class InlayHintsSuite extends BaseInlayHintsSuite { ) check( - "using-param".tag(IgnoreScala2), + "using-param".tag(IgnoreScala2.and(IgnoreForScala3CompilerPC)), """|case class User(name: String) |object Main { | implicit val imp: Int = 2 @@ -111,7 +132,7 @@ class InlayHintsSuite extends BaseInlayHintsSuite { |object Main { | implicit val imp: Int = 2 | def addOne(x: Int)(using one: Int)/*: Int<>*/ = x + one - | val x/*: Int<>*/ = addOne(1)/*(imp<<(3:15)>>)*/ + | val x/*: Int<>*/ = addOne(1)/*(using imp<<(3:15)>>)*/ |} |""".stripMargin ) @@ -145,7 +166,7 @@ class InlayHintsSuite extends BaseInlayHintsSuite { | def doX: Int |trait Yg: | def doY: String - |given (using Xg): Yg with + |given (using /*x$1<<(5:13)>>: */Xg): Yg with | def doY/*: String<>*/ = "7" |""".stripMargin ) @@ -545,7 +566,7 @@ class InlayHintsSuite extends BaseInlayHintsSuite { ) check( - "ord", + "ord".tag(IgnoreForScala3CompilerPC), """|object Main { | val ordered = "acb".sorted |} @@ -553,7 +574,14 @@ class InlayHintsSuite extends BaseInlayHintsSuite { """|object Main { | val ordered/*: String<>*/ = /*augmentString<>(*/"acb"/*)*/.sorted/*[Char<>]*//*(Char<>)*/ |} - |""".stripMargin + |""".stripMargin, + compat = Map( + "3" -> + """|object Main { + | val ordered/*: String<>*/ = /*augmentString<>(*/"acb"/*)*/.sorted/*[Char<>]*//*(using Char<>)*/ + |} + |""".stripMargin + ) ) check( @@ -599,7 +627,9 @@ class InlayHintsSuite extends BaseInlayHintsSuite { ) check( - "complex".tag(IgnoreScalaVersion.forLessThan("2.12.16")), + "complex".tag( + IgnoreScalaVersion.forLessThan("2.12.16").and(IgnoreForScala3CompilerPC) + ), """|object ScalatestMock { | class SRF | implicit val subjectRegistrationFunction: SRF = new SRF() @@ -673,7 +703,47 @@ class InlayHintsSuite extends BaseInlayHintsSuite { | def checkThing1[A](implicit ev: Eq[A])/*: Nothing<>*/ = ??? | def checkThing2[A](implicit ev: Eq[A], sem: Semigroup[A])/*: Nothing<>*/ = ??? |} - |""".stripMargin + |""".stripMargin, + compat = Map( + "3" -> + """|object ScalatestMock { + | class SRF + | implicit val subjectRegistrationFunction: SRF = new SRF() + | class Position + | implicit val here: Position = new Position() + | implicit class StringTestOps(name: String) { + | def should(right: => Unit)(implicit config: SRF): Unit = () + | def in(f: => Unit)(implicit pos: Position): Unit = () + | } + | implicit def instancesString: Eq[String] with Semigroup[String] = ??? + |} + | + |trait Eq[A] + |trait Semigroup[A] + | + |class DemoSpec { + | import ScalatestMock._ + | + | /*StringTestOps<<(6:17)>>(*/"foo"/*)*/ should { + | /*StringTestOps<<(6:17)>>(*/"checkThing1"/*)*/ in { + | checkThing1[String]/*(using instancesString<<(10:15)>>)*/ + | }/*(using here<<(5:15)>>)*/ + | /*StringTestOps<<(6:17)>>(*/"checkThing2"/*)*/ in { + | checkThing2[String]/*(using instancesString<<(10:15)>>, instancesString<<(10:15)>>)*/ + | }/*(using here<<(5:15)>>)*/ + | }/*(using subjectRegistrationFunction<<(3:15)>>)*/ + | + | /*StringTestOps<<(6:17)>>(*/"bar"/*)*/ should { + | /*StringTestOps<<(6:17)>>(*/"checkThing1"/*)*/ in { + | checkThing1[String]/*(using instancesString<<(10:15)>>)*/ + | }/*(using here<<(5:15)>>)*/ + | }/*(using subjectRegistrationFunction<<(3:15)>>)*/ + | + | def checkThing1[A](implicit ev: Eq[A])/*: Nothing<>*/ = ??? + | def checkThing2[A](implicit ev: Eq[A], sem: Semigroup[A])/*: Nothing<>*/ = ??? + |} + |""".stripMargin + ) ) check( @@ -744,4 +814,37 @@ class InlayHintsSuite extends BaseInlayHintsSuite { |case class ErrorMessage(error) |""".stripMargin ) + + // NOTE: We don't show inlayHints for anonymous given instances + check( + "anonymous-given".tag(IgnoreScala2.and(IgnoreForScala3CompilerPC)), + """|package example + | + |trait Ord[T]: + | def compare(x: T, y: T): Int + | + |given intOrd: Ord[Int] with + | def compare(x: Int, y: Int) = + | if x < y then -1 else if x > y then +1 else 0 + | + |given Ord[String] with + | def compare(x: String, y: String) = + | x.compare(y) + | + |""".stripMargin, + """|package example + | + |trait Ord[T]: + | def compare(x: T, y: T): Int + | + |given intOrd: Ord[Int] with + | def compare(x: Int, y: Int)/*: Int<>*/ = + | if x < y then -1 else if x > y then +1 else 0 + | + |given Ord[String] with + | def compare(x: String, y: String)/*: Int<>*/ = + | /*augmentString<>(*/x/*)*/.compare(y) + | + |""".stripMargin + ) } diff --git a/tests/unit/src/test/resources/expect/toplevels-scala3.expect b/tests/unit/src/test/resources/expect/toplevels-scala3.expect index eec168dc759..10b24a39e44 100644 --- a/tests/unit/src/test/resources/expect/toplevels-scala3.expect +++ b/tests/unit/src/test/resources/expect/toplevels-scala3.expect @@ -57,4 +57,4 @@ example/nested/LocalClass.scala -> example/nested/LocalClass# example/nested/package.scala -> example/PackageObjectSibling# example/nested/package.scala -> example/nested/package. example/package.scala -> example/package. -example/type/Backtick.scala -> example/type/Backtick# +example/type/Backtick.scala -> example/type/Backtick# \ No newline at end of file diff --git a/tests/unit/src/test/resources/inlayHints3/example/GivenAlias.scala b/tests/unit/src/test/resources/inlayHints3/example/GivenAlias.scala index 693c4e2af29..36606e7a4d6 100644 --- a/tests/unit/src/test/resources/inlayHints3/example/GivenAlias.scala +++ b/tests/unit/src/test/resources/inlayHints3/example/GivenAlias.scala @@ -8,7 +8,7 @@ given given_Char: Char = '?' given `given_Float`: Float = 3.0 given `* *` : Long = 5 -def method(using Int)/*: String<>*/ = "" +def method(using /*x$1<<(10:17)>>: */Int)/*: String<>*/ = "" object X: given Double = 4.0 @@ -28,7 +28,7 @@ trait Zg[T]: given Xg with def doX/*: Int<>*/ = 7 -given (using Xg): Yg with +given (using /*x$1<<(30:13)>>: */Xg): Yg with def doY/*: String<>*/ = "7" given [T]: Zg[T] with @@ -43,5 +43,5 @@ val f/*: Float<>*/ = given_Float val g/*: Long<>*/ = `* *` val i/*: Option<>[Int<>]*/ = X.of[Int] val x/*: given_Xg<<(27:6)>>.type*/ = given_Xg -val y/*: given_Yg<<(30:6)>>*/ = given_Yg/*(given_Xg<<(27:0)>>)*/ +val y/*: given_Yg<<(30:6)>>*/ = given_Yg/*(using given_Xg<<(27:0)>>)*/ val z/*: given_Zg_T<<(33:6)>>[String<>]*/ = given_Zg_T[String] \ No newline at end of file diff --git a/tests/unit/src/test/resources/inlayHints3/example/dotted.filename.scala b/tests/unit/src/test/resources/inlayHints3/example/dotted.filename.scala index 876bde134d4..a9d2a7fb4a0 100644 --- a/tests/unit/src/test/resources/inlayHints3/example/dotted.filename.scala +++ b/tests/unit/src/test/resources/inlayHints3/example/dotted.filename.scala @@ -1,3 +1,3 @@ package example -type Toplevel = Int +type Toplevel = Int \ No newline at end of file diff --git a/tests/unit/src/test/resources/semanticTokens3/example/dotted.filename.scala b/tests/unit/src/test/resources/semanticTokens3/example/dotted.filename.scala index b704249be2a..ab449098dcc 100644 --- a/tests/unit/src/test/resources/semanticTokens3/example/dotted.filename.scala +++ b/tests/unit/src/test/resources/semanticTokens3/example/dotted.filename.scala @@ -1,3 +1,3 @@ <>/*keyword*/ <>/*namespace*/ -<>/*keyword*/ <>/*type,definition*/ = <>/*class,abstract*/ +<>/*keyword*/ <>/*type,definition*/ = <>/*class,abstract*/ \ No newline at end of file