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]); }
/// <summary> /// Shows the schema. /// </summary> private void ShowSchema() { string fileText = string.Empty; string databaseText = string.Empty; if (this.fileSide != null) { fileText = this.GetFile(this.fileSide); } if (this.databaseSide != null) { databaseText = this.GetDBSchema(this.databaseSide); } if (this.checkBoxDiff.Active) { StyleTextLineMarker addMarker = new StyleTextLineMarker() { BackgroundColor = new Cairo.Color(0.8, 0.8, 1) }; StyleTextLineMarker deleteMarker = new StyleTextLineMarker() { BackgroundColor = new Cairo.Color(1, 0.8, 0.8) }; StyleTextLineMarker modifyMarker = new StyleTextLineMarker() { BackgroundColor = new Cairo.Color(1, 1, 0.6) }; StyleTextLineMarker grayMarker = new StyleTextLineMarker() { BackgroundColor = new Cairo.Color(0.6, 0.6, 0.6) }; DiffResult result = new DiffPlex.Differ().CreateLineDiffs(databaseText, fileText, false); Dictionary<int, TextLineMarker> databaseMarker = new Dictionary<int, TextLineMarker>(); Dictionary<int, TextLineMarker> fileMarker = new Dictionary<int, TextLineMarker>(); string[] fileTextArray = fileText.Split('\n'); string[] databaseTextArray = databaseText.Split('\n'); List<string> processedFile = new List<string>(); List<string> processedDatabase = new List<string>(); int i1 = 0; // original database line number, start from 0 int i2 = 0; // original file line number, start from 0 int d1 = -1; int d2 = -1; DiffBlock currentBlock = new DiffBlock(-1, 0, -1, 0); if (result.DiffBlocks.Count > 0) { currentBlock = result.DiffBlocks[0]; result.DiffBlocks.RemoveAt(0); } for (int i = 0; true; i++) { bool change = false; if (i1 == currentBlock.DeleteStartA) { d1 = 0; } if (d1 != -1) { change = true; if (d1 < currentBlock.DeleteCountA) { processedDatabase.Add(databaseTextArray[i1]); databaseMarker.Add(i + 1, deleteMarker); processedFile.Add(string.Empty); fileMarker.Add(i + 1, deleteMarker); i1++; d1++; } else { d1 = -1; } } if (i2 == currentBlock.InsertStartB) { d2 = 0; } if (d2 != -1) { change = true; if (d2 < currentBlock.InsertCountB) { processedDatabase.Add(string.Empty); if (databaseMarker.ContainsKey(i + 1)) { databaseMarker[i + 1] = modifyMarker; } else { databaseMarker.Add(i + 1, addMarker); } processedFile.Add(fileTextArray[i2]); if (fileMarker.ContainsKey(i + 1)) { fileMarker[i + 1] = modifyMarker; } else { fileMarker.Add(i + 1, addMarker); } i2++; d2++; } else { d2 = -1; } } // Stop the iteration if we are on the last line now if (i1 >= databaseTextArray.Length && i2 >= fileTextArray.Length) { break; } if (d1 == -1 && d2 == -1 && change) { if (result.DiffBlocks.Count > 0) { currentBlock = result.DiffBlocks[0]; result.DiffBlocks.RemoveAt(0); } else { currentBlock = new DiffBlock(-1, 0, -1, 0); } change = false; } // No changes, insert the original text without marker if (!change) { processedDatabase.Add(databaseTextArray[i1]); processedFile.Add(fileTextArray[i2]); i1++; i2++; } } this.docDB.Text = string.Join("\n", processedDatabase.ToArray()); this.docLocal.Text = string.Join("\n", processedFile.ToArray()); foreach (KeyValuePair<int, TextLineMarker> single in databaseMarker) { this.docDB.AddMarker(single.Key, single.Value); } foreach (KeyValuePair<int, TextLineMarker> single in fileMarker) { this.docLocal.AddMarker(single.Key, single.Value); } this.docDB.CommitUpdateAll(); } else { this.docDB.Text = databaseText; this.docLocal.Text = fileText; } }