AbstractGoToDefinitionService.cs 4.8 KB
Newer Older
J
Jonathon Marolf 已提交
1 2 3
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
4

5
using System;
6 7 8 9 10 11
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.LanguageServices;
12
using Microsoft.CodeAnalysis.Navigation;
13 14 15
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

16
namespace Microsoft.CodeAnalysis.Editor.GoToDefinition
17
{
I
isc30 已提交
18
    // GoToDefinition
19 20
    internal abstract class AbstractGoToDefinitionService : IGoToDefinitionService
    {
21 22 23 24 25
        /// <summary>
        /// Presenter used for <see cref="TryGoToDefinition(Document, int, CancellationToken)"/>
        /// Import lazily as this requires the UI thread.
        /// </summary>
        private readonly Lazy<IStreamingFindUsagesPresenter> _streamingPresenter;
26

27
        protected AbstractGoToDefinitionService(Lazy<IStreamingFindUsagesPresenter> streamingPresenter)
28
        {
29
            _streamingPresenter = streamingPresenter;
30 31
        }

32 33
        public async Task<IEnumerable<INavigableItem>> FindDefinitionsAsync(
            Document document, int position, CancellationToken cancellationToken)
34
        {
R
Ravi Chande 已提交
35
            var symbolService = document.GetLanguageService<IGoToDefinitionSymbolService>();
C
Cyrus Najmabadi 已提交
36
            var (symbol, _) = await symbolService.GetSymbolAndBoundSpanAsync(document, position, includeType: true, cancellationToken).ConfigureAwait(false);
37

38
            // Try to compute source definitions from symbol.
39
            var items = symbol != null
40
                ? NavigableItemFactory.GetItemsFromPreferredSourceLocations(document.Project.Solution, symbol, displayTaggedParts: null, cancellationToken: cancellationToken)
41
                : null;
42

43 44
            // realize the list here so that the consumer await'ing the result doesn't lazily cause
            // them to be created on an inappropriate thread.
45
            return items?.ToList();
46 47 48 49
        }

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

C
CyrusNajmabadi 已提交
58 59 60 61
            var isThirdPartyNavigationAllowed = IsThirdPartyNavigationAllowed(symbol, position, document, cancellationToken);

            return GoToDefinitionHelpers.TryGoToDefinition(symbol,
                document.Project,
62
                _streamingPresenter.Value,
C
CyrusNajmabadi 已提交
63 64 65
                thirdPartyNavigationAllowed: isThirdPartyNavigationAllowed,
                throwOnHiddenDefinition: true,
                cancellationToken: cancellationToken);
66 67
        }

68
        private static bool IsThirdPartyNavigationAllowed(ISymbol symbolToNavigateTo, int caretPosition, Document document, CancellationToken cancellationToken)
69
        {
C
CyrusNajmabadi 已提交
70
            var syntaxRoot = document.GetSyntaxRootSynchronously(cancellationToken);
71 72 73 74 75 76
            var syntaxFactsService = document.GetLanguageService<ISyntaxFactsService>();
            var containingTypeDeclaration = syntaxFactsService.GetContainingTypeDeclaration(syntaxRoot, caretPosition);

            if (containingTypeDeclaration != null)
            {
                var semanticModel = document.GetSemanticModelAsync(cancellationToken).WaitAndGetResult(cancellationToken);
77 78 79 80

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

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

88
                    if (Equals(containingTypeSymbol, candidateTypeSymbol))
89 90 91 92 93 94 95
                    {
                        // 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;
                    }
                }
96 97
            }

98
            return true;
99 100 101
        }
    }
}