From 36fdbc5f6f695371fbc64cbcaac80c91f3fa5bc4 Mon Sep 17 00:00:00 2001 From: rochala Date: Mon, 7 Aug 2023 14:40:26 +0200 Subject: [PATCH] Update presentation compiler to mtags: a829a6a --- .../main/dotty/tools/pc/HoverProvider.scala | 27 +- .../tools/pc/completions/Completions.scala | 10 +- .../tools/pc/utils/MtagsEnrichments.scala | 9 +- .../tools/pc/tests/hover/HoverDocSuite.scala | 256 ++++++++++++++++++ .../tools/pc/tests/hover/HoverTypeSuite.scala | 78 ++++++ .../dotty/tools/pc/utils/MockEntries.scala | 4 +- 6 files changed, 372 insertions(+), 12 deletions(-) create mode 100644 presentation-compiler/test/dotty/tools/pc/tests/hover/HoverDocSuite.scala diff --git a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala index cdd4b273bdcc..6c2251988f65 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala @@ -159,23 +159,34 @@ object HoverProvider: printer: ShortenedTypePrinter )(using Context): ju.Optional[HoverSignature] = path match case SelectDynamicExtractor(sel, n, name) => - def findRefinement(tp: Type): ju.Optional[HoverSignature] = + def findRefinement(tp: Type): Option[HoverSignature] = tp match - case RefinedType(info, refName, tpe) if name == refName.toString() => + case RefinedType(_, refName, tpe) if name == refName.toString() => val tpeString = if n == nme.selectDynamic then s": ${printer.tpe(tpe.resultType)}" else printer.tpe(tpe) - ju.Optional.of( + + val valOrDef = + if n == nme.selectDynamic && !tpe.isInstanceOf[ExprType] + then "val" + else "def" + + Some( new ScalaHover( expressionType = Some(tpeString), - symbolSignature = Some(s"def $name$tpeString") + symbolSignature = Some(s"$valOrDef $name$tpeString"), ) ) - case RefinedType(info, _, _) => - findRefinement(info) - case _ => ju.Optional.empty() + case RefinedType(parent, _, _) => + findRefinement(parent) + case _ => None + + val refTpe = sel.tpe.metalsDealias match + case r: RefinedType => Some(r) + case t: (TermRef | TypeProxy) => Some(t.termSymbol.info.metalsDealias) + case _ => None - findRefinement(sel.tpe.termSymbol.info.dealias) + refTpe.flatMap(findRefinement).asJava case _ => ju.Optional.empty() diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala index 29fe756d3776..2a8d3116b8a5 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -75,10 +75,18 @@ class Completions( case Import def include(sym: Symbol)(using Context): Boolean = + def hasSyntheticCursorSuffix: Boolean = + if !sym.name.endsWith(Cursor.value) then false + else + val realNameLength = sym.decodedName.length - Cursor.value.length + sym.source == pos.source && + sym.span.start + realNameLength == pos.span.end + val generalExclude = isUninterestingSymbol(sym) || !isNotLocalForwardReference(sym) || - sym.isPackageObject + sym.isPackageObject || + hasSyntheticCursorSuffix def isWildcardParam(sym: Symbol) = if sym.isTerm && sym.owner.isAnonymousFunction then diff --git a/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala b/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala index 086eec0c86b4..ff081c779342 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala @@ -197,9 +197,16 @@ object MtagsEnrichments extends CommonMtagsEnrichments: else symbol ) val sym = toSemanticdbSymbol(symbol) + def parentSymbols = + if symbol.name == nme.apply && symbol.maybeOwner.is(ModuleClass) then + List( + symbol.maybeOwner, + symbol.maybeOwner.companion, + ).filter(_ != NoSymbol) ++ symbol.allOverriddenSymbols + else symbol.allOverriddenSymbols val documentation = search.documentation( sym, - () => symbol.allOverriddenSymbols.map(toSemanticdbSymbol).toList.asJava + () => parentSymbols.iterator.map(toSemanticdbSymbol).toList.asJava, ) if documentation.isPresent then Some(documentation.get()) else None diff --git a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverDocSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverDocSuite.scala new file mode 100644 index 000000000000..06c629467166 --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverDocSuite.scala @@ -0,0 +1,256 @@ +package dotty.tools.pc.tests.hover + +import dotty.tools.pc.base.BaseHoverSuite + +import org.junit.Test +import dotty.tools.pc.utils.MockEntries +import scala.meta.pc.SymbolDocumentation + +class HoverDocSuite extends BaseHoverSuite: + + override protected def mockEntries: MockEntries = new MockEntries: + override def documentations: Set[SymbolDocumentation] = Set( + ScalaMockDocumentation("java/lang/String#substring().", "substring", List(MockParam("beginIndex"))), + ScalaMockDocumentation("java/util/Collections#emptyList().", "emptyList"), + ScalaMockDocumentation("_empty_/Alpha.apply().", "apply", List(MockParam("x"))), + ScalaMockDocumentation("_empty_/Alpha#", "init", List(MockParam("x"))), + ScalaMockDocumentation("scala/collection/LinearSeqOps#headOption().", "headOption"), + ) + + @Test def `doc` = + check( + """object a { + | <> + |} + |""".stripMargin, + """|**Expression type**: + |```scala + |java.util.List[Int] + |``` + |**Symbol signature**: + |```scala + |final def emptyList[T](): java.util.List[T] + |``` + |Found documentation for java/util/Collections#emptyList(). + |""".stripMargin, + ) + + @Test def `doc-parent` = + check( + """|object a { + | <> + |} + |""".stripMargin, + // Assert that the docstring is extracted. + """|```scala + |override def headOption: Option[Int] + |``` + |Found documentation for scala/collection/LinearSeqOps#headOption(). + |""".stripMargin, + + ) + + @Test def `java-method` = + check( + """|object a { + | <<"".substri@@ng(1)>> + |} + """.stripMargin, + """|```scala + |def substring(beginIndex: Int): String + |``` + |Found documentation for java/lang/String#substring(). + |""".stripMargin + ) + + @Test def `object` = + check( + """| + |/** + | * Doc about object + | */ + |object Alpha { + | def apply(x: Int) = x + 1 + |} + | + |object Main { + | val x = <> + |} + |""".stripMargin, + """|```scala + |def apply(x: Int): Int + |``` + |Found documentation for _empty_/Alpha.apply(). + |""".stripMargin, + ) + + @Test def `object1` = + check( + """| + |/** + | * Doc about object + | */ + |object Alpha { + | /** + | * Doc about method + | */ + | def apply(x: Int) = x + 1 + |} + | + |object Main { + | val x = <> + |} + |""".stripMargin, + """|```scala + |def apply(x: Int): Int + |``` + |Found documentation for _empty_/Alpha.apply(). + |""".stripMargin, + ) + + @Test def `case-class` = + check( + """| + |/** + | * Doc about case class + | * + | */ + |case class Alpha(x: Int) + | + |/** + | * Doc about object + | */ + |object Alpha { + | /** + | * Doc about method + | */ + | def apply(x: Int) = x + 1 + |} + | + |object Main { + | val x = <> + |} + |""".stripMargin, + """|```scala + |def apply(x: Int): Int + |``` + |Found documentation for _empty_/Alpha.apply(). + |""".stripMargin, + ) + + @Test def `case-class1` = + check( + """| + |/** + | * Doc about case class + | * + | */ + |case class Alpha(x: Int) + | + |/** + | * Doc about object + | */ + |object Alpha { + | def apply(x: Int) = x + 1 + |} + | + |object Main { + | val x = <> + |} + |""".stripMargin, + """|```scala + |def apply(x: Int): Int + |``` + |Found documentation for _empty_/Alpha.apply(). + | + |""".stripMargin, + ) + + @Test def `case-class2` = + check( + """| + |/** + | * Doc about case class + | * + | */ + |case class Alpha(x: Int) + | + |object Alpha { + | def apply(x: Int) = x + 1 + |} + | + |object Main { + | val x = <> + |} + |""".stripMargin, + """|```scala + |def apply(x: Int): Int + |``` + |Found documentation for _empty_/Alpha.apply(). + |""".stripMargin, + ) + + @Test def `case-class3` = + check( + """| + |/** + | * Doc about case class + | * + | */ + |case class Alpha(x: Int) + | + | + |object Main { + | val x = <> + |} + |""".stripMargin, + """|```scala + |def apply(x: Int): Alpha + |``` + |Found documentation for _empty_/Alpha.apply(). + |""".stripMargin, + ) + + @Test def `class` = + check( + """| + |/** + | * Doc about class + | * + | */ + |class Alpha(x: Int) + | + |object Alpha { + | def apply(x: Int) = x + 1 + |} + | + |object Main { + | val x = <> + |} + |""".stripMargin, + """|```scala + |def apply(x: Int): Int + |``` + |Found documentation for _empty_/Alpha.apply(). + |""".stripMargin, + ) + + @Test def `universal-apply` = + check( + """| + |/** + | * Doc about class + | * + | */ + |class Alpha(x: Int) + | + |object Main { + | val x = <> + |} + |""".stripMargin, + """|```scala + |def this(x: Int): Alpha + |``` + |Found documentation for _empty_/Alpha# + |""".stripMargin, + ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTypeSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTypeSuite.scala index 927e5a4947c8..f370b9ab50d7 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTypeSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTypeSuite.scala @@ -277,3 +277,81 @@ class HoverTypeSuite extends BaseHoverSuite: """|def scalameta: String |""".stripMargin.hover ) + + @Test def `macro` = + check( + """| + |import scala.quoted.* + | + |def myMacroImpl(using Quotes) = + | import quotes.reflect.Ident + | def foo = ??? match + | case x: I@@dent => x + | + | def bar: Ident = foo + | + | ??? + | + |""".stripMargin, + """|type Ident: Ident + |""".stripMargin.hover, + ) + + @Test def `macro2` = + check( + """| + | + |import scala.quoted.* + | + |def myMacroImpl(using Quotes) = + | import quotes.reflect.Ident + | def foo = ??? match + | case x: Ident => x + | + | def bar: Ide@@nt = foo + | + | ??? + | + |""".stripMargin, + """|type Ident: Ident + |""".stripMargin.hover, + ) + + @Test def `nested-selectable` = + check( + """|trait Sel extends Selectable: + | def selectDynamic(name: String): Any = ??? + |val sel = (new Sel {}).asInstanceOf[Sel { val foo: Sel { def bar: Int } }] + |val bar = sel.foo.ba@@r + |""".stripMargin, + """|def bar: Int + |""".stripMargin.hover, + ) + + @Test def `nested-selectable2` = + check( + """|class SimpleSelectable(key : String, value: Any) extends Selectable: + | def selectDynamic(name: String): Any = + | if(name == key) value else ??? + | + |type Node[T] = SimpleSelectable { val child: T } + | + |val leaf = SimpleSelectable("child", ()).asInstanceOf[Node[Unit]] + |val node = SimpleSelectable("child", leaf).asInstanceOf[Node[Node[Unit]]] + | + |val k = node.child.ch@@ild + |""".stripMargin, + """|val child: Unit + |""".stripMargin.hover, + ) + + @Test def `very-nested-selectable` = + check( + """|trait Sel extends Selectable: + | def selectDynamic(name: String): Any = ??? + |val sel = (new Sel {}).asInstanceOf[Sel { val foo: Sel { val bar: Sel { val ddd: Int } } }] + |val bar = sel.foo.bar.dd@@d + |""".stripMargin, + """|val ddd: Int + |""".stripMargin.hover, + ) diff --git a/presentation-compiler/test/dotty/tools/pc/utils/MockEntries.scala b/presentation-compiler/test/dotty/tools/pc/utils/MockEntries.scala index 4131cf5ff4b7..05cd2cb8c124 100644 --- a/presentation-compiler/test/dotty/tools/pc/utils/MockEntries.scala +++ b/presentation-compiler/test/dotty/tools/pc/utils/MockEntries.scala @@ -54,8 +54,8 @@ abstract class MockEntries: def apply( symbol: String, displayName: String, - typeParameterNames: Seq[String], - paramNames: Seq[String] + typeParameterNames: Seq[String] = Seq(), + paramNames: Seq[String] = Seq() ) = ScalaSymbolDocumentation( symbol,