diff --git a/DiffPlex.ConsoleRunner/DiffPlex.ConsoleRunner.csproj b/DiffPlex.ConsoleRunner/DiffPlex.ConsoleRunner.csproj index 848691cd..3cecf931 100644 --- a/DiffPlex.ConsoleRunner/DiffPlex.ConsoleRunner.csproj +++ b/DiffPlex.ConsoleRunner/DiffPlex.ConsoleRunner.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net9.0 diff --git a/DiffPlex/Chunkers/CharacterChunker.cs b/DiffPlex/Chunkers/CharacterChunker.cs index 8076c4fe..bc606179 100644 --- a/DiffPlex/Chunkers/CharacterChunker.cs +++ b/DiffPlex/Chunkers/CharacterChunker.cs @@ -1,19 +1,18 @@ using System.Collections.Generic; -namespace DiffPlex.Chunkers +namespace DiffPlex.Chunkers; + +public class CharacterChunker : IChunker { - public class CharacterChunker:IChunker - { - /// - /// Gets the default singleton instance of the chunker. - /// - public static CharacterChunker Instance { get; } = new CharacterChunker(); + /// + /// Gets the default singleton instance of the chunker. + /// + public static CharacterChunker Instance { get; } = new(); - public IReadOnlyList Chunk(string text) - { - var s = new string[text.Length]; - for (int i = 0; i < text.Length; i++) s[i] = text[i].ToString(); - return s; - } + public IReadOnlyList Chunk(string text) + { + var s = new string[text.Length]; + for (int i = 0; i < text.Length; i++) s[i] = text[i].ToString(); + return s; } } \ No newline at end of file diff --git a/DiffPlex/Chunkers/CustomFunctionChunker.cs b/DiffPlex/Chunkers/CustomFunctionChunker.cs index ac63c730..0e50ab9a 100644 --- a/DiffPlex/Chunkers/CustomFunctionChunker.cs +++ b/DiffPlex/Chunkers/CustomFunctionChunker.cs @@ -1,21 +1,14 @@ using System; using System.Collections.Generic; -namespace DiffPlex.Chunkers -{ - public class CustomFunctionChunker: IChunker - { - private readonly Func> customChunkerFunc; +namespace DiffPlex.Chunkers; - public CustomFunctionChunker(Func> customChunkerFunc) - { - if (customChunkerFunc == null) throw new ArgumentNullException(nameof(customChunkerFunc)); - this.customChunkerFunc = customChunkerFunc; - } +public class CustomFunctionChunker(Func> customChunkerFunc) : IChunker +{ + private readonly Func> customChunkerFunc = customChunkerFunc ?? throw new ArgumentNullException(nameof(customChunkerFunc)); - public IReadOnlyList Chunk(string text) - { - return customChunkerFunc(text); - } + public IReadOnlyList Chunk(string text) + { + return customChunkerFunc(text); } } \ No newline at end of file diff --git a/DiffPlex/Chunkers/DelimiterChunker.cs b/DiffPlex/Chunkers/DelimiterChunker.cs index cdb3ebb0..36308f7a 100644 --- a/DiffPlex/Chunkers/DelimiterChunker.cs +++ b/DiffPlex/Chunkers/DelimiterChunker.cs @@ -1,82 +1,73 @@ using System; using System.Collections.Generic; -namespace DiffPlex.Chunkers -{ - public class DelimiterChunker : IChunker - { - private readonly char[] delimiters; - - public DelimiterChunker(char[] delimiters) - { - if (delimiters is null || delimiters.Length == 0) - { - throw new ArgumentException($"{nameof(delimiters)} cannot be null or empty.", nameof(delimiters)); - } +namespace DiffPlex.Chunkers; - this.delimiters = delimiters; - } +public class DelimiterChunker(char[] delimiters) : IChunker +{ + private readonly char[] delimiters = delimiters is null || delimiters.Length == 0 + ? throw new ArgumentException($"{nameof(delimiters)} cannot be null or empty.", nameof(delimiters)) + : delimiters; - public IReadOnlyList Chunk(string str) + public IReadOnlyList Chunk(string str) + { + var list = new List(); + int begin = 0; + bool processingDelim = false; + int delimBegin = 0; + for (int i = 0; i < str.Length; i++) { - var list = new List(); - int begin = 0; - bool processingDelim = false; - int delimBegin = 0; - for (int i = 0; i < str.Length; i++) + if (Array.IndexOf(delimiters, str[i]) != -1) { - if (Array.IndexOf(delimiters, str[i]) != -1) + if (i >= str.Length - 1) { - if (i >= str.Length - 1) + if (processingDelim) { - if (processingDelim) - { - list.Add(str.Substring(delimBegin, (i + 1 - delimBegin))); - } - else - { - list.Add(str.Substring(begin, (i - begin))); - list.Add(str.Substring(i, 1)); - } + list.Add(str.Substring(delimBegin, (i + 1 - delimBegin))); } else { - if (!processingDelim) - { - // Add everything up to this delimeter as the next chunk (if there is anything) - if (i - begin > 0) - { - list.Add(str.Substring(begin, (i - begin))); - } - - processingDelim = true; - delimBegin = i; - } + list.Add(str.Substring(begin, (i - begin))); + list.Add(str.Substring(i, 1)); } - - begin = i + 1; } else { - if (processingDelim) + if (!processingDelim) { - if (i - delimBegin > 0) + // Add everything up to this delimeter as the next chunk (if there is anything) + if (i - begin > 0) { - list.Add(str.Substring(delimBegin, (i - delimBegin))); + list.Add(str.Substring(begin, (i - begin))); } - processingDelim = false; + processingDelim = true; + delimBegin = i; } + } - // If we are at the end, add the remaining as the last chunk - if (i >= str.Length - 1) + begin = i + 1; + } + else + { + if (processingDelim) + { + if (i - delimBegin > 0) { - list.Add(str.Substring(begin, (i + 1 - begin))); + list.Add(str.Substring(delimBegin, (i - delimBegin))); } + + processingDelim = false; } - } - return list; + // If we are at the end, add the remaining as the last chunk + if (i >= str.Length - 1) + { + list.Add(str.Substring(begin, (i + 1 - begin))); + } + } } + + return list; } } \ No newline at end of file diff --git a/DiffPlex/Chunkers/LineChunker.cs b/DiffPlex/Chunkers/LineChunker.cs index 79c6b6ed..e8edcc76 100644 --- a/DiffPlex/Chunkers/LineChunker.cs +++ b/DiffPlex/Chunkers/LineChunker.cs @@ -1,20 +1,19 @@ using System; using System.Collections.Generic; -namespace DiffPlex.Chunkers +namespace DiffPlex.Chunkers; + +public class LineChunker : IChunker { - public class LineChunker:IChunker - { - private readonly string[] lineSeparators = new[] {"\r\n", "\r", "\n"}; + private readonly string[] lineSeparators = ["\r\n", "\r", "\n"]; - /// - /// Gets the default singleton instance of the chunker. - /// - public static LineChunker Instance { get; } = new LineChunker(); + /// + /// Gets the default singleton instance of the chunker. + /// + public static LineChunker Instance { get; } = new(); - public IReadOnlyList Chunk(string text) - { - return text.Split(lineSeparators, StringSplitOptions.None); - } + public IReadOnlyList Chunk(string text) + { + return text.Split(lineSeparators, StringSplitOptions.None); } } \ No newline at end of file diff --git a/DiffPlex/Chunkers/LineEndingsPreservingChunker.cs b/DiffPlex/Chunkers/LineEndingsPreservingChunker.cs index c61e39c3..c6c39190 100644 --- a/DiffPlex/Chunkers/LineEndingsPreservingChunker.cs +++ b/DiffPlex/Chunkers/LineEndingsPreservingChunker.cs @@ -1,53 +1,52 @@ using System.Collections.Generic; -namespace DiffPlex.Chunkers +namespace DiffPlex.Chunkers; + +public class LineEndingsPreservingChunker : IChunker { - public class LineEndingsPreservingChunker:IChunker + private static readonly string[] EmptyArray = []; + + /// + /// Gets the default singleton instance of the chunker. + /// + public static LineEndingsPreservingChunker Instance { get; } = new(); + + public IReadOnlyList Chunk(string text) { - private static readonly string[] EmptyArray = new string[0]; + if (string.IsNullOrEmpty(text)) + return EmptyArray; - /// - /// Gets the default singleton instance of the chunker. - /// - public static LineEndingsPreservingChunker Instance { get; } = new LineEndingsPreservingChunker(); + var output = new List(); + var lastCut = 0; - public IReadOnlyList Chunk(string text) + for (int i = 0; i < text.Length; i++) { - if (string.IsNullOrEmpty(text)) - return EmptyArray; + int lineEndLen = 0; - var output = new List(); - var lastCut = 0; - - for (int i = 0; i < text.Length; i++) + if (text[i] == '\r') + { + lineEndLen = 1; + if (i + 1 < text.Length && text[i + 1] == '\n') + lineEndLen = 2; // CRLF + } + else if (text[i] == '\n') { - int lineEndLen = 0; - - if (text[i] == '\r') - { - lineEndLen = 1; - if (i + 1 < text.Length && text[i + 1] == '\n') - lineEndLen = 2; // CRLF - } - else if (text[i] == '\n') - { - lineEndLen = 1; // LF - } - - if (lineEndLen > 0) - { - int sliceLen = i - lastCut + lineEndLen; - output.Add(text.Substring(lastCut, sliceLen)); - - i += lineEndLen - 1; // we already consumed them - lastCut += sliceLen; - } + lineEndLen = 1; // LF } - if (lastCut != text.Length) // trailing line without EOL - output.Add(text.Substring(lastCut)); + if (lineEndLen > 0) + { + int sliceLen = i - lastCut + lineEndLen; + output.Add(text.Substring(lastCut, sliceLen)); - return output; + i += lineEndLen - 1; // we already consumed them + lastCut += sliceLen; + } } + + if (lastCut != text.Length) // trailing line without EOL + output.Add(text.Substring(lastCut)); + + return output; } } \ No newline at end of file diff --git a/DiffPlex/Chunkers/WordChunker.cs b/DiffPlex/Chunkers/WordChunker.cs index 66a9386f..935b3141 100644 --- a/DiffPlex/Chunkers/WordChunker.cs +++ b/DiffPlex/Chunkers/WordChunker.cs @@ -1,16 +1,15 @@ -namespace DiffPlex.Chunkers +namespace DiffPlex.Chunkers; + +public class WordChunker : DelimiterChunker { - public class WordChunker:DelimiterChunker - { - private static char[] WordSeparators { get; } = { ' ', '\t', '.', '(', ')', '{', '}', ',', '!', '?', ';' }; + private static char[] WordSeparators { get; } = [' ', '\t', '.', '(', ')', '{', '}', ',', '!', '?', ';']; - /// - /// Gets the default singleton instance of the chunker. - /// - public static WordChunker Instance { get; } = new WordChunker(); + /// + /// Gets the default singleton instance of the chunker. + /// + public static WordChunker Instance { get; } = new(); - public WordChunker() : base(WordSeparators) - { - } + public WordChunker() : base(WordSeparators) + { } } \ No newline at end of file diff --git a/DiffPlex/DiffBuilder/IInlineDiffBuilder.cs b/DiffPlex/DiffBuilder/IInlineDiffBuilder.cs index 4bcf306c..29a3d4ba 100644 --- a/DiffPlex/DiffBuilder/IInlineDiffBuilder.cs +++ b/DiffPlex/DiffBuilder/IInlineDiffBuilder.cs @@ -1,10 +1,9 @@ using DiffPlex.DiffBuilder.Model; -namespace DiffPlex.DiffBuilder +namespace DiffPlex.DiffBuilder; + +public interface IInlineDiffBuilder { - public interface IInlineDiffBuilder - { - DiffPaneModel BuildDiffModel(string oldText, string newText); - DiffPaneModel BuildDiffModel(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase, IChunker chunker); - } + DiffPaneModel BuildDiffModel(string oldText, string newText); + DiffPaneModel BuildDiffModel(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase, IChunker chunker); } diff --git a/DiffPlex/DiffBuilder/ISideBySideDiffBuilder.cs b/DiffPlex/DiffBuilder/ISideBySideDiffBuilder.cs index 02c93a07..8a5313dd 100644 --- a/DiffPlex/DiffBuilder/ISideBySideDiffBuilder.cs +++ b/DiffPlex/DiffBuilder/ISideBySideDiffBuilder.cs @@ -1,18 +1,17 @@ using DiffPlex.DiffBuilder.Model; -namespace DiffPlex.DiffBuilder +namespace DiffPlex.DiffBuilder; + +/// +/// Provides methods that generate differences between texts for displaying in a side by side view. +/// +public interface ISideBySideDiffBuilder { /// - /// Provides methods that generate differences between texts for displaying in a side by side view. + /// Builds a diff model for displaying diffs in a side by side view /// - public interface ISideBySideDiffBuilder - { - /// - /// Builds a diff model for displaying diffs in a side by side view - /// - /// The old text. - /// The new text. - /// The side by side diff model - SideBySideDiffModel BuildDiffModel(string oldText, string newText); - } + /// The old text. + /// The new text. + /// The side by side diff model + SideBySideDiffModel BuildDiffModel(string oldText, string newText); } \ No newline at end of file diff --git a/DiffPlex/DiffBuilder/InlineDiffBuilder.cs b/DiffPlex/DiffBuilder/InlineDiffBuilder.cs index 730341c5..55e2e610 100644 --- a/DiffPlex/DiffBuilder/InlineDiffBuilder.cs +++ b/DiffPlex/DiffBuilder/InlineDiffBuilder.cs @@ -4,116 +4,115 @@ using DiffPlex.DiffBuilder.Model; using DiffPlex.Model; -namespace DiffPlex.DiffBuilder +namespace DiffPlex.DiffBuilder; + +public class InlineDiffBuilder : IInlineDiffBuilder { - public class InlineDiffBuilder : IInlineDiffBuilder + private readonly IDiffer differ; + + /// + /// Gets the default singleton instance of the inline diff builder. + /// + public static InlineDiffBuilder Instance { get; } = new(); + + public InlineDiffBuilder(IDiffer differ = null) { - private readonly IDiffer differ; + this.differ = differ ?? Differ.Instance; + } - /// - /// Gets the default singleton instance of the inline diff builder. - /// - public static InlineDiffBuilder Instance { get; } = new InlineDiffBuilder(); + public DiffPaneModel BuildDiffModel(string oldText, string newText) + => BuildDiffModel(oldText, newText, ignoreWhitespace: true); - public InlineDiffBuilder(IDiffer differ = null) - { - this.differ = differ ?? Differ.Instance; - } + public DiffPaneModel BuildDiffModel(string oldText, string newText, bool ignoreWhitespace) + { + LineChunker chunker = new(); + return BuildDiffModel(oldText, newText, ignoreWhitespace, false, chunker); + } - public DiffPaneModel BuildDiffModel(string oldText, string newText) - => BuildDiffModel(oldText, newText, ignoreWhitespace: true); + public DiffPaneModel BuildDiffModel(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase, IChunker chunker) + { + if (oldText == null) throw new ArgumentNullException(nameof(oldText)); + if (newText == null) throw new ArgumentNullException(nameof(newText)); + + DiffPaneModel model = new(); + var diffResult = differ.CreateDiffs(oldText, newText, ignoreWhitespace, ignoreCase: ignoreCase, chunker); + BuildDiffPieces(diffResult, model.Lines); + + return model; + } - public DiffPaneModel BuildDiffModel(string oldText, string newText, bool ignoreWhitespace) - { - var chunker = new LineChunker(); - return BuildDiffModel(oldText, newText, ignoreWhitespace, false, chunker); - } + /// + /// Gets the inline textual diffs. + /// + /// The old text to diff. + /// The new text. + /// if ignore the white space; otherwise, . + /// if case-insensitive; otherwise, . + /// The chunker. + /// The diffs result. + public static DiffPaneModel Diff(string oldText, string newText, bool ignoreWhiteSpace = true, bool ignoreCase = false, IChunker chunker = null) + { + return Diff(Differ.Instance, oldText, newText, ignoreWhiteSpace, ignoreCase, chunker); + } - public DiffPaneModel BuildDiffModel(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase, IChunker chunker) - { - if (oldText == null) throw new ArgumentNullException(nameof(oldText)); - if (newText == null) throw new ArgumentNullException(nameof(newText)); - - var model = new DiffPaneModel(); - var diffResult = differ.CreateDiffs(oldText, newText, ignoreWhitespace, ignoreCase: ignoreCase, chunker); - BuildDiffPieces(diffResult, model.Lines); - - return model; - } + /// + /// Gets the inline textual diffs. + /// + /// The differ instance. + /// The old text to diff. + /// The new text. + /// if ignore the white space; otherwise, . + /// if case-insensitive; otherwise, . + /// The chunker. + /// The diffs result. + public static DiffPaneModel Diff(IDiffer differ, string oldText, string newText, bool ignoreWhiteSpace = true, bool ignoreCase = false, IChunker chunker = null) + { + if (oldText == null) throw new ArgumentNullException(nameof(oldText)); + if (newText == null) throw new ArgumentNullException(nameof(newText)); + + DiffPaneModel model = new(); + var diffResult = (differ ?? Differ.Instance).CreateDiffs(oldText, newText, ignoreWhiteSpace, ignoreCase, chunker ?? LineChunker.Instance); + BuildDiffPieces(diffResult, model.Lines); + + return model; + } - /// - /// Gets the inline textual diffs. - /// - /// The old text to diff. - /// The new text. - /// if ignore the white space; otherwise, . - /// if case-insensitive; otherwise, . - /// The chunker. - /// The diffs result. - public static DiffPaneModel Diff(string oldText, string newText, bool ignoreWhiteSpace = true, bool ignoreCase = false, IChunker chunker = null) - { - return Diff(Differ.Instance, oldText, newText, ignoreWhiteSpace, ignoreCase, chunker); - } + private static void BuildDiffPieces(DiffResult diffResult, List pieces) + { + int bPos = 0; - /// - /// Gets the inline textual diffs. - /// - /// The differ instance. - /// The old text to diff. - /// The new text. - /// if ignore the white space; otherwise, . - /// if case-insensitive; otherwise, . - /// The chunker. - /// The diffs result. - public static DiffPaneModel Diff(IDiffer differ, string oldText, string newText, bool ignoreWhiteSpace = true, bool ignoreCase = false, IChunker chunker = null) + foreach (var diffBlock in diffResult.DiffBlocks) { - if (oldText == null) throw new ArgumentNullException(nameof(oldText)); - if (newText == null) throw new ArgumentNullException(nameof(newText)); - - var model = new DiffPaneModel(); - var diffResult = (differ ?? Differ.Instance).CreateDiffs(oldText, newText, ignoreWhiteSpace, ignoreCase, chunker ?? LineChunker.Instance); - BuildDiffPieces(diffResult, model.Lines); - - return model; - } + for (; bPos < diffBlock.InsertStartB; bPos++) + pieces.Add(new(diffResult.PiecesNew[bPos], ChangeType.Unchanged, bPos + 1)); - private static void BuildDiffPieces(DiffResult diffResult, List pieces) - { - int bPos = 0; + int i = 0; + for (; i < Math.Min(diffBlock.DeleteCountA, diffBlock.InsertCountB); i++) + pieces.Add(new(diffResult.PiecesOld[i + diffBlock.DeleteStartA], ChangeType.Deleted)); - foreach (var diffBlock in diffResult.DiffBlocks) + i = 0; + for (; i < Math.Min(diffBlock.DeleteCountA, diffBlock.InsertCountB); i++) { - for (; bPos < diffBlock.InsertStartB; bPos++) - pieces.Add(new DiffPiece(diffResult.PiecesNew[bPos], ChangeType.Unchanged, bPos + 1)); - - int i = 0; - for (; i < Math.Min(diffBlock.DeleteCountA, diffBlock.InsertCountB); i++) - pieces.Add(new DiffPiece(diffResult.PiecesOld[i + diffBlock.DeleteStartA], ChangeType.Deleted)); + pieces.Add(new(diffResult.PiecesNew[i + diffBlock.InsertStartB], ChangeType.Inserted, bPos + 1)); + bPos++; + } - i = 0; - for (; i < Math.Min(diffBlock.DeleteCountA, diffBlock.InsertCountB); i++) + if (diffBlock.DeleteCountA > diffBlock.InsertCountB) + { + for (; i < diffBlock.DeleteCountA; i++) + pieces.Add(new(diffResult.PiecesOld[i + diffBlock.DeleteStartA], ChangeType.Deleted)); + } + else + { + for (; i < diffBlock.InsertCountB; i++) { - pieces.Add(new DiffPiece(diffResult.PiecesNew[i + diffBlock.InsertStartB], ChangeType.Inserted, bPos + 1)); + pieces.Add(new(diffResult.PiecesNew[i + diffBlock.InsertStartB], ChangeType.Inserted, bPos + 1)); bPos++; } - - if (diffBlock.DeleteCountA > diffBlock.InsertCountB) - { - for (; i < diffBlock.DeleteCountA; i++) - pieces.Add(new DiffPiece(diffResult.PiecesOld[i + diffBlock.DeleteStartA], ChangeType.Deleted)); - } - else - { - for (; i < diffBlock.InsertCountB; i++) - { - pieces.Add(new DiffPiece(diffResult.PiecesNew[i + diffBlock.InsertStartB], ChangeType.Inserted, bPos + 1)); - bPos++; - } - } } - - for (; bPos < diffResult.PiecesNew.Count; bPos++) - pieces.Add(new DiffPiece(diffResult.PiecesNew[bPos], ChangeType.Unchanged, bPos + 1)); } + + for (; bPos < diffResult.PiecesNew.Count; bPos++) + pieces.Add(new(diffResult.PiecesNew[bPos], ChangeType.Unchanged, bPos + 1)); } } diff --git a/DiffPlex/DiffBuilder/Model/DiffPaneModel.cs b/DiffPlex/DiffBuilder/Model/DiffPaneModel.cs index f7a19831..8b606ce4 100644 --- a/DiffPlex/DiffBuilder/Model/DiffPaneModel.cs +++ b/DiffPlex/DiffBuilder/Model/DiffPaneModel.cs @@ -1,20 +1,11 @@ using System.Collections.Generic; using System.Linq; -namespace DiffPlex.DiffBuilder.Model -{ - public class DiffPaneModel - { - public List Lines { get; } +namespace DiffPlex.DiffBuilder.Model; - public bool HasDifferences - { - get { return Lines.Any(x => x.Type != ChangeType.Unchanged); } - } +public class DiffPaneModel +{ + public List Lines { get; } = []; - public DiffPaneModel() - { - Lines = new List(); - } - } + public bool HasDifferences => Lines.Any(x => x.Type != ChangeType.Unchanged); } \ No newline at end of file diff --git a/DiffPlex/DiffBuilder/Model/DiffPiece.cs b/DiffPlex/DiffBuilder/Model/DiffPiece.cs index 5dc5f4f9..9e77e7c0 100644 --- a/DiffPlex/DiffBuilder/Model/DiffPiece.cs +++ b/DiffPlex/DiffBuilder/Model/DiffPiece.cs @@ -1,77 +1,76 @@ using System; using System.Collections.Generic; -namespace DiffPlex.DiffBuilder.Model +namespace DiffPlex.DiffBuilder.Model; + +public enum ChangeType +{ + Unchanged, + Deleted, + Inserted, + Imaginary, + Modified +} + +public class DiffPiece : IEquatable { - public enum ChangeType + public ChangeType Type { get; set; } + public int? Position { get; set; } + public string Text { get; set; } + public List SubPieces { get; set; } = []; + + public DiffPiece(string text, ChangeType type, int? position = null) { - Unchanged, - Deleted, - Inserted, - Imaginary, - Modified + Text = text; + Position = position; + Type = type; } - public class DiffPiece : IEquatable + public DiffPiece() + : this(null, ChangeType.Imaginary) { - public ChangeType Type { get; set; } - public int? Position { get; set; } - public string Text { get; set; } - public List SubPieces { get; set; } = new List(); + } - public DiffPiece(string text, ChangeType type, int? position = null) - { - Text = text; - Position = position; - Type = type; - } + public override bool Equals(object obj) + { + return Equals(obj as DiffPiece); + } - public DiffPiece() - : this(null, ChangeType.Imaginary) - { - } + public bool Equals(DiffPiece other) + { + return other != null + && Type == other.Type + && EqualityComparer.Default.Equals(Position, other.Position) + && Text == other.Text + && SubPiecesEqual(other); + } - public override bool Equals(object obj) - { - return Equals(obj as DiffPiece); - } + public override int GetHashCode() + { + var hashCode = 1688038063; + hashCode = hashCode * -1521134295 + Type.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Position); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Text); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(SubPieces?.Count); + return hashCode; + } - public bool Equals(DiffPiece other) - { - return other != null - && Type == other.Type - && EqualityComparer.Default.Equals(Position, other.Position) - && Text == other.Text - && SubPiecesEqual(other); - } + private bool SubPiecesEqual(DiffPiece other) + { + if (SubPieces is null) + return other.SubPieces is null; + else if (other.SubPieces is null) + return false; - public override int GetHashCode() - { - var hashCode = 1688038063; - hashCode = hashCode * -1521134295 + Type.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Position); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Text); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(SubPieces?.Count); - return hashCode; - } + if (SubPieces.Count != other.SubPieces.Count) + return false; - private bool SubPiecesEqual(DiffPiece other) + for (int i = 0; i < SubPieces.Count; i++) { - if (SubPieces is null) - return other.SubPieces is null; - else if (other.SubPieces is null) - return false; - - if (SubPieces.Count != other.SubPieces.Count) + if (!Equals(SubPieces[i], other.SubPieces[i])) return false; - - for (int i = 0; i < SubPieces.Count; i++) - { - if (!Equals(SubPieces[i], other.SubPieces[i])) - return false; - } - - return true; } + + return true; } } \ No newline at end of file diff --git a/DiffPlex/DiffBuilder/Model/SideBySideDiffModel.cs b/DiffPlex/DiffBuilder/Model/SideBySideDiffModel.cs index fed2bc25..e4acedc0 100644 --- a/DiffPlex/DiffBuilder/Model/SideBySideDiffModel.cs +++ b/DiffPlex/DiffBuilder/Model/SideBySideDiffModel.cs @@ -1,17 +1,10 @@ -namespace DiffPlex.DiffBuilder.Model -{ - /// - /// A model which represents differences between to texts to be shown side by side - /// - public class SideBySideDiffModel - { - public DiffPaneModel OldText { get; } - public DiffPaneModel NewText { get; } +namespace DiffPlex.DiffBuilder.Model; - public SideBySideDiffModel() - { - OldText = new DiffPaneModel(); - NewText = new DiffPaneModel(); - } - } +/// +/// A model which represents differences between to texts to be shown side by side +/// +public class SideBySideDiffModel +{ + public DiffPaneModel OldText { get; } = new(); + public DiffPaneModel NewText { get; } = new(); } \ No newline at end of file diff --git a/DiffPlex/DiffBuilder/SideBySideDiffBuilder.cs b/DiffPlex/DiffBuilder/SideBySideDiffBuilder.cs index 251d04e5..caf4e5fd 100644 --- a/DiffPlex/DiffBuilder/SideBySideDiffBuilder.cs +++ b/DiffPlex/DiffBuilder/SideBySideDiffBuilder.cs @@ -5,29 +5,29 @@ using DiffPlex.DiffBuilder.Model; using DiffPlex.Model; -namespace DiffPlex.DiffBuilder +namespace DiffPlex.DiffBuilder; + +public class SideBySideDiffBuilder : ISideBySideDiffBuilder { - public class SideBySideDiffBuilder : ISideBySideDiffBuilder - { - private readonly IDiffer differ; - private readonly IChunker lineChunker; - private readonly IChunker wordChunker; + private readonly IDiffer differ; + private readonly IChunker lineChunker; + private readonly IChunker wordChunker; - private delegate ChangeType PieceBuilder(string oldText, string newText, List oldPieces, List newPieces, bool ignoreWhitespace, bool ignoreCase); + private delegate ChangeType PieceBuilder(string oldText, string newText, List oldPieces, List newPieces, bool ignoreWhitespace, bool ignoreCase); - /// - /// Gets the default singleton instance. - /// - public static SideBySideDiffBuilder Instance { get; } = new SideBySideDiffBuilder(); + /// + /// Gets the default singleton instance. + /// + public static SideBySideDiffBuilder Instance { get; } = new(); - public SideBySideDiffBuilder(IDiffer differ, IChunker lineChunker, IChunker wordChunker) - { - this.differ = differ ?? Differ.Instance; - this.lineChunker = lineChunker ?? throw new ArgumentNullException(nameof(lineChunker)); - this.wordChunker = wordChunker ?? throw new ArgumentNullException(nameof(wordChunker)); - } + public SideBySideDiffBuilder(IDiffer differ, IChunker lineChunker, IChunker wordChunker) + { + this.differ = differ ?? Differ.Instance; + this.lineChunker = lineChunker ?? throw new ArgumentNullException(nameof(lineChunker)); + this.wordChunker = wordChunker ?? throw new ArgumentNullException(nameof(wordChunker)); + } - public SideBySideDiffBuilder(IDiffer differ = null) : + public SideBySideDiffBuilder(IDiffer differ = null) : this(differ, new LineChunker(), new WordChunker()) { } @@ -37,7 +37,7 @@ public SideBySideDiffBuilder(IDiffer differ, char[] wordSeparators) { } - public SideBySideDiffModel BuildDiffModel(string oldText, string newText) + public SideBySideDiffModel BuildDiffModel(string oldText, string newText) => BuildDiffModel(oldText, newText, ignoreWhitespace: true); public SideBySideDiffModel BuildDiffModel(string oldText, string newText, bool ignoreWhitespace) => BuildDiffModel( @@ -192,12 +192,11 @@ private static ChangeType BuildDiffPieces(DiffResult diffResult, List return ChangeType.Modified; } - if (newPieces.Any(x => x.Type is ChangeType.Modified or ChangeType.Inserted or ChangeType.Deleted)) - { - return ChangeType.Modified; - } - - return ChangeType.Unchanged; + if (newPieces.Any(x => x.Type is ChangeType.Modified or ChangeType.Inserted or ChangeType.Deleted)) + { + return ChangeType.Modified; } + + return ChangeType.Unchanged; } } \ No newline at end of file diff --git a/DiffPlex/DiffPlex.csproj b/DiffPlex/DiffPlex.csproj index c6c9a599..24de2e7a 100644 --- a/DiffPlex/DiffPlex.csproj +++ b/DiffPlex/DiffPlex.csproj @@ -1,7 +1,7 @@  - net45;netstandard2.0;net6.0 + net45;netstandard2.0;net9.0 1.9 diff DiffPlex is a diffing library that allows you to programmatically create text diffs. DiffPlex is a fast and tested library. diff --git a/DiffPlex/Differ.cs b/DiffPlex/Differ.cs index 281ee96c..0af52dda 100644 --- a/DiffPlex/Differ.cs +++ b/DiffPlex/Differ.cs @@ -3,150 +3,150 @@ using DiffPlex.Chunkers; using DiffPlex.Model; -namespace DiffPlex +namespace DiffPlex; + +public class Differ : IDiffer { - public class Differ : IDiffer - { - /// - /// Gets the default singleton instance of differ instance. - /// - public static Differ Instance { get; } = new Differ(); + /// + /// Gets the default singleton instance of differ instance. + /// + public static Differ Instance { get; } = new(); - public DiffResult CreateLineDiffs(string oldText, string newText, bool ignoreWhitespace) - { - return CreateDiffs(oldText, newText, ignoreWhitespace, false, new LineChunker()); - } + public DiffResult CreateLineDiffs(string oldText, string newText, bool ignoreWhitespace) + { + return CreateDiffs(oldText, newText, ignoreWhitespace, false, new LineChunker()); + } - public DiffResult CreateLineDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase) - { - return CreateDiffs(oldText, newText, ignoreWhitespace, ignoreCase, new LineChunker()); - } + public DiffResult CreateLineDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase) + { + return CreateDiffs(oldText, newText, ignoreWhitespace, ignoreCase, new LineChunker()); + } - public DiffResult CreateCharacterDiffs(string oldText, string newText, bool ignoreWhitespace) - { - return CreateDiffs(oldText, newText, ignoreWhitespace, false, new CharacterChunker()); - } + public DiffResult CreateCharacterDiffs(string oldText, string newText, bool ignoreWhitespace) + { + return CreateDiffs(oldText, newText, ignoreWhitespace, false, new CharacterChunker()); + } - public DiffResult CreateCharacterDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase) - { - return CreateDiffs(oldText, newText, ignoreWhitespace, ignoreCase, new CharacterChunker()); - } + public DiffResult CreateCharacterDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase) + { + return CreateDiffs(oldText, newText, ignoreWhitespace, ignoreCase, new CharacterChunker()); + } - public DiffResult CreateWordDiffs(string oldText, string newText, bool ignoreWhitespace, char[] separators) - { - return CreateDiffs(oldText, newText, ignoreWhitespace, false, new DelimiterChunker(separators)); - } + public DiffResult CreateWordDiffs(string oldText, string newText, bool ignoreWhitespace, char[] separators) + { + return CreateDiffs(oldText, newText, ignoreWhitespace, false, new DelimiterChunker(separators)); + } - public DiffResult CreateWordDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase, char[] separators) - { - return CreateDiffs(oldText, newText, ignoreWhitespace, ignoreCase, new DelimiterChunker(separators)); - } + public DiffResult CreateWordDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase, char[] separators) + { + return CreateDiffs(oldText, newText, ignoreWhitespace, ignoreCase, new DelimiterChunker(separators)); + } - public DiffResult CreateCustomDiffs(string oldText, string newText, bool ignoreWhiteSpace, Func chunker) - { - return CreateDiffs(oldText, newText, ignoreWhiteSpace, false, new CustomFunctionChunker(chunker)); - } + public DiffResult CreateCustomDiffs(string oldText, string newText, bool ignoreWhiteSpace, Func chunker) + { + return CreateDiffs(oldText, newText, ignoreWhiteSpace, false, new CustomFunctionChunker(chunker)); + } - public DiffResult CreateCustomDiffs(string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, Func chunker) - { - return CreateDiffs(oldText, newText, ignoreWhiteSpace, ignoreCase, new CustomFunctionChunker(chunker)); - } + public DiffResult CreateCustomDiffs(string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, Func chunker) + { + return CreateDiffs(oldText, newText, ignoreWhiteSpace, ignoreCase, new CustomFunctionChunker(chunker)); + } - public DiffResult CreateDiffs(string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, IChunker chunker) - { - if (oldText == null) throw new ArgumentNullException(nameof(oldText)); - if (newText == null) throw new ArgumentNullException(nameof(newText)); - if (chunker == null) throw new ArgumentNullException(nameof(chunker)); + public DiffResult CreateDiffs(string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, IChunker chunker) + { + if (oldText == null) throw new ArgumentNullException(nameof(oldText)); + if (newText == null) throw new ArgumentNullException(nameof(newText)); + if (chunker == null) throw new ArgumentNullException(nameof(chunker)); - var pieceHash = new Dictionary(ignoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal); - var lineDiffs = new List(); + var pieceHash = new Dictionary(ignoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal); + var lineDiffs = new List(); - var modOld = new ModificationData(oldText); - var modNew = new ModificationData(newText); + var modOld = new ModificationData(oldText); + var modNew = new ModificationData(newText); - BuildPieceHashes(pieceHash, modOld, ignoreWhiteSpace, chunker); - BuildPieceHashes(pieceHash, modNew, ignoreWhiteSpace, chunker); + BuildPieceHashes(pieceHash, modOld, ignoreWhiteSpace, chunker); + BuildPieceHashes(pieceHash, modNew, ignoreWhiteSpace, chunker); - BuildModificationData(modOld, modNew); + BuildModificationData(modOld, modNew); - int piecesALength = modOld.HashedPieces.Length; - int piecesBLength = modNew.HashedPieces.Length; - int posA = 0; - int posB = 0; + int piecesALength = modOld.HashedPieces.Length; + int piecesBLength = modNew.HashedPieces.Length; + int posA = 0; + int posB = 0; - do + do + { + while (posA < piecesALength + && posB < piecesBLength + && !modOld.Modifications[posA] + && !modNew.Modifications[posB]) { - while (posA < piecesALength - && posB < piecesBLength - && !modOld.Modifications[posA] - && !modNew.Modifications[posB]) - { - posA++; - posB++; - } + posA++; + posB++; + } - int beginA = posA; - int beginB = posB; - for (; posA < piecesALength && modOld.Modifications[posA]; posA++) ; + int beginA = posA; + int beginB = posB; + for (; posA < piecesALength && modOld.Modifications[posA]; posA++) ; - for (; posB < piecesBLength && modNew.Modifications[posB]; posB++) ; + for (; posB < piecesBLength && modNew.Modifications[posB]; posB++) ; - int deleteCount = posA - beginA; - int insertCount = posB - beginB; - if (deleteCount > 0 || insertCount > 0) - { - lineDiffs.Add(new DiffBlock(beginA, deleteCount, beginB, insertCount)); - } - } while (posA < piecesALength && posB < piecesBLength); + int deleteCount = posA - beginA; + int insertCount = posB - beginB; + if (deleteCount > 0 || insertCount > 0) + { + lineDiffs.Add(new(beginA, deleteCount, beginB, insertCount)); + } + } while (posA < piecesALength && posB < piecesBLength); - return new DiffResult(modOld.Pieces, modNew.Pieces, lineDiffs); - } + return new(modOld.Pieces, modNew.Pieces, lineDiffs); + } - /// - /// Finds the middle snake and the minimum length of the edit script comparing string A and B - /// - /// - /// Lower bound inclusive - /// Upper bound exclusive - /// - /// lower bound inclusive - /// upper bound exclusive - /// - protected static EditLengthResult CalculateEditLength(int[] A, int startA, int endA, int[] B, int startB, int endB) - { - int N = endA - startA; - int M = endB - startB; - int MAX = M + N + 1; + /// + /// Finds the middle snake and the minimum length of the edit script comparing string A and B + /// + /// + /// Lower bound inclusive + /// Upper bound exclusive + /// + /// lower bound inclusive + /// upper bound exclusive + /// + protected static EditLengthResult CalculateEditLength(int[] A, int startA, int endA, int[] B, int startB, int endB) + { + int N = endA - startA; + int M = endB - startB; + int MAX = M + N + 1; - var forwardDiagonal = new int[MAX + 1]; - var reverseDiagonal = new int[MAX + 1]; - return CalculateEditLength(A, startA, endA, B, startB, endB, forwardDiagonal, reverseDiagonal); - } + var forwardDiagonal = new int[MAX + 1]; + var reverseDiagonal = new int[MAX + 1]; + return CalculateEditLength(A, startA, endA, B, startB, endB, forwardDiagonal, reverseDiagonal); + } - private static EditLengthResult CalculateEditLength(int[] A, int startA, int endA, int[] B, int startB, int endB, int[] forwardDiagonal, int[] reverseDiagonal) - { - if (null == A) throw new ArgumentNullException(nameof(A)); - if (null == B) throw new ArgumentNullException(nameof(B)); + private static EditLengthResult CalculateEditLength(int[] A, int startA, int endA, int[] B, int startB, int endB, int[] forwardDiagonal, int[] reverseDiagonal) + { + if (null == A) throw new ArgumentNullException(nameof(A)); + if (null == B) throw new ArgumentNullException(nameof(B)); - if (A.Length == 0 && B.Length == 0) - { - return new EditLengthResult(); - } + if (A.Length == 0 && B.Length == 0) + { + return new(); + } - int N = endA - startA; - int M = endB - startB; - int MAX = M + N + 1; - int HALF = MAX / 2; - int delta = N - M; - bool deltaEven = delta % 2 == 0; - forwardDiagonal[1 + HALF] = 0; - reverseDiagonal[1 + HALF] = N + 1; + int N = endA - startA; + int M = endB - startB; + int MAX = M + N + 1; + int HALF = MAX / 2; + int delta = N - M; + bool deltaEven = delta % 2 == 0; + forwardDiagonal[1 + HALF] = 0; + reverseDiagonal[1 + HALF] = N + 1; - Log.WriteLine("Comparing strings"); - Log.WriteLine("\t{0} of length {1}", A, A.Length); - Log.WriteLine("\t{0} of length {1}", B, B.Length); + Log.WriteLine("Comparing strings"); + Log.WriteLine("\t{0} of length {1}", A, A.Length); + Log.WriteLine("\t{0} of length {1}", B, B.Length); - for (int D = 0; D <= HALF; D++) + for (int D = 0; D <= HALF; D++) { Log.WriteLine("\nSearching for a {0}-Path", D); // forward D-path @@ -189,7 +189,7 @@ private static EditLengthResult CalculateEditLength(int[] A, int startA, int end int revY = revX - k; if (revX <= x && revY <= y) { - return new EditLengthResult + return new() { EditLength = 2*D - 1, StartX = startX + startA, @@ -243,7 +243,7 @@ private static EditLengthResult CalculateEditLength(int[] A, int startA, int end int forY = forX - (k + delta); if (forX >= x && forY >= y) { - return new EditLengthResult + return new() { EditLength = 2*D, StartX = x + startA, @@ -352,8 +352,7 @@ private static void BuildPieceHashes(IDictionary pieceHash, Modific data.HashedPieces[i] = pieceHash.Count; pieceHash[piece] = pieceHash.Count; } - } - } + } } \ No newline at end of file diff --git a/DiffPlex/IChunker.cs b/DiffPlex/IChunker.cs index 9e9de1a2..a9d7c194 100644 --- a/DiffPlex/IChunker.cs +++ b/DiffPlex/IChunker.cs @@ -1,15 +1,14 @@ using System.Collections.Generic; -namespace DiffPlex +namespace DiffPlex; + +/// +/// Responsible for how to turn the document into pieces +/// +public interface IChunker { /// - /// Responsible for how to turn the document into pieces + /// Divide text into sub-parts /// - public interface IChunker - { - /// - /// Divide text into sub-parts - /// - IReadOnlyList Chunk(string text); - } + IReadOnlyList Chunk(string text); } \ No newline at end of file diff --git a/DiffPlex/IDiffer.cs b/DiffPlex/IDiffer.cs index c860e04f..0d78758e 100644 --- a/DiffPlex/IDiffer.cs +++ b/DiffPlex/IDiffer.cs @@ -1,46 +1,45 @@ using System; using DiffPlex.Model; -namespace DiffPlex +namespace DiffPlex; + +/// +/// Responsible for generating differences between texts +/// +public interface IDiffer { + [Obsolete("Use CreateDiffs method instead", false)] + DiffResult CreateLineDiffs(string oldText, string newText, bool ignoreWhitespace); + + [Obsolete("Use CreateDiffs method instead", false)] + DiffResult CreateLineDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase); + + [Obsolete("Use CreateDiffs method instead", false)] + DiffResult CreateCharacterDiffs(string oldText, string newText, bool ignoreWhitespace); + + [Obsolete("Use CreateDiffs method instead", false)] + DiffResult CreateCharacterDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase); + + [Obsolete("Use CreateDiffs method instead", false)] + DiffResult CreateWordDiffs(string oldText, string newText, bool ignoreWhitespace, char[] separators); + + [Obsolete("Use CreateDiffs method instead", false)] + DiffResult CreateWordDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase, char[] separators); + + [Obsolete("Use CreateDiffs method instead", false)] + DiffResult CreateCustomDiffs(string oldText, string newText, bool ignoreWhiteSpace, Func chunker); + + [Obsolete("Use CreateDiffs method instead", false)] + DiffResult CreateCustomDiffs(string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, Func chunker); + /// - /// Responsible for generating differences between texts + /// Creates a diff by comparing text line by line. /// - public interface IDiffer - { - [Obsolete("Use CreateDiffs method instead", false)] - DiffResult CreateLineDiffs(string oldText, string newText, bool ignoreWhitespace); - - [Obsolete("Use CreateDiffs method instead", false)] - DiffResult CreateLineDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase); - - [Obsolete("Use CreateDiffs method instead", false)] - DiffResult CreateCharacterDiffs(string oldText, string newText, bool ignoreWhitespace); - - [Obsolete("Use CreateDiffs method instead", false)] - DiffResult CreateCharacterDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase); - - [Obsolete("Use CreateDiffs method instead", false)] - DiffResult CreateWordDiffs(string oldText, string newText, bool ignoreWhitespace, char[] separators); - - [Obsolete("Use CreateDiffs method instead", false)] - DiffResult CreateWordDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase, char[] separators); - - [Obsolete("Use CreateDiffs method instead", false)] - DiffResult CreateCustomDiffs(string oldText, string newText, bool ignoreWhiteSpace, Func chunker); - - [Obsolete("Use CreateDiffs method instead", false)] - DiffResult CreateCustomDiffs(string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, Func chunker); - - /// - /// Creates a diff by comparing text line by line. - /// - /// The old text. - /// The new text. - /// If set to will ignore white space when determining if lines are the same. - /// Determine if the text comparision is case sensitive or not - /// Component responsible for tokenizing the compared texts - /// A object which details the differences - DiffResult CreateDiffs(string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, IChunker chunker); - } + /// The old text. + /// The new text. + /// If set to will ignore white space when determining if lines are the same. + /// Determine if the text comparision is case sensitive or not + /// Component responsible for tokenizing the compared texts + /// A object which details the differences + DiffResult CreateDiffs(string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, IChunker chunker); } \ No newline at end of file diff --git a/DiffPlex/IThreeWayDiffer.cs b/DiffPlex/IThreeWayDiffer.cs index 8a429323..62a847ff 100644 --- a/DiffPlex/IThreeWayDiffer.cs +++ b/DiffPlex/IThreeWayDiffer.cs @@ -1,34 +1,33 @@ using DiffPlex.Model; -namespace DiffPlex +namespace DiffPlex; + +/// +/// Responsible for generating three-way differences and merges between texts +/// +public interface IThreeWayDiffer { /// - /// Responsible for generating three-way differences and merges between texts + /// Creates a three-way diff by comparing base, old, and new text line by line. /// - public interface IThreeWayDiffer - { - /// - /// Creates a three-way diff by comparing base, old, and new text line by line. - /// - /// The common base text. - /// The old version of the text. - /// The new version of the text. - /// If set to will ignore white space when determining if lines are the same. - /// Determine if the text comparison is case sensitive or not - /// Component responsible for tokenizing the compared texts - /// A object which details the differences - ThreeWayDiffResult CreateDiffs(string baseText, string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, IChunker chunker); + /// The common base text. + /// The old version of the text. + /// The new version of the text. + /// If set to will ignore white space when determining if lines are the same. + /// Determine if the text comparison is case sensitive or not + /// Component responsible for tokenizing the compared texts + /// A object which details the differences + ThreeWayDiffResult CreateDiffs(string baseText, string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, IChunker chunker); - /// - /// Creates a three-way merge by comparing base, old, and new text line by line. - /// - /// The common base text. - /// The old version of the text. - /// The new version of the text. - /// If set to will ignore white space when determining if lines are the same. - /// Determine if the text comparison is case sensitive or not - /// Component responsible for tokenizing the compared texts - /// A object which contains the merged result and conflict information - ThreeWayMergeResult CreateMerge(string baseText, string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, IChunker chunker); - } + /// + /// Creates a three-way merge by comparing base, old, and new text line by line. + /// + /// The common base text. + /// The old version of the text. + /// The new version of the text. + /// If set to will ignore white space when determining if lines are the same. + /// Determine if the text comparison is case sensitive or not + /// Component responsible for tokenizing the compared texts + /// A object which contains the merged result and conflict information + ThreeWayMergeResult CreateMerge(string baseText, string oldText, string newText, bool ignoreWhiteSpace, bool ignoreCase, IChunker chunker); } diff --git a/DiffPlex/Log.cs b/DiffPlex/Log.cs index 6163e9e8..42364b34 100644 --- a/DiffPlex/Log.cs +++ b/DiffPlex/Log.cs @@ -1,19 +1,18 @@ using System.Diagnostics; -namespace DiffPlex +namespace DiffPlex; + +static class Log { - static class Log + [Conditional("LOG")] + public static void WriteLine(string format, params object[] args) { - [Conditional("LOG")] - public static void WriteLine(string format, params object[] args) - { - Debug.WriteLine(string.Format(format, args)); - } + Debug.WriteLine(string.Format(format, args)); + } - [Conditional("LOG")] - public static void Write(string format, params object[] args) - { - // not implemented - } + [Conditional("LOG")] + public static void Write(string format, params object[] args) + { + // not implemented } } diff --git a/DiffPlex/Model/DiffBlock.cs b/DiffPlex/Model/DiffBlock.cs index 7fa5fe8a..26a65df1 100644 --- a/DiffPlex/Model/DiffBlock.cs +++ b/DiffPlex/Model/DiffBlock.cs @@ -1,37 +1,27 @@ -namespace DiffPlex.Model +namespace DiffPlex.Model; + +/// +/// A block of consecutive edits from A and/or B +/// +public class DiffBlock(int deleteStartA, int deleteCountA, int insertStartB, int insertCountB) { /// - /// A block of consecutive edits from A and/or B + /// Position where deletions in A begin /// - public class DiffBlock - { - /// - /// Position where deletions in A begin - /// - public int DeleteStartA { get; } - - /// - /// The number of deletions in A - /// - public int DeleteCountA { get; } - - /// - /// Position where insertion in B begin - /// - public int InsertStartB { get; } + public int DeleteStartA { get; } = deleteStartA; - /// - /// The number of insertions in B - /// - public int InsertCountB { get; } + /// + /// The number of deletions in A + /// + public int DeleteCountA { get; } = deleteCountA; + /// + /// Position where insertion in B begin + /// + public int InsertStartB { get; } = insertStartB; - public DiffBlock(int deleteStartA, int deleteCountA, int insertStartB, int insertCountB) - { - DeleteStartA = deleteStartA; - DeleteCountA = deleteCountA; - InsertStartB = insertStartB; - InsertCountB = insertCountB; - } - } + /// + /// The number of insertions in B + /// + public int InsertCountB { get; } = insertCountB; } \ No newline at end of file diff --git a/DiffPlex/Model/DiffResult.cs b/DiffPlex/Model/DiffResult.cs index 8055eb1e..d9ee5f67 100644 --- a/DiffPlex/Model/DiffResult.cs +++ b/DiffPlex/Model/DiffResult.cs @@ -1,33 +1,25 @@ using System.Collections.Generic; -namespace DiffPlex.Model +namespace DiffPlex.Model; + +/// +/// The result of diffing two pieces of text +/// +public class DiffResult(IReadOnlyList piecesOld, IReadOnlyList piecesNew, IList blocks) { /// - /// The result of diffing two pieces of text + /// The chunked pieces of the old text /// - public class DiffResult - { - /// - /// The chunked pieces of the old text - /// - public IReadOnlyList PiecesOld { get; } - - /// - /// The chunked pieces of the new text - /// - public IReadOnlyList PiecesNew { get; } + public IReadOnlyList PiecesOld { get; } = piecesOld; + /// + /// The chunked pieces of the new text + /// + public IReadOnlyList PiecesNew { get; } = piecesNew; - /// - /// A collection of DiffBlocks which details deletions and insertions - /// - public IList DiffBlocks { get; } - public DiffResult(IReadOnlyList piecesOld, IReadOnlyList piecesNew, IList blocks) - { - PiecesOld = piecesOld; - PiecesNew = piecesNew; - DiffBlocks = blocks; - } - } + /// + /// A collection of DiffBlocks which details deletions and insertions + /// + public IList DiffBlocks { get; } = blocks; } \ No newline at end of file diff --git a/DiffPlex/Model/EditLengthResult.cs b/DiffPlex/Model/EditLengthResult.cs index 859014fa..17b362e6 100644 --- a/DiffPlex/Model/EditLengthResult.cs +++ b/DiffPlex/Model/EditLengthResult.cs @@ -1,23 +1,22 @@ -namespace DiffPlex.Model +namespace DiffPlex.Model; + +public enum Edit { - public enum Edit - { - None, - DeleteRight, - DeleteLeft, - InsertDown, - InsertUp - } + None, + DeleteRight, + DeleteLeft, + InsertDown, + InsertUp +} - public class EditLengthResult - { - public int EditLength { get; set; } +public class EditLengthResult +{ + public int EditLength { get; set; } - public int StartX { get; set; } - public int EndX { get; set; } - public int StartY { get; set; } - public int EndY { get; set; } + public int StartX { get; set; } + public int EndX { get; set; } + public int StartY { get; set; } + public int EndY { get; set; } - public Edit LastEdit { get; set; } - } + public Edit LastEdit { get; set; } } \ No newline at end of file diff --git a/DiffPlex/Model/ModificationData.cs b/DiffPlex/Model/ModificationData.cs index 9a6e0774..114ecdea 100644 --- a/DiffPlex/Model/ModificationData.cs +++ b/DiffPlex/Model/ModificationData.cs @@ -1,20 +1,14 @@ using System.Collections.Generic; -namespace DiffPlex.Model -{ - public class ModificationData - { - public int[] HashedPieces { get; set; } +namespace DiffPlex.Model; - public string RawData { get; } +public class ModificationData(string str) +{ + public int[] HashedPieces { get; set; } - public bool[] Modifications { get; set; } + public string RawData { get; } = str; - public IReadOnlyList Pieces { get; set; } + public bool[] Modifications { get; set; } - public ModificationData(string str) - { - RawData = str; - } - } + public IReadOnlyList Pieces { get; set; } } \ No newline at end of file diff --git a/DiffPlex/Model/ThreeWayConflictBlock.cs b/DiffPlex/Model/ThreeWayConflictBlock.cs index d2fd8ad9..9133eb0c 100644 --- a/DiffPlex/Model/ThreeWayConflictBlock.cs +++ b/DiffPlex/Model/ThreeWayConflictBlock.cs @@ -1,46 +1,39 @@ using System.Collections.Generic; -namespace DiffPlex.Model +namespace DiffPlex.Model; + +/// +/// Represents a conflict block in a three-way merge where automatic resolution was not possible +/// +public class ThreeWayConflictBlock( + int mergedStart, + IReadOnlyList basePieces, + IReadOnlyList oldPieces, + IReadOnlyList newPieces, + ThreeWayDiffBlock originalBlock) { /// - /// Represents a conflict block in a three-way merge where automatic resolution was not possible + /// Position in the merged result where this conflict starts /// - public class ThreeWayConflictBlock - { - /// - /// Position in the merged result where this conflict starts - /// - public int MergedStart { get; } - - /// - /// The base text pieces for this conflict - /// - public IReadOnlyList BasePieces { get; } + public int MergedStart { get; } = mergedStart; - /// - /// Old text pieces for this conflict - /// - public IReadOnlyList OldPieces { get; } + /// + /// The base text pieces for this conflict + /// + public IReadOnlyList BasePieces { get; } = basePieces; - /// - /// New text pieces for this conflict - /// - public IReadOnlyList NewPieces { get; } + /// + /// Old text pieces for this conflict + /// + public IReadOnlyList OldPieces { get; } = oldPieces; - /// - /// The original three-way diff block that caused this conflict - /// - public ThreeWayDiffBlock OriginalBlock { get; } + /// + /// New text pieces for this conflict + /// + public IReadOnlyList NewPieces { get; } = newPieces; - public ThreeWayConflictBlock(int mergedStart, IReadOnlyList basePieces, - IReadOnlyList oldPieces, IReadOnlyList newPieces, - ThreeWayDiffBlock originalBlock) - { - MergedStart = mergedStart; - BasePieces = basePieces; - OldPieces = oldPieces; - NewPieces = newPieces; - OriginalBlock = originalBlock; - } - } + /// + /// The original three-way diff block that caused this conflict + /// + public ThreeWayDiffBlock OriginalBlock { get; } = originalBlock; } diff --git a/DiffPlex/Model/ThreeWayDiffBlock.cs b/DiffPlex/Model/ThreeWayDiffBlock.cs index 2f763091..67adef39 100644 --- a/DiffPlex/Model/ThreeWayDiffBlock.cs +++ b/DiffPlex/Model/ThreeWayDiffBlock.cs @@ -1,86 +1,80 @@ -namespace DiffPlex.Model +namespace DiffPlex.Model; + +/// +/// Represents the type of change in a three-way diff +/// +public enum ThreeWayChangeType { /// - /// Represents the type of change in a three-way diff + /// No change from base in either old or new /// - public enum ThreeWayChangeType - { - /// - /// No change from base in either old or new - /// - Unchanged, - - /// - /// Change only in old, new unchanged from base - /// - OldOnly, - - /// - /// Change only in new, old unchanged from base - /// - NewOnly, - - /// - /// Same change in both old and new - /// - BothSame, - - /// - /// Different changes in old and new (conflict) - /// - Conflict - } - + Unchanged, + + /// + /// Change only in old, new unchanged from base + /// + OldOnly, + + /// + /// Change only in new, old unchanged from base + /// + NewOnly, + /// - /// A block representing changes in a three-way diff + /// Same change in both old and new /// - public class ThreeWayDiffBlock - { - /// - /// Position where the block starts in base - /// - public int BaseStart { get; } + BothSame, + + /// + /// Different changes in old and new (conflict) + /// + Conflict +} - /// - /// The number of pieces in base for this block - /// - public int BaseCount { get; } +/// +/// A block representing changes in a three-way diff +/// +public class ThreeWayDiffBlock( + int baseStart, + int baseCount, + int oldStart, + int oldCount, + int newStart, + int newCount, + ThreeWayChangeType changeType) +{ + /// + /// Position where the block starts in base + /// + public int BaseStart { get; } = baseStart; - /// - /// Position where the block starts in old - /// - public int OldStart { get; } + /// + /// The number of pieces in base for this block + /// + public int BaseCount { get; } = baseCount; - /// - /// The number of pieces in old for this block - /// - public int OldCount { get; } + /// + /// Position where the block starts in old + /// + public int OldStart { get; } = oldStart; - /// - /// Position where the block starts in new - /// - public int NewStart { get; } + /// + /// The number of pieces in old for this block + /// + public int OldCount { get; } = oldCount; - /// - /// The number of pieces in new for this block - /// - public int NewCount { get; } + /// + /// Position where the block starts in new + /// + public int NewStart { get; } = newStart; - /// - /// The type of change this block represents - /// - public ThreeWayChangeType ChangeType { get; } + /// + /// The number of pieces in new for this block + /// + public int NewCount { get; } = newCount; - public ThreeWayDiffBlock(int baseStart, int baseCount, int oldStart, int oldCount, - int newStart, int newCount, ThreeWayChangeType changeType) - { - BaseStart = baseStart; - BaseCount = baseCount; - OldStart = oldStart; - OldCount = oldCount; - NewStart = newStart; - NewCount = newCount; - ChangeType = changeType; - } - } + /// + /// The type of change this block represents + /// + public ThreeWayChangeType ChangeType { get; } = changeType; } diff --git a/DiffPlex/Model/ThreeWayDiffResult.cs b/DiffPlex/Model/ThreeWayDiffResult.cs index 5d934b4b..cf271a07 100644 --- a/DiffPlex/Model/ThreeWayDiffResult.cs +++ b/DiffPlex/Model/ThreeWayDiffResult.cs @@ -1,39 +1,33 @@ using System.Collections.Generic; -namespace DiffPlex.Model +namespace DiffPlex.Model; + +/// +/// The result of diffing three pieces of text (base, old, new) +/// +public class ThreeWayDiffResult( + IReadOnlyList piecesBase, + IReadOnlyList piecesOld, + IReadOnlyList piecesNew, + IList blocks) { /// - /// The result of diffing three pieces of text (base, old, new) + /// The chunked pieces of the base text /// - public class ThreeWayDiffResult - { - /// - /// The chunked pieces of the base text - /// - public IReadOnlyList PiecesBase { get; } - - /// - /// The chunked pieces of the old text - /// - public IReadOnlyList PiecesOld { get; } + public IReadOnlyList PiecesBase { get; } = piecesBase; - /// - /// The chunked pieces of the new text - /// - public IReadOnlyList PiecesNew { get; } + /// + /// The chunked pieces of the old text + /// + public IReadOnlyList PiecesOld { get; } = piecesOld; - /// - /// A collection of ThreeWayDiffBlocks which details the differences between the three texts - /// - public IList DiffBlocks { get; } + /// + /// The chunked pieces of the new text + /// + public IReadOnlyList PiecesNew { get; } = piecesNew; - public ThreeWayDiffResult(IReadOnlyList piecesBase, IReadOnlyList piecesOld, - IReadOnlyList piecesNew, IList blocks) - { - PiecesBase = piecesBase; - PiecesOld = piecesOld; - PiecesNew = piecesNew; - DiffBlocks = blocks; - } - } + /// + /// A collection of ThreeWayDiffBlocks which details the differences between the three texts + /// + public IList DiffBlocks { get; } = blocks; } diff --git a/DiffPlex/Model/ThreeWayMergeResult.cs b/DiffPlex/Model/ThreeWayMergeResult.cs index b4ace252..48595d3f 100644 --- a/DiffPlex/Model/ThreeWayMergeResult.cs +++ b/DiffPlex/Model/ThreeWayMergeResult.cs @@ -1,39 +1,33 @@ using System.Collections.Generic; -namespace DiffPlex.Model +namespace DiffPlex.Model; + +/// +/// The result of a three-way merge operation +/// +public class ThreeWayMergeResult( + IReadOnlyList mergedPieces, + bool isSuccessful, + IList conflictBlocks, + ThreeWayDiffResult diffResult) { /// - /// The result of a three-way merge operation + /// The merged text pieces /// - public class ThreeWayMergeResult - { - /// - /// The merged text pieces - /// - public IReadOnlyList MergedPieces { get; } - - /// - /// Whether the merge was successful without conflicts - /// - public bool IsSuccessful { get; } + public IReadOnlyList MergedPieces { get; } = mergedPieces; - /// - /// List of conflict blocks that could not be automatically merged - /// - public IList ConflictBlocks { get; } + /// + /// Whether the merge was successful without conflicts + /// + public bool IsSuccessful { get; } = isSuccessful; - /// - /// The three-way diff result that was used to create this merge - /// - public ThreeWayDiffResult DiffResult { get; } + /// + /// List of conflict blocks that could not be automatically merged + /// + public IList ConflictBlocks { get; } = conflictBlocks; - public ThreeWayMergeResult(IReadOnlyList mergedPieces, bool isSuccessful, - IList conflictBlocks, ThreeWayDiffResult diffResult) - { - MergedPieces = mergedPieces; - IsSuccessful = isSuccessful; - ConflictBlocks = conflictBlocks; - DiffResult = diffResult; - } - } + /// + /// The three-way diff result that was used to create this merge + /// + public ThreeWayDiffResult DiffResult { get; } = diffResult; } diff --git a/DiffPlex/Renderer/UnidiffRenderer.cs b/DiffPlex/Renderer/UnidiffRenderer.cs index 6ddfe535..c4376e1f 100644 --- a/DiffPlex/Renderer/UnidiffRenderer.cs +++ b/DiffPlex/Renderer/UnidiffRenderer.cs @@ -6,20 +6,20 @@ using DiffPlex.Chunkers; using DiffPlex.Model; -namespace DiffPlex.Renderer +namespace DiffPlex.Renderer; + +/// +/// Renderer for generating unified diff (unidiff) format output from diff results +/// +public class UnidiffRenderer { + private readonly IDiffer differ; + private readonly int contextLines; + /// - /// Renderer for generating unified diff (unidiff) format output from diff results + /// Gets the default singleton instance of the unidiff renderer. /// - public class UnidiffRenderer - { - private readonly IDiffer differ; - private readonly int contextLines; - - /// - /// Gets the default singleton instance of the unidiff renderer. - /// - public static UnidiffRenderer Instance { get; } = new UnidiffRenderer(); + public static UnidiffRenderer Instance { get; } = new(); /// /// Initializes a new instance of the class. @@ -302,12 +302,11 @@ private class DiffLine private class DiffHunk { - public int OldStartLine { get; set; } - public int OldLength { get; set; } - public int NewStartLine { get; set; } - public int NewLength { get; set; } - public List Lines { get; } = new List(); - } - #endregion + public int OldStartLine { get; set; } + public int OldLength { get; set; } + public int NewStartLine { get; set; } + public int NewLength { get; set; } + public List Lines { get; } = []; } + #endregion } \ No newline at end of file diff --git a/DiffPlex/ThreeWayDiffer.cs b/DiffPlex/ThreeWayDiffer.cs index 9fbf98c6..b8f4169c 100644 --- a/DiffPlex/ThreeWayDiffer.cs +++ b/DiffPlex/ThreeWayDiffer.cs @@ -3,51 +3,51 @@ using System.Linq; using DiffPlex.Model; -namespace DiffPlex +namespace DiffPlex; + +public class ThreeWayDiffer : IThreeWayDiffer { - public class ThreeWayDiffer : IThreeWayDiffer - { - /// - /// Gets the default singleton instance of three-way differ. - /// - public static ThreeWayDiffer Instance { get; } = new ThreeWayDiffer(); + /// + /// Gets the default singleton instance of three-way differ. + /// + public static ThreeWayDiffer Instance { get; } = new(); - private readonly IDiffer _differ = Differ.Instance; + private readonly IDiffer _differ = Differ.Instance; - public ThreeWayDiffResult CreateDiffs(string baseText, string oldText, string newText, - bool ignoreWhiteSpace, bool ignoreCase, IChunker chunker) - { - if (baseText == null) throw new ArgumentNullException(nameof(baseText)); - if (oldText == null) throw new ArgumentNullException(nameof(oldText)); - if (newText == null) throw new ArgumentNullException(nameof(newText)); - if (chunker == null) throw new ArgumentNullException(nameof(chunker)); + public ThreeWayDiffResult CreateDiffs(string baseText, string oldText, string newText, + bool ignoreWhiteSpace, bool ignoreCase, IChunker chunker) + { + if (baseText == null) throw new ArgumentNullException(nameof(baseText)); + if (oldText == null) throw new ArgumentNullException(nameof(oldText)); + if (newText == null) throw new ArgumentNullException(nameof(newText)); + if (chunker == null) throw new ArgumentNullException(nameof(chunker)); - var basePieces = chunker.Chunk(baseText); - var oldPieces = chunker.Chunk(oldText); - var newPieces = chunker.Chunk(newText); + var basePieces = chunker.Chunk(baseText); + var oldPieces = chunker.Chunk(oldText); + var newPieces = chunker.Chunk(newText); - // Create two-way diffs: base->old and base->new - var baseToOld = _differ.CreateDiffs(baseText, oldText, ignoreWhiteSpace, ignoreCase, chunker); - var baseToNew = _differ.CreateDiffs(baseText, newText, ignoreWhiteSpace, ignoreCase, chunker); + // Create two-way diffs: base->old and base->new + var baseToOld = _differ.CreateDiffs(baseText, oldText, ignoreWhiteSpace, ignoreCase, chunker); + var baseToNew = _differ.CreateDiffs(baseText, newText, ignoreWhiteSpace, ignoreCase, chunker); - var threeWayBlocks = CreateThreeWayDiffBlocks(basePieces, oldPieces, newPieces, - baseToOld, baseToNew, ignoreWhiteSpace, ignoreCase); + var threeWayBlocks = CreateThreeWayDiffBlocks(basePieces, oldPieces, newPieces, + baseToOld, baseToNew, ignoreWhiteSpace, ignoreCase); - return new ThreeWayDiffResult(basePieces, oldPieces, newPieces, threeWayBlocks); - } + return new(basePieces, oldPieces, newPieces, threeWayBlocks); + } - public ThreeWayMergeResult CreateMerge(string baseText, string oldText, string newText, - bool ignoreWhiteSpace, bool ignoreCase, IChunker chunker) - { - var diffResult = CreateDiffs(baseText, oldText, newText, ignoreWhiteSpace, ignoreCase, chunker); + public ThreeWayMergeResult CreateMerge(string baseText, string oldText, string newText, + bool ignoreWhiteSpace, bool ignoreCase, IChunker chunker) + { + var diffResult = CreateDiffs(baseText, oldText, newText, ignoreWhiteSpace, ignoreCase, chunker); - var mergedPieces = new List(); - var conflictBlocks = new List(); - var isSuccessful = true; + List mergedPieces = []; + List conflictBlocks = []; + var isSuccessful = true; - var baseIndex = 0; - var oldIndex = 0; - var newIndex = 0; + var baseIndex = 0; + var oldIndex = 0; + var newIndex = 0; foreach (var block in diffResult.DiffBlocks) { @@ -122,16 +122,16 @@ public ThreeWayMergeResult CreateMerge(string baseText, string oldText, string n newIndex += block.NewCount; } - // Add remaining unchanged content - while (baseIndex < diffResult.PiecesBase.Count) - { - mergedPieces.Add(diffResult.PiecesBase[baseIndex]); - baseIndex++; - } - - return new ThreeWayMergeResult(mergedPieces, isSuccessful, conflictBlocks, diffResult); + // Add remaining unchanged content + while (baseIndex < diffResult.PiecesBase.Count) + { + mergedPieces.Add(diffResult.PiecesBase[baseIndex]); + baseIndex++; } + return new(mergedPieces, isSuccessful, conflictBlocks, diffResult); + } + private List CreateThreeWayDiffBlocks(IReadOnlyList basePieces, IReadOnlyList oldPieces, IReadOnlyList newPieces, DiffResult baseToOld, DiffResult baseToNew, bool ignoreWhiteSpace, bool ignoreCase) @@ -149,9 +149,9 @@ private List CreateThreeWayDiffBlocks(IReadOnlyList b return blocks; } - var baseIndex = 0; - var oldIndex = 0; - var newIndex = 0; + var baseIndex = 0; + var oldIndex = 0; + var newIndex = 0; var oldBlockIndex = 0; var newBlockIndex = 0; @@ -295,12 +295,11 @@ public bool Equals(string x, string y) _ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } - public int GetHashCode(string obj) - { - if (obj == null) return 0; - var str = _ignoreWhiteSpace ? obj.Trim() : obj; - return _ignoreCase ? str.ToUpperInvariant().GetHashCode() : str.GetHashCode(); - } + public int GetHashCode(string obj) + { + if (obj == null) return 0; + var str = _ignoreWhiteSpace ? obj.Trim() : obj; + return _ignoreCase ? str.ToUpperInvariant().GetHashCode() : str.GetHashCode(); } } } diff --git a/Directory.Build.props b/Directory.Build.props index 4da81801..e3bb971d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ - latest + 13 diff --git a/Facts.DiffPlex/Facts.DiffPlex.csproj b/Facts.DiffPlex/Facts.DiffPlex.csproj index b80e2798..3399d38b 100644 --- a/Facts.DiffPlex/Facts.DiffPlex.csproj +++ b/Facts.DiffPlex/Facts.DiffPlex.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 diff --git a/Perf.DiffPlex/Perf.DiffPlex.csproj b/Perf.DiffPlex/Perf.DiffPlex.csproj index 0c38c522..327ea620 100644 --- a/Perf.DiffPlex/Perf.DiffPlex.csproj +++ b/Perf.DiffPlex/Perf.DiffPlex.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 diff --git a/WebDiffer/WebDiffer.csproj b/WebDiffer/WebDiffer.csproj index 6f465bbc..875a97ff 100644 --- a/WebDiffer/WebDiffer.csproj +++ b/WebDiffer/WebDiffer.csproj @@ -1,7 +1,7 @@ - net6.0 + net9.0 true