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 16 17
{
    internal abstract class AbstractGoToDefinitionService : IGoToDefinitionService
    {
18
        private readonly IEnumerable<Lazy<IStreamingFindUsagesPresenter>> _streamingPresenters;
19

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

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

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

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

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

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

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

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

            if (containingTypeDeclaration != null)
            {
                var semanticModel = document.GetSemanticModelAsync(cancellationToken).WaitAndGetResult(cancellationToken);
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
                var containingTypeSymbol = semanticModel.GetDeclaredSymbol(containingTypeDeclaration, cancellationToken) as ITypeSymbol;

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

                if (containingTypeSymbol != null &&
                    (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
        }
    }
}