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; }
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)); }
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)); }
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)); }
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); })); }