11package 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+
310import java .io .File
411import java .nio .file .Paths
5-
612import scala .collection .mutable
7- import scala .meta .internal .metals .{
8- CompilerVirtualFileParams ,
9- Fuzzy ,
10- WorkspaceSymbolQuery
11- }
12- import scala .meta .pc .SymbolSearchVisitor
1313import 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
2121object 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
2462class 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