GoToDefinitionHelpers.cs 6.4 KB
Newer Older
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
2 3 4

using System;
using System.Collections.Generic;
5
using System.Collections.Immutable;
6 7 8
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
9
using System.Threading.Tasks;
10 11 12
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Navigation;
13
using Microsoft.CodeAnalysis.Options;
14 15 16 17 18 19 20 21 22 23
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
{
    internal static class GoToDefinitionHelpers
    {
        public static bool TryGoToDefinition(
            ISymbol symbol,
            Project project,
            IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
24 25 26
            CancellationToken cancellationToken,
            bool thirdPartyNavigationAllowed = true,
            bool throwOnHiddenDefinition = false)
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
        {
            var alias = symbol as IAliasSymbol;
            if (alias != null)
            {
                var ns = alias.Target as INamespaceSymbol;
                if (ns != null && ns.IsGlobalNamespace)
                {
                    return false;
                }
            }

            // VB global import aliases have a synthesized SyntaxTree.
            // We can't go to the definition of the alias, so use the target type.

            var solution = project.Solution;
            if (symbol is IAliasSymbol &&
                NavigableItemFactory.GetPreferredSourceLocations(solution, symbol).All(l => project.Solution.GetDocument(l.SourceTree) == null))
            {
                symbol = ((IAliasSymbol)symbol).Target;
            }

            var definition = SymbolFinder.FindSourceDefinitionAsync(symbol, solution, cancellationToken).WaitAndGetResult(cancellationToken);
            cancellationToken.ThrowIfCancellationRequested();

            symbol = definition ?? symbol;

53
            if (thirdPartyNavigationAllowed && TryThirdPartyNavigation(symbol, solution))
54 55 56 57 58 59 60 61 62 63 64
            {
                return true;
            }

            // If it is a partial method declaration with no body, choose to go to the implementation
            // that has a method body.
            if (symbol is IMethodSymbol)
            {
                symbol = ((IMethodSymbol)symbol).PartialImplementationPart ?? symbol;
            }

65
            var options = project.Solution.Options;
66

67
            var preferredSourceLocations = NavigableItemFactory.GetPreferredSourceLocations(solution, symbol).ToArray();
68 69 70
            var displayParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(project, symbol);
            var title = displayParts.JoinText();

71 72 73
            if (!preferredSourceLocations.Any())
            {
                // If there are no visible source locations, then tell the host about the symbol and 
74
                // allow it to navigate to it.  This will either navigate to any non-visible source
75 76 77 78
                // locations, or it can appropriately deal with metadata symbols for hosts that can go 
                // to a metadata-as-source view.

                var symbolNavigationService = solution.Workspace.Services.GetService<ISymbolNavigationService>();
79 80
                return symbolNavigationService.TryNavigateToSymbol(
                    symbol, project,
81
                    options: options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true),
82
                    cancellationToken: cancellationToken);
83 84
            }

85 86 87 88
            var navigableItems = preferredSourceLocations.Select(location =>
                NavigableItemFactory.GetItemFromSymbolLocation(
                    solution, symbol, location,
                    displayTaggedParts: null)).ToImmutableArray();
89
            return TryGoToDefinition(navigableItems, title, options, presenters, throwOnHiddenDefinition);
90 91
        }

92
        private static bool TryGoToDefinition(
93 94
            ImmutableArray<INavigableItem> navigableItems,
            string title,
95
            OptionSet options,
96 97 98
            IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
            bool throwOnHiddenDefinition)
        {
99 100
            Contract.ThrowIfNull(options);

101
            // If we have a single location, then just navigate to it.
102
            if (navigableItems.Length == 1 && navigableItems[0].Document != null)
103
            {
104
                var firstItem = navigableItems[0];
105
                var workspace = firstItem.Document.Project.Solution.Workspace;
106 107
                var navigationService = workspace.Services.GetService<IDocumentNavigationService>();

108
                if (navigationService.CanNavigateToSpan(workspace, firstItem.Document.Id, firstItem.SourceSpan))
109
                {
110 111
                    return navigationService.TryNavigateToSpan(
                        workspace,
112
                        documentId: firstItem.Document.Id,
113
                        textSpan: firstItem.SourceSpan,
114
                        options: options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true));
115 116 117 118 119 120
                }
                else
                {
                    if (throwOnHiddenDefinition)
                    {
                        const int E_FAIL = -2147467259;
121
                        throw new COMException(EditorFeaturesResources.The_definition_of_the_object_is_hidden, E_FAIL);
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            else
            {
                // We have multiple viable source locations, so ask the host what to do. Most hosts
                // will simply display the results to the user and allow them to choose where to 
                // go.

                if (presenters.Any())
                {
137
                    presenters.First().Value.DisplayResult(title, navigableItems);
138 139 140 141 142 143 144
                    return true;
                }

                return false;
            }
        }

145
        private static bool TryThirdPartyNavigation(ISymbol symbol, Solution solution)
146 147 148 149 150 151 152
        {
            var symbolNavigationService = solution.Workspace.Services.GetService<ISymbolNavigationService>();

            // Notify of navigation so third parties can intercept the navigation
            return symbolNavigationService.TrySymbolNavigationNotify(symbol, solution);
        }
    }
C
CyrusNajmabadi 已提交
153
}