private static IHierarchicalDifferenceCollection ComputeDiffSpans(ITextDifferencingService diffService, TextDocument left, TextDocument right, CancellationToken cancellationToken) { // TODO: it would be nice to have a syntax based differ for presentation here, // current way of just using text differ has its own issue, and using syntax differ in compiler that are for incremental parser // has its own drawbacks. var oldText = left.GetTextAsync(cancellationToken).WaitAndGetResult(cancellationToken); var newText = right.GetTextAsync(cancellationToken).WaitAndGetResult(cancellationToken); var oldString = oldText.ToString(); var newString = newText.ToString(); // first try, cheapest way. var diffResult = diffService.DiffStrings(oldString, newString, new StringDifferenceOptions() { DifferenceType = StringDifferenceTypes.Line | StringDifferenceTypes.Word, WordSplitBehavior = WordSplitBehavior.WhiteSpaceAndPunctuation }); if (!ContainsBetterDiff(left, right, diffResult, cancellationToken)) { return(diffResult); } // second, try a bit more expansive way return(diffService.DiffStrings(oldString, newString, new StringDifferenceOptions() { DifferenceType = StringDifferenceTypes.Word, WordSplitBehavior = WordSplitBehavior.WhiteSpaceAndPunctuation })); }
private static IHierarchicalDifferenceCollection ComputeDiffSpans( ITextDifferencingService diffService, TextDocument left, TextDocument right, CancellationToken cancellationToken ) { // TODO: it would be nice to have a syntax based differ for presentation here, // current way of just using text differ has its own issue, and using syntax differ in compiler that are for incremental parser // has its own drawbacks. var oldText = left.GetTextSynchronously(cancellationToken); var newText = right.GetTextSynchronously(cancellationToken); var oldString = oldText.ToString(); var newString = newText.ToString(); return(diffService.DiffStrings( oldString, newString, new StringDifferenceOptions() { DifferenceType = StringDifferenceTypes.Line, } )); }
public static IHierarchicalDifferenceCollection ComputeDiffSpans(SourceText oldText, SourceText newText, ITextDifferencingService diffService, CancellationToken cancellationToken) { var diffResult = diffService.DiffStrings(oldText.ToString(), newText.ToString(), new StringDifferenceOptions() { DifferenceType = StringDifferenceTypes.Line }); return(diffResult); }
public IHierarchicalDifferenceCollection GetDifferences(IContentType contentType, string left, string right) { Initialize(); ITextDifferencingService diffService = this.diffSelectorService.GetTextDifferencingService(contentType); StringDifferenceOptions diffOptions = new StringDifferenceOptions(StringDifferenceTypes.Line, 0, true); IHierarchicalDifferenceCollection differences = diffService.DiffStrings(left, right, diffOptions); return(differences); }
//[Import] //ITextBufferFactoryService textBufferFactoryService = null; //[Import] //IDifferenceBufferFactoryService differenceBufferFactoryService = null; public HashSet <Tuple <int, int> > PerformDiffFiles(string file1, string file2) { HashSet <Tuple <int, int> > diffRanges = new HashSet <Tuple <int, int> >(); StringDifferenceOptions sdo = new StringDifferenceOptions() { DifferenceType = StringDifferenceTypes.Line | StringDifferenceTypes.Word, IgnoreTrimWhiteSpace = false }; var origContent = (new StreamReader(file1)).ReadToEnd(); var modifiedContent = (new StreamReader(file2)).ReadToEnd(); var diffs = differenceService.DiffStrings(origContent, modifiedContent, sdo); var diffList = new List <CCDifference>(); foreach (var d in diffs) { diffList.Add(new CCDifference(d.Left, d.Right, d.Before, d.After)); diffRanges.Add(Tuple.Create <int, int>(d.Left.Start, d.Left.End)); } return(diffRanges); }
/// <summary> /// Calculate the contained difference at the given index. Used by the concurrent dictionary's /// GetOrAdd that takes a value factory. /// </summary> private IHierarchicalDifferenceCollection CalculateContainedDiff(int index) { // We need to compute the next level of differences. var diff = this.Differences[index]; if (diff.DifferenceType == DifferenceType.Change) { Span leftSpan = this.left.GetSpanInOriginal(diff.Left); Span rightSpan = this.right.GetSpanInOriginal(diff.Right); string leftString = this.left.OriginalSubstring(leftSpan.Start, leftSpan.Length); string rightString = this.right.OriginalSubstring(rightSpan.Start, rightSpan.Length); return(differenceService.DiffStrings(leftString, rightString, options)); } return(null); }
/// <summary> /// Normalize a sequence of changes that were all applied consecutively to the same version of a buffer. Positions of the /// normalized changes are adjusted to account for other changes that occur at lower indexes in the /// buffer, and changes are sorted and merged if possible. /// </summary> /// <param name="changes">The changes to normalize.</param> /// <param name="differenceOptions">The options to use for minimal differencing, if any.</param> /// <param name="before">Text snapshot before the change (can be null).</param> /// <param name="after">Text snapshot after the change (can be null).</param> /// <returns>A (possibly empty) list of changes, sorted by Position, with adjacent and overlapping changes combined /// where possible.</returns> /// <exception cref="ArgumentNullException"><paramref name="changes"/> is null.</exception> private static IList <ITextChange> Normalize(IList <TextChange> changes, StringDifferenceOptions?differenceOptions, ITextDifferencingService textDifferencingService, ITextSnapshot before, ITextSnapshot after) { if (changes.Count == 1 && differenceOptions == null) { // By far the most common path // If we are computing minimal changes, we need to go through the // algorithm anyway, since this change may be split up into many // smaller changes FrugalList <ITextChange> singleResult = new FrugalList <ITextChange>(); singleResult.Add(changes[0]); return(singleResult); } else if (changes.Count == 0) { return(new FrugalList <ITextChange>()); } TextChange[] work = TextUtilities.StableSort(changes, TextChange.Compare); // work is now sorted by increasing Position int accumulatedDelta = 0; int a = 0; int b = 1; while (b < work.Length) { // examine a pair of changes and attempt to combine them TextChange aChange = work[a]; TextChange bChange = work[b]; int gap = bChange.OldPosition - aChange.OldEnd; if (gap > 0) { // independent changes aChange.NewPosition = aChange.OldPosition + accumulatedDelta; accumulatedDelta += aChange.Delta; a = b; b = a + 1; } else { // dependent changes. merge all adjacent dependent changes into a single change in one pass, // to avoid expensive pairwise concatenations. // // Use StringRebuilders (which allow strings to be concatenated without creating copies of the strings) to assemble the // pieces and then convert the rebuilders to a ReferenceChangeString (which wraps a StringRebuilder) at the end. StringRebuilder newRebuilder = aChange._newText.Content; StringRebuilder oldRebuilder = aChange._oldText.Content; int aChangeIncrementalDeletions = 0; do { newRebuilder = newRebuilder.Append(bChange._newText.Content); if (gap == 0) { // abutting deletions oldRebuilder = oldRebuilder.Append(bChange._oldText.Content); aChangeIncrementalDeletions += bChange.OldLength; aChange.LineBreakBoundaryConditions = (aChange.LineBreakBoundaryConditions & LineBreakBoundaryConditions.PrecedingReturn) | (bChange.LineBreakBoundaryConditions & LineBreakBoundaryConditions.SucceedingNewline); } else { // overlapping deletions if (aChange.OldEnd + aChangeIncrementalDeletions < bChange.OldEnd) { int overlap = aChange.OldEnd + aChangeIncrementalDeletions - bChange.OldPosition; oldRebuilder = oldRebuilder.Append(bChange._oldText.Content.Substring(Span.FromBounds(overlap, bChange._oldText.Length))); aChangeIncrementalDeletions += (bChange.OldLength - overlap); aChange.LineBreakBoundaryConditions = (aChange.LineBreakBoundaryConditions & LineBreakBoundaryConditions.PrecedingReturn) | (bChange.LineBreakBoundaryConditions & LineBreakBoundaryConditions.SucceedingNewline); } // else bChange deletion subsumed by aChange deletion } work[b] = null; b++; if (b == work.Length) { break; } bChange = work[b]; gap = bChange.OldPosition - aChange.OldEnd - aChangeIncrementalDeletions; } while (gap <= 0); work[a]._oldText = ReferenceChangeString.CreateChangeString(oldRebuilder); work[a]._newText = ReferenceChangeString.CreateChangeString(newRebuilder); if (b < work.Length) { aChange.NewPosition = aChange.OldPosition + accumulatedDelta; accumulatedDelta += aChange.Delta; a = b; b = a + 1; } } } // a points to the last surviving change work[a].NewPosition = work[a].OldPosition + accumulatedDelta; List <ITextChange> result = new List <ITextChange>(); if (differenceOptions.HasValue) { if (textDifferencingService == null) { throw new ArgumentNullException("stringDifferenceUtility"); } foreach (TextChange change in work) { if (change == null) { continue; } // Make sure this is a replacement if (change.OldLength == 0 || change.NewLength == 0) { result.Add(change); continue; } if (change.OldLength >= TextModelOptions.DiffSizeThreshold || change.NewLength >= TextModelOptions.DiffSizeThreshold) { change.IsOpaque = true; result.Add(change); continue; // too big to even attempt a diff. This is aimed at the reload-a-giant-file scenario // where OOM during diff is a distinct possibility. } // Make sure to turn off IgnoreTrimWhiteSpace, since that doesn't make sense in // the context of a minimal edit StringDifferenceOptions options = new StringDifferenceOptions(differenceOptions.Value); options.IgnoreTrimWhiteSpace = false; IHierarchicalDifferenceCollection diffs; if (before != null && after != null) { // Don't materialize the strings when we know the before and after snapshots. They might be really huge and cause OOM. // We will take this path in the file reload case. diffs = textDifferencingService.DiffSnapshotSpans(new SnapshotSpan(before, change.OldSpan), new SnapshotSpan(after, change.NewSpan), options); } else { // We need to evaluate the old and new text for the differencing service string oldText = change.OldText; string newText = change.NewText; if (oldText == newText) { // This change simply evaporates. This case occurs frequently in Venus and it is much // better to short circuit it here than to fire up the differencing engine. continue; } diffs = textDifferencingService.DiffStrings(oldText, newText, options); } // Keep track of deltas for the "new" position, for sanity check int delta = 0; // Add all the changes from the difference collection result.AddRange(GetChangesFromDifferenceCollection(ref delta, change, change._oldText, change._newText, diffs)); // Sanity check // If delta != 0, then we've constructed asymmetrical insertions and // deletions, which should be impossible Debug.Assert(delta == change.Delta, "Minimal edit delta should be equal to replaced text change's delta."); } } // If we aren't computing minimal changes, then copy over the non-null changes else { foreach (TextChange change in work) { if (change != null) { result.Add(change); } } } return(result); }
private static IHierarchicalDifferenceCollection ComputeDiffSpans(ITextDifferencingService diffService, TextDocument left, TextDocument right, CancellationToken cancellationToken) { // TODO: it would be nice to have a syntax based differ for presentation here, // current way of just using text differ has its own issue, and using syntax differ in compiler that are for incremental parser // has its own drawbacks. var oldText = left.GetTextAsync(cancellationToken).WaitAndGetResult(cancellationToken); var newText = right.GetTextAsync(cancellationToken).WaitAndGetResult(cancellationToken); var oldString = oldText.ToString(); var newString = newText.ToString(); return diffService.DiffStrings(oldString, newString, new StringDifferenceOptions() { DifferenceType = StringDifferenceTypes.Line, }); }
public static IHierarchicalDifferenceCollection ComputeDiffSpans(SourceText oldText, SourceText newText, ITextDifferencingService diffService, CancellationToken cancellationToken) { var diffResult = diffService.DiffStrings(oldText.ToString(), newText.ToString(), new StringDifferenceOptions() { DifferenceType = StringDifferenceTypes.Line }); return diffResult; }