diff --git a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs index 3ee2eb013e056..09100345c3016 100644 --- a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs +++ b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs @@ -270,7 +270,7 @@ public TestNavigateToSearchResult(TestWorkspace workspace, TextSpan sourceSpan) _sourceSpan = sourceSpan; } - public Document Document => _workspace.CurrentSolution.Projects.Single().Documents.Single(); + public INavigableItem.NavigableDocument Document => INavigableItem.NavigableDocument.FromDocument(_workspace.CurrentSolution.Projects.Single().Documents.Single()); public TextSpan SourceSpan => _sourceSpan; public string AdditionalInformation => throw new NotImplementedException(); diff --git a/src/EditorFeatures/Core.Wpf/NavigateTo/DefaultNavigateToPreviewService.cs b/src/EditorFeatures/Core.Wpf/NavigateTo/DefaultNavigateToPreviewService.cs index dab607a0ff8c6..db514260ce543 100644 --- a/src/EditorFeatures/Core.Wpf/NavigateTo/DefaultNavigateToPreviewService.cs +++ b/src/EditorFeatures/Core.Wpf/NavigateTo/DefaultNavigateToPreviewService.cs @@ -4,14 +4,16 @@ #nullable disable +using Microsoft.CodeAnalysis.Navigation; using Microsoft.VisualStudio.Language.NavigateTo.Interfaces; +using Microsoft.VisualStudio.Shell.Interop; namespace Microsoft.CodeAnalysis.Editor.Implementation.NavigateTo { internal sealed class DefaultNavigateToPreviewService : INavigateToPreviewService { - public int GetProvisionalViewingStatus(Document document) - => 0; + public __VSPROVISIONALVIEWINGSTATUS GetProvisionalViewingStatus(INavigableItem.NavigableDocument document) + => __VSPROVISIONALVIEWINGSTATUS.PVS_Disabled; public bool CanPreview(Document document) => true; diff --git a/src/EditorFeatures/Core.Wpf/NavigateTo/INavigateToPreviewService.cs b/src/EditorFeatures/Core.Wpf/NavigateTo/INavigateToPreviewService.cs index 1c80806e78000..caa2ea2b02d8a 100644 --- a/src/EditorFeatures/Core.Wpf/NavigateTo/INavigateToPreviewService.cs +++ b/src/EditorFeatures/Core.Wpf/NavigateTo/INavigateToPreviewService.cs @@ -5,13 +5,15 @@ #nullable disable using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Navigation; using Microsoft.VisualStudio.Language.NavigateTo.Interfaces; +using Microsoft.VisualStudio.Shell.Interop; namespace Microsoft.CodeAnalysis.Editor.Implementation.NavigateTo { internal interface INavigateToPreviewService : IWorkspaceService { - int GetProvisionalViewingStatus(Document document); + __VSPROVISIONALVIEWINGSTATUS GetProvisionalViewingStatus(INavigableItem.NavigableDocument document); bool CanPreview(Document document); void PreviewItem(INavigateToItemDisplay itemDisplay); } diff --git a/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemDisplay.cs b/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemDisplay.cs index af4844355fc48..2cf7dc85588cd 100644 --- a/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemDisplay.cs +++ b/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemDisplay.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Imaging.Interop; using Microsoft.VisualStudio.Language.NavigateTo.Interfaces; +using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; @@ -63,9 +64,6 @@ private ReadOnlyCollection CreateDescriptionItems() return new List().AsReadOnly(); } - var sourceText = document.GetTextSynchronously(CancellationToken.None); - var span = NavigateToUtilities.GetBoundedSpan(_searchResult.NavigableItem, sourceText); - var items = new List { new DescriptionItem( @@ -78,13 +76,19 @@ private ReadOnlyCollection CreateDescriptionItems() new[] { new DescriptionRun("File:", bold: true) }), new ReadOnlyCollection( new[] { new DescriptionRun(document.FilePath ?? document.Name) })), - new DescriptionItem( - new ReadOnlyCollection( - new[] { new DescriptionRun("Line:", bold: true) }), - new ReadOnlyCollection( - new[] { new DescriptionRun((sourceText.Lines.IndexOf(span.Start) + 1).ToString()) })) }; + if (document.TryGetTextSynchronously(document.Workspace.CurrentSolution, CancellationToken.None) is { } sourceText) + { + var span = NavigateToUtilities.GetBoundedSpan(_searchResult.NavigableItem, sourceText); + items.Add( + new DescriptionItem( + new ReadOnlyCollection( + new[] { new DescriptionRun("Line:", bold: true) }), + new ReadOnlyCollection( + new[] { new DescriptionRun((sourceText.Lines.IndexOf(span.Start) + 1).ToString()) }))); + } + var summary = _searchResult.Summary; if (!string.IsNullOrWhiteSpace(summary)) { @@ -111,13 +115,13 @@ public int GetProvisionalViewingStatus() var document = _searchResult.NavigableItem.Document; if (document == null) { - return 0; + return (int)__VSPROVISIONALVIEWINGSTATUS.PVS_Disabled; } - var workspace = document.Project.Solution.Workspace; + var workspace = document.Workspace; var previewService = workspace.Services.GetService(); - return previewService.GetProvisionalViewingStatus(document); + return (int)previewService.GetProvisionalViewingStatus(document); } public void PreviewItem() @@ -128,7 +132,7 @@ public void PreviewItem() return; } - var workspace = document.Project.Solution.Workspace; + var workspace = document.Workspace; var previewService = workspace.Services.GetService(); previewService.PreviewItem(this); diff --git a/src/EditorFeatures/Core.Wpf/Peek/PeekableItemSource.cs b/src/EditorFeatures/Core.Wpf/Peek/PeekableItemSource.cs index 933d0bb334382..51480e02a54fb 100644 --- a/src/EditorFeatures/Core.Wpf/Peek/PeekableItemSource.cs +++ b/src/EditorFeatures/Core.Wpf/Peek/PeekableItemSource.cs @@ -136,7 +136,7 @@ private static async IAsyncEnumerable GetPeekableItemsForNavigabl if (await navigationService.CanNavigateToPositionAsync( workspace, document.Id, item.SourceSpan.Start, cancellationToken).ConfigureAwait(false)) { - var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var text = await document.GetTextAsync(project.Solution, cancellationToken).ConfigureAwait(false); var linePositionSpan = text.Lines.GetLinePositionSpan(item.SourceSpan); if (document.FilePath != null) { diff --git a/src/EditorFeatures/Core/CodeDefinitionWindow/DefinitionContextTracker.cs b/src/EditorFeatures/Core/CodeDefinitionWindow/DefinitionContextTracker.cs index e70161c966b3b..abe65bdde88ef 100644 --- a/src/EditorFeatures/Core/CodeDefinitionWindow/DefinitionContextTracker.cs +++ b/src/EditorFeatures/Core/CodeDefinitionWindow/DefinitionContextTracker.cs @@ -171,7 +171,7 @@ internal async Task> GetContextFrom { if (await navigationService.CanNavigateToSpanAsync(workspace, item.Document.Id, item.SourceSpan, cancellationToken).ConfigureAwait(false)) { - var text = await item.Document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var text = await item.Document.GetTextAsync(document.Project.Solution, cancellationToken).ConfigureAwait(false); var linePositionSpan = text.Lines.GetLinePositionSpan(item.SourceSpan); if (item.Document.FilePath != null) diff --git a/src/EditorFeatures/Core/NavigateTo/NavigateToHelpers.cs b/src/EditorFeatures/Core/NavigateTo/NavigateToHelpers.cs index 905e681c594a6..8147413c8415d 100644 --- a/src/EditorFeatures/Core/NavigateTo/NavigateToHelpers.cs +++ b/src/EditorFeatures/Core/NavigateTo/NavigateToHelpers.cs @@ -35,7 +35,7 @@ private static async Task NavigateToAsync( if (document == null) return; - var workspace = document.Project.Solution.Workspace; + var workspace = document.Workspace; var navigationService = workspace.Services.GetRequiredService(); // Document tabs opened by NavigateTo are carefully created as preview or regular tabs diff --git a/src/EditorFeatures/Test2/CodeDefinitionWindow/CrossLanguageCodeDefinitionWindowTests.vb b/src/EditorFeatures/Test2/CodeDefinitionWindow/CrossLanguageCodeDefinitionWindowTests.vb index 7ff69e2a3dffb..cb77153a73490 100644 --- a/src/EditorFeatures/Test2/CodeDefinitionWindow/CrossLanguageCodeDefinitionWindowTests.vb +++ b/src/EditorFeatures/Test2/CodeDefinitionWindow/CrossLanguageCodeDefinitionWindowTests.vb @@ -22,10 +22,10 @@ Namespace Microsoft.CodeAnalysis.Editor.CodeDefinitionWindow.UnitTests Private Class FakeNavigableItem Implements INavigableItem - Private ReadOnly _document As Document + Private ReadOnly _document As INavigableItem.NavigableDocument Public Sub New(document As Document) - _document = document + _document = INavigableItem.NavigableDocument.FromDocument(document) End Sub Public ReadOnly Property ChildItems As ImmutableArray(Of INavigableItem) Implements INavigableItem.ChildItems @@ -46,7 +46,7 @@ Namespace Microsoft.CodeAnalysis.Editor.CodeDefinitionWindow.UnitTests End Get End Property - Public ReadOnly Property Document As Document Implements INavigableItem.Document + Public ReadOnly Property Document As INavigableItem.NavigableDocument Implements INavigableItem.Document Get Return _document End Get diff --git a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptNavigableItemWrapper.cs b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptNavigableItemWrapper.cs index 17b596a41950b..779960a026bfa 100644 --- a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptNavigableItemWrapper.cs +++ b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptNavigableItemWrapper.cs @@ -12,10 +12,12 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript internal sealed class VSTypeScriptNavigableItemWrapper : INavigableItem { private readonly IVSTypeScriptNavigableItem _navigableItem; + private readonly INavigableItem.NavigableDocument _navigableDocument; public VSTypeScriptNavigableItemWrapper(IVSTypeScriptNavigableItem navigableItem) { _navigableItem = navigableItem; + _navigableDocument = INavigableItem.NavigableDocument.FromDocument(navigableItem.Document); } public Glyph Glyph => _navigableItem.Glyph; @@ -26,7 +28,7 @@ public VSTypeScriptNavigableItemWrapper(IVSTypeScriptNavigableItem navigableItem public bool IsImplicitlyDeclared => _navigableItem.IsImplicitlyDeclared; - public Document Document => _navigableItem.Document; + public INavigableItem.NavigableDocument Document => _navigableDocument; public TextSpan SourceSpan => _navigableItem.SourceSpan; diff --git a/src/Features/Core/Portable/NavigateTo/RoslynNavigateToItem.cs b/src/Features/Core/Portable/NavigateTo/RoslynNavigateToItem.cs index 8a7f21285b884..25215ea1b1093 100644 --- a/src/Features/Core/Portable/NavigateTo/RoslynNavigateToItem.cs +++ b/src/Features/Core/Portable/NavigateTo/RoslynNavigateToItem.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.Serialization; using System.Threading; @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.NavigateTo { @@ -107,12 +106,12 @@ private class NavigateToSearchResult : INavigateToSearchResult, INavigableItem /// /// The that is contained within. /// - private readonly Document _itemDocument; + private readonly INavigableItem.NavigableDocument _itemDocument; /// /// The document the user was editing when they invoked the navigate-to operation. /// - private readonly Document? _activeDocument; + private readonly (DocumentId id, IReadOnlyList folders)? _activeDocument; private readonly string _additionalInformation; private readonly Lazy _secondarySort; @@ -123,33 +122,35 @@ public NavigateToSearchResult( Document? activeDocument) { _item = item; - _itemDocument = itemDocument; - _activeDocument = activeDocument; - _additionalInformation = ComputeAdditionalInformation(); + _itemDocument = INavigableItem.NavigableDocument.FromDocument(itemDocument); + if (activeDocument is not null) + _activeDocument = (activeDocument.Id, activeDocument.Folders); + + _additionalInformation = ComputeAdditionalInformation(in item, itemDocument); _secondarySort = new Lazy(ComputeSecondarySort); } - private string ComputeAdditionalInformation() + private static string ComputeAdditionalInformation(in RoslynNavigateToItem item, Document itemDocument) { // For partial types, state what file they're in so the user can disambiguate the results. - var combinedProjectName = ComputeCombinedProjectName(); - return (_item.DeclaredSymbolInfo.IsPartial, IsNonNestedNamedType()) switch + var combinedProjectName = ComputeCombinedProjectName(in item, itemDocument); + return (item.DeclaredSymbolInfo.IsPartial, IsNonNestedNamedType(in item)) switch { - (true, true) => string.Format(FeaturesResources._0_dash_1, _itemDocument.Name, combinedProjectName), - (true, false) => string.Format(FeaturesResources.in_0_1_2, _item.DeclaredSymbolInfo.ContainerDisplayName, _itemDocument.Name, combinedProjectName), + (true, true) => string.Format(FeaturesResources._0_dash_1, itemDocument.Name, combinedProjectName), + (true, false) => string.Format(FeaturesResources.in_0_1_2, item.DeclaredSymbolInfo.ContainerDisplayName, itemDocument.Name, combinedProjectName), (false, true) => string.Format(FeaturesResources.project_0, combinedProjectName), - (false, false) => string.Format(FeaturesResources.in_0_project_1, _item.DeclaredSymbolInfo.ContainerDisplayName, combinedProjectName), + (false, false) => string.Format(FeaturesResources.in_0_project_1, item.DeclaredSymbolInfo.ContainerDisplayName, combinedProjectName), }; } - private string ComputeCombinedProjectName() + private static string ComputeCombinedProjectName(in RoslynNavigateToItem item, Document itemDocument) { // If there aren't any additional matches in other projects, we don't need to merge anything. - if (_item.AdditionalMatchingProjects.Length > 0) + if (item.AdditionalMatchingProjects.Length > 0) { // First get the simple project name and flavor for the actual project we got a hit in. If we can't // figure this out, we can't create a merged name. - var firstProject = _itemDocument.Project; + var firstProject = itemDocument.Project; var (firstProjectName, firstProjectFlavor) = firstProject.State.NameAndFlavor; if (firstProjectName != null) @@ -162,7 +163,7 @@ private string ComputeCombinedProjectName() // Now, do the same for the other projects where we had a match. As above, if we can't figure out the // simple name/flavor, or if the simple project name doesn't match the simple project name we started // with then we can't merge these. - foreach (var additionalProjectId in _item.AdditionalMatchingProjects) + foreach (var additionalProjectId in item.AdditionalMatchingProjects) { var additionalProject = solution.GetRequiredProject(additionalProjectId); var (projectName, projectFlavor) = additionalProject.State.NameAndFlavor; @@ -178,17 +179,17 @@ private string ComputeCombinedProjectName() } // Couldn't compute a merged project name (or only had one project). Just return the name of hte project itself. - return _itemDocument.Project.Name; + return itemDocument.Project.Name; } string INavigateToSearchResult.AdditionalInformation => _additionalInformation; - private bool IsNonNestedNamedType() - => !_item.DeclaredSymbolInfo.IsNestedType && IsNamedType(); + private static bool IsNonNestedNamedType(in RoslynNavigateToItem item) + => !item.DeclaredSymbolInfo.IsNestedType && IsNamedType(in item); - private bool IsNamedType() + private static bool IsNamedType(in RoslynNavigateToItem item) { - switch (_item.DeclaredSymbolInfo.Kind) + switch (item.DeclaredSymbolInfo.Kind) { case DeclaredSymbolInfoKind.Class: case DeclaredSymbolInfoKind.Record: @@ -243,14 +244,14 @@ private string ComputeSecondarySort() int ComputeFolderDistance() { // No need to compute anything if there is no active document. Consider all documents equal. - if (_activeDocument == null) + if (_activeDocument is not { } activeDocument) return 0; // The result was in the active document, this get highest priority. - if (_activeDocument == _itemDocument) + if (activeDocument.id == _itemDocument.Id) return 0; - var activeFolders = _activeDocument.Folders; + var activeFolders = activeDocument.folders; var itemFolders = _itemDocument.Folders; // see how many folder they have in common. @@ -263,21 +264,21 @@ int ComputeFolderDistance() // Add one more to the result. This way if they share all the same folders that we still return // '1', indicating that this close to, but not as good a match as an exact file match. return activeDiff + itemDiff + 1; - } - - int GetCommonFolderCount() - { - var activeFolders = _activeDocument.Folders; - var itemFolders = _itemDocument.Folders; - var maxCommon = Math.Min(activeFolders.Count, itemFolders.Count); - for (var i = 0; i < maxCommon; i++) + int GetCommonFolderCount() { - if (activeFolders[i] != itemFolders[i]) - return i; - } + var activeFolders = activeDocument.folders; + var itemFolders = _itemDocument.Folders; - return maxCommon; + var maxCommon = Math.Min(activeFolders.Count, itemFolders.Count); + for (var i = 0; i < maxCommon; i++) + { + if (activeFolders[i] != itemFolders[i]) + return i; + } + + return maxCommon; + } } } @@ -353,7 +354,7 @@ ImmutableArray INavigableItem.DisplayTaggedParts /// bool INavigableItem.IsImplicitlyDeclared => false; - Document INavigableItem.Document => _itemDocument; + INavigableItem.NavigableDocument INavigableItem.Document => _itemDocument; TextSpan INavigableItem.SourceSpan => _item.DeclaredSymbolInfo.Span; diff --git a/src/Features/Core/Portable/Navigation/INavigableItem.cs b/src/Features/Core/Portable/Navigation/INavigableItem.cs index 14b31b6a0912b..cc4d24811b6de 100644 --- a/src/Features/Core/Portable/Navigation/INavigableItem.cs +++ b/src/Features/Core/Portable/Navigation/INavigableItem.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - +using System.Collections.Generic; using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Navigation @@ -33,7 +35,7 @@ internal interface INavigableItem /// bool IsImplicitlyDeclared { get; } - Document Document { get; } + NavigableDocument Document { get; } TextSpan SourceSpan { get; } /// @@ -45,5 +47,50 @@ internal interface INavigableItem bool IsStale { get; } ImmutableArray ChildItems { get; } + + public record NavigableDocument(NavigableProject Project, string Name, string? FilePath, IReadOnlyList Folders, DocumentId Id, bool IsSourceGeneratedDocument, Workspace Workspace) + { + public static NavigableDocument FromDocument(Document document) + => new( + NavigableProject.FromProject(document.Project), + document.Name, + document.FilePath, + document.Folders, + document.Id, + IsSourceGeneratedDocument: document is SourceGeneratedDocument, + document.Project.Solution.Workspace); + + /// + /// Get the within which is referenced by + /// this navigable item. The document is required to exist within the solution, e.g. a case where the + /// navigable item was constructed during a Find Symbols operation on the same solution instance. + /// + internal ValueTask GetRequiredDocumentAsync(Solution solution, CancellationToken cancellationToken) + => solution.GetRequiredDocumentAsync(Id, includeSourceGenerated: IsSourceGeneratedDocument, cancellationToken); + + /// + /// Get the of the within + /// which is referenced by this navigable item. The document is required to + /// exist within the solution, e.g. a case where the navigable item was constructed during a Find Symbols + /// operation on the same solution instance. + /// + internal async ValueTask GetTextAsync(Solution solution, CancellationToken cancellationToken) + { + var document = await GetRequiredDocumentAsync(solution, cancellationToken).ConfigureAwait(false); + return await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + } + + internal SourceText? TryGetTextSynchronously(Solution solution, CancellationToken cancellationToken) + { + var document = solution.GetDocument(Id); + return document?.GetTextSynchronously(cancellationToken); + } + } + + public record struct NavigableProject(string Name, ProjectId Id) + { + public static NavigableProject FromProject(Project project) + => new(project.Name, project.Id); + } } } diff --git a/src/Features/Core/Portable/Navigation/NavigableItemFactory.SymbolLocationNavigableItem.cs b/src/Features/Core/Portable/Navigation/NavigableItemFactory.SymbolLocationNavigableItem.cs index afc361db4d749..e8c50745f0684 100644 --- a/src/Features/Core/Portable/Navigation/NavigableItemFactory.SymbolLocationNavigableItem.cs +++ b/src/Features/Core/Portable/Navigation/NavigableItemFactory.SymbolLocationNavigableItem.cs @@ -4,9 +4,12 @@ #nullable disable +using System; using System.Collections.Immutable; +using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Navigation { @@ -18,6 +21,12 @@ private class SymbolLocationNavigableItem : INavigableItem private readonly ISymbol _symbol; private readonly Location _location; + /// + /// Lazily-initialized backing field for . + /// + /// + private StrongBox _lazyDocument; + public SymbolLocationNavigableItem( Solution solution, ISymbol symbol, @@ -38,8 +47,21 @@ public SymbolLocationNavigableItem( public bool IsImplicitlyDeclared => _symbol.IsImplicitlyDeclared; - public Document Document - => _location.IsInSource ? _solution.GetDocument(_location.SourceTree) : null; + public INavigableItem.NavigableDocument Document + { + get + { + return LazyInitialization.EnsureInitialized( + ref _lazyDocument, + static self => + { + return (self._location.IsInSource && self._solution.GetDocument(self._location.SourceTree) is { } document) + ? INavigableItem.NavigableDocument.FromDocument(document) + : null; + }, + this); + } + } public TextSpan SourceSpan => _location.SourceSpan; diff --git a/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs index 4c729a7fcc5dd..7e4c51e8713cb 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs @@ -59,7 +59,10 @@ public AbstractGoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSour } var location = await ProtocolConversions.TextSpanToLocationAsync( - definition.Document, definition.SourceSpan, definition.IsStale, cancellationToken).ConfigureAwait(false); + await definition.Document.GetRequiredDocumentAsync(document.Project.Solution, cancellationToken).ConfigureAwait(false), + definition.SourceSpan, + definition.IsStale, + cancellationToken).ConfigureAwait(false); locations.AddIfNotNull(location); } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Symbols/WorkspaceSymbolsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Symbols/WorkspaceSymbolsHandler.cs index 8d742c720c224..108cb201a8283 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Symbols/WorkspaceSymbolsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Symbols/WorkspaceSymbolsHandler.cs @@ -85,8 +85,10 @@ public LSPNavigateToCallback( public async Task AddItemAsync(Project project, INavigateToSearchResult result, CancellationToken cancellationToken) { + var document = await result.NavigableItem.Document.GetRequiredDocumentAsync(project.Solution, cancellationToken).ConfigureAwait(false); + var location = await ProtocolConversions.TextSpanToLocationAsync( - result.NavigableItem.Document, result.NavigableItem.SourceSpan, result.NavigableItem.IsStale, _context, cancellationToken).ConfigureAwait(false); + document, result.NavigableItem.SourceSpan, result.NavigableItem.IsStale, _context, cancellationToken).ConfigureAwait(false); if (location == null) return; diff --git a/src/Tools/ExternalAccess/FSharp/Internal/Navigation/InternalFSharpNavigableItem.cs b/src/Tools/ExternalAccess/FSharp/Internal/Navigation/InternalFSharpNavigableItem.cs index 158a8d99bb399..48cadf77ebc2e 100644 --- a/src/Tools/ExternalAccess/FSharp/Internal/Navigation/InternalFSharpNavigableItem.cs +++ b/src/Tools/ExternalAccess/FSharp/Internal/Navigation/InternalFSharpNavigableItem.cs @@ -4,6 +4,7 @@ #nullable disable +using System; using System.Collections.Immutable; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Navigation; using Microsoft.CodeAnalysis.Navigation; @@ -13,11 +14,14 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.Navigation { internal class InternalFSharpNavigableItem : INavigableItem { + private readonly INavigableItem.NavigableDocument _navigableDocument; + public InternalFSharpNavigableItem(FSharpNavigableItem item) { Glyph = FSharpGlyphHelpers.ConvertTo(item.Glyph); DisplayTaggedParts = item.DisplayTaggedParts; Document = item.Document; + _navigableDocument = INavigableItem.NavigableDocument.FromDocument(item.Document); SourceSpan = item.SourceSpan; } @@ -31,6 +35,8 @@ public InternalFSharpNavigableItem(FSharpNavigableItem item) public Document Document { get; } + INavigableItem.NavigableDocument INavigableItem.Document => _navigableDocument; + public TextSpan SourceSpan { get; } public bool IsStale => false; diff --git a/src/Tools/ExternalAccess/OmniSharp/GoToDefinition/OmniSharpFindDefinitionService.cs b/src/Tools/ExternalAccess/OmniSharp/GoToDefinition/OmniSharpFindDefinitionService.cs index 156b9e3c75623..43e2d9cd7492a 100644 --- a/src/Tools/ExternalAccess/OmniSharp/GoToDefinition/OmniSharpFindDefinitionService.cs +++ b/src/Tools/ExternalAccess/OmniSharp/GoToDefinition/OmniSharpFindDefinitionService.cs @@ -17,7 +17,10 @@ internal static async Task> FindDefinitio { var service = document.GetRequiredLanguageService(); var result = await service.FindDefinitionsAsync(document, position, cancellationToken).ConfigureAwait(false); - return result.NullToEmpty().SelectAsArray(original => new OmniSharpNavigableItem(original.DisplayTaggedParts, original.Document, original.SourceSpan)); + return await result.NullToEmpty().SelectAsArrayAsync( + async (original, solution, cancellationToken) => new OmniSharpNavigableItem(original.DisplayTaggedParts, await original.Document.GetRequiredDocumentAsync(solution, cancellationToken).ConfigureAwait(false), original.SourceSpan), + document.Project.Solution, + cancellationToken).ConfigureAwait(false); } } } diff --git a/src/Tools/ExternalAccess/OmniSharp/NavigateTo/OmniSharpNavigateToSearchService.cs b/src/Tools/ExternalAccess/OmniSharp/NavigateTo/OmniSharpNavigateToSearchService.cs index fabbeedf56a67..bc68564d8777f 100644 --- a/src/Tools/ExternalAccess/OmniSharp/NavigateTo/OmniSharpNavigateToSearchService.cs +++ b/src/Tools/ExternalAccess/OmniSharp/NavigateTo/OmniSharpNavigateToSearchService.cs @@ -2,10 +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.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Navigation; @@ -45,8 +42,9 @@ public OmniSharpNavigateToCallbackImpl(OmniSharpNavigateToCallback callback) _callback = callback; } - public Task AddItemAsync(Project project, INavigateToSearchResult result, CancellationToken cancellationToken) + public async Task AddItemAsync(Project project, INavigateToSearchResult result, CancellationToken cancellationToken) { + var document = await result.NavigableItem.Document.GetRequiredDocumentAsync(project.Solution, cancellationToken).ConfigureAwait(false); var omniSharpResult = new OmniSharpNavigateToSearchResult( result.AdditionalInformation, result.Kind, @@ -56,9 +54,9 @@ public Task AddItemAsync(Project project, INavigateToSearchResult result, Cancel result.NameMatchSpans, result.SecondarySort, result.Summary!, - new(result.NavigableItem.DisplayTaggedParts, result.NavigableItem.Document, result.NavigableItem.SourceSpan)); + new OmniSharpNavigableItem(result.NavigableItem.DisplayTaggedParts, document, result.NavigableItem.SourceSpan)); - return _callback(project, omniSharpResult, cancellationToken); + await _callback(project, omniSharpResult, cancellationToken).ConfigureAwait(false); } public void Done(bool isFullyLoaded) diff --git a/src/VisualStudio/Core/Def/NavigateTo/VisualStudioNavigateToPreviewService.cs b/src/VisualStudio/Core/Def/NavigateTo/VisualStudioNavigateToPreviewService.cs index 6209b1403c22b..7da1786fa10e5 100644 --- a/src/VisualStudio/Core/Def/NavigateTo/VisualStudioNavigateToPreviewService.cs +++ b/src/VisualStudio/Core/Def/NavigateTo/VisualStudioNavigateToPreviewService.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Implementation.NavigateTo; +using Microsoft.CodeAnalysis.Navigation; using Microsoft.VisualStudio.Language.NavigateTo.Interfaces; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.Implementation.Venus; @@ -16,14 +17,14 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.NavigateTo { internal sealed class VisualStudioNavigateToPreviewService : INavigateToPreviewService { - public int GetProvisionalViewingStatus(Document document) + public __VSPROVISIONALVIEWINGSTATUS GetProvisionalViewingStatus(INavigableItem.NavigableDocument document) { if (document.FilePath == null) { - return (int)__VSPROVISIONALVIEWINGSTATUS.PVS_Disabled; + return __VSPROVISIONALVIEWINGSTATUS.PVS_Disabled; } - return (int)VsShellUtilities.GetProvisionalViewingStatus(document.FilePath); + return (__VSPROVISIONALVIEWINGSTATUS)VsShellUtilities.GetProvisionalViewingStatus(document.FilePath); } public bool CanPreview(Document document) diff --git a/src/VisualStudio/Core/Def/Progression/GraphBuilder.cs b/src/VisualStudio/Core/Def/Progression/GraphBuilder.cs index d4d79d37e6043..22c5e347e8cf8 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphBuilder.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphBuilder.cs @@ -697,9 +697,9 @@ public void AddLink(GraphNode from, GraphCategory category, GraphNode to, Cancel } } - public async Task CreateNodeAsync(INavigateToSearchResult result, CancellationToken cancellationToken) + public async Task CreateNodeAsync(Solution solution, INavigateToSearchResult result, CancellationToken cancellationToken) { - var document = result.NavigableItem.Document; + var document = await result.NavigableItem.Document.GetRequiredDocumentAsync(solution, cancellationToken).ConfigureAwait(false); var project = document.Project; // If it doesn't belong to a document or project we can navigate to, then ignore entirely. @@ -756,7 +756,7 @@ NavigateToItemKind.EnumItem or symbolNode.AddCategory(category); symbolNode[DgmlNodeProperties.Icon] = GetIconString(result.NavigableItem.Glyph); symbolNode[RoslynGraphProperties.ContextDocumentId] = document.Id; - symbolNode[RoslynGraphProperties.ContextProjectId] = project.Id; + symbolNode[RoslynGraphProperties.ContextProjectId] = document.Project.Id; symbolNode[CodeNodeProperties.SourceLocation] = sourceLocation.Value; diff --git a/src/VisualStudio/Core/Def/Progression/GraphQueries/ProgressionNavigateToSearchCallback.cs b/src/VisualStudio/Core/Def/Progression/GraphQueries/ProgressionNavigateToSearchCallback.cs index cb8c92ba51656..54c9dd3b29645 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphQueries/ProgressionNavigateToSearchCallback.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphQueries/ProgressionNavigateToSearchCallback.cs @@ -38,7 +38,7 @@ public void ReportIncomplete() public async Task AddItemAsync(Project project, INavigateToSearchResult result, CancellationToken cancellationToken) { - var node = await _graphBuilder.CreateNodeAsync(result, cancellationToken).ConfigureAwait(false); + var node = await _graphBuilder.CreateNodeAsync(project.Solution, result, cancellationToken).ConfigureAwait(false); if (node != null) { // _context.OutputNodes is not threadsafe. So ensure only one navto callback can mutate it at a time. diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Definitions/GoToDefinitionHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Definitions/GoToDefinitionHandler.cs index 996ffafa02822..c2fab79dbeec5 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Definitions/GoToDefinitionHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Definitions/GoToDefinitionHandler.cs @@ -159,10 +159,12 @@ public GoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSourceFileSe var items = NavigableItemFactory.GetItemsFromPreferredSourceLocations(context.Solution, symbol, displayTaggedParts: null, cancellationToken); if (items.Any()) { + RoslynDebug.AssertNotNull(context.Solution); foreach (var item in items) { + var document = await item.Document.GetRequiredDocumentAsync(context.Solution, cancellationToken).ConfigureAwait(false); var location = await ProtocolConversions.TextSpanToLocationAsync( - item.Document, item.SourceSpan, item.IsStale, cancellationToken).ConfigureAwait(false); + document, item.SourceSpan, item.IsStale, cancellationToken).ConfigureAwait(false); locations.AddIfNotNull(location); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs index a08242af3caab..63763320a91fc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs @@ -48,10 +48,10 @@ public static Document GetRequiredDocument(this Solution solution, DocumentId do => solution.GetDocument(documentId) ?? throw CreateDocumentNotFoundException(); #if !CODE_STYLE - public static async Task GetRequiredDocumentAsync(this Solution solution, DocumentId documentId, bool includeSourceGenerated = false, CancellationToken cancellationToken = default) + public static async ValueTask GetRequiredDocumentAsync(this Solution solution, DocumentId documentId, bool includeSourceGenerated = false, CancellationToken cancellationToken = default) => (await solution.GetDocumentAsync(documentId, includeSourceGenerated, cancellationToken).ConfigureAwait(false)) ?? throw CreateDocumentNotFoundException(); - public static async Task GetRequiredTextDocumentAsync(this Solution solution, DocumentId documentId, CancellationToken cancellationToken = default) + public static async ValueTask GetRequiredTextDocumentAsync(this Solution solution, DocumentId documentId, CancellationToken cancellationToken = default) => (await solution.GetTextDocumentAsync(documentId, cancellationToken).ConfigureAwait(false)) ?? throw CreateDocumentNotFoundException(); #endif