Added hover and completion for specials

next on the todo list is macros and globals, and then I want to be able
to choose extra globals to add in.
This commit is contained in:
XeroOl 2023-06-03 01:26:07 -05:00
parent 957def8e42
commit 20b9094592
7 changed files with 60 additions and 22 deletions

View File

@ -20,6 +20,14 @@ user code."
" ...)"))
(if docstring (.. "\n" docstring) "")))
(fn metadata-format [{: binding : metadata}]
"formats a special using its builtin metadata magic"
(..
(code-block
(.. "(" (tostring binding) " " (table.concat metadata.fnl/arglist " ") ")"))
"\n"
metadata.fnl/docstring))
(λ fn? [symbol]
(if (sym? symbol)
(let [name (tostring symbol)]
@ -27,14 +35,14 @@ user code."
(= name "λ")
(= name "lambda")))))
(λ analyze-fn [ast]
(λ analyze-fn [?ast]
"if ast is a function definition, try to fill out as much of this as possible:
{: name
: arglist
: docstring
: fntype}
fntype is one of fn or λ or lambda"
(case ast
(case ?ast
;; name + docstring
(where [fntype name arglist docstring _body]
(fn? fntype)
@ -66,13 +74,16 @@ fntype is one of fn or λ or lambda"
:value
(case (analyze-fn result.definition)
{:fntype ?fntype :name ?name :arglist ?arglist :docstring ?docstring} (fn-format ?fntype ?name ?arglist ?docstring)
_ (code-block
(if (-?>> result.keys length (< 0))
_ (if (-?>> result.keys length (< 0))
(code-block
(.. "ERROR, I don't know how to show this "
"(. "
(view result.definition {:prefer-colon? true}) " "
(view result.keys {:prefer-colon? true}) ")")
(view result.definition {:prefer-colon? true}))))})
(view result.keys {:prefer-colon? true}) ")"))
result.metadata
(metadata-format result)
(code-block
(view result.definition {:prefer-colon? true}))))})
;; CompletionItemKind
(local kinds

View File

@ -140,7 +140,7 @@ Every time the client sends a message, it gets handled by a function in the corr
(λ scope-completion [self file byte ?symbol parents]
(let [scope (or (accumulate [result nil
i parent (ipairs parents)
_ parent (ipairs parents)
&until result]
(. file.scopes parent))
file.scope)
@ -150,7 +150,7 @@ Every time the client sends a message, it gets handled by a function in the corr
(collect-scope scope :manglings #(make-completion-item self file $ scope) result)
(when in-call-position?
(collect-scope scope :macros #{:label $ :kind kinds.Keyword} result)
(collect-scope scope :specials #{:label $ :kind kinds.Keyword} result))
(collect-scope scope :specials #(make-completion-item self file $ scope) result))
(icollect [_ k (ipairs file.allowed-globals) &into result]
{:label k :kind kinds.Variable})))
@ -162,9 +162,9 @@ Every time the client sends a message, it gets handled by a function in the corr
(case (language.search-assignment self file ref stack {})
{: definition}
(case (values definition (type definition))
(_str :string) (icollect [k v (pairs string)]
(_str :string) (icollect [k _ (pairs string)]
{:label k :kind kinds.Field})
(tbl :table) (icollect [k v (pairs tbl)]
(tbl :table) (icollect [k _ (pairs tbl)]
(if (= (type k) :string)
{:label k :kind kinds.Field})))
_ nil))))

View File

@ -90,6 +90,10 @@ the data provided by compiler.fnl."
(= 0 (length stack)) {:definition item} ;; BASE CASE !!
(error (.. "I don't know what to do with " (view item))))))
(local {:metadata METADATA
:scopes {:global {:specials SPECIALS}}}
(require :fennel.compiler))
(λ search-main [self file symbol opts ?byte]
"Find the definition of a symbol
@ -125,11 +129,8 @@ Returns:
(if (sym? symbol)
(let [split (utils.multi-sym-split symbol (if ?byte (+ 1 (- ?byte symbol.bytestart))))
stack (stack-add-split! [] split)]
(local {:metadata METADATA
:scopes {:global {:specials SPECIALS}}}
(require :fennel.compiler))
(case (. METADATA (. SPECIALS (tostring symbol)))
metadata {: metadata}
metadata {:binding symbol : metadata}
_ (case (. file.references symbol)
ref (search-assignment self file ref stack opts)
_ (case (. file.definitions symbol)
@ -141,9 +142,13 @@ Returns:
(find-local-definition file name ?scope.parent))))
(λ search-name-and-scope [self file name scope ?opts]
"find a definition just from the name of the item, and the scope it is in"
(assert (= (type name) :string))
(let [stack (stack-add-multisym! [] name)]
(case (find-local-definition file name scope)
def (search self file def.definition (stack-add-keys! stack def.keys) (or ?opts {})))))
(case (. METADATA (. SPECIALS name))
metadata {:binding (sym name) : metadata}
_ (case (find-local-definition file name scope)
def (search self file def.definition (stack-add-keys! stack def.keys) (or ?opts {}))))))
(λ past? [?ast byte]
;; check if a byte is past an ast object

View File

@ -119,6 +119,25 @@
(assert completion.kind "completion kind should be present")
(assert completion.documentation "completion documentation should be present")))))
; (it "offers rich information about global completions"
; (let [client (doto (create-client)
; (: :open-file! filename "("))
; [{:result completions}] (client:completion filename 0 1)
; _ (table.sort completions #(< $1.label $2.label))
; count (accumulate [sum 0 _ completion (ipairs completions)]
; (if (and (= (type completion.label) :string)
; (= (type completion.kind) :number)
; (= (type completion.documentation) :table))
; (+ sum 1)
; sum))]
; (if (< count (length completions))
; (print "!!!" (- (length completions) count) "items are missing a label or kind or doc"))
; (each [_ completion (ipairs completions)]
; (is.same (type completion.label) :string "unlabeled completion")
; (is.same (type completion.kind) :number (.. completion.label " needs a kind"))
; (is.same (type completion.documentation) :table (.. completion.label " needs documentation")))))))
;; (it "offers rich information about macro completions")
;; (it "offers rich information about variable completions")
;; (it "offers rich information about field completions")

