diff options
Diffstat (limited to 'src')
6 files changed, 107 insertions, 36 deletions
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala index 55e3b354e5..06a364e4a4 100644 --- a/src/compiler/scala/tools/nsc/Interpreter.scala +++ b/src/compiler/scala/tools/nsc/Interpreter.scala @@ -484,6 +484,9 @@ class Interpreter(val settings: Settings, out: PrintWriter) interpret("val %s = %s.value".format(name, binderName)) } + def quietBind(name: String, boundType: String, value: Any): IR.Result = + beQuietDuring { bind(name, boundType, value) } + /** Reset this interpreter, forgetting all user-specified requests. */ def reset() { virtualDirectory.clear @@ -789,7 +792,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) List(classOf[InvocationTargetException], classOf[ExceptionInInitializerError]) def onErr: Catcher[(String, Boolean)] = { case t: Throwable => - beQuietDuring { bind("lastException", "java.lang.Throwable", t) } + quietBind("lastException", "java.lang.Throwable", t) (stringFrom(t.printStackTrace(_)), false) } diff --git a/src/compiler/scala/tools/nsc/InterpreterLoop.scala b/src/compiler/scala/tools/nsc/InterpreterLoop.scala index 76f09f07f7..2b926d8e80 100644 --- a/src/compiler/scala/tools/nsc/InterpreterLoop.scala +++ b/src/compiler/scala/tools/nsc/InterpreterLoop.scala @@ -10,6 +10,7 @@ import java.io.{ BufferedReader, File, FileReader, PrintWriter } import java.io.IOException import scala.tools.nsc.{ InterpreterResults => IR } +import scala.collection.JavaConversions.asBuffer import interpreter._ import io.{ Process } @@ -22,30 +23,34 @@ object InterpreterControl { // a single interpreter command sealed abstract class Command extends Function1[List[String], Result] { - val name: String - val help: String + def name: String + def help: String def error(msg: String) = { println(":" + name + " " + msg + ".") Result(true, None) } - def getHelp(): String = ":" + name + " " + help + "." + def usage(): String } case class NoArgs(name: String, help: String, f: () => Result) extends Command { + def usage(): String = ":" + name def apply(args: List[String]) = if (args.isEmpty) f() else error("accepts no arguments") } case class LineArg(name: String, help: String, f: (String) => Result) extends Command { + def usage(): String = ":" + name + " <line>" def apply(args: List[String]) = f(args mkString " ") } case class OneArg(name: String, help: String, f: (String) => Result) extends Command { + def usage(): String = ":" + name + " <arg>" def apply(args: List[String]) = if (args.size == 1) f(args.head) else error("requires exactly one argument") } case class VarArgs(name: String, help: String, f: (List[String]) => Result) extends Command { + def usage(): String = ":" + name + " [arg]" def apply(args: List[String]) = f(args) } @@ -54,8 +59,6 @@ object InterpreterControl { } import InterpreterControl._ -// import scala.concurrent.ops.defaultRunner - /** The * <a href="http://scala-lang.org/" target="_top">Scala</a> * interactive shell. It provides a read-eval-print loop around @@ -76,6 +79,12 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) { /** The input stream from which commands come, set by main() */ var in: InteractiveReader = _ + def history = in match { + case x: JLineReader => Some(x.history) + case _ => None + } + def historyList: Seq[String] = + history map (x => asBuffer(x.getHistoryList): Seq[String]) getOrElse Nil /** The context class loader at the time this object was created */ protected val originalClassLoader = Thread.currentThread.getContextClassLoader @@ -126,8 +135,11 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) { /** print a friendly help message */ def printHelp() = { - out println "All commands can be abbreviated - for example :h or :he instead of :help.\n" - commands foreach { c => out println c.getHelp } + 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) } } /** Print a welcome message */ @@ -143,6 +155,36 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) { out.flush } + /** Show the history */ + def printHistory(xs: List[String]) { + val defaultLines = 20 + + if (history.isEmpty) + return println("No history available.") + + val current = history.get.getCurrentIndex + val count = try xs.head.toInt catch { case _: Exception => defaultLines } + val lines = historyList takeRight count + val offset = current - lines.size + 1 + + for ((line, index) <- lines.zipWithIndex) + println("%d %s".format(index + offset, line)) + } + + /** Search the history */ + def searchHistory(_cmdline: String) { + val cmdline = _cmdline.toLowerCase + + if (history.isEmpty) + return println("No history available.") + + val current = history.get.getCurrentIndex + val offset = current - historyList.size + 1 + + for ((line, index) <- historyList.zipWithIndex ; if line.toLowerCase contains cmdline) + println("%d %s".format(index + offset, line)) + } + /** Prompt to print when awaiting input */ val prompt = Properties.shellPromptString @@ -160,13 +202,15 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) { val standardCommands: List[Command] = { import CommandImplicits._ List( - NoArgs("help", "prints this help message", printHelp), + NoArgs("help", "print this help message", printHelp), + VarArgs("history", "show the history (optional arg: lines to show)", printHistory), + LineArg("h?", "search the history", searchHistory), OneArg("jar", "add a jar to the classpath", addJar), - OneArg("load", "followed by a filename loads a Scala file", load), + OneArg("load", "load and interpret a Scala file", load), NoArgs("power", "enable power user mode", power), - NoArgs("quit", "exits the interpreter", () => Result(false, None)), - NoArgs("replay", "resets execution and replays all previous commands", replay), - LineArg("sh", "forks a shell and runs a command", runShellCmd), + 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", runShellCmd), NoArgs("silent", "disable/enable automatic printing of results", verbosity) ) } @@ -296,9 +340,10 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) { replay() } - def power() = { + def power() { powerUserOn = true interpreter.powerUser() + interpreter.quietBind("history", "scala.collection.immutable.List[String]", historyList.toList) } def verbosity() = { @@ -381,7 +426,7 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) { // the interpeter is passed as an argument to expose tab completion info if (settings.Xnojline.value || emacsShell) new SimpleReader else if (settings.noCompletion.value) InteractiveReader.createDefault() - else InteractiveReader.createDefault(interpreter) + else InteractiveReader.createDefault(interpreter, this) } loadFiles(settings) @@ -399,7 +444,7 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) { // injects one value into the repl; returns pair of name and class def injectOne(name: String, obj: Any): Tuple2[String, String] = { val className = obj.asInstanceOf[AnyRef].getClass.getName - interpreter.bind(name, className, obj) + interpreter.quietBind(name, className, obj) (name, className) } diff --git a/src/compiler/scala/tools/nsc/interpreter/Completion.scala b/src/compiler/scala/tools/nsc/interpreter/Completion.scala index 2ecafa974a..2b9538b3fc 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Completion.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Completion.scala @@ -31,7 +31,12 @@ import scala.util.NameTransformer.{ decode, encode } // REPL completor - queries supplied interpreter for valid completions // based on current contents of buffer. -class Completion(val interpreter: Interpreter) extends Completor { +class Completion( + val interpreter: Interpreter, + val intLoop: InterpreterLoop) +extends Completor { + def this(interpreter: Interpreter) = this(interpreter, null) + import Completion._ import java.util.{ List => JList } import interpreter.compilerClasspath @@ -59,7 +64,7 @@ class Completion(val interpreter: Interpreter) extends Completor { } // One instance of a command line - class Buffer(s: String) { + class Buffer(s: String, verbose: Boolean) { val buffer = if (s == null) "" else s def isEmptyBuffer = buffer == "" @@ -133,25 +138,29 @@ class Completion(val interpreter: Interpreter) extends Completor { } def membersOfPredef() = membersOfId("scala.Predef") - def javaLangToHide(s: String) = + def javaLangToHide(s: String) = ( (s endsWith "Exception") || (s endsWith "Error") || (s endsWith "Impl") || (s startsWith "CharacterData") || !existsAndPublic("java.lang." + s) + ) def scalaToHide(s: String) = (List("Tuple", "Product", "Function") exists (x => (x + """\d+""").r findPrefixMatchOf s isDefined)) || (List("Exception", "Error") exists (s endsWith _)) - def defaultMembers = (List("scala", "java.lang") flatMap membersOfPath) ::: membersOfPredef + /** Hide all default members not verbose */ + def defaultMembers = + if (verbose) (List("scala", "java.lang") flatMap membersOfPath) ::: membersOfPredef + else Nil def pkgsStartingWith(s: String) = topLevelPackages() filter (_ startsWith s) def idsStartingWith(s: String) = { - // on a totally empty buffer, filter out res* + // only print res* when verbose val unqIds = - if (s == "") interpreter.unqualifiedIds filterNot (_ startsWith INTERPRETER_VAR_PREFIX) - else interpreter.unqualifiedIds + if (verbose) interpreter.unqualifiedIds + else interpreter.unqualifiedIds filterNot (_ startsWith INTERPRETER_VAR_PREFIX) (unqIds ::: defaultMembers) filter (_ startsWith s) } @@ -175,9 +184,21 @@ class Completion(val interpreter: Interpreter) extends Completor { (interpreter getClassObject ("scala." + path)) orElse (interpreter getClassObject ("java.lang." + path)) + def lastHistoryItem = + for (loop <- Option(intLoop) ; h <- loop.history) yield + h.getHistoryList.get(h.size - 1) + + // Is the buffer the same it was last time they hit tab? + private var lastTab: (String, String) = (null, null) + // jline's completion comes through here - we ask a Buffer for the candidates. - override def complete(_buffer: String, cursor: Int, candidates: JList[String]): Int = - new Buffer(_buffer) complete candidates + override def complete(_buffer: String, cursor: Int, candidates: JList[String]): Int = { + // println("_buffer = %s, cursor = %d".format(_buffer, cursor)) + val verbose = (_buffer, lastHistoryItem orNull) == lastTab + lastTab = (_buffer, lastHistoryItem orNull) + + new Buffer(_buffer, verbose) complete candidates + } def completePackageMembers(path: String): List[String] = getClassObject(path + "." + "package") map (getMembers(_, false)) getOrElse Nil diff --git a/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala b/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala index f2eb30cf14..500876bf69 100644 --- a/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala @@ -38,9 +38,9 @@ object InteractiveReader { /** Create an interactive reader. Uses <code>JLineReader</code> if the * library is available, but otherwise uses a <code>SimpleReader</code>. */ - def createDefault(interpreter: Interpreter): InteractiveReader = + def createDefault(interpreter: Interpreter, intLoop: InterpreterLoop = null): InteractiveReader = catching(exes: _*) - . opt (new JLineReader(interpreter)) + . opt (new JLineReader(interpreter, intLoop)) . getOrElse (new SimpleReader) } diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala index 59d6f0ac0a..b13b54a716 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala @@ -11,15 +11,17 @@ import java.io.File import jline.{ History, ConsoleReader, ArgumentCompletor } /** Reads from the console using JLine */ -class JLineReader(interpreter: Interpreter) extends InteractiveReader { - def this() = this(null) +class JLineReader(interpreter: Interpreter, intLoop: InterpreterLoop) extends InteractiveReader { + def this() = this(null, null) + def this(interpreter: Interpreter) = this(interpreter, null) + def history: History = consoleReader.getHistory + val consoleReader = { - val history = try { - new jline.History(new File(System.getProperty("user.home"), ".scala_history")) - } catch { + val history = + try new History(new File(System.getProperty("user.home"), ".scala_history")) // do not store history if error - case _ => new jline.History() - } + catch { case _: Exception => new History() } + val r = new jline.ConsoleReader() r setHistory history r setBellEnabled false @@ -30,7 +32,7 @@ class JLineReader(interpreter: Interpreter) extends InteractiveReader { val delimChars = "(){}[],`;'\" \t".toArray def isDelimiterChar(s: String, pos: Int) = delimChars contains s.charAt(pos) } - val comp = new ArgumentCompletor(new Completion(interpreter), delims) + val comp = new ArgumentCompletor(new Completion(interpreter, intLoop), delims) comp setStrict false r addCompletor comp // XXX make this use a setting diff --git a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala b/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala index 112b3e1e82..bca2e18e39 100644 --- a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package interpreter -import java.io.{BufferedReader, PrintWriter} +import java.io.{ BufferedReader, PrintWriter } /** Reads using standard JDK API */ class SimpleReader( |