@@ -10,12 +10,20 @@ import reporting.Reporter
10
10
import transform .TreeChecker
11
11
import rewrite .Rewrites
12
12
import java .io .{BufferedWriter , OutputStreamWriter }
13
+
14
+ import scala .annotation .tailrec
13
15
import scala .reflect .io .VirtualFile
14
16
import scala .util .control .NonFatal
15
17
16
18
/** A compiler run. Exports various methods to compile source files */
17
19
class Run (comp : Compiler )(implicit ctx : Context ) {
18
20
21
+ private final val ANSI_DEFAULT = " \u001B [0m"
22
+ private final val ANSI_RED = " \u001B [31m"
23
+ private final val ANSI_GREEN = " \u001B [32m"
24
+ private final val ANSI_YELLOW = " \u001B [33m"
25
+ private final val ANSI_MAGENTA = " \u001B [35m"
26
+
19
27
assert(comp.phases.last.last.id <= Periods .MaxPossiblePhaseId )
20
28
assert(ctx.runId <= Periods .MaxPossibleRunId )
21
29
@@ -56,26 +64,103 @@ class Run(comp: Compiler)(implicit ctx: Context) {
56
64
val phases = ctx.squashPhases(ctx.phasePlan,
57
65
ctx.settings.Yskip .value, ctx.settings.YstopBefore .value, ctx.settings.YstopAfter .value, ctx.settings.Ycheck .value)
58
66
ctx.usePhases(phases)
67
+ var lastPrintedTree : PrintedTree = NoPrintedTree
59
68
for (phase <- ctx.allPhases)
60
69
if (! ctx.reporter.hasErrors) {
61
70
val start = System .currentTimeMillis
62
71
units = phase.runOn(units)
63
- def foreachUnit (op : Context => Unit )(implicit ctx : Context ): Unit =
64
- for (unit <- units) op(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
65
- if (ctx.settings.Xprint .value.containsPhase(phase))
66
- foreachUnit(printTree)
72
+ if (ctx.settings.Xprint .value.containsPhase(phase)) {
73
+ for (unit <- units) {
74
+ lastPrintedTree =
75
+ printTree(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit), lastPrintedTree)
76
+ }
77
+ }
67
78
ctx.informTime(s " $phase " , start)
68
79
}
69
80
if (! ctx.reporter.hasErrors) Rewrites .writeBack()
70
81
}
71
82
72
- private def printTree (ctx : Context ) = {
83
+ private sealed trait PrintedTree
84
+ private final case class SomePrintedTree (phase : String , tree : String ) extends PrintedTree
85
+ private object NoPrintedTree extends PrintedTree
86
+
87
+ private def printTree (ctx : Context , last : PrintedTree ): PrintedTree = {
73
88
val unit = ctx.compilationUnit
74
89
val prevPhase = ctx.phase.prev // can be a mini-phase
75
90
val squashedPhase = ctx.squashed(prevPhase)
91
+ val treeString = unit.tpdTree.show(ctx)
92
+
93
+ ctx.echo(s " result of $unit after $squashedPhase: " )
94
+
95
+ last match {
96
+ case SomePrintedTree (phase, lastTreeSting) if lastTreeSting != treeString =>
97
+ ctx.echo(mkColoredDiffTree(treeString, lastTreeSting))
98
+ SomePrintedTree (squashedPhase.toString, treeString)
99
+
100
+ case SomePrintedTree (phase, lastTreeSting) =>
101
+ ctx.echo(" Unchanged since " + phase)
102
+ last
103
+
104
+ case NoPrintedTree =>
105
+ ctx.echo(treeString)
106
+ SomePrintedTree (squashedPhase.toString, treeString)
107
+ }
108
+ }
109
+
110
+ private def mkColoredDiffTree (treeString : String , lastTreeSting : String ): String = {
111
+ import difflib ._
112
+ import scala .collection .JavaConversions ._
113
+
114
+ @ tailrec def split (str : String , acc : List [String ]): List [String ] = {
115
+ if (str == " " ) {
116
+ acc.reverse
117
+ } else {
118
+ val head = str.charAt(0 )
119
+ val (token, rest) = if (Character .isAlphabetic(head) || Character .isDigit(head)) {
120
+ str.span(c => Character .isAlphabetic(c) || Character .isDigit(c))
121
+ } else if (Character .isMirrored(head) || Character .isWhitespace(head)) {
122
+ str.splitAt(1 )
123
+ } else {
124
+ str.span { c =>
125
+ ! Character .isAlphabetic(c) && ! Character .isDigit(c) &&
126
+ ! Character .isMirrored(c) && ! Character .isWhitespace(c)
127
+ }
128
+ }
129
+ split(rest, token :: acc)
130
+ }
131
+ }
132
+
133
+ val lines = split(treeString, Nil ).toArray
134
+
135
+ val printDiffDel = ctx.settings.XprintDiffDel .value
136
+ val diff = DiffUtils .diff(split(lastTreeSting, Nil ), lines.toList)
137
+
138
+ for (delta <- diff.getDeltas) {
139
+ val pos = delta.getRevised.getPosition
140
+ val endPos = pos + delta.getRevised.getLines.size - 1
141
+
142
+ delta.getType.toString match { // Issue #1355 forces us to use the toString
143
+ case " INSERT" =>
144
+ lines(pos) = ANSI_GREEN + lines(pos)
145
+ lines(endPos) = lines(endPos) + ANSI_DEFAULT
146
+
147
+ case " CHANGE" =>
148
+ val old = if (! printDiffDel) " " else
149
+ ANSI_MAGENTA + delta.getOriginal.getLines.mkString + ANSI_DEFAULT
150
+ lines(pos) = old + ANSI_YELLOW + lines(pos)
151
+ lines(endPos) = lines(endPos) + ANSI_DEFAULT
152
+
153
+ case " DELETE" if printDiffDel =>
154
+ val deleted = delta.getOriginal.getLines.mkString
155
+ if (! deleted.forall(Character .isWhitespace)) {
156
+ lines(pos) = ANSI_RED + deleted + ANSI_DEFAULT + lines(pos)
157
+ }
158
+
159
+ case _ =>
160
+ }
161
+ }
76
162
77
- ctx.echo(s " result of $unit after ${squashedPhase}: " )
78
- ctx.echo(unit.tpdTree.show(ctx))
163
+ lines.mkString
79
164
}
80
165
81
166
def compile (sourceCode : String ): Unit = {
0 commit comments