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);
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        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));
        }
Exemple #5
0
        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);
                    }
                }
            }
        }
Exemple #6
0
        public void AddOne()
        {
            var list = new FrugalList <int>();

            list.Add(42);
            AssertList(list, 42);
        }
Exemple #7
0
        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);
            }
Exemple #9
0
        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());
        }
Exemple #10
0
        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());
            }
        }
Exemple #11
0
        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));
        }
Exemple #12
0
        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);
                        }
                    }
                }
            }
        }
Exemple #13
0
        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);
        }
Exemple #14
0
        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);
        }
Exemple #15
0
        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));
        }
Exemple #16
0
        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);
        }
Exemple #17
0
        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)));
        }
Exemple #18
0
        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++;
            }
        }
Exemple #20
0
        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);
        }
Exemple #21
0
        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);
        }
Exemple #22
0
        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);
        }
Exemple #24
0
        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);
        }
Exemple #26
0
        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));
        }
Exemple #27
0
        /// <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);
        }
Exemple #28
0
        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));
        }
Exemple #29
0
        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));
        }
Exemple #30
0
        /// <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));
            }
        }