refactoring
This commit is contained in:
parent
80d4455ed3
commit
bb13e9b809
@ -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)))
|
||||
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -45,7 +45,6 @@
|
||||
file)))
|
||||
|
||||
|
||||
|
||||
{: get-by-uri
|
||||
: get-by-module
|
||||
: set-uri-contents
|
||||
|
||||
@ -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}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user