diff options
author | Nicolas Stucki <nicolas.stucki@gmail.com> | 2016-06-30 14:47:54 +0200 |
---|---|---|
committer | Nicolas Stucki <nicolas.stucki@gmail.com> | 2016-07-15 14:02:29 +0200 |
commit | f359953e0437c0b9b2205bf30c53c6206c6c0249 (patch) | |
tree | 2b49d9ead3bb6441fec62d8170845f37b4bff833 | |
parent | 409c6c30c8496529aace68967acccf88850145da (diff) | |
download | dotty-f359953e0437c0b9b2205bf30c53c6206c6c0249.tar.gz dotty-f359953e0437c0b9b2205bf30c53c6206c6c0249.tar.bz2 dotty-f359953e0437c0b9b2205bf30c53c6206c6c0249.zip |
Fix #1312: Improve Xprint
* Do not reprint a tree that has not changed.
* Highlight changes with yellow and insertions in green.
* -Xprint-diff-del: Inserts the deleted parts of the tree in red
and the parts that where changed in magenta.
-rw-r--r-- | project/Build.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/Run.scala | 46 | ||||
-rw-r--r-- | src/dotty/tools/dotc/config/ScalaSettings.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/util/DiffUtil.scala | 66 |
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 + } +} |