Exemplo n.º 1
0
        public bool TryNavigateToSymbol(ISymbol symbol, Project project, NavigationOptions options, CancellationToken cancellationToken)
        {
            if (project == null || symbol == null)
            {
                return(false);
            }

            symbol = symbol.OriginalDefinition;

            // Prefer visible source locations if possible.
            var sourceLocations        = symbol.Locations.Where(loc => loc.IsInSource);
            var visibleSourceLocations = sourceLocations.Where(loc => loc.IsVisibleSourceLocation());
            var sourceLocation         = visibleSourceLocations.FirstOrDefault() ?? sourceLocations.FirstOrDefault();

            if (sourceLocation != null)
            {
                var targetDocument = project.Solution.GetDocument(sourceLocation.SourceTree);
                if (targetDocument != null)
                {
                    var editorWorkspace   = targetDocument.Project.Solution.Workspace;
                    var navigationService = editorWorkspace.Services.GetRequiredService <IDocumentNavigationService>();
                    return(navigationService.TryNavigateToSpan(editorWorkspace, targetDocument.Id, sourceLocation.SourceSpan, options, cancellationToken));
                }
            }

            // We don't have a source document, so show the Metadata as Source view in a preview tab.

            if (!_metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
            {
                return(false);
            }

            // Should we prefer navigating to the Object Browser over metadata-as-source?
            if (_globalOptions.GetOption(VisualStudioNavigationOptions.NavigateToObjectBrowser, project.Language))
            {
                var libraryService = project.LanguageServices.GetService <ILibraryService>();
                if (libraryService == null)
                {
                    return(false);
                }

                var compilation = project.GetCompilationAsync(cancellationToken).WaitAndGetResult(cancellationToken);
                var navInfo     = libraryService.NavInfoFactory.CreateForSymbol(symbol, project, compilation);
                if (navInfo == null)
                {
                    navInfo = libraryService.NavInfoFactory.CreateForProject(project);
                }

                if (navInfo != null)
                {
                    var navigationTool = IServiceProviderExtensions.GetService <SVsObjBrowser, IVsNavigationTool>(_serviceProvider);
                    return(navigationTool.NavigateToNavInfo(navInfo) == VSConstants.S_OK);
                }

                // Note: we'll fallback to Metadata-As-Source if we fail to get IVsNavInfo, but that should never happen.
            }

            // Generate new source or retrieve existing source for the symbol in question
            return(ThreadingContext.JoinableTaskFactory.Run(() => TryNavigateToMetadataAsync(project, symbol, options, cancellationToken)));
        }
Exemplo n.º 2
0
        public async Task <IEnumerable <IPeekableItem> > GetPeekableItemsAsync(ISymbol symbol, Project project, IPeekResultFactory peekResultFactory, CancellationToken cancellationToken)
        {
            if (symbol == null)
            {
                throw new ArgumentNullException(nameof(symbol));
            }

            if (project == null)
            {
                throw new ArgumentNullException(nameof(project));
            }

            if (peekResultFactory == null)
            {
                throw new ArgumentNullException(nameof(peekResultFactory));
            }

            var results = new List <IPeekableItem>();

            var solution         = project.Solution;
            var sourceDefinition = await SymbolFinder.FindSourceDefinitionAsync(symbol, solution, cancellationToken).ConfigureAwait(false);

            // And if our definition actually is from source, then let's re-figure out what project it came from
            if (sourceDefinition != null)
            {
                var originatingProject = solution.GetProject(sourceDefinition.ContainingAssembly, cancellationToken);

                project = originatingProject ?? project;
            }

            string filePath;
            int    lineNumber;
            int    charOffset;

            var symbolNavigationService = solution.Workspace.Services.GetService <ISymbolNavigationService>();

            if (symbolNavigationService.WouldNavigateToSymbol(symbol, solution, out filePath, out lineNumber, out charOffset))
            {
                var position = new LinePosition(lineNumber, charOffset);
                results.Add(new ExternalFilePeekableItem(new FileLinePositionSpan(filePath, position, position), PredefinedPeekRelationships.Definitions, peekResultFactory));
            }
            else
            {
                var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);

                var symbolKey = SymbolKey.Create(symbol, compilation, cancellationToken);

                var firstLocation = symbol.Locations.FirstOrDefault();
                if (firstLocation != null)
                {
                    if (firstLocation.IsInSource || _metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
                    {
                        results.Add(new DefinitionPeekableItem(solution.Workspace, project.Id, symbolKey, peekResultFactory, _metadataAsSourceFileService));
                    }
                }
            }

            return(results);
        }
Exemplo n.º 3
0
        public async Task <IEnumerable <IPeekableItem> > GetPeekableItemsAsync(
            ISymbol symbol, Project project,
            IPeekResultFactory peekResultFactory,
            CancellationToken cancellationToken)
        {
            if (symbol == null)
            {
                throw new ArgumentNullException(nameof(symbol));
            }

            if (project == null)
            {
                throw new ArgumentNullException(nameof(project));
            }

            if (peekResultFactory == null)
            {
                throw new ArgumentNullException(nameof(peekResultFactory));
            }

            var results = new List <IPeekableItem>();

            var solution         = project.Solution;
            var sourceDefinition = await SymbolFinder.FindSourceDefinitionAsync(symbol, solution, cancellationToken).ConfigureAwait(false);

            // And if our definition actually is from source, then let's re-figure out what project it came from
            if (sourceDefinition != null)
            {
                var originatingProject = solution.GetProject(sourceDefinition.ContainingAssembly, cancellationToken);

                project = originatingProject ?? project;
            }

            var symbolNavigationService = solution.Workspace.Services.GetService <ISymbolNavigationService>();
            var definitionItem          = symbol.ToNonClassifiedDefinitionItem(solution, includeHiddenLocations: true);

            var result = await symbolNavigationService.GetExternalNavigationSymbolLocationAsync(definitionItem, cancellationToken).ConfigureAwait(false);

            if (result is var(filePath, linePosition))
            {
                results.Add(new ExternalFilePeekableItem(new FileLinePositionSpan(filePath, linePosition, linePosition), PredefinedPeekRelationships.Definitions, peekResultFactory));
            }
            else
            {
                var symbolKey = SymbolKey.Create(symbol, cancellationToken);

                var firstLocation = symbol.Locations.FirstOrDefault();
                if (firstLocation != null)
                {
                    if (firstLocation.IsInSource || _metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
                    {
                        results.Add(new DefinitionPeekableItem(solution.Workspace, project.Id, symbolKey, peekResultFactory, _metadataAsSourceFileService, _globalOptions));
                    }
                }
            }

            return(results);
        }
        protected async Task <LSP.Location[]?> GetDefinitionAsync(LSP.TextDocumentPositionParams request, bool typeOnly, RequestContext context, CancellationToken cancellationToken)
        {
            var document = context.Document;

            if (document == null)
            {
                return(null);
            }

            var locations = ArrayBuilder <LSP.Location> .GetInstance();

            var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false);

            var findDefinitionService = document.GetRequiredLanguageService <IFindDefinitionService>();

            var definitions = await findDefinitionService.FindDefinitionsAsync(document, position, cancellationToken).ConfigureAwait(false);

            if (definitions.Any())
            {
                foreach (var definition in definitions)
                {
                    if (!ShouldInclude(definition, typeOnly))
                    {
                        continue;
                    }

                    var location = await ProtocolConversions.TextSpanToLocationAsync(
                        definition.Document, definition.SourceSpan, definition.IsStale, cancellationToken).ConfigureAwait(false);

                    locations.AddIfNotNull(location);
                }
            }
            else if (document.SupportsSemanticModel && _metadataAsSourceFileService != null)
            {
                // No definition found - see if we can get metadata as source but that's only applicable for C#\VB.
                var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false);

                if (symbol != null && _metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
                {
                    if (!typeOnly || symbol is ITypeSymbol)
                    {
                        var options         = _globalOptions.GetMetadataAsSourceOptions(document.Project.LanguageServices);
                        var declarationFile = await _metadataAsSourceFileService.GetGeneratedFileAsync(document.Project, symbol, signaturesOnly : false, options, cancellationToken).ConfigureAwait(false);

                        var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span;
                        locations.Add(new LSP.Location
                        {
                            Uri   = new Uri(declarationFile.FilePath),
                            Range = ProtocolConversions.LinePositionToRange(linePosSpan),
                        });
                    }
                }
            }

            return(locations.ToArrayAndFree());
Exemplo n.º 5
0
        private static async Task <LSP.Location[]> GetSymbolDefinitionLocationsAsync(XamlSymbolDefinition symbolDefinition, RequestContext context, IMetadataAsSourceFileService metadataAsSourceFileService, CancellationToken cancellationToken)
        {
            Contract.ThrowIfNull(symbolDefinition.Symbol);

            using var _ = ArrayBuilder <LSP.Location> .GetInstance(out var locations);

            var symbol = symbolDefinition.Symbol;

            var items = NavigableItemFactory.GetItemsFromPreferredSourceLocations(context.Solution, symbol, displayTaggedParts: null, cancellationToken);

            if (items.Any())
            {
                foreach (var item in items)
                {
                    var location = await ProtocolConversions.TextSpanToLocationAsync(
                        item.Document, item.SourceSpan, item.IsStale, cancellationToken).ConfigureAwait(false);

                    locations.AddIfNotNull(location);
                }
            }
            else
            {
                var metadataLocation = symbol.Locations.Where(loc => loc.IsInMetadata).FirstOrDefault();
                if (metadataLocation != null && metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
                {
                    var project = context.Document?.GetCodeProject();
                    if (project != null)
                    {
                        var declarationFile = await metadataAsSourceFileService.GetGeneratedFileAsync(project, symbol, allowDecompilation : false, cancellationToken).ConfigureAwait(false);

                        var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span;
                        locations.Add(new LSP.Location
                        {
                            Uri   = new Uri(declarationFile.FilePath),
                            Range = ProtocolConversions.LinePositionToRange(linePosSpan),
                        });
                    }
                }
            }

            return(locations.ToArray());
        }
        public bool TryNavigateToSymbol(ISymbol symbol, Project project, bool usePreviewTab = false)
        {
            if (project == null || symbol == null)
            {
                return(false);
            }

            symbol = symbol.OriginalDefinition;

            // Prefer visible source locations if possible.
            var sourceLocations        = symbol.Locations.Where(loc => loc.IsInSource);
            var visibleSourceLocations = sourceLocations.Where(loc => loc.IsVisibleSourceLocation());
            var sourceLocation         = visibleSourceLocations.Any() ? visibleSourceLocations.First() : sourceLocations.FirstOrDefault();

            if (sourceLocation != null)
            {
                var targetDocument = project.Solution.GetDocument(sourceLocation.SourceTree);
                if (targetDocument != null)
                {
                    var editorWorkspace   = targetDocument.Project.Solution.Workspace;
                    var navigationService = editorWorkspace.Services.GetService <IDocumentNavigationService>();
                    return(navigationService.TryNavigateToSpan(editorWorkspace, targetDocument.Id, sourceLocation.SourceSpan, usePreviewTab));
                }
            }

            // We don't have a source document, so show the Metadata as Source view in a preview tab.

            var metadataLocation = symbol.Locations.Where(loc => loc.IsInMetadata).FirstOrDefault();

            if (metadataLocation == null || !_metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
            {
                return(false);
            }

            // Generate new source or retrieve existing source for the symbol in question
            var result = _metadataAsSourceFileService.GetGeneratedFileAsync(project, symbol).WaitAndGetResult(CancellationToken.None);

            var vsRunningDocumentTable4 = GetService <SVsRunningDocumentTable, IVsRunningDocumentTable4>();
            var fileAlreadyOpen         = vsRunningDocumentTable4.IsMonikerValid((string)result.FilePath);

            var openDocumentService = GetService <SVsUIShellOpenDocument, IVsUIShellOpenDocument>();

            IVsUIHierarchy      hierarchy;
            uint                itemId;
            IOleServiceProvider localServiceProvider;
            IVsWindowFrame      windowFrame;

            openDocumentService.OpenDocumentViaProject(result.FilePath, VSConstants.LOGVIEWID.TextView_guid, out localServiceProvider, out hierarchy, out itemId, out windowFrame);

            var documentCookie = vsRunningDocumentTable4.GetDocumentCookie(result.FilePath);

            var vsTextBuffer = (IVsTextBuffer)vsRunningDocumentTable4.GetDocumentData(documentCookie);
            var textBuffer   = _editorAdaptersFactory.GetDataBuffer((IVsTextBuffer)vsTextBuffer);

            if (!fileAlreadyOpen)
            {
                ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_IsProvisional, true));
                ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_OverrideCaption, result.DocumentTitle));
                ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_OverrideToolTip, result.DocumentTooltip));
            }

            windowFrame.Show();

            var openedDocument = textBuffer.AsTextContainer().GetRelatedDocuments().FirstOrDefault();

            if (openedDocument != null)
            {
                var editorWorkspace   = openedDocument.Project.Solution.Workspace;
                var navigationService = editorWorkspace.Services.GetService <IDocumentNavigationService>();
                return(navigationService.TryNavigateToSpan(editorWorkspace, openedDocument.Id, result.IdentifierLocation.SourceSpan, usePreviewTab: true));
            }

            return(true);
        }
