Beispiel #1
0
        private FinessedDifferenceCollection(IDifferenceCollection <string> original, IList <Match> newMatches)
        {
            LeftSequence  = original.LeftSequence;
            RightSequence = original.RightSequence;

            Differences = GenerateDifferences(LeftSequence.Count, RightSequence.Count, newMatches);
        }
Beispiel #2
0
        private void ConstructChanges()
        {
            IDifferenceCollection <SnapshotSpan> diffs = differ.GetDifferences();

            List <TextChange> changes = new List <TextChange>();
            int pos = this.textPosition;

            // each difference generates a text change
            foreach (Difference diff in diffs)
            {
                pos += GetMatchSize(differ.DeletedSpans, diff.Before);
                TextChange change = TextChange.Create(pos,
                                                      BufferFactoryService.StringRebuilderFromSnapshotSpans(differ.DeletedSpans, diff.Left),
                                                      BufferFactoryService.StringRebuilderFromSnapshotSpans(differ.InsertedSpans, diff.Right),
                                                      this.currentSnapshot);
                changes.Add(change);
                pos += change.OldLength;
            }
            this.normalizedChanges = NormalizedTextChangeCollection.Create(changes);
        }
        public IDifferenceCollection <SnapshotSpan> GetDifferences()
        {
            if (this.differences == null)
            {
                DecomposeSpans();

                var deletedSpans  = new List <SnapshotSpan>();
                var insertedSpans = new List <SnapshotSpan>();

                for (int s = 0; s < deletedSurrogates.Length; ++s)
                {
                    deletedSpans.AddRange(deletedSurrogates[s]);
                }

                for (int s = 0; s < insertedSurrogates.Length; ++s)
                {
                    insertedSpans.AddRange(insertedSurrogates[s]);
                }

                differences = this.diffService.DifferenceSequences(deletedSpans, insertedSpans);
            }

            return(differences);
        }
Beispiel #4
0
        /// <summary>
        /// Create a new hierarchical difference collection.
        /// </summary>
        /// <param name="differenceCollection">The underlying difference collection for this level
        /// of the hierarchy.</param>
        /// <param name="differenceService">The difference service to use for doing the next level of
        /// differencing</param>
        /// <param name="options">The options to use for the next level of differencing.
        /// If <see cref="StringDifferenceOptions.DifferenceType" /> is <c>0</c>, then
        /// no further differencing will take place.</param>
        public HierarchicalDifferenceCollection(IDifferenceCollection <string> differenceCollection,
                                                ITokenizedStringListInternal left,
                                                ITokenizedStringListInternal right,
                                                ITextDifferencingService differenceService,
                                                StringDifferenceOptions options)
        {
            if (differenceCollection == null)
            {
                throw new ArgumentNullException(nameof(differenceCollection));
            }
            if (left == null)
            {
                throw new ArgumentNullException(nameof(left));
            }
            if (right == null)
            {
                throw new ArgumentNullException(nameof(right));
            }
            if (!object.ReferenceEquals(left, differenceCollection.LeftSequence))
            {
                throw new ArgumentException("left must equal differenceCollection.LeftSequence");
            }
            if (!object.ReferenceEquals(right, differenceCollection.RightSequence))
            {
                throw new ArgumentException("right must equal differenceCollection.RightSequence");
            }

            this.left  = left;
            this.right = right;

            this.differenceCollection = differenceCollection;
            this.differenceService    = differenceService;
            this.options = options;

            containedDifferences = new ConcurrentDictionary <int, IHierarchicalDifferenceCollection>();
        }
