Skip to content

Commit

Permalink
Merge pull request #14627 from ckipp01/commands
Browse files Browse the repository at this point in the history
feat(repl): autocomplete repl commands
  • Loading branch information
prolativ authored Mar 9, 2022
2 parents 99c2b96 + 3323d5d commit f7ede22
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 15 deletions.
8 changes: 8 additions & 0 deletions compiler/src/dotty/tools/repl/JLineTerminal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ final class JLineTerminal extends java.io.Closeable {
// using dummy values, resulting parsed input is probably unused
defaultParsedLine

// In the situation where we have a partial command that we want to
// complete we need to ensure that the :<partial-word> isn't split into
// 2 tokens, but rather the entire thing is treated as the "word", in
// order to insure the : is replaced in the completion.
case ParseContext.COMPLETE if
ParseResult.commands.exists(command => command._1.startsWith(input)) =>
parsedLine(input, cursor)

case ParseContext.COMPLETE =>
// Parse to find completions (typically after a Tab).
def isCompletable(token: Token) = isIdentifier(token) || isKeyword(token)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/repl/ParseResult.scala
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ object ParseResult {
stats
}

private val commands: List[(String, String => ParseResult)] = List(
private[repl] val commands: List[(String, String => ParseResult)] = List(
Quit.command -> (_ => Quit),
Quit.alias -> (_ => Quit),
Help.command -> (_ => Help),
Expand Down
34 changes: 20 additions & 14 deletions compiler/src/dotty/tools/repl/ReplDriver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ 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] = {
protected final def completions(cursor: Int, expr: String, state0: State): List[Candidate] =
def makeCandidate(label: String) = {

new Candidate(
Expand All @@ -218,20 +218,26 @@ class ReplDriver(settings: Array[String],
/* complete = */ false // if true adds space when completing
)
}
implicit val state = newRun(state0)
compiler
.typeCheck(expr, errorsAllowed = true)
.map { tree =>
val file = SourceFile.virtual("<completions>", expr, maybeIncomplete = true)
val unit = CompilationUnit(file)(using state.context)
unit.tpdTree = tree
given Context = state.context.fresh.setCompilationUnit(unit)
val srcPos = SourcePosition(file, Span(cursor))
val (_, completions) = Completion.completions(srcPos)
completions.map(_.label).distinct.map(makeCandidate)

if expr.startsWith(":") then
ParseResult.commands.collect {
case command if command._1.startsWith(expr) => makeCandidate(command._1)
}
.getOrElse(Nil)
}
else
given state: State = newRun(state0)
compiler
.typeCheck(expr, errorsAllowed = true)
.map { tree =>
val file = SourceFile.virtual("<completions>", expr, maybeIncomplete = true)
val unit = CompilationUnit(file)(using state.context)
unit.tpdTree = tree
given Context = state.context.fresh.setCompilationUnit(unit)
val srcPos = SourcePosition(file, Span(cursor))
val (_, completions) = Completion.completions(srcPos)
completions.map(_.label).distinct.map(makeCandidate)
}
.getOrElse(Nil)
end completions

private def interpret(res: ParseResult)(implicit state: State): State = {
res match {
Expand Down
24 changes: 24 additions & 0 deletions compiler/test/dotty/tools/repl/TabcompleteTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,28 @@ class TabcompleteTests extends ReplTest {
|Foo.`bac"""stripMargin))
}

@Test def commands = initially {
assertEquals(
List(
":doc",
":exit",
":help",
":imports",
":load",
":quit",
":reset",
":settings",
":type"
),
tabComplete(":")
)
}

@Test def commandPreface = initially {
// This looks odd, but if we return :doc here it will result in ::doc in the REPL
assertEquals(
List(":doc"),
tabComplete(":d")
)
}
}

0 comments on commit f7ede22

Please sign in to comment.