private static void ProcessUserChange(FileInformation<string> fileInfo, VisualDiffOutput output) { string[] newlines = Common.Utility.StandardizeLineBreaks(fileInfo.UserFile.GetContents(), Common.Utility.LineBreaks.Unix).Split('\n'); ProcessSingleChange(fileInfo, output, newlines, ChangeType.User); }
public void Line_Is_In_Conflict() { VisualDiffOutput vdo = new VisualDiffOutput(); vdo.AddLine("line 1", Algorithm.Diff.ChangeType.User, true); vdo.AddLine("line 2", Algorithm.Diff.ChangeType.Template, true); vdo.AddLine("line 3", Algorithm.Diff.ChangeType.None); vdo.ConflictRanges.Add(new VisualDiffOutput.ConflictRange(0, 2)); Assert.That(vdo.IsLineInConflict(0), "First line is in conflict."); Assert.That(vdo.IsLineInConflict(1), "Second line is in conflict."); Assert.That(vdo.IsLineInConflict(2), Is.False, "Third line is not a conflict."); }
/// <summary> /// Take a TextFileInformation object, diff it if it hasn't been diffed, and create a VisualDiffOutput object that /// represents the output of the diff in a form that can be displayed to the user. /// </summary> /// <remarks> /// If the Prevgen file does not exist, it will perform a diff, then copy the NewGen file into the PrevGen temporarily, /// so that all changes will show as User changes. /// </remarks> /// <param name="fileInfo">The TextFileInformation object that has the information on the diffed files.</param> /// <returns>A VisualDiffOutput object containing all of the information needed to show the output of the diff to the user.</returns> private VisualDiffOutput ProcessDiff(FileInformation<string> fileInfo) { if (fileInfo == null) throw new ArgumentNullException("fileInfo"); VisualDiffOutput output = new VisualDiffOutput(); bool virtualPrevGen = false; string oldPrevGenFilePath = null; if (fileInfo.CurrentDiffResult.DiffPerformedSuccessfully == false) fileInfo.PerformDiff(); if (fileInfo.PrevGenFile.HasContents == false) { virtualPrevGen = true; oldPrevGenFilePath = fileInfo.PrevGenFile.FilePath; fileInfo.PrevGenFile.ReplaceContents(fileInfo.NewGenFile.GetContents(), true); } output.DiffType = fileInfo.CurrentDiffResult.DiffType; switch (fileInfo.CurrentDiffResult.DiffType) { case TypeOfDiff.ExactCopy: ProcessExactCopy(fileInfo, output); break; case TypeOfDiff.TemplateChangeOnly: ProcessTemplateChange(fileInfo, output); break; case TypeOfDiff.UserChangeOnly: ProcessUserChange(fileInfo, output); break; case TypeOfDiff.UserAndTemplateChange: ProcessUserAndTemplateChange(fileInfo, output); break; case TypeOfDiff.Conflict: case TypeOfDiff.Warning: ProcessUserAndTemplateChange(fileInfo, output); break; } if (virtualPrevGen) { fileInfo.PrevGenFile.FilePath = oldPrevGenFilePath; if (oldPrevGenFilePath != null) { fileInfo.PrevGenFile.ReplaceContents("", false); } } return output; }
private static void ProcessExactCopy(FileInformation<string> fileInfo, VisualDiffOutput output) { { string fileText = fileInfo.NewGenFile.GetContents(); string[] lines = Common.Utility.StandardizeLineBreaks(fileText, Common.Utility.LineBreaks.Unix).Split('\n'); foreach (string line in lines) { output.LeftLines.Add(new DiffLine(line, ChangeType.None)); output.RightLines.Add(new DiffLine(line, ChangeType.None)); } } }
public void Conflict_Range_Moves_Correctly() { VisualDiffOutput vdo = new VisualDiffOutput(); vdo.AddLine("line 0", Algorithm.Diff.ChangeType.None); vdo.AddLine("line 1", Algorithm.Diff.ChangeType.User, true); vdo.AddLine("line 2", Algorithm.Diff.ChangeType.Template, true); vdo.ConflictRanges.Add(new VisualDiffOutput.ConflictRange(1, 3)); Assert.That(vdo.IsLineInConflict(0), Is.False, "First line is not in conflict."); Assert.That(vdo.IsLineInConflict(1), "Second line is in conflict."); Assert.That(vdo.IsLineInConflict(2), "Third line is in conflict."); vdo.RemoveLine(0); Assert.That(vdo.LineCount, Is.EqualTo(2)); Assert.That(vdo.IsLineInConflict(0), "First line is in conflict."); Assert.That(vdo.IsLineInConflict(1), "Second line is in conflict."); }
private static void ProcessUserAndTemplateChange(FileInformation<string> fileInfo, VisualDiffOutput output) { string[] newlines = Common.Utility.StandardizeLineBreaks( fileInfo.NewGenFile.GetContents(), Common.Utility.LineBreaks.Unix) .Split('\n'); ProcessSingleChange(fileInfo, output, newlines, ChangeType.Template); newlines = Common.Utility.StandardizeLineBreaks( fileInfo.UserFile.GetContents(), Common.Utility.LineBreaks.Unix) .Split('\n'); VisualDiffOutput secondOutput = new VisualDiffOutput(); ProcessSingleChange(fileInfo, secondOutput, newlines, ChangeType.User); output.MergeOutput(secondOutput, ChangeType.Template, ChangeType.User); }
private static void ProcessSingleChange(FileInformation<string> fileInfo, VisualDiffOutput output, string[] newlines, ChangeType changeType) { string[] prevGenLines = Common.Utility.StandardizeLineBreaks(fileInfo.PrevGenFile.GetContents(), Common.Utility.LineBreaks.Unix).Split('\n'); Diff diff = new Diff(prevGenLines, newlines, true, true); // loop through parts of the diff foreach (Diff.Hunk hunk in diff) { if (hunk.Same) { for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { output.LeftLines.Add(new DiffLine(newlines[i])); output.RightLines.Add(new DiffLine(newlines[i])); } } else { // If the left and right of the hunk both have lines in the hunk, this is a change. if (hunk.Right.Count != 0 && hunk.Left.Count != 0) { for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { output.RightLines.Add(new DiffLine(newlines[i], changeType)); } for (int i = hunk.Left.Start; i <= hunk.Left.End; i++) { output.LeftLines.Add(new DiffLine(prevGenLines[i], ChangeType.None)); } while (output.LeftLines.Count < output.RightLines.Count) { output.LeftLines.Add(new DiffLine("", changeType, true)); } while (output.RightLines.Count < output.LeftLines.Count) { output.RightLines.Add(new DiffLine("", changeType, true)); } } else if (hunk.Right.Count != 0) // The right hand side has added lines. { for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { output.LeftLines.Add(new DiffLine("", changeType, true)); output.RightLines.Add(new DiffLine(newlines[i], changeType)); } } else // The left hand side has extra lines. { for (int i = hunk.Left.Start; i <= hunk.Left.End; i++) { output.LeftLines.Add(new DiffLine(prevGenLines[i], ChangeType.None)); output.RightLines.Add(new DiffLine("", changeType, true)); } } } } }
/// <summary> /// Forces the control to update the contents of its editors, and display the diff results. /// </summary> internal void Fill() { BusyPopulatingEditors = true; if (fileInformation == null) { editorOriginal.Text = ""; editorResolved.Text = ""; BusyPopulatingEditors = false; return; } if (string.IsNullOrEmpty(fileInformation.CurrentDiffResult.DiffWarningDescription) == false) { WarningText = (string.IsNullOrEmpty(WarningText) ? "" : WarningText + Environment.NewLine) + fileInformation.CurrentDiffResult.DiffWarningDescription; } else panelWarning.Visible = false; editorOriginal.SuspendPainting(); editorResolved.SuspendPainting(); currentDiffInfo = displayAlgorithm.GetDiffOutput(); editorOriginal.Document.Text = ""; editorResolved.Document.Text = ""; if (CurrentDiffInfo.DiffType == TypeOfDiff.Conflict) EnableEditing(true); FillEditors(); editorOriginal.SelectedView.ScrollToDocumentStart(); editorResolved.SelectedView.ScrollToDocumentStart(); displayAlgorithm.AddButtons(); HasUnsavedChanges = false; Refresh(); editorOriginal.ResumePainting(); editorResolved.ResumePainting(); BusyPopulatingEditors = false; }
public VisualDiffOutput ProcessMergeOutput() { if (fileInformation.UserFile.HasContents == false) { throw new InvalidOperationException("Cannot merge: User file has no contents"); } if (fileInformation.NewGenFile.HasContents == false) { throw new InvalidOperationException("Cannot merge: Template file has no contents"); } VisualDiffOutput vdo = new VisualDiffOutput(); string leftText = Common.Utility.StandardizeLineBreaks(fileInformation.NewGenFile.GetContents(), Common.Utility.LineBreaks.Unix); string rightText = Common.Utility.StandardizeLineBreaks(fileInformation.UserFile.GetContents(), Common.Utility.LineBreaks.Unix); StringUtility.RemoveTrailingLineBreaks(ref leftText); StringUtility.RemoveTrailingLineBreaks(ref rightText); leftText = Common.Utility.StandardizeLineBreaks(leftText, Common.Utility.LineBreaks.Unix); rightText = Common.Utility.StandardizeLineBreaks(rightText, Common.Utility.LineBreaks.Unix); string[] leftLines = leftText.Split('\n'); string[] rightLines = rightText.Split('\n'); Diff diff = new Diff(leftLines, rightLines, true, true); foreach (Diff.Hunk h in diff) { if (h.Same) { foreach (string line in h.Original()) { vdo.AddLine(line, ChangeType.None); } continue; } // Is this a conflict? if (h.Left.Count > 0 && h.Right.Count > 0) { int startIndex = vdo.LineCount; foreach (string line in h.Right) { vdo.LeftLines.Add(new DiffLine(line, ChangeType.User)); } foreach (string line in h.Left) { vdo.LeftLines.Add(new DiffLine(line, ChangeType.Template)); } for (int i = 0; i < h.Right.Count; i++) { vdo.RightLines.Add(new DiffLine("", ChangeType.None, true)); } for (int i = 0; i < h.Left.Count; i++) { vdo.RightLines.Add(new DiffLine("", ChangeType.None, true)); } vdo.ConflictRanges.Add(new VisualDiffOutput.ConflictRange(startIndex, vdo.LineCount)); continue; } // Not a conflict. Just add the new lines and put virtual lines on the left. // Only one of these will actually run - we've already handled the case where // both sides of the hunk have lines. foreach (string line in h.Left) { vdo.RightLines.Add(new DiffLine(line, ChangeType.Template)); vdo.LeftLines.Add(new DiffLine("", ChangeType.None, true)); } foreach (string line in h.Right) { vdo.RightLines.Add(new DiffLine(line, ChangeType.User)); vdo.LeftLines.Add(new DiffLine("", ChangeType.None, true)); } } if (vdo.LineCount > 0) { vdo.RemoveLine(vdo.LineCount - 1); } return(vdo); }
public VisualDiffOutput ProcessMergeOutput() { vdo = new VisualDiffOutput(); if (!fileInfo.NewGenFile.HasContents && !fileInfo.UserFile.HasContents && !fileInfo.PrevGenFile.HasContents) { vdo.DiffType = TypeOfDiff.ExactCopy; return vdo; } if (fileInfo.PrevGenFile.HasContents == false) { if (fileInfo.NewGenFile.HasContents == false) { // prevGen and NewGen are missing. Copy everything from user and mark exact copy. AddAllLinesFromList(user, ChangeType.None); return vdo; } if (fileInfo.UserFile.HasContents == false) { // prevGen and user are missing. Copy everything from NewGen and mark exact copy. AddAllLinesFromList(newgen, ChangeType.None); return vdo; } prevgen = ExtractFileToStringList(fileInfo.NewGenFile); } else { if (fileInfo.UserFile.HasContents == false && fileInfo.NewGenFile.HasContents) user = ExtractFileToStringList(fileInfo.PrevGenFile); else if (fileInfo.NewGenFile.HasContents == false && fileInfo.UserFile.HasContents) newgen = ExtractFileToStringList(fileInfo.PrevGenFile); else if (fileInfo.NewGenFile.HasContents == false && fileInfo.UserFile.HasContents == false) { // User and NewGen are missing. Copy everything from prevgen and mark everything missing. AddAllLinesFromList(prevgen, ChangeType.UserAndTemplate); return vdo; } } Queue<Merge.Conflict> conflicts; Queue<ExtendedRange> ranges; // We need to add a blank line to the bottom of each file, or any lines that are added // at the bottom of the new files will be discarded by the diff/merge. I have spent far // too long trying to work out why, this work around works so I'm going with it. If someone // fixes the underlying problem, this should still work fine. We remove the last line of the // merged lines to compensate for this. /* Begin Hack */ prevgen.Add(""); user.Add(""); newgen.Add(""); /* End Hack */ // Do the initial 3 way diff Merge.MergeLists(prevgen, user, newgen, out conflicts, out ranges); while (conflicts.Count + ranges.Count > 0) { // Get the next range or conflict int conflictStart = int.MaxValue, rangeStart = int.MaxValue; if (conflicts.Count > 0) { conflictStart = conflicts.Peek().Ranges[0].Start; } if (ranges.Count > 0) { rangeStart = ranges.Peek().ChangedRange.Start; } // If the next range came first, process it next. Otherwise process the next conflict. if (rangeStart < conflictStart) ProcessRange(ranges.Dequeue()); else ProcessConflict(conflicts.Dequeue()); } // Other half of the above hack. if (vdo.LeftLines[vdo.LeftLines.Count - 1].Text == "" && vdo.RightLines[vdo.RightLines.Count - 1].Text == "") vdo.RemoveLine(vdo.LeftLines.Count - 1); return vdo; }
internal void MergeOutput(VisualDiffOutput otherOutput, ChangeType thisChangeType, ChangeType otherChangeType) { string[] thisTempLines = LinesToStringArray(this.RightLines); string[] otherTempLines = LinesToStringArray(otherOutput.RightLines); this.LeftLines.Clear(); this.RightLines.Clear(); Diff diff = new Diff(thisTempLines, otherTempLines, true, true); // loop through parts of the diff foreach (Diff.Hunk hunk in diff) { int lineMismatch = hunk.Right.Start - hunk.Left.Start; if (hunk.Same) { for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { if (otherOutput.RightLines[i].Change != ChangeType.None) { //Both the user and template have changed to the same thing. Need to display this. leftLines.Add(new DiffLine(otherOutput.LeftLines[i].Text, thisChangeType | otherChangeType, otherOutput.LeftLines[i].IsVirtual)); rightLines.Add(new DiffLine(otherTempLines[i], thisChangeType | otherChangeType)); } else { leftLines.Add(new DiffLine(otherTempLines[i])); rightLines.Add(new DiffLine(otherTempLines[i])); } } } else { // If the left and right of the hunk both have lines, this could be a conflict. if (hunk.Right.Count != 0 && hunk.Left.Count != 0) { ConflictRange conflictRange = new ConflictRange(); conflictRange.StartLineIndex = leftLines.Count; for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { leftLines.Add(new DiffLine(otherTempLines[i], otherChangeType)); rightLines.Add(new DiffLine("", otherChangeType, true)); } for (int i = hunk.Left.Start; i <= hunk.Left.End; i++) { leftLines.Add(new DiffLine(thisTempLines[i], thisChangeType)); rightLines.Add(new DiffLine("", thisChangeType, true)); } conflictRange.EndLineIndex = leftLines.Count; conflictRanges.Add(conflictRange); } else if (hunk.Right.Count != 0) { for (int i = hunk.Right.Start; i <= hunk.Right.End; i++) { leftLines.Add(new DiffLine("", otherChangeType, true)); rightLines.Add(new DiffLine(otherTempLines[i], otherChangeType)); } } else { for (int i = hunk.Left.Start; i <= hunk.Left.End; i++) { leftLines.Add(new DiffLine("", thisChangeType, true)); rightLines.Add(new DiffLine(thisTempLines[i], thisChangeType)); } } } } }
public VisualDiffOutput ProcessMergeOutput() { if (fileInformation.UserFile.HasContents == false) throw new InvalidOperationException("Cannot merge: User file has no contents"); if (fileInformation.NewGenFile.HasContents == false) throw new InvalidOperationException("Cannot merge: Template file has no contents"); VisualDiffOutput vdo = new VisualDiffOutput(); string leftText = Common.Utility.StandardizeLineBreaks(fileInformation.NewGenFile.GetContents(), Common.Utility.LineBreaks.Unix); string rightText = Common.Utility.StandardizeLineBreaks(fileInformation.UserFile.GetContents(), Common.Utility.LineBreaks.Unix); StringUtility.RemoveTrailingLineBreaks(ref leftText); StringUtility.RemoveTrailingLineBreaks(ref rightText); leftText = Common.Utility.StandardizeLineBreaks(leftText, Common.Utility.LineBreaks.Unix); rightText = Common.Utility.StandardizeLineBreaks(rightText, Common.Utility.LineBreaks.Unix); string[] leftLines = leftText.Split('\n'); string[] rightLines = rightText.Split('\n'); Diff diff = new Diff(leftLines, rightLines, true, true); foreach(Diff.Hunk h in diff) { if(h.Same) { foreach(string line in h.Original()) { vdo.AddLine(line, ChangeType.None); } continue; } // Is this a conflict? if(h.Left.Count > 0 && h.Right.Count > 0) { int startIndex = vdo.LineCount; foreach (string line in h.Right) { vdo.LeftLines.Add(new DiffLine(line, ChangeType.User)); } foreach (string line in h.Left) { vdo.LeftLines.Add(new DiffLine(line, ChangeType.Template)); } for (int i = 0; i < h.Right.Count; i++) { vdo.RightLines.Add(new DiffLine("", ChangeType.None, true)); } for (int i = 0; i < h.Left.Count; i++) { vdo.RightLines.Add(new DiffLine("", ChangeType.None, true)); } vdo.ConflictRanges.Add(new VisualDiffOutput.ConflictRange(startIndex, vdo.LineCount)); continue; } // Not a conflict. Just add the new lines and put virtual lines on the left. // Only one of these will actually run - we've already handled the case where // both sides of the hunk have lines. foreach(string line in h.Left) { vdo.RightLines.Add(new DiffLine(line, ChangeType.Template)); vdo.LeftLines.Add(new DiffLine("", ChangeType.None, true)); } foreach (string line in h.Right) { vdo.RightLines.Add(new DiffLine(line, ChangeType.User)); vdo.LeftLines.Add(new DiffLine("", ChangeType.None, true)); } } if(vdo.LineCount > 0) vdo.RemoveLine(vdo.LineCount - 1); return vdo; }
private VisualDiffOutput CreateVisualDiffOutput() { VisualDiffOutput vdo = new VisualDiffOutput(); vdo.AddLine("line 0", Algorithm.Diff.ChangeType.None); vdo.AddLine("line 1", Algorithm.Diff.ChangeType.User, true); vdo.AddLine("line 2", Algorithm.Diff.ChangeType.Template, true); vdo.AddLine("line 3", Algorithm.Diff.ChangeType.None); vdo.ConflictRanges.Add(new VisualDiffOutput.ConflictRange(1, 3)); return vdo; }