Skip to content

Commit 154e7cb

Browse files
committed
Add navigation to overridden symbols
These are now shown in "definitions" if the cursor position is on a definition.
1 parent 9b491c1 commit 154e7cb

File tree

2 files changed

+43
-29
lines changed

2 files changed

+43
-29
lines changed

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

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ import NameKinds.SimpleNameKind
1818
object Interactive {
1919
import ast.tpd._
2020

21+
object Include { // should be an enum, really.
22+
type Set = Int
23+
val overridden = 1 // include trees whose symbol is overridden by `sym`
24+
val overriding = 2 // include trees whose symbol overrides `sym`
25+
val references = 4 // include references and not just definitions
26+
}
27+
2128
/** Does this tree define a symbol ? */
2229
def isDefinition(tree: Tree) =
2330
tree.isInstanceOf[DefTree with NameTree]
@@ -28,19 +35,17 @@ object Interactive {
2835
if (path.isEmpty) NoType
2936
else path.head.tpe
3037
}
38+
/** The closest enclosing tree with a symbol containing position `pos`.
39+
*/
40+
def enclosingTree(trees: List[SourceTree], pos: SourcePosition)(implicit ctx: Context): Tree =
41+
pathTo(trees, pos).dropWhile(!_.symbol.exists).headOption.getOrElse(tpd.EmptyTree)
3142

3243
/** The source symbol of the closest enclosing tree with a symbol containing position `pos`.
3344
*
3445
* @see sourceSymbol
3546
*/
36-
def enclosingSourceSymbol(trees: List[SourceTree], pos: SourcePosition)(implicit ctx: Context): Symbol = {
37-
pathTo(trees, pos).dropWhile(!_.symbol.exists).headOption match {
38-
case Some(tree) =>
39-
sourceSymbol(tree.symbol)
40-
case None =>
41-
NoSymbol
42-
}
43-
}
47+
def enclosingSourceSymbol(trees: List[SourceTree], pos: SourcePosition)(implicit ctx: Context): Symbol =
48+
sourceSymbol(enclosingTree(trees, pos).symbol)
4449

4550
/** A symbol related to `sym` that is defined in source code.
4651
*
@@ -65,14 +70,22 @@ object Interactive {
6570
/** Check if `tree` matches `sym`.
6671
* This is the case if the symbol defined by `tree` equals `sym`,
6772
* or the source symbol of tree equals sym,
68-
* or `includeOverridden is true, and `sym` is overriden by `tree`.
73+
* or `include` is `overridden`, and `tree` is overridden by `sym`,
74+
* or `include` is `overriding`, and `tree` overrides `sym`.
6975
*/
70-
def matchSymbol(tree: Tree, sym: Symbol, includeOverridden: Boolean)(implicit ctx: Context): Boolean =
76+
def matchSymbol(tree: Tree, sym: Symbol, include: Include.Set)(implicit ctx: Context): Boolean = {
77+
78+
def overrides(sym1: Symbol, sym2: Symbol) =
79+
sym1.owner.derivesFrom(sym2.owner) && sym1.overriddenSymbol(sym2.owner.asClass) == sym2
80+
7181
( sym == tree.symbol
7282
|| sym.exists && sym == sourceSymbol(tree.symbol)
73-
|| includeOverridden && sym.name == tree.symbol.name &&
74-
tree.symbol.owner.derivesFrom(sym.owner) && tree.symbol.overriddenSymbol(sym.owner.asClass) == sym
83+
|| include != 0 && sym.name == tree.symbol.name && sym.owner != tree.symbol.owner
84+
&& ( (include & Include.overridden) != 0 && overrides(sym, tree.symbol)
85+
|| (include & Include.overriding) != 0 && overrides(tree.symbol, sym)
86+
)
7587
)
88+
}
7689

7790
private def safely[T](op: => List[T]): List[T] =
7891
try op catch { case ex: TypeError => Nil }
@@ -135,16 +148,13 @@ object Interactive {
135148
* Note that nothing will be found for symbols not defined in source code,
136149
* use `sourceSymbol` to get a symbol related to `sym` that is defined in
137150
* source code.
138-
*
139-
* @param includeReferences If true, include references and not just definitions
140-
* @param includeOverridden If true, include trees whose symbol is overriden by `sym`
141151
*/
142-
def namedTrees(trees: List[SourceTree], includeReferences: Boolean, includeOverridden: Boolean, sym: Symbol)
152+
def namedTrees(trees: List[SourceTree], include: Include.Set, sym: Symbol)
143153
(implicit ctx: Context): List[SourceTree] =
144154
if (!sym.exists)
145155
Nil
146156
else
147-
namedTrees(trees, includeReferences, matchSymbol(_, sym, includeOverridden))
157+
namedTrees(trees, (include & Include.references) != 0, matchSymbol(_, sym, include))
148158

149159
/** Find named trees with a non-empty position whose name contains `nameSubstring` in `trees`.
150160
*

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

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import classpath.ClassPathEntries
2424
import reporting._, reporting.diagnostic.MessageContainer
2525
import util._
2626
import interactive._, interactive.InteractiveDriver._
27+
import Interactive.Include
2728

2829
import languageserver.config.ProjectConfig
2930

@@ -207,24 +208,27 @@ class DottyLanguageServer extends LanguageServer
207208
/*isIncomplete = */ false, items.map(completionItem).asJava))
208209
}
209210

