using NubLang.Ast; using NubLang.Modules; 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\"", } ]; 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 Handle(CompletionParams request, CancellationToken cancellationToken) { return Task.FromResult(HandleSync(request, cancellationToken)); } private CompletionList HandleSync(CompletionParams request, CancellationToken cancellationToken) { var completions = new List(); var compilationUnit = workspaceManager.GetTopLevelNodes(request.TextDocument.Uri.GetFileSystemPath()); var repository = workspaceManager.GetModuleRepository(); var function = compilationUnit .OfType() .FirstOrDefault(x => x.Body != null && x.Body.ContainsPosition(request.Position.Line, request.Position.Character)); if (function != null) { completions.AddRange(_statementSnippets); foreach (var module in repository.GetAll()) { foreach (var prototype in module.FunctionPrototypes) { var parameterStrings = new List(); foreach (var (index, parameter) in prototype.Parameters.Index()) { parameterStrings.AddRange($"${{{index + 1}:{parameter.NameToken.Value}}}"); } var isCurrentModule = false; var moduleDecl = compilationUnit.OfType().FirstOrDefault(); if (moduleDecl != null) { if (moduleDecl.NameToken.Value == module.Name) { isCurrentModule = true; } } completions.Add(new CompletionItem { Kind = CompletionItemKind.Function, Label = isCurrentModule ? prototype.NameToken.Value : $"{module.Name}::{prototype.NameToken.Value}", InsertTextFormat = InsertTextFormat.Snippet, InsertText = $"{(isCurrentModule ? "" : $"{module.Name}::")}{prototype.NameToken.Value}({string.Join(", ", parameterStrings)})", }); } } foreach (var parameter in function.Prototype.Parameters) { completions.Add(new CompletionItem { Kind = CompletionItemKind.Variable, Label = parameter.NameToken.Value, InsertText = parameter.NameToken.Value, }); } var variables = function.Body! .Descendants() .OfType(); foreach (var variable in variables) { completions.Add(new CompletionItem { Kind = CompletionItemKind.Variable, Label = variable.NameToken.Value, InsertText = variable.NameToken.Value, }); } } else { completions.AddRange(_definitionSnippets); } return new CompletionList(completions, false); } public override Task Handle(CompletionItem request, CancellationToken cancellationToken) { return Task.FromResult(new CompletionItem()); } }