aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--project/Build.scala1
-rw-r--r--src/dotty/tools/dotc/Run.scala46
-rw-r--r--src/dotty/tools/dotc/config/ScalaSettings.scala1
-rw-r--r--src/dotty/tools/dotc/util/DiffUtil.scala66
4 files changed, 105 insertions, 9 deletions
diff --git a/project/Build.scala b/project/Build.scala
index b53cf1081..f57c30e7d 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -100,6 +100,7 @@ object DottyBuild extends Build {
"org.scala-lang.modules" %% "scala-partest" % "1.0.11" % "test",
"com.novocode" % "junit-interface" % "0.11" % "test",
"jline" % "jline" % "2.12",
+ "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0",
"com.typesafe.sbt" % "sbt-interface" % sbtVersion.value),
// enable improved incremental compilation algorithm
incOptions := incOptions.value.withNameHashing(true),
diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala
index 928a59214..1c500c913 100644
--- a/src/dotty/tools/dotc/Run.scala
+++ b/src/dotty/tools/dotc/Run.scala
@@ -2,14 +2,20 @@ package dotty.tools
package dotc
import core._
-import Contexts._, Periods._, Symbols._, Phases._, Decorators._
+import Contexts._
+import Periods._
+import Symbols._
+import Phases._
+import Decorators._
import dotty.tools.dotc.transform.TreeTransforms.TreeTransformer
import io.PlainFile
-import util.{SourceFile, NoSource, Stats, SimpleMap}
+import util._
import reporting.Reporter
import transform.TreeChecker
import rewrite.Rewrites
import java.io.{BufferedWriter, OutputStreamWriter}
+
+import scala.annotation.tailrec
import scala.reflect.io.VirtualFile
import scala.util.control.NonFatal
@@ -56,26 +62,48 @@ class Run(comp: Compiler)(implicit ctx: Context) {
val phases = ctx.squashPhases(ctx.phasePlan,
ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, ctx.settings.YstopAfter.value, ctx.settings.Ycheck.value)
ctx.usePhases(phases)
+ var lastPrintedTree: PrintedTree = NoPrintedTree
for (phase <- ctx.allPhases)
if (!ctx.reporter.hasErrors) {
val start = System.currentTimeMillis
units = phase.runOn(units)
- def foreachUnit(op: Context => Unit)(implicit ctx: Context): Unit =
- for (unit <- units) op(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
- if (ctx.settings.Xprint.value.containsPhase(phase))
- foreachUnit(printTree)
+ if (ctx.settings.Xprint.value.containsPhase(phase)) {
+ for (unit <- units) {
+ lastPrintedTree =
+ printTree(lastPrintedTree)(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
+ }
+ }
ctx.informTime(s"$phase ", start)
}
if (!ctx.reporter.hasErrors) Rewrites.writeBack()
}
- private def printTree(ctx: Context) = {
+ private sealed trait PrintedTree
+ private final case class SomePrintedTree(phase: String, tree: String) extends PrintedTree
+ private object NoPrintedTree extends PrintedTree
+
+ private def printTree(last: PrintedTree)(implicit ctx: Context): PrintedTree = {
val unit = ctx.compilationUnit
val prevPhase = ctx.phase.prev // can be a mini-phase
val squashedPhase = ctx.squashed(prevPhase)
+ val treeString = unit.tpdTree.show
+
+ ctx.echo(s"result of $unit after $squashedPhase:")
- ctx.echo(s"result of $unit after ${squashedPhase}:")
- ctx.echo(unit.tpdTree.show(ctx))
+ last match {
+ case SomePrintedTree(phase, lastTreeSting) if lastTreeSting != treeString =>
+ val diff = DiffUtil.mkColoredCodeDiff(treeString, lastTreeSting, ctx.settings.XprintDiffDel.value)
+ ctx.echo(diff)
+ SomePrintedTree(squashedPhase.toString, treeString)
+
+ case SomePrintedTree(phase, lastTreeSting) =>
+ ctx.echo(" Unchanged since " + phase)
+ last
+
+ case NoPrintedTree =>
+ ctx.echo(treeString)
+ SomePrintedTree(squashedPhase.toString, treeString)
+ }
}
def compile(sourceCode: String): Unit = {
diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala
index bf56a486e..989197981 100644
--- a/src/dotty/tools/dotc/config/ScalaSettings.scala
+++ b/src/dotty/tools/dotc/config/ScalaSettings.scala
@@ -85,6 +85,7 @@ class ScalaSettings extends Settings.SettingGroup {
val writeICode = PhasesSetting("-Xprint-icode", "Log internal icode to *.icode files after", "icode")
val Xprintpos = BooleanSetting("-Xprint-pos", "Print tree positions, as offsets.")
val printtypes = BooleanSetting("-Xprint-types", "Print tree types (debugging option).")
+ val XprintDiffDel = BooleanSetting("-Xprint-diff-del", "Print deleted parts of the tree since last print.")
val prompt = BooleanSetting("-Xprompt", "Display a prompt after each error (debugging option).")
val script = StringSetting("-Xscript", "object", "Treat the source file as a script and wrap it in a main method.", "")
val mainClass = StringSetting("-Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d <jar>)", "")
diff --git a/src/dotty/tools/dotc/util/DiffUtil.scala b/src/dotty/tools/dotc/util/DiffUtil.scala
new file mode 100644
index 000000000..6718ccbf8
--- /dev/null
+++ b/src/dotty/tools/dotc/util/DiffUtil.scala
@@ -0,0 +1,66 @@
+package dotty.tools.dotc.util
+
+import scala.annotation.tailrec
+import difflib._
+
+object DiffUtil {
+
+ private final val ANSI_DEFAULT = "\u001B[0m"
+ private final val ANSI_RED = "\u001B[31m"
+ private final val ANSI_GREEN = "\u001B[32m"
+ private final val ANSI_YELLOW = "\u001B[33m"
+ private final val ANSI_MAGENTA = "\u001B[35m"
+
+ def mkColoredCodeDiff(code: String, lastCode: String, printDiffDel: Boolean): String = {
+ import scala.collection.JavaConversions._
+
+ @tailrec def split(str: String, acc: List[String]): List[String] = {
+ if (str == "") {
+ acc.reverse
+ } else {
+ val head = str.charAt(0)
+ val (token, rest) = if (Character.isAlphabetic(head) || Character.isDigit(head)) {
+ str.span(c => Character.isAlphabetic(c) || Character.isDigit(c))
+ } else if (Character.isMirrored(head) || Character.isWhitespace(head)) {
+ str.splitAt(1)
+ } else {
+ str.span { c =>
+ !Character.isAlphabetic(c) && !Character.isDigit(c) &&
+ !Character.isMirrored(c) && !Character.isWhitespace(c)
+ }
+ }
+ split(rest, token :: acc)
+ }
+ }
+
+ val lines = split(code, Nil).toArray
+ val diff = DiffUtils.diff(split(lastCode, Nil), lines.toList)
+
+ for (delta <- diff.getDeltas) {
+ val pos = delta.getRevised.getPosition
+ val endPos = pos + delta.getRevised.getLines.size - 1
+
+ delta.getType.toString match { // Issue #1355 forces us to use the toString
+ case "INSERT" =>
+ lines(pos) = ANSI_GREEN + lines(pos)
+ lines(endPos) = lines(endPos) + ANSI_DEFAULT
+
+ case "CHANGE" =>
+ val old = if (!printDiffDel) "" else
+ ANSI_MAGENTA + delta.getOriginal.getLines.mkString + ANSI_DEFAULT
+ lines(pos) = old + ANSI_YELLOW + lines(pos)
+ lines(endPos) = lines(endPos) + ANSI_DEFAULT
+
+ case "DELETE" if printDiffDel =>
+ val deleted = delta.getOriginal.getLines.mkString
+ if (!deleted.forall(Character.isWhitespace)) {
+ lines(pos) = ANSI_RED + deleted + ANSI_DEFAULT + lines(pos)
+ }
+
+ case _ =>
+ }
+ }
+
+ lines.mkString
+ }
+}