示例#1
0
        public async Task <IEnumerable <TextChange> > GetTextChangesAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken)
        {
            var oldText = await oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);

            var newText = await newDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);

            var diffService = _differenceSelectorService.GetTextDifferencingService(oldDocument.Project.LanguageServices.GetService <IContentTypeLanguageService>().GetDefaultContentType())
                              ?? _differenceSelectorService.DefaultTextDifferencingService;

            var differenceOptions = new StringDifferenceOptions()
            {
                DifferenceType = StringDifferenceTypes.Word
            };

            var oldTextSnapshot = oldText.FindCorrespondingEditorTextSnapshot();
            var newTextSnapshot = newText.FindCorrespondingEditorTextSnapshot();
            var useSnapshots    = oldTextSnapshot != null && newTextSnapshot != null;

            var diffResult = useSnapshots
                ? diffService.DiffSnapshotSpans(oldTextSnapshot.GetFullSpan(), newTextSnapshot.GetFullSpan(), differenceOptions)
                : diffService.DiffStrings(oldText.ToString(), newText.ToString(), differenceOptions);

            return(diffResult.Differences.Select(d =>
                                                 new TextChange(
                                                     diffResult.LeftDecomposition.GetSpanInOriginal(d.Left).ToTextSpan(),
                                                     newText.GetSubText(diffResult.RightDecomposition.GetSpanInOriginal(d.Right).ToTextSpan()).ToString())));
        }
        public SnapshotLineList(SnapshotSpan snapshotSpan, Func <ITextSnapshotLine, string> getLineTextCallback, StringDifferenceOptions options)
        {
            if (getLineTextCallback == null)
            {
                throw new ArgumentNullException(nameof(getLineTextCallback));
            }
            if ((options.DifferenceType & StringDifferenceTypes.Line) == 0)
            {
                throw new InvalidOperationException("This collection can only be used for line differencing");
            }

            _snapshotSpan        = snapshotSpan;
            _getLineTextCallback = getLineTextCallback;
            _options             = options;

            // Figure out the first and last line in the span
            var startLine = snapshotSpan.Start.GetContainingLine();
            int start     = snapshotSpan.Start.GetContainingLine().LineNumber;

            //Perf hack to avoid calling GetContainingLine() if the lines are the same.
            SnapshotPoint endPoint = snapshotSpan.End;
            int           end      = ((endPoint.Position < startLine.EndIncludingLineBreak) ? start : endPoint.GetContainingLine().LineNumber) + 1;

            _lineSpan = Span.FromBounds(start, end);
        }
        public async Task<IEnumerable<TextChange>> GetTextChangesAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken)
        {
            var oldText = await oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
            var newText = await newDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);

            var diffService = _differenceSelectorService.GetTextDifferencingService(oldDocument.Project.LanguageServices.GetService<IContentTypeLanguageService>().GetDefaultContentType())
                ?? _differenceSelectorService.DefaultTextDifferencingService;

            var differenceOptions = new StringDifferenceOptions()
            {
                DifferenceType = StringDifferenceTypes.Word
            };

            var oldTextSnapshot = oldText.FindCorrespondingEditorTextSnapshot();
            var newTextSnapshot = newText.FindCorrespondingEditorTextSnapshot();
            var useSnapshots = oldTextSnapshot != null && newTextSnapshot != null;

            var diffResult = useSnapshots
                ? diffService.DiffSnapshotSpans(oldTextSnapshot.GetFullSpan(), newTextSnapshot.GetFullSpan(), differenceOptions)
                : diffService.DiffStrings(oldText.ToString(), newText.ToString(), differenceOptions);

            return diffResult.Differences.Select(d =>
                new TextChange(
                    diffResult.LeftDecomposition.GetSpanInOriginal(d.Left).ToTextSpan(),
                    newText.GetSubText(diffResult.RightDecomposition.GetSpanInOriginal(d.Right).ToTextSpan()).ToString()));
        }
        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);
        }
        IHierarchicalDifferenceCollection DiffText(ITokenizedStringListInternal left, ITokenizedStringListInternal right, StringDifferenceTypes type, StringDifferenceOptions differenceOptions)
        {
            StringDifferenceOptions nextOptions = new StringDifferenceOptions(differenceOptions);

            nextOptions.DifferenceType &= ~type;

            var diffCollection = ComputeMatches(type, differenceOptions, left, right);

            return(new HierarchicalDifferenceCollection(diffCollection, left, right, this, nextOptions));
        }
        protected DecompositionListMaker(StringDifferenceOptions options)
        {
            // Make a copy, so we don't accidentally modify the one we're given
            options = new StringDifferenceOptions(options);
            if (options.WordSplitBehavior == WordSplitBehavior.LanguageAppropriate)
            {
                options.WordSplitBehavior = WordSplitBehavior.WhiteSpaceAndPunctuation;
            }

            Options = options;
        }
        private void CreateTokens(StringDifferenceOptions options, bool ignoreTrimWhiteSpace)
        {
            int       end               = this.OriginalLength;
            int       i                 = 0;
            int       tokenStart        = 0;
            TokenType previousTokenType = TokenType.WhiteSpace;
            bool      skipPreviousToken = ignoreTrimWhiteSpace; // Assume that the 1st whitespace token is ignoreable if we're trimming whitespace.

            while (i < end)
            {
                bool      skipNextToken;
                TokenType nextTokenType;
                int       breakLength = this.LengthOfLineBreak(i, end);
                if (breakLength != 0)
                {
                    nextTokenType = ignoreTrimWhiteSpace ? TokenType.WhiteSpace : TokenType.LineBreak;
                    skipNextToken = ignoreTrimWhiteSpace;
                }
                else
                {
                    nextTokenType = GetTokenType(this.CharacterAt(i), options.WordSplitBehavior);
                    skipNextToken = (nextTokenType == TokenType.WhiteSpace) ? skipPreviousToken : false;
                    breakLength   = 1;
                }

                if ((nextTokenType != previousTokenType) || (nextTokenType == TokenType.Symbol))
                {
                    if ((tokenStart < i) && !skipPreviousToken)
                    {
                        this.Tokens.Add(new Span(tokenStart, i - tokenStart));
                    }

                    previousTokenType = nextTokenType;
                    tokenStart        = i;
                }

                skipPreviousToken = skipNextToken;
                i += breakLength;
            }

            if ((end == 0) ||                                                           // 0-length sequences get a single token
                !(ignoreTrimWhiteSpace && (previousTokenType == TokenType.WhiteSpace))) // act as if there is an implicit line break at the end of the line.
            {
                this.Tokens.Add(new Span(tokenStart, end - tokenStart));
            }
        }
