From acc38ad8ff5150c8a9c4e685f6c8123addb1f408 Mon Sep 17 00:00:00 2001 From: nub31 Date: Thu, 23 Oct 2025 14:16:23 +0200 Subject: [PATCH] Jump to definition --- compiler/NubLang.LSP/AstExtensions.cs | 26 ++++++ compiler/NubLang.LSP/DefinitionHandler.cs | 97 +++++++++++++++++++++++ compiler/NubLang.LSP/HoverHandler.cs | 7 +- compiler/NubLang.LSP/Program.cs | 1 + 4 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 compiler/NubLang.LSP/DefinitionHandler.cs diff --git a/compiler/NubLang.LSP/AstExtensions.cs b/compiler/NubLang.LSP/AstExtensions.cs index 2c2b63c..ed1ea80 100644 --- a/compiler/NubLang.LSP/AstExtensions.cs +++ b/compiler/NubLang.LSP/AstExtensions.cs @@ -41,6 +41,32 @@ public static class AstExtensions 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.EnumerateDescendantsAndSelf()) + .Where(n => n.ContainsPosition(line, character)) + .OrderBy(n => n.Tokens.First().Span.Start.Line) + .ThenBy(n => n.Tokens.First().Span.Start.Column) + .LastOrDefault(); + } + + public static Node? DeepestNodeAtPosition(this Node node, int line, int character) + { + return node.EnumerateDescendantsAndSelf() + .Where(n => n.ContainsPosition(line, character)) + .OrderBy(n => n.Tokens.First().Span.Start.Line) + .ThenBy(n => n.Tokens.First().Span.Start.Column) + .LastOrDefault(); + } + public static IEnumerable EnumerateDescendantsAndSelf(this Node node) { yield return node; diff --git a/compiler/NubLang.LSP/DefinitionHandler.cs b/compiler/NubLang.LSP/DefinitionHandler.cs new file mode 100644 index 0000000..f363cd4 --- /dev/null +++ b/compiler/NubLang.LSP/DefinitionHandler.cs @@ -0,0 +1,97 @@ +using NubLang.Ast; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; +using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; + +namespace NubLang.LSP; + +internal class DefinitionHandler(WorkspaceManager workspaceManager) : DefinitionHandlerBase +{ + protected override DefinitionRegistrationOptions CreateRegistrationOptions(DefinitionCapability capability, ClientCapabilities clientCapabilities) + { + return new DefinitionRegistrationOptions(); + } + + public override Task 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) + { + if (parameter.Tokens.Count == 0) + { + return null; + } + + return new LocationOrLocationLinks(new Location + { + Uri = parameter.Tokens.First().Span.FilePath, + Range = new Range(parameter.Tokens.First().Span.Start.Line - 1, parameter.Tokens.First().Span.Start.Column - 1, parameter.Tokens.Last().Span.End.Line - 1, parameter.Tokens.Last().Span.End.Column - 1) + }); + } + + var variable = function?.Body? + .EnumerateDescendantsAndSelf() + .OfType() + .FirstOrDefault(x => x.Name == variableIdentifierNode.Name); + + if (variable != null) + { + if (variable.Tokens.Count == 0) + { + return null; + } + + return new LocationOrLocationLinks(new Location + { + Uri = variable.Tokens.First().Span.FilePath, + Range = new Range(variable.Tokens.First().Span.Start.Line - 1, variable.Tokens.First().Span.Start.Column - 1, variable.Tokens.Last().Span.End.Line - 1, variable.Tokens.Last().Span.End.Column - 1) + }); + } + + return null; + } + case FuncIdentifierNode funcIdentifierNode: + { + var functionBody = compilationUnit.ImportedFunctions.FirstOrDefault(x => x.Module == funcIdentifierNode.Module && x.Name == funcIdentifierNode.Name); + if (functionBody == null || functionBody.Tokens.Count == 0) + { + return null; + } + + return new LocationOrLocationLinks(new Location + { + Uri = functionBody.Tokens.First().Span.FilePath, + Range = new Range(functionBody.Tokens.First().Span.Start.Line - 1, functionBody.Tokens.First().Span.Start.Column - 1, functionBody.Tokens.Last().Span.End.Line - 1, functionBody.Tokens.Last().Span.End.Column - 1) + }); + } + default: + { + return null; + } + } + } +} \ No newline at end of file diff --git a/compiler/NubLang.LSP/HoverHandler.cs b/compiler/NubLang.LSP/HoverHandler.cs index 0974063..19e94e2 100644 --- a/compiler/NubLang.LSP/HoverHandler.cs +++ b/compiler/NubLang.LSP/HoverHandler.cs @@ -32,12 +32,7 @@ internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBas var line = request.Position.Line; var character = request.Position.Character; - var hoveredNode = compilationUnit.Functions - .SelectMany(x => x.EnumerateDescendantsAndSelf()) - .Where(n => n.ContainsPosition(line, character)) - .OrderBy(n => n.Tokens.First().Span.Start.Line) - .ThenBy(n => n.Tokens.First().Span.Start.Column) - .LastOrDefault(); + var hoveredNode = compilationUnit.DeepestNodeAtPosition(line, character); if (hoveredNode == null) { diff --git a/compiler/NubLang.LSP/Program.cs b/compiler/NubLang.LSP/Program.cs index 8218615..b7020aa 100644 --- a/compiler/NubLang.LSP/Program.cs +++ b/compiler/NubLang.LSP/Program.cs @@ -17,6 +17,7 @@ var server = await LanguageServer.From(options => options .WithHandler() .WithHandler() .WithHandler() + .WithHandler() .OnInitialize((server, request, ct) => { var workspaceManager = server.GetRequiredService();