Improved signature help and active argument detection.
This now uses a more correct algorithm to find the function call the user is typing and also adds active argument detection.
This commit is contained in:
parent
96aef99728
commit
f3d5984091
@ -39,7 +39,6 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find
|
|||||||
"
|
"
|
||||||
|
|
||||||
(local {: sym? : list? : sequence? : varg?} (require :fennel))
|
(local {: sym? : list? : sequence? : varg?} (require :fennel))
|
||||||
(local {: special?} (require :fennel-ls.compiler))
|
|
||||||
(local {: get-ast-info &as utils} (require :fennel-ls.utils))
|
(local {: get-ast-info &as utils} (require :fennel-ls.utils))
|
||||||
(local files (require :fennel-ls.files))
|
(local files (require :fennel-ls.files))
|
||||||
(local docs (require :fennel-ls.docs))
|
(local docs (require :fennel-ls.docs))
|
||||||
@ -218,7 +217,7 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find
|
|||||||
_ (case (find-local-definition file name scope)
|
_ (case (find-local-definition file name scope)
|
||||||
def (search-val server file def.definition (stack-add-keys! stack def.keys) opts))))))
|
def (search-val server file def.definition (stack-add-keys! stack def.keys) opts))))))
|
||||||
|
|
||||||
(λ _past? [?ast byte]
|
(λ past? [?ast byte]
|
||||||
;; check if a byte is past an ast object
|
;; check if a byte is past an ast object
|
||||||
(and (= (type ?ast) :table)
|
(and (= (type ?ast) :table)
|
||||||
(get-ast-info ?ast :byteend)
|
(get-ast-info ?ast :byteend)
|
||||||
@ -277,12 +276,37 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find
|
|||||||
(λ find-nearest-call [_server file byte]
|
(λ find-nearest-call [_server file byte]
|
||||||
"Find the nearest call
|
"Find the nearest call
|
||||||
|
|
||||||
returns the called symbol and the argument number position points to"
|
returns the called symbol and the number of the argument closest to byte"
|
||||||
(let [(_ [[call] parent-call]) (find-symbol file.ast byte)
|
(λ find-list [[call & parents]]
|
||||||
parent (?. parent-call 1)]
|
(if (. file.calls call)
|
||||||
(if (and parent (special? parent))
|
call
|
||||||
(values parent -1)
|
(if (next parents)
|
||||||
(values call -1))))
|
(find-list parents))))
|
||||||
|
|
||||||
|
(λ arg-index [call byte]
|
||||||
|
;; TODO: special handling for binding forms so we can point to the
|
||||||
|
;; individual arguments in an each or accumulate call.
|
||||||
|
;; Also need to split them up in formatter.fnl
|
||||||
|
(faccumulate [index nil
|
||||||
|
i (length call) 1 -1 &until index]
|
||||||
|
(if (contains? (. call i) byte)
|
||||||
|
; -2 because this is the 3rd element of the list, but
|
||||||
|
; the 2nd argument to the call, and LSP is 0-indexed
|
||||||
|
(- i 2)
|
||||||
|
(past? (. call i) byte)
|
||||||
|
; this means we are either at the end of the list or
|
||||||
|
; inserting between two arguments
|
||||||
|
(- i 1))))
|
||||||
|
|
||||||
|
(case-try (find-symbol file.ast byte)
|
||||||
|
(_symbol parents) (find-list parents)
|
||||||
|
[callee &as call] (values callee (arg-index call byte))
|
||||||
|
(catch _ nil)))
|
||||||
|
|
||||||
|
(λ find-definition [server file symbol ?byte]
|
||||||
|
(if (. file.definitions symbol)
|
||||||
|
(. file.definitions symbol)
|
||||||
|
(search-main server file symbol {:stop-early? false} {:byte ?byte})))
|
||||||
|
|
||||||
(λ find-nearest-definition [server file symbol ?byte]
|
(λ find-nearest-definition [server file symbol ?byte]
|
||||||
(if (. file.definitions symbol)
|
(if (. file.definitions symbol)
|
||||||
@ -292,6 +316,7 @@ returns the called symbol and the argument number position points to"
|
|||||||
{: find-symbol
|
{: find-symbol
|
||||||
: find-nearest-call
|
: find-nearest-call
|
||||||
: find-nearest-definition
|
: find-nearest-definition
|
||||||
|
: find-definition
|
||||||
: search-main
|
: search-main
|
||||||
: search-name-and-scope
|
: search-name-and-scope
|
||||||
:search-ast search-val}
|
:search-ast search-val}
|
||||||
|
|||||||
@ -19,6 +19,11 @@ user code. Fennel-ls doesn't support user-code formatting as of now."
|
|||||||
(.. ": " $2 $3)))
|
(.. ": " $2 $3)))
|
||||||
_ (tostring arg)))
|
_ (tostring arg)))
|
||||||
|
|
||||||
|
(fn render-arglist [?arglist]
|
||||||
|
(if ?arglist
|
||||||
|
(icollect [_ arg (ipairs ?arglist)]
|
||||||
|
{:label (render-arg arg)})))
|
||||||
|
|
||||||
(fn fn-signature-format [special name args]
|
(fn fn-signature-format [special name args]
|
||||||
(let [args (case (type (?. args 1))
|
(let [args (case (type (?. args 1))
|
||||||
:table (icollect [_ v (ipairs args)]
|
:table (icollect [_ v (ipairs args)]
|
||||||
@ -95,14 +100,14 @@ fntype is one of fn or λ or lambda"
|
|||||||
{:fntype ?fntype :name ?name :arglist ?arglist :docstring ?docstring}
|
{:fntype ?fntype :name ?name :arglist ?arglist :docstring ?docstring}
|
||||||
{:label (fn-signature-format ?fntype ?name ?arglist)
|
{:label (fn-signature-format ?fntype ?name ?arglist)
|
||||||
:documentation ?docstring
|
:documentation ?docstring
|
||||||
:parameters (if ?arglist
|
:parameters (render-arglist ?arglist)}
|
||||||
(icollect [_ arg (ipairs ?arglist)]
|
|
||||||
{:label (render-arg arg)}))}
|
|
||||||
_ (case symbol
|
_ (case symbol
|
||||||
{: binding :metadata {:fnl/arglist arglist :fnl/docstring docstring}}
|
{: binding :metadata {:fnl/arglist arglist :fnl/docstring docstring}}
|
||||||
{:label (fn-signature-format :fn binding arglist)
|
{:label (fn-signature-format :fn binding arglist)
|
||||||
:documentation docstring}
|
:documentation docstring
|
||||||
_ {:label (.. "ERROR: don't know how to format " (tostring symbol))
|
:parameters (render-arglist arglist)}
|
||||||
|
_ {:label (.. "ERROR: don't know how to format "
|
||||||
|
(view symbol {:one-line? true :depth 3}))
|
||||||
:documentation (code-block
|
:documentation (code-block
|
||||||
(view symbol {:depth 3}))})))
|
(view symbol {:depth 3}))})))
|
||||||
|
|
||||||
|
|||||||
@ -138,16 +138,18 @@ Every time the client sends a message, it gets handled by a function in the corr
|
|||||||
(catch _ nil))))
|
(catch _ nil))))
|
||||||
|
|
||||||
(λ requests.textDocument/signatureHelp [server
|
(λ requests.textDocument/signatureHelp [server
|
||||||
_send
|
_send
|
||||||
{:textDocument {: uri} : position}]
|
{:textDocument {: uri} : position}]
|
||||||
(let [file (files.get-by-uri server uri)
|
(let [file (files.get-by-uri server uri)
|
||||||
byte (utils.position->byte file.text position server.position-encoding)]
|
byte (utils.position->byte file.text position server.position-encoding)]
|
||||||
(case-try (analyzer.find-nearest-call server file byte)
|
(case-try (analyzer.find-nearest-call server file byte)
|
||||||
(symbol active-parameter)
|
(symbol active-parameter)
|
||||||
(analyzer.find-nearest-definition server file symbol)
|
(analyzer.find-definition server file symbol)
|
||||||
{:indeterminate nil &as result}
|
{:indeterminate nil &as definition}
|
||||||
|
(formatter.signature-help-format definition)
|
||||||
|
signature
|
||||||
(message.symbol->signature-help server file symbol
|
(message.symbol->signature-help server file symbol
|
||||||
(formatter.signature-help-format result)
|
signature
|
||||||
active-parameter)
|
active-parameter)
|
||||||
(catch _ nil))))
|
(catch _ nil))))
|
||||||
|
|
||||||
|
|||||||
@ -91,9 +91,16 @@ LSP json objects."
|
|||||||
:kind ?kind
|
:kind ?kind
|
||||||
:edit {:changes {uri (diagnostic.quickfix)}}}))
|
:edit {:changes {uri (diagnostic.quickfix)}}}))
|
||||||
|
|
||||||
(λ symbol->signature-help [_server _file _call signature _active-parameter]
|
(λ symbol->signature-help [_server _file _call signature active-parameter]
|
||||||
{:signatures [signature]
|
(let [params-count (length signature.parameters)
|
||||||
:activeSignature 0})
|
{:label last} (. signature.parameters params-count)]
|
||||||
|
{:signatures [signature]
|
||||||
|
:activeSignature 0 ; we only ever have one signature
|
||||||
|
:activeParameter (if (and (>= active-parameter params-count)
|
||||||
|
(= last "..."))
|
||||||
|
(- params-count 1)
|
||||||
|
(>= active-parameter 0)
|
||||||
|
active-parameter)}))
|
||||||
|
|
||||||
(λ multisym->range [server file ast n]
|
(λ multisym->range [server file ast n]
|
||||||
(let [spl (utils.multi-sym-split ast)
|
(let [spl (utils.multi-sym-split ast)
|
||||||
|
|||||||
@ -5,7 +5,9 @@
|
|||||||
(λ check-signature [expected response]
|
(λ check-signature [expected response]
|
||||||
(case response
|
(case response
|
||||||
{:signatures [{:label signature}]}
|
{:signatures [{:label signature}]}
|
||||||
(faith.= expected.signature signature)
|
(do
|
||||||
|
(faith.= expected.signature signature)
|
||||||
|
(faith.= expected.activeParameter response.activeParameter))
|
||||||
;; fail
|
;; fail
|
||||||
_ (faith.is nil (.. "Invalid response: " (view response)))))
|
_ (faith.is nil (.. "Invalid response: " (view response)))))
|
||||||
|
|
||||||
@ -15,62 +17,173 @@
|
|||||||
(check-signature expected result)))
|
(check-signature expected result)))
|
||||||
|
|
||||||
(fn test-fn-definition []
|
(fn test-fn-definition []
|
||||||
|
(check "(fn |)"
|
||||||
|
{:signature "(fn name? args docstring? ...)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
|
(check "(fn some-nam|)"
|
||||||
|
{:signature "(fn name? args docstring? ...)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
|
(check "(fn some-name [|]"
|
||||||
|
{:signature "(fn name? args docstring? ...)"
|
||||||
|
:activeParameter 1})
|
||||||
|
|
||||||
|
(check "(fn some-name [arg|])"
|
||||||
|
{:signature "(fn name? args docstring? ...)"
|
||||||
|
:activeParameter 1})
|
||||||
|
|
||||||
|
(check "(fn some-name [arg1 arg2]
|
||||||
|
\"docstring|\")"
|
||||||
|
{:signature "(fn name? args docstring? ...)"
|
||||||
|
:activeParameter 2})
|
||||||
|
|
||||||
|
(check "(fn some-name [arg1 arg2]
|
||||||
|
\"docstring\"
|
||||||
|
|"
|
||||||
|
{:signature "(fn name? args docstring? ...)"
|
||||||
|
:activeParameter 2}))
|
||||||
|
|
||||||
|
(fn test-local-function []
|
||||||
(check "(fn func [arg1 arg2] (print :hello))
|
(check "(fn func [arg1 arg2] (print :hello))
|
||||||
(func|)"
|
(func|)"
|
||||||
{:signature "(func arg1 arg2)"})
|
{:signature "(func arg1 arg2)"
|
||||||
|
:activeParameter nil})
|
||||||
|
|
||||||
(check "(fn func [arg1 arg2] (print :hello))
|
(check "(fn func [arg1 arg2] (print :hello))
|
||||||
(func |)"
|
(func |)"
|
||||||
{:signature "(func arg1 arg2)"})
|
{:signature "(func arg1 arg2)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
(check "(fn func [arg1 arg2] (print :hello))
|
(check "(fn func [arg1 arg2] (print :hello))
|
||||||
(func a1|)"
|
(func a1|)"
|
||||||
{:signature "(func arg1 arg2)"})
|
{:signature "(func arg1 arg2)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
(check "(fn func [arg1 arg2] (print :hello))
|
(check "(fn func [arg1 arg2] (print :hello))
|
||||||
(func a|1 a2)"
|
(func a|1 a2)"
|
||||||
{:signature "(func arg1 arg2)"})
|
{:signature "(func arg1 arg2)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
(check "(fn func [arg1 arg2] (print :hello))
|
(check "(fn func [arg1 arg2] (print :hello))
|
||||||
(func a1 a2|)"
|
(func a1 a2|)"
|
||||||
{:signature "(func arg1 arg2)"})
|
{:signature "(func arg1 arg2)"
|
||||||
|
:activeParameter 1})
|
||||||
|
|
||||||
(check "(fn func [arg1 arg2] (print :hello))
|
(check "(fn func [arg1 arg2] (print :hello))
|
||||||
(func a1 a|2)"
|
(func a1 a|2)"
|
||||||
{:signature "(func arg1 arg2)"}))
|
{:signature "(func arg1 arg2)"
|
||||||
|
:activeParameter 1}))
|
||||||
|
|
||||||
|
(fn test-literals []
|
||||||
|
(check "(fn func [arg1 arg2] (print :hello))
|
||||||
|
(func arg 10|2)"
|
||||||
|
{:signature "(func arg1 arg2)"
|
||||||
|
:activeParameter 1})
|
||||||
|
|
||||||
|
(check "(fn func [arg1 arg2] (print :hello))
|
||||||
|
(func arg \"10|2\")"
|
||||||
|
{:signature "(func arg1 arg2)"
|
||||||
|
:activeParameter 1}))
|
||||||
|
|
||||||
|
(fn test-vararg []
|
||||||
|
(check "(or a b|)"
|
||||||
|
{:signature "(or a b ...)"
|
||||||
|
:activeParameter 1})
|
||||||
|
|
||||||
|
(check "(or a b c|)"
|
||||||
|
{:signature "(or a b ...)"
|
||||||
|
:activeParameter 2})
|
||||||
|
|
||||||
|
(check "(or a b c d e|)"
|
||||||
|
{:signature "(or a b ...)"
|
||||||
|
:activeParameter 2}))
|
||||||
|
|
||||||
(fn test-lua-builtin []
|
(fn test-lua-builtin []
|
||||||
(check "(error msg lvl|)"
|
(check "(error msg lvl|)"
|
||||||
{:signature "(error message ?level)"}))
|
{:signature "(error message ?level)"
|
||||||
|
:activeParameter 1})
|
||||||
|
|
||||||
|
(check "(error msg lvl extr|)"
|
||||||
|
{:signature "(error message ?level)"
|
||||||
|
:activeParameter 2}))
|
||||||
|
|
||||||
(fn test-multisym []
|
(fn test-multisym []
|
||||||
(check "(table.concat tbl s|)"
|
(check "(table.concat tbl s|)"
|
||||||
{:signature "(table.concat list ?sep ?i ?j)"}))
|
{:signature "(table.concat list ?sep ?i ?j)"
|
||||||
|
:activeParameter 1}))
|
||||||
|
|
||||||
(fn test-destructuring-arg []
|
(fn test-destructuring-arg []
|
||||||
(check "(fn dstr [{:field name} arg2] {})
|
(check "(fn dstr [{:field name} arg2] {})
|
||||||
(dstr |)"
|
(dstr |)"
|
||||||
{:signature "(dstr {:field name} arg2)"})
|
{:signature "(dstr {:field name} arg2)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
(check "(fn dstr [{:field name} arg2] {})
|
(check "(fn dstr [{:field name} arg2] {})
|
||||||
(dstr arg1 ar|)"
|
(dstr arg1 ar|)"
|
||||||
{:signature "(dstr {:field name} arg2)"}))
|
{:signature "(dstr {:field name} arg2)"
|
||||||
|
:activeParameter 1})
|
||||||
|
|
||||||
(fn test-special []
|
(check "(fn dstr [{:field |} arg2] {})"
|
||||||
|
{:signature "(fn name? args docstring? ...)"
|
||||||
|
:activeParameter 1}))
|
||||||
|
|
||||||
|
(fn test-binding-form []
|
||||||
(check "(each |)"
|
(check "(each |)"
|
||||||
{:signature "(each [key value (iterator)] ...)"})
|
{:signature "(each [key value (iterator)] ...)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
(check "(each [|])"
|
(check "(each [|])"
|
||||||
{:signature "(each [key value (iterator)] ...)"})
|
{:signature "(each [key value (iterator)] ...)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
(check "(each [k val|])"
|
(check "(each [k val|])"
|
||||||
{:signature "(each [key value (iterator)] ...)"})
|
{:signature "(each [key value (iterator)] ...)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
(check "(each [33|])"
|
(check "(let [a 0]
|
||||||
{:signature "(each [key value (iterator)] ...)"}))
|
(error |))"
|
||||||
|
{:signature "(error message ?level)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
|
(check "(let [a|] (error))"
|
||||||
|
{:signature "(let [name1 val1 ... nameN valN] ...)"
|
||||||
|
:activeParameter 0}))
|
||||||
|
|
||||||
|
(fn test-indirect-definition []
|
||||||
|
(check "(let [a error]
|
||||||
|
(a |))"
|
||||||
|
{:signature "(error message ?level)"
|
||||||
|
:activeParameter 0}))
|
||||||
|
|
||||||
|
(fn test-destructuring-binding []
|
||||||
|
(check "(let [(a b|) {}]"
|
||||||
|
{:signature "(let [name1 val1 ... nameN valN] ...)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
|
(check "(let [(a {:b |}) {}]"
|
||||||
|
{:signature "(let [name1 val1 ... nameN valN] ...)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
|
(check "(let [{:field |} {}]"
|
||||||
|
{:signature "(let [name1 val1 ... nameN valN] ...)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
|
(check "(let [{:field {: nested |"
|
||||||
|
{:signature "(let [name1 val1 ... nameN valN] ...)"
|
||||||
|
:activeParameter 0})
|
||||||
|
|
||||||
|
(check "(let [{:field {: nested &as |}} {}]"
|
||||||
|
{:signature "(let [name1 val1 ... nameN valN] ...)"
|
||||||
|
:activeParameter 0}))
|
||||||
|
|
||||||
{: test-fn-definition
|
{: test-fn-definition
|
||||||
|
: test-local-function
|
||||||
|
: test-literals
|
||||||
|
: test-vararg
|
||||||
: test-lua-builtin
|
: test-lua-builtin
|
||||||
: test-multisym
|
: test-multisym
|
||||||
: test-destructuring-arg
|
: test-destructuring-arg
|
||||||
: test-special}
|
: test-binding-form
|
||||||
|
: test-indirect-definition
|
||||||
|
: test-destructuring-binding}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user