1
1
package dotty .tools .pc .utils
2
2
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
+
3
10
import java .io .File
4
11
import java .nio .file .Paths
5
-
6
12
import scala .collection .mutable
7
- import scala .meta .internal .metals .{
8
- CompilerVirtualFileParams ,
9
- Fuzzy ,
10
- WorkspaceSymbolQuery
11
- }
12
- import scala .meta .pc .SymbolSearchVisitor
13
13
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
14
18
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 .*
20
20
21
21
object TestingWorkspaceSearch :
22
22
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 ]) =
50
+ parts match
51
+ case " <empty>" :: Nil => List (SymbolPart (" _empty_" , " /" ))
52
+ case list if symbol.map(_.name) == List (" _empty_" ) => list.map(SymbolPart (_, " /" ))
53
+ case list => symbol ++ list.map(SymbolPart (_, " /" ))
54
+
55
+ object ParentSymbol :
56
+ def empty (fileName : String ) = ParentSymbol (Nil , fileName)
57
+
58
+ case class SymbolPart (name : String , suffix : String )
59
+ type SearchSymbol = List [SymbolPart ]
23
60
24
61
class TestingWorkspaceSearch (classpath : Seq [String ]):
25
62
val inputs : mutable.Map [String , String ] = mutable.Map .empty[String , String ]
@@ -30,8 +67,39 @@ class TestingWorkspaceSearch(classpath: Seq[String]):
30
67
defaultFlags ++
31
68
List (" -classpath" , classpath.mkString(File .pathSeparator))
32
69
70
+ private class SymbolCollector extends UntypedTreeAccumulator [List [Tree ]]:
71
+ override def apply (x : List [Tree ], tree : Tree )(using Context ): List [Tree ] = tree :: x
72
+
73
+ private def newSymbol (tree : Tree , parent : ParentSymbol )(using Context ): Option [SearchSymbol ] =
74
+ tree match
75
+ case PackageDef (name, _) =>
76
+ Some (parent.makePackage(namesFromSelect(name).reverse))
77
+ case ModuleDef (name, _) =>
78
+ Some (parent.makeTerm(name.decoded))
79
+ case ValDef (name, _, _) =>
80
+ parent.makeVal(name.decoded)
81
+ case t @ TypeDef (name, _ : Template ) if ! t.mods.is(Flags .Implicit ) =>
82
+ Some (parent.makeType(name.decoded))
83
+ case TypeDef (name, _) =>
84
+ parent.makeTypeAlias(name.decoded)
85
+ case DefDef (name, _, _, _) =>
86
+ parent.makeMethod(name.decoded)
87
+ case _ => None
88
+
89
+ def traverse (acc : List [SearchSymbol ], tree : Tree , parent : ParentSymbol )(using Context ): List [SearchSymbol ] =
90
+ val symbol = newSymbol(tree, parent)
91
+ val res = symbol.filter(_.lastOption.exists(_.suffix != " /" )).map(_ :: acc).getOrElse(acc)
92
+ val children = foldOver(Nil , tree).reverse
93
+ val newParent = symbol.map(ParentSymbol (_, parent.fileName)).getOrElse(parent)
94
+ children.foldLeft(res)((a, c) => traverse(a, c, newParent))
95
+
33
96
val driver = new InteractiveDriver (settings)
34
97
98
+ private def namesFromSelect (select : Tree )(using Context ): List [String ] =
99
+ select match
100
+ case Select (qual, name) => name.decoded :: namesFromSelect(qual)
101
+ case Ident (name) => List (name.decoded)
102
+
35
103
def search (
36
104
query : WorkspaceSymbolQuery ,
37
105
visitor : SymbolSearchVisitor ,
@@ -41,21 +109,17 @@ class TestingWorkspaceSearch(classpath: Seq[String]):
41
109
42
110
visitor match
43
111
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
- }
112
+ inputs.map: (path, text) =>
113
+ val nio = Paths .get(path)
114
+ val uri = nio.toUri()
115
+ driver.run(uri, text)
116
+ val run = driver.currentCtx.run
117
+ val unit = run.units.head
118
+ val symbols = SymbolCollector ().traverse(Nil , unit.untpdTree, ParentSymbol .empty(nio.getFileName().toString().stripSuffix(" .scala" )))
119
+ symbols.foreach: sym =>
120
+ val name = sym.last.name
121
+ if Fuzzy .matches(query.query, name)
122
+ then
123
+ val symbolsString = sym.map{ case SymbolPart (name, suffix) => name ++ suffix}.mkString
124
+ visitor.visitWorkspaceSymbol(Paths .get(" " ), symbolsString, null , null )
61
125
case _ =>
0 commit comments