mirror of https://github.com/sbt/sbt.git
history commands: !!, !?string, !-n, !n, !string
This commit is contained in:
parent
42f4a71584
commit
5222234ee1
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = "*"
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
Loading…
Reference in New Issue