Files
nub-lang/compiler/NubLang.LSP/HoverHandler.cs
nub31 085f7a1a6a ...
2025-11-03 12:52:17 +01:00

203 lines
9.0 KiB
C#

using System.Globalization;
using NubLang.Ast;
using NubLang.Modules;
using NubLang.Types;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
namespace NubLang.LSP;
internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBase
{
protected override HoverRegistrationOptions CreateRegistrationOptions(HoverCapability capability, ClientCapabilities clientCapabilities)
{
return new HoverRegistrationOptions
{
DocumentSelector = TextDocumentSelector.ForLanguage("nub")
};
}
public override Task<Hover?> Handle(HoverParams request, CancellationToken cancellationToken)
{
return Task.FromResult(HandleSync(request, cancellationToken));
}
private Hover? HandleSync(HoverParams request, CancellationToken cancellationToken)
{
var topLevelNodes = workspaceManager.GetTopLevelNodes(request.TextDocument.Uri.GetFileSystemPath());
var moduleDecl = topLevelNodes.OfType<ModuleNode>().FirstOrDefault();
if (moduleDecl == null)
{
return null;
}
var moduleRepository = workspaceManager.GetModuleRepository();
if (!moduleRepository.TryGet(moduleDecl.NameToken, out var module))
{
return null;
}
var line = request.Position.Line;
var character = request.Position.Character;
var hoveredNode = topLevelNodes.DeepestNodeAtPosition(line, character);
if (hoveredNode == null)
{
return null;
}
var message = CreateMessage(hoveredNode, moduleRepository, module, line, character);
if (message == null)
{
return null;
}
return new Hover
{
Contents = new MarkedStringsOrMarkupContent(new MarkupContent
{
Value = message,
Kind = MarkupKind.Markdown,
})
};
}
private static string? CreateMessage(Node hoveredNode, ModuleRepository repository, ModuleRepository.Module currentModule, int line, int character)
{
return hoveredNode switch
{
FuncNode funcNode => CreateFuncPrototypeMessage(funcNode.Prototype),
FuncPrototypeNode funcPrototypeNode => CreateFuncPrototypeMessage(funcPrototypeNode),
LocalFuncIdentifierNode funcIdentifierNode => CreateLocalFuncIdentifierMessage(funcIdentifierNode, currentModule),
ModuleFuncIdentifierNode funcIdentifierNode => CreateModuleFuncIdentifierMessage(funcIdentifierNode, repository),
FuncParameterNode funcParameterNode => CreateTypeNameMessage("Function parameter", funcParameterNode.NameToken.Value, funcParameterNode.Type),
VariableIdentifierNode variableIdentifierNode => CreateTypeNameMessage("Variable", variableIdentifierNode.NameToken.Value, variableIdentifierNode.Type),
VariableDeclarationNode variableDeclarationNode => CreateTypeNameMessage("Variable declaration", variableDeclarationNode.NameToken.Value, variableDeclarationNode.Type),
StructFieldAccessNode structFieldAccessNode => CreateTypeNameMessage("Struct field", $"{structFieldAccessNode.Target.Type}.{structFieldAccessNode.FieldToken.Value}", structFieldAccessNode.Type),
CStringLiteralNode cStringLiteralNode => CreateLiteralMessage(cStringLiteralNode.Type, '"' + cStringLiteralNode.Value + '"'),
StringLiteralNode stringLiteralNode => CreateLiteralMessage(stringLiteralNode.Type, '"' + stringLiteralNode.Value + '"'),
BoolLiteralNode boolLiteralNode => CreateLiteralMessage(boolLiteralNode.Type, boolLiteralNode.Value.ToString()),
Float32LiteralNode float32LiteralNode => CreateLiteralMessage(float32LiteralNode.Type, float32LiteralNode.Value.ToString(CultureInfo.InvariantCulture)),
Float64LiteralNode float64LiteralNode => CreateLiteralMessage(float64LiteralNode.Type, float64LiteralNode.Value.ToString(CultureInfo.InvariantCulture)),
I8LiteralNode i8LiteralNode => CreateLiteralMessage(i8LiteralNode.Type, i8LiteralNode.Value.ToString()),
I16LiteralNode i16LiteralNode => CreateLiteralMessage(i16LiteralNode.Type, i16LiteralNode.Value.ToString()),
I32LiteralNode i32LiteralNode => CreateLiteralMessage(i32LiteralNode.Type, i32LiteralNode.Value.ToString()),
I64LiteralNode i64LiteralNode => CreateLiteralMessage(i64LiteralNode.Type, i64LiteralNode.Value.ToString()),
U8LiteralNode u8LiteralNode => CreateLiteralMessage(u8LiteralNode.Type, u8LiteralNode.Value.ToString()),
U16LiteralNode u16LiteralNode => CreateLiteralMessage(u16LiteralNode.Type, u16LiteralNode.Value.ToString()),
U32LiteralNode u32LiteralNode => CreateLiteralMessage(u32LiteralNode.Type, u32LiteralNode.Value.ToString()),
U64LiteralNode u64LiteralNode => CreateLiteralMessage(u64LiteralNode.Type, u64LiteralNode.Value.ToString()),
StructInitializerNode structInitializerNode => CreateStructInitializerMessage(structInitializerNode, line, character),
// Expressions can have a generic fallback showing the resulting type
ExpressionNode expressionNode => $"""
**Expression** `{expressionNode.GetType().Name}`
```nub
{expressionNode.Type}
```
""",
BlockNode => null,
_ => hoveredNode.GetType().Name
};
}
private static string CreateStructInitializerMessage(StructInitializerNode structInitializerNode, int line, int character)
{
var hoveredInitializerName = structInitializerNode
.Initializers
.Select(x => x.Key)
.FirstOrDefault(x => x.ContainsPosition(line, character));
var structType = (NubStructType)structInitializerNode.Type;
if (hoveredInitializerName != null)
{
var field = structType.Fields.FirstOrDefault(x => x.Name == hoveredInitializerName.Value);
if (field != null)
{
return $"""
**Field** in `{structType}`
```nub
{hoveredInitializerName.Value}: {field.Type}
```
""";
}
else
{
return $"""
**Field** in `{structType}`
```nub
// Field not found
```
""";
}
}
return $"**Struct initializer** `{structType}`";
}
private static string CreateLiteralMessage(NubType type, string value)
{
return $"""
**Literal** `{type}`
```nub
{value}: {type}
```
""";
}
private static string CreateTypeNameMessage(string description, string name, NubType type)
{
return $"""
**{description}** `{name}`
```nub
{name}: {type}
```
""";
}
private static string CreateLocalFuncIdentifierMessage(LocalFuncIdentifierNode funcIdentifierNode, ModuleRepository.Module currentModule)
{
if (!currentModule.TryResolveFunc(funcIdentifierNode.NameToken, out var func, out _))
{
return $"""
**Function** `{funcIdentifierNode.NameToken.Value}`
```nub
// Declaration not found
```
""";
}
return CreateFuncPrototypeMessage(func);
}
private static string CreateModuleFuncIdentifierMessage(ModuleFuncIdentifierNode funcIdentifierNode, ModuleRepository repository)
{
if (!repository.TryGet(funcIdentifierNode.ModuleToken, out var module) || !module.TryResolveFunc(funcIdentifierNode.NameToken, out var func, out _))
{
return $"""
**Function** `{funcIdentifierNode.ModuleToken.Value}::{funcIdentifierNode.NameToken.Value}`
```nub
// Declaration not found
```
""";
}
return CreateFuncPrototypeMessage(func);
}
private static string CreateFuncPrototypeMessage(FuncPrototypeNode funcPrototypeNode)
{
var parameterText = string.Join(", ", funcPrototypeNode.Parameters.Select(x => $"{x.NameToken.Value}: {x.Type}"));
var externText = funcPrototypeNode.ExternSymbolToken != null ? $"extern \"{funcPrototypeNode.ExternSymbolToken.Value}\" " : "";
return $"""
**Function** `{funcPrototypeNode.NameToken.Value}`
```nub
{externText}func {funcPrototypeNode.NameToken.Value}({parameterText}): {funcPrototypeNode.ReturnType}
```
""";
}
}