macro modules and regular modules in separate namespaces

implements #49
This commit is contained in:
XeroOl 2025-04-10 14:24:49 -05:00
parent 945eb86b4c
commit a4d99be235
7 changed files with 45 additions and 28 deletions

View File

@ -127,7 +127,7 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find
(let [mod (. call 2)]
(if (= multival 1)
(when (= :string (type mod))
(let [newfile (files.get-by-module server mod)]
(let [newfile (files.get-by-module server mod file.macro-file?)]
(when newfile
(let [newitem (. newfile.ast (length newfile.ast))]
(when (= (length stack) 1)

View File

@ -434,6 +434,7 @@ identifiers are declared / referenced in which places."
(cmd))
;; TODO make this construct an object instead of mutating the file
(set file.macro-file? macro-file?)
(set file.ast ast)
(set file.calls calls)
(set file.lexical lexical)

View File

@ -93,6 +93,7 @@ However, when not an option, fennel-ls will fall back to positionEncoding=\"utf-
(λ initialize [server params]
(set server.files {})
(set server.modules {})
(set server.macro-modules {})
(set server.root-uri params.rootUri)
(set server.position-encoding (choose-position-encoding params))
(reload server)

View File

@ -11,6 +11,7 @@ in the \"server\" object."
(local {: compile} (require :fennel-ls.compiler))
(λ read-file [server uri]
;; preload is here so that tests can inject files
(case (?. server.preload uri)
preload {: uri :text preload}
_ (case uri
@ -29,20 +30,21 @@ in the \"server\" object."
(tset server.files uri file)
file))))
(λ get-by-module [server module]
(λ get-by-module [server module macro?]
(let [modules (if macro? server.macro-modules server.modules)]
;; check the cache
(case (. server.modules module)
(case (. modules module)
uri
(or (get-by-uri server uri)
;; if the cached uri isn't found, clear the cache and try again
(do (tset server.modules module nil)
(do (tset modules module nil)
(get-by-module server module)))
nil
(case (searcher.lookup server module)
(case (searcher.lookup server module macro?)
uri
(do
(tset server.modules module uri)
(get-by-uri server uri)))))
(tset modules module uri)
(get-by-uri server uri))))))
(λ set-uri-contents [server uri text]
(case (. server.files uri)

View File

@ -39,6 +39,7 @@ the `file.diagnostics` field, filling it with diagnostics."
;; this is way too specific; it's also safe to do this inside an `if` or `case`
(fn in-or? [calls symbol]
"Check if the symbol is in an expression like `(or unpack table.unpack)`, where we want to suppress the module field stuff."
(accumulate [in? false call (pairs calls) &until in?]
(and (sym? (. call 1) :or) (utils.find call symbol))))

View File

@ -24,13 +24,13 @@ I suspect this file may be gone after a bit of refactoring."
(case (io.open (uri->path uri))
f (do (f:close) true))))
(λ lookup [{:configuration {: fennel-path} :root-uri ?root-uri &as server} mod]
"Use the fennel path to find a file on disk"
(λ lookup [{:configuration {: fennel-path : macro-path} :root-uri ?root-uri &as server} mod macro?]
"Use the configured path to find a file on disk"
(when ?root-uri
(let [mod (mod:gsub "%." path-sep)
root-path (uri->path ?root-uri)]
(accumulate [uri nil
segment (fennel-path:gmatch "[^;]+")
segment (: (if macro? macro-path fennel-path) :gmatch "[^;]+")
&until uri]
(let [segment (segment:gsub "%?" mod)
segment (if (absolute-path? segment)

View File

@ -3,8 +3,8 @@
(local {: null} (require :dkjson))
(local {: view} (require :fennel))
(fn check [file-contents]
(let [{: client : uri : cursor :locations [location]} (create-client file-contents)
(fn check [file-contents ?config]
(let [{: client : uri : cursor :locations [location]} (create-client file-contents nil ?config)
[message] (client:definition uri cursor)]
(if location
(faith.= location message.result
@ -190,15 +190,6 @@
nil)
; (fn test-macro []
; (check "(macro ==my-macro== [] `nil)
; (my-mac|ro)")
; (check {:m.fnl ";; fennel-ls: macro-file
; (fn ==my-macro== [] `nil)
; {: my-macro}"
; :main.fnl "(import-macros m :m)
; (m.my-macro|)"}))
(fn test-no-crash []
(check "(macro cool [a b] `(let [,b 10] ,a))\n(cool |x ==x==)")
(check "(macro cool [a b] `(let [,b 10] ,a))\n(cool x x|)")
@ -217,6 +208,7 @@
(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
@ -224,12 +216,32 @@
;; 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| {} {} {})")
;; ;; Can see macros thru import-macros
;; (check {:m.mfnl "(fn ==my-macro== [] `nil)
;; {: my-macro}"
;; :main.fnl "(import-macros m :m)
;; (m.my-macro|)")
;; go-to-definition inside of macro files uses the macro path
(check {:abcde/friend.fnl "=={}=="
:friend.fnl "{}"
:main.fnl ";; fennel-ls: macro-file
(local x| (require :friend))"}
{:macro-path "abcde/?.fnl"})
(check {:abcde/friend.fnl "{}"
:friend.fnl "=={}=="
:main.fnl "(local x| (require :friend))"}
{:macro-path "abcde/?.fnl"})
nil)
; ;; (it "can go to a destructured function argument")
; ;; (it "will give up instead of freezing on recursive requires")
; ;; (it "will give up instead of freezing on recursive tables constructed with (set)")