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.
This commit is contained in:
XeroOl 2024-02-02 01:47:37 -06:00
parent 297f6c4fa9
commit 8b6d905450
6 changed files with 63 additions and 36 deletions

View File

@ -1,5 +1,8 @@
# Changelog
* Fix bug with renaming vars
* Improve multival tracking
## 0.1.0
### Initial Features

View File

@ -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]

View File

@ -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 !!

View File

@ -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

View File

@ -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")

View File

@ -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))")))