Improved goto-definition sifting destructures

This commit is contained in:
XeroOl 2022-08-13 14:20:40 -05:00
parent 44e7158e7d
commit c2cd1e50fe
No known key found for this signature in database
GPG Key ID: 9DD4B4B4DAED0322
5 changed files with 81 additions and 30 deletions

View File

@ -32,12 +32,13 @@
(λ compile [file] (λ compile [file]
"Compile the file, and record all the useful information from the compiler into the file object" "Compile the file, and record all the useful information from the compiler into the file object"
(local references []) (local references {})
(local definitions (doto {} (setmetatable has-tables-mt))) (local definitions-by-scope (doto {} (setmetatable has-tables-mt)))
(local definitions {})
(λ find-definition [name ?scope] (λ find-definition [name ?scope]
(when ?scope (when ?scope
(or (. definitions ?scope name) (or (. definitions-by-scope ?scope name)
(find-definition name ?scope.parent)))) (find-definition name ?scope.parent))))
(λ reference [ast scope] (λ reference [ast scope]
@ -53,15 +54,21 @@
;; recursively explore the binding (which, in the general case, is a destructuring assignment) ;; 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. ;; 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) ;; also, there's no logic for (values)
(λ recurse [binding] (λ recurse [binding keys]
(if (fennel.sym? binding) (if (fennel.sym? binding)
(tset (. definitions scope) (let [definition
(tostring binding) {: binding
{: binding :definition ?definition}) : ?definition
:?keys (fcollect [i 1 (length keys)]
(. keys i))}]
(tset (. definitions-by-scope scope) (tostring binding) definition)
(tset definitions binding definition))
(= :table (type binding)) (= :table (type binding))
(each [k v (iter binding)] (each [k v (iter binding)]
(recurse v)))) (table.insert keys k)
(recurse binding)) (recurse v keys)
(table.remove keys))))
(recurse binding []))
(λ define-function-name [ast scope] (λ define-function-name [ast scope]
;; add a function definition to the definitions ;; add a function definition to the definitions
@ -70,10 +77,10 @@
(and (fennel.sym? name) (and (fennel.sym? name)
(not (multisym? name)) ;; not dealing with multisym for now (not (multisym? name)) ;; not dealing with multisym for now
(fennel.sequence? args))) (fennel.sequence? args)))
(tset (. definitions scope.parent) (tset (. definitions-by-scope scope) ;; !!! parent or child?
(tostring name) (tostring name)
{:binding name {:binding name
:definition ast}))) :?definition ast})))
(λ define-function-args [ast scope] (λ define-function-args [ast scope]
;; add the definitions of function arguments to the definitions ;; add the definitions of function arguments to the definitions
@ -82,7 +89,7 @@
(where [_fn args] (fennel.sequence? args)) args (where [_fn args] (fennel.sequence? args)) args
(where [_fn _name args] (fennel.sequence? args)) args)) (where [_fn _name args] (fennel.sequence? args)) args))
(each [_ argument (ipairs args)] (each [_ argument (ipairs args)]
(define nil argument scope))) ;; we say function arguments are set to nil (define nil argument scope))) ;; we say function arguments are set to nil ;; !!! parent or child?
(λ define-function [ast scope] (λ define-function [ast scope]
;; handle the definitions of a function ;; handle the definitions of a function
@ -124,7 +131,8 @@
;; write things back to the file object ;; write things back to the file object
(set file.references references) (set file.references references)
;; (set file.definitions definitions) ;; not needed yet (set file.definitions definitions)
;; (set file.definitions-by-scope definitions-by-scope) ;; not needed yet
(set file.ast ast)) (set file.ast ast))
;; (set file.compiled? true)) ;; (set file.compiled? true))
{: compile} {: compile}

View File

@ -63,7 +63,7 @@ Every time the client sends a message, it gets handled by a function in the corr
(local byte (pos->byte file.text position.line position.character)) (local byte (pos->byte file.text position.line position.character))
(match (language.find-symbol file.ast byte) (match (language.find-symbol file.ast byte)
symbol symbol
(match (language.search-symbol self file symbol []) (match (language.search-main self file symbol [])
(definition result-file) ;; curse you, magical match rules (definition result-file) ;; curse you, magical match rules
(message.range-and-uri definition result-file)))) (message.range-and-uri definition result-file))))

View File

