better completions in destructure bindings

This commit is contained in:
XeroOl 2025-07-08 21:59:53 -05:00
parent b7e497228f
commit 28e20f74cb
4 changed files with 87 additions and 51 deletions

View File

@ -293,7 +293,16 @@ identifiers are declared / referenced in which places."
(let [old (tostring ?ast)] (let [old (tostring ?ast)]
(tset ?ast 1 "!!invalid-multi-symbol!!") (tset ?ast 1 "!!invalid-multi-symbol!!")
(table.insert defer #(tset ?ast 1 old)) (table.insert defer #(tset ?ast 1 old))
true)))) true))
(when (and (= 1 (msg:find "expected name and value"))
(list? ?ast))
(when (= 1 (length ?ast))
(table.insert ?ast (sym "_"))
(table.insert defer #(table.remove ?ast)))
(when (= 2 (length ?ast))
(table.insert ?ast nil*)
(table.insert defer #(table.remove ?ast)))
(= 3 (length ?ast)))))
(λ on-compile-error [_ msg ast call-me-to-reset-the-compiler] (λ on-compile-error [_ msg ast call-me-to-reset-the-compiler]
(let [range (or (message.ast->range server file ast) (let [range (or (message.ast->range server file ast)

View File

@ -40,7 +40,7 @@
"add the completion. also recursively adds the fields' completions" "add the completion. also recursively adds the fields' completions"
(when (not (. seen definition)) (when (not (. seen definition))
(set (. seen definition) true) (set (. seen definition) true)
(add-completion! name definition) (add-completion! name definition "Value")
(each [field def ?string-method (navigate.iter-fields server definition)] (each [field def ?string-method (navigate.iter-fields server definition)]
(if (or (= :self (tostring (?. def :metadata :fnl/arglist 1))) (if (or (= :self (tostring (?. def :metadata :fnl/arglist 1)))
?string-method ?string-method
@ -56,62 +56,77 @@
(add-completion-recursively! (.. name "." field) def))) (add-completion-recursively! (.. name "." field) def)))
(set (. seen definition) false))) (set (. seen definition) false)))
(each [name documentation (pairs hardcoded-completions)] (fn expression-completions []
(add-completion! name documentation)) (each [name documentation (pairs hardcoded-completions)]
(add-completion! name documentation))
(local seen-manglings {}) (local seen-manglings {})
(each [_ global* (ipairs file.allowed-globals)] (each [_ global* (ipairs file.allowed-globals)]
(when (not (. seen-manglings global*)) (when (not (. seen-manglings global*))
(set (. seen-manglings global*) true) (set (. seen-manglings global*) true)
(case (analyzer.search-name-and-scope server file global* scope) (case (analyzer.search-name-and-scope server file global* scope)
def (if (and (= :_G (tostring global*)) def (if (and (= :_G (tostring global*))
(not (: (tostring ?symbol) :match "_G[:.]"))) (not (: (tostring ?symbol) :match "_G[:.]")))
(add-completion! global* def) (add-completion! global* def)
(add-completion-recursively! global* def)) (add-completion-recursively! global* def))
_ (do _ (do
(io.stderr:write "BAD!!!! undocumented global: " (tostring global*) "\n") (io.stderr:write "BAD!!!! undocumented global: " (tostring global*) "\n")
(add-completion! global* {}))))) (add-completion! global* {})))))
(var scope scope)
(while scope
(each [mangling (pairs scope.manglings)]
(when (not (. seen-manglings mangling))
(set (. seen-manglings mangling) true)
(case (analyzer.search-name-and-scope server file mangling scope)
def (add-completion-recursively! mangling def)
_ (add-completion-recursively! mangling {}))))
(when in-call-position?
(each [macro* macro-value (pairs scope.macros)]
(add-completion! macro*
{:binding macro*
:metadata (. METADATA macro-value)}
:Keyword))
(each [special (pairs scope.specials)]
(case (analyzer.search-name-and-scope server file special scope)
def (add-completion! special def :Operator)
_ (do
(io.stderr:write "BAD!!!! undocumented special: " (tostring special) "\n")
{:label special}))))
(set scope scope.parent)))
(fn binding-completions []
"completions when you're writing a destructure pattern. We suggest identifiers which are unknown"
(each [_ {: message} (ipairs file.diagnostics)]
(case (message:match "unknown identifier: ([a-zA-Z0-9_-]+)")
identifier (add-completion! identifier {} :Variable))))
(if (. file.definitions ?symbol)
(binding-completions)
(expression-completions))
(var scope scope)
(while scope
(each [mangling (pairs scope.manglings)]
(when (not (. seen-manglings mangling))
(set (. seen-manglings mangling) true)
(case (analyzer.search-name-and-scope server file mangling scope)
def (add-completion-recursively! mangling def)
_ (add-completion-recursively! mangling {}))))
(when in-call-position?
(each [macro* macro-value (pairs scope.macros)]
(add-completion! macro*
{:binding macro*
:metadata (. METADATA macro-value)}
:Keyword))
(each [special (pairs scope.specials)]
(case (analyzer.search-name-and-scope server file special scope)
def (add-completion! special def :Operator)
_ (do
(io.stderr:write "BAD!!!! undocumented special: " (tostring special) "\n")
{:label special}))))
(set scope scope.parent))
(if server.can-do-good-completions? (if server.can-do-good-completions?
{:itemDefaults {:editRange range :data {: uri : byte}} {:itemDefaults {:editRange range :data {: uri : byte}}
:items results} :items results}
results))) results)))
(fn completionItem/resolve [server _send completion-item] (fn completionItem/resolve [server _send completion-item]
(or (. hardcoded-completions completion-item.label) (let [result
(let [{: uri : byte} completion-item.data (or (. hardcoded-completions completion-item.name)
file (files.get-by-uri server uri) (let [{: uri : byte} completion-item.data
(_symbol parents) (analyzer.find-symbol file.ast byte) file (files.get-by-uri server uri)
scope (or (accumulate [?find nil _ parent (ipairs parents) &until ?find] (_symbol parents) (analyzer.find-symbol file.ast byte)
(. file.scopes parent)) scope (or (accumulate [?find nil _ parent (ipairs parents) &until ?find]
file.scope)] (. file.scopes parent))
(case (analyzer.search-name-and-scope server file completion-item.label scope) file.scope)]
result (doto completion-item (tset :documentation (format.hover-format server completion-item.label result))))))) (analyzer.search-name-and-scope server file completion-item.label scope)))]
(when result
(set completion-item.documentation (format.hover-format server completion-item.label result)))
completion-item))
{: textDocument/completion {: textDocument/completion
: completionItem/resolve} : completionItem/resolve}

View File

@ -193,12 +193,13 @@ fntype is one of fn or λ or lambda"
{:label name {:label name
:documentation (when (not server.can-do-good-completions?) (hover-format server name definition)) :documentation (when (not server.can-do-good-completions?) (hover-format server name definition))
:textEdit (when (not server.can-do-good-completions?) {:newText name : range}) :textEdit (when (not server.can-do-good-completions?) {:newText name : range})
:kind (or (?. kinds ?kind) :kind (or (if (not= ?kind :Value) (?. kinds ?kind))
(case (navigate.getmetadata server definition) (case (navigate.getmetadata server definition)
metadata metadata
(or (?. kinds metadata.fls/itemKind) (or (?. kinds metadata.fls/itemKind)
(when metadata.fnl/arglist (when metadata.fnl/arglist
(if (name:find ":") kinds.Method kinds.Function)))) (if (name:find ":") kinds.Method kinds.Function))))
(?. kinds ?kind)
kinds.Text)}) kinds.Text)})
{: signature-help-format {: signature-help-format

View File

@ -164,7 +164,8 @@
(check "(local x {:field (fn [self])})\n(x::f" [] []) (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.|)))"
[:my-table.foo :my-table.bar] [{:label :my-table.foo :kind kinds.Value}
{:label :my-table.bar :kind kinds.Value}]
[]) [])
(check (check
{:main.fnl "(let [foo (require :fooo)] {:main.fnl "(let [foo (require :fooo)]
@ -256,6 +257,15 @@
[]) [])
nil) nil)
(fn test-destructure []
;; this is a binding variable, we don't want all the normal completions
(check "(local f|)\n(print foo)"
[:foo]
[:setmetatable :_G])
(check "(let [f|]\n(print foo)"
[:foo]
[:setmetatable :_G])
nil)
;; ;; Future tests / features ;; ;; Future tests / features
;; ;; Scope Ordering Rules ;; ;; Scope Ordering Rules
@ -283,4 +293,5 @@
: test-fn-arg : test-fn-arg
: test-field : test-field
: test-docs : test-docs
: test-module} : test-module
: test-destructure}