summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ILoop.scala8
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Pasted.scala93
-rw-r--r--test/files/run/repl-paste-2.check48
-rw-r--r--test/files/run/repl-paste-2.scala17
-rw-r--r--test/files/run/repl-transcript.check10
5 files changed, 112 insertions, 64 deletions
diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
index 8aa5f1a006..75503be3d1 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
@@ -605,8 +605,14 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
out.println("")
}
- def transcript(start: String) =
+ def transcript(start: String) = {
+ // Printing this message doesn't work very well becaues it's buried in the
+ // transcript they just pasted. Todo: a short timer goes off when
+ // lines stop coming which tells them to hit ctrl-D.
+ //
+ // out.println("// Detected repl transcript paste: ctrl-D to finish.")
apply(Iterator(start) ++ readWhile(_.trim != PromptString.trim))
+ }
}
import paste.{ ContinueString, PromptString }
diff --git a/src/compiler/scala/tools/nsc/interpreter/Pasted.scala b/src/compiler/scala/tools/nsc/interpreter/Pasted.scala
index df106e1f00..83321356a3 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Pasted.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Pasted.scala
@@ -9,12 +9,21 @@ 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.
+ *
+ * Most of the interesting code in here is due to my goal of
+ * "paste idempotence" i.e. the transcript resulting from pasting
+ * a transcript should itself be pasteable and should achieve
+ * the same result.
*/
abstract class Pasted {
def ContinueString: String
def PromptString: String
def interpret(line: String): Unit
+ def matchesPrompt(line: String) = matchesString(line, PromptString)
+ def matchesContinue(line: String) = matchesString(line, ContinueString)
+ def running = isRunning
+
private def matchesString(line: String, target: String): Boolean = (
(line startsWith target) ||
(line.nonEmpty && " \t".toSet(line.head) && matchesString(line.tail, target))
@@ -23,12 +32,61 @@ abstract class Pasted {
case -1 => line
case idx => line drop (idx + target.length)
}
+ private var isRunning = false
+ private val resReference = """(?<!^)(res\d+)""".r
+ private val resCreation = """^\s*(res\d+):.*""".r
+ private val resAssign = """^val (res\d+).*""".r
- def matchesPrompt(line: String) = matchesString(line, PromptString)
- def matchesContinue(line: String) = matchesString(line, ContinueString)
+ private class PasteAnalyzer(val lines: List[String]) {
+ val referenced = lines flatMap (resReference findAllIn _.trim.stripPrefix("res")) toSet
+ val cmds = lines reduceLeft append split PromptString filterNot (_.trim == "") toList
- private var isRunning = false
- def running = isRunning
+ /** If it's a prompt or continuation line, strip the formatting bits and
+ * assemble the code. Otherwise ship it off to be analyzed for res references
+ * and discarded.
+ */
+ def append(code: String, line: String): String =
+ if (matchesPrompt(line)) code + "\n" + line
+ else if (matchesContinue(line)) code + "\n" + stripString(line, ContinueString)
+ else fixResRefs(code, line)
+
+ /** If the line looks like
+ * res15: Int
+ *
+ * and the additional conditions hold that:
+ * 1) res15 is referenced from elsewhere in the transcript
+ * 2) the preceding repl line is not "val res15 = ..." because that
+ * indicates it has already been "val-ified" on a previous paste
+ *
+ * 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.
+ */
+ def fixResRefs(code: String, line: String) = line match {
+ case resCreation(resName) if referenced(resName) =>
+ code.lastIndexOf(PromptString) match {
+ case -1 => code
+ case idx =>
+ val (str1, str2) = code splitAt (idx + PromptString.length)
+ str2 match {
+ case resAssign(`resName`) => code
+ case _ => "%sval %s = { %s }".format(str1, resName, str2)
+ }
+ }
+ case _ => code
+ }
+
+ def run() {
+ println("// Replaying %d commands from transcript.\n" format cmds.size)
+ cmds foreach { cmd =>
+ print(PromptString)
+ interpret(cmd)
+ }
+ }
+ }
/** Commands start on lines beginning with "scala>" and each successive
* line which begins with the continuation string is appended to that command.
@@ -36,33 +94,8 @@ abstract class Pasted {
* 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
+ try new PasteAnalyzer(lines.toList) run()
finally isRunning = false
}
-
- private def append(code: String, line: String): String =
- if (matchesPrompt(line)) code + "\n" + line
- else if (matchesContinue(line)) code + "\n" + stripString(line, 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 = """^\s*(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
index 684f20e1d2..435592567d 100644
--- a/test/files/run/repl-paste-2.check
+++ b/test/files/run/repl-paste-2.check
@@ -3,14 +3,17 @@ Type :help for more information.
scala>
-scala> scala> 123
-res0: Int = 123
+scala> scala> 0123
+res4: Int = 0123
+
+scala> 123
+res5: Int = 123
scala> 567
-res1: Int = 567
+res6: Int = 567
-scala> res0 + res1
-res2: Int = 690
+scala> res5 + res6
+res7: Int = 690
scala> val x = dingus
<console>:7: error: not found: value dingus
@@ -21,35 +24,38 @@ scala> val x = "dingus"
x: java.lang.String = dingus
scala> x.length
-res3: Int = 6
+res9: Int = 6
+
+scala> x.length + res5
+res10: Int = 12
-scala> x.length + res3
-res4: Int = 12
+// Replaying 8 commands from transcript.
-// Replaying 7 commands from transcript.
+scala> 0123
+res0: Int = 83
-val res0 = { 123 }
-res0: Int = 123
+scala> val res5 = { 123 }
+res5: Int = 123
-val res1 = { 567 }
-res1: Int = 567
+scala> val res6 = { 567 }
+res6: Int = 567
-val res2 = { res0 + res1 }
-res2: Int = 690
+scala> res5 + res6
+res1: Int = 690
-val x = dingus
+scala> val x = dingus
<console>:7: error: not found: value dingus
val x = dingus
^
-val x = "dingus"
+scala> val x = "dingus"
x: java.lang.String = dingus
-val res3 = { x.length }
-res3: Int = 6
+scala> x.length
+res2: Int = 6
-val res4 = { x.length + res3 }
-res4: Int = 12
+scala> x.length + res5
+res3: Int = 129
scala>
diff --git a/test/files/run/repl-paste-2.scala b/test/files/run/repl-paste-2.scala
index 802c627701..f62927791d 100644
--- a/test/files/run/repl-paste-2.scala
+++ b/test/files/run/repl-paste-2.scala
@@ -2,14 +2,17 @@ import scala.tools.partest.ReplTest
object Test extends ReplTest {
def code = """
+scala> 0123
+res4: Int = 0123
+
scala> 123
-res0: Int = 123
+res5: Int = 123
scala> 567
-res1: Int = 567
+res6: Int = 567
-scala> res0 + res1
-res2: Int = 690
+scala> res5 + res6
+res7: Int = 690
scala> val x = dingus
<console>:7: error: not found: value dingus
@@ -20,9 +23,9 @@ scala> val x = "dingus"
x: java.lang.String = dingus
scala> x.length
-res3: Int = 6
+res9: Int = 6
-scala> x.length + res3
-res4: Int = 12
+scala> x.length + res5
+res10: Int = 12
"""
} \ No newline at end of file
diff --git a/test/files/run/repl-transcript.check b/test/files/run/repl-transcript.check
index c6563c67a9..03162451b6 100644
--- a/test/files/run/repl-transcript.check
+++ b/test/files/run/repl-transcript.check
@@ -19,19 +19,19 @@ scala> res6.sum + res5
// Replaying 5 commands from transcript.
-class Bippity
+scala> class Bippity
defined class Bippity
-def f = new Bippity
+scala> def f = new Bippity
f: Bippity
-val res5 = { 123 }
+scala> val res5 = { 123 }
res5: Int = 123
-val res6 = { 1 to 100 map (_ + 1) }
+scala> 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
+scala> res6.sum + res5
res0: Int = 5273