File: BracePairs\BracePairsTaggerProvider.cs
Web Access
Project: ..\..\..\src\EditorFeatures\Core\Microsoft.CodeAnalysis.EditorFeatures.csproj (Microsoft.CodeAnalysis.EditorFeatures)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Tagging;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Editor.Tagging;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.CodeAnalysis.Workspaces;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Tagging;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.BracePairs
{
#pragma warning disable CS0618 // IBracePairTag is obsolete while editor works on this API.
    [Export(typeof(IViewTaggerProvider))]
    [VisualStudio.Utilities.Name(nameof(BracePairsTaggerProvider))]
    [VisualStudio.Utilities.ContentType(ContentTypeNames.RoslynContentType)]
    [TagType(typeof(IBracePairTag))]
    internal sealed class BracePairsTaggerProvider : AsynchronousViewTaggerProvider<IBracePairTag>
    {
        [ImportingConstructor]
        [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
        public BracePairsTaggerProvider(
            IThreadingContext threadingContext,
            IGlobalOptionService globalOptionService,
            [Import(AllowDefault = true)] ITextBufferVisibilityTracker? visibilityTracker,
            IAsynchronousOperationListenerProvider listenerProvider)
            : base(threadingContext,
                  globalOptionService,
                  visibilityTracker,
                  listenerProvider.GetListener(FeatureAttribute.BracePairs))
        {
        }
 
        protected override TaggerDelay EventChangeDelay => TaggerDelay.NearImmediate;
 
        protected override ITaggerEventSource CreateEventSource(ITextView? textView, ITextBuffer subjectBuffer)
        {
            return TaggerEventSources.Compose(
                TaggerEventSources.OnTextChanged(subjectBuffer),
                TaggerEventSources.OnParseOptionChanged(subjectBuffer));
        }
 
        protected override IEnumerable<SnapshotSpan> GetSpansToTag(ITextView? textView, ITextBuffer subjectBuffer)
        {
            this.ThreadingContext.ThrowIfNotOnUIThread();
            Contract.ThrowIfNull(textView);
 
            // Find the visible span some 100 lines +/- what's actually in view.  This way
            // if the user scrolls up/down, we'll already have the results.
            var visibleSpanOpt = textView.GetVisibleLinesSpan(subjectBuffer, extraLines: 100);
            if (visibleSpanOpt == null)
            {
                // Couldn't find anything visible, just fall back to tagging all brace locations
                return base.GetSpansToTag(textView, subjectBuffer);
            }
 
            return SpecializedCollections.SingletonEnumerable(visibleSpanOpt.Value);
        }
 
        protected override async Task ProduceTagsAsync(TaggerContext<IBracePairTag> context, CancellationToken cancellationToken)
        {
            using var _ = ArrayBuilder<BracePairData>.GetInstance(out var bracePairs);
            foreach (var spanToTag in context.SpansToTag)
            {
                var document = spanToTag.Document;
                if (document is null)
                    continue;
 
                var service = document.GetLanguageService<IBracePairsService>();
                if (service is null)
                    continue;
 
                bracePairs.Clear();
                await service.AddBracePairsAsync(
                    document, spanToTag.SnapshotSpan.Span.ToTextSpan(), bracePairs, cancellationToken).ConfigureAwait(false);
 
                var snapshot = spanToTag.SnapshotSpan.Snapshot;
                foreach (var bracePair in bracePairs)
                {
                    var start = CreateSnapshotSpan(bracePair.Start, snapshot);
                    var end = CreateSnapshotSpan(bracePair.End, snapshot);
                    if (start is null && end is null)
                        continue;
 
                    context.AddTag(new TagSpan<IBracePairTag>(
                        new SnapshotSpan(snapshot, Span.FromBounds(bracePair.Start.Start, bracePair.End.End)),
                        new BracePairTag(start, end)));
                }
            }
 
            return;
 
            static SnapshotSpan? CreateSnapshotSpan(TextSpan span, ITextSnapshot snapshot)
                => span.IsEmpty ? null : span.ToSnapshotSpan(snapshot);
        }
 
        protected override bool TagEquals(IBracePairTag tag1, IBracePairTag tag2)
        {
            if (tag1 is null && tag2 is null)
                return true;
 
            if (tag1 is null || tag2 is null)
                return false;
 
            return SpanEquals(tag1.Start, tag2.Start) &&
                   SpanEquals(tag1.End, tag2.End);
        }
    }
#pragma warning restore CS0618 // Type or member is obsolete
}