From fa403fe1aa475f9ee677b5713192095647f815a4 Mon Sep 17 00:00:00 2001 From: rochala Date: Thu, 5 May 2022 17:35:23 +0200 Subject: [PATCH 1/4] fix signature help to now show enclosing apply symbol --- .../tools/dotc/interactive/Interactive.scala | 2 +- .../dotty/tools/dotc/util/Signatures.scala | 17 +++--- .../languageserver/DottyLanguageServer.scala | 8 ++- .../languageserver/SignatureHelpTest.scala | 59 +++++++++++++++++++ 4 files changed, 76 insertions(+), 10 deletions(-) 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..65122ec15962 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -42,25 +42,28 @@ object Signatures { /** * Extract (current parameter index, function index, functions) out of a method call. * - * @param path The path to the function application + * @param path The path to the function starting with closest one to `span` * @param span The position of the cursor * @return A triple containing the index of the parameter being edited, the index of the function * 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 UnApply(fun, _, patterns) :: _ => callInfo(span, patterns, fun, Signatures.countParams(fun)) + case Apply(fun, params) :: Apply(enclosingFun, enclosingParams) :: _ => + if !fun.span.contains(span) then + callInfo(span, params, fun, Signatures.countParams(fun)) + else + callInfo(span, enclosingParams, enclosingFun, Signatures.countParams(enclosingFun)) + case Apply(fun, params) :: _ => callInfo(span, params, fun, Signatures.countParams(fun)) case _ => (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..8928d852c939 100644 --- a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala +++ b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala @@ -556,8 +556,12 @@ 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 (paramN, callableN, alternatives) = Signatures.callInfo(path, pos.span) + val path = Interactive.pathTo(trees, pos).span { + case Apply(fun, _) => fun.span.contains(pos.span) + case UnApply(fun, _, _) => fun.span.contains(pos.span) + case _ => true + } + val (paramN, callableN, alternatives) = Signatures.callInfo(path._2, pos.span) val signatureInfos = alternatives.flatMap(Signatures.toSignature) new SignatureHelp(signatureInfos.map(signatureToSignatureInformation).asJava, callableN, paramN) 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) + } + } From 4f904664d7e83ea2e54522b07e49b441b35ba404 Mon Sep 17 00:00:00 2001 From: rochala Date: Thu, 5 May 2022 17:45:05 +0200 Subject: [PATCH 2/4] remove redundant cases --- compiler/src/dotty/tools/dotc/util/Signatures.scala | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index 65122ec15962..3a2fbaf7822b 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -50,11 +50,6 @@ object Signatures { 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) :: Apply(enclosingFun, enclosingParams) :: _ => - if !fun.span.contains(span) then - callInfo(span, params, fun, Signatures.countParams(fun)) - else - callInfo(span, enclosingParams, enclosingFun, Signatures.countParams(enclosingFun)) case Apply(fun, params) :: _ => callInfo(span, params, fun, Signatures.countParams(fun)) case _ => (0, 0, Nil) From b7a391aa566ba5564cf2fcdeaa23a90262a708ef Mon Sep 17 00:00:00 2001 From: rochala Date: Thu, 5 May 2022 18:01:13 +0200 Subject: [PATCH 3/4] simplify existing code --- .../src/dotty/tools/dotc/util/Signatures.scala | 14 ++++++-------- .../tools/languageserver/DottyLanguageServer.scala | 11 ++++++----- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index 3a2fbaf7822b..78b67d96cce8 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -42,18 +42,16 @@ object Signatures { /** * Extract (current parameter index, function index, functions) out of a method call. * - * @param path The path to the function starting with closest one to `span` + * @param enclosingApply Enclosing function application * @param span The position of the cursor * @return A triple containing the index of the parameter being edited, the index of the function * 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) - } + def callInfo(enclosingApply: Option[tpd.Tree], span: Span)(using Context): (Int, Int, List[SingleDenotation]) = + 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, diff --git a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala index 8928d852c939..c79bc9269483 100644 --- a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala +++ b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala @@ -556,12 +556,13 @@ class DottyLanguageServer extends LanguageServer val pos = sourcePosition(driver, uri, params.getPosition) val trees = driver.openedTrees(uri) - val path = Interactive.pathTo(trees, pos).span { - case Apply(fun, _) => fun.span.contains(pos.span) - case UnApply(fun, _, _) => fun.span.contains(pos.span) - case _ => true + val path = Interactive.pathTo(trees, pos).find { + case Apply(fun, _) => !fun.span.contains(pos.span) + case UnApply(fun, _, _) => !fun.span.contains(pos.span) + case _ => false } - val (paramN, callableN, alternatives) = Signatures.callInfo(path._2, pos.span) + + val (paramN, callableN, alternatives) = Signatures.callInfo(path, pos.span) val signatureInfos = alternatives.flatMap(Signatures.toSignature) new SignatureHelp(signatureInfos.map(signatureToSignatureInformation).asJava, callableN, paramN) From ff459b1c58b225a9259d2b69e81b84bad4bff751 Mon Sep 17 00:00:00 2001 From: rochala Date: Thu, 5 May 2022 19:02:36 +0200 Subject: [PATCH 4/4] revert signature change --- compiler/src/dotty/tools/dotc/util/Signatures.scala | 10 ++++++++-- .../tools/languageserver/DottyLanguageServer.scala | 7 +------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index 78b67d96cce8..d24b4d8c3bf9 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -42,12 +42,18 @@ object Signatures { /** * Extract (current parameter index, function index, functions) out of a method call. * - * @param enclosingApply Enclosing function application + * @param path The path to the function application * @param span The position of the cursor * @return A triple containing the index of the parameter being edited, the index of the function * being called, the list of overloads of this function). */ - def callInfo(enclosingApply: Option[tpd.Tree], span: Span)(using Context): (Int, Int, List[SingleDenotation]) = + def callInfo(path: List[tpd.Tree], span: Span)(using Context): (Int, Int, List[SingleDenotation]) = + 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)) diff --git a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala index c79bc9269483..179b8002dfb5 100644 --- a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala +++ b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala @@ -556,12 +556,7 @@ class DottyLanguageServer extends LanguageServer val pos = sourcePosition(driver, uri, params.getPosition) val trees = driver.openedTrees(uri) - val path = Interactive.pathTo(trees, pos).find { - case Apply(fun, _) => !fun.span.contains(pos.span) - case UnApply(fun, _, _) => !fun.span.contains(pos.span) - case _ => false - } - + val path = Interactive.pathTo(trees, pos) val (paramN, callableN, alternatives) = Signatures.callInfo(path, pos.span) val signatureInfos = alternatives.flatMap(Signatures.toSignature)