diff --git a/src/fennel-ls/handlers.fnl b/src/fennel-ls/handlers.fnl index 5c25224..c70c972 100644 --- a/src/fennel-ls/handlers.fnl +++ b/src/fennel-ls/handlers.fnl @@ -154,9 +154,9 @@ Every time the client sends a message, it gets handled by a function in the corr (send (message.diagnostics file))) (λ notifications.textDocument/didClose [self send {:textDocument {: uri}}] - ;; TODO reload from disk if we didn't get a didSave (local file (state.get-by-uri self uri)) - (set file.open? false)) + ;; TODO only reload from disk if we didn't get a didSave, instead of always + (state.flush-uri self uri)) (λ notifications.workspace/didChangeConfiguration [self send params] (state.write-config self params.fennel-ls)) diff --git a/src/fennel-ls/json-rpc.fnl b/src/fennel-ls/json-rpc.fnl index dadcb73..5652e5c 100644 --- a/src/fennel-ls/json-rpc.fnl +++ b/src/fennel-ls/json-rpc.fnl @@ -23,8 +23,8 @@ Luckily, I'm testing with Neovim, so I can pretend these problems don't exist fo header-line (let [sep (string.find header-line ": ") k (string.sub header-line 1 (- sep 1)) - v (string.sub header-line (+ sep 2))] - (tset header k (string.sub v 1 -2)) + v (string.sub header-line (+ sep 2) -2)] ;; trim off the \r + (tset header k v) (read-header in header))))) (λ read-n [in len ?buffer] diff --git a/src/fennel-ls/state.fnl b/src/fennel-ls/state.fnl index 9dd9f0d..4bc84a2 100644 --- a/src/fennel-ls/state.fnl +++ b/src/fennel-ls/state.fnl @@ -5,8 +5,8 @@ will be functions for managing user options. There is no global state in this project: all state will be stored in the \"self\" object." -(local utils (require :fennel-ls.utils)) (local searcher (require :fennel-ls.searcher)) +(local utils (require :fennel-ls.utils)) (local {: compile} (require :fennel-ls.compiler)) (λ read-file [uri] @@ -56,18 +56,31 @@ object." (compile self file) file))) +(λ flush-uri [self uri] + "get rid of data about a file, in case it changed in some way" + (tset self.files uri nil)) + (local default-config {:fennel-path "./?.fnl;./?/init.fnl;src/?.fnl;src/?/init.fnl" :macro-path "./?.fnl;./?/init-macros.fnl;./?/init.fnl;src/?.fnl;src/?/init-macros.fnl;src/?/init.fnl" :globals ""}) +;; TODO: set the warning levels of lints +;; allow all globals +;; allow some globals +;; pick from existing libraries of globals (ie love2d) +;; pick between different versions of lua (ie luajit) +;; pick a "compat always" mode that accpets anything if it could be valid in any lua +;; make a "compat strict" mode that warns about any lua-version-specific patterns +;; ie using (unpack) without saying (or table.unpack _G.unpack) or something like that + (λ write-config [self ?config] (if (not ?config) (set self.config default-config) ;; fast path, use all defaults (set self.config {;; fennel-path: ;; the path to use to find fennel files using (require) or (include) - :fennel-path (or ?config.fennelpath + :fennel-path (or ?config.fennel-path default-config.fennel-path) ;; macro-path: ;; the path to use to find fennel files using (require-macros) or (include-macros) @@ -84,7 +97,8 @@ object." (set self.root-uri params.rootUri) (write-config self)) -{: get-by-module +{: flush-uri + : get-by-module : get-by-uri : init-state : set-uri-contents diff --git a/test/completion-test.fnl b/test/completion-test.fnl index 827f358..fcd8945 100644 --- a/test/completion-test.fnl +++ b/test/completion-test.fnl @@ -13,9 +13,9 @@ (local filename (.. ROOT-URI "/imaginary-file.fnl")) (fn check-completion [body line col expected ?unexpected] - (local state (doto [] setup-server)) - (open-file state filename body) - (let [response (dispatch.handle* state (completion-at filename line col)) + (local self (doto [] setup-server)) + (open-file self filename body) + (let [response (dispatch.handle* self (completion-at filename line col)) seen (collect [_ suggestion (ipairs (. response 1 :result))] suggestion.label suggestion.label)] (if expected @@ -26,9 +26,9 @@ (is.nil (. seen exp) (.. exp " was suggested, but shouldn't be")))))) (fn check-no-completion [body line col expected ?unexpected] - (local state (doto [] setup-server)) - (open-file state filename body) - (let [response (dispatch.handle* state (completion-at filename line col))] + (local self (doto [] setup-server)) + (open-file self filename body) + (let [response (dispatch.handle* self (completion-at filename line col))] (is-matching (. response 1) {:jsonrpc "2.0" :id id :result nil} "there shouldn't be a result"))) diff --git a/test/diagnostic-test.fnl b/test/diagnostic-test.fnl index 1586a2b..fd7bcd2 100644 --- a/test/diagnostic-test.fnl +++ b/test/diagnostic-test.fnl @@ -24,8 +24,8 @@ (describe "diagnostic messages" (it "handles compile errors" - (local state (doto [] setup-server)) - (let [responses (open-file state filename "(do do)") + (local self (doto [] setup-server)) + (let [responses (open-file self filename "(do do)") diagnostic (match responses [{:params {: diagnostics}}] @@ -38,8 +38,8 @@ (is diagnostic "expected a diagnostic"))) (it "handles parse errors" - (local state (doto [] setup-server)) - (let [responses (open-file state filename "(do (print :hello(]") + (local self (doto [] setup-server)) + (let [responses (open-file self filename "(do (print :hello(]") diagnostic (match responses [{:params {: diagnostics}}] @@ -52,8 +52,8 @@ (is diagnostic "expected a diagnostic"))) (it "handles (match)" - (local state (doto [] setup-server)) - (let [responses (open-file state filename "(match)")] + (local self (doto [] setup-server)) + (let [responses (open-file self filename "(match)")] (is-matching responses [{:params {:diagnostics @@ -62,8 +62,8 @@ "diagnostics should always have a range"))) (it "gives more than one error" - (local state (doto [] setup-server)) - (let [responses (open-file state filename "(unknown-global-1 unknown-global-2)")] + (local self (doto [] setup-server)) + (let [responses (open-file self filename "(unknown-global-1 unknown-global-2)")] (is-matching responses [{:params {:diagnostics [a b]}}] "there should be a diagnostic for each one here")))) diff --git a/test/goto-definition-test.fnl b/test/goto-definition-test.fnl index 1b58e32..0888019 100644 --- a/test/goto-definition-test.fnl +++ b/test/goto-definition-test.fnl @@ -11,9 +11,9 @@ (describe "jump to definition" (fn check [request-file line char response-file start-line start-col end-line end-col] - (local state (doto [] setup-server)) - (let [message (dispatch.handle* state - (message.create-request 2 "textDocument/definition" + (local self (doto [] setup-server)) + (let [message (dispatch.handle* self + (message.create-request 2 :textDocument/definition {:position {:character char :line line} :textDocument {:uri (.. ROOT-URI "/" request-file)}})) uri (.. ROOT-URI "/" response-file)] diff --git a/test/hover-test.fnl b/test/hover-test.fnl index 52c96b3..cbf005d 100644 --- a/test/hover-test.fnl +++ b/test/hover-test.fnl @@ -11,8 +11,8 @@ (describe "hover" (fn check [request-file line char response-string] - (local state (doto [] setup-server)) - (let [message (dispatch.handle* state + (local self (doto [] setup-server)) + (let [message (dispatch.handle* self (message.create-request 2 "textDocument/hover" {:position {:character char :line line} :textDocument {:uri (.. ROOT-URI "/" request-file)}}))] diff --git a/test/init-macros.fnl b/test/init-macros.fnl index 043aabe..cddf0b4 100644 --- a/test/init-macros.fnl +++ b/test/init-macros.fnl @@ -1,3 +1,4 @@ +;; fennel-ls: macro-file "This document does not include tests. Instead it includes macros that are used for tests." (fn it [desc ...] diff --git a/test/init.fnl b/test/init.fnl index 4e3d327..fb9861a 100644 --- a/test/init.fnl +++ b/test/init.fnl @@ -7,3 +7,4 @@ (require :test.json-rpc-test) (require :test.misc-test) (require :test.string-processing-test) +(require :test.settings-test) diff --git a/test/settings-test.fnl b/test/settings-test.fnl new file mode 100644 index 0000000..7f9903c --- /dev/null +++ b/test/settings-test.fnl @@ -0,0 +1,43 @@ +(import-macros {: is-matching : describe : it : before-each} :test) +(local is (require :luassert)) + +(local {: view} (require :fennel)) +(local {: ROOT-URI + : open-file + : setup-server} (require :test.utils)) + +(local dispatch (require :fennel-ls.dispatch)) +(local message (require :fennel-ls.message)) + +(describe "settings" + (it "can set the path" + (local self (doto [] (setup-server {:fennel-ls {:fennel-path "./?/?.fnl"}}))) + (open-file self (.. ROOT-URI :/test.fnl) "(local {: this-is-in-modname} (require :modname))") + (let [[{:result {:range message}}] + (dispatch.handle* self + (message.create-request 2 :textDocument/definition + {:position {:character 12 :line 0} + :textDocument {:uri (.. ROOT-URI :/test.fnl)}}))] + (is.not.nil message) + "body"))) + + ;; (it "can set the path" + ;; (local self (doto [] (setup-server {:fennel-ls {:macro-path "./?/?.fnl"}})))) + + ;; (it "can infer the macro path from fennel-path" + ;; (local self (doto [] (setup-server {:fennel-ls {:fennel-path "./?/?.fnl"}})))) + + ;; (it "can accept an allowed global" + ;; (local self (doto [] (setup-server {:fennel-ls {:globals "vim"}})))) + + ;; (it "can accept a list of allowed globals" + ;; (local self (doto [] (setup-server {:fennel-ls {:globals "GAMESTATE,SCREEN_CENTER_X,ETC"}})))) + + ;; (it "can accept a way to allow all globals that match a pattern" + ;; (local self (doto [] (setup-server {:fennel-ls {:global-pattern "[A-Z]+"}})))) + + ;; (it "can turn off strict globals" + ;; (local self (doto [] (setup-server {:fennel-ls {:globals "*"}})))) + + ;; (it "can treat globals as a warning instead of an error" + ;; (local self (doto [] (setup-server {:fennel-ls {:diagnostics {:E202 "warning"}}}))))) diff --git a/test/test-project/modname/modname.fnl b/test/test-project/modname/modname.fnl new file mode 100644 index 0000000..9d260bc --- /dev/null +++ b/test/test-project/modname/modname.fnl @@ -0,0 +1,5 @@ +(fn this-is-in-modname [] + "this is a docstring" + nil) + +{: this-is-in-modname} diff --git a/test/utils.fnl b/test/utils.fnl index 2cd2020..9b30b68 100644 --- a/test/utils.fnl +++ b/test/utils.fnl @@ -24,12 +24,16 @@ :workspaceFolders [{:name ROOT-PATH :uri ROOT-URI}]}}) -(fn setup-server [state] - (dispatch.handle* state initialization-message)) +(fn setup-server [self ?config] + (dispatch.handle* self initialization-message) + (if ?config + (dispatch.handle* self {:jsonrpc "2.0" + :method :workspace/didChangeConfiguration + :params ?config}))) -(fn open-file [state name text] - (dispatch.handle* state - (message.create-notification "textDocument/didOpen" +(fn open-file [self name text] + (dispatch.handle* self + (message.create-notification :textDocument/didOpen {:textDocument {:uri name :languageId "fennel" @@ -37,7 +41,7 @@ : text}}))) (fn completion-at [file line character] - (message.create-request 2 "textDocument/completion" + (message.create-request 2 :textDocument/completion {:position {: line : character} :textDocument {:uri file}})) {: ROOT-URI