Skip to content

Commit 587ed71

Browse files
committed
Updated diff stuff
1 parent fe9d57f commit 587ed71

36 files changed

+1056
-861
lines changed

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
1-
# AngleSharp.Diffing
1+
# AngleSharp Diffing - A diff/compare library for AngleSharp
22
This library makes it possible to compare a AngleSharp _control_ `INodeList` and a _test_ `INodeList` and get a list of `IDiff` differences between them.
33

44
The _control_ nodes represents the expected, i.e. how the nodes are expected to look, and the _test_ nodes represents the other nodes that should be compared to the _control_ nodes.
55

66
## Usage
77

8-
Filters:
9-
- remove comments
8+
```csharp
9+
var diffs = DiffBuilder
10+
.Compare(controlHtml)
11+
.WithTest(testHtml)
12+
.Build();
13+
14+
```
15+
16+
#### Built-in filters:
17+
- **`RemoveCommentNodeFilter`**: remove all comment nodes.
1018
- whitespace only text nodes
1119

1220
Matchers:
1321
- searching matcher, that will match nodes of the same type, and, optionally, element with the same element name.
1422
- css selector matcher nodes and for attributes
1523

1624
Comparers:
25+
- **`DiffIgnoreAttributeComparer`**: allows you to specify an special attribute `diff:ignore="true"` (`="true"` optional) on control elements to ignore them, their attributes, and child nodes, during comparison. E.g. `<p diff:ignore>...</p>`.
1726
- ignore consecutive whitespace comparer inside textnodes (not in strings in script and style tags).
1827
- regex comparer
1928
- ignore case comparer (attr/text)

docs/HtmlDifferenceEngineFlow.svg

Lines changed: 1 addition & 708 deletions
Loading

docs/HtmlDifferenceEngineFlow.vsdx

6.36 KB
Binary file not shown.

