|
// 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.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.MetadataAsSource;
using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.LanguageServer.Protocol;
using Roslyn.Utilities;
using LSP = Roslyn.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.LanguageServer.Handler
{
internal abstract class AbstractGoToDefinitionHandler : ILspServiceDocumentRequestHandler<LSP.TextDocumentPositionParams, LSP.Location[]?>
{
private readonly IMetadataAsSourceFileService _metadataAsSourceFileService;
private readonly IGlobalOptionService _globalOptions;
public AbstractGoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSourceFileService, IGlobalOptionService globalOptions)
{
_metadataAsSourceFileService = metadataAsSourceFileService;
_globalOptions = globalOptions;
}
public bool MutatesSolutionState => false;
public bool RequiresLSPSolution => true;
public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.TextDocumentPositionParams request) => request.TextDocument;
public abstract Task<LSP.Location[]?> HandleRequestAsync(TextDocumentPositionParams request, 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;
if (workspace is null || document is null)
return SpecializedTasks.Null<LSP.Location[]>();
var linePosition = ProtocolConversions.PositionToLinePosition(request.Position);
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 forSymbolType, LinePosition linePosition, CancellationToken cancellationToken)
{
var locations = ArrayBuilder<LSP.Location>.GetInstance();
var position = await document.GetPositionFromLinePositionAsync(linePosition, cancellationToken).ConfigureAwait(false);
var service = document.GetLanguageService<INavigableItemsService>();
if (service is null)
return null;
var definitions = await service.GetNavigableItemsAsync(document, position, forSymbolType, cancellationToken).ConfigureAwait(false);
if (definitions.Length > 0)
{
foreach (var definition in definitions)
{
if (!ShouldInclude(definition, forSymbolType))
continue;
var location = await ProtocolConversions.TextSpanToLocationAsync(
await definition.Document.GetRequiredDocumentAsync(document.Project.Solution, cancellationToken).ConfigureAwait(false),
definition.SourceSpan,
definition.IsStale,
cancellationToken).ConfigureAwait(false);
locations.AddIfNotNull(location);
}
}
else if (document.SupportsSemanticModel && metadataAsSourceFileService != null)
{
// 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))
{
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),
});
}
}
return locations.ToArrayAndFree();
// local functions
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 (!forSymbolType)
{
return true;
}
switch (item.Glyph)
{
case Glyph.ClassPublic:
case Glyph.ClassProtected:
case Glyph.ClassPrivate:
case Glyph.ClassInternal:
case Glyph.DelegatePublic:
case Glyph.DelegateProtected:
case Glyph.DelegatePrivate:
case Glyph.DelegateInternal:
case Glyph.EnumPublic:
case Glyph.EnumProtected:
case Glyph.EnumPrivate:
case Glyph.EnumInternal:
case Glyph.EventPublic:
case Glyph.EventProtected:
case Glyph.EventPrivate:
case Glyph.EventInternal:
case Glyph.InterfacePublic:
case Glyph.InterfaceProtected:
case Glyph.InterfacePrivate:
case Glyph.InterfaceInternal:
case Glyph.ModulePublic:
case Glyph.ModuleProtected:
case Glyph.ModulePrivate:
case Glyph.ModuleInternal:
case Glyph.StructurePublic:
case Glyph.StructureProtected:
case Glyph.StructurePrivate:
case Glyph.StructureInternal:
return true;
default:
return false;
}
}
}
}
}
|