summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2012-07-27 15:34:39 +0200
committerMartin Odersky <odersky@gmail.com>2012-07-27 17:04:36 +0200
commite3bfe109732b7c1cce7493b2869614ed145660f6 (patch)
tree908a7b854cc3e5d26e324fc6e1d4e5b05d4a104d
parentb964bac5e295df92d304e97657d01481d63f2d62 (diff)
downloadscala-e3bfe109732b7c1cce7493b2869614ed145660f6.tar.gz
scala-e3bfe109732b7c1cce7493b2869614ed145660f6.tar.bz2
scala-e3bfe109732b7c1cce7493b2869614ed145660f6.zip
New Worksheet mixing scheme
Debug changes
-rw-r--r--src/compiler/scala/tools/nsc/interactive/REPL.scala11
-rw-r--r--src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala16
-rw-r--r--src/compiler/scala/tools/nsc/scratchpad/Executor.scala84
-rw-r--r--src/compiler/scala/tools/nsc/scratchpad/Mixer.scala98
-rw-r--r--src/library/scala/runtime/WorksheetSupport.scala87
5 files changed, 196 insertions, 100 deletions
diff --git a/src/compiler/scala/tools/nsc/interactive/REPL.scala b/src/compiler/scala/tools/nsc/interactive/REPL.scala
index a19463bee7..6876ea14e0 100644
--- a/src/compiler/scala/tools/nsc/interactive/REPL.scala
+++ b/src/compiler/scala/tools/nsc/interactive/REPL.scala
@@ -11,7 +11,7 @@ import scala.tools.nsc.symtab._
import scala.tools.nsc.ast._
import scala.tools.nsc.reporters._
import scala.tools.nsc.io._
-import scala.tools.nsc.scratchpad.{Executor, SourceInserter}
+import scala.tools.nsc.scratchpad.SourceInserter
import scala.tools.nsc.interpreter.AbstractFileClassLoader
import java.io.{File, FileWriter}
@@ -138,7 +138,6 @@ object REPL {
* @param arguments Further argumenrs to pass to the compiler
* @return Optionallu, if no -d option is given, the virtual directory
* contained the generated bytecode classes
- */
def compileInstrumented(iSourceName: String, arguments: List[String]): Option[AbstractFile] = {
println("compiling "+iSourceName)
val command = new CompilerCommand(iSourceName :: arguments, reporter.error(scala.reflect.internal.util.NoPosition, _))
@@ -176,6 +175,7 @@ object REPL {
println("done")
si.currentContents
}
+ */
/** The method for implementing worksheet functionality.
* @param arguments a file name, followed by optional command line arguments that are passed
@@ -186,7 +186,7 @@ object REPL {
* and outputs in the right column, or None if the presentation compiler
* does not respond to askInstrumented.
*/
- def instrument(arguments: List[String], line: Int): Option[Array[Char]] = {
+ def instrument(arguments: List[String], line: Int): Option[String] = {
val source = toSourceFile(arguments.head)
// strip right hand side comment column and any trailing spaces from all lines
val strippedSource = new BatchSourceFile(source.file, SourceInserter.stripRight(source.content))
@@ -197,8 +197,11 @@ object REPL {
case (iFullName, iContents) =>
println(s"instrumented source $iFullName = ${iContents.mkString}")
val iSourceName = writeInstrumented(iFullName, iContents)
- val vdirOpt = compileInstrumented(iSourceName, arguments.tail)
+ iSourceName
+/*
+ * val vdirOpt = compileInstrumented(iSourceName, arguments.tail)
runInstrumented(vdirOpt, iFullName, strippedSource.content)
+ */
}
}
diff --git a/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala b/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala
index 45f7c20ba0..bd1869e1a4 100644
--- a/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala
+++ b/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala
@@ -29,18 +29,10 @@ trait ScratchPadMaker { self: Global =>
private def nameType(sym: Symbol): String = nameType(sym.name.toString, sym.tpe)
private def literal(str: String) = "\"\"\""+str+"\"\"\""
-
- private val prologue = "import scala.tools.nsc.scratchpad.Executor._; def main(args: Array[String])=$execute{"
-
- def stringifiedContents = {
- contents flatMap {
- case '$' => """$$"""
- case '"' => """${'"'}"""
- case c => c.toString
- }
- }.mkString
-
- private def epilogue = "}(s\"\"\"" + stringifiedContents + "\"\"\")"
+
+ private val prologue = "import scala.runtime.WorksheetSupport._; def main(args: Array[String])=$execute{"
+
+ private val epilogue = "}"
private def applyPendingPatches(offset: Int) = {
if (skipped == 0) patches += Patch(offset, prologue)
diff --git a/src/compiler/scala/tools/nsc/scratchpad/Executor.scala b/src/compiler/scala/tools/nsc/scratchpad/Executor.scala
deleted file mode 100644
index d7b93b0031..0000000000
--- a/src/compiler/scala/tools/nsc/scratchpad/Executor.scala
+++ /dev/null
@@ -1,84 +0,0 @@
-package scala.tools.nsc.scratchpad
-
-import java.io.{PrintStream, OutputStreamWriter, Writer}
-
-import scala.runtime.ScalaRunTime.stringOf
-import java.lang.reflect.InvocationTargetException
-import scala.reflect.runtime.ReflectionUtils._
-
-object Executor {
-
- println("exec started")
-
- private var currentWriter: CommentWriter = null
-
- /** Execute module with given name, redirecting all output to given
- * source inserter. Catch all exceptions and print stacktrace of underlying causes.
- */
- @deprecated("use $execute instead")
- def execute(name: String, si: SourceInserter, classLoader: ClassLoader = getClass.getClassLoader) {
- val oldSysOut = System.out
- val oldSysErr = System.err
- val oldConsOut = Console.out
- val oldConsErr = Console.err
- val oldCwr = currentWriter
- currentWriter = new CommentWriter(si)
- val newOut = new PrintStream(new CommentOutputStream(currentWriter))
- System.setOut(newOut)
- System.setErr(newOut)
- Console.setOut(newOut)
- Console.setErr(newOut)
- try {
- singletonInstance(classLoader, name)
- } catch {
- case ex: Throwable =>
- unwrapThrowable(ex) match {
- case _: StopException => ;
- case cause => cause.printStackTrace()
- }
- } finally {
- currentWriter.close()
- System.setOut(oldSysOut)
- System.setErr(oldSysErr)
- Console.setOut(oldConsOut)
- Console.setErr(oldConsErr)
- currentWriter = oldCwr
- }
- }
-
- def $execute(op: => Unit)(source: String) = {
- val oldSysOut = System.out
- val oldSysErr = System.err
- val oldConsOut = Console.out
- val oldConsErr = Console.err
- val si = new SourceInserter(source.toCharArray)
- currentWriter = new CommentWriter(si)
- val newOut = new PrintStream(new CommentOutputStream(currentWriter))
- System.setOut(newOut)
- System.setErr(newOut)
- Console.setOut(newOut)
- Console.setErr(newOut)
- try {
- op
- } catch {
- case ex: StopException =>
- case ex: Throwable => ex.printStackTrace()
- } finally {
- currentWriter.close()
- System.setOut(oldSysOut)
- System.setErr(oldSysErr)
- Console.setOut(oldConsOut)
- Console.setErr(oldConsErr)
- println("done")
- println(si.currentContents.mkString)
- }
- }
-
- def $skip(n: Int) = currentWriter.skip(n)
-
- def $stop() = throw new StopException
-
- def $show(x: Any): String = stringOf(x, scala.Int.MaxValue)
-}
-
-class StopException extends Exception
diff --git a/src/compiler/scala/tools/nsc/scratchpad/Mixer.scala b/src/compiler/scala/tools/nsc/scratchpad/Mixer.scala
new file mode 100644
index 0000000000..46ccc32097
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/scratchpad/Mixer.scala
@@ -0,0 +1,98 @@
+package scala.tools.nsc.scratchpad
+
+import java.io.{FileInputStream, InputStreamReader, IOException}
+
+import scala.runtime.ScalaRunTime.stringOf
+import java.lang.reflect.InvocationTargetException
+import scala.reflect.runtime.ReflectionUtils._
+import collection.mutable.ArrayBuffer
+
+class Mixer {
+
+ protected val stdSeparator = "//> "
+ protected val ctdSeparator = "//| "
+ protected val sepColumn = 50
+ protected val tabInc = 8
+
+ type Comments = Seq[(Int, Array[Char])]
+
+ def parseComments(comments: Array[Char]): Iterator[(Int, Array[Char])] = new Iterator[(Int, Array[Char])] {
+ var idx = 0
+ def hasNext = idx < comments.length
+ def next() = {
+ val nextSpace = comments indexOf (' ', idx)
+ var nextNL = comments indexOf ('\n', nextSpace + 1)
+ if (nextNL < 0) nextNL = comments.length
+ val result =
+ (new String(comments.slice(idx, nextSpace)).toInt, comments.slice(nextSpace + 1, nextNL))
+ idx = nextNL + 1
+ result
+ }
+ }
+
+ def mix(source: Array[Char], comments: Array[Char]): Array[Char] = {
+ val mixed = new ArrayBuffer[Char]
+ var written = 0
+ def align() = {
+ var idx = mixed.lastIndexOf('\n') + 1
+ var col = 0
+ while (idx < mixed.length) {
+ col =
+ if (mixed(idx) == '\t') (col / tabInc) * tabInc + tabInc
+ else col + 1
+ idx += 1
+ }
+ if (col > sepColumn) {
+ mixed += '\n'
+ col = 0
+ }
+ mixed ++= (" " * (sepColumn - col))
+ }
+ for ((offset, cs) <- parseComments(comments)) {
+ val sep =
+ if (written < offset) {
+ for (i <- written until offset) mixed += source(i)
+ written = offset
+ stdSeparator
+ } else {
+ mixed += '\n'
+ ctdSeparator
+ }
+ align()
+ mixed ++= sep ++= cs
+ }
+ mixed ++= source.view(written, source.length)
+ mixed.toArray
+ }
+
+}
+
+object Mixer extends Mixer {
+
+ def contents(name: String): Array[Char] = {
+ val page = new Array[Char](2 << 14)
+ val buf = new ArrayBuffer[Char]
+ val in = new FileInputStream(name)
+ val rdr = new InputStreamReader(in)
+ var nread = 0
+ do {
+ nread = rdr.read(page, 0, page.length)
+ buf ++= (if (nread == page.length) page else page.take(nread))
+ } while (nread >= 0)
+ buf.toArray
+ }
+
+ def main(args: Array[String]) {
+ val mixer = new Mixer
+ try {
+ require(args.length == 2, "required arguments: file1 file2")
+ val source = contents(args(0))
+ val comments = contents(args(1))
+ val mixed = mixer.mix(source, comments)
+ println(mixed.mkString)
+ } catch {
+ case ex: IOException =>
+ println("error: "+ ex.getMessage)
+ }
+ }
+}
diff --git a/src/library/scala/runtime/WorksheetSupport.scala b/src/library/scala/runtime/WorksheetSupport.scala
new file mode 100644
index 0000000000..db6d6359a3
--- /dev/null
+++ b/src/library/scala/runtime/WorksheetSupport.scala
@@ -0,0 +1,87 @@
+package scala.runtime
+import java.io.{OutputStream, PrintStream}
+import scala.runtime.ScalaRunTime.stringOf
+
+/** A utility object that's needed by the code that executes a worksheet.
+ */
+object WorksheetSupport {
+
+ /** The offset in the source which should be printed */
+ private var currentOffset = 0
+
+ /** A stream that flushes in regular intervals so that output can be captured
+ * in real time. The flush interval is determined by the field "flushInterval".
+ * By default it is 30ms.
+ */
+ private class FlushedOutputStream(out: OutputStream) extends OutputStream {
+ private var lastFlush: Long = 0L
+ protected val flushInterval = 30000000L // 30ms
+ private var lastCh: Int = '\n'
+ override def write(b: Array[Byte], off: Int, len: Int) = {
+ for (idx <- off until (off + len min b.length)) writeOne(b(idx))
+ flush()
+ }
+ override def write(c: Int) {
+ writeOne(c)
+ flush()
+ }
+ override def flush() {
+ val current = System.nanoTime
+ if (current - lastFlush >= flushInterval) {
+ out.flush()
+ lastFlush = current
+ }
+ }
+ def writeOne(c: Int) {
+ if (lastCh == '\n') {
+ lastCh = 0
+ write((currentOffset+" ").getBytes)
+ }
+ out.write(c)
+ lastCh = c
+ }
+ def ensureNewLine() = if (lastCh != '\n') writeOne('\n')
+ }
+
+ private val flushedOut = new FlushedOutputStream(System.out)
+ private val printOut = new PrintStream(flushedOut)
+
+ private def redirected(op: => Unit) = {
+ val oldSysOut = System.out
+ val oldSysErr = System.err
+ val oldConsOut = Console.out
+ val oldConsErr = Console.err
+ System.setOut(printOut)
+ System.setErr(printOut)
+ Console.setOut(printOut)
+ Console.setErr(printOut)
+ try op
+ finally {
+ printOut.close()
+ System.setOut(oldSysOut)
+ System.setErr(oldSysErr)
+ Console.setOut(oldConsOut)
+ Console.setErr(oldConsErr)
+ }
+ }
+
+ def $execute(op: => Unit) = redirected {
+ try op
+ catch {
+ case ex: StopException => ;
+ case ex => ex.printStackTrace()
+ }
+ }
+
+ def $skip(n: Int) = {
+ flushedOut.ensureNewLine()
+ currentOffset += n
+ }
+
+ def $stop() = throw new StopException
+
+ def $show(x: Any): String = stringOf(x, scala.Int.MaxValue)
+}
+
+class StopException extends Exception
+