dev
This commit is contained in:
@@ -4,8 +4,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang", "NubLang\NubLang.
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang.CLI", "NubLang.CLI\NubLang.CLI.csproj", "{A22F17ED-FA17-45AB-92BA-CD02C28B3524}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang.CLI", "NubLang.CLI\NubLang.CLI.csproj", "{A22F17ED-FA17-45AB-92BA-CD02C28B3524}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang.LSP", "NubLang.LSP\NubLang.LSP.csproj", "{07968F84-0C2E-4D2E-8905-DC8A6140B4C0}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
EndGlobal
|
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