From ae66e6d002a35ff69e9519e3f5b64fe8e2f57476 Mon Sep 17 00:00:00 2001 From: XeroOl Date: Sun, 19 Jan 2025 19:56:37 -0600 Subject: [PATCH] analyzer can search into macro expansions and functions shoutouts to rudy for working with me on these changes. --- src/fennel-ls/analyzer.fnl | 15 ++++++++++++--- src/fennel-ls/compiler.fnl | 9 ++++++--- test/goto-definition.fnl | 30 +++++++++++++++++++++++++++--- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/fennel-ls/analyzer.fnl b/src/fennel-ls/analyzer.fnl index 10c2cf6..a1b3eab 100644 --- a/src/fennel-ls/analyzer.fnl +++ b/src/fennel-ls/analyzer.fnl @@ -42,6 +42,7 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find (local utils (require :fennel-ls.utils)) (local files (require :fennel-ls.files)) (local docs (require :fennel-ls.docs)) +(local {: view} (require :fennel)) (local get-ast-info utils.get-ast-info) @@ -146,11 +147,19 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find (where (or :fn :lambda :λ)) (if (and (= multival 1) (= 0 (length stack))) {:definition call : file}) ;; BASE CASE !! - ;; TODO expand-macros _ - (if (and (= multival 1) (= 0 (length stack))) - {:definition call : file}))))) ;; BASE CASE!! + (case (. file.macro-calls call) + macroexpanded (search-multival server file macroexpanded stack multival opts) + _ (case (search-val server file (. call 1) [] {}) + (where {: definition : file} + (list? definition) + (let [head (. definition 1)] + (or (sym? head :fn) + (sym? head :lambda) + (sym? head :λ)))) + + (search-multival server file (. definition (length definition)) stack multival opts))))))) (set search-multival (λ [server file ?ast stack multival opts] diff --git a/src/fennel-ls/compiler.fnl b/src/fennel-ls/compiler.fnl index ec5afc1..4f8d43c 100644 --- a/src/fennel-ls/compiler.fnl +++ b/src/fennel-ls/compiler.fnl @@ -67,7 +67,8 @@ identifiers are declared / referenced in which places." scopes {} ; ast -> scope calls {} ; all calls in the macro-expanded code -> true lexical {} ; all lists, tables, and symbols in the original source - require-calls {}] ; the keys are all the calls that start with `require + require-calls {} ; the keys are all the calls that start with `require + macro-calls {}]; ast -> expanded (local defer []) @@ -235,8 +236,9 @@ identifiers are declared / referenced in which places." (let [len (length ast)] (table.insert defer #(tset ast (+ len 1) nil)))))) - (fn macroexpand [ast _transformed scope] + (fn macroexpand [ast transformed scope] "every list that is a call to a macro" + (tset macro-calls ast transformed) (let [macro-id (. ast 1) macro-fn (accumulate [t scope.macros _ part (ipairs (utils.multi-sym-split macro-id))] @@ -423,6 +425,7 @@ identifiers are declared / referenced in which places." (set file.references references) (set file.require-calls require-calls) (set file.allowed-globals allowed-globals) - (set file.macro-refs macro-refs)))) + (set file.macro-refs macro-refs) + (set file.macro-calls macro-calls)))) {: compile} diff --git a/test/goto-definition.fnl b/test/goto-definition.fnl index d6c1ae9..8e040cb 100644 --- a/test/goto-definition.fnl +++ b/test/goto-definition.fnl @@ -205,22 +205,46 @@ (check "|#$...") nil) +(fn test-thru-function-return [] + (check {:button.fnl "(fn new [] + {:options =={}==}) + {: new}" + :main.fnl "(let [button (require :button) + b (button.new)] + b.options|"}) + (check "(fn foo [] (values {} =={}== {})) + (local (x y| z) (foo))") + (check "(fn foo [] [{} =={}== {}]) + (local [x y| z] (foo))") + nil) +(fn test-macro [] + "Macros are mostly unsupported for now. + Fennel-ls can understand the expansion of macros, because it mostly operates + on the post-expansion version of the AST." + ;; Can see the expansion of a macro + (check "(macro hello [x y z] y) + (local x| (hello {} =={}== {}))") + ;; ;; Can see the macro itself + ;; (check "==(macro hello [x y z] y)== + ;; (local x (hello| {} {} {})") + + nil) + ; ;; (it "can go to a destructured function argument") -; ;; (it "can go through more than one file") ; ;; (it "will give up instead of freezing on recursive requires") ; ;; (it "will give up instead of freezing on recursive tables constructed with (set)") -; ;; (it "finds the definition of in-file macros") ; ;; (it "can follow import-macros (destructuring)") ; ;; (it "can follow import-macros (namespaced)") ; ;; (it "can go to the definition in a lua file") ; ;; (it "finds (set a.b) definitions") ; ;; (it "finds (tset a :b) definitions") ; ;; (it "finds (setmetatable a {:__index {:b def}) definitions") -; ;; (it "finds definitions into a function (fn foo [] (local x 10) {: x}) (let [result (foo)] (print result.x)) finds result.x") ; ;; (it "finds definitions through a function (fn foo [{: y}] {:x y}) (let [result (foo {:y {}})] (print result.x)) finds result.x") ; ;; (it "finds through setmetatable with an :__index function") {: test-local : test-fields : test-thru-require + : test-thru-function-return + : test-macro : test-no-crash}