diff --git a/core/src/main/scala/com/softwaremill/diffx/instances/DiffForString.scala b/core/src/main/scala/com/softwaremill/diffx/instances/DiffForString.scala index 6a8c87c1..6e7ec377 100644 --- a/core/src/main/scala/com/softwaremill/diffx/instances/DiffForString.scala +++ b/core/src/main/scala/com/softwaremill/diffx/instances/DiffForString.scala @@ -11,12 +11,51 @@ class DiffForString(similarityThreshold: Double = 0.5) extends Diff[String] { val rows = generator.generateDiffRows(splitIntoLines(left), splitIntoLines(right)) val lineResults = processLineDiffs(rows) if (lineResults.forall(_.isIdentical)) { - IdenticalValue(left) + if (left == right) { + IdenticalValue(left) + } else { + val resultsByChar = + generator.generateDiffRows[Int](left.map(c => c.toInt).toList, right.map(c => c.toInt).toList) + val resultsAsString: List[DiffRow[String]] = resultsByChar.map { + case DiffRow.Insert(newLine) => DiffRow.Insert(newLine.toChar.toString) + case DiffRow.Delete(oldLine) => DiffRow.Delete(oldLine.toChar.toString) + case DiffRow.Change(oldLine, newLine) => DiffRow.Change(oldLine.toChar.toString, newLine.toChar.toString) + case DiffRow.Equal(oldLine, newLine) => DiffRow.Equal(oldLine.toChar.toString, newLine.toChar.toString) + } + DiffResultString(mergeIdentical(resultsAsString)) + } } else { DiffResultString(lineResults) } } + private def mergeIdentical(rows: List[DiffRow[String]]): List[DiffResult] = { + rows + .foldLeft(List[DiffResult](IdenticalValue[String](""))) { (acc, item) => + acc match { + case list @ ::(IdenticalValue(head: String), next) => + item match { + case DiffRow.Insert("\n") => DiffResultMissing("") :: list + case DiffRow.Insert(newLine) => DiffResultMissing(newLine) :: list + case DiffRow.Delete("\n") => DiffResultAdditional("") :: list + case DiffRow.Delete(oldLine) => DiffResultAdditional(oldLine) :: list + case DiffRow.Change(oldLine, newLine) => DiffResultValue(oldLine, newLine) :: list + case DiffRow.Equal(oldLine, newLine) => IdenticalValue(head ++ oldLine) :: next + } + case other => + item match { + case DiffRow.Insert("\n") => DiffResultMissing("") :: other + case DiffRow.Insert(newLine) => DiffResultMissing(newLine) :: other + case DiffRow.Delete("\n") => DiffResultAdditional("") :: other + case DiffRow.Delete(oldLine) => DiffResultAdditional(oldLine) :: other + case DiffRow.Change(oldLine, newLine) => DiffResultValue(oldLine, newLine) :: other + case DiffRow.Equal(oldLine, newLine) => IdenticalValue(oldLine) :: other + } + } + } + .reverse + } + private def processLineDiffs(rows: List[DiffRow[String]]) = { rows.map { case DiffRow.Insert(newLine) => DiffResultMissing(newLine) @@ -56,8 +95,10 @@ class DiffForString(similarityThreshold: Double = 0.5) extends Diff[String] { .map(_.mkString) } - private def processWordDiffs(words: List[DiffRow[String]]): List[DiffResult] = { - words.map { + private def processWordDiffs(words: List[DiffRow[String]]): List[DiffResult] = words.map(processWordDiff) + + private def processWordDiff(wd: DiffRow[String]) = { + wd match { case DiffRow.Insert(newLine) => DiffResultMissingChunk(newLine) case DiffRow.Delete(oldLine) => DiffResultAdditionalChunk(oldLine) case DiffRow.Change("", newLine) => DiffResultMissingChunk(newLine) diff --git a/core/src/test/scala/com/softwaremill/diffx/test/DiffStringTest.scala b/core/src/test/scala/com/softwaremill/diffx/test/DiffStringTest.scala index 9aea6ca9..6e7825dd 100644 --- a/core/src/test/scala/com/softwaremill/diffx/test/DiffStringTest.scala +++ b/core/src/test/scala/com/softwaremill/diffx/test/DiffStringTest.scala @@ -5,6 +5,7 @@ import com.softwaremill.diffx.{ DiffResultAdditional, DiffResultAdditionalChunk, DiffResultChunk, + DiffResultMissing, DiffResultMissingChunk, DiffResultString, DiffResultStringLine, @@ -239,5 +240,16 @@ class DiffStringTest extends AnyFreeSpec with Matchers { List(IdenticalValue("bob"), IdenticalValue("and mark"), DiffResultAdditional("alice went to school")) ) } + + "additional empty line should make a difference" in { + diffForString("hello", "hello\n") shouldBe DiffResultString(List(IdenticalValue("hello"), DiffResultMissing(""))) + diffForString("hello\n", "hello") shouldBe DiffResultString( + List(IdenticalValue("hello"), DiffResultAdditional("")) + ) + diffForString("\nhello", "hello") shouldBe DiffResultString( + List(DiffResultAdditional(""), IdenticalValue("hello")) + ) + diffForString("hello", "\nhello") shouldBe DiffResultString(List(DiffResultMissing(""), IdenticalValue("hello"))) + } } }