From 8b6d9054504fc09716e5a8096b409ff700ca99db Mon Sep 17 00:00:00 2001 From: XeroOl Date: Fri, 2 Feb 2024 01:47:37 -0600 Subject: [PATCH] make multival definition tracking much better there should now be no cases where multivals and table destructures are confused. ie, (local [_ x] (values 1 2)) and (local (_ x) [1 2]) are recognized as nonsense. --- changelog.md | 3 +++ src/fennel-ls/compiler.fnl | 39 ++++++++++++++++++----------------- src/fennel-ls/language.fnl | 18 ++++++++++++---- test/client.fnl | 2 +- test/goto-definition-test.fnl | 9 ++++++++ test/rename-test.fnl | 28 ++++++++++++++----------- 6 files changed, 63 insertions(+), 36 deletions(-) diff --git a/changelog.md b/changelog.md index 2f5c7c2..6cf4f8f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,8 @@ # Changelog +* Fix bug with renaming vars +* Improve multival tracking + ## 0.1.0 ### Initial Features diff --git a/src/fennel-ls/compiler.fnl b/src/fennel-ls/compiler.fnl index 4740444..2600aaa 100644 --- a/src/fennel-ls/compiler.fnl +++ b/src/fennel-ls/compiler.fnl @@ -90,7 +90,6 @@ later by fennel-ls.language to answer requests from the client." (do ;; already exists (assert (= symbol (. references symbol :symbol)) (.. "the symbol should always be the same"))) ;; (assert (= target (. references symbol :target)) (.. "different targets: " (view target) (view (. references symbol :target))))) - ;; (print "old and new" ref-type (. references symbol :ref-type))) (let [ref {: symbol : target : ref-type}] (tset references symbol ref) (table.insert target.referenced-by ref)))))) @@ -108,7 +107,7 @@ later by fennel-ls.language to answer requests from the client." ;; recursively explore the binding (which, in the general case, is a destructuring assignment) ;; right now I'm not keeping track of *how* the symbol was destructured: just finding all the symbols for now. ;; also, there's no logic for (values) - (λ recurse [binding keys] + (λ recurse [binding keys depth] (if (sym? binding) (let [definition {: binding @@ -117,31 +116,33 @@ later by fennel-ls.language to answer requests from the client." :keys (if (< 0 (length keys)) (fcollect [i 1 (length keys)] (. keys i))) + :multival keys.multival :var? (?. ?opts :isvar)}] (tset (. definitions-by-scope scope) (tostring binding) definition) (tset definitions binding definition)) (list? binding) - (if (and (is-values? ?definition) - (= (length binding) - (- (length ?definition) 1))) - (for [i 1 (length binding)] - (define (. ?definition (+ i 1)) (. binding i) scope ?opts)) - (recurse (. binding 1) keys)) + (let [nested? (not= depth 0)] + (if nested? (error (.. "I didn't expect to find a multival destructure in " (view binding) " at " (view keys)))) + (each [i child (ipairs binding)] + (set keys.multival i) + (recurse child keys (+ depth 1)) + (set keys.multival nil))) (table? binding) (accumulate [prev nil - k v (iter binding)] - (if (or (sym? k :&as) (sym? prev :&as)) - (recurse v keys) - (or (sym? k :&) (sym? prev :&)) - ;; currently the "rest" isn't counted as a binding - nil - (or (sym? v :&as) (sym? v :&)) - v + key child (iter binding)] + (if (or (sym? key :&as) (sym? prev :&as)) + ;; if its &as, just keep the keys the same + (recurse child keys (+ depth 1)) + (or (sym? key :&) (sym? prev :&)) + ;; currently the "rest" param is defined to [] + (define [] child scope ?opts) + (or (sym? child :&as) (sym? child :&)) + child (do - (table.insert keys k) - (recurse v keys) + (table.insert keys key) + (recurse child keys (+ depth 1)) (table.remove keys)))))) - (recurse binding [])) + (recurse binding [] 0)) (λ mutate [_?definition binding scope] (λ recurse [binding keys] diff --git a/src/fennel-ls/language.fnl b/src/fennel-ls/language.fnl index 92e518d..20e05ae 100644 --- a/src/fennel-ls/language.fnl +++ b/src/fennel-ls/language.fnl @@ -2,7 +2,7 @@ The high level analysis system that does deep searches following the data provided by compiler.fnl." -(local {: sym? : list? : sequence? : varg? : sym &as fennel} (require :fennel)) +(local {: sym? : list? : sequence? : varg? : sym : view} (require :fennel)) (local utils (require :fennel-ls.utils)) (local state (require :fennel-ls.state)) @@ -34,18 +34,28 @@ the data provided by compiler.fnl." (λ stack-add-multisym! [stack symbol] (stack-add-split! stack (utils.multi-sym-split symbol))) +(λ search-multival [self file ast stack ?multival opts] + (let [multival (or ?multival 1)] + (if (= 1 multival) + (search-ast self file ast stack opts) + (sym? ast) (values nil file) ;; BASE CASE !! + (list? ast) (if (sym? (. ast 1) :values) + (search-ast self file (. ast (+ 1 multival)) stack opts) + (values nil file)) ;; GIVING UP !! + (= :table (type ast)) (values nil file)))) ;; GIVING UP !! + (λ search-assignment [self file assignment stack opts] - (assert assignment.target (.. "WRONG TYPE" (fennel.traceback))) (let [{:target {:binding _ :definition ?definition :keys ?keys + :multival ?multival :fields ?fields}} assignment] (if (and (= 0 (length stack)) opts.stop-early?) (values assignment.target file) ;; BASE CASE!! ;; search a virtual field from :fields (and (not= 0 (length stack)) (?. ?fields (. stack (length stack)))) (search-assignment self file {:target (. ?fields (table.remove stack))} stack opts) - (search-ast self file ?definition (stack-add-keys! stack ?keys) opts)))) + (search-multival self file ?definition (stack-add-keys! stack ?keys) ?multival opts)))) (λ search-symbol [self file symbol stack opts] (if (= symbol -nil-) @@ -84,7 +94,7 @@ the data provided by compiler.fnl." (where [-setmetatable- tbl _mt]) (search-ast self file tbl stack opts) - ;; functions evaluate to "themselves" + ;; fn marks a function literal [-fn-] (values {:definition call} file) ;; BASE CASE !! diff --git a/test/client.fnl b/test/client.fnl index 71e5e24..c59fd29 100644 --- a/test/client.fnl +++ b/test/client.fnl @@ -11,7 +11,7 @@ (.. "file://" ROOT-PATH)) (local default-params - {:capabilities {} + {:capabilities {:general {:positionEncodings [:utf-8]}} :clientInfo {:name "Neovim" :version "0.7.2"} :initializationOptions {} :processId 16245 diff --git a/test/goto-definition-test.fnl b/test/goto-definition-test.fnl index 09b644d..c1794b5 100644 --- a/test/goto-definition-test.fnl +++ b/test/goto-definition-test.fnl @@ -104,6 +104,15 @@ _response (c:definition :foo.fnl 1 8)] nil)) + (it "can go through multival destructures" + (let [c (doto (create-client) + (: :open-file! :foo.fnl "(local [x y] (values [1 2] [3 4]))\n(local (a b) (values {:x y : y} {: x : y}))\n(print b.x a)")) + [find_b] (c:definition :foo.fnl 2 9)] + ;; it finds the first `x` symbol + (print (view find_b)) + (is.same find_b.result.range {:start {:line 0 :character 8} :end {:line 0 :character 9}}) + nil)) + ;; (it "can go through more than one extra file") ;; (it "will give up instead of freezing on recursive requires") ;; (it "finds the definition of in-file macros") diff --git a/test/rename-test.fnl b/test/rename-test.fnl index 8c76c41..fbae206 100644 --- a/test/rename-test.fnl +++ b/test/rename-test.fnl @@ -37,18 +37,22 @@ (check-rename "(fn [{: x}] x)" 0 8 :foo "(fn [{: foo}] foo)") (check-rename "(fn [{:x x}] x)" 0 9 :foo "(fn [{:x foo}] foo)")) - (it "renames a sym inside of lambda" - (check-rename "(λ [foo] (print foo))" 0 6 :something - "(λ [something] (print something))")) + (it "renames a sym inside of lambda" + (check-rename "(λ [foo] (print foo))" 0 6 :something + "(λ [something] (print something))")) - (it "renames a sym inside of set" - (check-rename "(var x 10)\n(set x 20)" 1 6 :something - "(var something 10)\n(set something 20)")) + (it "renames a sym inside of set" + (check-rename "(var x 10)\n(set x 20)" 1 6 :something + "(var something 10)\n(set something 20)")) - (it "renames a sym inside of set 2" - (check-rename "(var x 10)\n(var m 0)\n(set (m x) (values 10 20))" 2 8 :something - "(var something 10)\n(var m 0)\n(set (m something) (values 10 20))")) + (it "renames a sym inside of set 2" + (check-rename "(var x 10)\n(var m 0)\n(set (m x) (values 10 20))" 2 8 :something + "(var something 10)\n(var m 0)\n(set (m something) (values 10 20))")) - (it "renames a sym inside of macro that uses multiple times" - (check-rename "(var x 10)\n(doto x (set 20) (set 30))" 1 6 :something - "(var something 10)\n(doto something (set 20) (set 30))"))) + (it "renames a sym inside of set 3" + (check-rename "(var (x y) 10)\n(set (x y) 10)" 1 8 :something + "(var (x something) 10)\n(set (x something) 10)")) + + (it "renames a sym inside of macro that uses multiple times" + (check-rename "(var x 10)\n(doto x (set 20) (set 30))" 1 6 :something + "(var something 10)\n(doto something (set 20) (set 30))")))