diff --git a/src/fennel-ls/completion.fnl b/src/fennel-ls/completion.fnl index c45dc36..b065185 100644 --- a/src/fennel-ls/completion.fnl +++ b/src/fennel-ls/completion.fnl @@ -5,6 +5,7 @@ (local message (require :fennel-ls.message)) (local format (require :fennel-ls.formatter)) (local navigate (require :fennel-ls.navigate)) +(local compiler (require :fennel-ls.compiler)) (local {:metadata METADATA} (require :fennel.compiler)) (local hardcoded-completions @@ -20,16 +21,21 @@ (let [file (files.get-by-uri server uri) ;; find where the cursor is byte (utils.position->byte file.text position server.position-encoding) + ;; create a brand new file + file {:text (.. (file.text:sub 1 (- byte 1)) "|" (file.text:sub byte)) :uri file.uri} + _ (compiler.compile server file) ;; find what ast objects are under the cursor - (?symbol parents) (analyzer.find-symbol file.ast byte) + (symbol parents) (analyzer.find-symbol file.ast byte) ;; check what context I'm in in-call-position? (and (fennel.list? (. parents 1)) - (= ?symbol (. parents 1 1))) + (= symbol (. parents 1 1))) ;; find the first one that contains a scope scope (or (accumulate [?find nil _ parent (ipairs parents) &until ?find] (. file.scopes parent)) file.scope) - range (if ?symbol (message.ast->range server file ?symbol) {:start position :end position}) + range (case (message.ast->range server file symbol) + r (do (set r.end.character (- r.end.character 1)) r) + _ {:start position :end position}) results [] seen {}] @@ -67,7 +73,7 @@ (set (. seen-manglings global*) true) (case (analyzer.search-name-and-scope server file global* scope) def (if (and (= :_G (tostring global*)) - (not (: (tostring ?symbol) :match "_G[:.]"))) + (not (: (tostring symbol) :match "_G[:.]"))) (add-completion! global* def) (add-completion-recursively! global* def)) _ (do @@ -104,10 +110,10 @@ (case (message:match "unknown identifier: ([a-zA-Z0-9_-]+)") identifier (add-completion! identifier {} :Variable)))) - (if (. file.definitions ?symbol) - (binding-completions) - (expression-completions)) - + (when symbol + (if (. file.definitions symbol) + (binding-completions) + (expression-completions))) (if server.can-do-good-completions? {:itemDefaults {:editRange range :data {: uri : byte}} diff --git a/test/completion.fnl b/test/completion.fnl index d313a27..b0a6c49 100644 --- a/test/completion.fnl +++ b/test/completion.fnl @@ -39,7 +39,7 @@ (= e.textEdit.range.end.line c.textEdit.range.end.line) (= e.textEdit.range.end.character c.textEdit.range.end.character) (= e.textEdit.newText c.textEdit.newText)) - (and (= (type e.textEdit) :function)) (e.textEdit c.textEdit)))) + (and (= (type e.textEdit) :function) (e.textEdit c.textEdit))))) i))) (fn check [file-contents expected unexpected ?client-opts] @@ -121,8 +121,8 @@ (check "(local x {:field {:deep 100}})\n(if de" [:x.field.deep] []) (check "(local t {:field (fn [foo] nil)})\n(t|" [:t.field] []) (check "(local t {:field (fn [self] nil)})\n(t|" [:t:field] []) - (check "(local t {})\n(fn t.field [foo] nil)})\n(t|" [:t.field] []) - (check "(local t {})\n(fn t.field [self] nil)})\n(t|" [:t:field] []) + (check "(local t {})\n(fn t.field [foo] nil)\n(t|" [:t.field] []) + (check "(local t {})\n(fn t.field [self] nil)\n(t|" [:t:field] []) nil) (fn test-builtin [] @@ -258,13 +258,35 @@ nil) (fn test-destructure [] - ;; this is a binding variable, we don't want all the normal completions - (check "(local f|)\n(print foo)" + ;; this is in a destructure location, so we don't want all the normal completions + (check "(local |) + (print foo)" [:foo] - [:setmetatable :_G]) - (check "(let [f|]\n(print foo)" + [:math]) + (check "(local f|) + (print foo)" [:foo] - [:setmetatable :_G]) + [:math]) + (check "(let [f|] + (print foo))" + [:foo] + [:math]) + (check "(let [foo |] ; cursor is in an expression so we want expressions now + (print foo))" + [:math] + []) + nil) + +(fn test-no-completion [] + (check "; (" + [] + [:math]) + (check "\" (|\n\"" + [] + [:math]) + (check "\"\n(|\"" + [] + [:math]) nil) ;; ;; Future tests / features @@ -294,4 +316,5 @@ : test-field : test-docs : test-module - : test-destructure} + : test-destructure + : test-no-completion}