From e17ecef2927f9a6a953f5689c17397f5cc31f972 Mon Sep 17 00:00:00 2001 From: XeroOl Date: Tue, 8 Jul 2025 20:49:35 -0500 Subject: [PATCH] macroexpansion feature for hover and code action --- src/fennel-ls/formatter.fnl | 16 +++++++++++----- src/fennel-ls/handlers.fnl | 26 ++++++++++++++++++++------ test/hover.fnl | 4 ++-- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/fennel-ls/formatter.fnl b/src/fennel-ls/formatter.fnl index 399c10f..79469ac 100644 --- a/src/fennel-ls/formatter.fnl +++ b/src/fennel-ls/formatter.fnl @@ -166,14 +166,20 @@ fntype is one of fn or λ or lambda" "?") (view (or definition.binding definition.definition)))) -(λ hover-format [server name definition] +(λ hover-format [server name definition ?opts] "Format code that will appear when the user hovers over a symbol" {:kind "markdown" :value (.. (code-block (get-stub server name definition)) - (or (-?> (navigate.getmetadata server definition) - (. :fnl/docstring) - (->> (.. "\n---\n"))) - ""))}) + (case (navigate.getmetadata server definition) + {: fnl/docstring} + (.. "\n---\n" fnl/docstring) + _ "") + (case (?. ?opts :macroexpansion) + macroexpansion + (.. "\n---\n" + "Macro expands to:\n" + (code-block macroexpansion)) + _ ""))}) ;; CompletionItemKind (local kinds diff --git a/src/fennel-ls/handlers.fnl b/src/fennel-ls/handlers.fnl index 13cab31..408f5e2 100644 --- a/src/fennel-ls/handlers.fnl +++ b/src/fennel-ls/handlers.fnl @@ -156,10 +156,16 @@ Every time the client sends a message, it gets handled by a function in the corr (let [file (files.get-by-uri server uri) byte (utils.position->byte file.text position server.position-encoding)] (case-try (analyzer.find-symbol file.ast byte) - symbol (analyzer.search server file symbol {} {: byte}) - {:indeterminate nil &as result} {:contents (formatter.hover-format server (tostring symbol) result) - :range (message.ast->range server file - symbol)} + (symbol parents) (analyzer.search server file symbol {} {: byte}) + {:indeterminate nil &as result} + (let [opts {:macroexpansion (case-try parents + (where [[(= symbol) &as parent]]) + file.macro-calls + {parent expansion} + (fennel.view expansion) + (catch _ nil))}] + {:contents (formatter.hover-format server (tostring symbol) result opts) + :range (message.ast->range server file symbol)}) (catch _ nil)))) (set {:textDocument/completion requests.textDocument/completion @@ -206,8 +212,16 @@ Every time the client sends a message, it gets handled by a function in the corr (pos<= range-2.start range-1.end))) (λ requests.textDocument/codeAction [server _send {: range :textDocument {: uri}}] - (let [file (files.get-by-uri server uri)] - (icollect [_ diagnostic (ipairs file.diagnostics)] + (let [file (files.get-by-uri server uri) + byte (utils.position->byte file.text range.start server.position-encoding) + results []] + (case-try (analyzer.find-symbol file.ast byte) + (symbol_ [[symbol_ &as parent]]) file.macro-calls + {parent expansion} (table.insert results + {:title "Expand macro" + :edit {:changes {uri [{:range (message.ast->range server file parent) + :newText (fennel.view expansion)}]}}})) + (icollect [_ diagnostic (ipairs file.diagnostics) &into results] (if (overlap? diagnostic.range range) (message.diagnostic->code-action server file diagnostic :quickfix))))) diff --git a/test/hover.fnl b/test/hover.fnl index 45499da..62e896e 100644 --- a/test/hover.fnl +++ b/test/hover.fnl @@ -34,7 +34,7 @@ (fn test-builtins [] (check "(d|o nil)" "```fnl\n(do ...)\n```\n---\nEvaluate multiple forms; return last value.") - (check "(|doto nil (print))" "```fnl\n(doto val ...)\n```\n---\nEvaluate val and splice it into the first argument of subsequent forms.") + (check "(|doto nil (print))" "```fnl\n(doto val ...)\n```\n---\nEvaluate val and splice it into the first argument of subsequent forms.\n---\nMacro expands to:\n```fnl\n(do (print nil) nil)\n```") (check "(le|t [x 10] 10)" "```fnl\n(let [name1 val1 ... nameN valN] ...)\n```\n---\nIntroduces a new scope in which a given set of local bindings are used.") nil) @@ -153,7 +153,7 @@ except that it sets a new message handler `msgh`.") \"docstring!\" `(,a ,b ,c)) (fo|o print :hello :world)" - "```fnl\n(foo a b c)\n```\n---\ndocstring!") + "```fnl\n(foo a b c)\n```\n---\ndocstring!\n---\nMacro expands to:\n```fnl\n(print \"hello\" \"world\")\n```") ; (check {:main.fnl "(import-macros cool :cool) ; (coo|l.=)" ; :cool.fnl ";; fennel-ls: macro-file