Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable TypeDefinitionProvider LSP capability #75819

Merged
merged 5 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Change GoToTypeDefinition behavior to lookup the symbol's type and re…
…turn its location
  • Loading branch information
JoeRobich committed Nov 8, 2024
commit ea244f1fcc44bf5f79de21327229398771bd31de
2 changes: 1 addition & 1 deletion src/EditorFeatures/Core.Wpf/Peek/PeekableItemSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private async Task AugumentPeekSessionAsync(
if (service == null)
return;

var navigableItems = await service.GetNavigableItemsAsync(document, triggerPoint.Position, cancellationToken).ConfigureAwait(false);
var navigableItems = await service.GetNavigableItemsAsync(document, triggerPoint.Position, forSymbolType: false, cancellationToken).ConfigureAwait(false);
await foreach (var item in GetPeekableItemsForNavigableItemsAsync(
navigableItems, document.Project, _peekResultFactory, cancellationToken).ConfigureAwait(false))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ private static async Task<ImmutableArray<INavigableItem>> GetNavigableItemsAsync
// Try IFindDefinitionService first. Until partners implement this, it could fail to find a service, so fall back if it's null.
var findDefinitionService = document.GetLanguageService<INavigableItemsService>();
return findDefinitionService != null
? await findDefinitionService.GetNavigableItemsAsync(document, position, cancellationToken).ConfigureAwait(false)
? await findDefinitionService.GetNavigableItemsAsync(document, position, forSymbolType: false, cancellationToken).ConfigureAwait(false)
: [];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Navigation;
internal abstract class AbstractNavigableItemsService : INavigableItemsService
{
public async Task<ImmutableArray<INavigableItem>> GetNavigableItemsAsync(
Document document, int position, CancellationToken cancellationToken)
Document document, int position, bool forSymbolType, CancellationToken cancellationToken)
{
var symbolService = document.GetRequiredLanguageService<IGoToDefinitionSymbolService>();

Expand Down Expand Up @@ -46,6 +46,22 @@ await GetSymbolAsync(document.WithFrozenPartialSemantics(cancellationToken)).Con
if (symbol is null or IErrorTypeSymbol)
return null;

if (forSymbolType)
{
// We have found the symbol at the position in the document. Now we need to find the symbol's type.
var typeSymbol = symbol.GetSymbolType() as ISymbol;
if (typeSymbol is null)
return null;

typeSymbol = await SymbolFinder.FindSourceDefinitionAsync(typeSymbol, solution, cancellationToken).ConfigureAwait(false) ?? typeSymbol;
typeSymbol = await GoToDefinitionFeatureHelpers.TryGetPreferredSymbolAsync(solution, typeSymbol, cancellationToken).ConfigureAwait(false);

if (typeSymbol is null or IErrorTypeSymbol)
return null;

symbol = typeSymbol;
}

return (symbol, solution);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
namespace Microsoft.CodeAnalysis.Navigation;

/// <summary>
/// Service used for features that want to find all the locations to potentially navigate to for a symbol at a
/// particular location, with enough information provided to display those locations in a rich fashion. Differs from
/// <see cref="IDefinitionLocationService"/> in that this can show a rich display of the items, not just navigate to
/// them.
/// Service used for features that want to find all the locations to potentially navigate to for a symbol or its type
/// at a particular location, with enough information provided to display those locations in a rich fashion. Differs
/// from <see cref="IDefinitionLocationService"/> in that this can show a rich display of the items, not just navigate
/// to them.
/// </summary>
internal interface INavigableItemsService : ILanguageService
{
/// <summary>
/// Finds the definitions for the symbol at the specific position in the document.
/// Finds the definitions for the symbol or its type at the specific position in the document.
/// </summary>
Task<ImmutableArray<INavigableItem>> GetNavigableItemsAsync(Document document, int position, CancellationToken cancellationToken);
Task<ImmutableArray<INavigableItem>> GetNavigableItemsAsync(Document document, int position, bool forSymbolType, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public AbstractGoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSour

public abstract Task<LSP.Location[]?> HandleRequestAsync(TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken);

protected Task<LSP.Location[]?> GetDefinitionAsync(LSP.TextDocumentPositionParams request, bool typeOnly, RequestContext context, CancellationToken cancellationToken)
protected Task<LSP.Location[]?> GetDefinitionAsync(LSP.TextDocumentPositionParams request, bool forSymbolType, RequestContext context, CancellationToken cancellationToken)
{
var workspace = context.Workspace;
var document = context.Document;
Expand All @@ -44,10 +44,10 @@ public AbstractGoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSour

var linePosition = ProtocolConversions.PositionToLinePosition(request.Position);

return GetDefinitionsAsync(_globalOptions, _metadataAsSourceFileService, workspace, document, typeOnly, linePosition, cancellationToken);
return GetDefinitionsAsync(_globalOptions, _metadataAsSourceFileService, workspace, document, forSymbolType, linePosition, cancellationToken);
}

internal static async Task<LSP.Location[]?> GetDefinitionsAsync(IGlobalOptionService globalOptions, IMetadataAsSourceFileService? metadataAsSourceFileService, Workspace workspace, Document document, bool typeOnly, LinePosition linePosition, CancellationToken cancellationToken)
internal static async Task<LSP.Location[]?> GetDefinitionsAsync(IGlobalOptionService globalOptions, IMetadataAsSourceFileService? metadataAsSourceFileService, Workspace workspace, Document document, bool forSymbolType, LinePosition linePosition, CancellationToken cancellationToken)
{
var locations = ArrayBuilder<LSP.Location>.GetInstance();
var position = await document.GetPositionFromLinePositionAsync(linePosition, cancellationToken).ConfigureAwait(false);
Expand All @@ -56,12 +56,12 @@ public AbstractGoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSour
if (service is null)
return null;

var definitions = await service.GetNavigableItemsAsync(document, position, cancellationToken).ConfigureAwait(false);
var definitions = await service.GetNavigableItemsAsync(document, position, forSymbolType, cancellationToken).ConfigureAwait(false);
if (definitions.Length > 0)
{
foreach (var definition in definitions)
{
if (!ShouldInclude(definition, typeOnly))
if (!ShouldInclude(definition, forSymbolType))
continue;

var location = await ProtocolConversions.TextSpanToLocationAsync(
Expand All @@ -76,35 +76,35 @@ await definition.Document.GetRequiredDocumentAsync(document.Project.Solution, ca
{
// No definition found - see if we can get metadata as source but that's only applicable for C#\VB.
var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false);
if (forSymbolType)
symbol = symbol?.GetSymbolType();

if (symbol != null && metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
{
if (!typeOnly || symbol is ITypeSymbol)
var options = globalOptions.GetMetadataAsSourceOptions();
var declarationFile = await metadataAsSourceFileService.GetGeneratedFileAsync(workspace, document.Project, symbol, signaturesOnly: false, options: options, cancellationToken: cancellationToken).ConfigureAwait(false);

var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span;
locations.Add(new LSP.Location
{
var options = globalOptions.GetMetadataAsSourceOptions();
var declarationFile = await metadataAsSourceFileService.GetGeneratedFileAsync(workspace, document.Project, symbol, signaturesOnly: false, options: options, cancellationToken: cancellationToken).ConfigureAwait(false);

var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span;
locations.Add(new LSP.Location
{
Uri = ProtocolConversions.CreateAbsoluteUri(declarationFile.FilePath),
Range = ProtocolConversions.LinePositionToRange(linePosSpan),
});
}
Uri = ProtocolConversions.CreateAbsoluteUri(declarationFile.FilePath),
Range = ProtocolConversions.LinePositionToRange(linePosSpan),
});
}
}

return locations.ToArrayAndFree();

// local functions
static bool ShouldInclude(INavigableItem item, bool typeOnly)
static bool ShouldInclude(INavigableItem item, bool forSymbolType)
{
if (item.Glyph is Glyph.Namespace)
{
// Never return namespace symbols as part of the go to definition result.
return false;
}

if (!typeOnly)
if (!forSymbolType)
{
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ public GoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSourceFileSe
}

public override Task<LSP.Location[]?> HandleRequestAsync(LSP.TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken)
=> GetDefinitionAsync(request, typeOnly: false, context, cancellationToken);
=> GetDefinitionAsync(request, forSymbolType: false, context, cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ public GoToTypeDefinitionHandler(IMetadataAsSourceFileService metadataAsSourceFi
}

public override Task<LSP.Location[]?> HandleRequestAsync(LSP.TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken)
=> GetDefinitionAsync(request, typeOnly: true, context, cancellationToken);
=> GetDefinitionAsync(request, forSymbolType: true, context, cancellationToken);
}
}