diff --git a/src/fennel-ls/analyzer.fnl b/src/fennel-ls/analyzer.fnl index 9c653e8..d14e2cb 100644 --- a/src/fennel-ls/analyzer.fnl +++ b/src/fennel-ls/analyzer.fnl @@ -305,13 +305,13 @@ returns the called symbol and the number of the argument closest to byte" (λ find-definition [server file symbol ?byte] (if (. file.definitions symbol) - (. file.definitions symbol) - (search-main server file symbol {:stop-early? false} {:byte ?byte}))) + (. file.definitions symbol) + (search-main server file symbol {:stop-early? false} {:byte ?byte}))) (λ find-nearest-definition [server file symbol ?byte] (if (. file.definitions symbol) - (. file.definitions symbol) - (search-main server file symbol {:stop-early? true} {:byte ?byte}))) + (. file.definitions symbol) + (search-main server file symbol {:stop-early? true} {:byte ?byte}))) {: find-symbol : find-nearest-call diff --git a/src/fennel-ls/formatter.fnl b/src/fennel-ls/formatter.fnl index ec80fd3..0748aa1 100644 --- a/src/fennel-ls/formatter.fnl +++ b/src/fennel-ls/formatter.fnl @@ -19,20 +19,27 @@ user code. Fennel-ls doesn't support user-code formatting as of now." (.. ": " $2 $3))) _ (tostring arg))) -(fn render-arglist [?arglist] - (if ?arglist - (icollect [_ arg (ipairs ?arglist)] - {:label (render-arg arg)}))) - (fn fn-signature-format [special name args] - (let [args (case (type (?. args 1)) + "Returns the LSP-formatted signature and parameters objects" + (fn render-arglist [arglist offset] + (var offset offset) + (icollect [_ arg (ipairs arglist)] + (let [rendered {:label [offset (+ offset (length arg))]}] + (set offset (+ 1 (. rendered :label 2))) + rendered))) + + (let [name (tostring (or name special)) + args (case (type (?. args 1)) :table (icollect [_ v (ipairs args)] (render-arg v)) - _ args)] - (.. "(" - (tostring (or name special)) " " - (table.concat args " ") - ")"))) + _ args) + ;; + 2 for the opening paren and the space + args-offset (+ 2 (length name))] + (values (.. "(" + name " " + (table.concat args " ") + ")") + (render-arglist args args-offset)))) (fn fn-format [special name args docstring] (.. (code-block (fn-signature-format special name args)) @@ -96,20 +103,26 @@ fntype is one of fn or λ or lambda" "Return a signatureHelp lsp object symbol can be an actual ast symbol or a binding object from a docset" - (case (analyze-fn symbol.definition) + (case-try (analyze-fn symbol.definition) {:fntype ?fntype :name ?name :arglist ?arglist :docstring ?docstring} - {:label (fn-signature-format ?fntype ?name ?arglist) + (fn-signature-format ?fntype ?name ?arglist) + (signature parameters) + {:label signature :documentation ?docstring - :parameters (render-arglist ?arglist)} - _ (case symbol - {: binding :metadata {:fnl/arglist arglist :fnl/docstring docstring}} - {:label (fn-signature-format :fn binding arglist) - :documentation docstring - :parameters (render-arglist arglist)} - _ {:label (.. "ERROR: don't know how to format " - (view symbol {:one-line? true :depth 3})) - :documentation (code-block - (view symbol {:depth 3}))}))) + :parameters parameters} + ;; if we couldn't get the info from the ast, try the metadata + (catch _ (case-try symbol + {: binding :metadata {:fnl/arglist arglist + :fnl/docstring docstring}} + (fn-signature-format "" binding arglist) + (signature parameters) + {:label signature + :documentation docstring + :parameters parameters} + (catch _ {:label (.. "ERROR: don't know how to format " + (view symbol {:one-line? true :depth 3})) + :documentation (code-block + (view symbol {:depth 3}))}))))) (λ hover-format [result] "Format code that will appear when the user hovers over a symbol" diff --git a/src/fennel-ls/message.fnl b/src/fennel-ls/message.fnl index 0c2008f..e8ac597 100644 --- a/src/fennel-ls/message.fnl +++ b/src/fennel-ls/message.fnl @@ -92,12 +92,10 @@ LSP json objects." :edit {:changes {uri (diagnostic.quickfix)}}})) (λ symbol->signature-help [_server _file _call signature active-parameter] - (let [params-count (length signature.parameters) - {:label last} (. signature.parameters params-count)] + (let [params-count (length signature.parameters)] {:signatures [signature] :activeSignature 0 ; we only ever have one signature - :activeParameter (if (and (>= active-parameter params-count) - (= last "...")) + :activeParameter (if (>= active-parameter params-count) (- params-count 1) (>= active-parameter 0) active-parameter)})) diff --git a/test/signature-help.fnl b/test/signature-help.fnl index 17b4c56..66d0d88 100644 --- a/test/signature-help.fnl +++ b/test/signature-help.fnl @@ -4,10 +4,11 @@ (λ check-signature [expected response] (case response - {:signatures [{:label signature}]} + {:signatures [{:label signature : parameters}]} (do (faith.= expected.signature signature) - (faith.= expected.activeParameter response.activeParameter)) + (faith.= expected.activeParameter response.activeParameter) + (faith.= expected.parameters parameters)) ;; fail _ (faith.is nil (.. "Invalid response: " (view response))))) @@ -16,166 +17,237 @@ [{: result}] (client:signature-help uri cursor)] (check-signature expected result))) +(local fn-signature "(fn name? args docstring? ...)") +(local fn-params [{:label [4 9]} + {:label [10 14]} + {:label [15 25]} + {:label [26 29]}]) + +(local err-signature "(error message ?level)") +(local err-params [{:label [7 14]} + {:label [15 21]}]) + +(local let-signature "(let [name1 val1 ... nameN valN] ...)") +(local let-params [{:label [5 32]} + {:label [33 36]}]) + +(local function-signature "(func arg1 arg2)") +(local function-params [{:label [6 10]} + {:label [11 15]}]) + (fn test-fn-definition [] (check "(fn |)" - {:signature "(fn name? args docstring? ...)" - :activeParameter 0}) + {:signature fn-signature + :activeParameter 0 + :parameters fn-params}) (check "(fn some-nam|)" - {:signature "(fn name? args docstring? ...)" - :activeParameter 0}) + {:signature fn-signature + :activeParameter 0 + :parameters fn-params}) (check "(fn some-name [|]" - {:signature "(fn name? args docstring? ...)" - :activeParameter 1}) + {:signature fn-signature + :activeParameter 1 + :parameters fn-params}) (check "(fn some-name [arg|])" - {:signature "(fn name? args docstring? ...)" - :activeParameter 1}) + {:signature fn-signature + :activeParameter 1 + :parameters fn-params}) (check "(fn some-name [arg1 arg2] \"docstring|\")" - {:signature "(fn name? args docstring? ...)" - :activeParameter 2}) + {:signature fn-signature + :activeParameter 2 + :parameters fn-params}) (check "(fn some-name [arg1 arg2] \"docstring\" |" - {:signature "(fn name? args docstring? ...)" - :activeParameter 2})) + {:signature fn-signature + :activeParameter 2 + :parameters fn-params})) (fn test-local-function [] (check "(fn func [arg1 arg2] (print :hello)) (func|)" - {:signature "(func arg1 arg2)" - :activeParameter nil}) + {:signature function-signature + :activeParameter nil + :parameters function-params}) (check "(fn func [arg1 arg2] (print :hello)) (func |)" - {:signature "(func arg1 arg2)" - :activeParameter 0}) + {:signature function-signature + :activeParameter 0 + :parameters function-params}) (check "(fn func [arg1 arg2] (print :hello)) (func a1|)" - {:signature "(func arg1 arg2)" - :activeParameter 0}) + {:signature function-signature + :activeParameter 0 + :parameters function-params}) (check "(fn func [arg1 arg2] (print :hello)) (func a|1 a2)" - {:signature "(func arg1 arg2)" - :activeParameter 0}) + {:signature function-signature + :activeParameter 0 + :parameters function-params}) (check "(fn func [arg1 arg2] (print :hello)) (func a1 a2|)" - {:signature "(func arg1 arg2)" - :activeParameter 1}) + {:signature function-signature + :activeParameter 1 + :parameters function-params}) (check "(fn func [arg1 arg2] (print :hello)) (func a1 a|2)" - {:signature "(func arg1 arg2)" - :activeParameter 1})) + {:signature function-signature + :activeParameter 1 + :parameters function-params})) (fn test-literals [] (check "(fn func [arg1 arg2] (print :hello)) (func arg 10|2)" - {:signature "(func arg1 arg2)" - :activeParameter 1}) + {:signature function-signature + :activeParameter 1 + :parameters function-params}) (check "(fn func [arg1 arg2] (print :hello)) (func arg \"10|2\")" - {:signature "(func arg1 arg2)" - :activeParameter 1})) + {:signature function-signature + :activeParameter 1 + :parameters function-params})) (fn test-vararg [] + (local or-params [{:label [4 5]} + {:label [6 7]} + {:label [8 11]}]) + (check "(or a b|)" {:signature "(or a b ...)" - :activeParameter 1}) + :activeParameter 1 + :parameters or-params}) (check "(or a b c|)" {:signature "(or a b ...)" - :activeParameter 2}) + :activeParameter 2 + :parameters or-params}) (check "(or a b c d e|)" {:signature "(or a b ...)" - :activeParameter 2})) + :activeParameter 2 + :parameters or-params})) (fn test-lua-builtin [] + (check "(error msg|)" + {:signature err-signature + :activeParameter 0 + :parameters err-params}) + (check "(error msg lvl|)" - {:signature "(error message ?level)" - :activeParameter 1}) + {:signature err-signature + :activeParameter 1 + :parameters err-params}) (check "(error msg lvl extr|)" - {:signature "(error message ?level)" - :activeParameter 2})) + {:signature err-signature + :activeParameter 1 + :parameters err-params})) (fn test-multisym [] (check "(table.concat tbl s|)" {:signature "(table.concat list ?sep ?i ?j)" - :activeParameter 1})) + :activeParameter 1 + :parameters [{:label [14 18]} + {:label [19 23]} + {:label [24 26]} + {:label [27 29]}]})) (fn test-destructuring-arg [] + (local dstr-params [{:label [6 19]} + {:label [20 24]}]) + (check "(fn dstr [{:field name} arg2] {}) (dstr |)" {:signature "(dstr {:field name} arg2)" - :activeParameter 0}) + :activeParameter 0 + :parameters dstr-params}) (check "(fn dstr [{:field name} arg2] {}) (dstr arg1 ar|)" {:signature "(dstr {:field name} arg2)" - :activeParameter 1}) + :activeParameter 1 + :parameters dstr-params}) (check "(fn dstr [{:field |} arg2] {})" - {:signature "(fn name? args docstring? ...)" - :activeParameter 1})) + {:signature fn-signature + :activeParameter 1 + :parameters fn-params})) (fn test-binding-form [] + (local each-signature "(each [key value (iterator)] ...)") + (local each-params [{:label [6 28]} + {:label [29 32]}]) + (check "(each |)" - {:signature "(each [key value (iterator)] ...)" - :activeParameter 0}) + {:signature each-signature + :activeParameter 0 + :parameters each-params}) (check "(each [|])" - {:signature "(each [key value (iterator)] ...)" - :activeParameter 0}) + {:signature each-signature + :activeParameter 0 + :parameters each-params}) (check "(each [k val|])" - {:signature "(each [key value (iterator)] ...)" - :activeParameter 0}) + {:signature each-signature + :activeParameter 0 + :parameters each-params}) (check "(let [a 0] (error |))" - {:signature "(error message ?level)" - :activeParameter 0}) + {:signature err-signature + :activeParameter 0 + :parameters err-params}) (check "(let [a|] (error))" - {:signature "(let [name1 val1 ... nameN valN] ...)" - :activeParameter 0})) + {:signature let-signature + :activeParameter 0 + :parameters let-params})) (fn test-indirect-definition [] (check "(let [a error] (a |))" - {:signature "(error message ?level)" - :activeParameter 0})) + {:signature err-signature + :activeParameter 0 + :parameters err-params})) (fn test-destructuring-binding [] (check "(let [(a b|) {}]" - {:signature "(let [name1 val1 ... nameN valN] ...)" - :activeParameter 0}) + {:signature let-signature + :activeParameter 0 + :parameters let-params}) (check "(let [(a {:b |}) {}]" - {:signature "(let [name1 val1 ... nameN valN] ...)" - :activeParameter 0}) + {:signature let-signature + :activeParameter 0 + :parameters let-params}) (check "(let [{:field |} {}]" - {:signature "(let [name1 val1 ... nameN valN] ...)" - :activeParameter 0}) + {:signature let-signature + :activeParameter 0 + :parameters let-params}) (check "(let [{:field {: nested |" - {:signature "(let [name1 val1 ... nameN valN] ...)" - :activeParameter 0}) + {:signature let-signature + :activeParameter 0 + :parameters let-params}) (check "(let [{:field {: nested &as |}} {}]" - {:signature "(let [name1 val1 ... nameN valN] ...)" - :activeParameter 0})) + {:signature let-signature + :activeParameter 0 + :parameters let-params})) {: test-fn-definition : test-local-function