history commands: !!, !?string, !-n, !n, !string

This commit is contained in:
Mark Harrah 2010-05-03 08:17:19 -04:00
parent 42f4a71584
commit 5222234ee1
3 changed files with 110 additions and 21 deletions

View File

@ -36,6 +36,10 @@ abstract class JLine extends LineReader
case null => None
case x => Some(x.trim)
}
def getHistory: Array[String] =
JLine.synchronized {
reader.getHistory.getHistoryList.toArray(new Array[String](0))
}
}
private object JLine
{
@ -49,23 +53,33 @@ private object JLine
}
def createReader() =
withTerminal { t =>
t.synchronized
{
val cr = new ConsoleReader
t.enableEcho()
cr.setBellEnabled(false)
cr
}
val cr = new ConsoleReader
t.enableEcho()
cr.setBellEnabled(false)
cr
}
def withJLine[T](action: => T): T =
withTerminal { t =>
t.synchronized
t.disableEcho()
try { action }
finally { t.enableEcho() }
}
private[sbt] def initializeHistory(cr: ConsoleReader, historyPath: Option[Path], log: Logger): Unit =
for(historyLocation <- historyPath)
{
val historyFile = historyLocation.asFile
Control.trapAndLog(log)
{
t.disableEcho()
try { action }
finally { t.enableEcho() }
historyFile.getParentFile.mkdirs()
cr.getHistory.setHistoryFile(historyFile)
}
}
def simple(historyPath: Option[Path], log: Logger): SimpleReader = new SimpleReader(historyPath, log)
}
class SimpleReader private[sbt] (historyPath: Option[Path], log: Logger) extends JLine
{
protected[this] val reader = JLine.createReader()
JLine.initializeHistory(reader, historyPath, log)
}
object SimpleReader extends JLine
{
@ -77,15 +91,7 @@ private[sbt] final class LazyJLineReader(historyPath: Option[Path], completor: =
{
val cr = new ConsoleReader
cr.setBellEnabled(false)
for(historyLocation <- historyPath)
{
val historyFile = historyLocation.asFile
Control.trapAndLog(log)
{
historyFile.getParentFile.mkdirs()
cr.getHistory.setHistoryFile(historyFile)
}
}
JLine.initializeHistory(cr, historyPath, log)
cr.addCompletor(new LazyCompletor(completor))
cr
}

View File

@ -197,6 +197,15 @@ class xMain extends xsbti.AppMain
case Some(newProject) => continue(newProject, tail, failAction)
case None => failed(BuildErrorExitCode)
}
case action :: tail if action.startsWith(HistoryPrefix) =>
historyCommand(action.substring(HistoryPrefix.length).trim, baseProject.historyPath, project.log) match
{
case Some(command) =>
println(command) //better to print it than to log it
continue(project, command :: tail, failAction)
case None => failed(UsageErrorExitCode)
}
case action :: tail if action.startsWith(FileCommandsPrefix) =>
getSource(action.substring(FileCommandsPrefix.length).trim, baseProject.info.projectDirectory) match
@ -271,6 +280,37 @@ class xMain extends xsbti.AppMain
if(message eq null) None else Some(message)
}
}
private def historyCommand(s: String, historyPath: Option[Path], log: Logger): Option[String] =
{
val lines = historyPath.toList.flatMap(h => xsbt.FileUtilities.readLines(h.asFile) ).toArray
if(lines.isEmpty)
{
log.warn("No history")
None
}
else
{
def replaceLastCommand(s: String) {
lines(lines.length - 1) = s
historyPath foreach { h => xsbt.FileUtilities.writeLines(h.asFile, lines) }
}
val command = historyCommand(complete.History(lines, log), s, log)
command.foreach(replaceLastCommand)
command
}
}
private def historyCommand(history: complete.History, s: String, log: Logger): Option[String] =
{
val ContainsPrefix = "?"
val Last = "!"
val PreviousPrefix = "-"
if(s == Last)
history !!
else if(s.startsWith(ContainsPrefix))
history !? s.substring(ContainsPrefix.length)
else
history ! s
}
object SetProject
{
def unapply(s: String) =
@ -355,6 +395,7 @@ class xMain extends xsbti.AppMain
reader.readLine("> ").getOrElse(ExitCommand)
}
val HistoryPrefix = "!"
/** The name of the command that loads a console with access to the current project through the variable 'project'.*/
val ProjectConsoleAction = "console-project"
/** The name of the command that shows the current project and logging level of that project.*/
@ -398,7 +439,7 @@ class xMain extends xsbti.AppMain
/** The prefix used to identify a file or local port to read commands from. */
val FileCommandsPrefix = "<"
/** The prefix used to identify the action to run after an error*/
val FailureHandlerPrefix = "!"
val FailureHandlerPrefix = "-"
/** The prefix used to identify commands for managing processors.*/
val ProcessorPrefix = "*"

View File

@ -0,0 +1,42 @@
package sbt.complete
import History.number
final class History private(lines: Array[String], log: Logger) extends NotNull
{
private def reversed = lines.reverse
def all: Seq[String] = lines.toArray
def size = lines.length
def !! : Option[String] = !- (1)
def apply(i: Int): Option[String] = if(0 <= i && i < size) Some( lines(i) ) else { log.error("Invalid history index: " + i); None }
def !(i: Int): Option[String] = apply(i)
def !(s: String): Option[String] =
number(s) match
{
case Some(n) => if(n < 0) !- (-n) else apply(n)
case None => nonEmpty(s) { reversed.find(_.startsWith(s)) }
}
def !- (n: Int): Option[String] = apply(size - n - 1)
def !?(s: String): Option[String] = nonEmpty(s) { reversed.drop(1).find(_.contains(s)) }
private def nonEmpty[T](s: String)(act: => Option[T]): Option[T] =
if(s.isEmpty)
{
log.error("No action specified to history command")
None
}
else
act
}
object History
{
def apply(lines: Seq[String], log: Logger): History = new History(lines.toArray, log)
private def number(s: String): Option[Int] =
try { Some(s.toInt) }
catch { case e: NumberFormatException => None }
}