public IEnumerable<DiffMatchPatch.Patch> MakePatch(string contentOne, string contentTwo, CompareOptions options) { var diff = new my.utils.Diff(); var changes = diff.DiffText(contentOne, contentTwo); var contentOneLines = contentOne.Split(new[] { Environment.NewLine }, StringSplitOptions.None); var contentTwoLines = contentTwo.Split(new[] { Environment.NewLine }, StringSplitOptions.None); var chunks = new HashSet<Chunk>(); Chunk currentChunk = null; for (var i = 0; i < changes.Length; i++) { var change = changes[i]; Diff.Item? nextChange = null; if (changes.Length > i + 1) nextChange = changes.ElementAtOrDefault(i + 1); var continuation = currentChunk != null; if (!continuation) { currentChunk = CreateChunk(change, options); chunks.Add(currentChunk); } if (change.StartA != 0 && !continuation) { // no start context needed currentChunk.start1 = change.StartA - options.ContextSize; currentChunk.start2 = change.StartB - options.ContextSize; // stick some context in var start = change.StartB - options.ContextSize; for (var j = start; j < change.StartB; j++) { currentChunk.diffs.Add(new Line(Operation.EQUAL, contentTwoLines[j])); } } if (change.deletedA > 0) { for (var j = 0; j < change.deletedA; j++) { var line = contentOneLines[j + change.StartA]; currentChunk.diffs.Add(new Line(Operation.DELETE, line)); } } if (change.insertedB > 0) { for (var j = 0; j < change.insertedB; j++) { var line = contentTwoLines[j + change.StartB]; currentChunk.diffs.Add(new Line(Operation.INSERT, line)); } } var start2 = change.StartB + change.insertedB; int end; if (nextChange.HasValue) end = Min(start2 + options.ContextSize, contentTwoLines.Length, nextChange.Value.StartB); else end = Min(start2 + options.ContextSize, contentTwoLines.Length); for (var j = start2; j < end; j++) { currentChunk.diffs.Add(new Line(Operation.EQUAL, contentTwoLines[j])); } if (nextChange.HasValue && nextChange.Value.StartB - end > 0) { // need to split the diff into multiple chunks currentChunk = null; } } foreach (var chunk in chunks) { chunk.length1 = chunk.diffs.Count(x => x.operation == Operation.EQUAL || x.operation == Operation.DELETE); chunk.length2 = chunk.diffs.Count(x => x.operation == Operation.EQUAL || x.operation == Operation.INSERT); } return chunks; }
public static Diff Compare(string fileOnePath, string fileOneContent, string fileTwoPath, string fileTwoContent, CompareOptions options) { if (fileOneContent == null && fileTwoContent == null) throw new InvalidOperationException("Both files were null"); if (options.BomMode == BomMode.Ignore) { if (fileOneContent != null) fileOneContent = RemoveBom(fileOneContent); if (fileTwoContent != null) fileTwoContent = RemoveBom(fileTwoContent); } if (fileTwoContent == null) return DeletedFileDiff(fileOneContent, fileOnePath); if (fileOneContent == null) return NewFileDiff(fileTwoContent, fileTwoPath); if (IsBinary(fileOneContent)) throw new BinaryFileException(fileOnePath); if (IsBinary(fileTwoContent)) throw new BinaryFileException(fileTwoPath); var patchMaker = new PatchMaker(); var patches = patchMaker.MakePatch(fileOneContent, fileTwoContent, options); var chunks = new List<Chunk>(); foreach (var patch in patches) { var originalRange = new ChangeRange(patch.start1 + 1, patch.length1); var newRange = new ChangeRange(patch.start2 + 1, patch.length2); var range = new ChunkRange(originalRange, newRange); var snippets = new List<ISnippet>(); var lines = new List<DiffMatchPatch.Diff>(); Operation? previousOperation = null; var isModification = false; foreach (var diff in patch.diffs) { if (previousOperation == null) previousOperation = diff.operation; if (previousOperation == Operation.DELETE && diff.operation == Operation.INSERT) isModification = true; else if (previousOperation != diff.operation) { // different operation if (previousOperation == Operation.EQUAL) snippets.Add(new ContextSnippet(lines.Select(x => new ContextLine(x.text)).Cast<ILine>())); else if (isModification) snippets.Add(new ModificationSnippet( lines .Where(x => x.operation == Operation.DELETE) .Select(x => new SubtractionLine(x.text)) .Cast<ILine>(), lines .Where(x => x.operation == Operation.INSERT) .Select(x => new AdditionLine(x.text)) .Cast<ILine>() )); else if (previousOperation == Operation.INSERT) snippets.Add(new AdditionSnippet(lines.Select(x => new AdditionLine(x.text)).Cast<ILine>())); else if (previousOperation == Operation.DELETE) snippets.Add(new SubtractionSnippet(lines.Select(x => new SubtractionLine(x.text)).Cast<ILine>())); lines.Clear(); isModification = false; } lines.Add(diff); previousOperation = diff.operation; } if (lines.Count > 0) { if (previousOperation == Operation.INSERT) snippets.Add(new AdditionSnippet(lines.Select(x => new AdditionLine(x.text)).Cast<ILine>())); else if (previousOperation == Operation.DELETE) snippets.Add(new SubtractionSnippet(lines.Select(x => new SubtractionLine(x.text)).Cast<ILine>())); else snippets.Add(new ContextSnippet(lines.Select(x => new ContextLine(x.text)).Cast<ILine>())); } chunks.Add(new Chunk(range, snippets)); } var header = new Header(new FormatType("generated"), new[] { new File('a', fileOnePath), new File('b', fileTwoPath) }); return new Diff(header, chunks); }
private Chunk CreateChunk(Diff.Item change, CompareOptions options) { var chunk = new Chunk(); chunk.start1 = change.StartA; chunk.start2 = change.StartB; chunk.length1 = change.deletedA + options.ContextSize; chunk.length2 = change.insertedB + options.ContextSize; chunk.diffs = new List<Line>(); return chunk; }