211+
/** If cursor is on a reference, show its definition and all overriding definitions.
212+
* If cursor is on a definition, show this definition together with all overridden
213+
* and overriding definitions.
214+
* For performance reasons we currently look for overrides only in the file
215+
* where `sym` is defined.
216+
*/
210217
override def definition(params: TextDocumentPositionParams) = computeAsync { cancelToken =>
211218
val uri = new URI(params.getTextDocument.getUri)
212219
val driver = driverFor(uri)
213220
implicit val ctx = driver.currentCtx
214221

215222
val pos = sourcePosition(driver, uri, params.getPosition)
216-
val sym = Interactive.enclosingSourceSymbol(driver.openedTrees(uri), pos)
223+
val enclTree = Interactive.enclosingTree(driver.openedTrees(uri), pos)
224+
val sym = Interactive.sourceSymbol(enclTree.symbol)
217225

218226
if (sym == NoSymbol) Nil.asJava
219227
else {
220-
// This returns the position of sym as well as the overrides of sym, but
221-
// for performance we only look for overrides in the file where sym is
222-
// defined.
223-
// We need a configuration option to choose how "go to definition" should
224-
// behave with respect to overriding and overriden definitions, ideally
225-
// this should be part of the LSP protocol.
226228
val trees = SourceTree.fromSymbol(sym.topLevelClass.asClass).toList
227-
val defs = Interactive.namedTrees(trees, includeReferences = false, includeOverridden = true, sym)
229+
var include = Include.overriding
230+
if (enclTree.isInstanceOf[MemberDef]) include |= Include.overridden
231+
val defs = Interactive.namedTrees(trees, include, sym)
228232
defs.map(d => location(d.namePos)).asJava
229233
}
230234
}
@@ -246,7 +250,7 @@ class DottyLanguageServer extends LanguageServer
246250
val trees = driver.allTreesContaining(sym.name.sourceModuleName.toString)
247251
val refs = Interactive.namedTrees(trees, includeReferences = true, (tree: tpd.NameTree) =>
248252
(includeDeclaration || !Interactive.isDefinition(tree))
249-
&& Interactive.matchSymbol(tree, sym, includeOverridden = true))
253+
&& Interactive.matchSymbol(tree, sym, Include.overriding))
250254

251255
refs.map(ref => location(ref.namePos)).asJava
252256
}
@@ -267,8 +271,8 @@ class DottyLanguageServer extends LanguageServer
267271
val newName = params.getNewName
268272

269273
val refs = Interactive.namedTrees(trees, includeReferences = true, tree =>
270-
(Interactive.matchSymbol(tree, sym, includeOverridden = true)
271-
|| (linkedSym != NoSymbol && Interactive.matchSymbol(tree, linkedSym, includeOverridden = true))))
274+
(Interactive.matchSymbol(tree, sym, Include.overriding)
275+
|| (linkedSym != NoSymbol && Interactive.matchSymbol(tree, linkedSym, Include.overriding))))
272276

273277
val changes = refs.groupBy(ref => toUri(ref.source).toString).mapValues(_.map(ref => new TextEdit(range(ref.namePos), newName)).asJava)
274278

@@ -287,7 +291,7 @@ class DottyLanguageServer extends LanguageServer
287291

288292
if (sym == NoSymbol) Nil.asJava
289293
else {
290-
val refs = Interactive.namedTrees(uriTrees, includeReferences = true, includeOverridden = true, sym)
294+
val refs = Interactive.namedTrees(uriTrees, Include.references | Include.overriding, sym)
291295
refs.map(ref => new DocumentHighlight(range(ref.namePos), DocumentHighlightKind.Read)).asJava
292296
}
293297
}

0 commit comments

Comments
 (0)