Function Repository Resource:

ExpressionLineDiff

Source Notebook

Highlight the changes between two expressions

Contributed by: Richard Hennigan (Wolfram Research)

ResourceFunction["ExpressionLineDiff"][expr1,expr2]

highlights changes between expr1 and expr2.

Details and Options

ResourceFunction["ExpressionLineDiff"] displays changes between two expressions in a manner similar to many Git diff utilities that are used to display file changes.
ResourceFunction["ExpressionLineDiff"] has the following options:
"TrimmingThreshold"2the maximum number of consecutive unchanged lines to display
"IndentSize"1the number of spaces to use per indentation level
"PageWidth"100the desired formatting width of the output
PerformanceGoal"Quality"control the level of detail in the highlighting
"MergeThreshold"2minimum length of consecutive unchanged characters required to merge into surrounding changes
ResourceFunction["ExpressionLineDiff"] uses the resource function ReadableForm to convert expressions to lines.

Examples

Basic Examples (2) 

Highlight the differences between two expressions on a per-line basis:

In[1]:=
expr1 = Cell[BoxData[RowBox[{"1", "+", "1"}]], "Input", CellLabel -> "In[1]:=", CellTags -> {"neat"}, CellID -> 731245485];
expr2 = Cell[BoxData[RowBox[{"1", "+", "1"}]], "Input", CellLabel -> "In[2]:=", CellTags -> {"neat"}, CellID -> 731245435];
In[2]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2]
Out[2]=
Image

Check larger expressions:

In[3]:=
expr1 = ResourceFunction["BirdSay", All, ResourceVersion -> "2.0.0"];
expr2 = ResourceFunction["BirdSay", All, ResourceVersion -> "3.0.0"];
In[4]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2]
Out[4]=
Image

Options (9) 

TrimmingThreshold (2) 

Control the maximum number of consecutive unchanged lines to display:

In[5]:=
expr1 = ResourceFunction["BirdSay", All, ResourceVersion -> "2.0.0"];
expr2 = ResourceFunction["BirdSay", All, ResourceVersion -> "3.0.0"];
In[6]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2, "TrimmingThreshold" -> 3]
Out[6]=
Image

Show all lines:

In[7]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2, "TrimmingThreshold" -> None]
Out[7]=
Image

IndentSize (1) 

Adjust the amount of indentation:

In[8]:=
expr1 = ResourceFunction["BirdSay", All, ResourceVersion -> "2.0.0"];
expr2 = ResourceFunction["BirdSay", All, ResourceVersion -> "3.0.0"];
In[9]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2, "IndentSize" -> 8, "TrimmingThreshold" -> None]
Out[9]=
Image

PageWidth (2) 

Reducing the page width will increase the number of lines:

In[10]:=
expr1 = ResourceFunction["BirdSay", All, ResourceVersion -> "2.0.0"];
expr2 = ResourceFunction["BirdSay", All, ResourceVersion -> "3.0.0"];
In[11]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2, "PageWidth" -> 30]
Out[11]=
Image

Conversely, a larger page width will decrease the number of lines:

In[12]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2, "PageWidth" -> 150]
Out[12]=
Image

PerformanceGoal (2) 

By default, ExpressionLineDiff will use darker colors to highlight per-character changes:

In[13]:=
expr1 = ResourceFunction["BirdSay", All, ResourceVersion -> "2.0.0"];
expr2 = ResourceFunction["BirdSay", All, ResourceVersion -> "3.0.0"];
In[14]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2]
Out[14]=
Image

With the option setting PerformanceGoal"Speed", only per-line highlighting will be used:

In[15]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2, PerformanceGoal -> "Speed"]
Out[15]=
Image

MergeThreshold (2) 

Control how many unchanged characters get merged into surrounding changes:

In[16]:=
expr1 = "1234567890";
expr2 = "1a3b56c890d";
In[17]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2, "MergeThreshold" -> 2]
Out[17]=
Image
In[18]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2, "MergeThreshold" -> 1]
Out[18]=
Image

A merge threshold of zero highlights exact changes:

In[19]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2, "MergeThreshold" -> 0]
Out[19]=
Image

Applications (3) 

Quickly find why two expressions that otherwise appear identical are different:

In[20]:=
expr1 = PieChart[{1, 2, 3}]
Out[20]=
Image
In[21]:=
expr2 = PieChart[{1, 2, 3}]
Out[21]=
Image

These are not in fact SameQ:

In[22]:=
expr1 === expr2
Out[22]=
Image

The difference is due to underlying Dynamic elements that use different local variables:

In[23]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2]
Out[23]=
Image

Possible Issues (2) 

ExpressionLineDiff compares InputForm strings, so highlighting does not necessarily align with subexpressions:

In[24]:=
expr1 = {123, 456, 789, xy, 0};
expr2 = {123, 4567, 789, xz, 0};
In[25]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2]
Out[25]=
Image

This is comparable to using SequenceAlignment on the string representations:

In[26]:=
SequenceAlignment[ToString[expr1], ToString[expr2]]
Out[26]=
Image
In[27]:=
% /. {a_String, b_String} :> Column[{Item[a, Background -> LightRed], Item[b, Background -> LightGreen]}]
Out[27]=
Image

For flat lists, SequenceAlignment can identify changes per element:

In[28]:=
SequenceAlignment[expr1, expr2]
Out[28]=
Image
In[29]:=
% /. {a_List, b_List} :> Column[{Item[a, Background -> LightRed], Item[b, Background -> LightGreen]}]
Out[29]=
Image

With the option setting PerformanceGoal"Speed", ExpressionLineDiff only looks for changes on a per-line basis and does not highlight detailed changes:

In[30]:=
expr1 = Range[25];
expr2 = ReplacePart[expr1, 13 -> 31];
In[31]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2, PerformanceGoal -> "Speed"]
Out[31]=
Image

Using a smaller page width can increase the accuracy of highlighting at the cost of readability:

In[32]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2, "PageWidth" -> 10, PerformanceGoal -> "Speed"]
Out[32]=
Image

For small expressions, it's better to use the default option setting PerformanceGoal"Quality" to add additional per-character highlighting:

In[33]:=
ResourceFunction["ExpressionLineDiff"][expr1, expr2, PerformanceGoal -> "Quality"]
Out[33]=
Image

Version History

  • 1.1.2 – 17 January 2023
  • 1.1.1 – 21 August 2020
  • 1.1.0 – 17 August 2020
  • 1.0.0 – 13 May 2020

Related Resources

License Information