Ejemplo n.º 1
0
        public ElisionBuffer(IProjectionEditResolver resolver,
                             IContentType contentType,
                             ITextBuffer sourceBuffer,
                             NormalizedSpanCollection exposedSpans,
                             ElisionBufferOptions options,
                             ITextDifferencingService textDifferencingService,
                             GuardedOperations guardedOperations)
            : base(resolver, contentType, textDifferencingService, guardedOperations)
        {
            Debug.Assert(sourceBuffer != null);
            this.sourceBuffer   = sourceBuffer;
            this.sourceSnapshot = sourceBuffer.CurrentSnapshot;

            BaseBuffer baseSourceBuffer = (BaseBuffer)sourceBuffer;

            this.eventHook = new WeakEventHook(this, baseSourceBuffer);

            this.group = baseSourceBuffer.group;
            this.group.AddMember(this);

            this.content = new ElisionMap(this.sourceSnapshot, exposedSpans);

            StringRebuilder newBuilder = StringRebuilder.Empty;

            for (int i = 0; (i < exposedSpans.Count); ++i)
            {
                newBuilder = newBuilder.Append(BufferFactoryService.StringRebuilderFromSnapshotAndSpan(this.sourceSnapshot, exposedSpans[i]));
            }
            this.builder = newBuilder;

            this.elisionOptions = options;
            this.currentVersion.SetLength(content.Length);
            this.currentElisionSnapshot = new ElisionSnapshot(this, this.sourceSnapshot, base.currentVersion, this.builder, this.content, (options & ElisionBufferOptions.FillInMappingMode) != 0);
            this.currentSnapshot        = this.currentElisionSnapshot;
        }
Ejemplo n.º 2
0
        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,
            }
                       ));
        }
Ejemplo n.º 3
0
        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
            }));
        }
Ejemplo n.º 4
0
        public ElisionBuffer(IProjectionEditResolver resolver,
                             IContentType contentType,
                             ITextBuffer sourceBuffer,
                             NormalizedSpanCollection exposedSpans,
                             ElisionBufferOptions options,
                             ITextDifferencingService textDifferencingService,
                             GuardedOperations guardedOperations)
            : base(resolver, contentType, textDifferencingService, guardedOperations)
        {
            Debug.Assert(sourceBuffer != null);
            this.sourceBuffer   = sourceBuffer;
            this.sourceSnapshot = sourceBuffer.CurrentSnapshot;

            BaseBuffer baseSourceBuffer = (BaseBuffer)sourceBuffer;

            this.eventHook = new WeakEventHook(this, baseSourceBuffer);

            this.group = baseSourceBuffer.group;
            this.group.AddMember(this);

            this.content        = new ElisionMap(this.sourceSnapshot, exposedSpans);
            this.elisionOptions = options;
            this.currentVersion.InternalLength = content.Length;
            this.currentElisionSnapshot        = new ElisionSnapshot(this, this.sourceSnapshot, base.currentVersion, this.content, (options & ElisionBufferOptions.FillInMappingMode) != 0);
            this.currentSnapshot = this.currentElisionSnapshot;
        }
Ejemplo n.º 5
0
 public TextBuffer(IContentType contentType, StringRebuilder content, ITextDifferencingService textDifferencingService, GuardedOperations guardedOperations, bool spurnGroup)
     : base(contentType, content.Length, textDifferencingService, guardedOperations)
 {
     // Parameters are validated outside
     this.group           = new BufferGroup(this);
     this.builder         = content;
     this.spurnGroup      = spurnGroup;
     this.currentSnapshot = this.TakeSnapshot();
 }
Ejemplo n.º 6
0
        protected BaseBuffer(IContentType contentType, int initialLength, ITextDifferencingService textDifferencingService, GuardedOperations guardedOperations)
        {
            // parameters are validated outside
            Debug.Assert(contentType != null);

            this.contentType             = contentType;
            this.currentVersion          = new TextVersion(this, 0, 0, initialLength);
            this.textDifferencingService = textDifferencingService;
            this.guardedOperations       = guardedOperations;
        }
        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);
        }
Ejemplo n.º 8
0
        protected BaseBuffer(IContentType contentType, int initialLength, ITextDifferencingService textDifferencingService, GuardedOperations guardedOperations)
        {
            // parameters are validated outside
            Debug.Assert(contentType != null);

            this.contentType    = contentType;
            this.currentVersion = new TextVersion(this, new TextImageVersion(initialLength));
            // this.builder should be set in calling ctor
            this.textDifferencingService = textDifferencingService;
            this.guardedOperations       = guardedOperations;
        }
        public ITextDifferencingService GetTextDifferencingService(IContentType contentType)
        {
            ITextDifferencingService service =
                _guardedOperations.InvokeBestMatchingFactory
                    (_textDifferencingServices,
                    contentType,
                    differencingService => differencingService,
                    _contentTypeRegistryService, this);

            return(service ?? DefaultTextDifferencingService);
        }
Ejemplo n.º 10
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>();
        }
 /// <summary>
 /// Construct a normalized version of the given TextChange collection.
 /// </summary>
 /// <param name="changes">List of changes to normalize</param>
 /// <param name="differenceOptions">The difference options to use for minimal differencing, if any.</param>
 /// <param name="textDifferencingService">The difference service to use, if differenceOptions were supplied.</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>
 private NormalizedTextChangeCollection(IList <TextChange> changes, StringDifferenceOptions?differenceOptions, ITextDifferencingService textDifferencingService,
                                        ITextSnapshot before, ITextSnapshot after)
     : base(Normalize(changes, differenceOptions, textDifferencingService, before, after))
 {
 }
        public static INormalizedTextChangeCollection Create(IList <TextChange> changes, StringDifferenceOptions?differenceOptions, ITextDifferencingService textDifferencingService,
                                                             ITextSnapshot before = null, ITextSnapshot after = null)
        {
            INormalizedTextChangeCollection result = GetTrivialCollection(changes);

            return(result != null ? result : new NormalizedTextChangeCollection(changes, differenceOptions, textDifferencingService, before, after));
        }
        /// <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);
        }
Ejemplo n.º 14
0
 /// <summary>
 /// Construct a normalized version of the given TextChange collection.
 /// </summary>
 /// <param name="changes">List of changes to normalize</param>
 /// <param name="differenceOptions">The difference options to use for minimal differencing, if any.</param>
 /// <param name="textDifferencingService">The difference service to use, if differenceOptions were supplied.</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>
 private NormalizedTextChangeCollection(IReadOnlyList <TextChange> changes, StringDifferenceOptions?differenceOptions, ITextDifferencingService textDifferencingService,
                                        ITextSnapshot before, ITextSnapshot after)
 {
     _changes = Normalize(changes, differenceOptions, textDifferencingService, before, after);
 }
Ejemplo n.º 15
0
 public TextBuffer(IContentType contentType, StringRebuilder content, ITextDifferencingService textDifferencingService, GuardedOperations guardedOperations)
     : this(contentType, content, textDifferencingService, guardedOperations, false)
 {
 }
Ejemplo n.º 16
0
        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,
            });
        }
Ejemplo n.º 17
0
 protected BaseProjectionBuffer(IProjectionEditResolver resolver, IContentType contentType, ITextDifferencingService textDifferencingService, GuardedOperations guardedOperations)
     : base(contentType, 0, textDifferencingService, guardedOperations)
 {
     this.resolver = resolver;   // null is OK
 }
Ejemplo n.º 18
0
        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;
        }
Ejemplo n.º 19
0
        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);
        }