more refactoring churn for no reason

This commit is contained in:
XeroOl 2022-08-08 18:11:20 -05:00
parent f610201b9d
commit e57833f1e5
No known key found for this signature in database
GPG Key ID: 9DD4B4B4DAED0322
10 changed files with 154 additions and 136 deletions

View File

@ -1,4 +1,4 @@
(local core (require :fennel-ls.core)) (local dispatch (require :fennel-ls.dispatch))
(local json-rpc (require :fennel-ls.json-rpc)) (local json-rpc (require :fennel-ls.json-rpc))
(λ main-loop [in out] (λ main-loop [in out]
@ -6,7 +6,7 @@
(local state []) (local state [])
(while true (while true
(let [msg (json-rpc.read in)] (let [msg (json-rpc.read in)]
(core.handle state send msg)))) (dispatch.handle state send msg))))
(λ main [] (λ main []
(main-loop (main-loop

View File

@ -1,10 +1,4 @@
(local fennel (require :fennel)) (local fennel (require :fennel))
(local fennelutils (require :fennel.utils))
(local utils (require :fennel-ls.utils))
(local insert table.insert)
(local sym? fennel.sym?)
(local list? fennel.list?)
;; words surrounded by - are symbols, ;; words surrounded by - are symbols,
;; because fennel doesn't allow 'require in a runtime file ;; because fennel doesn't allow 'require in a runtime file
@ -13,32 +7,6 @@
(local -λ- (fennel.sym :λ)) (local -λ- (fennel.sym :λ))
(local -lambda- (fennel.sym :lambda)) (local -lambda- (fennel.sym :lambda))
(λ contains? [?ast byte]
;; check if an ast contains a byte
(and (= (type ?ast) :table)
(utils.get-ast-info ?ast :bytestart)
(utils.get-ast-info ?ast :byteend)
(<= (utils.get-ast-info ?ast :bytestart)
byte
(+ 1 (utils.get-ast-info ?ast :byteend)))))
(λ does-not-contain? [?ast byte]
;; check if a byte is in range of the ast
(and (= (type ?ast) :table)
(utils.get-ast-info ?ast :bytestart)
(utils.get-ast-info ?ast :byteend)
(not
(<= (utils.get-ast-info ?ast :bytestart)
byte
(+ 1 (utils.get-ast-info ?ast :byteend))))))
(λ past? [?ast byte]
;; check if a byte is past an ast object
(and (= (type ?ast) :table)
(utils.get-ast-info ?ast :bytestart)
(< byte (utils.get-ast-info ?ast :bytestart))
false))
(λ multisym? [t] (λ multisym? [t]
;; check if t is a symbol with multiple parts, eg. foo.bar.baz ;; check if t is a symbol with multiple parts, eg. foo.bar.baz
@ -61,7 +29,7 @@
(tset self key val) (tset self key val)
val))}) val))})
analyze [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 [])
@ -84,6 +52,7 @@
;; Add a definition to the definitions ;; Add a definition to the definitions
;; 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)
(λ recurse [binding] (λ recurse [binding]
(if (fennel.sym? binding) (if (fennel.sym? binding)
(tset (. definitions scope) (tset (. definitions scope)
@ -156,68 +125,5 @@
(set file.references references) (set file.references references)
;; (set file.definitions definitions) ;; not needed yet ;; (set file.definitions definitions) ;; not needed yet
(set file.ast ast)) (set file.ast ast))
;; (set file.analyzed? true)) ;; (set file.compiled? true))
{: compile}
(var
(search-assignment
search-symbol
search)
nil)
(set search-assignment
(λ search-assignment [self file binding ?definition stack]
(if (= 0 (length stack))
binding
;; TODO sift down the binding
(search self file ?definition stack))))
(set search-symbol
(λ search-symbol [self file symbol stack]
(let [split (utils.multi-sym-split symbol)]
(for [i (length split) 2 -1]
(table.insert stack (. split i))))
(match (. file.references symbol)
to (search-assignment self file to.binding to.definition stack)
nil nil)))
(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 ?recursively-called]
(if (not= :table (type ast))
nil
(does-not-contain? ast byte)
nil
(sym? ast)
ast
(or (not ?recursively-called)
(fennel.list? ast)
(fennel.sequence? ast))
;; TODO binary search
(accumulate
[result nil
_ v (ipairs ast)
&until (or result (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)))))
{: analyze
: find-symbol
: search-symbol}

View File

@ -1,4 +1,4 @@
"core "Dispatch
This module is responsible for deciding which code to call in response This module is responsible for deciding which code to call in response
to a given LSP request from the client. to a given LSP request from the client.

View File

@ -1,14 +1,13 @@
"Big dispatch "Handlers
You finally made it. Here is the main code that implements the language server protocol You finally made it. Here is the main code that implements the language server protocol
Every time the client sends a message, it gets handled by a function in the corresponding table type. Every time the client sends a message, it gets handled by a function in the corresponding table type.
(ie, a textDocument/didChange notification will call notifications.textDocument/didChange (ie, a textDocument/didChange notification will call notifications.textDocument/didChange
and a textDocument/defintion request will call requests.textDocument/didChange)" and a textDocument/defintion request will call requests.textDocument/didChange)"
(local utils (require :fennel-ls.utils)) (local {: pos->byte : apply-changes} (require :fennel-ls.utils))
(local message (require :fennel-ls.message)) (local message (require :fennel-ls.message))
(local state (require :fennel-ls.state)) (local state (require :fennel-ls.state))
(local analyze (require :fennel-ls.analyze)) (local language (require :fennel-ls.language))
(local requests []) (local requests [])
(local notifications []) (local notifications [])
@ -61,17 +60,17 @@ Every time the client sends a message, it gets handled by a function in the corr
(λ requests.textDocument/definition [self send {: position :textDocument {: uri}}] (λ requests.textDocument/definition [self send {: position :textDocument {: uri}}]
(local file (state.get-by-uri self uri)) (local file (state.get-by-uri self uri))
(local byte (utils.pos->byte file.text position.line position.character)) (local byte (pos->byte file.text position.line position.character))
(match (analyze.find-symbol file.ast byte) (match (language.find-symbol file.ast byte)
symbol symbol
(match (analyze.search-symbol self file symbol []) (match (language.search-symbol self file symbol [])
definition definition
{:range (message.range file.text definition) {:range (message.range file.text definition)
:uri uri}))) :uri uri})))
(λ notifications.textDocument/didChange [self send {: contentChanges :textDocument {: uri}}] (λ notifications.textDocument/didChange [self send {: contentChanges :textDocument {: uri}}]
(local file (state.get-by-uri self uri)) (local file (state.get-by-uri self uri))
(assert file.open?) (assert file.open?)
(utils.apply-changes (. self.files uri) contentChanges)) (apply-changes (. self.files uri) contentChanges))
(λ notifications.textDocument/didOpen [self send {:textDocument {: languageId : text : uri}}] (λ notifications.textDocument/didOpen [self send {:textDocument {: languageId : text : uri}}]
(local file (state.set-uri-contents self uri text)) (local file (state.set-uri-contents self uri text))

104
src/fennel-ls/language.fnl Normal file
View File

@ -0,0 +1,104 @@
(local fennel (require :fennel))
(local fennelutils (require :fennel.utils))
(local utils (require :fennel-ls.utils))
(local state (require :fennel-ls.state))
(local get-ast-info utils.get-ast-info)
(local sym? fennel.sym?)
(local list? fennel.list?)
(var
(search-assignment
search-symbol
search)
nil)
(set search-assignment
(λ search-assignment [self file binding ?definition stack]
(if (= 0 (length stack))
binding
;; TODO sift down the binding
(search self file ?definition stack))))
(set search-symbol
(λ search-symbol [self file symbol stack]
(let [split (utils.multi-sym-split symbol)]
(for [i (length split) 2 -1]
(table.insert stack (. split i))))
(match (. file.references symbol)
to (search-assignment self file to.binding to.definition stack)
nil nil)))
(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 with parameters bound)
(match item
[-require- mod]
(let [newfile (state.get-by-module self mod)
newitem (. newfile.ast (length newfile.ast))]
(print newfile.uri mod)
(search self newfile newitem stack))
_ (error (.. "I don't know what to do with " (fennel.view item)))))))
(λ past? [?ast byte]
;; check if a byte is past an ast object
(and (= (type ?ast) :table)
(get-ast-info ?ast :bytestart)
(< byte (get-ast-info ?ast :bytestart))
false))
(λ contains? [?ast byte]
;; check if an ast contains a byte
(and (= (type ?ast) :table)
(get-ast-info ?ast :bytestart)
(get-ast-info ?ast :byteend)
(<= (get-ast-info ?ast :bytestart)
byte
(+ 1 (utils.get-ast-info ?ast :byteend)))))
(λ does-not-contain? [?ast byte]
;; check if a byte is in range of the ast
(and (= (type ?ast) :table)
(get-ast-info ?ast :bytestart)
(get-ast-info ?ast :byteend)
(not
(<= (get-ast-info ?ast :bytestart)
byte
(+ 1 (get-ast-info ?ast :byteend))))))
(λ find-symbol [ast byte ?recursively-called]
(if (not= :table (type ast))
nil
(does-not-contain? ast byte)
nil
(and (sym? ast) (contains? ast byte))
ast
(or (not ?recursively-called)
(fennel.list? ast)
(fennel.sequence? ast))
;; TODO binary search
(accumulate
[result nil
_ v (ipairs ast)
&until (or result (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)))))
{: find-symbol
: search-symbol}

View File

@ -1,9 +1,11 @@
"Message "Message
Here are all the constructors for the various JSON-RPC responses Here are all the message constructor helpers for the various
that may need to be sent to the client. LSP and JSON-RPC responses that may need to be sent to the client.
I have them all here because I have a feeling I am conflating
missing fields with null fields, and I want to have one location
to look to fix this in the future."
I have them all here because I have a feeling I am conflating missing fields with null fields,
and I want to have one location to look to fix this in the future."
(local utils (require :fennel-ls.utils)) (local utils (require :fennel-ls.utils))
(local error-codes (local error-codes

View File

@ -1,7 +1,7 @@
(local utils (require :fennel-ls.utils)) (local utils (require :fennel-ls.utils))
(local searcher (require :fennel-ls.searcher)) (local searcher (require :fennel-ls.searcher))
(local {: analyze} (require :fennel-ls.analyze)) (local {: compile} (require :fennel-ls.compiler))
(λ init-state [self params] (λ init-state [self params]
(set self.files {}) (set self.files {})
@ -16,19 +16,26 @@
(λ get-by-uri [self uri] (λ get-by-uri [self uri]
(or (. self.files uri) (or (. self.files uri)
(let [file (read-file uri)] (let [file (read-file uri)]
(analyze file) (compile file)
(tset self.files uri file) (tset self.files uri file)
file))) file)))
(λ get-by-module [self module] (λ get-by-module [self module]
;; check the cache
(match (. self.modules module) (match (. self.modules module)
uri (or (get-by-uri self uri) uri
;; if the cached uri isn't found, clear the cache and try again (or (get-by-uri self uri)
(do (tset self.modules module nil) ;; if the cached uri isn't found, clear the cache and try again
(get-by-module self module))) (do (tset self.modules module nil)
nil (let [uri (searcher.lookup self module)] (get-by-module self module)))
(tset self.modules module uri) nil
(get-by-uri self uri)))) (match (searcher.lookup self module)
uri
(do
(tset self.modules module uri)
(get-by-uri self uri))
nil
(error (.. "cannot find module " module)))))
(λ set-uri-contents [self uri text] (λ set-uri-contents [self uri text]
(if (. self.files uri) (if (. self.files uri)
@ -36,12 +43,12 @@
(let [file (. self.files uri)] (let [file (. self.files uri)]
(when (not= text file.text) (when (not= text file.text)
(set file.text text) (set file.text text)
(analyze file) (compile file)
file)) file))
;; create new file ;; create new file
(let [file {: uri : text}] (let [file {: uri : text}]
(tset self.files uri file) (tset self.files uri file)
(analyze file) (compile file)
file))) file)))

View File

@ -5,14 +5,14 @@
(local {: ROOT-URI (local {: ROOT-URI
: setup-server} (require :test.util)) : setup-server} (require :test.util))
(local core (require :fennel-ls.core)) (local dispatch (require :fennel-ls.dispatch))
(local message (require :fennel-ls.message)) (local message (require :fennel-ls.message))
(describe "jump to definition" (describe "jump to definition"
(fn check [request-file line char response-file start-line start-col end-line end-col] (fn check [request-file line char response-file start-line start-col end-line end-col]
(local state (doto [] setup-server)) (local state (doto [] setup-server))
(let [message (core.handle* state (let [message (dispatch.handle* state
(message.create-request 2 "textDocument/definition" (message.create-request 2 "textDocument/definition"
{:position {:character char :line line} {:position {:character char :line line}
:textDocument {:uri (.. ROOT-URI "/" request-file)}})) :textDocument {:uri (.. ROOT-URI "/" request-file)}}))
@ -47,13 +47,13 @@
(check "example.fnl" 21 9 "example.fnl" 16 13 16 16)) (check "example.fnl" 21 9 "example.fnl" 16 13 16 16))
(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 "handles (local _ (require XXX)" (it "handles (local _ (require XXX))"
;; (check "example.fnl" 0 11 "foo.fnl" 0 0 0 0)) (check "example.fnl" 0 10 "foo.fnl" 0 0 0 0))
;; (it "handles (require XXX))" (it "handles (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 field inside of a table")
;; (it "can go to a destructured function argument") ;; (it "can go to a destructured function argument")

View File

@ -2,7 +2,7 @@
(local is (require :luassert)) (local is (require :luassert))
(local {: ROOT-PATH : ROOT-URI} (require :test.util)) (local {: ROOT-PATH : ROOT-URI} (require :test.util))
(local core (require :fennel-ls.core)) (local dispatch (require :fennel-ls.dispatch))
(local server-initialize-message (local server-initialize-message
{:id 1 {:id 1
@ -22,7 +22,7 @@
(describe "language server" (describe "language server"
(it "responds to initialize" (it "responds to initialize"
(is-matching (is-matching
(core.handle* [] server-initialize-message) (dispatch.handle* [] server-initialize-message)
[{:jsonrpc "2.0" :id 1 [{:jsonrpc "2.0" :id 1
:result {:capabilities {} :result {:capabilities {}
:serverInfo {:name "fennel-ls" : version}}}]))) :serverInfo {:name "fennel-ls" : version}}}])))

View File

@ -1,4 +1,4 @@
(local core (require :fennel-ls.core)) (local dispatch (require :fennel-ls.dispatch))
(local ROOT-PATH (local ROOT-PATH
(-> (io.popen "pwd") (-> (io.popen "pwd")
@ -24,6 +24,6 @@
:uri ROOT-URI}]}}) :uri ROOT-URI}]}})
(fn setup-server [state] (fn setup-server [state]
(core.handle* state initialization-message)) (dispatch.handle* state initialization-message))
{: ROOT-URI : setup-server} {: ROOT-URI : setup-server}