clean up some tests, restore (fn M.method []) completion handling
This commit is contained in:
parent
2800038005
commit
3f1c2ea361
@ -20,6 +20,7 @@
|
||||
* Ignore unused locals if they end in underscore.
|
||||
* Settings file: `flsproject.fnl`. Settings are now editor agnostic.
|
||||
* Support `:intersection` as a Lua version; only includes globals present in every Lua.
|
||||
* Support better completions. The eglot client is no longer a special case.
|
||||
|
||||
### Changes
|
||||
|
||||
|
||||
@ -83,14 +83,26 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find
|
||||
:keys ?keys
|
||||
:multival ?multival
|
||||
:fields ?fields}} assignment]
|
||||
(when (and (= 0 (length stack))
|
||||
opts.save-last-binding)
|
||||
(tset opts.save-last-binding 1 assignment.target))
|
||||
(if (and (= 0 (length stack)) opts.stop-early?)
|
||||
assignment.target ;; BASE CASE!!
|
||||
;; search a virtual field from :fields
|
||||
|
||||
;; :fields
|
||||
(and (not= 0 (length stack)) (?. ?fields (. stack (length stack))))
|
||||
(search-assignment server file {:target (. ?fields (table.remove stack))} stack opts)
|
||||
|
||||
;; This case is hard to explain.
|
||||
;; Under these conditions, search-multival is planning on returning {:definition ?definition : file}
|
||||
;; but assignment.target is more rich with information.
|
||||
;; Specifically, it has the :fields key, which is useful in finding non-local fields.
|
||||
;; For example, The `my-method` field of `M` in `(local M {})\n(fn M.my-method [])`
|
||||
;; is shared using this :fields mechanism.
|
||||
(and (not (list? ?definition))
|
||||
(not (varg? ?definition))
|
||||
(= 1 (or 1 ?multival))
|
||||
(not (sym? ?definition))
|
||||
(= 0 (length stack)))
|
||||
assignment.target
|
||||
|
||||
(search-multival server file ?definition (stack-add-keys! stack ?keys) (or ?multival 1) opts))))
|
||||
|
||||
(λ search-reference [server file ref stack opts]
|
||||
@ -212,10 +224,10 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find
|
||||
opts (or ?opts {})]
|
||||
(case (docs.get-builtin server (. split 1))
|
||||
metadata (search-document server metadata stack opts)
|
||||
_ (case (docs.get-global server (. split 1))
|
||||
metadata (search-document server metadata stack opts)
|
||||
_ (case (find-local-definition file name scope)
|
||||
def (search-val server file def.definition (stack-add-keys! stack def.keys) opts))))))
|
||||
_ (case (find-local-definition file name scope)
|
||||
def (search-val server file def.definition (stack-add-keys! stack def.keys) opts)
|
||||
_ (case (docs.get-global server (. split 1))
|
||||
metadata (search-document server metadata stack opts))))))
|
||||
|
||||
(λ past? [?ast byte]
|
||||
;; check if a byte is past an ast object
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
file.scope)
|
||||
range (if ?symbol (message.ast->range server file ?symbol) {:start position :end position})
|
||||
results []
|
||||
seen []]
|
||||
seen {}]
|
||||
|
||||
(fn add-completion [definition name]
|
||||
(table.insert results
|
||||
@ -53,45 +53,54 @@
|
||||
(and (fennel.list? def.definition)
|
||||
;; TODO check that arg is called `self`
|
||||
(or (and (fennel.sym? (. def.definition 1) "fn")
|
||||
(fennel.sym? (. def.definition 2) "self"))
|
||||
(fennel.sym? (?. def.definition 2 1) "self"))
|
||||
(and (fennel.sym? (. def.definition 1) "λ")
|
||||
(fennel.sym? (. def.definition 2) "self")))))
|
||||
(fennel.sym? (?. def.definition 2 1) "self")))))
|
||||
(add-completion-recursively def (.. name ":" field))
|
||||
(add-completion-recursively def (.. name "." field)))
|
||||
_ (do
|
||||
(io.stderr:write "BAD!!!! undocumented field: " (tostring field) "\n")
|
||||
{:label field})))))
|
||||
(when definition.fields
|
||||
(each [field value (pairs definition.fields)]
|
||||
(when (= (type field) :string)
|
||||
(if (or (= :self (tostring (?. value :metadata :fnl/arglist 1)))
|
||||
(and (fennel.list? value.definition)
|
||||
;; TODO check that arg is called `self`
|
||||
(or (and (fennel.sym? (. value.definition 1) "fn")
|
||||
(fennel.sym? (. value.definition 2) "self"))
|
||||
(and (fennel.sym? (. value.definition 1) "λ")
|
||||
(fennel.sym? (. value.definition 2) "self")))))
|
||||
(add-completion-recursively value (.. name ":" field))
|
||||
(add-completion-recursively value (.. name "." field))))))
|
||||
(each [field value (pairs definition.fields)]
|
||||
(when (= (type field) :string)
|
||||
(if (or (= :self (tostring (?. value :metadata :fnl/arglist 1)))
|
||||
(and (fennel.list? value.definition)
|
||||
;; TODO check that arg is called `self`
|
||||
(or (and (fennel.sym? (. value.definition 1) "fn")
|
||||
(fennel.sym? (?. value.definition 2 1) "self"))
|
||||
(and (fennel.sym? (. value.definition 1) "λ")
|
||||
(fennel.sym? (?. value.definition 2 1) "self")))))
|
||||
(add-completion-recursively value (.. name ":" field))
|
||||
(add-completion-recursively value (.. name "." field))))))
|
||||
|
||||
(set (. seen definition) false)))
|
||||
;; end yield
|
||||
|
||||
(local seen-manglings {})
|
||||
|
||||
(each [_ global* (ipairs file.allowed-globals)]
|
||||
(case (analyzer.search-name-and-scope server file global* scope)
|
||||
def (if (and (= :_G (tostring global*))
|
||||
(not (: (tostring ?symbol) :match "_G[:.]")))
|
||||
(add-completion def global*)
|
||||
(add-completion-recursively def global*))
|
||||
_ (do
|
||||
(io.stderr:write "BAD!!!! undocumented global: " (tostring global*) "\n")
|
||||
{:label global*})))
|
||||
(when (not (. seen-manglings global*))
|
||||
(set (. seen-manglings global*) true)
|
||||
(case (analyzer.search-name-and-scope server file global* scope)
|
||||
def (if (and (= :_G (tostring global*))
|
||||
(not (: (tostring ?symbol) :match "_G[:.]")))
|
||||
(add-completion def global*)
|
||||
(add-completion-recursively def global*))
|
||||
_ (do
|
||||
(io.stderr:write "BAD!!!! undocumented global: " (tostring global*) "\n")
|
||||
{:label global*}))))
|
||||
|
||||
|
||||
(var scope scope)
|
||||
(while scope
|
||||
(each [mangling (pairs scope.manglings)]
|
||||
(case (analyzer.search-name-and-scope server file mangling scope)
|
||||
def (add-completion-recursively def mangling)
|
||||
_ (add-completion-recursively {} mangling)))
|
||||
(when (not (. seen-manglings mangling))
|
||||
(set (. seen-manglings mangling) true)
|
||||
(case (analyzer.search-name-and-scope server file mangling scope)
|
||||
def (add-completion-recursively def mangling)
|
||||
_ (add-completion-recursively {} mangling))))
|
||||
|
||||
(when in-call-position?
|
||||
(each [macro* (pairs scope.macros)]
|
||||
(table.insert results {:label macro* :filterText macro* :textEdit {:newText macro* : range}}))
|
||||
|
||||
@ -154,29 +154,29 @@
|
||||
nil)
|
||||
|
||||
(fn test-field []
|
||||
(check "(local x {:field (fn [])})\n(x:" [:x:field] [])
|
||||
(check "(local x {:field (fn [self])})\n(x:" [:x:field] [])
|
||||
(check "(local x {:field (fn [self])})\n(x:fi|" [:x:field] [])
|
||||
;; regression test for not crashing
|
||||
(check "(local x {:field (fn [])})\n(x::f" [] [])
|
||||
(check "(local x {:field (fn [self])})\n(x::f" [] [])
|
||||
(check
|
||||
"(let [my-table {:foo 10 :bar 20}]\n my-table.|)))"
|
||||
[:foo :bar]
|
||||
[:local :doto :+]) ;; no specials or macros because it's not in a head position
|
||||
[:my-table.foo :my-table.bar]
|
||||
[])
|
||||
(check
|
||||
{:main.fnl "(let [foo (require :fooo)]
|
||||
foo.|)))"
|
||||
:fooo.fnl "(fn my-export [x] (print x))
|
||||
{: my-export :constant 10}"}
|
||||
[:my-export :constant]
|
||||
[:_G :local :doto :+])
|
||||
[:foo.my-export :foo.constant]
|
||||
[])
|
||||
(check
|
||||
{:main.fnl "(let [foo (require :fooo)]
|
||||
foo.|)))"
|
||||
:fooo.fnl "(local M {:constant 10})
|
||||
(fn M.my-export [x] (print x))
|
||||
M"}
|
||||
[:my-export :constant]
|
||||
[:_G :local :doto :+]) ;; no globals, specials, macros, or others
|
||||
(check "(local x {:field (fn [])})\n(x:fi|" [:field] [:table])
|
||||
[:foo.my-export :foo.constant]
|
||||
[])
|
||||
nil)
|
||||
|
||||
(fn test-docs []
|
||||
@ -231,12 +231,11 @@
|
||||
(check "(coroutine.y|"
|
||||
[{:label "yield"
|
||||
:documentation #(and $.value ($.value:find "```fnl\n(coroutine.yield ...)\n```" 1 true))}]
|
||||
["coroutine" "_G" "do"
|
||||
{:documentation #(= nil $)}])
|
||||
[{:documentation #(= nil $)}])
|
||||
(check "(local c coroutine)
|
||||
(c.y"
|
||||
[{:label "yield"}]
|
||||
["coroutine" "_G" "do"])
|
||||
["coroutine.yield" "c.yield"]
|
||||
[{:documentation #(= nil $)}])
|
||||
(check "(local t table)
|
||||
(t.i"
|
||||
["insert"]
|
||||
@ -248,24 +247,6 @@
|
||||
nil)
|
||||
|
||||
|
||||
(fn test-eglot-fields []
|
||||
"tests for handling Eglot specially"
|
||||
(check "(coroutine.y|"
|
||||
[{:label "coroutine.yield"
|
||||
:filterText "coroutine.yield"
|
||||
:documentation #(and $.value ($.value:find "```fnl\n(coroutine.yield ...)\n```" 1 true))}]
|
||||
[])
|
||||
(check "(local c coroutine)
|
||||
(c.y"
|
||||
[{:label "c.yield"
|
||||
:filterText "c.yield"
|
||||
:textEdit #(= nil $)}]
|
||||
[])
|
||||
(check "(local x {:field (fn [])})
|
||||
(x:"
|
||||
[:x:field]
|
||||
[]))
|
||||
|
||||
;; ;; Future tests / features
|
||||
;; ;; Scope Ordering Rules
|
||||
;; (it "does not suggest locals past the suggestion location when a symbol is partially typed")
|
||||
@ -292,5 +273,4 @@
|
||||
: test-fn-arg
|
||||
: test-field
|
||||
: test-docs
|
||||
: test-module
|
||||
: test-eglot-fields}
|
||||
: test-module}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user