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:
parent
957def8e42
commit
20b9094592
@ -20,6 +20,14 @@ user code."
|
|||||||
" ...)"))
|
" ...)"))
|
||||||
(if docstring (.. "\n" docstring) "")))
|
(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]
|
(λ fn? [symbol]
|
||||||
(if (sym? symbol)
|
(if (sym? symbol)
|
||||||
(let [name (tostring symbol)]
|
(let [name (tostring symbol)]
|
||||||
@ -27,14 +35,14 @@ user code."
|
|||||||
(= name "λ")
|
(= name "λ")
|
||||||
(= name "lambda")))))
|
(= name "lambda")))))
|
||||||
|
|
||||||
(λ analyze-fn [ast]
|
(λ analyze-fn [?ast]
|
||||||
"if ast is a function definition, try to fill out as much of this as possible:
|
"if ast is a function definition, try to fill out as much of this as possible:
|
||||||
{: name
|
{: name
|
||||||
: arglist
|
: arglist
|
||||||
: docstring
|
: docstring
|
||||||
: fntype}
|
: fntype}
|
||||||
fntype is one of fn or λ or lambda"
|
fntype is one of fn or λ or lambda"
|
||||||
(case ast
|
(case ?ast
|
||||||
;; name + docstring
|
;; name + docstring
|
||||||
(where [fntype name arglist docstring _body]
|
(where [fntype name arglist docstring _body]
|
||||||
(fn? fntype)
|
(fn? fntype)
|
||||||
@ -66,13 +74,16 @@ fntype is one of fn or λ or lambda"
|
|||||||
:value
|
:value
|
||||||
(case (analyze-fn result.definition)
|
(case (analyze-fn result.definition)
|
||||||
{:fntype ?fntype :name ?name :arglist ?arglist :docstring ?docstring} (fn-format ?fntype ?name ?arglist ?docstring)
|
{: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 "
|
(.. "ERROR, I don't know how to show this "
|
||||||
"(. "
|
"(. "
|
||||||
(view result.definition {:prefer-colon? true}) " "
|
(view result.definition {:prefer-colon? true}) " "
|
||||||
(view result.keys {:prefer-colon? true}) ")")
|
(view result.keys {:prefer-colon? true}) ")"))
|
||||||
(view result.definition {:prefer-colon? true}))))})
|
result.metadata
|
||||||
|
(metadata-format result)
|
||||||
|
(code-block
|
||||||
|
(view result.definition {:prefer-colon? true}))))})
|
||||||
|
|
||||||
;; CompletionItemKind
|
;; CompletionItemKind
|
||||||
(local kinds
|
(local kinds
|
||||||
|
|||||||
@ -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]
|
(λ scope-completion [self file byte ?symbol parents]
|
||||||
(let [scope (or (accumulate [result nil
|
(let [scope (or (accumulate [result nil
|
||||||
i parent (ipairs parents)
|
_ parent (ipairs parents)
|
||||||
&until result]
|
&until result]
|
||||||
(. file.scopes parent))
|
(. file.scopes parent))
|
||||||
file.scope)
|
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)
|
(collect-scope scope :manglings #(make-completion-item self file $ scope) result)
|
||||||
(when in-call-position?
|
(when in-call-position?
|
||||||
(collect-scope scope :macros #{:label $ :kind kinds.Keyword} result)
|
(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]
|
(icollect [_ k (ipairs file.allowed-globals) &into result]
|
||||||
{:label k :kind kinds.Variable})))
|
{: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 {})
|
(case (language.search-assignment self file ref stack {})
|
||||||
{: definition}
|
{: definition}
|
||||||
(case (values definition (type definition))
|
(case (values definition (type definition))
|
||||||
(_str :string) (icollect [k v (pairs string)]
|
(_str :string) (icollect [k _ (pairs string)]
|
||||||
{:label k :kind kinds.Field})
|
{:label k :kind kinds.Field})
|
||||||
(tbl :table) (icollect [k v (pairs tbl)]
|
(tbl :table) (icollect [k _ (pairs tbl)]
|
||||||
(if (= (type k) :string)
|
(if (= (type k) :string)
|
||||||
{:label k :kind kinds.Field})))
|
{:label k :kind kinds.Field})))
|
||||||
_ nil))))
|
_ nil))))
|
||||||
|
|||||||
@ -90,6 +90,10 @@ the data provided by compiler.fnl."
|
|||||||
(= 0 (length stack)) {:definition item} ;; BASE CASE !!
|
(= 0 (length stack)) {:definition item} ;; BASE CASE !!
|
||||||
(error (.. "I don't know what to do with " (view item))))))
|
(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]
|
(λ search-main [self file symbol opts ?byte]
|
||||||
"Find the definition of a symbol
|
"Find the definition of a symbol
|
||||||
|
|
||||||
@ -125,11 +129,8 @@ Returns:
|
|||||||
(if (sym? symbol)
|
(if (sym? symbol)
|
||||||
(let [split (utils.multi-sym-split symbol (if ?byte (+ 1 (- ?byte symbol.bytestart))))
|
(let [split (utils.multi-sym-split symbol (if ?byte (+ 1 (- ?byte symbol.bytestart))))
|
||||||
stack (stack-add-split! [] split)]
|
stack (stack-add-split! [] split)]
|
||||||
(local {:metadata METADATA
|
|
||||||
:scopes {:global {:specials SPECIALS}}}
|
|
||||||
(require :fennel.compiler))
|
|
||||||
(case (. METADATA (. SPECIALS (tostring symbol)))
|
(case (. METADATA (. SPECIALS (tostring symbol)))
|
||||||
metadata {: metadata}
|
metadata {:binding symbol : metadata}
|
||||||
_ (case (. file.references symbol)
|
_ (case (. file.references symbol)
|
||||||
ref (search-assignment self file ref stack opts)
|
ref (search-assignment self file ref stack opts)
|
||||||
_ (case (. file.definitions symbol)
|
_ (case (. file.definitions symbol)
|
||||||
@ -141,9 +142,13 @@ Returns:
|
|||||||
(find-local-definition file name ?scope.parent))))
|
(find-local-definition file name ?scope.parent))))
|
||||||
|
|
||||||
(λ search-name-and-scope [self file name scope ?opts]
|
(λ 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)]
|
(let [stack (stack-add-multisym! [] name)]
|
||||||
(case (find-local-definition file name scope)
|
(case (. METADATA (. SPECIALS name))
|
||||||
def (search self file def.definition (stack-add-keys! stack def.keys) (or ?opts {})))))
|
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]
|
(λ past? [?ast byte]
|
||||||
;; check if a byte is past an ast object
|
;; check if a byte is past an ast object
|
||||||
|
|||||||
@ -119,6 +119,25 @@
|
|||||||
(assert completion.kind "completion kind should be present")
|
(assert completion.kind "completion kind should be present")
|
||||||
(assert completion.documentation "completion documentation 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 macro completions")
|
||||||
;; (it "offers rich information about variable completions")
|
;; (it "offers rich information about variable completions")
|
||||||
;; (it "offers rich information about field completions")
|
;; (it "offers rich information about field completions")
|
||||||
|
|||||||
@ -49,4 +49,7 @@
|
|||||||
(is-matching message [{:jsonrpc "2.0" :id 2}] "")))
|
(is-matching message [{:jsonrpc "2.0" :id 2}] "")))
|
||||||
|
|
||||||
(it "can go backward through (case)"
|
(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.")))
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
(local {: view} (require :fennel))
|
(local {: view} (require :fennel))
|
||||||
(local {: expect} (require :test.lust))
|
(local {: expect} (require :test.lust))
|
||||||
;; lust uses weird terminology, but equal is by __eq, same is by recursively having the same contents
|
;; 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)
|
(setmetatable {:equal #(do ((. (expect $1) :to :be) $2) true)
|
||||||
:same #(do ((. (expect $1) :to :equal) $2) true)
|
:same #(do ((. (expect $1) :to :equal) $2 $3) true)
|
||||||
:nil #(do ((. (expect $1) :to_not :exist)) true)
|
:nil #(do ((. (expect $1) :to_not :exist)) true)
|
||||||
:not {:nil #(do ((. (expect $1) :to :exist)) true)}
|
:not {:nil #(do ((. (expect $1) :to :exist)) true)}
|
||||||
:truthy #(do ((. (expect $1) :to :be :truthy)) true)}
|
:truthy #(do ((. (expect $1) :to :be :truthy)) true)}
|
||||||
|
|||||||
@ -147,10 +147,10 @@ local paths = {
|
|||||||
end
|
end
|
||||||
},
|
},
|
||||||
equal = {
|
equal = {
|
||||||
test = function(v, x)
|
test = function(v, x, message)
|
||||||
return strict_eq(v, x),
|
return strict_eq(v, x),
|
||||||
'expected ' .. tostring(v) .. ' and ' .. tostring(x) .. ' to be exactly equal',
|
message or '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 not be exactly equal'
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
have = {
|
have = {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user