From 761df5e683fda975a25e6c1bd3dbf211be50507b Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 16 Nov 2021 09:59:22 -0800 Subject: [PATCH 1/2] Properly support TableEntryNavigateEventArgs.ShouldActivate --- .../FindReferences/Entries/DocumentSpanEntry.cs | 6 ++++-- .../FindReferences/Entries/MetadataDefinitionItemEntry.cs | 4 ++-- .../FindReferences/Entries/SimpleMessageEntry.cs | 4 ++-- .../FindReferencesTableControlEventProcessorProvider.cs | 2 +- .../Implementation/FindReferences/ISupportsNavigation.cs | 2 +- .../Implementation/FindReferences/RoslynDefinitionBucket.cs | 4 ++-- .../IntegrationTests/CSharp/CSharpFindReferences.cs | 3 +-- .../IntegrationTests/CSharp/CSharpSourceGenerators.cs | 2 +- .../IntegrationTests/VisualBasic/BasicFindReferences.cs | 3 +-- .../TestUtilities/InProcess/FindReferencesWindow_InProc.cs | 4 ++-- .../OutOfProcess/FindReferencesWindow_OutOfProc.cs | 4 ++-- 11 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs index 35a129100e4bb..13aec63e24620 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs @@ -293,7 +293,7 @@ public bool CanNavigateTo() return false; } - public Task NavigateToAsync(bool isPreview, CancellationToken cancellationToken) + public Task NavigateToAsync(bool isPreview, bool shouldActivate, CancellationToken cancellationToken) { Contract.ThrowIfFalse(CanNavigateTo()); @@ -309,7 +309,9 @@ public Task NavigateToAsync(bool isPreview, CancellationToken cancellationToken) workspace, _excerptResult.Document.Id, _excerptResult.Span, - solution.Options.WithChangedOption(NavigationOptions.PreferProvisionalTab, isPreview), + solution.Options + .WithChangedOption(NavigationOptions.PreferProvisionalTab, isPreview) + .WithChangedOption(NavigationOptions.ActivateTab, shouldActivate), cancellationToken); } } diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/MetadataDefinitionItemEntry.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/MetadataDefinitionItemEntry.cs index 54cd2dd673991..e7c399d4c693b 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/MetadataDefinitionItemEntry.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/MetadataDefinitionItemEntry.cs @@ -37,9 +37,9 @@ public MetadataDefinitionItemEntry( public bool CanNavigateTo() => true; - public Task NavigateToAsync(bool isPreview, CancellationToken cancellationToken) + public Task NavigateToAsync(bool isPreview, bool shouldActivate, CancellationToken cancellationToken) => DefinitionBucket.DefinitionItem.TryNavigateToAsync( - Presenter._workspace, showInPreviewTab: isPreview, activateTab: !isPreview, cancellationToken); // Only activate the tab if not opening in preview + Presenter._workspace, showInPreviewTab: isPreview, activateTab: shouldActivate, cancellationToken); // Only activate the tab if requested protected override IList CreateLineTextInlines() => DefinitionBucket.DefinitionItem.DisplayParts diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/SimpleMessageEntry.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/SimpleMessageEntry.cs index 0548a82889cd9..60f63b59c3ad2 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/SimpleMessageEntry.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/SimpleMessageEntry.cs @@ -48,11 +48,11 @@ public static Task CreateAsync( public bool CanNavigateTo() => _navigationBucket != null && _navigationBucket.CanNavigateTo(); - public Task NavigateToAsync(bool isPreview, CancellationToken cancellationToken) + public Task NavigateToAsync(bool isPreview, bool shouldActivate, CancellationToken cancellationToken) { Contract.ThrowIfFalse(CanNavigateTo()); Contract.ThrowIfNull(_navigationBucket); - return _navigationBucket.NavigateToAsync(isPreview, cancellationToken); + return _navigationBucket.NavigateToAsync(isPreview, shouldActivate, cancellationToken); } } } diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/FindReferencesTableControlEventProcessorProvider.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/FindReferencesTableControlEventProcessorProvider.cs index 4c17453d718ba..a2cc4b1f8e61e 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/FindReferencesTableControlEventProcessorProvider.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/FindReferencesTableControlEventProcessorProvider.cs @@ -82,7 +82,7 @@ async static Task ProcessNavigateAsync( allowCancellation: true, showProgress: false); - await supportsNavigation.NavigateToAsync(e.IsPreview, context.UserCancellationToken).ConfigureAwait(false); + await supportsNavigation.NavigateToAsync(e.IsPreview, e.ShouldActivate, context.UserCancellationToken).ConfigureAwait(false); } } } diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/ISupportsNavigation.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/ISupportsNavigation.cs index 7b9cac4600b7c..d0c1b69592a85 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/ISupportsNavigation.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/ISupportsNavigation.cs @@ -10,6 +10,6 @@ namespace Microsoft.VisualStudio.LanguageServices.FindUsages internal interface ISupportsNavigation { bool CanNavigateTo(); - Task NavigateToAsync(bool isPreview, CancellationToken cancellationToken); + Task NavigateToAsync(bool isPreview, bool shouldActivate, CancellationToken cancellationToken); } } diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs index 850970c700cb1..5ab3fbb6bf43e 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs @@ -66,9 +66,9 @@ public static RoslynDefinitionBucket Create( public bool CanNavigateTo() => true; - public Task NavigateToAsync(bool isPreview, CancellationToken cancellationToken) + public Task NavigateToAsync(bool isPreview, bool shouldActivate, CancellationToken cancellationToken) => DefinitionItem.TryNavigateToAsync( - _presenter._workspace, showInPreviewTab: isPreview, activateTab: !isPreview, cancellationToken); // Only activate the tab if not opening in preview + _presenter._workspace, showInPreviewTab: isPreview, activateTab: shouldActivate, cancellationToken); // Only activate the tab if requested public override bool TryGetValue(string key, out object? content) { diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpFindReferences.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpFindReferences.cs index 257008c6ad004..cdf2e472407f4 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpFindReferences.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpFindReferences.cs @@ -75,9 +75,8 @@ void M() } }); - VisualStudio.FindReferencesWindow.NavigateTo(activeWindowCaption, results[0], isPreview: false); + VisualStudio.FindReferencesWindow.NavigateTo(activeWindowCaption, results[0], isPreview: false, shouldActivate: true); // Assert we are in the right file now - VisualStudio.Editor.Activate(); Assert.Equal("Class1.cs", VisualStudio.Shell.GetActiveWindowCaption()); Assert.Equal("Program", VisualStudio.Editor.GetLineTextAfterCaret()); } diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpSourceGenerators.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpSourceGenerators.cs index b1c4d35bdfe28..f23cb4166d768 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpSourceGenerators.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpSourceGenerators.cs @@ -123,7 +123,7 @@ public static void Main() var programReferencesCaption = $"'{HelloWorldGenerator.GeneratedEnglishClassName}' references"; var results = VisualStudio.FindReferencesWindow.GetContents(programReferencesCaption); var referenceInGeneratedFile = results.Single(r => r.Code.Contains("")); - VisualStudio.FindReferencesWindow.NavigateTo(programReferencesCaption, referenceInGeneratedFile, isPreview: isPreview); + VisualStudio.FindReferencesWindow.NavigateTo(programReferencesCaption, referenceInGeneratedFile, isPreview: isPreview, shouldActivate: true); // Assert we are in the right file now Assert.Equal($"{HelloWorldGenerator.GeneratedEnglishClassName}.cs {ServicesVSResources.generated_suffix}", VisualStudio.Shell.GetActiveWindowCaption()); diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicFindReferences.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicFindReferences.cs index 9fa3833be6bcc..0f95394e6baca 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicFindReferences.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicFindReferences.cs @@ -110,9 +110,8 @@ End Class } }); - VisualStudio.FindReferencesWindow.NavigateTo(activeWindowCaption, results[0], isPreview: false); + VisualStudio.FindReferencesWindow.NavigateTo(activeWindowCaption, results[0], isPreview: false, shouldActivate: true); // Assert we are in the right file now - VisualStudio.Editor.Activate(); Assert.Equal("Class1.vb", VisualStudio.Shell.GetActiveWindowCaption()); Assert.Equal("Alpha As Int32", VisualStudio.Editor.GetLineTextAfterCaret()); } diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/FindReferencesWindow_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/FindReferencesWindow_InProc.cs index 4e648b6deea9d..2676a3eaad7da 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/FindReferencesWindow_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/FindReferencesWindow_InProc.cs @@ -53,7 +53,7 @@ public Reference[] GetContents(string windowCaption) }); } - public void NavigateTo(string windowCaption, Reference reference, bool isPreview) + public void NavigateTo(string windowCaption, Reference reference, bool isPreview, bool shouldActivate) { InvokeOnUIThread(cancellationToken => { @@ -63,7 +63,7 @@ public void NavigateTo(string windowCaption, Reference reference, bool isPreview { if (reference.Equals(CreateReference(item))) { - item.NavigateTo(isPreview); + item.NavigateTo(isPreview, shouldActivate); } } }); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/FindReferencesWindow_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/FindReferencesWindow_OutOfProc.cs index 7e16a73f79d6d..b93c44b0b03fe 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/FindReferencesWindow_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/FindReferencesWindow_OutOfProc.cs @@ -39,9 +39,9 @@ public Reference[] GetContents(string windowCaption) return _inProc.GetContents(windowCaption); } - public void NavigateTo(string windowCaption, Reference reference, bool isPreview) + public void NavigateTo(string windowCaption, Reference reference, bool isPreview, bool shouldActivate) { - _inProc.NavigateTo(windowCaption, reference, isPreview); + _inProc.NavigateTo(windowCaption, reference, isPreview, shouldActivate); } } } From fcd52eb42cf0b375011c9944c68ca5ead5eac972 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 19 Nov 2021 17:30:54 -0800 Subject: [PATCH 2/2] Wait for navigation operations to complete --- .../TestUtilities/InProcess/Editor_InProc.cs | 27 ++++++++++++++++++- .../OutOfProcess/Editor_OutOfProc.cs | 4 +++ .../FindReferencesWindow_OutOfProc.cs | 10 +++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs index 50bfd857206a8..faca11b3b878f 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs @@ -18,10 +18,13 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Implementation.Highlighting; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.UnitTests; +using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.IntegrationTest.Utilities.Common; using Microsoft.VisualStudio.IntegrationTest.Utilities.Input; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion; +using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Classification; @@ -29,10 +32,10 @@ using Microsoft.VisualStudio.Text.Outlining; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; using UIAutomationClient; -using Xunit; using ThreadHelper = Microsoft.VisualStudio.Shell.ThreadHelper; namespace Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess @@ -820,5 +823,27 @@ public void SendExplicitFocus() var view = GetActiveVsTextView(); view.SendExplicitFocus(); }); + + public void WaitForEditorOperations(TimeSpan timeout) + { + var joinableTaskCollection = InvokeOnUIThread(cancellationToken => + { + var shell = GetGlobalService(); + if (shell.IsPackageLoaded(DefGuidList.guidEditorPkg, out var editorPackage) == VSConstants.S_OK) + { + var asyncPackage = (AsyncPackage)editorPackage; + var collection = asyncPackage.GetPropertyValue("JoinableTaskCollection"); + return collection; + } + + return null; + }); + + if (joinableTaskCollection is not null) + { + using var cts = new CancellationTokenSource(timeout); + joinableTaskCollection.JoinTillEmptyAsync(cts.Token).Wait(cts.Token); + } + } } } diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs index e8feb704368de..85bd06a3edcff 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs @@ -2,6 +2,7 @@ // 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.Collections.Immutable; using System.Linq; @@ -377,5 +378,8 @@ public void GoToImplementation(string expectedWindowName) public void SendExplicitFocus() => _editorInProc.SendExplicitFocus(); + + public void WaitForEditorOperations(TimeSpan timeout) + => _editorInProc.WaitForEditorOperations(timeout); } } diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/FindReferencesWindow_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/FindReferencesWindow_OutOfProc.cs index b93c44b0b03fe..53a9649a69fe5 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/FindReferencesWindow_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/FindReferencesWindow_OutOfProc.cs @@ -42,6 +42,16 @@ public Reference[] GetContents(string windowCaption) public void NavigateTo(string windowCaption, Reference reference, bool isPreview, bool shouldActivate) { _inProc.NavigateTo(windowCaption, reference, isPreview, shouldActivate); + WaitForNavigate(); + } + + private void WaitForNavigate() + { + // Navigation operations handled by Roslyn are tracked by FeatureAttribute.FindReferences + VisualStudioInstance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.FindReferences); + + // Navigation operations handled by the editor are tracked within its own JoinableTaskFactory instance + VisualStudioInstance.Editor.WaitForEditorOperations(Helper.HangMitigatingTimeout); } } }