...
This commit is contained in:
355
compiler/NubLang.LSP/AstExtensions.cs
Normal file
355
compiler/NubLang.LSP/AstExtensions.cs
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
using NubLang.Ast;
|
||||||
|
|
||||||
|
namespace NubLang.LSP;
|
||||||
|
|
||||||
|
public static class AstExtensions
|
||||||
|
{
|
||||||
|
public static IEnumerable<Node> EnumerateDescendantsAndSelf(this Node node)
|
||||||
|
{
|
||||||
|
yield return node;
|
||||||
|
|
||||||
|
switch (node)
|
||||||
|
{
|
||||||
|
case FuncNode func:
|
||||||
|
{
|
||||||
|
foreach (var n in func.Prototype.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (func.Body != null)
|
||||||
|
{
|
||||||
|
foreach (var n in func.Body.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FuncPrototypeNode proto:
|
||||||
|
{
|
||||||
|
foreach (var n in proto.Parameters.SelectMany(param => param.EnumerateDescendantsAndSelf()))
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BlockNode block:
|
||||||
|
{
|
||||||
|
foreach (var n in block.Statements.SelectMany(stmt => stmt.EnumerateDescendantsAndSelf()))
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case StatementFuncCallNode stmtCall:
|
||||||
|
{
|
||||||
|
foreach (var n in stmtCall.FuncCall.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ReturnNode { Value: not null } ret:
|
||||||
|
{
|
||||||
|
foreach (var n in ret.Value.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AssignmentNode assign:
|
||||||
|
{
|
||||||
|
foreach (var n in assign.Target.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var n in assign.Value.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IfNode ifNode:
|
||||||
|
{
|
||||||
|
foreach (var n in ifNode.Condition.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var n in ifNode.Body.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ifNode.Else.HasValue)
|
||||||
|
{
|
||||||
|
if (ifNode.Else.Value.IsCase1(out var elseIfNode))
|
||||||
|
{
|
||||||
|
foreach (var n in elseIfNode.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ifNode.Else.Value.IsCase2(out var elseNode))
|
||||||
|
{
|
||||||
|
foreach (var n in elseNode.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VariableDeclarationNode decl:
|
||||||
|
{
|
||||||
|
if (decl.Assignment != null)
|
||||||
|
{
|
||||||
|
foreach (var n in decl.Assignment.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WhileNode whileNode:
|
||||||
|
{
|
||||||
|
foreach (var n in whileNode.Condition.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var n in whileNode.Body.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ForSliceNode forSlice:
|
||||||
|
{
|
||||||
|
foreach (var n in forSlice.Target.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var n in forSlice.Body.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ForConstArrayNode forConst:
|
||||||
|
{
|
||||||
|
foreach (var n in forConst.Target.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var n in forConst.Body.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DeferNode defer:
|
||||||
|
{
|
||||||
|
foreach (var n in defer.Statement.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryExpressionNode bin:
|
||||||
|
{
|
||||||
|
foreach (var n in bin.Left.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var n in bin.Right.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UnaryExpressionNode unary:
|
||||||
|
{
|
||||||
|
foreach (var n in unary.Operand.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FuncCallNode call:
|
||||||
|
{
|
||||||
|
foreach (var n in call.Expression.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var n in call.Parameters.SelectMany(param => param.EnumerateDescendantsAndSelf()))
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ArrayInitializerNode arrInit:
|
||||||
|
{
|
||||||
|
foreach (var n in arrInit.Values.SelectMany(val => val.EnumerateDescendantsAndSelf()))
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ConstArrayInitializerNode constArrInit:
|
||||||
|
{
|
||||||
|
foreach (var n in constArrInit.Values.SelectMany(val => val.EnumerateDescendantsAndSelf()))
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ArrayIndexAccessNode arrIndex:
|
||||||
|
{
|
||||||
|
foreach (var n in arrIndex.Target.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var n in arrIndex.Index.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ConstArrayIndexAccessNode constArrIndex:
|
||||||
|
{
|
||||||
|
foreach (var n in constArrIndex.Target.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var n in constArrIndex.Index.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SliceIndexAccessNode sliceIndex:
|
||||||
|
{
|
||||||
|
foreach (var n in sliceIndex.Target.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var n in sliceIndex.Index.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AddressOfNode addr:
|
||||||
|
{
|
||||||
|
foreach (var n in addr.LValue.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case StructFieldAccessNode field:
|
||||||
|
{
|
||||||
|
foreach (var n in field.Target.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case StructInitializerNode structInit:
|
||||||
|
{
|
||||||
|
foreach (var n in structInit.Initializers.SelectMany(kv => kv.Value.EnumerateDescendantsAndSelf()))
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DereferenceNode deref:
|
||||||
|
{
|
||||||
|
foreach (var n in deref.Target.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ConvertIntNode convInt:
|
||||||
|
{
|
||||||
|
foreach (var n in convInt.Value.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ConvertFloatNode convFloat:
|
||||||
|
{
|
||||||
|
foreach (var n in convFloat.Value.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ConvertCStringToStringNode convStr:
|
||||||
|
{
|
||||||
|
foreach (var n in convStr.Value.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FloatToIntBuiltinNode ftoi:
|
||||||
|
{
|
||||||
|
foreach (var n in ftoi.Value.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ConstArrayToSliceNode constSlice:
|
||||||
|
{
|
||||||
|
foreach (var n in constSlice.Array.EnumerateDescendantsAndSelf())
|
||||||
|
{
|
||||||
|
yield return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,9 +35,9 @@ public class DiagnosticsPublisher
|
|||||||
Diagnostics.DiagnosticSeverity.Error => DiagnosticSeverity.Error,
|
Diagnostics.DiagnosticSeverity.Error => DiagnosticSeverity.Error,
|
||||||
_ => null
|
_ => null
|
||||||
},
|
},
|
||||||
Message = $"{nubDiagnostic.Message}\nhelp: {nubDiagnostic.Help}",
|
Message = $"{nubDiagnostic.Message}\n{(nubDiagnostic.Help == null ? "" : $"help: {nubDiagnostic.Help}")}",
|
||||||
Range = nubDiagnostic.Span.HasValue
|
Range = nubDiagnostic.Span.HasValue
|
||||||
? new Range(nubDiagnostic.Span.Value.Start.Line, nubDiagnostic.Span.Value.Start.Column, nubDiagnostic.Span.Value.End.Line, nubDiagnostic.Span.Value.Start.Column)
|
? new Range(nubDiagnostic.Span.Value.Start.Line - 1, nubDiagnostic.Span.Value.Start.Column - 1, nubDiagnostic.Span.Value.End.Line - 1, nubDiagnostic.Span.Value.End.Column - 1)
|
||||||
: new Range(),
|
: new Range(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,195 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using NubLang.Ast;
|
||||||
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
|
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
|
||||||
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
|
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
|
||||||
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
|
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
|
||||||
|
|
||||||
namespace NubLang.LSP;
|
namespace NubLang.LSP;
|
||||||
|
|
||||||
internal class HoverHandler : HoverHandlerBase
|
internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBase
|
||||||
{
|
{
|
||||||
protected override HoverRegistrationOptions CreateRegistrationOptions(HoverCapability capability, ClientCapabilities clientCapabilities)
|
protected override HoverRegistrationOptions CreateRegistrationOptions(HoverCapability capability, ClientCapabilities clientCapabilities)
|
||||||
{
|
{
|
||||||
return new HoverRegistrationOptions
|
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
|
return new Hover
|
||||||
{
|
{
|
||||||
Contents = new MarkedStringsOrMarkupContent(new MarkupContent
|
Contents = new MarkedStringsOrMarkupContent(new MarkupContent
|
||||||
{
|
{
|
||||||
Value = "# uwu",
|
Value = message,
|
||||||
Kind = MarkupKind.Markdown,
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ internal class TextDocumentSyncHandler(WorkspaceManager workspaceManager) : Text
|
|||||||
{
|
{
|
||||||
public override TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri)
|
public override TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri)
|
||||||
{
|
{
|
||||||
return new TextDocumentAttributes(uri, "nublang");
|
return new TextDocumentAttributes(uri, "nub");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<Unit> Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken)
|
public override Task<Unit> Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using NubLang.Ast;
|
||||||
using NubLang.Syntax;
|
using NubLang.Syntax;
|
||||||
using OmniSharp.Extensions.LanguageServer.Protocol;
|
using OmniSharp.Extensions.LanguageServer.Protocol;
|
||||||
|
|
||||||
@@ -5,9 +6,10 @@ namespace NubLang.LSP;
|
|||||||
|
|
||||||
public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher)
|
public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher)
|
||||||
{
|
{
|
||||||
private readonly Dictionary<DocumentUri, SyntaxTree> _files = new();
|
private readonly Dictionary<DocumentUri, SyntaxTree> _syntaxTrees = new();
|
||||||
|
private readonly Dictionary<DocumentUri, CompilationUnit> _compilationUnits = new();
|
||||||
|
|
||||||
public void UpdateFile(DocumentUri path)
|
public void UpdateFile(DocumentUri path, bool typeCheck = true)
|
||||||
{
|
{
|
||||||
var text = File.ReadAllText(path.GetFileSystemPath());
|
var text = File.ReadAllText(path.GetFileSystemPath());
|
||||||
var tokenizer = new Tokenizer(path.GetFileSystemPath(), text);
|
var tokenizer = new Tokenizer(path.GetFileSystemPath(), text);
|
||||||
@@ -19,11 +21,40 @@ public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher)
|
|||||||
var result = parser.Parse(tokenizer.Tokens);
|
var result = parser.Parse(tokenizer.Tokens);
|
||||||
diagnosticsPublisher.Publish(path, parser.Diagnostics);
|
diagnosticsPublisher.Publish(path, parser.Diagnostics);
|
||||||
|
|
||||||
_files[path] = result;
|
_syntaxTrees[path] = result;
|
||||||
|
|
||||||
|
if (typeCheck)
|
||||||
|
{
|
||||||
|
TypeCheck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TypeCheck()
|
||||||
|
{
|
||||||
|
var modules = Module.Collect(_syntaxTrees.Select(x => x.Value).ToList());
|
||||||
|
|
||||||
|
foreach (var (path, syntaxTree) in _syntaxTrees)
|
||||||
|
{
|
||||||
|
var typeChecker = new TypeChecker(syntaxTree, modules);
|
||||||
|
var result = typeChecker.Check();
|
||||||
|
diagnosticsPublisher.Publish(path, typeChecker.Diagnostics);
|
||||||
|
_compilationUnits[path] = result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveFile(Uri path)
|
public void RemoveFile(Uri path)
|
||||||
{
|
{
|
||||||
_files.Remove(path);
|
_syntaxTrees.Remove(path);
|
||||||
|
_compilationUnits.Remove(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<DocumentUri, CompilationUnit> GetCompilationUnits()
|
||||||
|
{
|
||||||
|
return _compilationUnits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompilationUnit? GetCompilationUnit(DocumentUri path)
|
||||||
|
{
|
||||||
|
return _compilationUnits.GetValueOrDefault(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -679,7 +679,7 @@ public sealed class TypeChecker
|
|||||||
return new FuncCallNode(expression.Tokens, funcType.ReturnType, accessor, parameters);
|
return new FuncCallNode(expression.Tokens, funcType.ReturnType, accessor, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExpressionNode CheckIdentifier(ExpressionSyntax expression, string moduleName, string name)
|
private ExpressionNode? CheckIdentifier(ExpressionSyntax expression, string moduleName, string name)
|
||||||
{
|
{
|
||||||
if (!_importedModules.TryGetValue(moduleName, out var module))
|
if (!_importedModules.TryGetValue(moduleName, out var module))
|
||||||
{
|
{
|
||||||
@@ -707,10 +707,7 @@ public sealed class TypeChecker
|
|||||||
return new EnumReferenceIntermediateNode(expression.Tokens, moduleName, name);
|
return new EnumReferenceIntermediateNode(expression.Tokens, moduleName, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new TypeCheckerException(Diagnostic
|
return null;
|
||||||
.Error($"No exported symbol {name} not found in module {moduleName}")
|
|
||||||
.At(expression)
|
|
||||||
.Build());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExpressionNode CheckLocalIdentifier(LocalIdentifierSyntax expression, NubType? _)
|
private ExpressionNode CheckLocalIdentifier(LocalIdentifierSyntax expression, NubType? _)
|
||||||
@@ -722,13 +719,31 @@ public sealed class TypeChecker
|
|||||||
return new VariableIdentifierNode(expression.Tokens, scopeIdent.Type, expression.Name);
|
return new VariableIdentifierNode(expression.Tokens, scopeIdent.Type, expression.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CheckIdentifier(expression, Scope.Module, expression.Name);
|
var ident = CheckIdentifier(expression, Scope.Module, expression.Name);
|
||||||
|
if (ident == null)
|
||||||
|
{
|
||||||
|
throw new TypeCheckerException(Diagnostic
|
||||||
|
.Error($"There is no identifier named {expression.Name}")
|
||||||
|
.At(expression)
|
||||||
|
.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ident;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExpressionNode CheckModuleIdentifier(ModuleIdentifierSyntax expression, NubType? _)
|
private ExpressionNode CheckModuleIdentifier(ModuleIdentifierSyntax expression, NubType? _)
|
||||||
{
|
{
|
||||||
// note(nub31): Unlike local identifiers, module identifiers does not look for local variables
|
// note(nub31): Unlike local identifiers, module identifiers does not look for local variables
|
||||||
return CheckIdentifier(expression, expression.Module, expression.Name);
|
var ident = CheckIdentifier(expression, expression.Module, expression.Name);
|
||||||
|
if (ident == null)
|
||||||
|
{
|
||||||
|
throw new TypeCheckerException(Diagnostic
|
||||||
|
.Error($"Module {expression.Module} does not export a member named {expression.Name}")
|
||||||
|
.At(expression)
|
||||||
|
.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ident;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExpressionNode CheckStringLiteral(StringLiteralSyntax expression, NubType? expectedType)
|
private ExpressionNode CheckStringLiteral(StringLiteralSyntax expression, NubType? expectedType)
|
||||||
@@ -911,9 +926,16 @@ public sealed class TypeChecker
|
|||||||
{
|
{
|
||||||
var statements = new List<StatementNode>();
|
var statements = new List<StatementNode>();
|
||||||
foreach (var statement in node.Statements)
|
foreach (var statement in node.Statements)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
statements.Add(CheckStatement(statement));
|
statements.Add(CheckStatement(statement));
|
||||||
}
|
}
|
||||||
|
catch (TypeCheckerException e)
|
||||||
|
{
|
||||||
|
Diagnostics.Add(e.Diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new BlockNode(node.Tokens, statements);
|
return new BlockNode(node.Tokens, statements);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "nublang",
|
"name": "nub",
|
||||||
"displayName": "Nub Language Support",
|
"displayName": "Nub Language Support",
|
||||||
"description": "Language server client for nub lang",
|
"description": "Language server client for nub lang",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"contributes": {
|
"contributes": {
|
||||||
"languages": [
|
"languages": [
|
||||||
{
|
{
|
||||||
"id": "nublang",
|
"id": "nub",
|
||||||
"extensions": [
|
"extensions": [
|
||||||
".nub"
|
".nub"
|
||||||
],
|
],
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
],
|
],
|
||||||
"grammars": [
|
"grammars": [
|
||||||
{
|
{
|
||||||
"language": "nublang",
|
"language": "nub",
|
||||||
"scopeName": "source.nub",
|
"scopeName": "source.nub",
|
||||||
"path": "./syntaxes/nub.tmLanguage.json"
|
"path": "./syntaxes/nub.tmLanguage.json"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ export function activate(context: ExtensionContext) {
|
|||||||
const serverExecutable = '/home/oliste/repos/nub-lang/compiler/NubLang.LSP/bin/Debug/net9.0/NubLang.LSP';
|
const serverExecutable = '/home/oliste/repos/nub-lang/compiler/NubLang.LSP/bin/Debug/net9.0/NubLang.LSP';
|
||||||
|
|
||||||
client = new LanguageClient(
|
client = new LanguageClient(
|
||||||
'nublang',
|
'nub',
|
||||||
'nublang client',
|
'nub lsp client',
|
||||||
{
|
{
|
||||||
run: {
|
run: {
|
||||||
command: serverExecutable,
|
command: serverExecutable,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
||||||
"name": "nublang",
|
"name": "nub",
|
||||||
"scopeName": "source.nub",
|
"scopeName": "source.nub",
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user