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

Add reference handler #11

Merged
merged 8 commits into from
Oct 12, 2023
72 changes: 72 additions & 0 deletions src/Facility.LanguageServer/FsdDefinitionUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,68 @@ from field in service.GetDescendants().OfType<ServiceFieldInfo>()
select service.FindMember(name)).FirstOrDefault();
}

public static IEnumerable<ServicePart> GetReferencedServicePartsAtPosition(this ServiceInfo service, Position requestPosition, bool includeDeclaration)
{
var members = service.GetDescendants().OfType<ServiceMemberInfo>().ToList().AsReadOnly();

// memberAtCursor will be null if the cursor is not on a member name.
var memberAtCursor = members
.Select(member =>
{
var part = member.GetPart(ServicePartKind.Name);
var name = member.Name;

return (part, name);
})
.Where(x => x.part != null && requestPosition >= x.part.Position && requestPosition < x.part.EndPosition)
.Select(x => (x.name, x.part))
.FirstOrDefault();

var fields = service.GetDescendants().OfType<ServiceFieldInfo>().ToList().AsReadOnly();

// fieldTypeNameAtCursor will be null if the cursor is not on a field type name.
var fieldTypeNameAtCursor = fields
.Select(field =>
{
var part = field.GetPart(ServicePartKind.TypeName);
var typeName = service.GetFieldTypeName(field);

return (part, typeName);
})
.Where(x => x.part != null && requestPosition >= x.part.Position && requestPosition < x.part.EndPosition)
.Select(x => x.typeName)
.FirstOrDefault();

var referencedFields = fields
.Select(field =>
{
var part = field.GetPart(ServicePartKind.TypeName);
var typeName = service.GetFieldTypeName(field);

var type = service.GetFieldType(field);
var memberTypeName = type?.GetMemberTypeName();

return (part, memberTypeName, typeName);
})
.Where(x => x.part != null && ((memberAtCursor.name != null && x.memberTypeName == memberAtCursor.name) || x.typeName == fieldTypeNameAtCursor))
.Select(x => x.part);

var referencedMembers = members
.Select(member =>
{
var part = member.GetPart(ServicePartKind.Name);
var name = member.Name;

return (part, name);
})
.Where(x => x.part != null && x.name == fieldTypeNameAtCursor)
.Select(x => x.part);

return includeDeclaration
? referencedFields.Concat(referencedMembers)
: referencedFields;
}

private static string GetMemberTypeName(this ServiceTypeInfo type)
{
switch (type.Kind)
Expand All @@ -36,5 +98,15 @@ private static string GetMemberTypeName(this ServiceTypeInfo type)
}
return null;
}

private static string GetFieldTypeName(this ServiceInfo service, ServiceFieldInfo field)
{
var type = service.GetFieldType(field);

while (type?.ValueType is { } valueType)
type = valueType;

return type?.ToString() ?? field.TypeName;
}
}
}
47 changes: 47 additions & 0 deletions src/Facility.LanguageServer/FsdReferenceHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Facility.Definition;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;

namespace Facility.LanguageServer
{
internal sealed class FsdReferenceHandler : FsdRequestHandler, IReferencesHandler
{
public FsdReferenceHandler(
ILanguageServerFacade router,
ILanguageServerConfiguration configuration,
IDictionary<DocumentUri, ServiceInfo> serviceInfos)
: base(router, configuration, serviceInfos)
{
}

public ReferenceRegistrationOptions GetRegistrationOptions(ReferenceCapability capability, ClientCapabilities clientCapabilities) =>
new ReferenceRegistrationOptions()
{
DocumentSelector = DocumentSelector,
};

public async Task<LocationContainer> Handle(ReferenceParams request, CancellationToken cancellationToken)
{
var documentUri = request.TextDocument.Uri;
var service = GetService(documentUri);
if (service == null)
return null;

var position = new Position(request.Position);

var serviceParts = service.GetReferencedServicePartsAtPosition(position, request.Context.IncludeDeclaration);

var locations = serviceParts.Select(part => new Location()
{
Uri = documentUri,
Range = new Range(new Position(part!.Position), new Position(part.EndPosition)),
});

return new LocationContainer(locations);
}
}
}
1 change: 1 addition & 0 deletions src/Facility.LanguageServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
.AddSingleton<IDictionary<DocumentUri, ServiceInfo>>(serviceInfos))
.WithHandler<FsdSyncHandler>()
.WithHandler<FsdDefinitionHandler>()
.WithHandler<FsdReferenceHandler>()
.WithHandler<FsdHoverHandler>()
.WithServices(
x => x
Expand Down