summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ILoop.scala144
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Pasted.scala59
2 files changed, 97 insertions, 106 deletions
diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
index 71224b3016..06e3e54c53 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
@@ -43,7 +43,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
var in: InteractiveReader = _ // the input stream from which commands come
var settings: Settings = _
var intp: IMain = _
- var power: Power = _
+
+ lazy val power = new Power(this)
// TODO
// object opt extends AestheticSettings
@@ -109,7 +110,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
if (intp ne null) {
intp.close
intp = null
- power = null
Thread.currentThread.setContextClassLoader(originalClassLoader)
}
}
@@ -211,6 +211,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
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),
@@ -224,7 +225,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
List(
LineArg("dump", "displays a view of the interpreter's internal state", dumpCommand),
LineArg("phase", "set the implicit phase for power commands", phaseCommand),
- LineArg("symfilter", "change the filter for symbol printing", symfilterCmd),
LineArg("wrap", "code to wrap around all executions", wrapCommand)
)
}
@@ -237,10 +237,10 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
private val typeTransforms = List(
"scala.collection.immutable." -> "immutable.",
- "scala.collection.mutable." -> "mutable.",
- "scala.collection.generic." -> "generic.",
- "java.lang." -> "jl.",
- "scala.runtime." -> "runtime."
+ "scala.collection.mutable." -> "mutable.",
+ "scala.collection.generic." -> "generic.",
+ "java.lang." -> "jl.",
+ "scala.runtime." -> "runtime."
)
private def implicitsCommand(line: String): Result = {
@@ -337,17 +337,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
if (line == "") "Cleared wrapper."
else "Set wrapper to '" + line + "'"
}
-
- private def symfilterCmd(line: String): Result = {
- if (line == "") {
- power.vars.symfilter set "_ => true"
- "Remove symbol filter."
- }
- else {
- power.vars.symfilter set line
- "Set symbol filter to '" + line + "'."
- }
- }
private def pathToPhased = intp.pathToTerm("power") + ".phased"
private def phaseCommand(name: String): Result = {
// This line crashes us in TreeGen:
@@ -391,8 +380,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
/** Available commands */
def commands: List[LoopCommand] = standardCommands ++ (
- if (power == null) Nil
- else powerCommands
+ if (isReplPower) powerCommands else Nil
)
val replayQuestionMessage =
@@ -522,13 +510,13 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
}
def powerCmd(): Result = {
- if (power != null)
- return "Already in power mode."
-
- power = new Power(this)
+ if (isReplPower) return "Already in power mode."
+ else enablePowerMode()
+ }
+ def enablePowerMode() = {
isReplPower = true
power.unleash()
- power.banner
+ out.println(power.banner)
}
def verbosity() = {
@@ -572,88 +560,33 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
}
}
- private val CONTINUATION_STRING = " | "
- private val PROMPT_STRING = "scala> "
-
- /** If it looks like they're pasting in a scala interpreter
- * transcript, remove all the formatting we inserted so we
- * can make some sense of it.
- */
- private var pasteStamp: Long = 0
-
- /** Returns true if it's long enough to quit. */
- def updatePasteStamp(): Boolean = {
- /* Enough milliseconds between readLines to call it a day. */
- val PASTE_FINISH = 1000
-
- val prevStamp = pasteStamp
- pasteStamp = System.currentTimeMillis
-
- (pasteStamp - prevStamp > PASTE_FINISH)
-
+ private def readWhile(cond: String => Boolean) = {
+ Iterator continually in.readLine("") takeWhile (x => x != null && cond(x))
}
- /** TODO - we could look for the usage of resXX variables in the transcript.
- * Right now backreferences to auto-named variables will break.
- */
-
- /** The trailing lines complication was an attempt to work around the introduction
- * of newlines in e.g. email messages of repl sessions. It doesn't work because
- * an unlucky newline can always leave you with a syntactically valid first line,
- * which is executed before the next line is considered. So this doesn't actually
- * accomplish anything, but I'm leaving it in case I decide to try harder.
- */
- case class PasteCommand(cmd: String, trailing: ListBuffer[String] = ListBuffer[String]())
- /** Commands start on lines beginning with "scala>" and each successive
- * line which begins with the continuation string is appended to that command.
- * Everything else is discarded. When the end of the transcript is spotted,
- * all the commands are replayed.
- */
- @tailrec private def cleanTranscript(lines: List[String], acc: List[PasteCommand]): List[PasteCommand] = lines match {
- case Nil => acc.reverse
- case x :: xs if x startsWith PROMPT_STRING =>
- val first = x stripPrefix PROMPT_STRING
- val (xs1, xs2) = xs span (_ startsWith CONTINUATION_STRING)
- val rest = xs1 map (_ stripPrefix CONTINUATION_STRING)
- val result = (first :: rest).mkString("", "\n", "\n")
-
- cleanTranscript(xs2, PasteCommand(result) :: acc)
-
- case ln :: lns =>
- val newacc = acc match {
- case Nil => Nil
- case PasteCommand(cmd, trailing) :: accrest =>
- PasteCommand(cmd, trailing :+ ln) :: accrest
- }
- cleanTranscript(lns, newacc)
+ def pasteCommand(): Result = {
+ out.println("// Entering paste mode (ctrl-D to finish)\n")
+ out.flush()
+ val code = readWhile(_ => true) mkString "\n"
+ out.println("\n// Exiting paste mode, now interpreting.\n")
+ intp interpret code
+ ()
}
- /** The timestamp is for safety so it doesn't hang looking for the end
- * of a transcript. Ad hoc parsing can't be too demanding. You can
- * also use ctrl-D to start it parsing.
- */
- @tailrec private def interpretAsPastedTranscript(lines: List[String]) {
- val line = in.readLine("")
- val finished = updatePasteStamp()
-
- if (line == null || finished || line.trim == PROMPT_STRING.trim) {
- val xs = cleanTranscript(lines.reverse, Nil)
- println("Replaying %d commands from interpreter transcript." format xs.size)
- for (PasteCommand(cmd, trailing) <- xs) {
- out.flush()
- def runCode(code: String, extraLines: List[String]) {
- (intp interpret code) match {
- case IR.Incomplete if extraLines.nonEmpty =>
- runCode(code + "\n" + extraLines.head, extraLines.tail)
- case _ => ()
- }
- }
- runCode(cmd, trailing.toList)
- }
+ private object paste extends Pasted {
+ val ContinueString = " | "
+ val PromptString = "scala> "
+
+ def interpret(line: String): Unit = {
+ out.println(line.trim)
+ intp interpret line
+ out.println("")
}
- else
- interpretAsPastedTranscript(line :: lines)
+
+ def transcript(start: String) =
+ apply(Iterator(start) ++ readWhile(_.trim != PromptString.trim))
}
+ import paste.{ ContinueString, PromptString }
/** Interpret expressions starting with the first line.
* Read lines until a complete compilation unit is available
@@ -675,7 +608,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
out.println("You typed two blank lines. Starting a new command.")
None
}
- else in.readLine(CONTINUATION_STRING) match {
+ else in.readLine(ContinueString) match {
case null =>
// we know compilation is going to fail since we're at EOF and the
// parser thinks the input is still incomplete, but since this is
@@ -699,9 +632,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
* and avoid the interpreter, as it's likely not valid scala code.
*/
if (code == "") None
- else if (code startsWith PROMPT_STRING) {
- updatePasteStamp()
- interpretAsPastedTranscript(List(code))
+ else if (!paste.running && code.startsWith(PromptString)) {
+ paste.transcript(code)
None
}
else if (Completion.looksLikeInvocation(code) && intp.mostRecentVar != "") {
@@ -784,7 +716,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
intp.initialize()
if (isReplPower) {
plushln("Starting in power mode, one moment...\n")
- powerCmd()
+ enablePowerMode()
}
loop()
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/Pasted.scala b/src/compiler/scala/tools/nsc/interpreter/Pasted.scala
new file mode 100644
index 0000000000..e605bb010d
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/Pasted.scala
@@ -0,0 +1,59 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package interpreter
+
+/** If it looks like they're pasting in a scala interpreter
+ * transcript, remove all the formatting we inserted so we
+ * can make some sense of it.
+ */
+abstract class Pasted {
+ def ContinueString: String
+ def PromptString: String
+ def interpret(line: String): Unit
+
+ private var isRunning = false
+ def running = isRunning
+
+ /** Commands start on lines beginning with "scala>" and each successive
+ * line which begins with the continuation string is appended to that command.
+ * Everything else is discarded. When the end of the transcript is spotted,
+ * all the commands are replayed.
+ */
+ def apply(lines: TraversableOnce[String]) = {
+ val cmds = lines reduceLeft append split PromptString filterNot (_.trim == "") toList;
+ println("// Replaying %d commands from transcript.\n" format cmds.size)
+
+ isRunning = true
+ try cmds foreach interpret
+ finally isRunning = false
+ }
+
+ private def isPrompted(line: String) = line startsWith PromptString
+ private def isContinuation(line: String) = line startsWith ContinueString
+
+ private def append(code: String, line: String): String =
+ if (isPrompted(line)) code + "\n" + line
+ else if (isContinuation(line)) code + "\n" + line.stripPrefix(ContinueString)
+ else fixResRefs(code, line)
+
+ /** If the line looks like
+ * res15: Int
+ * then we go back in time to the preceding scala> prompt and rewrite
+ * the line containing <expr> as
+ * val res15 = { <expr> }
+ * and the rest as they say is rewritten history.
+ *
+ * In all other cases, discard the line.
+ */
+ private val resRegex = """^(res\d+):.*""".r
+ private def fixResRefs(code: String, line: String) = line match {
+ case resRegex(resName) if code contains PromptString =>
+ val (str1, str2) = code splitAt code.lastIndexOf(PromptString) + PromptString.length
+ "%sval %s = { %s }".format(str1, resName, str2)
+ case _ => code
+ }
+}