This repository has been archived on 2025-10-23. You can view files and clone it, but cannot push or open issues or pull requests.
Files
nub-lang-archive/compiler/NubLang.LSP/HoverHandler.cs
2025-10-23 14:16:23 +02:00

153 lines
6.8 KiB
C#

using System.Globalization;
using NubLang.Ast;
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 compilationUnit = workspaceManager.GetCompilationUnit(request.TextDocument.Uri);
if (compilationUnit == null)
{
return null;
}
var line = request.Position.Line;
var character = request.Position.Character;
var hoveredNode = compilationUnit.DeepestNodeAtPosition(line, character);
if (hoveredNode == null)
{
return null;
}
var message = CreateMessage(hoveredNode, compilationUnit);
if (message == null)
{
return null;
}
return new Hover
{
Contents = new MarkedStringsOrMarkupContent(new MarkupContent
{
Value = message,
Kind = MarkupKind.Markdown,
})
};
}
private static string? CreateMessage(Node hoveredNode, CompilationUnit compilationUnit)
{
return hoveredNode switch
{
FuncNode funcNode => CreateFuncPrototypeNodeMessage(funcNode.Prototype),
FuncPrototypeNode funcPrototypeNode => CreateFuncPrototypeNodeMessage(funcPrototypeNode),
FuncIdentifierNode funcIdentifierNode => CreateFuncIdentifierNodeMessage(funcIdentifierNode, compilationUnit),
FuncParameterNode funcParameterNode => CreateVariableIdentifierNodeMessage(funcParameterNode.Name, funcParameterNode.Type),
VariableIdentifierNode variableIdentifierNode => CreateVariableIdentifierNodeMessage(variableIdentifierNode.Name, variableIdentifierNode.Type),
VariableDeclarationNode variableDeclarationNode => CreateVariableIdentifierNodeMessage(variableDeclarationNode.Name, variableDeclarationNode.Type),
CStringLiteralNode cStringLiteralNode => CreateLiteralNodeMessage(cStringLiteralNode.Type, '"' + cStringLiteralNode.Value + '"'),
StringLiteralNode stringLiteralNode => CreateLiteralNodeMessage(stringLiteralNode.Type, '"' + stringLiteralNode.Value + '"'),
BoolLiteralNode boolLiteralNode => CreateLiteralNodeMessage(boolLiteralNode.Type, boolLiteralNode.Value.ToString()),
Float32LiteralNode float32LiteralNode => CreateLiteralNodeMessage(float32LiteralNode.Type, float32LiteralNode.Value.ToString(CultureInfo.InvariantCulture)),
Float64LiteralNode float64LiteralNode => CreateLiteralNodeMessage(float64LiteralNode.Type, float64LiteralNode.Value.ToString(CultureInfo.InvariantCulture)),
I8LiteralNode i8LiteralNode => CreateLiteralNodeMessage(i8LiteralNode.Type, i8LiteralNode.Value.ToString()),
I16LiteralNode i16LiteralNode => CreateLiteralNodeMessage(i16LiteralNode.Type, i16LiteralNode.Value.ToString()),
I32LiteralNode i32LiteralNode => CreateLiteralNodeMessage(i32LiteralNode.Type, i32LiteralNode.Value.ToString()),
I64LiteralNode i64LiteralNode => CreateLiteralNodeMessage(i64LiteralNode.Type, i64LiteralNode.Value.ToString()),
U8LiteralNode u8LiteralNode => CreateLiteralNodeMessage(u8LiteralNode.Type, u8LiteralNode.Value.ToString()),
U16LiteralNode u16LiteralNode => CreateLiteralNodeMessage(u16LiteralNode.Type, u16LiteralNode.Value.ToString()),
U32LiteralNode u32LiteralNode => CreateLiteralNodeMessage(u32LiteralNode.Type, u32LiteralNode.Value.ToString()),
U64LiteralNode u64LiteralNode => CreateLiteralNodeMessage(u64LiteralNode.Type, u64LiteralNode.Value.ToString()),
// Expressions can have a generic fallback showing the resulting type
ExpressionNode expressionNode => CreateGenericExpressionNodeMessage(expressionNode),
// Explicit null returns, can be removed when the default is null instead of the debug type
BlockNode => null,
StatementNode statementNode => CreateGenericStatementNodeMessage(statementNode),
_ => hoveredNode.GetType().Name
};
}
private static string CreateLiteralNodeMessage(NubType type, string value)
{
return $"""
**Literal** `{type}`
```nub
{value}: {type}
```
""";
}
private static string CreateVariableIdentifierNodeMessage(string name, NubType type)
{
return $"""
**Variable** `{name}`
```nub
{name}: {type}
```
""";
}
private static string CreateFuncIdentifierNodeMessage(FuncIdentifierNode funcIdentifierNode, CompilationUnit compilationUnit)
{
var func = compilationUnit.ImportedFunctions.FirstOrDefault(x => x.Module == funcIdentifierNode.Module && x.Name == funcIdentifierNode.Name);
if (func == null)
{
return $"""
**Function** `{funcIdentifierNode.Module}::{funcIdentifierNode.Name}`
```nub
// Declaration not found
```
""";
}
return CreateFuncPrototypeNodeMessage(func);
}
private static string CreateFuncPrototypeNodeMessage(FuncPrototypeNode funcPrototypeNode)
{
var parameterText = string.Join(", ", funcPrototypeNode.Parameters.Select(x => $"{x.Name}: {x.Type}"));
var externText = funcPrototypeNode.ExternSymbol != null ? $"extern \"{funcPrototypeNode.ExternSymbol}\" " : "";
return $"""
**Function** `{funcPrototypeNode.Name}`
```nub
{externText}func {funcPrototypeNode.Name}({parameterText}): {funcPrototypeNode.ReturnType}
```
""";
}
private static string CreateGenericStatementNodeMessage(StatementNode statementNode)
{
return $"**Statement** `{statementNode.GetType().Name}`";
}
private static string CreateGenericExpressionNodeMessage(ExpressionNode expressionNode)
{
return $"""
**Expression** `{expressionNode.GetType().Name}`
```nub
{expressionNode.Type}
```
""";
}
}