@@ -70,7 +70,9 @@ object DiffUtil {
70
70
* differences are highlighted.
71
71
*/
72
72
def mkColoredLineDiff (expected : Seq [String ], actual : Seq [String ]): String = {
73
- val expectedSize = EOF .length max expected.maxBy(_.length).length
73
+ val longestExpected = expected.map(_.length).maxOption.getOrElse(0 )
74
+ val longestActual = actual.map(_.length).maxOption.getOrElse(0 )
75
+ val expectedSize = EOF .length max longestActual max longestExpected
74
76
actual.padTo(expected.length, " " ).zip(expected.padTo(actual.length, " " )).map { case (act, exp) =>
75
77
mkColoredLineDiff(exp, act, expectedSize)
76
78
}.mkString(System .lineSeparator)
@@ -101,11 +103,75 @@ object DiffUtil {
101
103
case Deleted (str) => deleted(str)
102
104
}.mkString
103
105
106
+ (expectedDiff, actualDiff)
104
107
val pad = " " * 0 .max(expectedSize - expected.length)
105
108
106
109
expectedDiff + pad + " | " + actualDiff
107
110
}
108
111
112
+ private def ensureLineSeparator (str : String ): String =
113
+ if str.endsWith(System .lineSeparator) then
114
+ str
115
+ else
116
+ str + System .lineSeparator
117
+
118
+ /**
119
+ * Returns a colored diffs by comparison of lines instead of tokens.
120
+ * It will automatically group subsequential pairs of `Insert` and `Delete`
121
+ * in order to improve the readability
122
+ *
123
+ * @param expected The expected lines
124
+ * @param actual The actual lines
125
+ * @return A string with colored diffs between `expected` and `actual` grouped whenever possible
126
+ */
127
+ def mkColoredHorizontalLineDiff (expected : String , actual : String ): String = {
128
+ val indent = 2
129
+ val tab = " " * indent
130
+ val insertIndent = " +" ++ (" " * (indent - 1 ))
131
+ val deleteIndent = " -" ++ (" " * (indent - 1 ))
132
+
133
+ if actual.isEmpty then
134
+ (expected.linesIterator.map(line => added(insertIndent + line)).toList :+ deleted(" --- EMPTY OUTPUT ---" ))
135
+ .map(ensureLineSeparator).mkString
136
+ else if expected.isEmpty then
137
+ (added(" --- NO VALUE EXPECTED ---" ) +: actual.linesIterator.map(line => deleted(deleteIndent + line)).toList)
138
+ .map(ensureLineSeparator).mkString
139
+ else
140
+ lazy val diff = {
141
+ val expectedTokens = expected.linesWithSeparators.toArray
142
+ val actualTokens = actual.linesWithSeparators.toArray
143
+ hirschberg(actualTokens, expectedTokens)
144
+ }.toList
145
+
146
+ val transformedDiff = diff.flatMap {
147
+ case Modified (original, str) => Seq (
148
+ Inserted (ensureLineSeparator(original)), Deleted (ensureLineSeparator(str))
149
+ )
150
+ case other => Seq (other)
151
+ }
152
+
153
+ val zipped = transformedDiff zip transformedDiff.drop(1 )
154
+
155
+ val (acc, inserts, deletions) = zipped.foldLeft((Seq [Patch ](), Seq [Inserted ](), Seq [Deleted ]())): (acc, patches) =>
156
+ val (currAcc, inserts, deletions) = acc
157
+ patches match
158
+ case (currentPatch : Inserted , nextPatch : Deleted ) =>
159
+ (currAcc, inserts :+ currentPatch, deletions)
160
+ case (currentPatch : Deleted , nextPatch : Inserted ) =>
161
+ (currAcc, inserts, deletions :+ currentPatch)
162
+ case (currentPatch, nextPatch) =>
163
+ (currAcc :++ inserts :++ deletions :+ currentPatch, Seq .empty, Seq .empty)
164
+
165
+ val stackedDiff = acc :++ inserts :++ deletions :+ diff.last
166
+
167
+ stackedDiff.collect {
168
+ case Unmodified (str) => tab + str
169
+ case Inserted (str) => added(insertIndent + str)
170
+ case Deleted (str) => deleted(deleteIndent + str)
171
+ }.map(ensureLineSeparator).mkString
172
+
173
+ }
174
+
109
175
def mkColoredCodeDiff (code : String , lastCode : String , printDiffDel : Boolean ): String = {
110
176
val tokens = splitTokens(code, Nil ).toArray
111
177
val lastTokens = splitTokens(lastCode, Nil ).toArray
0 commit comments