diff --git a/src/VisualStudio/Core/Def/TaskList/VisualStudioTaskListService.cs b/src/VisualStudio/Core/Def/TaskList/VisualStudioTaskListService.cs index 063a95ab92c1b..70819f274f888 100644 --- a/src/VisualStudio/Core/Def/TaskList/VisualStudioTaskListService.cs +++ b/src/VisualStudio/Core/Def/TaskList/VisualStudioTaskListService.cs @@ -18,8 +18,10 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.TaskList; -using Microsoft.VisualStudio.LanguageServices.ExternalAccess.VSTypeScript.Api; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using TaskListItem = Microsoft.CodeAnalysis.TaskList.TaskListItem; namespace Microsoft.VisualStudio.LanguageServices.TaskList { @@ -30,6 +32,7 @@ internal class VisualStudioTaskListService : { private readonly IThreadingContext _threadingContext; private readonly VisualStudioWorkspaceImpl _workspace; + private readonly IAsyncServiceProvider _asyncServiceProvider; private readonly EventListenerTracker _eventListenerTracker; private readonly TaskListListener _listener; @@ -42,10 +45,12 @@ public VisualStudioTaskListService( VisualStudioWorkspaceImpl workspace, IGlobalOptionService globalOptions, IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, + SVsServiceProvider asyncServiceProvider, [ImportMany] IEnumerable> eventListeners) { _threadingContext = threadingContext; _workspace = workspace; + _asyncServiceProvider = (IAsyncServiceProvider)asyncServiceProvider; _eventListenerTracker = new EventListenerTracker(eventListeners, WellKnownEventListeners.TaskListProvider); _listener = new TaskListListener( @@ -77,6 +82,12 @@ private async Task StartAsync(Workspace workspace) var workspaceStatus = workspace.Services.GetRequiredService(); await workspaceStatus.WaitUntilFullyLoadedAsync(_threadingContext.DisposalToken).ConfigureAwait(false); + // Wait until the task list is actually visible so that we don't perform pointless work analyzing files + // when the user would not even see the results. When we actually do register the analyer (in + // _listener.Start below), solution-crawler will reanalyze everything with this analayzer, so it will + // still find and present all the relevant items to the user. + await WaitUntilTaskListActivatedAsync().ConfigureAwait(false); + // Now that we've started, let the VS todo list know to start listening to us _eventListenerTracker.EnsureEventListener(_workspace, this); @@ -93,6 +104,36 @@ private async Task StartAsync(Workspace workspace) } } + private async Task WaitUntilTaskListActivatedAsync() + { + var cancellationToken = _threadingContext.DisposalToken; + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + var taskList = await _asyncServiceProvider.GetServiceAsync(_threadingContext.JoinableTaskFactory).ConfigureAwait(true); + + var control = taskList.TableControl.Control; + + // if control is already visible, we can proceed to collect task list items. + if (control.IsVisible) + return; + + // otherwise, wait for it to become visible. + var taskSource = new TaskCompletionSource(); + control.IsVisibleChanged += Control_IsVisibleChanged; + + await taskSource.Task.ConfigureAwait(false); + + return; + + void Control_IsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e) + { + if (control.IsVisible) + { + control.IsVisibleChanged -= Control_IsVisibleChanged; + taskSource.TrySetResult(true); + } + } + } + public ImmutableArray GetTaskListItems(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) => _listener.GetTaskListItems(documentId); }