Exemplo n.º 7
0
        public bool TryNavigateToSymbol(ISymbol symbol, Project project, OptionSet?options, CancellationToken cancellationToken)
        {
            if (project == null || symbol == null)
            {
                return(false);
            }

            options ??= project.Solution.Options;
            symbol = symbol.OriginalDefinition;

            // Prefer visible source locations if possible.
            var sourceLocations        = symbol.Locations.Where(loc => loc.IsInSource);
            var visibleSourceLocations = sourceLocations.Where(loc => loc.IsVisibleSourceLocation());
            var sourceLocation         = visibleSourceLocations.FirstOrDefault() ?? sourceLocations.FirstOrDefault();

            if (sourceLocation != null)
            {
                var targetDocument = project.Solution.GetDocument(sourceLocation.SourceTree);
                if (targetDocument != null)
                {
                    var editorWorkspace   = targetDocument.Project.Solution.Workspace;
                    var navigationService = editorWorkspace.Services.GetRequiredService <IDocumentNavigationService>();
                    return(navigationService.TryNavigateToSpan(editorWorkspace, targetDocument.Id, sourceLocation.SourceSpan, options, cancellationToken));
                }
            }

            // We don't have a source document, so show the Metadata as Source view in a preview tab.

            var metadataLocation = symbol.Locations.Where(loc => loc.IsInMetadata).FirstOrDefault();

            if (metadataLocation == null || !_metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
            {
                return(false);
            }

            // Should we prefer navigating to the Object Browser over metadata-as-source?
            if (_globalOptions.GetOption(VisualStudioNavigationOptions.NavigateToObjectBrowser, project.Language))
            {
                var libraryService = project.LanguageServices.GetService <ILibraryService>();
                if (libraryService == null)
                {
                    return(false);
                }

                var compilation = project.GetCompilationAsync(cancellationToken).WaitAndGetResult(cancellationToken);
                var navInfo     = libraryService.NavInfoFactory.CreateForSymbol(symbol, project, compilation);
                if (navInfo == null)
                {
                    navInfo = libraryService.NavInfoFactory.CreateForProject(project);
                }

                if (navInfo != null)
                {
                    var navigationTool = IServiceProviderExtensions.GetService <SVsObjBrowser, IVsNavigationTool>(_serviceProvider);
                    return(navigationTool.NavigateToNavInfo(navInfo) == VSConstants.S_OK);
                }

                // Note: we'll fallback to Metadata-As-Source if we fail to get IVsNavInfo, but that should never happen.
            }

            // Generate new source or retrieve existing source for the symbol in question
            var allowDecompilation = false;

            // Check whether decompilation is supported for the project. We currently only support this for C# projects.
            if (project.LanguageServices.GetService <IDecompiledSourceService>() != null)
            {
                allowDecompilation = project.Solution.Workspace.Options.GetOption(FeatureOnOffOptions.NavigateToDecompiledSources) && !symbol.IsFromSource();
            }

            var result = _metadataAsSourceFileService.GetGeneratedFileAsync(project, symbol, allowDecompilation, cancellationToken).WaitAndGetResult(cancellationToken);

            var vsRunningDocumentTable4 = IServiceProviderExtensions.GetService <SVsRunningDocumentTable, IVsRunningDocumentTable4>(_serviceProvider);
            var fileAlreadyOpen         = vsRunningDocumentTable4.IsMonikerValid(result.FilePath);

            var openDocumentService = IServiceProviderExtensions.GetService <SVsUIShellOpenDocument, IVsUIShellOpenDocument>(_serviceProvider);

            openDocumentService.OpenDocumentViaProject(result.FilePath, VSConstants.LOGVIEWID.TextView_guid, out var localServiceProvider, out var hierarchy, out var itemId, out var windowFrame);

            var documentCookie = vsRunningDocumentTable4.GetDocumentCookie(result.FilePath);

            // The cast from dynamic to object doesn't change semantics, but avoids loading the dynamic binder
            // which saves us JIT time in this method.
            var vsTextBuffer = (IVsTextBuffer)(object)vsRunningDocumentTable4.GetDocumentData(documentCookie);
            var textBuffer   = _editorAdaptersFactory.GetDataBuffer(vsTextBuffer);

            if (!fileAlreadyOpen)
            {
                ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_IsProvisional, true));
                ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_OverrideCaption, result.DocumentTitle));
                ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_OverrideToolTip, result.DocumentTooltip));
            }

            windowFrame.Show();

            var openedDocument = textBuffer?.AsTextContainer().GetRelatedDocuments().FirstOrDefault();

            if (openedDocument != null)
            {
                var editorWorkspace   = openedDocument.Project.Solution.Workspace;
                var navigationService = editorWorkspace.Services.GetRequiredService <IDocumentNavigationService>();

                return(navigationService.TryNavigateToSpan(
                           editorWorkspace,
                           openedDocument.Id,
                           result.IdentifierLocation.SourceSpan,
                           options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true),
                           cancellationToken));
            }

            return(true);
        }
        public bool TryNavigateToSymbol(ISymbol symbol, Project project, OptionSet options, CancellationToken cancellationToken)
        {
            if (project == null || symbol == null)
            {
                return(false);
            }

            options = options ?? project.Solution.Workspace.Options;
            symbol  = symbol.OriginalDefinition;

            // Prefer visible source locations if possible.
            var sourceLocations        = symbol.Locations.Where(loc => loc.IsInSource);
            var visibleSourceLocations = sourceLocations.Where(loc => loc.IsVisibleSourceLocation());
            var sourceLocation         = visibleSourceLocations.FirstOrDefault() ?? sourceLocations.FirstOrDefault();

            if (sourceLocation != null)
            {
                var targetDocument = project.Solution.GetDocument(sourceLocation.SourceTree);
                if (targetDocument != null)
                {
                    var editorWorkspace   = targetDocument.Project.Solution.Workspace;
                    var navigationService = editorWorkspace.Services.GetService <IDocumentNavigationService>();
                    return(navigationService.TryNavigateToSpan(editorWorkspace, targetDocument.Id, sourceLocation.SourceSpan, options));
                }
            }

            // We don't have a source document, so show the Metadata as Source view in a preview tab.

            var metadataLocation = symbol.Locations.Where(loc => loc.IsInMetadata).FirstOrDefault();

            if (metadataLocation == null || !_metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
            {
                return(false);
            }

            // Should we prefer navigating to the Object Browser over metadata-as-source?
            if (options.GetOption(VisualStudioNavigationOptions.NavigateToObjectBrowser, project.Language))
            {
                var libraryService = project.LanguageServices.GetService <ILibraryService>();
                if (libraryService == null)
                {
                    return(false);
                }

                var compilation = project.GetCompilationAsync(cancellationToken).WaitAndGetResult(cancellationToken);
                var navInfo     = libraryService.NavInfoFactory.CreateForSymbol(symbol, project, compilation);
                if (navInfo == null)
                {
                    navInfo = libraryService.NavInfoFactory.CreateForProject(project);
                }

                if (navInfo != null)
                {
                    var navigationTool = _serviceProvider.GetService <SVsObjBrowser, IVsNavigationTool>();
                    return(navigationTool.NavigateToNavInfo(navInfo) == VSConstants.S_OK);
                }

                // Note: we'll fallback to Metadata-As-Source if we fail to get IVsNavInfo, but that should never happen.
            }

            // Generate new source or retrieve existing source for the symbol in question
            var result = _metadataAsSourceFileService.GetGeneratedFileAsync(project, symbol, cancellationToken).WaitAndGetResult(cancellationToken);

            var vsRunningDocumentTable4 = _serviceProvider.GetService <SVsRunningDocumentTable, IVsRunningDocumentTable4>();
            var fileAlreadyOpen         = vsRunningDocumentTable4.IsMonikerValid(result.FilePath);

            var openDocumentService = _serviceProvider.GetService <SVsUIShellOpenDocument, IVsUIShellOpenDocument>();

            IVsUIHierarchy      hierarchy;
            uint                itemId;
            IOleServiceProvider localServiceProvider;
            IVsWindowFrame      windowFrame;

            openDocumentService.OpenDocumentViaProject(result.FilePath, VSConstants.LOGVIEWID.TextView_guid, out localServiceProvider, out hierarchy, out itemId, out windowFrame);

            var documentCookie = vsRunningDocumentTable4.GetDocumentCookie(result.FilePath);

            var vsTextBuffer = (IVsTextBuffer)vsRunningDocumentTable4.GetDocumentData(documentCookie);
            var textBuffer   = _editorAdaptersFactory.GetDataBuffer(vsTextBuffer);

            if (!fileAlreadyOpen)
            {
                ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_IsProvisional, true));
                ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_OverrideCaption, result.DocumentTitle));
                ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_OverrideToolTip, result.DocumentTooltip));
            }

            windowFrame.Show();

            var openedDocument = textBuffer.AsTextContainer().GetRelatedDocuments().FirstOrDefault();

            if (openedDocument != null)
            {
                var editorWorkspace   = openedDocument.Project.Solution.Workspace;
                var navigationService = editorWorkspace.Services.GetService <IDocumentNavigationService>();

                return(navigationService.TryNavigateToSpan(
                           workspace: editorWorkspace,
                           documentId: openedDocument.Id,
                           textSpan: result.IdentifierLocation.SourceSpan,
                           options: options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true)));
            }

            return(true);
        }
