public void DisplayDiff(string filename, string fullpath, string author, string old, string updated) { grid.RowDefinitions.Clear(); var rd = new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }; grid.RowDefinitions.Add(rd); // Remove the line number blocks and such. actionsThem is the last item in the XAML int x = grid.Children.IndexOf(message); grid.Children.RemoveRange(x + 1, grid.Children.Count - x); message.Visibility = Visibility.Collapsed; if (filename.EndsWith(".docx") || filename.EndsWith(".doc")) { // Word document. So let the user open the doc in Word message.Visibility = Visibility.Visible; message.Text = "This is a Word document. Please save it to view its contents."; message.Inlines.Add(" You can also "); var fakeUri = new Uri("http://asdf.com"); var link = new Hyperlink(new Run("view the changes")) { NavigateUri = fakeUri }; link.RequestNavigate += (s, e) => CompareInWord(old, updated, filename, Path.GetDirectoryName(fullpath), author); message.Inlines.Add(link); message.Inlines.Add(" in Word."); } else if (SentenceFilter.IsBinary(old) || SentenceFilter.IsBinary(updated)) { message.Visibility = Visibility.Visible; message.Text = "This file is in binary. Please save it to view its contents."; } else { // Compute the diff, break it into blocks if (old == null) old = ""; if (updated == null) updated = ""; var d = new Differ(); var dr = d.CreateLineDiffs(old, updated, false); int curBlock = 0, numBlocks = dr.DiffBlocks.Count; List<LineBlock> lineBlocks = new List<LineBlock>(); for (int i = 0; i <= dr.PiecesOld.Length; i++) { while (curBlock < numBlocks && dr.DiffBlocks[curBlock].DeleteStartA < i) { curBlock++; } if (curBlock < numBlocks && dr.DiffBlocks[curBlock].DeleteStartA == i) { var db = dr.DiffBlocks[curBlock]; if (db.DeleteCountA > 0) { lineBlocks.Add(new LineBlock(Util.ArraySlice(dr.PiecesOld, db.DeleteStartA, db.DeleteCountA), BlockType.ChangeDelete)); } if (db.InsertCountB > 0) { lineBlocks.Add(new LineBlock(Util.ArraySlice(dr.PiecesNew, db.InsertStartB, db.InsertCountB), BlockType.ChangeAdd)); } i += db.DeleteCountA; curBlock++; } if (i < dr.PiecesOld.Length) { lineBlocks.Add(new LineBlock(Util.ArraySlice(dr.PiecesOld, i, 1))); } } // Draw the actual blocks. DrawLineBlocks(lineBlocks); } }
private void ProcessDiff(string original, string myVersion, string newVersion) { // Do a line-by-line diff, marking changed line groups. // Also, find intersecting diffs and mark them as conflicted changes for resolution. var d = new Differ(); var diffs = new DiffResult[2] { d.CreateLineDiffs(original, myVersion ?? "", false), d.CreateLineDiffs(original, newVersion ?? "", false) }; string[][] newPieces = new string[2][]; newPieces[0] = diffs[0].PiecesNew; newPieces[1] = diffs[1].PiecesNew; if (myVersion == "") newPieces[0] = new string[] { "" }; if (newVersion == "") newPieces[1] = new string[] { "" }; var dblocks = new List<Tuple<DiffBlock, int>>(); for (int side = 0; side < 2; side++) { foreach (var block in diffs[side].DiffBlocks) { DiffBlock actualBlock = block; if (diffs[side].PiecesNew.Length == 0 && block.InsertCountB == 0 && block.InsertStartB == 0) { actualBlock = new DiffBlock(block.DeleteStartA, block.DeleteCountA, 0, 1); } dblocks.Add(new Tuple<DiffBlock, int>(actualBlock, side)); } } dblocks.Sort((a, b) => a.Item1.DeleteStartA.CompareTo(b.Item1.DeleteStartA)); content = new List<LineBlock>[2]; for (int i = 0; i < 2; i++) { content[i] = new List<LineBlock>(); } conflictOrigBlocks = new List<LineBlock>(); string[] origLines = diffs[0].PiecesOld; int nextOriginalLine = 0; for (int i = 0; i < dblocks.Count; ) { DiffBlock block = dblocks[i].Item1; int owner = dblocks[i].Item2; // Add unchanged (original) lines. if (block.DeleteStartA > nextOriginalLine) { foreach (var lineBlocks in content) { lineBlocks.Add(new LineBlock(Util.ArraySlice(origLines, nextOriginalLine, block.DeleteStartA - nextOriginalLine))); } nextOriginalLine = block.DeleteStartA; } int rangeStart = block.DeleteStartA; int rangeEnd = rangeStart + block.DeleteCountA; int j = i; // If this change intersects any other changes, then merge them together to form a block. while (j < dblocks.Count && dblocks[j].Item1.DeleteStartA <= rangeEnd) { rangeEnd = Math.Max(rangeEnd, dblocks[j].Item1.DeleteStartA + dblocks[j].Item1.DeleteCountA); j++; } if (j == i + 1) { // A regular change. var oldBlock = new LineBlock(Util.ArraySlice(diffs[owner].PiecesOld, block.DeleteStartA, block.DeleteCountA), BlockType.ChangeDelete); var newBlock = new LineBlock(Util.ArraySlice(newPieces[owner], block.InsertStartB, block.InsertCountB), BlockType.ChangeAdd); if (block.DeleteCountA != 0 && block.InsertCountB != 0) { oldBlock.type = BlockType.Conflict; newBlock.type = BlockType.Conflict; } else if (block.DeleteCountA == 0) { oldBlock.type = BlockType.Blank; } else if (block.InsertCountB == 0) { newBlock.type = BlockType.Blank; } // can't both be empty! ProcessBlockDiff(oldBlock, newBlock); content[owner].Add(newBlock); content[1 - owner].Add(oldBlock); conflictOrigBlocks.Add(owner == 0 ? newBlock : oldBlock); } else { // Create a change block. for (int side = 0; side < 2; side++) { int curOriginalLine = rangeStart; var conflictBlock = new LineBlock(); var origBlock = new LineBlock(); conflictBlock.type = BlockType.Conflict; for (int k = i; k < j; k++) { DiffBlock subBlock = dblocks[k].Item1; if (dblocks[k].Item2 != side) continue; if (subBlock.DeleteStartA > curOriginalLine) { conflictBlock.AddLines(Util.ArraySlice(origLines, curOriginalLine, subBlock.DeleteStartA - curOriginalLine)); } curOriginalLine = subBlock.DeleteStartA + subBlock.DeleteCountA; var oldBlock = new LineBlock(Util.ArraySlice(diffs[side].PiecesOld, subBlock.DeleteStartA, subBlock.DeleteCountA), BlockType.ChangeDelete); var newBlock = new LineBlock(Util.ArraySlice(newPieces[side], subBlock.InsertStartB, subBlock.InsertCountB), BlockType.ChangeAdd); ProcessBlockDiff(oldBlock, newBlock, false); origBlock.Append(oldBlock); conflictBlock.Append(newBlock); } if (rangeEnd > curOriginalLine) { conflictBlock.AddLines(Util.ArraySlice(origLines, curOriginalLine, rangeEnd - curOriginalLine)); } if (conflictBlock.lines.Count == 0) { conflictBlock.type = BlockType.ChangeDelete; } content[side].Add(conflictBlock); if (side == 0) { conflictOrigBlocks.Add(origBlock); } } int last = content[0].Count - 1; if (content[0][last].ToString() == content[1][last].ToString()) { // Not actually a conflict if they're both the same change. if (content[0][last].lines.Count == 0) { // If both are deleted, just show nothing. content[0].RemoveAt(last); content[1].RemoveAt(last); } else { // Make them normal blocks. content[0][last] = new LineBlock(content[0][last].lines.Select(x => x.ToString()).ToArray()); content[1][last] = new LineBlock(content[1][last].lines.Select(x => x.ToString()).ToArray()); } conflictOrigBlocks.RemoveAt(conflictOrigBlocks.Count - 1); } } nextOriginalLine = rangeEnd; i = j; } // Add any remaining unchanged lines. if (origLines.Length > nextOriginalLine) { foreach (var lineBlocks in content) { lineBlocks.Add(new LineBlock(Util.ArraySlice(origLines, nextOriginalLine, origLines.Length - nextOriginalLine))); } } origBlocks = new LineBlock[2][]; origBlocks[0] = new LineBlock[content[0].Count]; origBlocks[1] = new LineBlock[content[1].Count]; content[0].CopyTo(origBlocks[0]); content[1].CopyTo(origBlocks[1]); }
private void ProcessBlockDiff(LineBlock oldLineBlock, LineBlock newLineBlock, bool modifyOld = true) { // Do block-by-block diffs inside LineBlocks. var oldBlocks = new List<Block>(); var newBlocks = new List<Block>(); foreach (var line in oldLineBlock.lines) { oldBlocks.AddRange(line.blocks); } foreach (var line in newLineBlock.lines) { newBlocks.AddRange(line.blocks); } var d = new Differ(); DiffResult diff = d.CreateLineDiffs(String.Join("\n", oldBlocks.Select(x => x.ToString()).ToArray()), String.Join("\n", newBlocks.Select(x => x.ToString()).ToArray()), false); foreach (DiffBlock dblock in diff.DiffBlocks) { if (modifyOld) { for (int i = 0; i < dblock.DeleteCountA && dblock.DeleteStartA + i < oldBlocks.Count; i++) { oldBlocks[i + dblock.DeleteStartA].type = BlockType.ChangeDelete; } } for (int i = 0; i < dblock.InsertCountB && dblock.InsertStartB + i < newBlocks.Count; i++) { newBlocks[i + dblock.InsertStartB].type = BlockType.ChangeAdd; } } }