summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-01-11 23:29:25 +0000
committerPaul Phillips <paulp@improving.org>2010-01-11 23:29:25 +0000
commitd1ac90fb48c3d9e60de39591329e6d61644907f3 (patch)
tree472ccdafa52f88b5b04c707fe2d055294c6c8009 /src/compiler
parent7ba3ff508e1993c41fdbd7664bad17ebbfdb74ce (diff)
downloadscala-d1ac90fb48c3d9e60de39591329e6d61644907f3.tar.gz
scala-d1ac90fb48c3d9e60de39591329e6d61644907f3.tar.bz2
scala-d1ac90fb48c3d9e60de39591329e6d61644907f3.zip
A few repl features.
:history <N> shows N lines of history :h? <str> greps the history for str Altered tab-completion to be less verbose on the first tab, but notice when tab has been hit twice without any other input, and then be more verbose. And prettified the repl help text.
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/Interpreter.scala5
-rw-r--r--src/compiler/scala/tools/nsc/InterpreterLoop.scala75
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Completion.scala39
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala4
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/JLineReader.scala18
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala2
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(