src/CompareResult.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,21 @@
22
{
33
public enum CompareResult
44
{
5+
/// <summary>
6+
/// Indicates the two compared nodes or attributes are the same.
7+
/// </summary>
58
Same,
6-
Different
9+
/// <summary>
10+
/// Indicates the two compared nodes or attributes are the same AND no further comparison should happen on any child nodes or attributes.
11+
/// </summary>
12+
SameAndBreak,
13+
/// <summary>
14+
/// Indicates the two compared nodes or attributes are the different.
15+
/// </summary>
16+
Different,
17+
/// <summary>
18+
/// Indicates the two compared nodes or attributes are the different AND no further comparison should happen on any child nodes or attributes.
19+
/// </summary>
20+
DifferentAndBreak
721
}
822
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
using Egil.AngleSharp.Diffing.Comparisons;
2+
3+
namespace Egil.AngleSharp.Diffing.Comparers.Chainable
4+
{
5+
public delegate CompareResult ChainableAttributeComparerStrategy(in IAttributeComparison comparison, CompareResult currentDecision);
6+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using AngleSharp.Dom;
2+
using Egil.AngleSharp.Diffing.Comparisons;
3+
4+
namespace Egil.AngleSharp.Diffing.Comparers.Chainable
5+
{
6+
public delegate CompareResult ChainableNodeComparerStrategy<TNode>(in IComparison<TNode> comparison, CompareResult currentDecision)
7+
where TNode : INode;
8+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using AngleSharp.Dom;
7+
using Egil.AngleSharp.Diffing.Comparisons;
8+
9+
namespace Egil.AngleSharp.Diffing.Comparers.Chainable
10+
{
11+
public class ChainedCompareStrategy : ICompareStrategy
12+
{
13+
private readonly List<IChainableNodeCompareStrategy<IElement>> _elementComparers = new List<IChainableNodeCompareStrategy<IElement>>();
14+
private readonly List<IChainableNodeCompareStrategy<IComment>> _commentComparers = new List<IChainableNodeCompareStrategy<IComment>>();
15+
private readonly List<IChainableNodeCompareStrategy<IText>> _textComparers = new List<IChainableNodeCompareStrategy<IText>>();
16+
private readonly List<IChainableNodeCompareStrategy<INode>> _nodeComparers = new List<IChainableNodeCompareStrategy<INode>>();
17+
private readonly List<IChainableAttributeCompareStrategy> _attrComparers = new List<IChainableAttributeCompareStrategy>();
18+
19+
public CompareResult Compare<TNode>(in IComparison<TNode> nodeComparison) where TNode : INode
20+
{
21+
return nodeComparison switch
22+
{
23+
IComparison<IElement> comparison => ApplyNodeComparers(in comparison, _elementComparers),
24+
IComparison<IComment> comparison => ApplyNodeComparers(in comparison, _commentComparers),
25+
IComparison<IText> comparison => ApplyNodeComparers(in comparison, _textComparers),
26+
IComparison<INode> comparison => ApplyNodeComparers(in comparison, _nodeComparers),
27+
_ => throw new InvalidOperationException("Unknown comparison type")
28+
};
29+
}
30+
31+
private CompareResult ApplyNodeComparers<TNode>(in IComparison<TNode> comparison, List<IChainableNodeCompareStrategy<TNode>> comparers) where TNode : INode
32+
{
33+
var result = CompareResult.Same;
34+
35+
foreach (var comparer in comparers)
36+
{
37+
result = comparer.Compare(comparison, result);
38+
}
39+
40+
return result;
41+
}
42+
43+
public CompareResult Compare(in IAttributeComparison comparison)
44+
{
45+
var result = CompareResult.Same;
46+
47+
foreach (var comparer in _attrComparers)
48+
{
49+
result = comparer.Compare(comparison, result);
50+
}
51+
52+
return result;
53+
}
54+
55+
public void AddComparer<TNode>(ChainableNodeComparerStrategy<TNode> comparer)
56+
where TNode : INode
57+
{
58+
switch (comparer)
59+
{
60+
case ChainableNodeComparerStrategy<IElement> filter:
61+
AddComparer(new NodeComparerDelegateWrapper<IElement>(filter));
62+
break;
63+
case ChainableNodeComparerStrategy<IComment> filter:
64+
AddComparer(new NodeComparerDelegateWrapper<IComment>(filter));
65+
break;
66+
case ChainableNodeComparerStrategy<IText> filer:
67+
AddComparer(new NodeComparerDelegateWrapper<IText>(filer));
68+
break;
69+
case ChainableNodeComparerStrategy<INode> filer:
70+
AddComparer(new NodeComparerDelegateWrapper<INode>(filer));
71+
break;
72+
}
73+
}
74+
75+
public void AddComparer(ChainableAttributeComparerStrategy comparer) => AddComparer(new AttrComparerDelegateWrapper(comparer));
76+
77+
public void AddComparer(IChainableNodeCompareStrategy<IElement> compareStrategy)
78+
{
79+
_elementComparers.Add(compareStrategy);
80+
}
81+
82+
public void AddComparer(IChainableNodeCompareStrategy<IComment> compareStrategy)
83+
{
84+
_commentComparers.Add(compareStrategy);
85+
}
86+
87+
public void AddComparer(IChainableNodeCompareStrategy<IText> compareStrategy)
88+
{
89+
_textComparers.Add(compareStrategy);
90+
}
91+
92+
public void AddComparer(IChainableNodeCompareStrategy<INode> compareStrategy)
93+
{
94+
_elementComparers.Add(compareStrategy);
95+
_commentComparers.Add(compareStrategy);
96+
_textComparers.Add(compareStrategy);
97+
_nodeComparers.Add(compareStrategy);
98+
}
99+
100+
public void AddComparer(IChainableAttributeCompareStrategy compareStrategy)
101+
{
102+
_attrComparers.Add(compareStrategy);
103+
}
104+
}
105+
106+
class NodeComparerDelegateWrapper<TNode> : IChainableNodeCompareStrategy<TNode> where TNode : INode
107+
{
108+
private readonly ChainableNodeComparerStrategy<TNode> _strategy;
109+
110+
public NodeComparerDelegateWrapper(ChainableNodeComparerStrategy<TNode> strategy)
111+
{
112+
_strategy = strategy;
113+
}
114+
115+
public CompareResult Compare(IComparison<TNode> comparison, CompareResult currentDecision)
116+
{
117+
return _strategy(comparison, currentDecision);
118+
}
119+
}
120+
121+
class AttrComparerDelegateWrapper : IChainableAttributeCompareStrategy
122+
{
123+
private readonly ChainableAttributeComparerStrategy _strategy;
124+
125+
public AttrComparerDelegateWrapper(ChainableAttributeComparerStrategy strategy)
126+
{
127+
_strategy = strategy;
128+
}
129+
130+
public CompareResult Compare(IAttributeComparison comparison, CompareResult currentDecision)
131+
{
132+
return _strategy(comparison, currentDecision);
133+
}
134+
}
135+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Egil.AngleSharp.Diffing.Comparisons;
2+
3+
namespace Egil.AngleSharp.Diffing.Comparers.Chainable
4+
{
5+
public interface IChainableAttributeCompareStrategy
6+
{
7+
CompareResult Compare(IAttributeComparison comparison, CompareResult currentDecision);
8+
}
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using AngleSharp.Dom;
2+
using Egil.AngleSharp.Diffing.Comparisons;
3+
4+
namespace Egil.AngleSharp.Diffing.Comparers.Chainable
5+
{
6+
public interface IChainableNodeCompareStrategy<in TNode> where TNode : INode
7+
{
8+
CompareResult Compare(IComparison<TNode> comparison, CompareResult currentDecision);
9+
}
10+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using AngleSharp.Dom;
7+
using Egil.AngleSharp.Diffing.Comparisons;
8+
using Egil.AngleSharp.Diffing.Comparers.Chainable;
9+
10+
namespace Egil.AngleSharp.Diffing.Comparers
11+
{
12+
public class DiffIgnoreAttributeCompareStrategy : IChainableNodeCompareStrategy<IElement>
13+
{
14+
public CompareResult Compare(IComparison<IElement> comparison, CompareResult currentDecision)
15+
{
16+
if (comparison is null) throw new ArgumentNullException(nameof(comparison));
17+
18+
var ignoreAttr = comparison.Control.Node.Attributes["diff:ignore"];
19+
20+
if (ignoreAttr is { } && ignoreAttr.IsEmptyOrEquals("TRUE"))
21+
return CompareResult.SameAndBreak;
22+
else
23+
return currentDecision;
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)