54

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:

my attempt

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?

cormullion
  • 24,243
  • 4
  • 64
  • 133

3 Answers3

46

The undocumented System`Dump`showStringDiff function neatly does the diff and highlights it for you. The simplest usage is:

System`Dump`showStringDiff[text1, text2]

diff

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

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

styled diff

A related undocumented function System`Dump`showStringDiffs 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.

Mathematica graphics

rm -rf
  • 88,781
  • 21
  • 293
  • 472
25

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], ")"}]

syntax

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

enter image description here

DavidC
  • 16,724
  • 1
  • 42
  • 94
16

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] & /@ %]

enter image description here

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] & /@ %]

enter image description here

David Slater
  • 1,525
  • 10
  • 13