...
This commit is contained in:
@@ -1,28 +1,195 @@
|
||||
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 : HoverHandlerBase
|
||||
internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBase
|
||||
{
|
||||
protected override HoverRegistrationOptions CreateRegistrationOptions(HoverCapability capability, ClientCapabilities clientCapabilities)
|
||||
{
|
||||
return new HoverRegistrationOptions
|
||||
{
|
||||
DocumentSelector = TextDocumentSelector.ForLanguage("nublang")
|
||||
DocumentSelector = TextDocumentSelector.ForLanguage("nub")
|
||||
};
|
||||
}
|
||||
|
||||
public override async Task<Hover?> Handle(HoverParams request, CancellationToken cancellationToken)
|
||||
public override Task<Hover?> Handle(HoverParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(HandleSync(request));
|
||||
}
|
||||
|
||||
private Hover? HandleSync(HoverParams request)
|
||||
{
|
||||
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.Functions
|
||||
.SelectMany(x => x.EnumerateDescendantsAndSelf())
|
||||
.Where(n => IsHoveringOverNode(n, line, character))
|
||||
.OrderBy(n => n.Tokens.First().Span.Start.Line)
|
||||
.ThenBy(n => n.Tokens.First().Span.Start.Column)
|
||||
.LastOrDefault();
|
||||
|
||||
if (hoveredNode == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var message = CreateMessage(hoveredNode, compilationUnit);
|
||||
if (message == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Hover
|
||||
{
|
||||
Contents = new MarkedStringsOrMarkupContent(new MarkupContent
|
||||
{
|
||||
Value = "# uwu",
|
||||
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}
|
||||
```
|
||||
""";
|
||||
}
|
||||
|
||||
private static bool IsHoveringOverNode(Node node, int line, int character)
|
||||
{
|
||||
if (node.Tokens.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var start = node.Tokens.First().Span.Start;
|
||||
var end = node.Tokens.Last().Span.End;
|
||||
|
||||
var startLine = start.Line - 1;
|
||||
var startChar = start.Column - 1;
|
||||
var endLine = end.Line - 1;
|
||||
var endChar = end.Column - 1;
|
||||
|
||||
if (line < startLine || line > endLine) return false;
|
||||
|
||||
if (line > startLine && line < endLine) return true;
|
||||
|
||||
if (startLine == endLine)
|
||||
{
|
||||
return character >= startChar && character <= endChar;
|
||||
}
|
||||
|
||||
if (line == startLine)
|
||||
{
|
||||
return character >= startChar;
|
||||
}
|
||||
|
||||
if (line == endLine)
|
||||
{
|
||||
return character <= endChar;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user