Skip to content

Commit 02b6cda

Browse files
committed
REPL command completion is cursor sensitive
Completes `:lo^x.s` to `:load^x.s` and `:lo` to `:load ^`.
1 parent fc39f78 commit 02b6cda

File tree

2 files changed

+27
-17
lines changed

2 files changed

+27
-17
lines changed

src/repl/scala/tools/nsc/interpreter/Completion.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ object NoCompletion extends Completion {
2121
}
2222

2323
object Completion {
24-
case class Candidates(cursor: Int, candidates: List[String]) { }
24+
case class Candidates(cursor: Int, candidates: List[String])
2525
val NoCandidates = Candidates(-1, Nil)
2626

2727
// a leading dot plus something, but not ".." or "./", ignoring leading whitespace

src/repl/scala/tools/nsc/interpreter/LoopCommands.scala

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ trait LoopCommands { self: { def echo(msg: String): Unit } =>
8686
}
8787
def ambiguousError(cmd: String): Result = {
8888
matchingCommands(cmd) match {
89-
case Nil => echo(cmd + ": no such command. Type :help for help.")
89+
case Nil => echo(s"No such command '$cmd'. Type :help for help.")
9090
case xs => echo(cmd + " is ambiguous: did you mean " + xs.map(":" + _.name).mkString(" or ") + "?")
9191
}
9292
Result(keepRunning = true, None)
@@ -95,7 +95,7 @@ trait LoopCommands { self: { def echo(msg: String): Unit } =>
9595
// all commands with given prefix
9696
private def matchingCommands(cmd: String) = commands.filter(_.name.startsWith(cmd.stripPrefix(":")))
9797

98-
// extract command from partial name, or prefer exact match if multiple matches
98+
// extract unique command from partial name, or prefer exact match if multiple matches
9999
private object CommandMatch {
100100
def unapply(name: String): Option[LoopCommand] =
101101
matchingCommands(name) match {
@@ -108,6 +108,7 @@ trait LoopCommands { self: { def echo(msg: String): Unit } =>
108108
// extract command name and rest of line
109109
private val commandish = """(\S+)(?:\s+)?(.*)""".r
110110

111+
// expect line includes leading colon
111112
def colonCommand(line: String): Result = line.trim match {
112113
case "" => helpSummary()
113114
case commandish(CommandMatch(cmd), rest) => cmd(rest)
@@ -117,21 +118,30 @@ trait LoopCommands { self: { def echo(msg: String): Unit } =>
117118

118119
import Completion.Candidates
119120

120-
def colonCompletion(line: String, cursor: Int): Completion = line.trim match {
121-
case commandish(name @ CommandMatch(cmd), rest) =>
122-
if (name.length > cmd.name.length) cmd.completion
123-
else
124-
new Completion {
125-
def resetVerbosity(): Unit = ()
126-
def complete(buffer: String, cursor: Int) = Candidates(cursor - name.length + 1, List(cmd.name))
121+
def colonCompletion(line: String, cursor: Int): Completion =
122+
line match {
123+
case commandish(name0, rest) =>
124+
val name = name0 take cursor
125+
val cmds = matchingCommands(name)
126+
val cursorAtName = cursor <= name.length
127+
cmds match {
128+
case Nil => NoCompletion
129+
case cmd :: Nil if !cursorAtName => cmd.completion
130+
case cmd :: Nil if cmd.name == name => NoCompletion
131+
case cmd :: Nil =>
132+
val completion = if (cmd.isInstanceOf[NullaryCmd] || cursor < line.length) cmd.name else cmd.name + " "
133+
new Completion {
134+
def resetVerbosity(): Unit = ()
135+
def complete(buffer: String, cursor: Int) = Candidates(cursor = 1, List(completion))
136+
}
137+
case cmd :: rest =>
138+
new Completion {
139+
def resetVerbosity(): Unit = ()
140+
def complete(buffer: String, cursor: Int) = Candidates(cursor = 1, cmds.map(_.name))
141+
}
127142
}
128-
case commandish(name, _) if matchingCommands(name).nonEmpty =>
129-
new Completion {
130-
def resetVerbosity(): Unit = ()
131-
def complete(buffer: String, cursor: Int) = Candidates(cursor - name.length + 1, matchingCommands(name).map(_.name))
132-
}
133-
case _ => NoCompletion
134-
}
143+
case _ => NoCompletion
144+
}
135145

136146
class NullaryCmd(name: String, help: String, detailedHelp: Option[String],
137147
f: String => Result) extends LoopCommand(name, help, detailedHelp) {

0 commit comments

Comments
 (0)