Skip to content

Commit 25d436b

Browse files
committed
Move Signatures to dotty.tools.dotc.util
And address review comments.
1 parent 604c067 commit 25d436b

File tree

5 files changed

+131
-97
lines changed

5 files changed

+131
-97
lines changed

language-server/src/dotty/tools/languageserver/Signatures.scala renamed to compiler/src/dotty/tools/dotc/util/Signatures.scala

Lines changed: 95 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,59 @@
1-
package dotty.tools.languageserver
1+
package dotty.tools.dotc.util
22

3-
import dotty.tools.dotc.ast.tpd._
3+
import dotty.tools.dotc.ast.Trees._
4+
import dotty.tools.dotc.ast.tpd
45
import dotty.tools.dotc.core.Constants.Constant
56
import dotty.tools.dotc.core.Contexts.Context
67
import dotty.tools.dotc.core.Denotations.SingleDenotation
8+
import dotty.tools.dotc.core.Flags.Implicit
9+
import dotty.tools.dotc.core.Names.TermName
710
import dotty.tools.dotc.util.Positions.Position
8-
import dotty.tools.dotc.core.Types.{ErrorType, MethodType}
11+
import dotty.tools.dotc.core.Types.{ErrorType, MethodType, PolyType}
912
import dotty.tools.dotc.reporting.diagnostic.messages
1013

11-
import org.eclipse.lsp4j.{ParameterInformation, SignatureInformation}
12-
1314
import scala.collection.JavaConverters._
1415

