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 ], 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 ]
23
61
24
62
class TestingWorkspaceSearch (classpath : Seq [String ]):
25
63
val inputs : mutable.Map [String , String ] = mutable.Map .empty[String , String ]
@@ -30,8 +68,41 @@ class TestingWorkspaceSearch(classpath: Seq[String]):
30
68
defaultFlags ++
31
69
List (" -classpath" , classpath.mkString(File .pathSeparator))
32
70
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
+
33
99
val driver = new InteractiveDriver (settings)
34
100
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
+
35
106
def search (
36
107
query : WorkspaceSymbolQuery ,
37
108
visitor : SymbolSearchVisitor ,
@@ -41,21 +112,17 @@ class TestingWorkspaceSearch(classpath: Seq[String]):
41
112
42
113
visitor match
43
114
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 )
61
128
case _ =>
0 commit comments