示例#8
0
        public HashSet <Tuple <int, int> > PerformDiffString(string origContent, string modifiedContent)
        {
            HashSet <Tuple <int, int> > diffRanges = new HashSet <Tuple <int, int> >();
            StringDifferenceOptions     sdo        = new StringDifferenceOptions()
            {
                DifferenceType       = StringDifferenceTypes.Line | StringDifferenceTypes.Word,
                IgnoreTrimWhiteSpace = false
            };

            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);
        }
示例#9
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>();
        }
示例#10
0
 /// <summary>
 /// Create a set of edit options for computing a minimal difference,
 /// with the given <see cref="StringDifferenceOptions" />.
 /// </summary>
 public EditOptions(StringDifferenceOptions differenceOptions)
 {
     this.ComputeMinimalChange = true;
     this.DifferenceOptions    = differenceOptions;
 }
        public IHierarchicalDifferenceCollection DiffStrings(string leftString, string rightString, StringDifferenceOptions differenceOptions)
        {
            if (leftString == null)
            {
                throw new ArgumentNullException(nameof(leftString));
            }
            if (rightString == null)
            {
                throw new ArgumentNullException(nameof(rightString));
            }

            StringDifferenceTypes        type;
            ITokenizedStringListInternal left;
            ITokenizedStringListInternal right;

            if (differenceOptions.DifferenceType.HasFlag(StringDifferenceTypes.Line))
            {
                type = StringDifferenceTypes.Line;

                left  = new LineDecompositionList(leftString, differenceOptions.IgnoreTrimWhiteSpace);
                right = new LineDecompositionList(rightString, differenceOptions.IgnoreTrimWhiteSpace);
            }
            else if (differenceOptions.DifferenceType.HasFlag(StringDifferenceTypes.Word))
            {
                type = StringDifferenceTypes.Word;

                left  = new WordDecompositionList(leftString, differenceOptions);
                right = new WordDecompositionList(rightString, differenceOptions);
            }
            else if (differenceOptions.DifferenceType.HasFlag(StringDifferenceTypes.Character))
            {
                type = StringDifferenceTypes.Character;

                left  = new CharacterDecompositionList(leftString);
                right = new CharacterDecompositionList(rightString);
            }
            else
            {
                throw new ArgumentOutOfRangeException("differenceOptions");
            }

            return(DiffText(left, right, type, differenceOptions));
        }
        public IHierarchicalDifferenceCollection DiffSnapshotSpans(SnapshotSpan leftSpan, SnapshotSpan rightSpan, StringDifferenceOptions differenceOptions, Func <ITextSnapshotLine, string> getLineTextCallback)
        {
            StringDifferenceTypes        type;
            ITokenizedStringListInternal left;
            ITokenizedStringListInternal right;

            if (differenceOptions.DifferenceType.HasFlag(StringDifferenceTypes.Line))
            {
                type = StringDifferenceTypes.Line;

                left  = new SnapshotLineList(leftSpan, getLineTextCallback, differenceOptions);
                right = new SnapshotLineList(rightSpan, getLineTextCallback, differenceOptions);
            }
            else if (differenceOptions.DifferenceType.HasFlag(StringDifferenceTypes.Word))
            {
                type = StringDifferenceTypes.Word;

                left  = new WordDecompositionList(leftSpan, differenceOptions);
                right = new WordDecompositionList(rightSpan, differenceOptions);
            }
            else if (differenceOptions.DifferenceType.HasFlag(StringDifferenceTypes.Character))
            {
                type = StringDifferenceTypes.Character;

                left  = new CharacterDecompositionList(leftSpan);
                right = new CharacterDecompositionList(rightSpan);
            }
            else
            {
                throw new ArgumentOutOfRangeException("differenceOptions");
            }

            return(DiffText(left, right, type, differenceOptions));
        }
 public IHierarchicalDifferenceCollection DiffSnapshotSpans(SnapshotSpan leftSpan, SnapshotSpan rightSpan, StringDifferenceOptions differenceOptions)
 {
     return(this.DiffSnapshotSpans(leftSpan, rightSpan, differenceOptions, DefaultGetLineTextCallback));
 }
 static IDifferenceCollection <string> ComputeMatches(StringDifferenceTypes differenceType, StringDifferenceOptions differenceOptions,
                                                      IList <string> leftSequence, IList <string> rightSequence,
                                                      IList <string> originalLeftSequence, IList <string> originalRightSequence)
 {
     return(MaximalSubsequenceAlgorithm.DifferenceSequences(leftSequence, rightSequence, originalLeftSequence, originalRightSequence, differenceOptions.ContinueProcessingPredicate));
 }
 static IDifferenceCollection <string> ComputeMatches(StringDifferenceTypes differenceType, StringDifferenceOptions differenceOptions,
                                                      IList <string> leftSequence, IList <string> rightSequence)
 {
     return(ComputeMatches(differenceType, differenceOptions, leftSequence, rightSequence, leftSequence, rightSequence));
 }
 public WordDecompositionList(SnapshotSpan original, StringDifferenceOptions options)
     : base(original)
 {
     this.CreateTokens(options, options.IgnoreTrimWhiteSpace);
 }
 public StringDecompositionListMaker(string s, StringDifferenceOptions options)
     : base(options)
 {
     _string = s;
 }