Exemplo n.º 9
0
        /// <summary>
        /// Internal for testing purposes.
        /// </summary>
        internal async Task <ImmutableArray <CodeDefinitionWindowLocation> > GetContextFromPointAsync(
            Document document, int position, CancellationToken cancellationToken)
        {
            var workspace      = document.Project.Solution.Workspace;
            var navigableItems = await GoToDefinitionHelpers.GetDefinitionsAsync(document, position, cancellationToken).ConfigureAwait(false);

            if (navigableItems?.Any() == true)
            {
                var navigationService = workspace.Services.GetRequiredService <IDocumentNavigationService>();

                using var _ = PooledObjects.ArrayBuilder <CodeDefinitionWindowLocation> .GetInstance(out var builder);

                foreach (var item in navigableItems)
                {
                    if (await navigationService.CanNavigateToSpanAsync(workspace, item.Document.Id, item.SourceSpan, cancellationToken).ConfigureAwait(false))
                    {
                        var text = await item.Document.GetTextAsync(cancellationToken).ConfigureAwait(false);

                        var linePositionSpan = text.Lines.GetLinePositionSpan(item.SourceSpan);

                        if (item.Document.FilePath != null)
                        {
                            builder.Add(new CodeDefinitionWindowLocation(item.DisplayTaggedParts.JoinText(), item.Document.FilePath, linePositionSpan.Start));
                        }
                    }
                }

                return(builder.ToImmutable());
            }

            // We didn't have regular source references, but possibly:
            // 1. Another language (like XAML) will take over via ISymbolNavigationService
            // 2. There are no locations from source, so we'll try to generate a metadata as source file and use that
            var symbol = await SymbolFinder.FindSymbolAtPositionAsync(
                document,
                position,
                cancellationToken : cancellationToken).ConfigureAwait(false);

            if (symbol == null)
            {
                return(ImmutableArray <CodeDefinitionWindowLocation> .Empty);
            }

            var symbolNavigationService = workspace.Services.GetRequiredService <ISymbolNavigationService>();
            var definitionItem          = symbol.ToNonClassifiedDefinitionItem(document.Project.Solution, includeHiddenLocations: false);
            var result = await symbolNavigationService.GetExternalNavigationSymbolLocationAsync(definitionItem, cancellationToken).ConfigureAwait(false);

            if (result != null)
            {
                return(ImmutableArray.Create(new CodeDefinitionWindowLocation(symbol.ToDisplayString(), result.Value.filePath, result.Value.linePosition)));
            }
            else if (_metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
            {
                var allowDecompilation = _globalOptions.GetOption(FeatureOnOffOptions.NavigateToDecompiledSources);
                var declarationFile    = await _metadataAsSourceFileService.GetGeneratedFileAsync(document.Project, symbol, signaturesOnly : false, allowDecompilation, cancellationToken).ConfigureAwait(false);

                var identifierSpan = declarationFile.IdentifierLocation.GetLineSpan().Span;
                return(ImmutableArray.Create(new CodeDefinitionWindowLocation(symbol.ToDisplayString(), declarationFile.FilePath, identifierSpan.Start)));
            }

            return(ImmutableArray <CodeDefinitionWindowLocation> .Empty);
        }
        public async Task <INavigableLocation?> GetNavigableLocationAsync(
            ISymbol symbol, Project project, CancellationToken cancellationToken)
        {
            if (project == null || symbol == null)
            {
                return(null);
            }

            symbol = symbol.OriginalDefinition;

            // Prefer visible source locations if possible.
            var sourceLocations        = symbol.Locations.Where(loc => loc.IsInSource);
            var visibleSourceLocations = sourceLocations.Where(loc => loc.IsVisibleSourceLocation());
            var sourceLocation         = visibleSourceLocations.FirstOrDefault() ?? sourceLocations.FirstOrDefault();

            if (sourceLocation != null)
            {
                var targetDocument = project.Solution.GetDocument(sourceLocation.SourceTree);
                if (targetDocument != null)
                {
                    var editorWorkspace   = targetDocument.Project.Solution.Workspace;
                    var navigationService = editorWorkspace.Services.GetRequiredService <IDocumentNavigationService>();
                    return(await navigationService.GetLocationForSpanAsync(
                               editorWorkspace, targetDocument.Id, sourceLocation.SourceSpan,
                               allowInvalidSpan : false, cancellationToken).ConfigureAwait(false));
                }
            }

            // We don't have a source document, so show the Metadata as Source view in a preview tab.

            if (!_metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
            {
                return(null);
            }

            // Should we prefer navigating to the Object Browser over metadata-as-source?
            if (_globalOptions.GetOption(VisualStudioNavigationOptions.NavigateToObjectBrowser, project.Language))
            {
                var libraryService = project.LanguageServices.GetService <ILibraryService>();
                if (libraryService == null)
                {
                    return(null);
                }

                var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);

                var navInfo = libraryService.NavInfoFactory.CreateForSymbol(symbol, project, compilation);
                navInfo ??= libraryService.NavInfoFactory.CreateForProject(project);

                if (navInfo != null)
                {
                    var navigationTool = _serviceProvider.GetServiceOnMainThread <SVsObjBrowser, IVsNavigationTool>();
                    return(new NavigableLocation(async(options, cancellationToken) =>
                    {
                        await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
                        return navigationTool.NavigateToNavInfo(navInfo) == VSConstants.S_OK;
                    }));
                }

                // Note: we'll fallback to Metadata-As-Source if we fail to get IVsNavInfo, but that should never happen.
            }

            // Generate new source or retrieve existing source for the symbol in question
            return(await GetNavigableLocationForMetadataAsync(project, symbol, cancellationToken).ConfigureAwait(false));
        }