Skip to content

Commit 5c641af

Browse files
Backport "change mock symbol search" to 3.5.2 (#21476)
Backports #21296 to the 3.5.2 branch. PR submitted by the release tooling. [skip ci]
2 parents 73a5cc1 + c520699 commit 5c641af

File tree

3 files changed

+113
-34
lines changed

3 files changed

+113
-34
lines changed

presentation-compiler/src/main/dotty/tools/pc/CompilerSearchVisitor.scala

+7-5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import dotty.tools.dotc.core.Contexts.*
1212
import dotty.tools.dotc.core.Flags
1313
import dotty.tools.dotc.core.Names.*
1414
import dotty.tools.dotc.core.Symbols.*
15+
import dotty.tools.pc.utils.InteractiveEnrichments.companion
1516

1617
class CompilerSearchVisitor(
1718
visitSymbol: Symbol => Boolean
@@ -91,11 +92,12 @@ class CompilerSearchVisitor(
9192
range: org.eclipse.lsp4j.Range
9293
): Int =
9394
val gsym = SemanticdbSymbols.inverseSemanticdbSymbol(symbol).headOption
94-
gsym
95-
.filter(isAccessible)
96-
.map(visitSymbol)
97-
.map(_ => 1)
98-
.getOrElse(0)
95+
val matching = for
96+
sym0 <- gsym.toList
97+
sym <- if sym0.companion.is(Flags.Synthetic) then List(sym0, sym0.companion) else List(sym0)
98+
if isAccessible(sym)
99+
yield visitSymbol(sym)
100+
matching.size
99101

100102
def shouldVisitPackage(pkg: String): Boolean =
101103
isAccessible(requiredPackage(normalizePackage(pkg)))

presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala

+10
Original file line numberDiff line numberDiff line change
@@ -937,3 +937,13 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite:
937937
|""".stripMargin,
938938
""
939939
)
940+
941+
@Test def `metals-i6593` =
942+
check(
943+
"""|package a:
944+
| class UniqueObject
945+
|package b:
946+
| val i = Uniq@@
947+
|""".stripMargin,
948+
"UniqueObject(): UniqueObject - a"
949+
)
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,63 @@
11
package dotty.tools.pc.utils
22

3+
import dotty.tools.dotc.ast.untpd.*
4+
import dotty.tools.dotc.core.Contexts.Context
5+
import dotty.tools.dotc.core.Flags
6+
import dotty.tools.dotc.interactive.InteractiveDriver
7+
import dotty.tools.pc.CompilerSearchVisitor
8+
import dotty.tools.pc.utils.InteractiveEnrichments.decoded
9+
310
import java.io.File
411
import java.nio.file.Paths
5-
612
import scala.collection.mutable
7-
import scala.meta.internal.metals.{
8-
CompilerVirtualFileParams,
9-
Fuzzy,
10-
WorkspaceSymbolQuery
11-
}
12-
import scala.meta.pc.SymbolSearchVisitor
1313
import scala.language.unsafeNulls
14+
import scala.meta.internal.metals.CompilerVirtualFileParams
15+
import scala.meta.internal.metals.Fuzzy
16+
import scala.meta.internal.metals.WorkspaceSymbolQuery
17+
import scala.meta.pc.SymbolSearchVisitor
1418

15-
import dotty.tools.dotc.core.Contexts.Context
16-
import dotty.tools.dotc.core.Symbols.*
17-
import dotty.tools.dotc.interactive.InteractiveDriver
18-
import dotty.tools.dotc.semanticdb.SemanticSymbolBuilder
19-
import dotty.tools.pc.CompilerSearchVisitor
19+
import TestingWorkspaceSearch.*
2020

2121
object TestingWorkspaceSearch:
2222
def empty: TestingWorkspaceSearch = new TestingWorkspaceSearch(Nil)
23+
class Disambiguator:
24+
val nameMap = mutable.Map[String, Int]()
25+
def methodPart(name: String) =
26+
val i = nameMap.getOrElse(name, 0)
27+
nameMap.put(name, i + 1)
28+
if i == 0 then "()."
29+
else s"(+$i)."
30+
31+
case class ParentSymbol(symbol: SearchSymbol, fileName: String):
32+
private val dis: Disambiguator = new Disambiguator
33+
private def isPackage = symbol.lastOption.exists(_.suffix == "/")
34+
private def isMethod = symbol.lastOption.exists(_.suffix.endsWith(")."))
35+
private def isInit = symbol.lastOption.exists(_.name == "<init>")
36+
private def filePackage = SymbolPart(fileName, "$package.")
37+
private def member(part: SymbolPart)=
38+
if isPackage then Some(symbol :+ filePackage :+ part)
39+
else if isMethod then
40+
if isInit then Some(symbol.dropRight(1) :+ part)
41+
else None
42+
else Some(symbol :+ part)
43+
def makeMethod(newPart: String) = member(SymbolPart(newPart, dis.methodPart(newPart)))
44+
def makeVal(newPart: String) =
45+
member(SymbolPart(newPart, "."))
46+
def makeTypeAlias(newPart: String) = member(SymbolPart(newPart, "#"))
47+
def makeType(newPart: String) = symbol :+ SymbolPart(newPart, "#")
48+
def makeTerm(newPart: String) = symbol :+ SymbolPart(newPart, ".")
49+
def makePackage(parts: List[String], isPackageObject: Boolean = false) =
50+
val suffix = if isPackageObject then "/package." else "/"
51+
parts match
52+
case "<empty>" :: Nil => List(SymbolPart("_empty_", suffix))
53+
case list if symbol.map(_.name) == List("_empty_") => list.map(SymbolPart(_, suffix))
54+
case list => symbol ++ list.map(SymbolPart(_, suffix))
55+
56+
object ParentSymbol:
57+
def empty(fileName: String) = ParentSymbol(Nil, fileName)
58+
59+
case class SymbolPart(name: String, suffix: String)
60+
type SearchSymbol = List[SymbolPart]
2361

2462
class TestingWorkspaceSearch(classpath: Seq[String]):
2563
val inputs: mutable.Map[String, String] = mutable.Map.empty[String, String]
@@ -30,8 +68,41 @@ class TestingWorkspaceSearch(classpath: Seq[String]):
3068
defaultFlags ++
3169
List("-classpath", classpath.mkString(File.pathSeparator))
3270

71+
private class SymbolCollector extends UntypedTreeAccumulator[List[Tree]]:
72+
override def apply(x: List[Tree], tree: Tree)(using Context): List[Tree] = tree :: x
73+
74+
private def newSymbol(tree: Tree, parent: ParentSymbol)(using Context): Option[SearchSymbol] =
75+
tree match
76+
case PackageDef(name, _) =>
77+
Some(parent.makePackage(namesFromSelect(name).reverse))
78+
case m @ ModuleDef(name, _) if m.mods.is(Flags.Package) =>
79+
Some(parent.makePackage(List(name.decoded), isPackageObject = true))
80+
case ModuleDef(name, _) =>
81+
Some(parent.makeTerm(name.decoded))
82+
case ValDef(name, _, _) =>
83+
parent.makeVal(name.decoded)
84+
case t @ TypeDef(name, _: Template) if !t.mods.is(Flags.Implicit) =>
85+
Some(parent.makeType(name.decoded))
86+
case TypeDef(name, _) =>
87+
parent.makeTypeAlias(name.decoded)
88+
case DefDef(name, _, _, _) =>
89+
parent.makeMethod(name.decoded)
90+
case _ => None
91+
92+
def traverse(acc: List[SearchSymbol], tree: Tree, parent: ParentSymbol)(using Context): List[SearchSymbol] =
93+
val symbol = newSymbol(tree, parent)
94+
val res = symbol.filter(_.lastOption.exists(_.suffix != "/")).map(_ :: acc).getOrElse(acc)
95+
val children = foldOver(Nil, tree).reverse
96+
val newParent = symbol.map(ParentSymbol(_, parent.fileName)).getOrElse(parent)
97+
children.foldLeft(res)((a, c) => traverse(a, c, newParent))
98+
3399
val driver = new InteractiveDriver(settings)
34100

101+
private def namesFromSelect(select: Tree)(using Context): List[String] =
102+
select match
103+
case Select(qual, name) => name.decoded :: namesFromSelect(qual)
104+
case Ident(name) => List(name.decoded)
105+
35106
def search(
36107
query: WorkspaceSymbolQuery,
37108
visitor: SymbolSearchVisitor,
@@ -41,21 +112,17 @@ class TestingWorkspaceSearch(classpath: Seq[String]):
41112

42113
visitor match
43114
case visitor: CompilerSearchVisitor =>
44-
inputs.map { (path, text) =>
45-
46-
val nioPath = Paths.get(path)
47-
val uri = nioPath.toUri()
48-
val symbols = DefSymbolCollector(driver, CompilerVirtualFileParams(uri, text)).namedDefSymbols
49-
50-
// We have to map symbol from this Context, to one in PresentationCompiler
51-
// To do it we are searching it with semanticdb symbol
52-
val semanticSymbolBuilder = SemanticSymbolBuilder()
53-
symbols
54-
.filter((symbol, _) => filter(symbol))
55-
.filter((_, name) => Fuzzy.matches(query.query, name))
56-
.map(symbol => semanticSymbolBuilder.symbolName(symbol._1))
57-
.map(
58-
visitor.visitWorkspaceSymbol(Paths.get(""), _, null, null)
59-
)
60-
}
115+
inputs.map: (path, text) =>
116+
val nio = Paths.get(path)
117+
val uri = nio.toUri()
118+
driver.run(uri, text)
119+
val run = driver.currentCtx.run
120+
val unit = run.units.head
121+
val symbols = SymbolCollector().traverse(Nil, unit.untpdTree, ParentSymbol.empty(nio.getFileName().toString().stripSuffix(".scala")))
122+
symbols.foreach: sym =>
123+
val name = sym.last.name
124+
if Fuzzy.matches(query.query, name)
125+
then
126+
val symbolsString = sym.map{ case SymbolPart(name, suffix) => name ++ suffix}.mkString
127+
visitor.visitWorkspaceSymbol(Paths.get(""), symbolsString, null, null)
61128
case _ =>

0 commit comments

Comments
 (0)