summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ILoop.scala144
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Pasted.scala59
-rw-r--r--test/files/run/repl-paste-2.check31
-rw-r--r--test/files/run/repl-paste-2.scala28
-rw-r--r--test/files/run/repl-paste.check13
-rw-r--r--test/files/run/repl-paste.scala19
-rw-r--r--test/files/run/repl-transcript.check23
-rw-r--r--test/files/run/repl-transcript.scala20
8 files changed, 231 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
+ }
+}
diff --git a/test/files/run/repl-paste-2.check b/test/files/run/repl-paste-2.check
new file mode 100644
index 0000000000..fb7a818f1a
--- /dev/null
+++ b/test/files/run/repl-paste-2.check
@@ -0,0 +1,31 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala>
+scala> // Replaying 7 commands from transcript.
+
+val res0 = { 123 }
+res0: Int = 123
+
+val res1 = { 567 }
+res1: Int = 567
+
+val res2 = { res0 + res1 }
+res2: Int = 690
+
+val x = dingus
+<console>:7: error: not found: value dingus
+ val x = dingus
+ ^
+
+val x = "dingus"
+x: java.lang.String = dingus
+
+val res3 = { x.length }
+res3: Int = 6
+
+val res4 = { x.length + res3 }
+res4: Int = 12
+
+
+scala>
diff --git a/test/files/run/repl-paste-2.scala b/test/files/run/repl-paste-2.scala
new file mode 100644
index 0000000000..802c627701
--- /dev/null
+++ b/test/files/run/repl-paste-2.scala
@@ -0,0 +1,28 @@
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ def code = """
+scala> 123
+res0: Int = 123
+
+scala> 567
+res1: Int = 567
+
+scala> res0 + res1
+res2: Int = 690
+
+scala> val x = dingus
+<console>:7: error: not found: value dingus
+ val x = dingus
+ ^
+
+scala> val x = "dingus"
+x: java.lang.String = dingus
+
+scala> x.length
+res3: Int = 6
+
+scala> x.length + res3
+res4: Int = 12
+ """
+} \ No newline at end of file
diff --git a/test/files/run/repl-paste.check b/test/files/run/repl-paste.check
new file mode 100644
index 0000000000..4c9de85e67
--- /dev/null
+++ b/test/files/run/repl-paste.check
@@ -0,0 +1,13 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala> // Entering paste mode (ctrl-D to finish)
+
+
+// Exiting paste mode, now interpreting.
+
+defined class Dingus
+defined module Dingus
+x: Int = 110
+
+scala>
diff --git a/test/files/run/repl-paste.scala b/test/files/run/repl-paste.scala
new file mode 100644
index 0000000000..e2ebab1e45
--- /dev/null
+++ b/test/files/run/repl-paste.scala
@@ -0,0 +1,19 @@
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ def code = ":paste\n" + (
+ """
+ class Dingus
+ {
+ private val x = 5
+ def y = Dingus.x * 2
+ }
+ object Dingus
+ {
+ private val x = 55
+ }
+
+ val x = (new Dingus).y
+ """
+ )
+} \ No newline at end of file
diff --git a/test/files/run/repl-transcript.check b/test/files/run/repl-transcript.check
new file mode 100644
index 0000000000..b2a8d2e156
--- /dev/null
+++ b/test/files/run/repl-transcript.check
@@ -0,0 +1,23 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala>
+scala> // Replaying 5 commands from transcript.
+
+class Bippity
+defined class Bippity
+
+def f = new Bippity
+f: Bippity
+
+val res5 = { 123 }
+res5: Int = 123
+
+val res6 = { 1 to 100 map (_ + 1) }
+res6: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101)
+
+res6.sum + res5
+res0: Int = 5273
+
+
+scala>
diff --git a/test/files/run/repl-transcript.scala b/test/files/run/repl-transcript.scala
new file mode 100644
index 0000000000..b39279aea3
--- /dev/null
+++ b/test/files/run/repl-transcript.scala
@@ -0,0 +1,20 @@
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ def code = """
+scala> class Bippity
+defined class Bippity
+
+scala> def f = new Bippity
+f: Bippity
+
+scala> 123
+res5: Int = 123
+
+scala> 1 to 100 map (_ + 1)
+res6: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101)
+
+scala> res6.sum + res5
+ """
+}
+