diff --git a/src/fennel-ls/analyzer.fnl b/src/fennel-ls/analyzer.fnl index 80dcf6d..932b9f1 100644 --- a/src/fennel-ls/analyzer.fnl +++ b/src/fennel-ls/analyzer.fnl @@ -164,6 +164,7 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find nil)))) +(local {:metadata METADATA} (require :fennel.compiler)) ;; the options thing is getting out of hand (λ search-main [server file symbol opts initialization-opts] "Find the definition of a symbol" @@ -184,7 +185,9 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find _ (case (. file.references symbol) ref (search-reference server file ref stack opts) _ (case (. file.definitions symbol) - def (search-multival server file def.definition (stack-add-keys! stack def.keys) (or def.multival 1) opts))))))) + def (search-multival server file def.definition (stack-add-keys! stack def.keys) (or def.multival 1) opts) + _ (case (. file.macro-refs symbol) + ref {:binding symbol :metadata (. METADATA ref)}))))))) (λ find-local-definition [file name ?scope] (when ?scope diff --git a/src/fennel-ls/compiler.fnl b/src/fennel-ls/compiler.fnl index d7e229a..a465af2 100644 --- a/src/fennel-ls/compiler.fnl +++ b/src/fennel-ls/compiler.fnl @@ -64,6 +64,7 @@ identifiers are declared / referenced in which places." definitions {} ; symbol -> definition diagnostics {} ; [diagnostic] references {} ; symbol -> references + macro-refs {} ; symbol -> macro scopes {} ; ast -> scope calls {} ; all calls in the macro-expanded code -> true lexical {} ; all lists, tables, and symbols in the original source @@ -211,7 +212,7 @@ identifiers are declared / referenced in which places." (tset scopes ast scope)) (λ call [ast scope] - "every list that is a call to a special or macro or function" + "every list that is a call to a special or function" (tset calls ast true) (tset scopes ast scope) ;; Most calls aren't interesting, but here's the list of the ones that are: @@ -231,6 +232,17 @@ identifiers are declared / referenced in which places." (let [len (length ast)] (table.insert defer #(tset ast (+ len 1) nil)))))) + (fn macroexpand [ast _transformed scope] + "every list that is a call to a macro" + (let [macro-id (. ast 1) + macro-fn (accumulate [t scope.macros + _ part (ipairs (utils.multi-sym-split macro-id))] + (if (= (type t) :table) + (. t part)))] + (when (= (type macro-fn) :function) + (assert (sym? macro-id) "macros should be syms") + (tset macro-refs macro-id macro-fn)))) + (λ attempt-to-recover! [msg ?ast] (or (= 1 (msg:find "unknown identifier")) (= 1 (msg:find "local %S+ was overshadowed by a special form or macro")) @@ -300,6 +312,9 @@ identifiers are declared / referenced in which places." (each [_ v (ipairs (utils.split-spaces server.configuration.extra-globals))] (table.insert allowed-globals v)) + (fn parse-ast [parser] + (icollect [ok ast parser &until (not ok)] ast)) + ;; TODO clean up this code. It's awful now that there is error handling (let [macro-file? (= (file.text:sub 1 24) ";; fennel-ls: macro-file") plugin @@ -308,7 +323,7 @@ identifiers are declared / referenced in which places." : symbol-to-expression : call : destructure - ;; : macroexpand + : macroexpand ;; :fn fn-hook ;; :do there's a do hook ;; :chunk I don't know what this one is @@ -322,6 +337,7 @@ identifiers are declared / referenced in which places." opts {:filename file.uri :plugins [plugin] :allowedGlobals allowed-globals + :useMetadata true :requireAsInclude false : scope} filter-errors (fn _filter-errors [component ...] @@ -339,7 +355,7 @@ identifiers are declared / referenced in which places." (fn _p1 [p2 p3] (filter-errors :parser (xpcall #(p p2 p3) fennel.traceback)))) - ast (icollect [ok ast parser &until (not ok)] ast)] + ast (parse-ast parser)] (λ parsed [ast] "runs on every ast tree that was parsed" @@ -385,6 +401,7 @@ identifiers are declared / referenced in which places." (each [_ cmd (ipairs defer)] (cmd)) + ;; TODO make this construct an object instead of mutating the file (set file.ast ast) (set file.calls calls) (set file.lexical lexical) @@ -395,6 +412,7 @@ identifiers are declared / referenced in which places." (set file.diagnostics diagnostics) (set file.references references) (set file.require-calls require-calls) - (set file.allowed-globals allowed-globals)))) + (set file.allowed-globals allowed-globals) + (set file.macro-refs macro-refs)))) {: compile} diff --git a/test/hover.fnl b/test/hover.fnl index f98661c..407fec2 100644 --- a/test/hover.fnl +++ b/test/hover.fnl @@ -145,11 +145,12 @@ except that it sets a new message handler `msgh`.") `(let [,a ,b] ,c)) (bind x print |x)" #($:find "```fnl\n(print ...)\n```" 1 true)) - ; (check "(macro foo [a b c] - ; \"docstring!\" - ; `(,a ,b ,c)) - ; (fo|o print :hello :world)" - ; "```fnl\n(macro foo [a b c] ...)\n```\ndocstring!") + + (check "(macro foo [a b c] + \"docstring!\" + `(,a ,b ,c)) + (fo|o print :hello :world)" + "```fnl\n(foo a b c)\n```\ndocstring!") nil) (fn test-reader []