WIP: dev #1
@@ -4,8 +4,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang", "NubLang\NubLang.
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang.CLI", "NubLang.CLI\NubLang.CLI.csproj", "{A22F17ED-FA17-45AB-92BA-CD02C28B3524}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang.LSP", "NubLang.LSP\NubLang.LSP.csproj", "{07968F84-0C2E-4D2E-8905-DC8A6140B4C0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -20,9 +18,5 @@ Global
|
||||
{A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{07968F84-0C2E-4D2E-8905-DC8A6140B4C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{07968F84-0C2E-4D2E-8905-DC8A6140B4C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{07968F84-0C2E-4D2E-8905-DC8A6140B4C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{07968F84-0C2E-4D2E-8905-DC8A6140B4C0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
using NubLang.Ast;
|
||||
using NubLang.Syntax;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
|
||||
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;
|
||||
|
||||
namespace NubLang.LSP;
|
||||
|
||||
public static class AstExtensions
|
||||
{
|
||||
public static Location ToLocation(this Node node)
|
||||
{
|
||||
if (node.Tokens.Count == 0)
|
||||
{
|
||||
return new Location();
|
||||
}
|
||||
|
||||
return new Location
|
||||
{
|
||||
Uri = node.Tokens.First().Span.FilePath,
|
||||
Range = new Range(node.Tokens.First().Span.Start.Line - 1, node.Tokens.First().Span.Start.Column - 1, node.Tokens.Last().Span.End.Line - 1, node.Tokens.Last().Span.End.Column - 1)
|
||||
};
|
||||
}
|
||||
|
||||
public static bool ContainsPosition(this 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;
|
||||
}
|
||||
|
||||
public static FuncNode? FunctionAtPosition(this CompilationUnit compilationUnit, int line, int character)
|
||||
{
|
||||
return compilationUnit
|
||||
.Functions
|
||||
.FirstOrDefault(x => x.ContainsPosition(line, character));
|
||||
}
|
||||
|
||||
public static Node? DeepestNodeAtPosition(this CompilationUnit compilationUnit, int line, int character)
|
||||
{
|
||||
return compilationUnit.Functions
|
||||
.SelectMany(x => x.DescendantsAndSelf())
|
||||
.Where(n => n.ContainsPosition(line, character))
|
||||
.OrderBy(n => n.Tokens.First().Span.Start.Line)
|
||||
.ThenBy(n => n.Tokens.First().Span.Start.Column)
|
||||
.LastOrDefault();
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
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 CompletionHandler(WorkspaceManager workspaceManager) : CompletionHandlerBase
|
||||
{
|
||||
private readonly CompletionItem[] _definitionSnippets =
|
||||
[
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "func",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "func ${1:name}(${2:params})\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "struct",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "struct ${1:name}\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "module",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "module \"$0\"",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "import",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "import \"$0\"",
|
||||
}
|
||||
];
|
||||
|
||||
private readonly CompletionItem[] _statementSnippets =
|
||||
[
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "let",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "let ${1:name} = $0",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "if",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "if ${1:condition}\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "else if",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "else if ${1:condition}\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "else",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "else\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "while",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "while ${1:condition}\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "for",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "for ${1:name}, ${2:index} in ${3:array}\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "return",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "return $0",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "defer",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "defer $0",
|
||||
}
|
||||
];
|
||||
|
||||
protected override CompletionRegistrationOptions CreateRegistrationOptions(CompletionCapability capability, ClientCapabilities clientCapabilities)
|
||||
{
|
||||
return new CompletionRegistrationOptions();
|
||||
}
|
||||
|
||||
public override Task<CompletionList> Handle(CompletionParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(HandleSync(request, cancellationToken));
|
||||
}
|
||||
|
||||
private CompletionList HandleSync(CompletionParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
var completions = new List<CompletionItem>();
|
||||
var position = request.Position;
|
||||
|
||||
var uri = request.TextDocument.Uri;
|
||||
var compilationUnit = workspaceManager.GetCompilationUnit(uri);
|
||||
if (compilationUnit != null)
|
||||
{
|
||||
var function = compilationUnit.Functions.FirstOrDefault(x => x.Body != null && x.Body.ContainsPosition(position.Line, position.Character));
|
||||
if (function != null)
|
||||
{
|
||||
completions.AddRange(_statementSnippets);
|
||||
|
||||
foreach (var prototype in compilationUnit.ImportedFunctions)
|
||||
{
|
||||
var parameterStrings = new List<string>();
|
||||
foreach (var (index, parameter) in prototype.Parameters.Index())
|
||||
{
|
||||
parameterStrings.AddRange($"${{{index + 1}:{parameter.Name}}}");
|
||||
}
|
||||
|
||||
completions.Add(new CompletionItem
|
||||
{
|
||||
Kind = CompletionItemKind.Function,
|
||||
Label = $"{prototype.Module}::{prototype.Name}",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = $"{prototype.Module}::{prototype.Name}({string.Join(", ", parameterStrings)})",
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var parameter in function.Prototype.Parameters)
|
||||
{
|
||||
completions.Add(new CompletionItem
|
||||
{
|
||||
Kind = CompletionItemKind.Variable,
|
||||
Label = parameter.Name,
|
||||
InsertText = parameter.Name,
|
||||
});
|
||||
}
|
||||
|
||||
var variables = function.Body!
|
||||
.Descendants()
|
||||
.OfType<VariableDeclarationNode>();
|
||||
|
||||
foreach (var variable in variables)
|
||||
{
|
||||
completions.Add(new CompletionItem
|
||||
{
|
||||
Kind = CompletionItemKind.Variable,
|
||||
Label = variable.Name,
|
||||
InsertText = variable.Name,
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
completions.AddRange(_definitionSnippets);
|
||||
}
|
||||
}
|
||||
|
||||
return new CompletionList(completions, false);
|
||||
}
|
||||
|
||||
public override Task<CompletionItem> Handle(CompletionItem request, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(new CompletionItem());
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
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 DefinitionHandler(WorkspaceManager workspaceManager) : DefinitionHandlerBase
|
||||
{
|
||||
protected override DefinitionRegistrationOptions CreateRegistrationOptions(DefinitionCapability capability, ClientCapabilities clientCapabilities)
|
||||
{
|
||||
return new DefinitionRegistrationOptions();
|
||||
}
|
||||
|
||||
public override Task<LocationOrLocationLinks?> Handle(DefinitionParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(HandleSync(request, cancellationToken));
|
||||
}
|
||||
|
||||
private LocationOrLocationLinks? HandleSync(DefinitionParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
var uri = request.TextDocument.Uri;
|
||||
var compilationUnit = workspaceManager.GetCompilationUnit(uri);
|
||||
if (compilationUnit == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var line = request.Position.Line;
|
||||
var character = request.Position.Character;
|
||||
|
||||
var node = compilationUnit.DeepestNodeAtPosition(line, character);
|
||||
|
||||
switch (node)
|
||||
{
|
||||
case VariableIdentifierNode variableIdentifierNode:
|
||||
{
|
||||
var function = compilationUnit.FunctionAtPosition(line, character);
|
||||
|
||||
var parameter = function?.Prototype.Parameters.FirstOrDefault(x => x.Name == variableIdentifierNode.Name);
|
||||
if (parameter != null)
|
||||
{
|
||||
return new LocationOrLocationLinks(parameter.ToLocation());
|
||||
}
|
||||
|
||||
var variable = function?.Body?
|
||||
.Descendants()
|
||||
.OfType<VariableDeclarationNode>()
|
||||
.FirstOrDefault(x => x.Name == variableIdentifierNode.Name);
|
||||
|
||||
if (variable != null)
|
||||
{
|
||||
return new LocationOrLocationLinks(variable.ToLocation());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
case FuncIdentifierNode funcIdentifierNode:
|
||||
{
|
||||
var prototype = compilationUnit.ImportedFunctions.FirstOrDefault(x => x.Module == funcIdentifierNode.Module && x.Name == funcIdentifierNode.Name);
|
||||
if (prototype != null)
|
||||
{
|
||||
return new LocationOrLocationLinks(prototype.ToLocation());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
|
||||
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;
|
||||
|
||||
namespace NubLang.LSP;
|
||||
|
||||
public class DiagnosticsPublisher
|
||||
{
|
||||
private readonly ILanguageServerFacade _server;
|
||||
|
||||
public DiagnosticsPublisher(ILanguageServerFacade server)
|
||||
{
|
||||
_server = server;
|
||||
}
|
||||
|
||||
public void Publish(DocumentUri uri, IEnumerable<Diagnostics.Diagnostic> diagnostics)
|
||||
{
|
||||
_server.TextDocument.PublishDiagnostics(new PublishDiagnosticsParams
|
||||
{
|
||||
Uri = uri,
|
||||
Diagnostics = new Container<Diagnostic>(diagnostics.Select(MapDiagnostic))
|
||||
});
|
||||
}
|
||||
|
||||
private static Diagnostic MapDiagnostic(Diagnostics.Diagnostic nubDiagnostic)
|
||||
{
|
||||
return new Diagnostic
|
||||
{
|
||||
Severity = nubDiagnostic.Severity switch
|
||||
{
|
||||
Diagnostics.DiagnosticSeverity.Info => DiagnosticSeverity.Information,
|
||||
Diagnostics.DiagnosticSeverity.Warning => DiagnosticSeverity.Warning,
|
||||
Diagnostics.DiagnosticSeverity.Error => DiagnosticSeverity.Error,
|
||||
_ => null
|
||||
},
|
||||
Message = $"{nubDiagnostic.Message}\n{(nubDiagnostic.Help == null ? "" : $"help: {nubDiagnostic.Help}")}",
|
||||
Range = nubDiagnostic.Span.HasValue
|
||||
? 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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
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}
|
||||
```
|
||||
""";
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyName>nublsp</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OmniSharp.Extensions.LanguageServer" Version="0.19.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NubLang\NubLang.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,34 +0,0 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NubLang.LSP;
|
||||
using OmniSharp.Extensions.LanguageServer.Server;
|
||||
|
||||
var server = await LanguageServer.From(options => options
|
||||
.WithInput(Console.OpenStandardInput())
|
||||
.WithOutput(Console.OpenStandardOutput())
|
||||
.WithServices(services =>
|
||||
{
|
||||
services.AddSingleton<DiagnosticsPublisher>();
|
||||
services.AddSingleton<WorkspaceManager>();
|
||||
})
|
||||
.ConfigureLogging(x => x
|
||||
.AddLanguageProtocolLogging()
|
||||
.SetMinimumLevel(LogLevel.Debug))
|
||||
.WithHandler<TextDocumentSyncHandler>()
|
||||
.WithHandler<HoverHandler>()
|
||||
.WithHandler<CompletionHandler>()
|
||||
.WithHandler<DefinitionHandler>()
|
||||
.OnInitialize((server, request, ct) =>
|
||||
{
|
||||
var workspaceManager = server.GetRequiredService<WorkspaceManager>();
|
||||
|
||||
if (request.RootPath != null)
|
||||
{
|
||||
workspaceManager.Init(request.RootPath);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
})
|
||||
);
|
||||
|
||||
await server.WaitForExit;
|
||||
@@ -1,44 +0,0 @@
|
||||
using MediatR;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
|
||||
|
||||
namespace NubLang.LSP;
|
||||
|
||||
internal class TextDocumentSyncHandler(WorkspaceManager workspaceManager) : TextDocumentSyncHandlerBase
|
||||
{
|
||||
public override TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri)
|
||||
{
|
||||
return new TextDocumentAttributes(uri, "nub");
|
||||
}
|
||||
|
||||
public override Task<Unit> Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath());
|
||||
return Unit.Task;
|
||||
}
|
||||
|
||||
public override Task<Unit> Handle(DidChangeTextDocumentParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath());
|
||||
return Unit.Task;
|
||||
}
|
||||
|
||||
public override Task<Unit> Handle(DidSaveTextDocumentParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath());
|
||||
return Unit.Task;
|
||||
}
|
||||
|
||||
public override Task<Unit> Handle(DidCloseTextDocumentParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath());
|
||||
return Unit.Task;
|
||||
}
|
||||
|
||||
protected override TextDocumentSyncRegistrationOptions CreateRegistrationOptions(TextSynchronizationCapability capability, ClientCapabilities clientCapabilities)
|
||||
{
|
||||
return new TextDocumentSyncRegistrationOptions();
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using NubLang.Ast;
|
||||
using NubLang.Syntax;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol;
|
||||
|
||||
namespace NubLang.LSP;
|
||||
|
||||
public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher)
|
||||
{
|
||||
private readonly Dictionary<string, SyntaxTree> _syntaxTrees = new();
|
||||
private readonly Dictionary<string, CompilationUnit> _compilationUnits = new();
|
||||
|
||||
public void Init(string rootPath)
|
||||
{
|
||||
var files = Directory.GetFiles(rootPath, "*.nub", SearchOption.AllDirectories);
|
||||
foreach (var path in files)
|
||||
{
|
||||
var text = File.ReadAllText(path);
|
||||
var tokenizer = new Tokenizer(path, text);
|
||||
|
||||
tokenizer.Tokenize();
|
||||
diagnosticsPublisher.Publish(path, tokenizer.Diagnostics);
|
||||
|
||||
var parser = new Parser();
|
||||
var parseResult = parser.Parse(tokenizer.Tokens);
|
||||
diagnosticsPublisher.Publish(path, parser.Diagnostics);
|
||||
|
||||
_syntaxTrees[path] = parseResult;
|
||||
}
|
||||
|
||||
foreach (var (fsPath, syntaxTree) in _syntaxTrees)
|
||||
{
|
||||
var modules = Module.Collect(_syntaxTrees.Select(x => x.Value).ToList());
|
||||
|
||||
var typeChecker = new TypeChecker(syntaxTree, modules);
|
||||
var result = typeChecker.Check();
|
||||
diagnosticsPublisher.Publish(fsPath, typeChecker.Diagnostics);
|
||||
_compilationUnits[fsPath] = result;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateFile(DocumentUri path)
|
||||
{
|
||||
var fsPath = path.GetFileSystemPath();
|
||||
|
||||
var text = File.ReadAllText(fsPath);
|
||||
var tokenizer = new Tokenizer(fsPath, text);
|
||||
tokenizer.Tokenize();
|
||||
diagnosticsPublisher.Publish(path, tokenizer.Diagnostics);
|
||||
|
||||
var parser = new Parser();
|
||||
var syntaxTree = parser.Parse(tokenizer.Tokens);
|
||||
diagnosticsPublisher.Publish(path, parser.Diagnostics);
|
||||
_syntaxTrees[fsPath] = syntaxTree;
|
||||
|
||||
var modules = Module.Collect(_syntaxTrees.Select(x => x.Value).ToList());
|
||||
|
||||
var typeChecker = new TypeChecker(syntaxTree, modules);
|
||||
var result = typeChecker.Check();
|
||||
diagnosticsPublisher.Publish(fsPath, typeChecker.Diagnostics);
|
||||
_compilationUnits[fsPath] = result;
|
||||
}
|
||||
|
||||
public void RemoveFile(DocumentUri path)
|
||||
{
|
||||
var fsPath = path.GetFileSystemPath();
|
||||
_syntaxTrees.Remove(fsPath);
|
||||
_compilationUnits.Remove(fsPath);
|
||||
}
|
||||
|
||||
public CompilationUnit? GetCompilationUnit(DocumentUri path)
|
||||
{
|
||||
return _compilationUnits.GetValueOrDefault(path.GetFileSystemPath());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user