public ElisionMap EditSpans(ITextSnapshot sourceSnapshot, NormalizedSpanCollection spansToElide, NormalizedSpanCollection spansToExpand, out FrugalList <TextChange> textChanges) { textChanges = new FrugalList <TextChange>(); NormalizedSpanCollection beforeSourceSpans = new NormalizedSnapshotSpanCollection(GetSourceSpans(sourceSnapshot, 0, this.spanCount)); NormalizedSpanCollection afterElisionSourceSpans = NormalizedSpanCollection.Difference(beforeSourceSpans, spansToElide); NormalizedSpanCollection elisionChangeSpans = NormalizedSpanCollection.Difference(beforeSourceSpans, afterElisionSourceSpans); foreach (Span s in elisionChangeSpans) { textChanges.Add(TextChange.Create(this.root.MapFromSourceSnapshotToNearest(s.Start, 0), BufferFactoryService.StringRebuilderFromSnapshotAndSpan(sourceSnapshot, s), StringRebuilder.Empty, sourceSnapshot)); } NormalizedSpanCollection afterExpansionSourceSpans = NormalizedSpanCollection.Union(afterElisionSourceSpans, spansToExpand); NormalizedSpanCollection expansionChangeSpans = NormalizedSpanCollection.Difference(afterExpansionSourceSpans, afterElisionSourceSpans); foreach (Span s in expansionChangeSpans) { textChanges.Add(TextChange.Create(this.root.MapFromSourceSnapshotToNearest(s.Start, 0), StringRebuilder.Empty, BufferFactoryService.StringRebuilderFromSnapshotAndSpan(sourceSnapshot, s), sourceSnapshot)); } return(textChanges.Count > 0 ? new ElisionMap(sourceSnapshot, afterExpansionSourceSpans) : this); }
public ElisionMap EditSpans(ITextSnapshot sourceSnapshot, NormalizedSpanCollection spansToElide, NormalizedSpanCollection spansToExpand, out FrugalList <TextChange> textChanges) { textChanges = new FrugalList <TextChange>(); NormalizedSpanCollection beforeSourceSpans = new NormalizedSnapshotSpanCollection(GetSourceSpans(sourceSnapshot, 0, this.spanCount)); NormalizedSpanCollection afterElisionSourceSpans = NormalizedSpanCollection.Difference(beforeSourceSpans, spansToElide); NormalizedSpanCollection elisionChangeSpans = NormalizedSpanCollection.Difference(beforeSourceSpans, afterElisionSourceSpans); foreach (Span s in elisionChangeSpans) { textChanges.Add(new TextChange(this.root.MapFromSourceSnapshotToNearest(s.Start, 0), new ReferenceChangeString(new SnapshotSpan(sourceSnapshot, s)), ChangeString.EmptyChangeString, sourceSnapshot)); } NormalizedSpanCollection afterExpansionSourceSpans = NormalizedSpanCollection.Union(afterElisionSourceSpans, spansToExpand); NormalizedSpanCollection expansionChangeSpans = NormalizedSpanCollection.Difference(afterExpansionSourceSpans, afterElisionSourceSpans); foreach (Span s in expansionChangeSpans) { textChanges.Add(new TextChange(this.root.MapFromSourceSnapshotToNearest(s.Start, 0), ChangeString.EmptyChangeString, new ReferenceChangeString(new SnapshotSpan(sourceSnapshot, s)), sourceSnapshot)); } return(textChanges.Count > 0 ? new ElisionMap(sourceSnapshot, afterExpansionSourceSpans) : this); }
public void AddTwo() { var list = new FrugalList <int>(); list.Add(1); list.Add(2); AssertList(list, 1, 2); }
internal void AddDerivedOptions(EditorOptions derived) { // Get rid of the expired refs DerivedEditorOptions.RemoveAll(weakref => !weakref.IsAlive); DerivedEditorOptions.Add(new WeakReference(derived)); }
private void RemoveSourceBuffer(IProjectionBufferBase projBuffer, ITextBuffer sourceBuffer, FrugalList <ITextBuffer> removedFromGraphBuffers) { IList <IProjectionBufferBase> importingProjectionBuffers = this.importingProjectionBufferMap[sourceBuffer]; importingProjectionBuffers.Remove(projBuffer); if (importingProjectionBuffers.Count == 0) { removedFromGraphBuffers.Add(sourceBuffer); this.importingProjectionBufferMap.Remove(sourceBuffer); for (int i = 0; (i < this.eventHooks.Count); ++i) { if (this.eventHooks[i].SourceBuffer == sourceBuffer) { this.eventHooks[i].UnsubscribeFromSourceBuffer(); this.eventHooks.RemoveAt(i); break; } } IProjectionBufferBase removedProjBufferBase = sourceBuffer as IProjectionBufferBase; if (removedProjBufferBase != null) { foreach (ITextBuffer furtherSourceBuffer in removedProjBufferBase.SourceBuffers) { RemoveSourceBuffer(removedProjBufferBase, furtherSourceBuffer, removedFromGraphBuffers); } } } }
public void AddOne() { var list = new FrugalList <int>(); list.Add(42); AssertList(list, 42); }
private void AddSourceBuffer(IProjectionBufferBase projBuffer, ITextBuffer sourceBuffer, FrugalList <ITextBuffer> addedToGraphBuffers) { bool firstEncounter = false; FrugalList <IProjectionBufferBase> importingProjectionBuffers; if (!this.importingProjectionBufferMap.TryGetValue(sourceBuffer, out importingProjectionBuffers)) { // sourceBuffer is new to this buffer graph addedToGraphBuffers.Add(sourceBuffer); firstEncounter = true; importingProjectionBuffers = new FrugalList <IProjectionBufferBase>(); this.importingProjectionBufferMap.Add(sourceBuffer, importingProjectionBuffers); this.eventHooks.Add(new WeakEventHookForBufferGraph(this, sourceBuffer)); } importingProjectionBuffers.Add(projBuffer); if (firstEncounter) { IProjectionBufferBase addedProjBufferBase = sourceBuffer as IProjectionBufferBase; if (addedProjBufferBase != null) { foreach (ITextBuffer furtherSourceBuffer in addedProjBufferBase.SourceBuffers) { AddSourceBuffer(addedProjBufferBase, furtherSourceBuffer, addedToGraphBuffers); } } } }
public IEnumerable <TSource> GetSources(IEnumerable <ITextBuffer> buffers) { if (_textView.IsClosed) { Debug.Fail("Intellisense is trying to operate after its view has been closed."); return(Enumerable.Empty <TSource>()); } // For each buffer that should be involved, either return the cached source for that buffer, or create a source and // cache it for later use. IList <TSource> sources = new FrugalList <TSource>(); foreach (var buffer in buffers) { var bufferSources = (IReadOnlyCollection <TSource>)_bufferCache[buffer]; if (bufferSources == null) { bufferSources = _sourceCreator(buffer); _bufferCache.Add(buffer, bufferSources); buffer.ContentTypeChanged += OnContentTypeChanged; } foreach (var source in bufferSources) { sources.Add(source); } } return(sources); }
public void AddMany() { var list = new FrugalList <int>(); var range = Enumerable.Range(0, 5); foreach (var cur in range) { list.Add(cur); } AssertList(list, range.ToArray()); }
public void AddExhaustive() { const int count = 20; var list = new FrugalList <int>(); var range = Enumerable.Range(0, count).ToArray(); for (int i = 0; i < range.Length; i++) { list.Add(range[i]); AssertList(list, Enumerable.Range(0, i + 1).ToArray()); } }
internal override ReadOnlyCollection <SnapshotSpan> MapReplacementSpanToSourceSnapshots(Span replacementSpan, ITextBuffer excludedBuffer) { // This is exactly like ordinary span mapping, except that empty source spans at the ends of the replacementSpan are included. FrugalList <SnapshotSpan> mappedSpans = new FrugalList <SnapshotSpan>(); int rover = FindLowestSpanIndexOfPosition(replacementSpan.Start); int roverHi = FindHighestSpanIndexOfPosition(replacementSpan.End); // sourceSpans[rover] contains span.Start SnapshotSpan sourceSpan = this.sourceSpans[rover]; { SnapshotPoint mappedStart = sourceSpan.Start + (replacementSpan.Start - this.cumulativeLengths[rover]); int mappedLength = mappedStart.Position + replacementSpan.Length < sourceSpan.End ? replacementSpan.Length : sourceSpan.End.Position - mappedStart; SnapshotSpan mappedSpan = new SnapshotSpan(mappedStart, mappedLength); if (mappedSpan.Length > 0 || mappedSpan.Snapshot.TextBuffer != excludedBuffer) { mappedSpans.Add(new SnapshotSpan(mappedStart, mappedLength)); } } // walk forward until we cover the entire span while (rover < roverHi) { rover++; sourceSpan = this.sourceSpans[rover]; SnapshotSpan mappedSpan = replacementSpan.End >= this.cumulativeLengths[rover + 1] ? sourceSpan : new SnapshotSpan(sourceSpan.Snapshot, new Span(sourceSpan.Start, replacementSpan.End - this.cumulativeLengths[rover])); if (mappedSpan.Length > 0 || mappedSpan.Snapshot.TextBuffer != excludedBuffer) { mappedSpans.Add(mappedSpan); } } Debug.Assert(replacementSpan.Length == mappedSpans.Sum((SnapshotSpan s) => s.Length), "Inconsistency in MapReplacementSpanToSourceSnapshots"); return(new ReadOnlyCollection <SnapshotSpan>(mappedSpans)); }
public void SubjectBuffersConnected( ITextView textView, ConnectionReason reason, IReadOnlyCollection <ITextBuffer> subjectBuffers) { IntellisenseManager manager = textView.Properties.GetOrCreateSingletonProperty( delegate { return(new IntellisenseManager(this, textView)); }); // Create the appropriate Intellisense controllers for the content types in the buffer graph. It's important that we do // this after creating the brokers, as the controllers will most likely start using the brokers immediately. for (int f = 0; f < this.IntellisenseControllerFactories.Count; ++f) { var factory = this.IntellisenseControllerFactories[f]; // filter subject buffers to get the ones that match the factory content types FrugalList <ITextBuffer> matchingSubjectBuffers = new FrugalList <ITextBuffer>(); foreach (string factoryContentType in factory.Metadata.ContentTypes) { foreach (ITextBuffer subjectBuffer in subjectBuffers) { if (subjectBuffer.ContentType.IsOfType(factoryContentType) && !matchingSubjectBuffers.Contains(subjectBuffer)) { matchingSubjectBuffers.Add(subjectBuffer); } } } if (matchingSubjectBuffers.Count > 0) { // This controller factory is registered for the content type we understand. Go ahead and create // one. Note that this won't give us a handle to a controller object. We wouldn't be able to do anything // with such a reference anyway. if (manager.Controllers[f] == null) { manager.Controllers[f] = this.GuardedOperations.InstantiateExtension (factory, factory, provider => provider.TryCreateIntellisenseController(textView, matchingSubjectBuffers)); } else { foreach (ITextBuffer matchingSubjectBuffer in matchingSubjectBuffers) { manager.Controllers[f].ConnectSubjectBuffer(matchingSubjectBuffer); } } } } }
private static FrugalList <SnapshotSpan> MapDownOneLevel(FrugalList <SnapshotSpan> inputSpans, Predicate <ITextSnapshot> match, ref ITextSnapshot chosenSnapshot, ref FrugalList <Span> targetSpans) { FrugalList <SnapshotSpan> downSpans = new FrugalList <SnapshotSpan>(); foreach (SnapshotSpan inputSpan in inputSpans) { IProjectionBufferBase projBuffer = (IProjectionBufferBase)inputSpan.Snapshot.TextBuffer; IProjectionSnapshot projSnap = projBuffer.CurrentSnapshot; if (projSnap.SourceSnapshots.Count > 0) { IList <SnapshotSpan> mappedSpans = projSnap.MapToSourceSnapshots(inputSpan); for (int s = 0; s < mappedSpans.Count; ++s) { SnapshotSpan mappedSpan = mappedSpans[s]; ITextBuffer mappedBuffer = mappedSpan.Snapshot.TextBuffer; if (mappedBuffer.CurrentSnapshot == chosenSnapshot) { targetSpans.Add(mappedSpan.Span); } else if (chosenSnapshot == null && match(mappedBuffer.CurrentSnapshot)) { chosenSnapshot = mappedBuffer.CurrentSnapshot; targetSpans.Add(mappedSpan.Span); } else { IProjectionBufferBase mappedProjBuffer = mappedBuffer as IProjectionBufferBase; if (mappedProjBuffer != null) { downSpans.Add(mappedSpan); } } } } } return(downSpans); }
private Span MapNullSpansToSourceSnapshots(IElisionSnapshot elisionSnapshot, Span span, FrugalList <SnapshotSpan> result) { // TODO: this is identical to projection snapshot; can it be shared? FrugalList <SnapshotPoint> points = MapInsertionPointToSourceSnapshots(elisionSnapshot, span.Start); for (int p = 0; p < points.Count; ++p) { SnapshotPoint point = points[p]; SnapshotSpan mappedSpan = new SnapshotSpan(point.Snapshot, point.Position, 0); if (result.Count == 0 || mappedSpan != result[result.Count - 1]) { result.Add(mappedSpan); } } return(span); }
public Collection <ITextBuffer> GetTextBuffers(Predicate <ITextBuffer> match) { if (match == null) { throw new ArgumentNullException("match"); } FrugalList <ITextBuffer> buffers = new FrugalList <ITextBuffer>(); foreach (ITextBuffer buffer in this.importingProjectionBufferMap.Keys) { if (match(buffer)) { buffers.Add(buffer); } } return(new Collection <ITextBuffer>(buffers)); }
private IEnumerable <ISignatureHelpSource> CreateSourcesForBuffer(ITextBuffer buffer) { FrugalList <ISignatureHelpSource> sources = new FrugalList <ISignatureHelpSource>(); foreach (var source in this.GuardedOperations.InvokeMatchingFactories (this.OrderedSignatureHelpSourceProviderExports, provider => provider.TryCreateSignatureHelpSource(buffer), buffer.ContentType, this)) { #if DEBUG Helpers.TrackObject(this.ObjectTrackers, "SignatureHelp Sources", source); #endif sources.Add(source); } return(sources); }
public void MapToSourceSnapshotsInFillInMode(ITextSnapshot sourceSnapshot, Span span, FrugalList <SnapshotSpan> result) { SnapshotPoint?start; SnapshotPoint?end; if (span.Length == 0) { start = span.Start == 0 ? new SnapshotPoint(sourceSnapshot, 0) : this.root.MapToSourceSnapshot(sourceSnapshot, span.Start, 0, PositionAffinity.Predecessor); end = span.End == this.Length ? new SnapshotPoint(sourceSnapshot, sourceSnapshot.Length) : this.root.MapToSourceSnapshot(sourceSnapshot, span.End, 0, PositionAffinity.Successor); } else { start = this.root.MapToSourceSnapshot(sourceSnapshot, span.Start, 0, PositionAffinity.Successor); end = this.root.MapToSourceSnapshot(sourceSnapshot, span.End, 0, PositionAffinity.Predecessor); } Debug.Assert(start.HasValue); Debug.Assert(end.HasValue); result.Add(new SnapshotSpan(sourceSnapshot, Span.FromBounds(start.Value, end.Value))); }
protected internal virtual NormalizedSpanCollection GetReadOnlyExtentsImplementation(Span span) { FrugalList <Span> spans = new FrugalList <Span>(); if (this.readOnlyRegionSpanCollection != null) { foreach (ReadOnlySpan readOnlySpan in this.readOnlyRegionSpanCollection.QueryAllEffectiveReadOnlySpans(this.currentVersion)) { Span readOnlySpanSpan = readOnlySpan.GetSpan(this.currentSnapshot); Span?overlapSpan = (readOnlySpanSpan == span) ? readOnlySpanSpan : readOnlySpanSpan.Overlap(span); if (overlapSpan.HasValue) { spans.Add(overlapSpan.Value); } } } return(new NormalizedSpanCollection(spans)); }
/// <summary> /// Build a forest of all content types and associate a rank with each content type, /// indicating number of ancestors in the forest. E.g. any gets rank 0, text - 1, /// projection - 1, code - 2, inert - 0. /// </summary> public StableContentTypeComparer(IContentTypeRegistryService contentTypeRegistryService) { _contentTypeRanks = new Dictionary <string, int>(StringComparer.OrdinalIgnoreCase); contentTypeRegistryService = contentTypeRegistryService ?? throw new ArgumentNullException(nameof(contentTypeRegistryService)); var unprocessedBaseTypes = new Dictionary <IContentType, int>(); var derivedTypes = new Dictionary <IContentType, FrugalList <IContentType> >(); foreach (IContentType curContentType in contentTypeRegistryService.ContentTypes) { unprocessedBaseTypes[curContentType] = curContentType.BaseTypes.Count(); derivedTypes[curContentType] = new FrugalList <IContentType>(); } foreach (IContentType curContentType in contentTypeRegistryService.ContentTypes) { foreach (IContentType baseContentType in curContentType.BaseTypes) { FrugalList <IContentType> baseDerivedContentTypes = derivedTypes[baseContentType]; baseDerivedContentTypes.Add(curContentType); } } int groupRank = 0; while (unprocessedBaseTypes.Count > 0) { IEnumerable <IContentType> contentTypesWithoutBase = unprocessedBaseTypes.Where(kvp => kvp.Value == 0).Select(kvp => kvp.Key).ToList(); foreach (IContentType curContentTypeWithoutBase in contentTypesWithoutBase) { _contentTypeRanks[curContentTypeWithoutBase.TypeName] = groupRank; unprocessedBaseTypes.Remove(curContentTypeWithoutBase); foreach (IContentType curDerivedType in derivedTypes[curContentTypeWithoutBase]) { unprocessedBaseTypes[curDerivedType] = unprocessedBaseTypes[curDerivedType] - 1; } } groupRank++; } }
public IList <ClassificationSpan> GetClassificationSpans(SnapshotSpan trackingSpan) { IClassificationType currentParamType = _classificationTypeRegistry.GetClassificationType("currentParam"); if (currentParamType == null) { throw (new InvalidOperationException("Unable to retrieve 'current parameter' classification type")); } bool usePrettyPrintedContent; if (!_textBuffer.Properties.TryGetProperty <bool>(SignatureHelpParameterBoldingClassfier.UsePrettyPrintedContentKey, out usePrettyPrintedContent)) { usePrettyPrintedContent = false; } FrugalList <ClassificationSpan> classSpans = new FrugalList <ClassificationSpan>(); if ((_session != null) && (_session.SelectedSignature != null) && (_session.SelectedSignature.CurrentParameter != null)) { Span span = usePrettyPrintedContent ? _session.SelectedSignature.CurrentParameter.PrettyPrintedLocus : _session.SelectedSignature.CurrentParameter.Locus; if ((trackingSpan.IntersectsWith(span)) && (span.End <= _textBuffer.CurrentSnapshot.Length)) { // We only have one classification span to add, and that's the classification span for the current parameter. classSpans.Add (new ClassificationSpan (new SnapshotSpan(_textBuffer.CurrentSnapshot, span), currentParamType)); } } return(classSpans); }
public NormalizedSnapshotSpanCollection MapUpToSnapshot(SnapshotSpan span, SpanTrackingMode trackingMode, ITextSnapshot targetSnapshot) { if (targetSnapshot == null) { throw new ArgumentNullException("targetSnapshot"); } NormalizedSnapshotSpanCollection results = MapUpToBuffer(span, trackingMode, targetSnapshot.TextBuffer); if ((results.Count > 0) && (results[0].Snapshot != targetSnapshot)) { FrugalList <SnapshotSpan> translatedSpans = new FrugalList <SnapshotSpan>(); foreach (SnapshotSpan s in results) { translatedSpans.Add(s.TranslateTo(targetSnapshot, trackingMode)); } results = new NormalizedSnapshotSpanCollection(translatedSpans); } return(results); }
public ReadOnlyCollection <ISignatureHelpSession> GetSessions(ITextView textView) { FrugalList <ISignatureHelpSession> tempSessionList = new FrugalList <ISignatureHelpSession>(); IIntellisenseSessionStack sessionStack = this.IntellisenseSessionStackMap.GetStackForTextView(textView); if (sessionStack == null) { return(tempSessionList.AsReadOnly()); } foreach (var session in sessionStack.Sessions) { ISignatureHelpSession sigHelpSession = session as ISignatureHelpSession; if (sigHelpSession != null) { tempSessionList.Add(sigHelpSession); } } return(tempSessionList.AsReadOnly()); }
public IEnumerable <ITextBuffer> ResolveBuffersForCommand <TArgs>() where TArgs : EditorCommandArgs { var sourceSnapshotPoints = new FrugalList <SnapshotPoint>(new[] { _textView.Caret.Position.BufferPosition }); var resolvedBuffers = new FrugalList <ITextBuffer>(); for (int i = 0; i < sourceSnapshotPoints.Count; i++) { SnapshotPoint curSnapshotPoint = sourceSnapshotPoints[i]; if (curSnapshotPoint.Snapshot is IProjectionSnapshot curProjectionSnapshot) { sourceSnapshotPoints.AddRange(curProjectionSnapshot.MapToSourceSnapshots(curSnapshotPoint)); } // As the set of buffers isn't likely to exceed 5, just use the list to determine whether we've seen it before ITextBuffer curBuffer = curSnapshotPoint.Snapshot.TextBuffer; if (!resolvedBuffers.Contains(curBuffer)) { resolvedBuffers.Add(curBuffer); } } return(resolvedBuffers); }
private FrugalList <SnapshotSpan> MapUpOneLevel(FrugalList <SnapshotSpan> spans, ref ITextSnapshot chosenSnapshot, Predicate <ITextSnapshot> match, FrugalList <Span> topSpans) { FrugalList <SnapshotSpan> upSpans = new FrugalList <SnapshotSpan>(); foreach (SnapshotSpan span in spans) { FrugalList <IProjectionBufferBase> targetBuffers; if (this.importingProjectionBufferMap.TryGetValue(span.Snapshot.TextBuffer, out targetBuffers)) { if (targetBuffers != null) // make sure we don't fall off the top { foreach (IProjectionBufferBase projBuffer in targetBuffers) { IList <Span> projSpans = projBuffer.CurrentSnapshot.MapFromSourceSnapshot(span); if (projBuffer.CurrentSnapshot == chosenSnapshot) { topSpans.AddRange(projSpans); } else if (chosenSnapshot == null && match(projBuffer.CurrentSnapshot)) { chosenSnapshot = projBuffer.CurrentSnapshot; topSpans.AddRange(projSpans); } else { foreach (Span projSpan in projSpans) { upSpans.Add(new SnapshotSpan(projBuffer.CurrentSnapshot, projSpan)); } } } } } } return(upSpans); }
/// <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 TextContentChangedEventRaiser IncorporateChanges() { Debug.Assert(this.sourceSnapshot == this.pendingContentChangedEventArgs[0].Before); FrugalList <TextChange> projectedChanges = new FrugalList <TextChange>(); var args0 = this.pendingContentChangedEventArgs[0]; INormalizedTextChangeCollection sourceChanges; // Separate the easy and common case: if (this.pendingContentChangedEventArgs.Count == 1) { sourceChanges = args0.Changes; this.sourceSnapshot = args0.After; } else { // there is more than one snapshot of the source buffer to deal with. Since the changes may be // interleaved by position, we need to get a normalized list in sequence. First we denormalize the // changes so they are all relative to the same single starting snapshot, then we normalize them again into // a single list. // This relies crucially on the fact that we know something about the multiple snapshots: they were // induced by projection span adjustments, and the changes across them are independent. That is to say, // it is not the case that text inserted in one snapshot is deleted in a later snapshot in the series. DumpPendingContentChangedEventArgs(); List <TextChange> denormalizedChanges = new List <TextChange>() { new TextChange(int.MaxValue, StringRebuilder.Empty, StringRebuilder.Empty, LineBreakBoundaryConditions.None) }; for (int a = 0; a < this.pendingContentChangedEventArgs.Count; ++a) { NormalizedTextChangeCollection.Denormalize(this.pendingContentChangedEventArgs[a].Changes, denormalizedChanges); } DumpPendingChanges(new List <Tuple <ITextBuffer, List <TextChange> > >() { new Tuple <ITextBuffer, List <TextChange> >(this.sourceBuffer, denormalizedChanges) }); FrugalList <TextChange> slicedChanges = new FrugalList <TextChange>(); // remove the sentinel for (int d = 0; d < denormalizedChanges.Count - 1; ++d) { slicedChanges.Add(denormalizedChanges[d]); } sourceChanges = NormalizedTextChangeCollection.Create(slicedChanges); this.sourceSnapshot = this.pendingContentChangedEventArgs[this.pendingContentChangedEventArgs.Count - 1].After; } if (sourceChanges.Count > 0) { this.content = this.content.IncorporateChanges(sourceChanges, projectedChanges, args0.Before, this.sourceSnapshot, this.currentElisionSnapshot); } this.pendingContentChangedEventArgs.Clear(); ElisionSnapshot beforeSnapshot = this.currentElisionSnapshot; SetCurrentVersionAndSnapshot(NormalizedTextChangeCollection.Create(projectedChanges)); this.editApplicationInProgress = false; return(new TextContentChangedEventRaiser(beforeSnapshot, this.currentElisionSnapshot, args0.Options, args0.EditTag)); }
/// <summary> /// Calculate the selection rectangles that should be drawn on an ITextViewLine. /// </summary> /// <param name="line">The line on which the selection rectangles are to be calculated.</param> /// <param name="span">The span of the selection on that line.</param> /// <param name="selectionEnd">The end of the entire selection.</param> /// <param name="isBoxSelection">Is this a box selection?</param> /// <param name="isVirtualSpaceEnabled">Is virtual space turned on?</param> /// <remarks>internal for testability</remarks> internal static IList <Tuple <double, double> > CalculateVisualOverlapsForLine(ITextViewLine line, VirtualSnapshotSpan span, SnapshotPoint selectionEnd, bool isBoxSelection, bool isVirtualSpaceEnabled) { VirtualSnapshotPoint start = span.Start; VirtualSnapshotPoint end = span.End; // if the requested start and ending points are the same, then simply return a caret wide bound on the line if (start == end) { double xLeft = SkiaTextCaret.GetXCoordinateFromVirtualBufferPosition(line, start); return(new FrugalList <Tuple <double, double> > () { new Tuple <double, double> (xLeft, xLeft + 2) }); //TODO: SystemParameters.CaretWidth) }; } //If box selection is on, then every line is the "last" line of the selection. bool isLastLineOfSelection = isBoxSelection || (selectionEnd < line.EndIncludingLineBreak) || //Selection ends before the end of line ((line.LineBreakLength == 0) && line.IsLastTextViewLineForSnapshotLine); //Or this is the last line. IList <Tuple <double, double> > leftRightPairs; //Draw the appropriate bits of the selection for virtual space. if (start.Position.Position == line.End.Position) { //The start is at (or beyond) the end of the line (therefore nothing inside the line is selected). double xStart = SkiaTextCaret.GetXCoordinateFromVirtualBufferPosition(line, start); double xEnd; if (isLastLineOfSelection) { //Selection ends on this line as well ... draw a rectangle between them. xEnd = SkiaTextCaret.GetXCoordinateFromVirtualBufferPosition(line, end); } else { //Selection ends on a subsequent line. Show things differently when virtual space is turned on. xEnd = isVirtualSpaceEnabled ? double.MaxValue : (xStart + line.EndOfLineWidth); } leftRightPairs = new FrugalList <Tuple <double, double> > (); if (xEnd > xStart) { leftRightPairs.Add(new Tuple <double, double> (xStart, xEnd)); } } else { //Add the bounds for the text in the line's interior. var bounds = line.GetNormalizedTextBounds(new SnapshotSpan(start.Position, end.Position)); leftRightPairs = new List <Tuple <double, double> > (bounds.Count + 1); foreach (var bound in bounds) { leftRightPairs.Add(new Tuple <double, double> (bound.Left, bound.Right)); } double xEnd = double.MinValue; if (isLastLineOfSelection) { if (end.IsInVirtualSpace) { xEnd = SkiaTextCaret.GetXCoordinateFromVirtualBufferPosition(line, end); } } else if (isVirtualSpaceEnabled) { xEnd = double.MaxValue; } double xStart = line.TextRight; if (xEnd > xStart) { if ((leftRightPairs.Count > 0) && (leftRightPairs [leftRightPairs.Count - 1].Item2 >= xStart)) { xStart = leftRightPairs [leftRightPairs.Count - 1].Item1; leftRightPairs.RemoveAt(leftRightPairs.Count - 1); } leftRightPairs.Add(new Tuple <double, double> (xStart, xEnd)); } } return(leftRightPairs); }
public override ReadOnlyCollection <SnapshotSpan> MapToSourceSnapshotsForRead(Span span) { if (span.End > this.Length) { throw new ArgumentOutOfRangeException("span"); } FrugalList <SnapshotSpan> mappedSpans = new FrugalList <SnapshotSpan>(); if (span.Length == 0) { // First check for a degenerate snapshot having no source spans if (span.Start == 0 && this.sourceSpans.Count == 0) { return(new ReadOnlyCollection <SnapshotSpan>(mappedSpans)); } // Zero-length spans are special in that they may map to more than one zero-length source span. // Defer to the point mapping implementation and then convert back to spans. ReadOnlyCollection <SnapshotPoint> points = MapInsertionPointToSourceSnapshots(span.Start, null); for (int p = 0; p < points.Count; ++p) { SnapshotPoint point = points[p]; SnapshotSpan mappedSpan = new SnapshotSpan(point.Snapshot, point.Position, 0); // avoid duplicates, caused by mapping the null span on a seam between source spans // that come from the same source buffer and are adjacent in that source buffer // Example: source spans are [0..10) and [10..20) from same source buffer, and we // are requested to map the span at the seam, corresponding to [10..10). if (mappedSpans.Count == 0 || mappedSpan != mappedSpans[mappedSpans.Count - 1]) { mappedSpans.Add(mappedSpan); } } } else { int rover = FindHighestSpanIndexOfPosition(span.Start); // sourceSpans[rover] contains span.Start SnapshotSpan sourceSpan = this.sourceSpans[rover]; SnapshotPoint mappedStart = sourceSpan.Start + (span.Start - this.cumulativeLengths[rover]); int mappedLength = mappedStart.Position + span.Length < sourceSpan.End ? span.Length : sourceSpan.End.Position - mappedStart; mappedSpans.Add(new SnapshotSpan(mappedStart, mappedLength)); // walk forward until we cover the entire span while (mappedLength < span.Length) { sourceSpan = this.sourceSpans[++rover]; if (span.End >= this.cumulativeLengths[rover + 1]) { mappedLength += sourceSpan.Length; mappedSpans.Add(sourceSpan); } else { mappedLength += span.End - this.cumulativeLengths[rover]; mappedSpans.Add(new SnapshotSpan(sourceSpan.Snapshot, new Span(sourceSpan.Start, span.End - this.cumulativeLengths[rover]))); } } } return(new ReadOnlyCollection <SnapshotSpan>(mappedSpans)); }
public override ReadOnlyCollection <Span> MapFromSourceSnapshot(SnapshotSpan sourceSpan) { List <InvertedSource> orderedSources; if (!this.sourceSnapshotMap.TryGetValue(sourceSpan.Snapshot, out orderedSources)) { throw new ArgumentException("The span does not belong to a source snapshot of the projection snapshot"); } Span spanToMap = sourceSpan.Span; // binary search for source span containing spanToMap.Start int lo = 0; int hi = orderedSources.Count - 1; int mid = 0; while (lo <= hi) { mid = (lo + hi) / 2; if (spanToMap.Start < orderedSources[mid].sourceSpan.Start) { hi = mid - 1; } else if (spanToMap.Start > orderedSources[mid].sourceSpan.End) { lo = mid + 1; } else { break; // orderedSources[mid].sourceSpan contains (or abuts at the end) sourceSpan.Start } } FrugalList <Span> result = new FrugalList <Span>(); if (spanToMap.Start > orderedSources[mid].sourceSpan.End) { Debug.Assert(hi < lo, "Projection source span search exit invariant violated"); // the binary search failed (hi and lo crossed) because spanToMap.Start did // not intersect any source span. However, it may be that some part of spanToMap will // intersect the next span, so we start our scan one span further along. // another way to think of this: if the binary search failed, mid will designate either the source span // to the left of the gap containing start or to the right of the gap containing start. This case // is where orderedSources[mid] lies to the left of the gap, and we don't want the loop below to blow out on // the first iteration if spanToMap intersects orderedSources[mid+1]. mid++; } for (int rover = mid; rover < orderedSources.Count; ++rover) { Span?s = spanToMap.Intersection(orderedSources[rover].sourceSpan); if (!s.HasValue) { Debug.Assert(orderedSources[rover].sourceSpan.Start > spanToMap.End); break; } if (s.Value.Length > 0 || spanToMap.Length == 0) { result.Add(new Span(orderedSources[rover].projectedPosition + (s.Value.Start - orderedSources[rover].sourceSpan.Start), s.Value.Length)); } } return(new ReadOnlyCollection <Span>(result)); }
/// <summary> /// Map insertion point in projection buffer into set of insertion points in source buffers. The /// result will have only one element unless the insertion is at the boundary of source spans, in which /// case there can be two (or more if empty source spans appear at the insertion location). /// </summary> internal override ReadOnlyCollection <SnapshotPoint> MapInsertionPointToSourceSnapshots(int position, ITextBuffer excludedBuffer) { if (position < 0 || position > this.Length) { throw new ArgumentOutOfRangeException("position"); } int rover = FindLowestSpanIndexOfPosition(position); SnapshotSpan sourceSpan = this.sourceSpans[rover]; if (position < cumulativeLengths[rover + 1]) { // point is not on a seam FrugalList <SnapshotPoint> singleResult = new FrugalList <SnapshotPoint>(); singleResult.Add(sourceSpan.Start + (position - this.cumulativeLengths[rover])); return(new ReadOnlyCollection <SnapshotPoint>(singleResult)); } else { // point is at the boundary of source spans (this includes being at the // very beginning or end of the buffer, but that will work out OK). var sourceInsertionPoints = new FrugalList <SnapshotPoint>(); // include the end point of the source span on the left var firstSnapshotPoint = new SnapshotPoint(sourceSpan.Snapshot, sourceSpan.End); if (sourceSpan.Snapshot.TextBuffer != excludedBuffer) { sourceInsertionPoints.Add(firstSnapshotPoint); } // include all consecutive source spans of zero length (typically there are none of these) while (++rover < this.sourceSpans.Count && this.cumulativeLengths[rover] == this.cumulativeLengths[rover + 1]) { sourceSpan = this.sourceSpans[rover]; if (sourceSpan.Snapshot.TextBuffer != excludedBuffer) { sourceInsertionPoints.Add(new SnapshotPoint(sourceSpan.Snapshot, sourceSpan.Start)); } } // include first nonzero length source span (if any) if (rover < this.sourceSpans.Count) { sourceSpan = this.sourceSpans[rover]; if (sourceSpan.Snapshot.TextBuffer != excludedBuffer) { sourceInsertionPoints.Add(new SnapshotPoint(sourceSpan.Snapshot, sourceSpan.Start)); } } if (sourceInsertionPoints.Count == 0) { // Where position falls in the seam between two (or more if they are 0 length) spans from excludedBuffer and there // are no snapshot points from the "real" buffers. In this case, the best thing to do is simply return the span from // the excluded buffer (which is consistent with our behavior when position falls inside the middle of an excluded span). sourceInsertionPoints.Add(firstSnapshotPoint); } return(new ReadOnlyCollection <SnapshotPoint>(sourceInsertionPoints)); } }