Skip to content

Commit 7a8dd59

Browse files
authored
Merge pull request #5057 from dotty-staging/topic/go-to-def-named-args
Support go-to-definition on named arguments
2 parents e307755 + 5180c93 commit 7a8dd59

File tree

3 files changed

+114
-4
lines changed

3 files changed

+114
-4
lines changed

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,15 @@ object Interactive {
3939
else path.head.tpe
4040
}
4141

42-
/** The closest enclosing tree with a symbol containing position `pos`.
42+
/** The closest enclosing tree with a symbol containing position `pos`, or the `EmptyTree`.
4343
*/
4444
def enclosingTree(trees: List[SourceTree], pos: SourcePosition)(implicit ctx: Context): Tree =
45-
pathTo(trees, pos).dropWhile(!_.symbol.exists).headOption.getOrElse(tpd.EmptyTree)
45+
enclosingTree(pathTo(trees, pos))
46+
47+
/** The closes enclosing tree with a symbol, or the `EmptyTree`.
48+
*/
49+
def enclosingTree(path: List[Tree])(implicit ctx: Context): Tree =
50+
path.dropWhile(!_.symbol.exists).headOption.getOrElse(tpd.EmptyTree)
4651

4752
/** The source symbol of the closest enclosing tree with a symbol containing position `pos`.
4853
*

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

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,31 @@ class DottyLanguageServer extends LanguageServer
248248
implicit val ctx = driver.currentCtx
249249

250250
val pos = sourcePosition(driver, uri, params.getPosition)
251-
val enclTree = Interactive.enclosingTree(driver.openedTrees(uri), pos)
252-
val sym = Interactive.sourceSymbol(enclTree.symbol)
251+
val path = Interactive.pathTo(driver.openedTrees(uri), pos)
252+
val enclTree = Interactive.enclosingTree(path)
253+
254+
val sym = {
255+
val sym = path match {
256+
// For a named arg, find the target `DefDef` and jump to the param
257+
case NamedArg(name, _) :: Apply(fn, _) :: _ =>
258+
val funSym = fn.symbol
259+
if (funSym.name == StdNames.nme.copy
260+
&& funSym.is(Synthetic)
261+
&& funSym.owner.is(CaseClass)) {
262+
funSym.owner.info.member(name).symbol
263+
} else {
264+
val classTree = funSym.topLevelClass.asClass.tree
265+
tpd.defPath(funSym, classTree).lastOption.flatMap {
266+
case DefDef(_, _, paramss, _, _) =>
267+
paramss.flatten.find(_.name == name).map(_.symbol)
268+
}.getOrElse(fn.symbol)
269+
}
270+
271+
case _ =>
272+
enclTree.symbol
273+
}
274+
Interactive.sourceSymbol(sym)
275+
}
253276

254277
if (sym == NoSymbol) Nil.asJava
255278
else {

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

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dotty.tools.languageserver
33
import org.junit.Test
44

55
import dotty.tools.languageserver.util.Code._
6+
import dotty.tools.languageserver.util.embedded.CodeMarker
67

78
class DefinitionTest {
89

@@ -43,4 +44,85 @@ class DefinitionTest {
4344
).definition(m3 to m4, List(m1 to m2))
4445
}
4546

47+
@Test def goToDefNamedArg: Unit = {
48+
code"""object Foo {
49+
def foo(${m1}x${m2}: Int) = ${m3}x${m4}
50+
foo(${m5}x${m6} = 2)
51+
}""".withSource
52+
.definition(m1 to m2, List(m1 to m2))
53+
.definition(m3 to m4, List(m1 to m2))
54+
.definition(m5 to m6, List(m1 to m2))
55+
}
56+
57+
@Test def goToDefNamedArgOverload: Unit = {
58+
val m9 = new CodeMarker("m9")
59+
val m10 = new CodeMarker("m10")
60+
val m11 = new CodeMarker("m11")
61+
val m12 = new CodeMarker("m12")
62+
val m13 = new CodeMarker("m13")
63+
val m14 = new CodeMarker("m14")
64+
65+
code"""object Foo {
66+
def foo(${m1}x${m2}: String): String = ${m3}x${m4}
67+
def foo(${m5}x${m6}: Int): String = foo(${m7}x${m8} = ${m9}x${m10}.toString)
68+
foo(${m11}x${m12} = "a")
69+
foo(${m13}x${m14} = 2)
70+
}""".withSource
71+
.definition(m1 to m2, List(m1 to m2))
72+
.definition(m3 to m4, List(m1 to m2))
73+
.definition(m5 to m6, List(m5 to m6))
74+
.definition(m7 to m8, List(m1 to m2))
75+
.definition(m9 to m10, List(m5 to m6))
76+
.definition(m11 to m12, List(m1 to m2))
77+
.definition(m13 to m14, List(m5 to m6))
78+
}
79+
80+
@Test def goToConstructorNamedArg: Unit = {
81+
withSources(
82+
code"""class Foo(${m1}x${m2}: Int)""",
83+
code"""class Bar extends Foo(${m3}x${m4} = 5)""",
84+
code"""object Buzz { new Foo(${m5}x${m6} = 2) }"""
85+
) .definition(m1 to m2, List(m1 to m2))
86+
.definition(m3 to m4, List(m1 to m2))
87+
.definition(m5 to m6, List(m1 to m2))
88+
}
89+
90+
@Test def goToConstructorNamedArgOverload: Unit = {
91+
val m9 = new CodeMarker("m9")
92+
val m10 = new CodeMarker("m10")
93+
val m11 = new CodeMarker("m11")
94+
val m12 = new CodeMarker("m12")
95+
96+
withSources(
97+
code"""class Foo(${m1}x${m2}: String) {
98+
def this(${m3}x${m4}: Int) = this(${m5}x${m6} = ${m7}x${m8}.toString)
99+
}""",
100+
code"""object Bar {
101+
new Foo(${m9}x${m10} = 1)
102+
new Foo(${m11}x${m12} = "a")
103+
}"""
104+
) .definition(m1 to m2, List(m1 to m2))
105+
.definition(m3 to m4, List(m3 to m4))
106+
.definition(m5 to m6, List(m1 to m2))
107+
.definition(m7 to m8, List(m3 to m4))
108+
.definition(m9 to m10, List(m3 to m4))
109+
.definition(m11 to m12, List(m1 to m2))
110+
}
111+
112+
@Test def goToParamCopyMethod: Unit = {
113+
val m9 = new CodeMarker("m9")
114+
val m10 = new CodeMarker("m10")
115+
116+
withSources(
117+
code"""case class Foo(${m1}x${m2}: Int, ${m3}y${m4}: String)""",
118+
code"""object Bar {
119+
Foo(0, "a").copy(${m5}x${m6} = 1, ${m7}y${m8} = "b")
120+
Foo(2, "c").copy(${m9}y${m10} = "d")"""
121+
) .definition(m1 to m2, List(m1 to m2))
122+
.definition(m3 to m4, List(m3 to m4))
123+
.definition(m5 to m6, List(m1 to m2))
124+
.definition(m7 to m8, List(m3 to m4))
125+
.definition(m9 to m10, List(m3 to m4))
126+
}
127+
46128
}

0 commit comments

Comments
 (0)