diff options
author | Martin Odersky <odersky@gmail.com> | 2012-08-02 18:42:01 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2012-08-03 13:28:11 +0200 |
commit | f16e2c0874b4ecd4d5f0609f7a1c6a429d11c187 (patch) | |
tree | 2ffee893d6958c041a0f0cc475c476a8bf6b20bd | |
parent | 217aaf7497990cf5cd23bca2bb57b5b6ea708873 (diff) | |
download | scala-f16e2c0874b4ecd4d5f0609f7a1c6a429d11c187.tar.gz scala-f16e2c0874b4ecd4d5f0609f7a1c6a429d11c187.tar.bz2 scala-f16e2c0874b4ecd4d5f0609f7a1c6a429d11c187.zip |
Better worksheet support.
We now move instrumentation info to the end of lines where possible so that line and column positions are kept unchanged wrt original source. This is important for error reporting.
4 files changed, 40 insertions, 19 deletions
diff --git a/src/compiler/scala/tools/nsc/interactive/REPL.scala b/src/compiler/scala/tools/nsc/interactive/REPL.scala index 6876ea14e0..65e7eb5fc6 100644 --- a/src/compiler/scala/tools/nsc/interactive/REPL.scala +++ b/src/compiler/scala/tools/nsc/interactive/REPL.scala @@ -124,9 +124,9 @@ object REPL { * @param iContents An Array[Char] containing the instrumented source * @return The name of the instrumented source file */ - def writeInstrumented(iFullName: String, iContents: Array[Char]): String = { + def writeInstrumented(iFullName: String, suffix: String, iContents: Array[Char]): String = { val iSimpleName = iFullName drop ((iFullName lastIndexOf '.') + 1) - val iSourceName = iSimpleName + "$instrumented.scala" + val iSourceName = iSimpleName + suffix val ifile = new FileWriter(iSourceName) ifile.write(iContents) ifile.close() @@ -186,18 +186,20 @@ 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[String] = { + def instrument(arguments: List[String], line: Int): Option[(String, 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)) + val strippedContents = SourceInserter.stripRight(source.content) + val strippedSource = new BatchSourceFile(source.file, strippedContents) println("stripped source = "+strippedSource) comp.askReload(List(strippedSource), reloadResult) comp.askInstrumented(strippedSource, line, instrumentedResult) using(instrumentedResult) { case (iFullName, iContents) => println(s"instrumented source $iFullName = ${iContents.mkString}") - val iSourceName = writeInstrumented(iFullName, iContents) - iSourceName + val iSourceName = writeInstrumented(iFullName, "$instrumented.scala", iContents) + val sSourceName = writeInstrumented(iFullName, "$stripped.scala", strippedContents) + (iSourceName, sSourceName) /* * val vdirOpt = compileInstrumented(iSourceName, arguments.tail) runInstrumented(vdirOpt, iFullName, strippedSource.content) @@ -227,9 +229,9 @@ object REPL { case List("complete", file, off1) => doComplete(makePos(file, off1, off1)) case "instrument" :: arguments => - println(instrument(arguments, -1).map(_.mkString)) + println(instrument(arguments, -1)) case "instrumentTo" :: line :: arguments => - println(instrument(arguments, line.toInt).map(_.mkString)) + println(instrument(arguments, line.toInt)) case List("quit") => comp.askShutdown() exit(1) // Don't use sys yet as this has to run on 2.8.2 also. diff --git a/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala b/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala index f7b1c42fd6..efc393c812 100644 --- a/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala +++ b/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala @@ -3,7 +3,7 @@ package interactive import scala.reflect.internal.util.{SourceFile, BatchSourceFile, RangePosition} import collection.mutable.ArrayBuffer -import reflect.internal.Chars.isLineBreakChar +import reflect.internal.Chars.{isLineBreakChar, isWhitespace} trait ScratchPadMaker { self: Global => @@ -40,14 +40,28 @@ trait ScratchPadMaker { self: Global => toPrint.clear() } + /** The position where to insert an instrumentation statement in front of giuven statement. + * This is at the latest `stat.pos.start`. But in order not to mess with column numbers + * in position we try to insert it at the end of the preceding line instead. + * To be safe, this can be done only if there's only whitespace between that position and + * statement's start position. + */ + private def instrumentPos(stat: Tree): Int = { + var start = stat.pos.start + while (start > 0 && isWhitespace(contents(start - 1))) start -= 1 + if (start > 0 && isLineBreakChar(contents(start - 1))) start -= 1 + start + } + private def addSkip(stat: Tree): Unit = { - if (stat.pos.start > skipped) applyPendingPatches(stat.pos.start) + val ipos = instrumentPos(stat) + if (stat.pos.start > skipped) applyPendingPatches(ipos) if (stat.pos.start >= endOffset) - patches += Patch(stat.pos.start, ";$stop()") + patches += Patch(ipos, ";$stop()") var end = stat.pos.end if (end > skipped) { while (end < contents.length && !isLineBreakChar(contents(end))) end += 1 - patches += Patch(stat.pos.start, ";$skip("+(end-skipped)+"); ") + patches += Patch(ipos, ";$skip("+(end-skipped)+"); ") skipped = end } } diff --git a/src/compiler/scala/tools/nsc/scratchpad/Mixer.scala b/src/compiler/scala/tools/nsc/scratchpad/Mixer.scala index 46ccc32097..67ff916b11 100644 --- a/src/compiler/scala/tools/nsc/scratchpad/Mixer.scala +++ b/src/compiler/scala/tools/nsc/scratchpad/Mixer.scala @@ -23,7 +23,7 @@ class Mixer { val nextSpace = comments indexOf (' ', idx) var nextNL = comments indexOf ('\n', nextSpace + 1) if (nextNL < 0) nextNL = comments.length - val result = + val result = (new String(comments.slice(idx, nextSpace)).toInt, comments.slice(nextSpace + 1, nextNL)) idx = nextNL + 1 result @@ -46,7 +46,10 @@ class Mixer { mixed += '\n' col = 0 } - mixed ++= (" " * (sepColumn - col)) + while (col < sepColumn) { + mixed += ' ' + col += 1 + } } for ((offset, cs) <- parseComments(comments)) { val sep = diff --git a/src/library/scala/runtime/WorksheetSupport.scala b/src/library/scala/runtime/WorksheetSupport.scala index f9ffb9197b..6f2a4d382d 100644 --- a/src/library/scala/runtime/WorksheetSupport.scala +++ b/src/library/scala/runtime/WorksheetSupport.scala @@ -18,7 +18,7 @@ object WorksheetSupport { protected def width = 80 // output width, by default 80 characters protected def tabInc = 8 // tab increment, by default 8 characters private var lastFlush: Long = 0L - private var col = 0 + private var col = -1 override def write(b: Array[Byte], off: Int, len: Int) = { for (idx <- off until (off + len min b.length)) writeOne(b(idx)) flush() @@ -35,16 +35,18 @@ object WorksheetSupport { } } def writeOne(c: Int) { - if (col == 0) + if (col < 0) { + col = 0 write((currentOffset+" ").getBytes) + } out.write(c) col = - if (c == '\n') 0 + if (c == '\n') -1 else if (c == '\t') (col / tabInc) * tabInc + tabInc else col + 1 if (col >= width) writeOne('\n') } - def ensureNewLine() = if (col != 0) writeOne('\n') + def ensureNewLine() = if (col > 0) writeOne('\n') } private val flushedOut = new FlushedOutputStream(System.out) @@ -73,7 +75,7 @@ object WorksheetSupport { try op catch { case ex: StopException => ; - case ex => ex.printStackTrace() + case ex: Throwable => ex.printStackTrace() } } |