示例#18
0
 public IDifferenceBuffer CreateDifferenceBuffer(ITextBuffer leftBaseBuffer, ITextBuffer rightBaseBuffer, StringDifferenceOptions options, bool disableEditing = false, bool wrapLeftBuffer = true, bool wrapRightBuffer = true) => throw new System.NotImplementedException();
        public IHierarchicalDifferenceCollection DiffSnapshotSpans(SnapshotSpan left, SnapshotSpan right, StringDifferenceOptions differenceOptions, Func <ITextSnapshotLine, string> getLineTextCallback)
        {
            if (left == null)
            {
                throw new ArgumentNullException("left");
            }
            if (right == null)
            {
                throw new ArgumentNullException("right");
            }

            var leftDecompositions  = new SnapshotSpanDecompositionListMaker(left, differenceOptions, getLineTextCallback);
            var rightDecompositions = new SnapshotSpanDecompositionListMaker(right, differenceOptions, getLineTextCallback);

            return(DiffText(leftDecompositions, rightDecompositions, differenceOptions));
        }
        public IHierarchicalDifferenceCollection DiffStrings(string left, string right, StringDifferenceOptions differenceOptions)
        {
            if (left == null)
            {
                throw new ArgumentNullException("left");
            }
            if (right == null)
            {
                throw new ArgumentNullException("right");
            }

            var leftDecompositions  = new StringDecompositionListMaker(left, differenceOptions);
            var rightDecompositions = new StringDecompositionListMaker(right, differenceOptions);

            return(DiffText(leftDecompositions, rightDecompositions, differenceOptions));
        }
        IHierarchicalDifferenceCollection DiffText(DecompositionListMaker left, DecompositionListMaker right, StringDifferenceOptions differenceOptions)
        {
            if (left == null)
            {
                throw new ArgumentNullException("left");
            }
            if (right == null)
            {
                throw new ArgumentNullException("right");
            }

            StringDifferenceTypes differenceType = differenceOptions.DifferenceType;

            if (differenceType == 0)
            {
                throw new ArgumentOutOfRangeException("differenceOptions");
            }

            if ((differenceType & StringDifferenceTypes.Line) != 0)
            {
                //The DecompositionListMaker creates a new copy of the list so make sure we only ask once
                var leftUnfilteredLineList  = left.UnfilteredLineList;
                var rightUnfilteredLineList = right.UnfilteredLineList;

                var diffCollection = ComputeMatches(differenceType, differenceOptions, left.FilteredLineList, right.FilteredLineList, leftUnfilteredLineList, rightUnfilteredLineList);

                //Try to clean up the differences so things align better with the block structure of typical languages.
                diffCollection = FinessedDifferenceCollection.FinesseLineDifferences(diffCollection, leftUnfilteredLineList, rightUnfilteredLineList);

                StringDifferenceOptions nextOptions = new StringDifferenceOptions(differenceOptions);
                nextOptions.DifferenceType &= ~StringDifferenceTypes.Line;

                return(new HierarchicalDifferenceCollection(diffCollection, leftUnfilteredLineList, rightUnfilteredLineList, this, nextOptions));
            }
            else if ((differenceType & StringDifferenceTypes.Word) != 0)
            {
                var leftWords  = left.WordList;
                var rightWords = right.WordList;

                var diffCollection = ComputeMatches(StringDifferenceTypes.Word, differenceOptions, leftWords, rightWords);

                StringDifferenceOptions nextOptions = new StringDifferenceOptions(differenceOptions);
                nextOptions.DifferenceType &= ~StringDifferenceTypes.Word;

                return(new HierarchicalDifferenceCollection(diffCollection, leftWords, rightWords, this, nextOptions));
            }
            else if ((differenceType & StringDifferenceTypes.Character) != 0)
            {
                var leftChars  = left.CharacterList;
                var rightChars = right.CharacterList;

                var diffCollection = ComputeMatches(StringDifferenceTypes.Character, differenceOptions, leftChars, rightChars);

                // This should always be 0.
                StringDifferenceOptions nextOptions = new StringDifferenceOptions(differenceOptions);
                nextOptions.DifferenceType &= ~StringDifferenceTypes.Character;

                Debug.Assert(nextOptions.DifferenceType == 0,
                             "After character differencing, the difference type should be empty (invalid).");

                return(new HierarchicalDifferenceCollection(diffCollection, leftChars, rightChars, this, nextOptions));
            }
            else
            {
                throw new ArgumentOutOfRangeException("differenceOptions");
            }
        }
示例#22
0
 /// <summary>
 /// Create a set of edit options.
 /// </summary>
 public EditOptions(bool computeMinimalChange, StringDifferenceOptions differenceOptions)
 {
     this.ComputeMinimalChange = computeMinimalChange;
     this.DifferenceOptions    = differenceOptions;
 }
 public SnapshotSpanDecompositionListMaker(SnapshotSpan snapshotSpan, StringDifferenceOptions options, Func <ITextSnapshotLine, string> getLineTextCallback)
     : base(options)
 {
     _snapshotSpan        = snapshotSpan;
     _getLineTextCallback = getLineTextCallback;
 }
        /// <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);
        }
 public WordDecompositionList(string original, StringDifferenceOptions options)
     : base(original)
 {
     this.CreateTokens(options, ignoreTrimWhiteSpace: false);    // We never paid attention to trim whitespace for strings when doing line or word diffs.
 }