Beispiel #5
0
        /// <summary>
        /// Given a set of line difference, finesse the results to produce something likely closer to what the user
        /// actually changed.  This means:
        /// 1) Transpose diffs forward and backward:
        ///  a)If the user has inserted a new method, say, the diff tends to be off a line or two, e.g. the first few
        ///    lines of the new doc comment may show up as matching the first few lines of the next method's doc comment, or
        ///    vice versa.  This slides the diff around until it appears to match up with probable "blocks" in the source file.
        ///
        ///  b)If the user has ignore (trim) whitespace on, the computed, greedy "match" may be worse than an equally
        ///    correct match with the diff transposed backward.  For example, if you have something like:
        ///
        ///    1|  {
        ///    2|    {
        ///    3|      foo ()
        ///    4|      {
        ///    5|      }
        ///    6|      bar ()
        ///    7|      {
        ///    8|      }
        ///    9|    }
        ///    0|  }
        ///
        ///    ...and you delete the two methods, the ignore (trim) whitespace diff may be greedy about matching the
        ///    close braces.  Instead of showing lines 3-8 as being deleted, it may show lines 3-4, 6-7, and 8-9 being
        ///    deleted, matching with the close brackets on lines 5 and 7.  Even worse, since matches are chosen from
        ///    the right (after) file, the "matches" on lines 5 and 7 will be at the indent level of 9 and 10, shown
        ///    in the middle of the lines 3-8 block that is all indented at the same level.
        /// </summary>
        public static IDifferenceCollection <string> FinesseLineDifferences(IDifferenceCollection <string> lineDifferences, IList <string> leftLines, IList <string> rightLines)
        {
            if (lineDifferences.Differences.Count == 0)
            {
                return(lineDifferences);
            }

            // Assume there will be approximately the same number of matches after we're done.
            List <Match> matches = new List <Match>(lineDifferences.Differences.Count + 1);

            int lastDiffOffset = 0;

            for (int i = 0; i < lineDifferences.Differences.Count; i++)
            {
                var diff = lineDifferences.Differences[i];

                Match before = diff.Before;
                if (lastDiffOffset != 0 && before != null)
                {
                    before = new Match(MoveStartPoint(before.Left, lastDiffOffset),
                                       MoveStartPoint(before.Right, lastDiffOffset));
                }

                Match after          = diff.After;
                int   thisDiffOffset = 0;

                // #1a) Try to transpose the diff forward
                while (after != null && after.Length > 0)
                {
                    // See how far we can transpose this diff forward (towards the end of the file)
                    int offset = TransposeDiffForward(after, diff, lineDifferences.LeftSequence, lineDifferences.RightSequence, leftLines, rightLines);

                    // If we didn't move it at all, we're done.
                    if (offset == 0)
                    {
                        break;
                    }

                    thisDiffOffset += offset;

                    // Offset our after match by the amount the diff chunk moved forwards
                    after = MoveStartPoint(after, offset);

                    // If the diff chunk just ate the entire match sequence, then combine this
                    // diff with the next diff, and use the next diff's After as our new after match.
                    if (after.Length == 0)
                    {
                        if (i < lineDifferences.Differences.Count - 1)
                        {
                            i++;
                            after = lineDifferences.Differences[i].After ?? new Match(new Span(leftLines.Count, 0), new Span(rightLines.Count, 0));

                            // The offset (for the next round) resets, since we're moving on to the next diff here.
                            thisDiffOffset = 0;
                        }
                        else
                        {
                            after = new Match(new Span(leftLines.Count, 0), new Span(rightLines.Count, 0));
                        }
                    }

                    // If we didn't have a "before" before, we have one now.
                    if (before == null)
                    {
                        before = new Match(new Span(0, 0), new Span(0, 0));
                    }

                    // Grow the before match
                    before = MoveEndPoint(before, offset);

                    // Update the current diff to include the lines it ate from the after match (and
                    // possibly the next diff chunk as well, if we detected that the entire match
                    // was eaten above).
                    diff = new Difference(Span.FromBounds(before.Left.End, after.Left.Start),
                                          Span.FromBounds(before.Right.End, after.Right.Start),
                                          MaybeMatch(before),
                                          MaybeMatch(after));
                }

                // #1b) Try to transpose the diff backward.
                while (before != null && before.Length > 0)
                {
                    // See how far we can transpose this diff backwards (towards the start of the file)
                    int offset = TransposeDiffBackward(before, diff, lineDifferences.LeftSequence, lineDifferences.RightSequence, leftLines, rightLines);

                    // If we didn't move it at all, we're done.
                    if (offset == 0)
                    {
                        break;
                    }

                    thisDiffOffset += offset;

                    // Offset our before match by the amount the diff chunk moved backwards
                    before = MoveEndPoint(before, offset);

                    // If the diff chunk just ate the entire match sequence, then skip our before Match
                    // back to the previous Match.
                    if (before.Length == 0)
                    {
                        if (matches.Count > 0)
                        {
                            before = matches[matches.Count - 1];
                            matches.RemoveAt(matches.Count - 1);
                        }
                        else
                        {
                            before = new Match(new Span(0, 0), new Span(0, 0));
                        }
                    }

                    // If we didn't have an "after" before, we have one now.
                    if (after == null)
                    {
                        after = new Match(new Span(leftLines.Count, 0), new Span(rightLines.Count, 0));
                    }

                    // Grow the after match
                    after = MoveStartPoint(after, offset);

                    // Update the current diff to include the lines it ate from the before match (and
                    // possibly the previous diff chunk as well, if we detected that the entire match
                    // was eaten above).
                    diff = new Difference(Span.FromBounds(before.Left.End, after.Left.Start),
                                          Span.FromBounds(before.Right.End, after.Right.Start),
                                          MaybeMatch(before),
                                          MaybeMatch(after));
                }

                if (before != null && before.Length > 0)
                {
                    matches.Add(before);
                }

                if (i == lineDifferences.Differences.Count - 1 &&
                    after != null && after.Length > 0)
                {
                    matches.Add(after);
                }

                lastDiffOffset = thisDiffOffset;
            }

            return(new FinessedDifferenceCollection(lineDifferences, matches));
        }
Beispiel #6
0
 /// <summary>
 /// Initializes a new instance of <see cref="ProjectionSpanDifference"/>.
 /// </summary>
 /// <param name="differenceCollection">The collection of snapshot spans that include the differences.</param>
 /// <param name="insertedSpans">A read-only collection of the inserted snapshot spans.</param>
 /// <param name="deletedSpans">A read-only collection of the deleted snapshot spans.</param>
 public ProjectionSpanDifference(IDifferenceCollection <SnapshotSpan> differenceCollection, ReadOnlyCollection <SnapshotSpan> insertedSpans, ReadOnlyCollection <SnapshotSpan> deletedSpans)
 {
     DifferenceCollection = differenceCollection;
     InsertedSpans        = insertedSpans;
     DeletedSpans         = deletedSpans;
 }