fix completion scope bug and fix completion macro call special case and add completions for nil and false and true and improve completion test speeds
I did a lot in one big commit. fix bug where (each [foo (ipairs [])] |) wouldn't suggest foo fix bug with (doto foo |) wouldn't suggest specials and macros, even though it expands to | in a call position completions for true, false, nil, .nan, .inf docs for true, false, nil clarify error message when passing a folder to --lint improved test performance for completion tests
This commit is contained in:
parent
1733cbab94
commit
2a23d2f950
@ -183,7 +183,7 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find
|
|||||||
(if (sym? ast) (search-symbol server file ast stack opts)
|
(if (sym? ast) (search-symbol server file ast stack opts)
|
||||||
(= 0 (length stack)) {:definition ast : file} ;; BASE CASE !!
|
(= 0 (length stack)) {:definition ast : file} ;; BASE CASE !!
|
||||||
(= :table (type ast)) (search-table server file ast stack opts)
|
(= :table (type ast)) (search-table server file ast stack opts)
|
||||||
(= :string (type ast)) (search-document server (docs.get-global server :string) stack opts))
|
(= :string (type ast)) (search-document server (docs.get-global server nil :string) stack opts))
|
||||||
nil))))
|
nil))))
|
||||||
|
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ initialization-opts: {:stack ?list[ast]
|
|||||||
metadata (search-document server metadata stack opts)
|
metadata (search-document server metadata stack opts)
|
||||||
_ (case (find-local-definition file base-name scope)
|
_ (case (find-local-definition file base-name scope)
|
||||||
def (search-definition server file def stack opts)
|
def (search-definition server file def stack opts)
|
||||||
_ (case (docs.get-global server base-name)
|
_ (case (docs.get-global server scope base-name)
|
||||||
metadata (search-document server metadata stack opts))))))
|
metadata (search-document server metadata stack opts))))))
|
||||||
|
|
||||||
(λ past? [?ast byte]
|
(λ past? [?ast byte]
|
||||||
|
|||||||
@ -6,13 +6,20 @@ compiler's plugin hook callbacks. It stores lexical info about which
|
|||||||
identifiers are declared / referenced in which places."
|
identifiers are declared / referenced in which places."
|
||||||
|
|
||||||
(local {: sym? : list? : sequence? : table? : varg? : sym : view &as fennel} (require :fennel))
|
(local {: sym? : list? : sequence? : table? : varg? : sym : view &as fennel} (require :fennel))
|
||||||
(local {:scopes {:global {: specials}}} (require :fennel.compiler))
|
(local {:scopes {:global {: specials} :compiler compiler-scope}} (require :fennel.compiler))
|
||||||
|
(local {: make-compiler-env} (require :fennel.specials))
|
||||||
|
|
||||||
(local docs (require :fennel-ls.docs))
|
(local docs (require :fennel-ls.docs))
|
||||||
(local message (require :fennel-ls.message))
|
(local message (require :fennel-ls.message))
|
||||||
(local searcher (require :fennel-ls.searcher))
|
(local searcher (require :fennel-ls.searcher))
|
||||||
(local utils (require :fennel-ls.utils))
|
(local utils (require :fennel-ls.utils))
|
||||||
|
|
||||||
(local print print)
|
(local print print)
|
||||||
|
|
||||||
|
(local macro-globals
|
||||||
|
(icollect [k (pairs (make-compiler-env))]
|
||||||
|
k))
|
||||||
|
|
||||||
(local nil* (sym :nil))
|
(local nil* (sym :nil))
|
||||||
|
|
||||||
(fn special? [item]
|
(fn special? [item]
|
||||||
@ -39,10 +46,6 @@ identifiers are declared / referenced in which places."
|
|||||||
(= (type candidate.specials) :table)
|
(= (type candidate.specials) :table)
|
||||||
(= (type candidate.gensyms) :table)))
|
(= (type candidate.gensyms) :table)))
|
||||||
|
|
||||||
(λ ast->macro-ast [ast]
|
|
||||||
[(fennel.list (sym :eval-compiler)
|
|
||||||
((or (. table :unpack) _G.unpack) ast))])
|
|
||||||
|
|
||||||
(λ 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
|
||||||
(and (sym? t)
|
(and (sym? t)
|
||||||
@ -97,7 +100,7 @@ identifiers are declared / referenced in which places."
|
|||||||
;; find reference
|
;; find reference
|
||||||
(let [name (string.match (tostring symbol) "[^%.:]+")]
|
(let [name (string.match (tostring symbol) "[^%.:]+")]
|
||||||
(case (or (find-definition (tostring name) scope)
|
(case (or (find-definition (tostring name) scope)
|
||||||
(docs.get-global server name))
|
(docs.get-global server scope name))
|
||||||
target (let [ref {: symbol : target : ref-type}]
|
target (let [ref {: symbol : target : ref-type}]
|
||||||
(tset references symbol ref)
|
(tset references symbol ref)
|
||||||
(when target.referenced-by
|
(when target.referenced-by
|
||||||
@ -214,12 +217,16 @@ identifiers are declared / referenced in which places."
|
|||||||
;; handle the definitions of a function
|
;; handle the definitions of a function
|
||||||
(define-function-name ast scope))
|
(define-function-name ast scope))
|
||||||
|
|
||||||
(λ compile-for [_ast scope binding]
|
(λ compile-for [ast scope binding]
|
||||||
|
(tset scopes ast scope)
|
||||||
(define nil* binding scope))
|
(define nil* binding scope))
|
||||||
|
|
||||||
(λ compile-each [_ast scope bindings]
|
(λ compile-each [ast scope bindings]
|
||||||
|
(tset scopes ast scope)
|
||||||
(each [_ binding (ipairs bindings)]
|
(each [_ binding (ipairs bindings)]
|
||||||
(define nil* binding scope)))
|
(if (and (sym? binding)
|
||||||
|
(not (. scope.gensyms (tostring binding))))
|
||||||
|
(define nil* binding scope))))
|
||||||
|
|
||||||
(λ compile-fn [ast scope]
|
(λ compile-fn [ast scope]
|
||||||
(tset scopes ast scope)
|
(tset scopes ast scope)
|
||||||
@ -231,7 +238,8 @@ identifiers are declared / referenced in which places."
|
|||||||
(λ call [ast scope]
|
(λ call [ast scope]
|
||||||
"every list that is a call to a special or function"
|
"every list that is a call to a special or function"
|
||||||
(tset calls ast true)
|
(tset calls ast true)
|
||||||
(tset scopes ast scope)
|
(when (not (. scopes ast))
|
||||||
|
(tset scopes ast scope))
|
||||||
;; Most calls aren't interesting, but here's the list of the ones that are:
|
;; Most calls aren't interesting, but here's the list of the ones that are:
|
||||||
(let [head (. ast 1)]
|
(let [head (. ast 1)]
|
||||||
(case (and (sym? head) (tostring head))
|
(case (and (sym? head) (tostring head))
|
||||||
@ -341,13 +349,13 @@ identifiers are declared / referenced in which places."
|
|||||||
:severity message.severity.WARN
|
:severity message.severity.WARN
|
||||||
:code :compiler-warning})))
|
:code :compiler-warning})))
|
||||||
|
|
||||||
(local allowed-globals (docs.get-all-globals server))
|
|
||||||
(icollect [extra-global (server.configuration.extra-globals:gmatch "[^ ]+")
|
|
||||||
&into allowed-globals]
|
|
||||||
extra-global)
|
|
||||||
|
|
||||||
(let [macro-file? (or (file.uri:match "%.fnlm$")
|
(let [macro-file? (or (file.uri:match "%.fnlm$")
|
||||||
(= (file.text:sub 1 24) ";; fennel-ls: macro-file"))
|
(= (file.text:sub 1 24) ";; fennel-ls: macro-file"))
|
||||||
|
allowed-globals (if macro-file? macro-globals
|
||||||
|
(icollect [extra-global (server.configuration.extra-globals:gmatch "[^ ]+")
|
||||||
|
&into (docs.get-all-globals server)]
|
||||||
|
extra-global))
|
||||||
plugin
|
plugin
|
||||||
{:name "fennel-ls"
|
{:name "fennel-ls"
|
||||||
:versions ["1.4.1" "1.4.2" "1.5.0" "1.5.1" "1.5.3" "1.5.4"]
|
:versions ["1.4.1" "1.4.2" "1.5.0" "1.5.1" "1.5.3" "1.5.4"]
|
||||||
@ -364,7 +372,9 @@ identifiers are declared / referenced in which places."
|
|||||||
:pre-each compile-each
|
:pre-each compile-each
|
||||||
:pre-fn compile-fn
|
:pre-fn compile-fn
|
||||||
:pre-do compile-do}
|
:pre-do compile-do}
|
||||||
scope (fennel.scope)
|
scope (if macro-file?
|
||||||
|
(fennel.scope compiler-scope)
|
||||||
|
(fennel.scope))
|
||||||
opts {:filename file.uri
|
opts {:filename file.uri
|
||||||
:plugins [plugin]
|
:plugins [plugin]
|
||||||
:allowedGlobals allowed-globals
|
:allowedGlobals allowed-globals
|
||||||
@ -426,7 +436,7 @@ identifiers are declared / referenced in which places."
|
|||||||
(set _G.print #(values))
|
(set _G.print #(values))
|
||||||
(table.insert defer #(set _G.print print))
|
(table.insert defer #(set _G.print print))
|
||||||
|
|
||||||
(each [_i form (ipairs (if macro-file? (ast->macro-ast ast) ast))]
|
(each [_i form (ipairs ast)]
|
||||||
(filter-errors :compiler
|
(filter-errors :compiler
|
||||||
;; this entire block of code is for making the instruction limit work
|
;; this entire block of code is for making the instruction limit work
|
||||||
(xpcall #(let [limiting? (not= server.configuration.compiler-instruction-limit -1)]
|
(xpcall #(let [limiting? (not= server.configuration.compiler-instruction-limit -1)]
|
||||||
|
|||||||
@ -28,15 +28,17 @@ the client to forward information to the resolve request by setting the `data`
|
|||||||
to {: uri : byte}. When this capability exists, `server.can-do-good-completions?`
|
to {: uri : byte}. When this capability exists, `server.can-do-good-completions?`
|
||||||
is set to true and we report that we support completionItem/resolve."
|
is set to true and we report that we support completionItem/resolve."
|
||||||
|
|
||||||
|
(local fennel (require :fennel))
|
||||||
|
(local {:metadata METADATA} (require :fennel.compiler))
|
||||||
|
|
||||||
(local files (require :fennel-ls.files))
|
(local files (require :fennel-ls.files))
|
||||||
(local utils (require :fennel-ls.utils))
|
(local utils (require :fennel-ls.utils))
|
||||||
(local analyzer (require :fennel-ls.analyzer))
|
(local analyzer (require :fennel-ls.analyzer))
|
||||||
(local fennel (require :fennel))
|
|
||||||
(local message (require :fennel-ls.message))
|
(local message (require :fennel-ls.message))
|
||||||
(local format (require :fennel-ls.formatter))
|
(local format (require :fennel-ls.formatter))
|
||||||
(local navigate (require :fennel-ls.navigate))
|
(local navigate (require :fennel-ls.navigate))
|
||||||
(local compiler (require :fennel-ls.compiler))
|
(local compiler (require :fennel-ls.compiler))
|
||||||
(local {:metadata METADATA} (require :fennel.compiler))
|
(local docs (require :fennel-ls.docs))
|
||||||
|
|
||||||
(λ textDocument/completion [server _send {: position :textDocument {: uri}}]
|
(λ textDocument/completion [server _send {: position :textDocument {: uri}}]
|
||||||
;; get the file
|
;; get the file
|
||||||
@ -49,9 +51,12 @@ is set to true and we report that we support completionItem/resolve."
|
|||||||
;; find what ast objects are under the cursor
|
;; find what ast objects are under the cursor
|
||||||
(symbol parents) (analyzer.find-symbol file.ast byte)
|
(symbol parents) (analyzer.find-symbol file.ast byte)
|
||||||
;; check what context I'm in
|
;; check what context I'm in
|
||||||
in-call-position? (and (fennel.list? (. parents 1))
|
in-call-position? (or (and (fennel.list? (. parents 1))
|
||||||
(= symbol (. parents 1 1)))
|
(= symbol (. parents 1 1)))
|
||||||
;; find the first one that contains a scope
|
(accumulate [?find nil ast (pairs file.calls)
|
||||||
|
&until ?find]
|
||||||
|
(= (. ast 1) symbol)))
|
||||||
|
;; find the first parent that contains a scope
|
||||||
scope (or (accumulate [?find nil _ parent (ipairs parents) &until ?find]
|
scope (or (accumulate [?find nil _ parent (ipairs parents) &until ?find]
|
||||||
(. file.scopes parent))
|
(. file.scopes parent))
|
||||||
file.scope)
|
file.scope)
|
||||||
@ -100,6 +105,9 @@ is set to true and we report that we support completionItem/resolve."
|
|||||||
(io.stderr:write "BAD!!!! undocumented global: " (tostring global*) "\n")
|
(io.stderr:write "BAD!!!! undocumented global: " (tostring global*) "\n")
|
||||||
(add-completion! global* {})))))
|
(add-completion! global* {})))))
|
||||||
|
|
||||||
|
(each [k v (pairs docs.literals)]
|
||||||
|
(add-completion! k v))
|
||||||
|
|
||||||
(var scope scope)
|
(var scope scope)
|
||||||
(while scope
|
(while scope
|
||||||
(each [mangling (pairs scope.manglings)]
|
(each [mangling (pairs scope.manglings)]
|
||||||
|
|||||||
@ -9,7 +9,8 @@ Handles grabbing the documentation from sources other than fennel code;
|
|||||||
(local fennel (require :fennel))
|
(local fennel (require :fennel))
|
||||||
(local {:metadata METADATA
|
(local {:metadata METADATA
|
||||||
:scopes {:global {:specials SPECIALS
|
:scopes {:global {:specials SPECIALS
|
||||||
:macros MACROS}}}
|
:macros MACROS}
|
||||||
|
:compiler compiler-scope}}
|
||||||
(require :fennel.compiler))
|
(require :fennel.compiler))
|
||||||
|
|
||||||
(local docset-ext ".lua")
|
(local docset-ext ".lua")
|
||||||
@ -93,26 +94,36 @@ Handles grabbing the documentation from sources other than fennel code;
|
|||||||
&until g]
|
&until g]
|
||||||
(and enabled? (. (get-library library-name) global-name))))
|
(and enabled? (. (get-library library-name) global-name))))
|
||||||
|
|
||||||
(fn get-global [server global-name]
|
(fn get-global [server ?scope global-name]
|
||||||
(or (get-library-global server global-name)
|
(or (get-library-global server global-name)
|
||||||
(. (get-lua-version server.configuration.lua-version) global-name)))
|
(. (get-lua-version server.configuration.lua-version) global-name)
|
||||||
|
(if (do (var x ?scope)
|
||||||
|
(while (and x (not= x compiler-scope))
|
||||||
|
(set x x.parent))
|
||||||
|
(= x compiler-scope))
|
||||||
|
(. (require :fennel-ls.docs.generated.compiler-env) global-name))))
|
||||||
|
|
||||||
(local hardcoded-special-items
|
(local literals
|
||||||
{:nil {:metadata {:fnl/docstring "Represents the absence of a useful value."
|
{:nil {:definition (fennel.sym :nil)
|
||||||
|
:metadata {:fnl/docstring "Represents the absence of a useful value."
|
||||||
:fls/itemKind "Keyword"}}
|
:fls/itemKind "Keyword"}}
|
||||||
:true {:metadata {:fnl/docstring "A boolean value representing truth."
|
:true {:definition true
|
||||||
|
:metadata {:fnl/docstring "A boolean value representing truth."
|
||||||
:fls/itemKind "Keyword"}}
|
:fls/itemKind "Keyword"}}
|
||||||
:false {:metadata {:fnl/docstring "A boolean value representing falsehood."
|
:false {:definition false
|
||||||
|
:metadata {:fnl/docstring "A boolean value representing falsehood."
|
||||||
:fls/itemKind "Keyword"}}
|
:fls/itemKind "Keyword"}}
|
||||||
:.nan {:metadata {:fnl/docstring "NaN"
|
:.nan {:definition .nan
|
||||||
|
:metadata {:fnl/docstring "NaN"
|
||||||
:fls/itemKind "Constant"}}
|
:fls/itemKind "Constant"}}
|
||||||
:.inf {:metadata {:fnl/docstring "inf"
|
:.inf {:definition .inf
|
||||||
|
:metadata {:fnl/docstring "inf"
|
||||||
:fls/itemKind "Constant"}}})
|
:fls/itemKind "Constant"}}})
|
||||||
|
|
||||||
(fn get-builtin [_server builtin-name]
|
(fn get-builtin [_server builtin-name]
|
||||||
(or (. specials builtin-name)
|
(or (. specials builtin-name)
|
||||||
(. macros* builtin-name)
|
(. macros* builtin-name)
|
||||||
(. hardcoded-special-items builtin-name)))
|
(. literals builtin-name)))
|
||||||
|
|
||||||
(λ validate-lua-version [lua-version invalid]
|
(λ validate-lua-version [lua-version invalid]
|
||||||
(case (. lua-versions lua-version)
|
(case (. lua-versions lua-version)
|
||||||
@ -133,4 +144,5 @@ Handles grabbing the documentation from sources other than fennel code;
|
|||||||
: get-builtin
|
: get-builtin
|
||||||
: get-all-globals
|
: get-all-globals
|
||||||
: validate-lua-version
|
: validate-lua-version
|
||||||
: validate-libraries}
|
: validate-libraries
|
||||||
|
: literals}
|
||||||
|
|||||||
@ -14,6 +14,8 @@ This module has high level helpers for creating/getting \"file\" objects."
|
|||||||
{: uri : text})
|
{: uri : text})
|
||||||
_ (case (io.open (utils.uri->path uri) "r")
|
_ (case (io.open (utils.uri->path uri) "r")
|
||||||
file (let [text (file:read :*a)]
|
file (let [text (file:read :*a)]
|
||||||
|
(when (not text)
|
||||||
|
(error (.. "could not read file:" (utils.uri->path uri))))
|
||||||
(file:close)
|
(file:close)
|
||||||
{: uri : text})))))
|
{: uri : text})))))
|
||||||
|
|
||||||
|
|||||||
@ -693,7 +693,7 @@ You can read more about how to add lints in docs/linting.md"
|
|||||||
(let [s (tostring arg)]
|
(let [s (tostring arg)]
|
||||||
(or (= s "...") (= s "&"))))
|
(or (= s "...") (= s "&"))))
|
||||||
;; exception: (table.insert table item) can take a third argument
|
;; exception: (table.insert table item) can take a third argument
|
||||||
max-params (if (= result (. (docs.get-global server :table) :fields :insert))
|
max-params (if (= result (. (docs.get-global server nil :table) :fields :insert))
|
||||||
3
|
3
|
||||||
(length signature))
|
(length signature))
|
||||||
method-call? (and (sym? (. ast 1))
|
method-call? (and (sym? (. ast 1))
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
"TODO name this thing"
|
"TODO name this thing"
|
||||||
(coroutine.wrap
|
(coroutine.wrap
|
||||||
#(if (= (type definition.definition) :string)
|
#(if (= (type definition.definition) :string)
|
||||||
(each [key value (pairs (-> (docs.get-global server :string) (. :fields)))]
|
(each [key value (pairs (-> (docs.get-global server nil :string) (. :fields)))]
|
||||||
(when (or (= (type key) :string) (= (type key) :number))
|
(when (or (= (type key) :string) (= (type key) :number))
|
||||||
(coroutine.yield key value true)))
|
(coroutine.yield key value true)))
|
||||||
(do
|
(do
|
||||||
@ -22,10 +22,15 @@
|
|||||||
(when (or (= (type key) :string) (= (type key) :number))
|
(when (or (= (type key) :string) (= (type key) :number))
|
||||||
(coroutine.yield key value))))))))
|
(coroutine.yield key value))))))))
|
||||||
|
|
||||||
|
(λ has-fields [server_ definition]
|
||||||
|
(or (= (type definition.definition) :string)
|
||||||
|
(fennel.table? definition.definition)
|
||||||
|
definition.fields))
|
||||||
|
|
||||||
(λ _get-field [server definition key]
|
(λ _get-field [server definition key]
|
||||||
(let [fields (or definition.fields
|
(let [fields (or definition.fields
|
||||||
(when (= (type definition.definition) :string)
|
(when (= (type definition.definition) :string)
|
||||||
(. docs.get-global server :string)))]
|
(. (docs.get-global server nil :string) :fields)))]
|
||||||
(or (?. fields key)
|
(or (?. fields key)
|
||||||
(when (fennel.table? definition.definition)
|
(when (fennel.table? definition.definition)
|
||||||
(analyzer.search server definition.file definition.definition {} {:stack [key]})))))
|
(analyzer.search server definition.file definition.definition {} {:stack [key]})))))
|
||||||
@ -53,4 +58,5 @@
|
|||||||
|
|
||||||
{: _get-field
|
{: _get-field
|
||||||
: iter-fields
|
: iter-fields
|
||||||
|
: has-fields
|
||||||
: getmetadata}
|
: getmetadata}
|
||||||
|
|||||||
@ -160,9 +160,7 @@ WARNING: this is only used in the test code, not in the real language server"
|
|||||||
|
|
||||||
(fn multi-sym-split [symbol ?offset]
|
(fn multi-sym-split [symbol ?offset]
|
||||||
(local symbol (tostring symbol))
|
(local symbol (tostring symbol))
|
||||||
(if (or (= symbol ".")
|
(if (or (symbol:find "^%.")
|
||||||
(= symbol "..")
|
|
||||||
(= symbol "...")
|
|
||||||
(= symbol ":")
|
(= symbol ":")
|
||||||
(= symbol "?."))
|
(= symbol "?."))
|
||||||
[symbol]
|
[symbol]
|
||||||
|
|||||||
@ -85,45 +85,30 @@
|
|||||||
"")))))
|
"")))))
|
||||||
(expected completions))))
|
(expected completions))))
|
||||||
|
|
||||||
(fn test-global []
|
(fn test-basic []
|
||||||
(check "(" [{:label :setmetatable :kind kinds.Function}] [])
|
;; basic scoping rules
|
||||||
(check "(" [:_G :debug :table :io :getmetatable :setmetatable :_VERSION
|
(check "(local yes1 10)
|
||||||
:ipairs :pairs :next] [:this-is-not-a-global])
|
(fn yes2 [no2])
|
||||||
(check "#nil\n(" [:_G :debug :table :io :getmetatable :setmetatable
|
(do (local no1 11))
|
||||||
:_VERSION :ipairs :pairs :next] [])
|
(let [yes3 (fn [no3] no3)]
|
||||||
(check "(if ge" [:getmetatable] [])
|
(let [{:item y} {:item {:es4 12}}]
|
||||||
(check "(table.i" [:table.insert] [])
|
(fn [yes5 {: yes6}]
|
||||||
(check "(tablei" [:table.insert] [])
|
(each [no4 {: no5} (pairs _G)]
|
||||||
nil)
|
nil)
|
||||||
|
(each [yes7 {: yes8} (pairs _G)]
|
||||||
(fn test-local []
|
|"
|
||||||
(check "(local x 10)\n(print |)" [:x] [:+])
|
[:yes1 :yes2 :yes3 :y.es4 :yes5 :yes6 :yes7 :yes8
|
||||||
(check "(local x (doto 10 or and +))\n(print |)" [:x] [])
|
:_G :debug :table :table.insert :io :getmetatable :_VERSION :ipairs :pairs :next {:label :setmetatable :kind kinds.Function}
|
||||||
(check "(local x 10)\n|\n" [:x] [])
|
:true :false :.nan :.inf :nil
|
||||||
(check "(do (local x 10))\n|" [] [:x])
|
{:label "coroutine.yield"
|
||||||
(check "(let [foo 10 bar 20]
|
:documentation #(and $.value ($.value:find "```fnl\n(coroutine.yield ...)\n```" 1 true))}]
|
||||||
|)" [:foo :bar] [])
|
[:no1 :no2 :no3 :no4 :no5 :+
|
||||||
(check "(let [foo 10]
|
:this-variable-does-not-exist
|
||||||
(let [bar 20]
|
:_G.coroutine.yield
|
||||||
|))" [:foo :bar] [])
|
:_G._G.coroutine.yield
|
||||||
(check "(let [foo 10]
|
#(and (= nil $.documentation)
|
||||||
(let [bar 20]
|
(not= $.label :yes8))])
|
||||||
fo|))" [:foo :bar] [])
|
;; no duplicates
|
||||||
(check "(let [foo 10]
|
|
||||||
(let [bar 20]
|
|
||||||
|" [:foo :bar] [])
|
|
||||||
(check "(let [foo 10]
|
|
||||||
(let [bar 20]
|
|
||||||
fo|" [:foo :bar] [])
|
|
||||||
(check "(local foo 10)
|
|
||||||
(local bar (let [y foo] |" [:foo :y] [])
|
|
||||||
(check "(let [foo 10
|
|
||||||
bar 20
|
|
||||||
_ |" [:foo :bar] [])
|
|
||||||
(check "(let [foo 10
|
|
||||||
bar 20
|
|
||||||
_ fo|" [:foo :bar] [])
|
|
||||||
(check "(local x {:field 100})\n(if x.fi" [:x.field] [])
|
|
||||||
(check "(let [x 10] (let [x 10] x"
|
(check "(let [x 10] (let [x 10] x"
|
||||||
(fn [completions]
|
(fn [completions]
|
||||||
(faith.= 1 (accumulate [number-of-x 0 _ completion (ipairs completions.items)]
|
(faith.= 1 (accumulate [number-of-x 0 _ completion (ipairs completions.items)]
|
||||||
@ -131,72 +116,51 @@
|
|||||||
(+ number-of-x 1)
|
(+ number-of-x 1)
|
||||||
number-of-x))))
|
number-of-x))))
|
||||||
[])
|
[])
|
||||||
;; stretchy completions
|
;; completions of fields (nested)
|
||||||
(check "(local x {:field 100})\n(if fi" [:x.field] [])
|
(check "(local x {:y {:z {:a {:b 1}}}}) ; deep tables
|
||||||
(check "(local x {:field {:deep 100}})\n(if de" [:x.field.deep] [])
|
(local m {}) ; split modules
|
||||||
(check "(local t {:field (fn [foo] nil)})\n(t|" [:t.field] [])
|
(fn m.function [])
|
||||||
(check "(local t {:field (fn [self] nil)})\n(t|" [:t:field] [])
|
(fn m.method [self])
|
||||||
(check "(local t {})\n(fn t.field [foo] nil)\n(t|" [:t.field] [])
|
(local m2 {:function m.function :method m.method})
|
||||||
(check "(local t {})\n(fn t.field [self] nil)\n(t|" [:t:field] [])
|
|"
|
||||||
|
[:x :x.y :x.y.z.a.b
|
||||||
|
:m.function
|
||||||
|
:m:method
|
||||||
|
:m2.function
|
||||||
|
:m2:method]
|
||||||
|
[])
|
||||||
|
|
||||||
|
;; regression test for not crashing
|
||||||
|
(check "(local x {:field (fn [self])})\n(x::f" [:x:field] [])
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
(fn test-builtin []
|
(fn test-builtin []
|
||||||
(check "(|)" [:do :let :fn :doto :-> :-?>> :?.] [])
|
;; specials and macros are only suggested in callable positions
|
||||||
;; it's not the language server's job to do filtering,
|
(check "(macro funny [] `nil)
|
||||||
;; so there's no negative assertions here for other symbols
|
(|)"
|
||||||
(check "(d|)" [:do :doto] [])
|
[:do :let :fn :doto :-> :-?>> :?. :funny]
|
||||||
;; in fact, for fuzzy-matching clients, you especially want to make sure the server isn't filtering
|
[])
|
||||||
(check "(t|)" [:doto :setmetatable] [])
|
(check "(do |)"
|
||||||
;; specials only are suggested in callable positions
|
[]
|
||||||
(check "(do |)" [] [:do :let :fn :-> :-?>> :?.])
|
[:do :let :fn :doto :-> :-?>> :?.])
|
||||||
(check "|\n" [] [:do :let :fn :-> :-?>> :?.])
|
;; tricky case
|
||||||
(check "d|\n" [] [:do :let :fn :-> :-?>> :?.])
|
(check "(doto f |)"
|
||||||
|
[:do :let :fn :doto :-> :-?>> :?.]
|
||||||
|
[])
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
(fn test-macro []
|
|
||||||
(check "(macro funny [] `nil)\n(|)" [:funny] [])
|
|
||||||
nil)
|
|
||||||
|
|
||||||
(fn test-local-in-macro []
|
(fn test-local-in-macro []
|
||||||
(check "(local item 10)\n(doto it|)" [:item] [])
|
|
||||||
(check "(local item 10)\n(doto |)" [:item] [])
|
|
||||||
(check "(local item 10)\n(case 1 1 it|)" [:item] [])
|
(check "(local item 10)\n(case 1 1 it|)" [:item] [])
|
||||||
(check "(local item 10)\n(case 1 1 |)" [:item] [])
|
|
||||||
nil)
|
|
||||||
|
|
||||||
(fn test-fn-arg []
|
|
||||||
(check "(fn [x] (print x))\n" [] [:x])
|
|
||||||
(check "(fn [x] (print x))\n(print " [] [:x])
|
|
||||||
(check "(fn foo [z]\n (let [x 10 y 20]\n |" [:x :y :z] [])
|
|
||||||
(check "(fn foo [arg1 arg2 arg3]\n |)" [:arg1 :arg2 :arg3] [])
|
|
||||||
(check "(fn foo [arg1 arg2 arg3]\n (do (do (do |))))" [:arg1 :arg2 :arg3] [])
|
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
(fn test-field []
|
(fn test-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 [self])})\n(x::f" [] [])
|
|
||||||
(check
|
(check
|
||||||
"(let [my-table {:foo 10 :bar 20}]\n my-table.|)))"
|
"(let [my-table {:foo 10 :bar 20}]\n my-table.|)))"
|
||||||
[{:label :my-table.foo :kind kinds.Value}
|
[{:label :my-table.foo :kind kinds.Value}
|
||||||
{:label :my-table.bar :kind kinds.Value}]
|
{:label :my-table.bar :kind kinds.Value}]
|
||||||
[])
|
[])
|
||||||
(check
|
|
||||||
{:main.fnl "(let [foo (require :fooo)]
|
|
||||||
foo.|)))"
|
|
||||||
:fooo.fnl "(fn my-export [x] (print x))
|
|
||||||
{: my-export :constant 10}"}
|
|
||||||
[: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"}
|
|
||||||
[:foo.my-export :foo.constant]
|
|
||||||
[])
|
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
(fn test-docs []
|
(fn test-docs []
|
||||||
@ -251,21 +215,17 @@
|
|||||||
nil)
|
nil)
|
||||||
|
|
||||||
(fn test-module []
|
(fn test-module []
|
||||||
(check "(coroutine.y|"
|
(check
|
||||||
[{:label "coroutine.yield"
|
{:main.fnl "(let [foo (require :fooo)
|
||||||
:documentation #(and $.value ($.value:find "```fnl\n(coroutine.yield ...)\n```" 1 true))}]
|
bar (require :baar)]
|
||||||
[{:documentation #(= nil $)}])
|
|)"
|
||||||
(check "(local c coroutine)
|
:fooo.fnl "(fn my-export [x] (print x))
|
||||||
(c.y"
|
{: my-export :constant 10}"
|
||||||
["coroutine.yield" "c.yield"]
|
:baar.fnl "(local M {:constant 10})
|
||||||
[{:documentation #(= nil $)}])
|
(fn M.my-export [x] (print x))
|
||||||
(check "(local t table)
|
M"}
|
||||||
(t.i"
|
[:foo.my-export :foo.constant
|
||||||
["table.insert" "t.insert"]
|
:bar.my-export :bar.constant]
|
||||||
[{:documentation #(= nil $)}])
|
|
||||||
(check "debug.deb|"
|
|
||||||
[{:label "debug.debug"
|
|
||||||
:documentation #(and $.value ($.value:find "```fnl\n(debug.debug)\n```" 1 true))}]
|
|
||||||
[])
|
[])
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
@ -275,18 +235,10 @@
|
|||||||
(print foo)"
|
(print foo)"
|
||||||
[:foo]
|
[:foo]
|
||||||
[:math])
|
[:math])
|
||||||
(check "(local f|)
|
|
||||||
(print foo)"
|
|
||||||
[:foo]
|
|
||||||
[:math])
|
|
||||||
(check "(let [f|]
|
(check "(let [f|]
|
||||||
(print foo))"
|
(print foo))"
|
||||||
[:foo]
|
[:foo]
|
||||||
[:math])
|
[:math])
|
||||||
(check "(let [foo |] ; cursor is in an expression so we want expressions now
|
|
||||||
(print foo))"
|
|
||||||
[:math]
|
|
||||||
[])
|
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
(fn test-no-completion []
|
(fn test-no-completion []
|
||||||
@ -303,11 +255,16 @@
|
|||||||
[]
|
[]
|
||||||
[:math])
|
[:math])
|
||||||
|
|
||||||
(check "(fn foo|)"
|
(check "(fn foo| [])"
|
||||||
[]
|
[]
|
||||||
["foo|"])
|
["foo|"])
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
|
(fn test-compiler-env []
|
||||||
|
(check ";; fennel-ls: macro-file\n("
|
||||||
|
[:sym :sym? :list :quote :icollect :math]
|
||||||
|
[:os]))
|
||||||
|
|
||||||
;; ;; Future tests / features
|
;; ;; Future tests / features
|
||||||
;; ;; Scope Ordering Rules
|
;; ;; Scope Ordering Rules
|
||||||
;; (it "does not suggest locals past the suggestion location when a symbol is partially typed")
|
;; (it "does not suggest locals past the suggestion location when a symbol is partially typed")
|
||||||
@ -326,14 +283,12 @@
|
|||||||
;; (it "suggests keys when typing out destructuring, as in `(local {: typinghere} (require :mod))`")
|
;; (it "suggests keys when typing out destructuring, as in `(local {: typinghere} (require :mod))`")
|
||||||
;; (it "only suggests tables for `ipairs` / begin work on type checking system")
|
;; (it "only suggests tables for `ipairs` / begin work on type checking system")
|
||||||
|
|
||||||
{: test-global
|
{: test-basic
|
||||||
: test-local
|
|
||||||
: test-builtin
|
: test-builtin
|
||||||
: test-macro
|
|
||||||
: test-local-in-macro
|
: test-local-in-macro
|
||||||
: test-fn-arg
|
|
||||||
: test-field
|
: test-field
|
||||||
: test-docs
|
: test-docs
|
||||||
: test-module
|
: test-module
|
||||||
: test-destructure
|
: test-destructure
|
||||||
: test-no-completion}
|
: test-no-completion
|
||||||
|
: test-compiler-env}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user