diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs index 3819d7bf0a4a6..c8d8240113c86 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs @@ -36,7 +36,7 @@ internal partial class StreamingFindUsagesPresenter /// contents of that line, and hovering will reveal a tooltip showing that line along /// with a few lines above/below it. /// - private class DocumentSpanEntry : AbstractDocumentSpanEntry, ISupportsNavigation + private sealed class DocumentSpanEntry : AbstractDocumentSpanEntry, ISupportsNavigation { private readonly HighlightSpanKind _spanKind; private readonly ExcerptResult _excerptResult; @@ -280,30 +280,37 @@ private static Span GetRegionSpanForReference(SourceText sourceText, TextSpan so sourceText.Lines[lastLineNumber].End); } - async Task ISupportsNavigation.TryNavigateToAsync(bool isPreview, CancellationToken cancellationToken) + public bool CanNavigateTo() { - // If the document is a source generated document, we need to do the navigation ourselves; - // this is because the file path given to the table control isn't a real file path to a file - // on disk. if (_excerptResult.Document is SourceGeneratedDocument) { var workspace = _excerptResult.Document.Project.Solution.Workspace; var documentNavigationService = workspace.Services.GetService(); - if (documentNavigationService != null) - { - await this.Presenter.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - return documentNavigationService.TryNavigateToSpan( - workspace, - _excerptResult.Document.Id, - _excerptResult.Span, - workspace.Options.WithChangedOption(NavigationOptions.PreferProvisionalTab, isPreview), - cancellationToken); - } + return documentNavigationService != null; } return false; } + + public Task NavigateToAsync(bool isPreview, CancellationToken cancellationToken) + { + Contract.ThrowIfFalse(CanNavigateTo()); + + // If the document is a source generated document, we need to do the navigation ourselves; + // this is because the file path given to the table control isn't a real file path to a file + // on disk. + + var workspace = _excerptResult.Document.Project.Solution.Workspace; + var documentNavigationService = workspace.Services.GetRequiredService(); + + return documentNavigationService.TryNavigateToSpanAsync( + workspace, + _excerptResult.Document.Id, + _excerptResult.Span, + workspace.Options.WithChangedOption(NavigationOptions.PreferProvisionalTab, isPreview), + cancellationToken); + } } } } diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/Entry.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/Entry.cs index c8683248ec7a4..2ab874137c2aa 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/Entry.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/Entry.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; using System.Windows; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Wpf; diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/MetadataDefinitionItemEntry.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/MetadataDefinitionItemEntry.cs index 6895c3a9d8c6f..54cd2dd673991 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/MetadataDefinitionItemEntry.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/MetadataDefinitionItemEntry.cs @@ -34,13 +34,16 @@ public MetadataDefinitionItemEntry( return null; } - Task ISupportsNavigation.TryNavigateToAsync(bool isPreview, CancellationToken cancellationToken) + public bool CanNavigateTo() + => true; + + public Task NavigateToAsync(bool isPreview, CancellationToken cancellationToken) => DefinitionBucket.DefinitionItem.TryNavigateToAsync( Presenter._workspace, showInPreviewTab: isPreview, activateTab: !isPreview, cancellationToken); // Only activate the tab if not opening in preview protected override IList CreateLineTextInlines() => DefinitionBucket.DefinitionItem.DisplayParts - .ToInlines(Presenter.ClassificationFormatMap, Presenter.TypeMap); + .ToInlines(Presenter.ClassificationFormatMap, Presenter.TypeMap); } } } diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/SimpleMessageEntry.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/SimpleMessageEntry.cs index ee68ad11f7bb6..0548a82889cd9 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/SimpleMessageEntry.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/SimpleMessageEntry.cs @@ -5,12 +5,13 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.Shell.TableManager; +using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.FindUsages { internal partial class StreamingFindUsagesPresenter { - private class SimpleMessageEntry : Entry, ISupportsNavigation + private sealed class SimpleMessageEntry : Entry, ISupportsNavigation { private readonly RoslynDefinitionBucket? _navigationBucket; private readonly string _message; @@ -44,8 +45,15 @@ public static Task CreateAsync( }; } - public async Task TryNavigateToAsync(bool isPreview, CancellationToken cancellationToken) - => _navigationBucket != null && await _navigationBucket.TryNavigateToAsync(isPreview, cancellationToken).ConfigureAwait(false); + public bool CanNavigateTo() + => _navigationBucket != null && _navigationBucket.CanNavigateTo(); + + public Task NavigateToAsync(bool isPreview, CancellationToken cancellationToken) + { + Contract.ThrowIfFalse(CanNavigateTo()); + Contract.ThrowIfNull(_navigationBucket); + return _navigationBucket.NavigateToAsync(isPreview, cancellationToken); + } } } } diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/FindReferencesTableControlEventProcessorProvider.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/FindReferencesTableControlEventProcessorProvider.cs index d0f704671f7e8..4c17453d718ba 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/FindReferencesTableControlEventProcessorProvider.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/FindReferencesTableControlEventProcessorProvider.cs @@ -59,16 +59,15 @@ public override void PreprocessNavigate(ITableEntryHandle entry, TableEntryNavig { var supportsNavigation = entry.Identity as ISupportsNavigation ?? (entry.TryGetValue(StreamingFindUsagesPresenter.SelfKeyName, out var item) ? item as ISupportsNavigation : null); - if (supportsNavigation == null) + if (supportsNavigation != null && + supportsNavigation.CanNavigateTo()) { - base.PreprocessNavigate(entry, e); - return; + // Fire and forget + e.Handled = true; + _ = ProcessNavigateAsync(supportsNavigation, e, _listener, _operationExecutor); } - // Fire and forget - e.Handled = true; - _ = ProcessNavigateAsync(supportsNavigation, e, _listener, _operationExecutor); - + base.PreprocessNavigate(entry, e); return; async static Task ProcessNavigateAsync( @@ -83,7 +82,7 @@ async static Task ProcessNavigateAsync( allowCancellation: true, showProgress: false); - await supportsNavigation.TryNavigateToAsync(e.IsPreview, context.UserCancellationToken).ConfigureAwait(false); + await supportsNavigation.NavigateToAsync(e.IsPreview, 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 e447dc9745b7c..7b9cac4600b7c 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/ISupportsNavigation.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/ISupportsNavigation.cs @@ -9,6 +9,7 @@ namespace Microsoft.VisualStudio.LanguageServices.FindUsages { internal interface ISupportsNavigation { - Task TryNavigateToAsync(bool isPreview, CancellationToken cancellationToken); + bool CanNavigateTo(); + Task NavigateToAsync(bool isPreview, CancellationToken cancellationToken); } } diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs index 1f7e02bfaefae..850970c700cb1 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs @@ -63,7 +63,10 @@ public static RoslynDefinitionBucket Create( name, expandedByDefault, presenter, context, definitionItem); } - public Task TryNavigateToAsync(bool isPreview, CancellationToken cancellationToken) + public bool CanNavigateTo() + => true; + + public Task NavigateToAsync(bool isPreview, CancellationToken cancellationToken) => DefinitionItem.TryNavigateToAsync( _presenter._workspace, showInPreviewTab: isPreview, activateTab: !isPreview, cancellationToken); // Only activate the tab if not opening in preview