1516
object Signatures {
1617

17-
def callInfo(path: List[Tree], pos: Position)(implicit ctx: Context): (Int, Int, List[SingleDenotation]) = {
18+
/**
19+
* Represent a method signature.
20+
*
21+
* @param name The name of the method
22+
* @param tparams The type parameters and their bounds
23+
* @param paramss The parameter lists of this method
24+
* @param returnType The return type of this method, if this is not a constructor.
25+
* @param doc The documentation for this method.
26+
*/
27+
case class Signature(name: String, tparams: List[String], paramss: List[List[Param]], returnType: Option[String], doc: Option[String] = None) {
28+
}
29+
30+
/**
31+
* Represent a method's parameter.
32+
*
33+
* @param name The name of the parameter
34+
* @param tpe The type of the parameter
35+
* @param doc The documentation of this parameter
36+
* @param isImplicit Is this parameter implicit?
37+
*/
38+
case class Param(name: String, tpe: String, doc: Option[String] = None, isImplicit: Boolean = false) {
39+
def show: String =
40+
s"$name: $tpe"
41+
}
42+
43+
/**
44+
* Extract (current parameter index, function index, functions) out of a method call.
45+
*
46+
* @param path The path to the function application
47+
* @param pos The position of the cursor
48+
* @return A triple containing the index of the parameter being edited, the index of the function
49+
* being called, the list of overloads of this function).
50+
*/
51+
def callInfo(path: List[tpd.Tree], pos: Position)(implicit ctx: Context): (Int, Int, List[SingleDenotation]) = {
1852
path match {
1953
case Apply(fun, params) :: _ =>
2054
val alreadyAppliedCount = Signatures.countParams(fun)
2155
val paramIndex = params.indexWhere(_.pos.contains(pos)) match {
22-
case -1 => ((params.length - 1) max 0) + alreadyAppliedCount
56+
case -1 => (params.length - 1 max 0) + alreadyAppliedCount
2357
case n => n + alreadyAppliedCount
2458
}
2559

@@ -42,6 +76,56 @@ object Signatures {
4276
}
4377
}
4478

79+
def toSignature(denot: SingleDenotation)(implicit ctx: Context): Option[Signature] = {
80+
val symbol = denot.symbol
81+
val docComment = ParsedComment.docOf(symbol)
82+
val classTree = symbol.topLevelClass.asClass.rootTree
83+
val isImplicit: TermName => Boolean = tpd.defPath(symbol, classTree).lastOption match {
84+
case Some(DefDef(_, _, paramss, _, _)) =>
85+
val flatParams = paramss.flatten
86+
name => flatParams.find(_.name == name).map(_.symbol.is(Implicit)).getOrElse(false)
87+
case _ =>
88+
_ => false
89+
}
90+
91+
denot.info.stripPoly match {
92+
case tpe: MethodType =>
93+
val infos = {
94+
tpe.paramInfoss.zip(tpe.paramNamess).map { case (infos, names) =>
95+
infos.zip(names).map { case (info, name) =>
96+
Signatures.Param(name.show,
97+
info.widenTermRefExpr.show,
98+
docComment.flatMap(_.paramDoc(name)),
99+
isImplicit = isImplicit(name))
100+
}
101+
}
102+
}
103+
104+
val typeParams = denot.info match {
105+
case poly: PolyType =>
106+
poly.paramNames.zip(poly.paramInfos).map { case (x, y) => x.show + y.show }
107+
case _ =>
108+
Nil
109+
}
110+
111+
val (name, returnType) =
112+
if (symbol.isConstructor) (symbol.owner.name.show, None)
113+
else (denot.name.show, Some(tpe.finalResultType.widenTermRefExpr.show))
114+
115+
val signature =
116+
Signatures.Signature(name,
117+
typeParams,
118+
infos,
119+
returnType,
120+
docComment.map(_.mainDoc))
121+
122+
Some(signature)
123+
124+
case other =>
125+
None
126+
}
127+
}
128+
45129
/**
46130
* The number of parameters that are applied in `tree`.
47131
*
@@ -51,7 +135,7 @@ object Signatures {
51135
* @param tree The tree to inspect.
52136
* @return The number of parameters that are passed.
53137
*/
54-
private def countParams(tree: Tree): Int = {
138+
private def countParams(tree: tpd.Tree): Int = {
55139
tree match {
56140
case Apply(fun, params) => countParams(fun) + params.length
57141
case _ => 0
@@ -71,7 +155,7 @@ object Signatures {
71155
* @return A pair composed of the index of the best alternative (0 if no alternatives
72156
* were found), and the list of alternatives.
73157
*/
74-
private def alternativesFromError(err: ErrorType, params: List[Tree])(implicit ctx: Context): (Int, List[SingleDenotation]) = {
158+
private def alternativesFromError(err: ErrorType, params: List[tpd.Tree])(implicit ctx: Context): (Int, List[SingleDenotation]) = {
75159
val alternatives =
76160
err.msg match {
77161
case messages.AmbiguousOverload(_, alternatives, _) =>
@@ -96,7 +180,7 @@ object Signatures {
96180
val alternativesScores = alternatives.map { alt =>
97181
alt.info.stripPoly match {
98182
case tpe: MethodType =>
99-
userParamsTypes.zip(tpe.paramInfos).takeWhile(_ <:< _).size
183+
userParamsTypes.zip(tpe.paramInfos).takeWhile{ case (t0, t1) => t0 <:< t1 }.size
100184
case _ =>
101185
0
102186
}
@@ -108,36 +192,5 @@ object Signatures {
108192
(bestAlternative, alternatives)
109193
}
110194

111-
case class Signature(name: String, tparams: List[String], paramss: List[List[Param]], returnType: Option[String], doc: Option[String] = None) {
112-
def toSignatureInformation: SignatureInformation = {
113-
val paramInfoss = paramss.map(_.map(_.toParameterInformation))
114-
val paramLists = paramss.map { paramList =>
115-
val labels = paramList.map(_.show)
116-
val prefix = if (paramList.exists(_.isImplicit)) "implicit " else ""
117-
labels.mkString(prefix, ", ", "")
118-
}.mkString("(", ")(", ")")
119-
val tparamsLabel = if (tparams.isEmpty) "" else tparams.mkString("[", ", ", "]")
120-
val returnTypeLabel = returnType.map(t => s": $t").getOrElse("")
121-
val label = s"$name$tparamsLabel$paramLists$returnTypeLabel"
122-
val documentation = doc.map(DottyLanguageServer.hoverContent)
123-
val signature = new SignatureInformation(label)
124-
signature.setParameters(paramInfoss.flatten.asJava)
125-
documentation.foreach(signature.setDocumentation(_))
126-
signature
127-
}
128-
}
129-
130-
case class Param(name: String, tpe: String, doc: Option[String] = None, isImplicit: Boolean = false) {
131-
132-
def toParameterInformation: ParameterInformation = {
133-
val label = s"$name: $tpe"
134-
val documentation = doc.map(DottyLanguageServer.hoverContent)
135-
val info = new ParameterInformation(label)
136-
documentation.foreach(info.setDocumentation(_))
137-
info
138-
}
139-
140-
def show: String =
141-
s"$name: $tpe"
142-
}
143195
}
196+

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

Lines changed: 28 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -470,58 +470,9 @@ class DottyLanguageServer extends LanguageServer
470470
val path = Interactive.pathTo(trees, pos).dropWhile(!_.isInstanceOf[Apply])
471471

472472
val (paramN, callableN, alternatives) = Signatures.callInfo(path, pos.pos)
473+
val signatureInfos = alternatives.flatMap(Signatures.toSignature)
473474

474-
val signatureInfos = alternatives.flatMap { denot =>
475-
val symbol = denot.symbol
476-
val docComment = ParsedComment.docOf(symbol)
477-
val classTree = symbol.topLevelClass.asClass.rootTree
478-
val isImplicit: TermName => Boolean = tpd.defPath(symbol, classTree).lastOption match {
479-
case Some(DefDef(_, _, paramss, _, _)) =>
480-
val flatParams = paramss.flatten
481-
name => flatParams.find(_.name == name).map(_.symbol.is(Implicit)).getOrElse(false)
482-
case _ =>
483-
_ => false
484-
}
485-
486-
denot.info.stripPoly match {
487-
case tpe: MethodType =>
488-
val infos = {
489-
tpe.paramInfoss.zip(tpe.paramNamess).map { (infos, names) =>
490-
infos.zip(names).map { (info, name) =>
491-
Signatures.Param(name.show,
492-
info.widenTermRefExpr.show,
493-
docComment.flatMap(_.paramDoc(name)),
494-
isImplicit = isImplicit(name))
495-
}
496-
}
497-
}
498-
499-
val typeParams = denot.info match {
500-
case poly: PolyType =>
501-
poly.paramNames.zip(poly.paramInfos).map((x, y) => x.show + y.show)
502-
case _ =>
503-
Nil
504-
}
505-
506-
val (name, returnType) =
507-
if (symbol.isConstructor) (symbol.owner.name.show, None)
508-
else (denot.name.show, Some(tpe.finalResultType.widenTermRefExpr.show))
509-
510-
val signature =
511-
Signatures.Signature(name,
512-
typeParams,
513-
infos,
514-
returnType,
515-
docComment.map(_.mainDoc))
516-
517-
Some(signature)
518-
519-
case other =>
520-
None
521-
}
522-
}
523-
524-
new SignatureHelp(signatureInfos.map(_.toSignatureInformation).asJava, callableN, paramN)
475+
new SignatureHelp(signatureInfos.map(signatureToSignatureInformation).asJava, callableN, paramN)
525476
}
526477

527478
override def getTextDocumentService: TextDocumentService = this
@@ -830,4 +781,30 @@ object DottyLanguageServer {
830781

831782
location(pos, positionMapper).map(l => new lsp4j.SymbolInformation(name, symbolKind(sym), l, containerName))
832783
}
784+
785+
/** Convert `signature` to a `SignatureInformation` */
786+
def signatureToSignatureInformation(signature: Signatures.Signature): lsp4j.SignatureInformation = {
787+
val paramInfoss = signature.paramss.map(_.map(paramToParameterInformation))
788+
val paramLists = signature.paramss.map { paramList =>
789+
val labels = paramList.map(_.show)
790+
val prefix = if (paramList.exists(_.isImplicit)) "implicit " else ""
791+
labels.mkString(prefix, ", ", "")
792+
}.mkString("(", ")(", ")")
793+
val tparamsLabel = if (signature.tparams.isEmpty) "" else signature.tparams.mkString("[", ", ", "]")
794+
val returnTypeLabel = signature.returnType.map(t => s": $t").getOrElse("")
795+
val label = s"${signature.name}$tparamsLabel$paramLists$returnTypeLabel"
796+
val documentation = signature.doc.map(DottyLanguageServer.hoverContent)
797+
val sig = new lsp4j.SignatureInformation(label)
798+
sig.setParameters(paramInfoss.flatten.asJava)
799+
documentation.foreach(sig.setDocumentation(_))
800+
sig
801+
}
802+
803+
/** Convert `param` to `ParameterInformation` */
804+
private def paramToParameterInformation(param: Signatures.Param): lsp4j.ParameterInformation = {
805+
val documentation = param.doc.map(DottyLanguageServer.hoverContent)
806+
val info = new lsp4j.ParameterInformation(param.show)
807+
documentation.foreach(info.setDocumentation(_))
808+
info
809+
}
833810
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ package dotty.tools.languageserver
33
import org.junit.Test
44

55
import dotty.tools.languageserver.util.Code._
6-
import dotty.tools.languageserver.Signatures.{Param => P, Signature => S}
6+
7+
import dotty.tools.dotc.util.Signatures.{Param => P, Signature => S}
78

89
class SignatureHelpTest {
910

language-server/test/dotty/tools/languageserver/util/CodeTester.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package dotty.tools.languageserver.util
22

3-
import dotty.tools.languageserver.Signatures.Signature
43
import dotty.tools.languageserver.util.Code._
54
import dotty.tools.languageserver.util.actions._
65
import dotty.tools.languageserver.util.embedded.CodeMarker
76
import dotty.tools.languageserver.util.server.{TestFile, TestServer}
7+
8+
import dotty.tools.dotc.util.Signatures.Signature
9+
810
import org.eclipse.lsp4j.{CompletionItemKind, DocumentHighlightKind}
911

1012
/**

language-server/test/dotty/tools/languageserver/util/actions/SignatureHelp.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package dotty.tools.languageserver.util.actions
22

3+
import dotty.tools.languageserver.DottyLanguageServer
34
import dotty.tools.languageserver.util.PositionContext
45
import dotty.tools.languageserver.util.embedded.CodeMarker
56

6-
import dotty.tools.languageserver.Signatures.Signature
7+
import dotty.tools.dotc.util.Signatures.Signature
78

89
import org.eclipse.lsp4j.{MarkupContent, ParameterInformation, SignatureInformation}
910
import org.junit.Assert.{assertEquals, assertFalse, assertTrue}
@@ -28,7 +29,7 @@ class SignatureHelp(override val marker: CodeMarker,
2829
activeSignature: Option[Int],
2930
activeParam: Int) extends ActionOnMarker {
3031

31-
val expectedSignatures = expected.map(_.toSignatureInformation)
32+
val expectedSignatures = expected.map(DottyLanguageServer.signatureToSignatureInformation)
3233

3334
override def execute(): Exec[Unit] = {
3435
val results = server.signatureHelp(marker.toTextDocumentPositionParams).get()

0 commit comments

Comments
 (0)