-
Notifications
You must be signed in to change notification settings - Fork 83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Diff opeation with lines context #22
base: master
Are you sure you want to change the base?
Changes from all commits
7819e6e
b9f8f27
ee16ac7
5e1bbb0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFrameworks>net45;netcoreapp2.2</TargetFrameworks> | ||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||
<PackageProjectUrl>https://github.com/brianpos/htmldiff.net</PackageProjectUrl> | ||
<PackageLicenseUrl>https://github.com/brianpos/htmldiff.net/blob/master/license.txt</PackageLicenseUrl> | ||
<Description>A library for comparing two HTML files/snippets and highlighting the differences using simple HTML. | ||
(this is a fork of Rohland de Charmoy's HtmlDiff library, with lines of context to include, and compiled for netcore2/net45)</Description> | ||
<Authors>Brian Postlethwaite</Authors> | ||
<RepositoryUrl>https://github.com/brianpos/htmldiff.net</RepositoryUrl> | ||
<RepositoryType>github</RepositoryType> | ||
<PackageReleaseNotes>This is a slightly updated version that better supports XML diffs, with a context number of lines</PackageReleaseNotes> | ||
<Company /> | ||
<Version>1.2.0</Version> | ||
<Product>HtmlDiff</Product> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Compile Include="..\HtmlDiff\Action.cs" Link="Action.cs" /> | ||
<Compile Include="..\HtmlDiff\Diff.cs" Link="Diff.cs" /> | ||
<Compile Include="..\HtmlDiff\Match.cs" Link="Match.cs" /> | ||
<Compile Include="..\HtmlDiff\MatchFinder.cs" Link="MatchFinder.cs" /> | ||
<Compile Include="..\HtmlDiff\MatchOptions.cs" Link="MatchOptions.cs" /> | ||
<Compile Include="..\HtmlDiff\Mode.cs" Link="Mode.cs" /> | ||
<Compile Include="..\HtmlDiff\Operation.cs" Link="Operation.cs" /> | ||
<Compile Include="..\HtmlDiff\Utils.cs" Link="Utils.cs" /> | ||
<Compile Include="..\HtmlDiff\WordSplitter.cs" Link="WordSplitter.cs" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,6 +64,23 @@ public class HtmlDiff | |
/// </summary> | ||
public bool IgnoreWhitespaceDifferences { get; set; } | ||
|
||
/// <summary> | ||
/// If greater than 0, the text will be broken into div sections that contain | ||
/// the number of lines before and after each change. | ||
/// Where changes are closer than the context, a new section is not started, and all content output | ||
/// If there are no changes, then a null string is returned. | ||
/// </summary> | ||
/// <example>LinesContext = 1 // Only the line with the change is returned</example> | ||
/// <example>LinesContext = 4 // The line with the change is returned and up to 3 lines before and after</example> | ||
public int LinesContext { get; set; } | ||
|
||
/// <summary> | ||
/// When using the Context option, the name of the class that is injected into the groupers | ||
/// </summary> | ||
/// <remarks>If LinesContext is provided, and no ContextSelectionClass is </remarks> | ||
/// <example></example> | ||
public string ContextSectionClass { get; set; } | ||
|
||
/// <summary> | ||
/// If some match is too small and located far from its neighbors then it is considered as orphan | ||
/// and removed. For example: | ||
|
@@ -92,6 +109,7 @@ public class HtmlDiff | |
public HtmlDiff(string oldText, string newText) | ||
{ | ||
RepeatingWordsAccuracy = 1d; //by default all repeating words should be compared | ||
ContextSectionClass = "diffsection"; | ||
|
||
_oldText = oldText; | ||
_newText = newText; | ||
|
@@ -107,14 +125,16 @@ public static string Execute(string oldText, string newText) | |
} | ||
|
||
/// <summary> | ||
/// Builds the HTML diff output | ||
/// Builds the HTML diff output (Note: If there is a context specified and the content is equal, then null will be returned) | ||
/// </summary> | ||
/// <returns>HTML diff markup</returns> | ||
public string Build() | ||
{ | ||
// If there is no difference, don't bother checking for differences | ||
if (_oldText == _newText) | ||
{ | ||
if (LinesContext > 0) | ||
return null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure returning null is the best option. Perhaps an empty string would be better. This goes for the other null return values in the method. |
||
return _newText; | ||
} | ||
|
||
|
@@ -124,14 +144,37 @@ public string Build() | |
|
||
List<Operation> operations = Operations(); | ||
|
||
foreach (Operation item in operations) | ||
if ((operations.Count == 0 || operations.Count == 1 && operations[0].Action == Action.Equal) && LinesContext > 0) | ||
return null; | ||
|
||
if (operations.Count > 0 && operations[0].Action != Action.Equal && LinesContext > 0) | ||
{ | ||
_startSectionAdded = true; | ||
_content.AppendFormat("<div class=\"{0}\"> <!-- start diff section -->\r\n", ContextSectionClass); | ||
} | ||
|
||
bool changesAfter = false; | ||
for (int nItem = 0; nItem < operations.Count; nItem++) | ||
{ | ||
PerformOperation(item); | ||
Operation item = operations[nItem]; | ||
changesAfter = false; | ||
if (nItem + 1 < operations.Count && operations[nItem + 1].Action != Action.Equal) | ||
changesAfter = true; | ||
PerformOperation(item, changesAfter); | ||
} | ||
|
||
// Put in an end section | ||
if (LinesContext > 0 && _startSectionAdded) | ||
_content.Append("</div> <!-- end diff section -->\r\n\r\n"); | ||
|
||
// Remove any empty sections | ||
if (LinesContext > 0) | ||
_content.Replace(String.Format("<div class=\"{0}\"> <!-- start diff section -->\r\n</div> <!-- end diff section -->\r\n\r\n", ContextSectionClass), ""); | ||
return _content.ToString(); | ||
} | ||
|
||
bool _startSectionAdded = false; | ||
|
||
/// <summary> | ||
/// Uses <paramref name="expression"/> to group text together so that any change detected within the group is treated as a single block | ||
/// </summary> | ||
|
@@ -154,16 +197,16 @@ private void SplitInputsToWords() | |
_newText = null; | ||
} | ||
|
||
private void PerformOperation(Operation operation) | ||
private void PerformOperation(Operation operation, bool changesAfter) | ||
{ | ||
#if DEBUG | ||
operation.PrintDebugInfo(_oldWords, _newWords); | ||
// operation.PrintDebugInfo(_oldWords, _newWords); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was this commented out by mistake? |
||
#endif | ||
|
||
switch (operation.Action) | ||
{ | ||
case Action.Equal: | ||
ProcessEqualOperation(operation); | ||
ProcessEqualOperation(operation, changesAfter); | ||
break; | ||
case Action.Delete: | ||
ProcessDeleteOperation(operation, "diffdel"); | ||
|
@@ -197,11 +240,55 @@ private void ProcessDeleteOperation(Operation operation, string cssClass) | |
InsertTag("del", cssClass, text); | ||
} | ||
|
||
private void ProcessEqualOperation(Operation operation) | ||
private void ProcessEqualOperation(Operation operation, bool changesAfter) | ||
{ | ||
string[] result = | ||
_newWords.Where((s, pos) => pos >= operation.StartInNew && pos < operation.EndInNew).ToArray(); | ||
_content.Append(String.Join("", result)); | ||
if (LinesContext > 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider moving to a separate method.
|
||
{ | ||
int linesInSection = result.Count(m => m.StartsWith("\r\n")); | ||
if (linesInSection > LinesContext * 2) // Only put breaks in if the gap is bigger than the size of the content | ||
{ | ||
// Need to split the context down. | ||
int lines=0; | ||
foreach (var item in result) | ||
{ | ||
if (lines < LinesContext || lines > linesInSection-LinesContext) | ||
_content.Append(item); | ||
if (item.StartsWith("\r\n")) | ||
lines++; | ||
|
||
// Close the previous context | ||
if (lines == LinesContext && _startSectionAdded) | ||
{ | ||
_startSectionAdded = false; | ||
_content.Append("</div> <!-- end diff section -->\r\n\r\n"); | ||
if (!changesAfter) | ||
return; | ||
} | ||
|
||
// Now start the new context | ||
if (lines == linesInSection - LinesContext && !_startSectionAdded) | ||
{ | ||
_startSectionAdded = true; | ||
_content.AppendFormat("<div class=\"{0}\"> <!-- start diff section -->\r\n", ContextSectionClass); | ||
} | ||
} | ||
} | ||
else | ||
{ | ||
if (!_startSectionAdded) | ||
{ | ||
_startSectionAdded = true; | ||
_content.AppendFormat("<div class=\"{0}\"> <!-- start diff section -->\r\n", ContextSectionClass); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is duplicated a few times. Perhaps introduce a static method to return the template for the start/end of sections. Even better would be to allow callers to specify their own template mechanism. i.e. Introduce an IDiffSummaryTemplate with a default implementation as per the original code above. |
||
} | ||
_content.Append(String.Join("", result)); | ||
} | ||
} | ||
else | ||
{ | ||
_content.Append(String.Join("", result)); | ||
} | ||
} | ||
|
||
|
||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if it would be neater to drop these properties in favour of a dedicated public method to process the summary.
Example:
This makes it easier for callers to understand the configuration options available to them when building a summary of changes.