summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-04-05 23:19:26 +0000
committerPaul Phillips <paulp@improving.org>2011-04-05 23:19:26 +0000
commit4415640dc47397f58639532ca0d7ca3c3414bcd7 (patch)
tree3c5bbcb357797351ac31bcba402125d093e57d54
parent7d15e93f56d345d39f6d11dfc67415acf01a4e32 (diff)
downloadscala-4415640dc47397f58639532ca0d7ca3c3414bcd7.tar.gz
scala-4415640dc47397f58639532ca0d7ca3c3414bcd7.tar.bz2
scala-4415640dc47397f58639532ca0d7ca3c3414bcd7.zip
Some accumulated cleanups around the interprete...
Some accumulated cleanups around the interpreter loop commands as I attempt to make my way all the way to the promised land of documenting it. No review.
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ILoop.scala136
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala75
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/package.scala3
3 files changed, 130 insertions, 84 deletions
diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
index ccec5c0e22..8aa5f1a006 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
@@ -44,6 +44,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
var settings: Settings = _
var intp: IMain = _
+ def codeQuoted(s: String) = intp.memberHandlers.string2codeQuoted(s)
+
lazy val power = {
val g = intp.global
Power[g.type](this, g)
@@ -65,7 +67,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
// Install a signal handler so we can be prodded.
private val signallable =
- if (isReplDebug) Signallable("Dump repl state.")(dumpCommand(""))
+ if (isReplDebug) Signallable("Dump repl state.")(dumpCommand())
else null
// classpath entries added via :cp
@@ -149,12 +151,14 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
}
/** print a friendly help message */
- def printHelp() = {
+ def helpCommand() = {
+ val usageWidth = commands map (_.usageMsg.length) max
+ val cmds = commands map (x => (x.usageMsg, x.help))
+ val formatStr = "%-" + usageWidth + "s %s"
+
out println "All commands can be abbreviated - for example :he instead of :help.\n"
- val cmds = commands map (x => (x.usage, x.help))
- val width: Int = cmds map { case (x, _) => x.length } max
- val formatStr = "%-" + width + "s %s"
- cmds foreach { case (usage, help) => out println formatStr.format(usage, help) }
+ for ((use, help) <- cmds)
+ out println formatStr.format(use, help)
}
/** Print a welcome message */
@@ -171,18 +175,23 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
}
/** Show the history */
- def printHistory(xs: List[String]): Result = {
- if (history eq NoHistory)
- return "No history available."
+ lazy val historyCommand = new LoopCommand("history", "show the history (optional num is commands to show)") {
+ override def usage = "[num]"
+ def defaultLines = 20
- val defaultLines = 20
- val current = history.index
- val count = try xs.head.toInt catch { case _: Exception => defaultLines }
- val lines = history.asStrings takeRight count
- val offset = current - lines.size + 1
+ def apply(line: String): Result = {
+ if (history eq NoHistory)
+ return "No history available."
- for ((line, index) <- lines.zipWithIndex)
- println("%3d %s".format(index + offset, line))
+ val xs = words(line)
+ val current = history.index
+ val count = try xs.head.toInt catch { case _: Exception => defaultLines }
+ val lines = history.asStrings takeRight count
+ val offset = current - lines.size + 1
+
+ for ((line, index) <- lines.zipWithIndex)
+ println("%3d %s".format(index + offset, line))
+ }
}
/** Some print conveniences */
@@ -204,37 +213,35 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
/** Prompt to print when awaiting input */
def prompt = currentPrompt
+ import LoopCommand.{ cmd, nullary }
+
/** Standard commands **/
- val standardCommands: List[LoopCommand] = {
- List(
- LineArg("cp", "add an entry (jar or directory) to the classpath", addClasspath),
- NoArgs("help", "print this help message", printHelp),
- VarArgs("history", "show the history (optional arg: lines to show)", printHistory),
- LineArg("h?", "search the history", searchHistory),
- LineArg("implicits", "show the implicits in scope (-v to include Predef)", implicitsCommand),
- LineArg("javap", "disassemble a file or class name", javapCommand),
- LineArg("keybindings", "show how ctrl-[A-Z] and other keys are bound", keybindingsCommand),
- OneArg("load", "load and interpret a Scala file", load),
- NoArgs("paste", "enter paste mode: all input up to ctrl-D compiled together", pasteCommand),
- NoArgs("power", "enable power user mode", powerCmd),
- NoArgs("quit", "exit the interpreter", () => Result(false, None)),
- NoArgs("replay", "reset execution and replay all previous commands", replay),
- LineArg("sh", "fork a shell and run a command", shCommand),
- NoArgs("silent", "disable/enable automatic printing of results", verbosity),
- LineArg("type", "display the type of an expression without evaluating it", typeCommand)
- )
- }
+ val standardCommands = List(
+ cmd("cp", "<path>", "add an entry (jar or directory) to the classpath", addClasspath),
+ nullary("help", "print this help message", helpCommand),
+ historyCommand,
+ cmd("h?", "<string>", "search the history", searchHistory),
+ cmd("implicits", "[-v]", "show the implicits in scope", implicitsCommand),
+ cmd("javap", "<path|class>", "disassemble a file or class name", javapCommand),
+ nullary("keybindings", "show how ctrl-[A-Z] and other keys are bound", keybindingsCommand),
+ cmd("load", "<path>", "load and interpret a Scala file", loadCommand),
+ nullary("paste", "enter paste mode: all input up to ctrl-D compiled together", pasteCommand),
+ nullary("power", "enable power user mode", powerCmd),
+ nullary("quit", "exit the interpreter", () => Result(false, None)),
+ nullary("replay", "reset execution and replay all previous commands", replay),
+ shCommand,
+ nullary("silent", "disable/enable automatic printing of results", verbosity),
+ cmd("type", "<expr>", "display the type of an expression without evaluating it", typeCommand)
+ )
/** Power user commands */
- val powerCommands: List[LoopCommand] = {
- List(
- LineArg("dump", "displays a view of the interpreter's internal state", dumpCommand),
- LineArg("phase", "set the implicit phase for power commands", phaseCommand),
- LineArg("wrap", "code to wrap around all executions", wrapCommand)
- )
- }
+ val powerCommands: List[LoopCommand] = List(
+ nullary("dump", "displays a view of the interpreter's internal state", dumpCommand),
+ cmd("phase", "<phase>", "set the implicit phase for power commands", phaseCommand),
+ cmd("wrap", "<code>", "code to wrap around all executions", wrapCommand)
+ )
- private def dumpCommand(line: String): Result = {
+ private def dumpCommand(): Result = {
println(power)
history.asStrings takeRight 30 foreach println
in.redrawLine()
@@ -341,7 +348,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
else res.show()
}
}
- private def keybindingsCommand(line: String): Result = {
+ private def keybindingsCommand(): Result = {
if (in.keyBindings.isEmpty) "Key bindings unavailable."
else {
println("Reading jline properties for default key bindings.")
@@ -486,16 +493,15 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
}
/** fork a shell and run a command */
- def shCommand(cmd: String): Result = {
- if (cmd == "")
- return "Usage: sh <command line>"
-
- intp quietRun "import _root_.scala.sys.process._"
- val pb = Process(cmd)
- intp.bind("builder", pb)
- val stdout = Process(cmd).lines
- intp.bind("stdout", stdout)
- ()
+ lazy val shCommand = new LoopCommand("sh", "run a shell command (result is implicitly => List[String])") {
+ override def usage = "<command line>"
+ def apply(line: String): Result = line match {
+ case "" => showUsage()
+ case _ =>
+ val toRun = classOf[ProcessResult].getName + "(" + codeQuoted(line) + ")"
+ intp interpret toRun
+ ()
+ }
}
def withFile(filename: String)(action: File => Unit) {
@@ -505,7 +511,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
else out.println("That file does not exist")
}
- def load(arg: String) = {
+ def loadCommand(arg: String) = {
var shouldReplay: Option[String] = None
withFile(arg)(f => {
interpretAllFrom(f)
@@ -558,20 +564,20 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
else return Result(true, interpretStartingWith(line))
}
- val tokens = (line drop 1 split """\s+""").toList
- if (tokens.isEmpty)
- return withError(ambiguous(commands))
-
- val (cmd :: args) = tokens
+ val line2 = line.tail
+ val (cmd, rest) = line2 indexWhere (_.isWhitespace) match {
+ case -1 => (line2, "")
+ case idx => (line2 take idx, line2 drop idx dropWhile (_.isWhitespace))
+ }
// this lets us add commands willy-nilly and only requires enough command to disambiguate
commands.filter(_.name startsWith cmd) match {
- case List(x) => x(args)
+ case List(f) => f(rest)
case Nil => withError("Unknown command. Type :help for help.")
- case xs =>
- xs find (_.name == cmd) match {
- case Some(exact) => exact(args)
- case _ => withError(ambiguous(xs))
+ case fs =>
+ fs find (_.name == cmd) match {
+ case Some(exact) => exact(rest)
+ case _ => withError(ambiguous(fs))
}
}
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala b/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala
index 24fde9425f..2fa7f1501b 100644
--- a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala
@@ -6,43 +6,80 @@
package scala.tools.nsc
package interpreter
+import collection.{ mutable, immutable }
+import mutable.ListBuffer
+
+class ProcessResult(val line: String) {
+ import sys.process._
+ private val buffer = new ListBuffer[String]
+
+ val builder = Process(line)
+ val logger = ProcessLogger(buffer += _)
+ val exitCode = builder ! logger
+ def lines = buffer.toList
+
+ def show() = lines foreach println
+ override def toString = "`%s` (%d lines, exit %d)".format(line, buffer.size, exitCode)
+}
+object ProcessResult {
+ implicit def processResultToOutputLines(pr: ProcessResult): List[String] = pr.lines
+ def apply(line: String): ProcessResult = new ProcessResult(line)
+}
+
trait LoopCommands {
protected def out: java.io.PrintWriter
// a single interpreter command
- sealed abstract class LoopCommand extends (List[String] => Result) {
- def name: String
- def help: String
- def commandError(msg: String) = {
- out.println(":" + name + " " + msg + ".")
+ abstract class LoopCommand(val name: String, val help: String) extends (String => Result) {
+ def usage: String = ""
+ def usageMsg: String = ":" + name + (
+ if (usage == "") "" else " " + usage
+ )
+ def apply(line: String): Result
+ protected def words(line: String): List[String] = line split "\\s+" toList
+
+ // called if no args are given
+ def showUsage(): Result = {
+ "usage is " + usageMsg
Result(true, None)
}
- def usage(): String
+
+ def onError(msg: String) = {
+ out.println("error: " + msg)
+ showUsage()
+ }
}
- case class NoArgs(name: String, help: String, f: () => Result) extends LoopCommand {
- def usage(): String = ":" + name
- def apply(args: List[String]) = if (args.isEmpty) f() else commandError("accepts no arguments")
+ object LoopCommand {
+ def nullary(name: String, help: String, f: () => Result): LoopCommand =
+ new NullaryCmd(name, help, _ => f())
+
+ def cmd(name: String, usage: String, help: String, f: String => Result): LoopCommand =
+ if (usage == "") new NullaryCmd(name, help, f)
+ else new LineCmd(name, usage, help, f)
+
+ def varargs(name: String, usage: String, help: String, f: List[String] => Result): LoopCommand =
+ new VarArgsCmd(name, usage, help, f)
}
- case class LineArg(name: String, help: String, f: (String) => Result) extends LoopCommand {
- def usage(): String = ":" + name + " "
- def apply(args: List[String]) = f(args mkString " ")
+ class NullaryCmd(name: String, help: String, f: String => Result) extends LoopCommand(name, help) {
+ def apply(line: String): Result = f(line)
}
- case class OneArg(name: String, help: String, f: (String) => Result) extends LoopCommand {
- def usage(): String = ":" + name + " "
- def apply(args: List[String]) =
- if (args.size == 1) f(args.head)
- else commandError("requires exactly one argument")
+ class LineCmd(name: String, argWord: String, help: String, f: String => Result) extends LoopCommand(name, help) {
+ override def usage = argWord
+ def apply(line: String): Result = f(line)
}
- case class VarArgs(name: String, help: String, f: (List[String]) => Result) extends LoopCommand {
- def usage(): String = ":" + name + " [arg]"
+ class VarArgsCmd(name: String, argWord: String, help: String, f: List[String] => Result)
+ extends LoopCommand(name, help) {
+ override def usage = argWord
+ def apply(line: String): Result = apply(words(line))
def apply(args: List[String]) = f(args)
}
// the result of a single command
case class Result(val keepRunning: Boolean, val lineToRecord: Option[String])
+
object Result {
// the default result means "keep running, and don't record that line"
val default = Result(true, None)
diff --git a/src/compiler/scala/tools/nsc/interpreter/package.scala b/src/compiler/scala/tools/nsc/interpreter/package.scala
index b5ba2d9b8e..322224ed36 100644
--- a/src/compiler/scala/tools/nsc/interpreter/package.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/package.scala
@@ -29,6 +29,9 @@ package object interpreter {
type InputStream = java.io.InputStream
type OutputStream = java.io.OutputStream
+ private[nsc] val JLineDebug = "scala.tools.jline.internal.Log.debug"
+ private[nsc] val JLineTrace = "scala.tools.jline.internal.Log.trace"
+
private[nsc] val DebugProperty = "scala.repl.debug"
private[nsc] val TraceProperty = "scala.repl.trace"
private[nsc] val PowerProperty = "scala.repl.power"