File: Workspaces\ITextBufferVisibilityTracker.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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
 
namespace Microsoft.CodeAnalysis.Workspaces
{
    /// <summary>
    /// All methods must be called on UI thread.
    /// </summary>
    internal interface ITextBufferVisibilityTracker
    {
        /// <summary>
        /// Whether or not this text buffer is in an actively visible <see cref="ITextView"/>.
        /// </summary>
        bool IsVisible(ITextBuffer subjectBuffer);
 
        /// <summary>
        /// Registers to hear about visibility changes for this particular buffer.  Note: registration will not trigger
        /// a call to <paramref name="callback"/>.  If clients need that information, they should check the <see
        /// cref="IsVisible"/> state of the <paramref name="subjectBuffer"/> themselves.
        /// </summary>
        void RegisterForVisibilityChanges(ITextBuffer subjectBuffer, Action callback);
 
        /// <summary>
        /// Unregister equivalent of <see cref="RegisterForVisibilityChanges"/>.
        /// </summary>
        void UnregisterForVisibilityChanges(ITextBuffer subjectBuffer, Action callback);
    }
 
    internal static class ITextBufferVisibilityTrackerExtensions
    {
        /// <summary>
        /// Waits the specified amount of time while the specified <paramref name="subjectBuffer"/> is not visible.  If any
        /// document visibility changes happen, the delay will cancel.
        /// </summary>
        public static async Task DelayWhileNonVisibleAsync(
            this ITextBufferVisibilityTracker? service,
            IThreadingContext threadingContext,
            ITextBuffer subjectBuffer,
            TimeSpan timeSpan,
            CancellationToken cancellationToken)
        {
            // Only add a delay if we have access to a service that will tell us when the buffer become visible or not.
            if (service is null)
                return;
 
            await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
            if (service.IsVisible(subjectBuffer))
                return;
 
            // ensure we listen for visibility changes before checking.  That way we don't have a race where we check
            // something see it is not visible, but then do not hear about its visibility change because we've hooked up
            // our event after that happens.
            var visibilityChangedTaskSource = new TaskCompletionSource<bool>();
            var callback = void () => visibilityChangedTaskSource.TrySetResult(true);
            service.RegisterForVisibilityChanges(subjectBuffer, callback);
 
            try
            {
                // Listen to when the active document changed so that we startup work on a document once it becomes visible.
                var delayTask = Task.Delay(timeSpan, cancellationToken);
                await Task.WhenAny(delayTask, visibilityChangedTaskSource.Task).ConfigureAwait(false);
            }
            finally
            {
                await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
                service.UnregisterForVisibilityChanges(subjectBuffer, callback);
            }
        }
    }
}