Skip to content

Commit 2b84d7c

Browse files
authored
Merge pull request #5443 from dotty-staging/ide-workspace-symbols
Small improvements to `workspace/symbol`
2 parents 2ec79d1 + e86b1b8 commit 2b84d7c

File tree

5 files changed

+74
-27
lines changed

5 files changed

+74
-27
lines changed

compiler/src/dotty/tools/dotc/interactive/Interactive.scala

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import scala.collection._
88
import ast.{NavigateAST, Trees, tpd, untpd}
99
import core._, core.Decorators.{sourcePos => _, _}
1010
import Contexts._, Flags._, Names._, NameOps._, Symbols._, Trees._, Types._
11+
import transform.SymUtils.decorateSymbol
1112
import util.Positions._, util.SourceFile, util.SourcePosition
1213
import core.Denotations.SingleDenotation
1314
import NameKinds.SimpleNameKind
@@ -33,6 +34,7 @@ object Interactive {
3334
def isDefinitions: Boolean = (bits & definitions.bits) != 0
3435
def isLinkedClass: Boolean = (bits & linkedClass.bits) != 0
3536
def isImports: Boolean = (bits & imports.bits) != 0
37+
def isLocal: Boolean = (bits & local.bits) != 0
3638
}
3739

3840
/** The empty set */
@@ -59,6 +61,9 @@ object Interactive {
5961
/** Include imports in the results */
6062
val imports: Set = Set(1 << 5)
6163

64+
/** Include local symbols, inspect local trees */
65+
val local: Set = Set(1 << 6)
66+
6267
/** All the flags */
6368
val all: Set = Set(~0)
6469
}
@@ -164,40 +169,44 @@ object Interactive {
164169
else
165170
namedTrees(trees, include, matchSymbol(_, sym, include))
166171

167-
/** Find named trees with a non-empty position whose name contains `nameSubstring` in `trees`.
168-
*/
169-
def namedTrees(trees: List[SourceTree], nameSubstring: String)
170-
(implicit ctx: Context): List[SourceTree] = {
171-
val predicate: NameTree => Boolean = _.name.toString.contains(nameSubstring)
172-
namedTrees(trees, Include.empty, predicate)
173-
}
174-
175172
/** Find named trees with a non-empty position satisfying `treePredicate` in `trees`.
176173
*
177-
* @param includeReferences If true, include references and not just definitions
174+
* @param trees The trees to inspect.
175+
* @param include Whether to include references, definitions, etc.
176+
* @param treePredicate An additional predicate that the trees must match.
177+
* @return The trees with a non-empty position satisfying `treePredicate`.
178178
*/
179-
def namedTrees(trees: List[SourceTree], include: Include.Set, treePredicate: NameTree => Boolean)
180-
(implicit ctx: Context): List[SourceTree] = safely {
179+
def namedTrees(trees: List[SourceTree],
180+
include: Include.Set,
181+
treePredicate: NameTree => Boolean = util.common.alwaysTrue
182+
)(implicit ctx: Context): List[SourceTree] = safely {
181183
val buf = new mutable.ListBuffer[SourceTree]
182184

183185
def traverser(source: SourceFile) = {
184186
new untpd.TreeTraverser {
187+
private def handle(utree: untpd.NameTree): Unit = {
188+
val tree = utree.asInstanceOf[tpd.NameTree]
189+
if (tree.symbol.exists
190+
&& !tree.symbol.is(Synthetic)
191+
&& !tree.symbol.isPrimaryConstructor
192+
&& tree.pos.exists
193+
&& !tree.pos.isZeroExtent
194+
&& (include.isReferences || isDefinition(tree))
195+
&& treePredicate(tree))
196+
buf += SourceTree(tree, source)
197+
}
185198
override def traverse(tree: untpd.Tree)(implicit ctx: Context) = {
186199
tree match {
187200
case imp: untpd.Import if include.isImports && tree.hasType =>
188201
val tree = imp.asInstanceOf[tpd.Import]
189202
val selections = tpd.importSelections(tree)
190203
traverse(imp.expr)
191204
selections.foreach(traverse)
205+
case utree: untpd.ValOrDefDef if tree.hasType =>
206+
handle(utree)
207+
if (include.isLocal) traverseChildren(tree)
192208
case utree: untpd.NameTree if tree.hasType =>
193-
val tree = utree.asInstanceOf[tpd.NameTree]
194-
if (tree.symbol.exists
195-
&& !tree.symbol.is(Synthetic)
196-
&& tree.pos.exists
197-
&& !tree.pos.isZeroExtent
198-
&& (include.isReferences || isDefinition(tree))
199-
&& treePredicate(tree))
200-
buf += SourceTree(tree, source)
209+
handle(utree)
201210
traverseChildren(tree)
202211
case tree: untpd.Inlined =>
203212
traverse(tree.call)
@@ -228,8 +237,7 @@ object Interactive {
228237
)(implicit ctx: Context): List[SourceTree] = {
229238
val linkedSym = symbol.linkedClass
230239
val fullPredicate: NameTree => Boolean = tree =>
231-
( !tree.symbol.isPrimaryConstructor
232-
&& (includes.isDefinitions || !Interactive.isDefinition(tree))
240+
( (includes.isDefinitions || !Interactive.isDefinition(tree))
233241
&& ( Interactive.matchSymbol(tree, symbol, includes)
234242
|| ( includes.isLinkedClass
235243
&& linkedSym.exists
@@ -329,6 +337,7 @@ object Interactive {
329337
def findDefinitions(path: List[Tree], pos: SourcePosition, driver: InteractiveDriver)(implicit ctx: Context): List[SourceTree] = {
330338
enclosingSourceSymbols(path, pos).flatMap { sym =>
331339
val enclTree = enclosingTree(path)
340+
val includeLocal = if (sym.exists && sym.isLocal) Include.local else Include.empty
332341

333342
val (trees, include) =
334343
if (enclTree.isInstanceOf[MemberDef])
@@ -347,7 +356,7 @@ object Interactive {
347356
(Nil, Include.empty)
348357
}
349358

350-
findTreesMatching(trees, include, sym)
359+
findTreesMatching(trees, include | includeLocal, sym)
351360
}
352361
}
353362

language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ class DottyLanguageServer extends LanguageServer
312312

313313
val includes = {
314314
val includeDeclaration = params.getContext.isIncludeDeclaration
315-
Include.references | Include.overriding | Include.imports |
315+
Include.references | Include.overriding | Include.imports | Include.local |
316316
(if (includeDeclaration) Include.definitions else Include.empty)
317317
}
318318

@@ -458,7 +458,7 @@ class DottyLanguageServer extends LanguageServer
458458

459459
val uriTrees = driver.openedTrees(uri)
460460

461-
val defs = Interactive.namedTrees(uriTrees, Include.empty, _ => true)
461+
val defs = Interactive.namedTrees(uriTrees, Include.empty)
462462
(for {
463463
d <- defs if !isWorksheetWrapper(d)
464464
info <- symbolInfo(d.tree.symbol, d.namePos, positionMapperFor(d.source))
@@ -471,8 +471,8 @@ class DottyLanguageServer extends LanguageServer
471471
drivers.values.toList.flatMap { driver =>
472472
implicit val ctx = driver.currentCtx
473473

474-
val trees = driver.allTrees
475-
val defs = Interactive.namedTrees(trees, nameSubstring = query)
474+
val trees = driver.sourceTreesContaining(query)
475+
val defs = Interactive.namedTrees(trees, Include.empty, _.name.toString.contains(query))
476476
defs.flatMap(d => symbolInfo(d.tree.symbol, d.namePos, positionMapperFor(d.source)))
477477
}.asJava
478478
}
@@ -500,7 +500,7 @@ class DottyLanguageServer extends LanguageServer
500500
val predicates = definitions.map(Interactive.implementationFilter(_)(ctx))
501501
tree => predicates.exists(_(tree))
502502
}
503-
val matches = Interactive.namedTrees(trees, Include.empty, predicate)(ctx)
503+
val matches = Interactive.namedTrees(trees, Include.local, predicate)(ctx)
504504
matches.map(tree => location(tree.namePos(ctx), positionMapperFor(tree.source)))
505505
}
506506
}.toList

language-server/test/dotty/tools/languageserver/DocumentSymbolTest.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,10 @@ class DocumentSymbolTest {
3838
.documentSymbol(m1, (m1 to m2).symInfo("Foo", SymbolKind.Module),
3939
(m3 to m4).symInfo("Foo", SymbolKind.Class))
4040
}
41+
42+
@Test def documentSymbolSynthetic: Unit = {
43+
code"""case class ${m1}Foo${m2}(${m3}x${m4}: Int)""".withSource
44+
.documentSymbol(m1, (m1 to m2).symInfo("Foo", SymbolKind.Class),
45+
(m3 to m4).symInfo("x", SymbolKind.Field, "Foo"))
46+
}
4147
}

language-server/test/dotty/tools/languageserver/ReferencesTest.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,4 +346,14 @@ class ReferencesTest {
346346
.references(m11 to m12, List(m9 to m10, m11 to m12), withDecl = false)
347347
}
348348

349+
@Test def referenceInsideLocalMember: Unit = {
350+
withSources(
351+
code"""object A {
352+
| val ${m1}foo${m2} = 0
353+
| def fizz = println(${m3}foo${m4})
354+
|}"""
355+
).references(m1 to m2, List(m1 to m2, m3 to m4), withDecl = true)
356+
.references(m1 to m2, List(m3 to m4), withDecl = false)
357+
}
358+
349359
}

language-server/test/dotty/tools/languageserver/SymbolTest.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,26 @@ class SymbolTest {
3939
.symbol("Foo", (m1 to m2).symInfo("Foo", SymbolKind.Module),
4040
(m3 to m4).symInfo("Foo", SymbolKind.Class))
4141
}
42+
43+
@Test def multipleProjects0: Unit = {
44+
val p0 = Project.withSources(
45+
code"""class ${m1}Foo${m2}"""
46+
)
47+
48+
val p1 = Project.dependingOn(p0).withSources(
49+
code"""class ${m3}Bar${m4} extends Foo"""
50+
)
51+
52+
withProjects(p0, p1)
53+
.symbol("Foo", (m1 to m2).symInfo("Foo", SymbolKind.Class))
54+
}
55+
56+
@Test def noLocalSymbols: Unit = {
57+
code"""object O {
58+
def foo = {
59+
val hello = 0
60+
}
61+
}""".withSource
62+
.symbol("hello")
63+
}
4264
}

0 commit comments

Comments
 (0)