예제 #1
0
        public async Task GetSymbolsAsync(GoToSymbolContext context)
        {
            var document          = context.Document;
            var position          = context.Position;
            var cancellationToken = context.CancellationToken;
            var service           = document.GetRequiredLanguageService <IGoToDefinitionSymbolService>();

            // [includeType: false]
            // Enable Ctrl+Click on tokens with aliased, referenced or declared symbol.
            // If the token has none of those but does have a type (mostly literals), we're not interested
            var(symbol, span) = await service.GetSymbolAndBoundSpanAsync(document, position, includeType : false, cancellationToken).ConfigureAwait(false);

            if (symbol == null)
            {
                return;
            }

            var solution    = document.Project.Solution;
            var definitions = await GoToDefinitionHelpers.GetDefinitionsAsync(symbol, solution, thirdPartyNavigationAllowed : true, cancellationToken).ConfigureAwait(false);

            foreach (var definition in definitions)
            {
                if (await definition.CanNavigateToAsync(solution.Workspace, cancellationToken).ConfigureAwait(false))
                {
                    context.AddItem(WellKnownSymbolTypes.Definition, definition);
                }
            }

            context.Span = span;
        }
예제 #2
0
        private async Task <INavigableLocation?> GetAlternativeLocationIfAlreadyOnDefinitionAsync(
            Document document, int position, ISymbol symbol, CancellationToken cancellationToken)
        {
            var project  = document.Project;
            var solution = project.Solution;

            var sourceLocations = symbol.Locations.WhereAsArray(loc => loc.IsInSource);

            if (sourceLocations.Length != 1)
            {
                return(null);
            }

            var definitionLocation = sourceLocations[0];

            if (!definitionLocation.SourceSpan.IntersectsWith(position))
            {
                return(null);
            }

            var definitionTree     = definitionLocation.SourceTree;
            var definitionDocument = solution.GetDocument(definitionTree);

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

            // Ok, we were already on the definition. Look for better symbols we could show results
            // for instead. For now, just see if we're on an interface member impl. If so, we can
            // instead navigate to the actual interface member.
            //
            // In the future we can expand this with other mappings if appropriate.
            var interfaceImpls = symbol.ExplicitOrImplicitInterfaceImplementations();

            if (interfaceImpls.Length == 0)
            {
                return(null);
            }

            var title = string.Format(EditorFeaturesResources._0_implemented_members,
                                      FindUsagesHelpers.GetDisplayName(symbol));

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

            foreach (var impl in interfaceImpls)
            {
                builder.AddRange(await GoToDefinitionHelpers.GetDefinitionsAsync(
                                     impl, solution, thirdPartyNavigationAllowed: false, cancellationToken).ConfigureAwait(false));
            }

            var definitions = builder.ToImmutable();

            return(await _streamingPresenter.GetStreamingLocationAsync(
                       _threadingContext, solution.Workspace, title, definitions, cancellationToken).ConfigureAwait(false));
        }
예제 #3
0
        public async Task <INavigableLocation?> FindDefinitionLocationAsync(Document document, int position, CancellationToken cancellationToken)
        {
            var symbolService = document.GetRequiredLanguageService <IGoToDefinitionSymbolService>();
            var targetPositionOfControlFlow = await symbolService.GetTargetIfControlFlowAsync(
                document, position, cancellationToken).ConfigureAwait(false);

            if (targetPositionOfControlFlow is not null)
            {
                return(await GetNavigableLocationAsync(
                           document, targetPositionOfControlFlow.Value, cancellationToken).ConfigureAwait(false));
            }

            // Try to compute the referenced symbol and attempt to go to definition for the symbol.
            var(symbol, _) = await symbolService.GetSymbolAndBoundSpanAsync(
                document, position, includeType : true, cancellationToken).ConfigureAwait(false);

            if (symbol is null)
            {
                return(null);
            }

            // if the symbol only has a single source location, and we're already on it,
            // try to see if there's a better symbol we could navigate to.
            var remappedLocation = await GetAlternativeLocationIfAlreadyOnDefinitionAsync(
                document, position, symbol, cancellationToken).ConfigureAwait(false);

            if (remappedLocation != null)
            {
                return(remappedLocation);
            }

            var isThirdPartyNavigationAllowed = await IsThirdPartyNavigationAllowedAsync(
                symbol, position, document, cancellationToken).ConfigureAwait(false);

            return(await GoToDefinitionHelpers.GetDefinitionLocationAsync(
                       symbol,
                       document.Project.Solution,
                       _threadingContext,
                       _streamingPresenter,
                       thirdPartyNavigationAllowed : isThirdPartyNavigationAllowed,
                       cancellationToken : cancellationToken).ConfigureAwait(false));
        }
