|
// 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.Tagging;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Tagging;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Threading;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Tagging
{
/// <summary>
/// Tagger event that fires once the compilation is available in the remote OOP process for a particular project.
/// Used to trigger things such as:
/// <list type="bullet">
/// <item>reclassification pass as classification may show either cached classifications (from a previous session),
/// or incomplete classifications due to frozen-partial compilations being used.</item>
/// <item>recomputation of navigation bar items due to frozen-partial compilations being used.</item>
/// <item>recomputation of inheritance margin items due to frozen-partial compilations being used.</item>
/// </list>
/// </summary>
internal sealed class CompilationAvailableTaggerEventSource : ITaggerEventSource
{
private readonly ITextBuffer _subjectBuffer;
/// <summary>
/// Other event sources we're composing over. If they fire, we should reclassify. However, after they fire, we
/// should also refire an event once we get the next full compilation ready.
/// </summary>
private readonly ITaggerEventSource _underlyingSource;
private readonly CompilationAvailableEventSource _eventSource;
private readonly Action _onCompilationAvailable;
public CompilationAvailableTaggerEventSource(
ITextBuffer subjectBuffer,
IAsynchronousOperationListener asyncListener,
params ITaggerEventSource[] eventSources)
{
_subjectBuffer = subjectBuffer;
_eventSource = new CompilationAvailableEventSource(asyncListener);
_underlyingSource = TaggerEventSources.Compose(eventSources);
_onCompilationAvailable = () => this.Changed?.Invoke(this, TaggerEventArgs.Empty);
}
public event EventHandler<TaggerEventArgs>? Changed;
public void Connect()
{
// When we are connected to, connect to all our underlying sources and have them notify us when they've changed.
_underlyingSource.Connect();
_underlyingSource.Changed += OnUnderlyingSourceChanged;
}
public void Disconnect()
{
_underlyingSource.Changed -= OnUnderlyingSourceChanged;
_underlyingSource.Disconnect();
_eventSource.Dispose();
}
public void Pause()
=> _underlyingSource.Pause();
public void Resume()
=> _underlyingSource.Resume();
private void OnUnderlyingSourceChanged(object? sender, TaggerEventArgs args)
{
// First, notify anyone listening to us that something definitely changed.
this.Changed?.Invoke(this, args);
var document = _subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
if (document == null)
return;
_eventSource.EnsureCompilationAvailability(document.Project, _onCompilationAvailable);
}
}
}
|