# Visualizing the difference between two sequences (strings)

I'm trying to develop a way of comparing two sequences (probably originally text, such as text writing or source code, possibly converted to lists). As a familiar example, consider the revisions display for any typical SE question or the diff output from a command-line diff.

I found a cool Mathematica function called SequenceAlignment which looks promising:

text1 = ExampleData[{"Text", "ToBeOrNotToBe"}];
text2 = StringReplace[text1, {"s" -> "th"}]

(*
To be, or not to be,--that ith the quethtion:-- Whether 'tith nobler
in the mind to thuffer The thlingth and arrowth of outrageouth
fortune Or to take armth againtht a thea of troubleth, And by
oppothing end them? ...
*)

(lisp programming... :)

Now:

sa = SequenceAlignment[text1, text2]

gives:

{"To be, or not to be,--that i", {"s", "th"}, " the que", {"s", "th"},
"tion:-- Whether 'ti", {"s", "th"}, " nobler in the mind to ", {"s", "th"},
"uffer " ...

which I want to convert to some kind of colored display. The best I've managed so far is this:

Reap[
If[Length[#] == 2,
Sow[Column[{Style[#[[1]], Red], Style[#[[2]], Green]}]],
Sow[Style[#, Black]]]
& /@
sa]

but it's not a pretty display:

How can I make this display look like a single piece of text with colored markup, like the SE revisions display? And would it be possible to process Mathematica code as well - without evaluating said code first, obviously?

-
A nice question for a graphical designer. A best fit for this site could be: "I want the output like this. How to do it in Mma?" –  belisarius Oct 19 '12 at 12:07
@belisarius I've focussed the question a bit more. I don't know how to get from the information in sa to a text display. And I don't know whether SequenceAlignment is the right tool for the job... –  cormullion Oct 19 '12 at 12:12
Ok, +1 then. Nice question –  belisarius Oct 19 '12 at 12:15
Your approach might encounter a problem in some cases, try to use text2 = StringReplace[text1, {"s" -> "ss"}] and see how your output looks like. –  VLC Oct 19 '12 at 12:19
@VLC yes, it's not good so far :) –  cormullion Oct 19 '12 at 12:23

Here's a start (perhaps it's better to say continuation since you've already gotten started):

Row@Flatten[sa /. {a_, b_} :> { Style[a, Red], "(", Style[b, Green], ")"}]

By capturing the word fragmentth to the left and right of a, you thhould be able to end up with thomething more like:

-
Yeth, that's exthellent! –  cormullion Oct 20 '12 at 7:31

The undocumented SystemDumpshowStringDiff function neatly does the diff and highlights it for you. The simplest usage is:

SystemDumpshowStringDiff[text1, text2]

You can choose custom colours for the highlights with the Styles option. You can also change the background, font weight, add a strikethrough, etc.:

SystemDumpshowStringDiff[text1, text2, Styles -> {
{Bold, Background -> LightGreen, Darker@Green},
{FontVariations -> {"StrikeThrough" -> True}, Background -> LightRed, Darker@Red}
}]

A related undocumented function SystemDumpshowStringDiffs takes the same arguments and options, but returns a list of two strings with the deletions marked in one and the additions in the other.

-
How did you find this?! +1 –  Mr.Wizard Oct 19 '12 at 15:10
More great hidden functionality! The problem is this has the same problem with wrapping across lines that most of the methods have –  David Slater Oct 19 '12 at 15:10
What are you doing on your free time? ... no, wait, don't answer. –  belisarius Oct 19 '12 at 15:37
Good find - a pretty cool option hidden away. I wonder why it's been included at all if no-one knows about it... –  cormullion Oct 20 '12 at 7:29
+1 Very informative and nicely presented. –  David Carraher Oct 20 '12 at 11:37

Using How to join two Style[]d strings, we can get a decent looking display on the the test case

Flatten[Map[If[Length[#] == 2, {Style[#[[1]], Red, FontVariations -> {"StrikeThrough" -> True}], Style[#[[2]], Green]}, #] &, sa]];
Apply[StringJoin, ToString[#, StandardForm] & /@ %]

Note that with this method text correctly wraps across lines

Update

Using background to do the highlighting doesn't work well since you can't control the background size the default way. How to pad Background size in Style calls is an attempt to do text highlighting, but doesn't work well in this case because of text wrapping issues:

Flatten[Map[
If[Length[#] == 2,
{Framed[Style[#[[1]], FontVariations -> {"StrikeThrough" -> True}],FrameStyle -> None, FrameMargins -> 2, Background -> Lighter@Red],
Framed[#[[2]], FrameStyle -> None, FrameMargins -> 2, Background -> Lighter@Green]},
Framed[#, FrameStyle -> None, FrameMargins -> 2]] &, sa]];
Apply[StringJoin, ToString[#, StandardForm] & /@ %]

-
Like it. Good attempt on background colouring too. –  cormullion Oct 20 '12 at 7:34