Skip to content

Backport "Repl - method signatures in autocomplete" to LTS #20995

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 41 additions & 14 deletions compiler/src/dotty/tools/repl/ReplDriver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import dotty.tools.dotc.util.{SourceFile, SourcePosition}
import dotty.tools.dotc.{CompilationUnit, Driver}
import dotty.tools.dotc.config.CompilerCommand
import dotty.tools.io.*
import dotty.tools.repl.Rendering.showUser
import dotty.tools.runner.ScalaClassLoader.*
import org.jline.reader.*

Expand Down Expand Up @@ -148,11 +149,36 @@ class ReplDriver(settings: Array[String],

/** Blockingly read a line, getting back a parse result */
def readLine()(using state: State): ParseResult = {
val completer: Completer = { (_, line, candidates) =>
val comps = completions(line.cursor, line.line, state)
candidates.addAll(comps.asJava)
}
given Context = state.context
val completer: Completer = { (lineReader, line, candidates) =>
def makeCandidate(label: String) = {
new Candidate(
/* value = */ label,
/* displ = */ stripBackTicks(label), // displayed value
/* group = */ null, // can be used to group completions together
/* descr = */ null, // TODO use for documentation?
/* suffix = */ null,
/* key = */ null,
/* complete = */ false // if true adds space when completing
)
}
val comps = completionsWithSignatures(line.cursor, line.line, state)
candidates.addAll(comps.map(_.label).distinct.map(makeCandidate).asJava)
val lineWord = line.word()
comps.filter(c => c.label == lineWord && c.symbols.nonEmpty) match
case Nil =>
case exachMatches =>
val terminal = lineReader.nn.getTerminal
lineReader.callWidget(LineReader.CLEAR)
terminal.writer.println()
exachMatches.foreach: exact =>
exact.symbols.foreach: sym =>
terminal.writer.println(SyntaxHighlighting.highlight(sym.showUser))
lineReader.callWidget(LineReader.REDRAW_LINE)
lineReader.callWidget(LineReader.REDISPLAY)
terminal.flush()
}

try {
val line = terminal.readLine(completer)
ParseResult(line)
Expand Down Expand Up @@ -228,24 +254,26 @@ class ReplDriver(settings: Array[String],
else
label

/** Extract possible completions at the index of `cursor` in `expr` */
@deprecated("Use completionsWithSignatures instead", "3.3.4")
protected final def completions(cursor: Int, expr: String, state0: State): List[Candidate] =
def makeCandidate(label: String) = {

completionsWithSignatures(cursor, expr, state0).map: c =>
new Candidate(
/* value = */ label,
/* displ = */ stripBackTicks(label), // displayed value
/* value = */ c.label,
/* displ = */ stripBackTicks(c.label), // displayed value
/* group = */ null, // can be used to group completions together
/* descr = */ null, // TODO use for documentation?
/* suffix = */ null,
/* key = */ null,
/* complete = */ false // if true adds space when completing
)
}
end completions


/** Extract possible completions at the index of `cursor` in `expr` */
protected final def completionsWithSignatures(cursor: Int, expr: String, state0: State): List[Completion] =
if expr.startsWith(":") then
ParseResult.commands.collect {
case command if command._1.startsWith(expr) => makeCandidate(command._1)
case command if command._1.startsWith(expr) => Completion(command._1, "", List())
}
else
given state: State = newRun(state0)
Expand All @@ -258,11 +286,10 @@ class ReplDriver(settings: Array[String],
unit.tpdTree = tpdTree
given Context = state.context.fresh.setCompilationUnit(unit)
val srcPos = SourcePosition(file, Span(cursor))
val completions = try Completion.completions(srcPos)._2 catch case NonFatal(_) => Nil
completions.map(_.label).distinct.map(makeCandidate)
try Completion.completions(srcPos)._2 catch case NonFatal(_) => Nil
}
.getOrElse(Nil)
end completions
end completionsWithSignatures

protected def interpret(res: ParseResult, quiet: Boolean = false)(using state: State): State = {
res match {
Expand Down
2 changes: 1 addition & 1 deletion compiler/test/dotty/tools/repl/TabcompleteTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class TabcompleteTests extends ReplTest {

/** Returns the `(<instance completions>, <companion completions>)`*/
private def tabComplete(src: String)(implicit state: State): List[String] =
completions(src.length, src, state).map(_.value).sorted
completionsWithSignatures(src.length, src, state).map(_.label).sorted.distinct

@Test def tabCompleteList = initially {
val comp = tabComplete("List.r")
Expand Down