예제 #4
0
        public bool TryGoToDefinition(Document document, int position, CancellationToken cancellationToken)
        {
            var symbolService = document.GetRequiredLanguageService <IGoToDefinitionSymbolService>();
            var targetPositionOfControlFlow = symbolService.GetTargetIfControlFlowAsync(document, position, cancellationToken).WaitAndGetResult(cancellationToken);

            if (targetPositionOfControlFlow is not null)
            {
                return(TryNavigateToSpan(document, targetPositionOfControlFlow.Value, cancellationToken));
            }

            // Try to compute the referenced symbol and attempt to go to definition for the symbol.
            var(symbol, _) = symbolService.GetSymbolAndBoundSpanAsync(document, position, includeType: true, cancellationToken).WaitAndGetResult(cancellationToken);
            if (symbol is null)
            {
                return(false);
            }

            // if the symbol only has a single source location, and we're already on it,
            // try to see if there's a better symbol we could navigate to.
            var remapped = TryGoToAlternativeLocationIfAlreadyOnDefinition(document, position, symbol, cancellationToken);

            if (remapped)
            {
                return(true);
            }

            var isThirdPartyNavigationAllowed = IsThirdPartyNavigationAllowed(symbol, position, document, cancellationToken);

            return(GoToDefinitionHelpers.TryGoToDefinition(
                       symbol,
                       document.Project.Solution,
                       _threadingContext,
                       _streamingPresenter,
                       thirdPartyNavigationAllowed: isThirdPartyNavigationAllowed,
                       cancellationToken: cancellationToken));
        }
예제 #5
0
        private bool TryGoToAlternativeLocationIfAlreadyOnDefinition(
            Document document, int position,
            ISymbol symbol, CancellationToken cancellationToken)
        {
            var project  = document.Project;
            var solution = project.Solution;

            var sourceLocations = symbol.Locations.WhereAsArray(loc => loc.IsInSource);

            if (sourceLocations.Length != 1)
            {
                return(false);
            }

            var definitionLocation = sourceLocations[0];

            if (!definitionLocation.SourceSpan.IntersectsWith(position))
            {
                return(false);
            }

            var definitionTree     = definitionLocation.SourceTree;
            var definitionDocument = solution.GetDocument(definitionTree);

            if (definitionDocument != document)
            {
                return(false);
            }

            // Ok, we were already on the definition. Look for better symbols we could show results
            // for instead. For now, just see if we're on an interface member impl. If so, we can
            // instead navigate to the actual interface member.
            //
            // In the future we can expand this with other mappings if appropriate.
            var interfaceImpls = symbol.ExplicitOrImplicitInterfaceImplementations();

            if (interfaceImpls.Length == 0)
            {
                return(false);
            }

            var title = string.Format(EditorFeaturesResources._0_implemented_members,
                                      FindUsagesHelpers.GetDisplayName(symbol));

            return(_threadingContext.JoinableTaskFactory.Run(async() =>
            {
                using var _ = ArrayBuilder <DefinitionItem> .GetInstance(out var definitions);
                foreach (var impl in interfaceImpls)
                {
                    // Use ConfigureAwait(true) here.  Not for a correctness requirements, but because we're
                    // already blocking the UI thread by being in a JTF.Run call.  So we might as well try to
                    // continue to use the blocking UI thread to do as much work as possible instead of making
                    // it wait for threadpool threads to be available to process the work.
                    definitions.AddRange(await GoToDefinitionHelpers.GetDefinitionsAsync(
                                             impl, solution, thirdPartyNavigationAllowed: false, cancellationToken).ConfigureAwait(true));
                }

                return await _streamingPresenter.TryNavigateToOrPresentItemsAsync(
                    _threadingContext, solution.Workspace, title, definitions.ToImmutable(), cancellationToken).ConfigureAwait(true);
            }));
        }