diff --git a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala index 8ee950707296..8ec7d52c98f6 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala @@ -79,7 +79,7 @@ object Interactive { def enclosingTree(trees: List[SourceTree], pos: SourcePosition)(using Context): Tree = enclosingTree(pathTo(trees, pos)) - /** The closes enclosing tree with a symbol, or the `EmptyTree`. + /** The closest enclosing tree with a symbol, or the `EmptyTree`. */ def enclosingTree(path: List[Tree])(using Context): Tree = path.dropWhile(!_.symbol.exists).headOption.getOrElse(tpd.EmptyTree) diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index ead84dc9f7d0..d24b4d8c3bf9 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -48,19 +48,21 @@ object Signatures { * being called, the list of overloads of this function). */ def callInfo(path: List[tpd.Tree], span: Span)(using Context): (Int, Int, List[SingleDenotation]) = - path match { - case UnApply(fun, _, patterns) :: _ => - callInfo(span, patterns, fun, Signatures.countParams(fun)) - case Apply(fun, params) :: _ => - callInfo(span, params, fun, Signatures.countParams(fun)) - case _ => - (0, 0, Nil) + val enclosingApply = path.find { + case Apply(fun, _) => !fun.span.contains(span) + case UnApply(fun, _, _) => !fun.span.contains(span) + case _ => false } + enclosingApply.map { + case UnApply(fun, _, patterns) => callInfo(span, patterns, fun, Signatures.countParams(fun)) + case Apply(fun, params) => callInfo(span, params, fun, Signatures.countParams(fun)) + }.getOrElse((0, 0, Nil)) + def callInfo( span: Span, - params: List[Tree[Type]], - fun: Tree[Type], + params: List[Tree[Type]], + fun: Tree[Type], alreadyAppliedCount : Int )(using Context): (Int, Int, List[SingleDenotation]) = val paramIndex = params.indexWhere(_.span.contains(span)) match { diff --git a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala index c91d66877c6b..179b8002dfb5 100644 --- a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala +++ b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala @@ -556,7 +556,7 @@ class DottyLanguageServer extends LanguageServer val pos = sourcePosition(driver, uri, params.getPosition) val trees = driver.openedTrees(uri) - val path = Interactive.pathTo(trees, pos).dropWhile(t => !t.isInstanceOf[Apply | UnApply]) + val path = Interactive.pathTo(trees, pos) val (paramN, callableN, alternatives) = Signatures.callInfo(path, pos.span) val signatureInfos = alternatives.flatMap(Signatures.toSignature) diff --git a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala index bc4facd7b88f..171a3ac5615d 100644 --- a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala +++ b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala @@ -433,4 +433,63 @@ class SignatureHelpTest { Some("Option[A]"), None) ), None, 2) } + + @Test def nestedApplySignatures: Unit = { + val signatures = (1 to 5).map { i => + S(s"foo$i", Nil, List(List(P("x", "Int"))), Some("Int")) + } + val booSignature = S(s"boo", Nil, List(List(P("x", "Int"), P("y", "Int"))), Some("Int")) + code"""|object O: + | def foo1(x: Int): Int = ??? + | def foo2(x: Int): Int = ??? + | def foo3(x: Int): Int = ??? + | def foo4(x: Int): Int = ??? + | def foo5(x: Int): Int = ??? + | def boo(x: Int, y: Int): Int = ??? + | boo(${m1}, fo${m2}o1(fo${m3}o2(fo${m4}o3(fo${m5}o4(fo${m6}o5(${m7}))))))""" + .signatureHelp(m1, List(booSignature), None, 0) + .signatureHelp(m2, List(booSignature), None, 1) + .signatureHelp(m3, List(signatures(0)), None, 0) + .signatureHelp(m4, List(signatures(1)), None, 0) + .signatureHelp(m5, List(signatures(2)), None, 0) + .signatureHelp(m6, List(signatures(3)), None, 0) + .signatureHelp(m7, List(signatures(4)), None, 0) + } + + @Test def multipleNestedApplySignatures: Unit = { + val simpleSignature = S(s"simpleFoo", Nil, List(List(P("x", "Int"))), Some("Int")) + val complicatedSignature = S(s"complicatedFoo", Nil, List(List(P("x", "Int"), P("y", "Int"), P("z", "Int"))), Some("Int")) + code"""|object O: + | def simpleFoo(x: Int): Int = ??? + | def complicatedFoo(x: Int, y: Int, z: Int): Int = ??? + | simpleFoo( + | complicated${m1}Foo( + | simp${m2}leFoo(${m3}), + | complic${m4}atedFoo( + | 2, + | ${m5}, + | simpleF${m6}oo(${m7})), + | complicated${m8}Foo(5,${m9},7) + | ) + | )""" + .signatureHelp(m1, List(simpleSignature), None, 0) + .signatureHelp(m2, List(complicatedSignature), None, 0) + .signatureHelp(m3, List(simpleSignature), None, 0) + .signatureHelp(m4, List(complicatedSignature), None, 1) + .signatureHelp(m5, List(complicatedSignature), None, 1) + .signatureHelp(m6, List(complicatedSignature), None, 2) + .signatureHelp(m7, List(simpleSignature), None, 0) + .signatureHelp(m8, List(complicatedSignature), None, 2) + .signatureHelp(m9, List(complicatedSignature), None, 1) + } + + @Test def noHelpSignatureWithPositionedOnName: Unit = { + val signature = S(s"foo", Nil, List(List(P("x", "Int"))), Some("Int")) + code"""|object O: + | def foo(x: Int): Int = ??? + | f${m1}oo(${m2})""" + .signatureHelp(m1, Nil, None, 0) + .signatureHelp(m2, List(signature), None, 0) + } + }