From 0d8b92792981ccb47ac31f92aefe0935b0de4362 Mon Sep 17 00:00:00 2001 From: XeroOl Date: Sat, 3 Sep 2022 12:52:34 -0500 Subject: [PATCH] goto-definition with injected fields (fn a.b []) --- README.md | 4 ++-- src/fennel-ls/compiler.fnl | 17 ++++++++++++----- src/fennel-ls/language.fnl | 21 ++++++++++++++------- test/goto-definition-test.fnl | 5 +++-- test/test-project/goto-definition.fnl | 4 ++++ 5 files changed, 35 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index be7cfca..45ca013 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Features / To Do List / Things I would enjoy patches for: - [X] `require` and cross-module definition lookups - [ ] goes to a.method on `(: a :method)` when triggered at `:method` - [X] expanded macros (a little bit) - - [ ] table mutation via `fn` special: `(fn obj.new-field [])` + - [X] table mutation via `fn` special: `(fn obj.new-field [])` - [ ] macro calls / which macros are in scope - [ ] setmetatable - [ ] function arguments / function calls @@ -36,7 +36,7 @@ Features / To Do List / Things I would enjoy patches for: - [X] from current scope - [ ] from macros (only on first form in a list) - [ ] from specials (only on first form in a list) - - [ ] "dot completion" for table fields + - [X] "dot completion" for table fields - [ ] dot completion is aware of a string's fields - [ ] from anywhere else that I'm forgetting right now - [ ] actually compliant rules about lexical scope (only see things declared before, not after) diff --git a/src/fennel-ls/compiler.fnl b/src/fennel-ls/compiler.fnl index 5d0af63..66c745f 100644 --- a/src/fennel-ls/compiler.fnl +++ b/src/fennel-ls/compiler.fnl @@ -5,6 +5,7 @@ later by fennel-ls.language to answer requests from the client." (local {: sym? : list? : sequence? : sym : view &as fennel} (require :fennel)) (local message (require :fennel-ls.message)) +(local utils (require :fennel-ls.utils)) ;; words surrounded by - are symbols, ;; because fennel doesn't allow 'require in a runtime file @@ -85,12 +86,18 @@ later by fennel-ls.language to answer requests from the client." (match ast (where [_fn name args] (and (sym? name) - (not (multisym? name)) ;; not dealing with multisym for now (sequence? args))) - (tset (. definitions-by-scope scope) ;; !!! TODO somehow insert into child scope - (tostring name) - {:binding name - :definition ast}))) + (let [def {:binding name :definition ast}] + (if (multisym? name) + (match (utils.multi-sym-split name) + [ref field nil] + (let [target (find-definition ref scope)] + (set target.fields (or target.fields {})) + (tset target.fields field def))) ;; TODO more complicated function name metadata + + (tset (. definitions-by-scope scope) + (tostring name) + def))))) (λ define-function-args [ast scope] ;; add the definitions of function arguments to the definitions diff --git a/src/fennel-ls/language.fnl b/src/fennel-ls/language.fnl index 96c8c32..71746e4 100644 --- a/src/fennel-ls/language.fnl +++ b/src/fennel-ls/language.fnl @@ -19,14 +19,21 @@ the data provided by compiler.fnl." (var search nil) ;; all of the search functions are mutually recursive (λ search-assignment [self file assignment stack opts] - (let [{: binding :definition ?definition :keys ?keys} assignment] + (let [{: binding + :definition ?definition + :keys ?keys + :fields ?fields} assignment] (if (and (= 0 (length stack)) opts.stop-early?) - (values assignment file) ;; BASE CASE!! - (do - (if ?keys - (fcollect [i (length ?keys) 1 -1 &into stack] - (. ?keys i))) - (search self file ?definition stack opts))))) + (values assignment file) ;; BASE CASE!! + + (and (not= 0 (length stack)) (?. ?fields (. stack (length stack)))) + (search-assignment self file (. ?fields (table.remove stack)) stack opts) + + (do + (if ?keys + (fcollect [i (length ?keys) 1 -1 &into stack] + (. ?keys i))) + (search self file ?definition stack opts))))) (λ search-symbol [self file symbol stack opts] (if (= symbol -nil-) diff --git a/test/goto-definition-test.fnl b/test/goto-definition-test.fnl index e53fe31..d410bf7 100644 --- a/test/goto-definition-test.fnl +++ b/test/goto-definition-test.fnl @@ -70,7 +70,7 @@ (check :goto-definition.fnl 1 5 :bar.fnl 0 0 0 2)) (it "goes to the last form of `do` and `let`" - (check :goto-definition.fnl 47 13 :goto-definition.fnl 47 30 47 52))) + (check :goto-definition.fnl 47 13 :goto-definition.fnl 47 30 47 52)) ;; TODO ;; (it "doesn't leak function arguments to the surrounding scope") @@ -82,7 +82,8 @@ ;; (it "can follow import-macros (namespaced)") ;; (it "can go to the definition even in a lua file") ;; (it "finds (set a.b) definitions") - ;; (it "finds (fn a.b [] ...) declarations") + (it "finds (fn a.b [] ...) declarations" + (check :goto-definition.fnl 51 12 :goto-definition.fnl 50 4 50 22))) ;; (it "finds (tset a :b) definitions") ;; (it "finds (setmetatable a {__index {:b def}) definitions") ;; (it "finds definitions into a function (fn foo [] (local x 10) {: x}) (let [result (foo)] (print result.x)) finds result.x") diff --git a/test/test-project/goto-definition.fnl b/test/test-project/goto-definition.fnl index 5078808..7f5ec58 100644 --- a/test/test-project/goto-definition.fnl +++ b/test/test-project/goto-definition.fnl @@ -46,3 +46,7 @@ (print funny.field) (local object (do (let [a 10] {:my-definition :here}))) + +(local module {}) +(fn module.my-function [a b c] "docstring" (let [body (+ a b c)] body)) +(module.my-function 1 2 3)