AbstractGoToDefinitionService.cs 4.6 KB
Newer Older
1 2 3 4 5 6 7 8 9
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.LanguageServices;
10
using Microsoft.CodeAnalysis.Navigation;
11 12 13
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

14
namespace Microsoft.CodeAnalysis.Editor.GoToDefinition
15
{
I
isc30 已提交
16
    // GoToDefinition
17 18
    internal abstract class AbstractGoToDefinitionService : IGoToDefinitionService
    {
19
        private readonly IEnumerable<Lazy<IStreamingFindUsagesPresenter>> _streamingPresenters;
20

C
CyrusNajmabadi 已提交
21
        protected AbstractGoToDefinitionService(
22
            IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters)
23
        {
24
            _streamingPresenters = streamingPresenters;
25 26
        }

27 28
        public async Task<IEnumerable<INavigableItem>> FindDefinitionsAsync(
            Document document, int position, CancellationToken cancellationToken)
29
        {
R
Ravi Chande 已提交
30
            var symbolService = document.GetLanguageService<IGoToDefinitionSymbolService>();
I
isc30 已提交
31
            var (symbol, span) = await symbolService.GetSymbolAndBoundSpanAsync(document, position, includeType: true, cancellationToken).ConfigureAwait(false);
32

33
            // Try to compute source definitions from symbol.
34
            var items = symbol != null
35
                ? NavigableItemFactory.GetItemsFromPreferredSourceLocations(document.Project.Solution, symbol, displayTaggedParts: null, cancellationToken: cancellationToken)
36
                : null;
37

38 39
            // realize the list here so that the consumer await'ing the result doesn't lazily cause
            // them to be created on an inappropriate thread.
40
            return items?.ToList();
41 42 43 44
        }

        public bool TryGoToDefinition(Document document, int position, CancellationToken cancellationToken)
        {
45
            // Try to compute the referenced symbol and attempt to go to definition for the symbol.
R
Ravi Chande 已提交
46
            var symbolService = document.GetLanguageService<IGoToDefinitionSymbolService>();
I
isc30 已提交
47
            var (symbol, _) = symbolService.GetSymbolAndBoundSpanAsync(document, position, includeType: true, cancellationToken).WaitAndGetResult(cancellationToken);
48
            if (symbol is null)
49
            {
C
CyrusNajmabadi 已提交
50
                return false;
51 52
            }

C
CyrusNajmabadi 已提交
53 54 55 56
            var isThirdPartyNavigationAllowed = IsThirdPartyNavigationAllowed(symbol, position, document, cancellationToken);

            return GoToDefinitionHelpers.TryGoToDefinition(symbol,
                document.Project,
57
                _streamingPresenters,
C
CyrusNajmabadi 已提交
58 59 60
                thirdPartyNavigationAllowed: isThirdPartyNavigationAllowed,
                throwOnHiddenDefinition: true,
                cancellationToken: cancellationToken);
61 62
        }

63
        private static bool IsThirdPartyNavigationAllowed(ISymbol symbolToNavigateTo, int caretPosition, Document document, CancellationToken cancellationToken)
64
        {
C
CyrusNajmabadi 已提交
65
            var syntaxRoot = document.GetSyntaxRootSynchronously(cancellationToken);
66 67 68 69 70 71
            var syntaxFactsService = document.GetLanguageService<ISyntaxFactsService>();
            var containingTypeDeclaration = syntaxFactsService.GetContainingTypeDeclaration(syntaxRoot, caretPosition);

            if (containingTypeDeclaration != null)
            {
                var semanticModel = document.GetSemanticModelAsync(cancellationToken).WaitAndGetResult(cancellationToken);
72 73 74 75

                // Allow third parties to navigate to all symbols except types/constructors
                // if we are navigating from the corresponding type.

C
CyrusNajmabadi 已提交
76
                if (semanticModel.GetDeclaredSymbol(containingTypeDeclaration, cancellationToken) is ITypeSymbol containingTypeSymbol &&
77 78 79 80 81 82 83 84 85 86 87 88 89 90
                    (symbolToNavigateTo is ITypeSymbol || symbolToNavigateTo.IsConstructor()))
                {
                    var candidateTypeSymbol = symbolToNavigateTo is ITypeSymbol
                        ? symbolToNavigateTo
                        : symbolToNavigateTo.ContainingType;

                    if (containingTypeSymbol == candidateTypeSymbol)
                    {
                        // We are navigating from the same type, so don't allow third parties to perform the navigation.
                        // This ensures that if we navigate to a class from within that class, we'll stay in the same file
                        // rather than navigate to, say, XAML.
                        return false;
                    }
                }
91 92
            }

93
            return true;
94 95 96
        }
    }
}