File: InlineRename\Taggers\AbstractRenameTagger.cs
Web Access
Project: src\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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Tagging;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename;
 
internal abstract class AbstractRenameTagger<T> : ITagger<T>, IDisposable where T : ITag
{
    private readonly ITextBuffer _buffer;
    private readonly InlineRenameService _renameService;
 
    private InlineRenameSession.OpenTextBufferManager _bufferManager;
    private IEnumerable<RenameTrackingSpan> _currentSpans;
 
    protected AbstractRenameTagger(ITextBuffer buffer, InlineRenameService renameService)
    {
        _buffer = buffer;
        _renameService = renameService;
 
        _renameService.ActiveSessionChanged += OnActiveSessionChanged;
 
        if (_renameService.ActiveSession != null)
        {
            AttachToSession(_renameService.ActiveSession);
        }
    }
 
    private void OnActiveSessionChanged(object sender, InlineRenameService.ActiveSessionChangedEventArgs e)
    {
        if (e.PreviousSession != null)
        {
            DetachFromSession();
        }
 
        if (_renameService.ActiveSession != null)
        {
            AttachToSession(_renameService.ActiveSession);
        }
    }
 
    private void AttachToSession(InlineRenameSession session)
    {
        if (session.TryGetBufferManager(_buffer, out _bufferManager))
        {
            _bufferManager.SpansChanged += OnSpansChanged;
            OnSpansChanged();
        }
    }
 
    private void DetachFromSession()
    {
        if (_bufferManager != null)
        {
            RaiseTagsChangedForEntireBuffer();
 
            _bufferManager.SpansChanged -= OnSpansChanged;
            _bufferManager = null;
            _currentSpans = null;
        }
    }
 
    private void OnSpansChanged()
    {
        _currentSpans = _bufferManager.GetRenameTrackingSpans();
        RaiseTagsChangedForEntireBuffer();
    }
 
    private void RaiseTagsChangedForEntireBuffer()
        => TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(_buffer.CurrentSnapshot.GetFullSpan()));
 
    public void Dispose()
    {
        _renameService.ActiveSessionChanged -= OnActiveSessionChanged;
 
        if (_renameService.ActiveSession != null)
        {
            DetachFromSession();
        }
    }
 
    public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
 
    IEnumerable<ITagSpan<T>> ITagger<T>.GetTags(NormalizedSnapshotSpanCollection spans)
        => GetTags(spans);
 
    public IEnumerable<TagSpan<T>> GetTags(NormalizedSnapshotSpanCollection spans)
    {
        if (_renameService.ActiveSession == null)
        {
            yield break;
        }
 
        var renameSpans = _currentSpans;
        if (renameSpans != null)
        {
            var snapshot = spans.First().Snapshot;
            foreach (var renameSpan in renameSpans)
            {
                var span = renameSpan.TrackingSpan.GetSpan(snapshot);
                if (spans.OverlapsWith(span))
                {
                    if (TryCreateTagSpan(span, renameSpan.Type, out var tagSpan))
                    {
                        yield return tagSpan;
                    }
                }
            }
        }
    }
 
    protected abstract bool TryCreateTagSpan(SnapshotSpan span, RenameSpanKind type, out TagSpan<T> tagSpan);
}