View File

@ -49,4 +49,7 @@
(is-matching message [{:jsonrpc "2.0" :id 2}] "")))
(it "can go backward through (case)"
(check "hover.fnl" 22 22 "```fnl\n{:AB :CD}\n```")))
(check "hover.fnl" 22 22 "```fnl\n{:AB :CD}\n```"))
(it "hovers over a special"
(check "hover.fnl" 5 2 "```fnl\n(let [name1 val1 ... nameN valN] ...)\n```\nIntroduces a new scope in which a given set of local bindings are used.")))

View File

@ -2,8 +2,8 @@
(local {: view} (require :fennel))
(local {: expect} (require :test.lust))
;; lust uses weird terminology, but equal is by __eq, same is by recursively having the same contents
(setmetatable {:equal #(do ((. (expect $1) :to :be) $2) true)
:same #(do ((. (expect $1) :to :equal) $2) true)
(setmetatable {:equal #(do ((. (expect $1) :to :be) $2) true)
:same #(do ((. (expect $1) :to :equal) $2 $3) true)
:nil #(do ((. (expect $1) :to_not :exist)) true)
:not {:nil #(do ((. (expect $1) :to :exist)) true)}
:truthy #(do ((. (expect $1) :to :be :truthy)) true)}

View File

@ -147,10 +147,10 @@ local paths = {
end
},
equal = {
test = function(v, x)
test = function(v, x, message)
return strict_eq(v, x),
'expected ' .. tostring(v) .. ' and ' .. tostring(x) .. ' to be exactly equal',
'expected ' .. tostring(v) .. ' and ' .. tostring(x) .. ' to not be exactly equal'
message or 'expected ' .. tostring(v) .. ' and ' .. tostring(x) .. ' to be exactly equal',
message or 'expected ' .. tostring(v) .. ' and ' .. tostring(x) .. ' to not be exactly equal'
end
},
have = {