Skip to content

Commit

Permalink
feat(repl): method signatures in autocomplete
Browse files Browse the repository at this point in the history
  • Loading branch information
Florian3k committed Mar 11, 2024
1 parent 46b7bf5 commit b6cf280
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 35 deletions.
83 changes: 49 additions & 34 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 @@ -149,11 +150,38 @@ 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
)
}
completions(line.cursor, line.line, state) match
case Left(cmds) => candidates.addAll(cmds.map(makeCandidate).asJava)
case Right(comps) =>
val lineWord = line.word()
candidates.addAll(comps.map(c => makeCandidate(c.label)).asJava)
comps.filter(_.label == lineWord) match
case Nil =>
case exachMatches =>
val terminal = lineReader.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 @@ -230,39 +258,26 @@ class ReplDriver(settings: Array[String],
label

/** Extract possible completions at the index of `cursor` in `expr` */
protected final def completions(cursor: Int, expr: String, state0: State): List[Candidate] =
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
)
}

protected final def completions(cursor: Int, expr: String, state0: State): Either[List[String], List[Completion]] =
if expr.startsWith(":") then
ParseResult.commands.collect {
case command if command._1.startsWith(expr) => makeCandidate(command._1)
}
Left(ParseResult.commands.collect {
case command if command._1.startsWith(expr) => command._1
})
else
given state: State = newRun(state0)
compiler
.typeCheck(expr, errorsAllowed = true)
.map { (untpdTree, tpdTree) =>
val file = SourceFile.virtual("<completions>", expr, maybeIncomplete = true)
val unit = CompilationUnit(file)(using state.context)
unit.untpdTree = untpdTree
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)
}
.getOrElse(Nil)
Right:
compiler
.typeCheck(expr, errorsAllowed = true)
.map { (untpdTree, tpdTree) =>
val file = SourceFile.virtual("<completions>", expr, maybeIncomplete = true)
val unit = CompilationUnit(file)(using state.context)
unit.untpdTree = untpdTree
unit.tpdTree = tpdTree
given Context = state.context.fresh.setCompilationUnit(unit)
val srcPos = SourcePosition(file, Span(cursor))
try Completion.completions(srcPos)._2 catch case NonFatal(_) => Nil
}
.getOrElse(Nil)
end completions

protected def interpret(res: ParseResult, quiet: Boolean = false)(using state: State): State = {
Expand Down
4 changes: 3 additions & 1 deletion compiler/test/dotty/tools/repl/ReplTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ extends ReplDriver(options, new PrintStream(out, true, StandardCharsets.UTF_8.na

/** Returns the `(<instance completions>, <companion completions>)`*/
def tabComplete(src: String)(implicit state: State): List[String] =
completions(src.length, src, state).map(_.value).sorted
completions(src.length, src, state) match
case Left(cmds) => cmds
case Right(comps) => comps.map(_.label)

extension [A](state: State)
infix def andThen(op: State ?=> A): A = op(using state)
Expand Down

0 comments on commit b6cf280

Please sign in to comment.