diff --git a/file.fnl b/file.fnl deleted file mode 100644 index a23aba2..0000000 --- a/file.fnl +++ /dev/null @@ -1,2 +0,0 @@ -(match [0 10] - [1 a] a) diff --git a/src/fennel-ls/parser.fnl b/src/fennel-ls/parser.fnl index 6d04b6e..cf66d0e 100644 --- a/src/fennel-ls/parser.fnl +++ b/src/fennel-ls/parser.fnl @@ -2,7 +2,7 @@ (local util (require :fennel-ls.util)) (λ get-ast-info [?ast info] - "find a given key of info from an AST object" + ;; find a given key of info from an AST object (or (?. (getmetatable ?ast) info) (. ?ast info))) diff --git a/src/fennel-ls/plugin.fnl b/src/fennel-ls/plugin.fnl index 69223bc..bba54fb 100644 --- a/src/fennel-ls/plugin.fnl +++ b/src/fennel-ls/plugin.fnl @@ -8,70 +8,62 @@ (local -λ- (fennel.sym :λ)) (local -lambda- (fennel.sym :lambda)) -;; types of things in the file.references list -{:from "a literal range" :to "a literal range"} -{:from "a literal range" :to-other-module ["modname" "key1" "key2" "key3" "key4" "etc"]} - -(λ table? [t] - (= :table (type t))) - -(λ string? [t] - (= :string (type t))) - (λ multisym? [t] + ;; check if t is a symbol with multiple parts, eg. foo.bar.baz (and (fennel.sym? t) (let [t (tostring t)] (or (t:find "%.") (t:find ":"))))) (λ iter [t] + ;; iterate through a list, sequence, or table (if (or (fennel.list? t) (fennel.sequence? t)) (ipairs t) (pairs t))) +(local has-tables-mt + {:__index + (λ [self key] + (let [val {}] + (tset self key val) + val))}) (λ analyze [file] - (assert file.text (fennel.view file)) - (assert file.uri) - (set file.references []) + "Compile the file, and record all the useful information from the compiler into the file object" - (local definitions - (doto {} - (setmetatable - {:__index - (λ [self key] - (let [val {}] - (tset self key val) - val))}))) + (local references []) + (local definitions (doto {} (setmetatable has-tables-mt))) - (λ find-variable [name ?scope] + (λ find-definition [name ?scope] (when ?scope (or (. definitions ?scope name) - (find-variable name ?scope.parent)))) + (find-definition name ?scope.parent)))) (λ reference [ast scope] - "called whenever a variable is referenced" + ;; Add a reference to the references (assert (fennel.sym? ast)) ;; find reference (let [name (string.match (tostring ast) "[^%.:]+") - target (find-variable (tostring name) scope)] - (tset file.references ast target))) + target (find-definition (tostring name) scope)] + (tset references ast target))) (λ define [?definition binding scope] - "called whenever a local variable or destructure statement is introduced" + ;; Add a definition to the definitions + ;; 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. (λ recurse [binding] (if (fennel.sym? binding) - (tset (. definitions scope) - (tostring binding) - {: binding :definition ?definition}) - (table? binding) - (each [k v (iter binding)] - (recurse v)))) + (tset (. definitions scope) + (tostring binding) + {: binding :definition ?definition}) + (= :table (type binding)) + (each [k v (iter binding)] + (recurse v)))) (recurse binding)) (λ define-function-name [ast scope] + ;; add a function definition to the definitions (match ast (where [_fn name args] (and (fennel.sym? name) @@ -83,23 +75,26 @@ :definition ast}))) (λ define-function-args [ast scope] + ;; add the definitions of function arguments to the definitions (local args (match ast (where [_fn args] (fennel.sequence? args)) args (where [_fn _name args] (fennel.sequence? args)) args)) (each [_ argument (ipairs args)] - (define nil argument scope))) ;; we say function arguments are "nil" for now + (define nil argument scope))) ;; we say function arguments are set to nil (λ define-function [ast scope] - "Introduces the various symbols exported by a function. -This cannot be done through the :fn feature of the compiler plugin system, because it needs to be -called *before* the body of the function is processed." + ;; handle the definitions of a function (define-function-name ast scope) (define-function-args ast scope)) (λ call [ast scope] - "called for every function call. Most calls aren't interesting, but fn is" + ;; handles every function call + ;; Most calls aren't interesting, but here's the list of the ones that are: (match ast + ;; This cannot be done through the :fn feature of the compiler plugin system + ;; because it needs to be called *before* the body of the function is processed. + ;; TODO check if hashfn needs to be here [-fn-] (define-function ast scope) [-λ-] @@ -111,16 +106,25 @@ called *before* the body of the function is processed." {:name "fennel-ls" :versions ["1.2.0"] :symbol-to-expression reference - : call + :call call :destructure define}) - (set file.ast (icollect [ok ast (fennel.parser file.text)] - ast)) + (local ast + (icollect [ok ast (fennel.parser file.text)] + ast)) + (local scope (fennel.scope)) - (each [_i form (ipairs file.ast)] + (each [_i form (ipairs ast)] (fennel.compile form {:filename file.uri : scope - :plugins [plugin]}))) + :plugins [plugin]})) + + ;; write things back to the file object + (set file.references references) + ;; (set file.definitions definitions) ;; not needed yet + (set file.ast ast)) + ;; (set file.analyzed? true)) + {: analyze} diff --git a/src/fennel-ls/state.fnl b/src/fennel-ls/state.fnl index fc86efc..29a7d1b 100644 --- a/src/fennel-ls/state.fnl +++ b/src/fennel-ls/state.fnl @@ -45,7 +45,6 @@ file))) - {: get-by-uri : get-by-module : set-uri-contents diff --git a/src/fennel-ls/the-actual-code.fnl b/src/fennel-ls/the-actual-code.fnl index 05ee5eb..18789d9 100644 --- a/src/fennel-ls/the-actual-code.fnl +++ b/src/fennel-ls/the-actual-code.fnl @@ -64,94 +64,76 @@ Every time the client sends a message, it gets handled by a function in the corr {:capabilities capabilities :serverInfo {:name "fennel-ls" :version "0.0.0"}}) -(λ string? [j] - (= (type j) :string)) - -(λ get-assignment-of-symbol [file symbol] - ;; TODO inline - (. file.references symbol)) - ;; These three functions are mutually recursive -(var (search-item - search-assignment - search-symbol) +(var + (search-assignment + search-symbol + search) nil) -(set search-item - (λ search-item [self file item stack] - (if ;; table - (fennelutils.table? item) - (if (. item (. stack (length stack))) - (search-item self file (. item (table.remove stack)) stack) - nil) - ;; symbol - (sym? item) - (search-symbol self file item stack) - ;; TODO - ;; functioncall (into body) - ;; require functioncall (into module) - - ;; else - true (error (.. "I don't know what to do with " (fennel.view item)))))) - (set search-assignment (λ search-assignment [self file binding ?definition stack] (if (= 0 (length stack)) binding ;; TODO sift down the binding - (search-item self file ?definition stack)))) + (search self file ?definition stack)))) (set search-symbol (λ search-symbol [self file symbol stack] (let [split (util.multi-sym-split symbol)] (for [i (length split) 2 -1] (table.insert stack (. split i)))) - (match (get-assignment-of-symbol file symbol) + (match (. file.references symbol) to (search-assignment self file to.binding to.definition stack) nil nil))) -(λ iter [t] - (if (or (fennel.list? t) - (fennel.sequence? t)) - (ipairs t) - (pairs t))) +(set search + (λ search [self file item stack] + (if (fennelutils.table? item) + (if (. item (. stack (length stack))) + (search self file (. item (table.remove stack)) stack) + nil) + (sym? item) + (search-symbol self file item stack) + ;; TODO + ;; functioncall (continue searching in body) + ;; require call (search in the new module) + :else (error (.. "I don't know what to do with " (fennel.view item)))))) -(λ find-symbol* [ast byte] +(λ find-symbol [ast byte ?recursively-called] (if (not= :table (type ast)) nil (parser.does-not-contain? ast byte) nil (sym? ast) ast - (or (fennel.list? ast) + (or (not ?recursively-called) + (fennel.list? ast) (fennel.sequence? ast)) ;; TODO binary search - (accumulate [result nil - _ v (ipairs ast) &until (or result (parser.past? v byte))] - (find-symbol* v byte)) - :else (accumulate [result nil - k v (pairs ast) &until result] - (or - (find-symbol* k byte) - (find-symbol* v byte))))) - -(λ find-symbol [ast byte] - ;; TODO binary search - (accumulate [result nil - _ v (ipairs ast) &until (or result (parser.past? v byte))] - (find-symbol* v byte))) + (accumulate + [result nil + _ v (ipairs ast) + &until (or result (parser.past? v byte))] + (find-symbol v byte true)) + :else + (accumulate + [result nil + k v (pairs ast) + &until result] + (or + (find-symbol k byte true) + (find-symbol v byte true))))) (λ requests.textDocument/definition [self send {: position :textDocument {: uri}}] (local file (state.get-by-uri self uri)) (local byte (util.pos->byte file.text position.line position.character)) - (local stack []) (match (find-symbol file.ast byte) symbol - (match (search-symbol self file symbol stack) + (match (search-symbol self file symbol []) definition {:range (parser.range file.text definition) - :uri uri}) - nil nil)) + :uri uri}))) (λ notifications.textDocument/didChange [self send {: contentChanges :textDocument {: uri}}] (local file (state.get-by-uri self uri)) @@ -163,6 +145,7 @@ Every time the client sends a message, it gets handled by a function in the corr (set file.open? true)) (λ notifications.textDocument/didClose [self send {:textDocument {: uri}}] + ;; TODO fix (local file (state.get-by-uri self uri)) (set file.open? false)) @@ -175,4 +158,3 @@ Every time the client sends a message, it gets handled by a function in the corr {: requests : notifications} - diff --git a/src/fennel-ls/util.fnl b/src/fennel-ls/util.fnl index a44f81d..142b6e3 100644 --- a/src/fennel-ls/util.fnl +++ b/src/fennel-ls/util.fnl @@ -53,9 +53,10 @@ These functions are all pure functions, which makes me happy." (text:sub end)))) (λ apply-changes [initial-text contentChanges] - "Take's a list of Language-Server-Protocol contentChanges and applies them to a piece of text. Doesn't yet handle UTF8 UTF16 magic from the protocol" - (accumulate [contents initial-text - _ change (ipairs contentChanges)] + "Takes a list of Language-Server-Protocol `contentChanges` and applies them to a piece of text. Doesn't yet handle UTF8 UTF16 magic from the protocol" + (accumulate + [contents initial-text + _ change (ipairs contentChanges)] (match change ;; Handle a change {:range {: start : end} : text} @@ -78,9 +79,6 @@ These functions are all pure functions, which makes me happy." (icollect [word (: (.. sym ".") :gmatch "(.-)[%.:]")] word)) -(fn reversed [tab]) - - {: uri->path : path->uri : pos->byte diff --git a/test.fnl b/test.fnl deleted file mode 100644 index b18e953..0000000 --- a/test.fnl +++ /dev/null @@ -1,10 +0,0 @@ -(var (a b c) nil) - - -(set a (fn a [] - (b))) - -(set b (fn b [] - (print "yay"))) - -(a)