142 lines
6.7 KiB
C#
142 lines
6.7 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 => CreateFuncPrototypeMessage(funcNode.Prototype),
|
|
FuncPrototypeNode funcPrototypeNode => CreateFuncPrototypeMessage(funcPrototypeNode),
|
|
FuncIdentifierNode funcIdentifierNode => CreateFuncIdentifierMessage(funcIdentifierNode, compilationUnit),
|
|
FuncParameterNode funcParameterNode => CreateTypeNameMessage("Function parameter", funcParameterNode.Name, funcParameterNode.Type),
|
|
VariableIdentifierNode variableIdentifierNode => CreateTypeNameMessage("Variable", variableIdentifierNode.Name, variableIdentifierNode.Type),
|
|
VariableDeclarationNode variableDeclarationNode => CreateTypeNameMessage("Variable declaration", variableDeclarationNode.Name, variableDeclarationNode.Type),
|
|
StructFieldAccessNode structFieldAccessNode => CreateTypeNameMessage("Struct field", $"{structFieldAccessNode.Target.Type}.{structFieldAccessNode.Field}", 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()),
|
|
// 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 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 CreateFuncIdentifierMessage(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 CreateFuncPrototypeMessage(func);
|
|
}
|
|
|
|
private static string CreateFuncPrototypeMessage(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.Module}::{funcPrototypeNode.Name}`
|
|
```nub
|
|
{externText}func {funcPrototypeNode.Module}::{funcPrototypeNode.Name}({parameterText}): {funcPrototypeNode.ReturnType}
|
|
```
|
|
""";
|
|
}
|
|
} |