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; }
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, } )); }
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 })); }
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; }
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(); }
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); }
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); }
/// <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); }
/// <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); }
public TextBuffer(IContentType contentType, StringRebuilder content, ITextDifferencingService textDifferencingService, GuardedOperations guardedOperations) : this(contentType, content, textDifferencingService, guardedOperations, false) { }
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, }); }
protected BaseProjectionBuffer(IProjectionEditResolver resolver, IContentType contentType, ITextDifferencingService textDifferencingService, GuardedOperations guardedOperations) : base(contentType, 0, textDifferencingService, guardedOperations) { this.resolver = resolver; // null is OK }
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 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); }