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