@ -15,21 +15,23 @@
nil) nil)
(set search-assignment (set search-assignment
(λ search-assignment [self file binding ?definition stack] (λ search-assignment [self file {: binding : ?definition : ?keys} stack]
(if (= 0 (length stack)) (if (= 0 (length stack))
(values binding file) ;; BASE CASE!! (values binding file) ;; BASE CASE!!
(do
;; TODO sift down the binding (if ?keys
(search self file ?definition stack)))) (fcollect [i (length ?keys) 1 -1 &into stack]
(. ?keys i)))
(search self file ?definition stack)))))
(set search-symbol (set search-symbol
(λ search-symbol [self file symbol stack] (λ search-symbol [self file symbol stack]
(let [split (utils.multi-sym-split symbol)] (let [split (utils.multi-sym-split symbol)]
(for [i (length split) 2 -1] (fcollect [i (length split) 2 -1 &into stack]
(table.insert stack (. split i)))) (. split i))) ;; TODO test coverage for this line
(match (. file.references symbol) (match (. file.references symbol)
to (search-assignment self file to.binding to.definition stack) to (search-assignment self file to stack))))
nil nil))) ;; BASE CASE: Give up
(set search (set search
(λ search [self file item stack] (λ search [self file item stack]
@ -37,7 +39,9 @@
(fennelutils.table? item) (fennelutils.table? item)
(if (. item (. stack (length stack))) (if (. item (. stack (length stack)))
(search self file (. item (table.remove stack)) stack) (search self file (. item (table.remove stack)) stack)
nil) ;; BASE CASE: Give up (= 0 (length stack))
(values item file) ;; BASE CASE !!
nil) ;; BASE CASE Give up
(sym? item) (sym? item)
(search-symbol self file item stack) (search-symbol self file item stack)
;; TODO ;; TODO
@ -49,6 +53,22 @@
(search self newfile newitem stack)) (search self newfile newitem stack))
_ (error (.. "I don't know what to do with " (fennel.view item))))))) _ (error (.. "I don't know what to do with " (fennel.view item)))))))
(λ search-main [self file symbol]
;; TODO partial byting, go to different defitition sites depending on which section of the symbol the trigger happens on
;; The stack is the multi-sym parts still to search
;; for example, if I'm searching for "foo.bar.baz", my "item" or "symbol" is foo,
;; and the stack has ["baz" "bar"], with "bar" at the "top"/"end" of the stack as the next key to search.
(local stack
(let [split (utils.multi-sym-split symbol)]
(fcollect [i (length split) 2 -1]
(. split i))))
(match (values (. file.references symbol) (. file.definitions symbol))
(ref _)
(search-assignment self file ref stack)
(_ def)
(search self file def.?definition stack)))
(λ past? [?ast byte] (λ past? [?ast byte]
;; check if a byte is past an ast object ;; check if a byte is past an ast object
(and (= (type ?ast) :table) (and (= (type ?ast) :table)
@ -101,4 +121,5 @@
(find-symbol v byte true))))) (find-symbol v byte true)))))
{: find-symbol {: find-symbol
: search-symbol} : search-symbol
: search-main}

View File

@ -49,19 +49,25 @@
(it "can go to a function inside a table" (it "can go to a function inside a table"
(check "example.fnl" 28 6 "example.fnl" 4 4 4 7)) (check "example.fnl" 28 6 "example.fnl" 4 4 4 7))
;; (it "can go to a field inside of a table")
(it "can go to a function in another file when accessed by multisym" (it "can go to a function in another file when accessed by multisym"
(check "example.fnl" 7 7 "foo.fnl" 2 4 2 13))) (check "example.fnl" 7 7 "foo.fnl" 2 4 2 13))
;; (it "goes further if you go to definition on a binding") (it "goes further if you go to definition on a binding"
(check "example.fnl" 31 12 "example.fnl" 23 4 23 5))
;; (it "can go to a destructured function argument")
;; it can go up and down destructuring
(it "can trace a variable that was introduced with destructuring assignment"
(check "example.fnl" 38 15 "example.fnl" 33 7 33 13)))
;; (it "handles (local _ (require XXX))")
;; (check "example.fnl" 0 10 "foo.fnl" 0 0 0 0)
;; (it "works directly on a require/include (require XXX))" ;; (it "works directly on a require/include (require XXX))"
;; (check "example.fnl" 1 5 "bar.fnl" 0 0 0 0)) ;; (check "example.fnl" 1 5 "bar.fnl" 0 0 0 0))
;; (it "can go to a field inside of a table")
;; (it "can go to a destructured function argument")
;; (it "can go to a reference that occurs in a macro") ;; (it "can go to a reference that occurs in a macro")
;; (it "doesn't have ghost definitions from the same byte ranges as the macro files it's using") ;; (it "doesn't have ghost definitions from the same byte ranges as the macro files it's using")
;; (it "can go to a function in another file imported via destructuring assignment") ;; (it "can go to a function in another file imported via destructuring assignment")

View File

@ -28,3 +28,19 @@
(obj.bar 2 3) (obj.bar 2 3)
(obj:a) (obj:a)
(local redefinition b)
(local findme 10)
(local deep {:a {:b {:field findme}}})
(local {:a {:b shallow}} deep)
(local mixed [{:key [5 {:foo shallow}]}])
(local [{:key [_ {:foo funny}]}] mixed)
(print funny.field)
(local findme 10) ;; via field access instead of destructure
(local deep {:a {:b {:field findme}}})
(local shallow deep.a.b)
(local mixed [{:key [5 {:foo shallow}]}])
(local funny (. mixed 1 :key 2 foo))
(print funny.field)