First attempt at code actions, no tests yet
This commit is contained in:
parent
ca2dbf9237
commit
7c81ffcbb3
@ -10,6 +10,7 @@
|
||||
:method "initialize"
|
||||
:params {:capabilities {:general {:positionEncodings [:utf-8]}}
|
||||
:clientInfo {:name "fennel-ls"}
|
||||
; " don't think this is a valid URI, but I want to operate in the current directory
|
||||
:rootUri "file://."}}))]
|
||||
(var should-err? false)
|
||||
(each [_ filename (ipairs filenames)]
|
||||
|
||||
@ -9,6 +9,10 @@ the `file.diagnostics` field, filling it with diagnostics."
|
||||
(local {:scopes {:global {: specials}}}
|
||||
(require :fennel.compiler))
|
||||
|
||||
(local diagnostic-mt {:__json_exclude_keys {:quickfix true}})
|
||||
(fn diagnostic [self]
|
||||
(setmetatable self diagnostic-mt))
|
||||
|
||||
(local ops {"+" 1 "-" 1 "*" 1 "/" 1 "//" 1 "%" 1 "^" 1 ">" 1 "<" 1 ">=" 1 "<=" 1 "=" 1 "not=" 1 ".." 1 "." 1 "and" 1 "or" 1 "band" 1 "bor" 1 "bxor" 1 "bnot" 1 "lshift" 1 "rshift" 1})
|
||||
(fn special? [item]
|
||||
(and (sym? item)
|
||||
@ -28,11 +32,14 @@ the `file.diagnostics` field, filling it with diagnostics."
|
||||
&until reference]
|
||||
(or (= ref.ref-type :read)
|
||||
(= ref.ref-type :mutate)))))
|
||||
{:range (message.ast->range self file symbol)
|
||||
:message (.. "unused definition: " (tostring symbol))
|
||||
:severity message.severity.WARN
|
||||
:code 301
|
||||
:codeDescription "unused-definition"}))
|
||||
(diagnostic
|
||||
{:range (message.ast->range self file symbol)
|
||||
:message (.. "unused definition: " (tostring symbol))
|
||||
:severity message.severity.WARN
|
||||
:code 301
|
||||
:codeDescription "unused-definition"
|
||||
:quickfix #[{:range (message.ast->range symbol)
|
||||
:newText (.. "_" (tostring symbol))}]})))
|
||||
|
||||
(λ unknown-module-field [self file]
|
||||
"any multisym whose definition can't be found through a (require) call"
|
||||
@ -73,15 +80,21 @@ the `file.diagnostics` field, filling it with diagnostics."
|
||||
(sym? (. last-item 1) :table.unpack))
|
||||
(. file.lexical last-item)
|
||||
(. file.lexical call))
|
||||
{:range (message.ast->range self file last-item)
|
||||
:message (.. "faulty unpack call: " (tostring op) " isn't variadic at runtime."
|
||||
(if (sym? op "..")
|
||||
(let [unpackme (view (. last-item 2))]
|
||||
(.. " Use (table.concat " unpackme ") instead of (.. (unpack " unpackme "))"))
|
||||
(.. " Use a loop when you have a dynamic number of arguments to (" (tostring op) ")")))
|
||||
:severity message.severity.WARN
|
||||
:code 304
|
||||
:codeDescription "bad-unpack"})))
|
||||
(diagnostic
|
||||
{:range (message.ast->range self file last-item)
|
||||
:message (.. "faulty unpack call: " (tostring op) " isn't variadic at runtime."
|
||||
(if (sym? op "..")
|
||||
(let [unpackme (view (. last-item 2))]
|
||||
(.. " Use (table.concat " unpackme ") instead of (.. (unpack " unpackme "))"))
|
||||
(.. " Use a loop when you have a dynamic number of arguments to (" (tostring op) ")")))
|
||||
:severity message.severity.WARN
|
||||
:code 304
|
||||
:codeDescription "bad-unpack"
|
||||
:quickfix (if (and (= (length call) 2)
|
||||
(= (length (. call 2)) 2)
|
||||
(sym? op ".."))
|
||||
#[{:range (message.ast->range self file call)
|
||||
:newText (.. "(table.concat " (view (. call 2 2)) ")")}])}))))
|
||||
|
||||
(λ var-never-set [self file symbol definition]
|
||||
(if (and definition.var? (not definition.var-set) (. file.lexical symbol))
|
||||
@ -94,15 +107,23 @@ the `file.diagnostics` field, filling it with diagnostics."
|
||||
(local op-identity-value {:+ 0 :* 1 :and true :or false :band -1 :bor 0 :.. ""})
|
||||
(λ op-with-no-arguments [self file op call]
|
||||
"A call like (+) that could be replaced with a literal"
|
||||
(if (and (op? op)
|
||||
(not (. call 2))
|
||||
(. file.lexical call)
|
||||
(not= nil (. op-identity-value (tostring op))))
|
||||
{:range (message.ast->range self file call)
|
||||
:message (.. "write " (view (. op-identity-value (tostring op))) " instead of (" (tostring op) ")")
|
||||
:severity message.severity.WARN
|
||||
:code 306
|
||||
:codeDescription "op-with-no-arguments"}))
|
||||
(let [identity (. op-identity-value (tostring op))]
|
||||
(if (and (op? op)
|
||||
(not (. call 2))
|
||||
(. file.lexical call)
|
||||
(not= nil identity))
|
||||
(diagnostic
|
||||
{:range (message.ast->range self file call)
|
||||
:message (.. "write " (view identity) " instead of (" (tostring op) ")")
|
||||
:severity message.severity.WARN
|
||||
:code 306
|
||||
:codeDescription "op-with-no-arguments"
|
||||
:quickfix #[{:range (message.ast->range self file call)
|
||||
:newText (view identity)}]}))))
|
||||
|
||||
; (fn quickfix.op-with-no-arguments [item]
|
||||
; {:range item.range
|
||||
; :newText (tostring item.data)})
|
||||
|
||||
(λ multival-in-middle-of-call [self file fun call arg index]
|
||||
"generally, values and unpack are signs that the user is trying to do
|
||||
|
||||
@ -11,7 +11,6 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
(local language (require :fennel-ls.language))
|
||||
(local formatter (require :fennel-ls.formatter))
|
||||
(local utils (require :fennel-ls.utils))
|
||||
|
||||
(local fennel (require :fennel))
|
||||
|
||||
(local requests [])
|
||||
@ -33,7 +32,7 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
:referencesProvider {:workDoneProgress false}
|
||||
;; :documentHighlightProvider nil
|
||||
;; :documentSymbolProvider nil
|
||||
;; :codeActionProvider nil
|
||||
:codeActionProvider {:workDoneProgress false}
|
||||
;; :codeLensProvider nil
|
||||
;; :documentLinkProvider nil
|
||||
;; :colorProvider nil
|
||||
@ -229,6 +228,23 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
{:changes {definition.file.uri usages}})
|
||||
(catch _ nil))))
|
||||
|
||||
(fn pos<= [pos-1 pos-2]
|
||||
(or (< pos-1.line pos-2.line)
|
||||
(and (= pos-1.line pos-2.line)
|
||||
(<= pos-1.character pos-2.character))))
|
||||
|
||||
(fn overlap? [range-1 range-2]
|
||||
(and (pos<= range-1.start range-2.end)
|
||||
(pos<= range-2.start range-1.end)))
|
||||
|
||||
(λ requests.textDocument/codeAction [self send {: range :textDocument {: uri} &as params}]
|
||||
(let [file (state.get-by-uri self uri)]
|
||||
(icollect [_ diagnostic (ipairs file.diagnostics)]
|
||||
(if (and (overlap? diagnostic.range range)
|
||||
diagnostic.quickfix)
|
||||
{:title diagnostic.codeDescription
|
||||
:edit {:changes {uri (diagnostic.quickfix)}}}))))
|
||||
|
||||
(λ notifications.textDocument/didChange [self send {: contentChanges :textDocument {: uri}}]
|
||||
(local file (state.get-by-uri self uri))
|
||||
(state.set-uri-contents self uri (utils.apply-changes file.text contentChanges self.position-encoding))
|
||||
@ -243,12 +259,12 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
|
||||
(λ notifications.textDocument/didSave [self send {:textDocument {: uri}}]
|
||||
;; TODO be careful about which modules need to be recomputed, and also eagerly flush existing files
|
||||
(tset (require :fennel) :macro-loaded []))
|
||||
(set fennel.macro-loaded []))
|
||||
|
||||
(λ notifications.textDocument/didClose [self send {:textDocument {: uri}}]
|
||||
(local file (state.get-by-uri self uri))
|
||||
(set file.open? false)
|
||||
(tset (require :fennel) :macro-loaded [])
|
||||
(set fennel.macro-loaded [])
|
||||
;; TODO only reload from disk if we didn't get a didSave, instead of always
|
||||
(state.flush-uri self uri))
|
||||
|
||||
|
||||
15
test/code-action.fnl
Normal file
15
test/code-action.fnl
Normal file
@ -0,0 +1,15 @@
|
||||
(local _faith (require :faith))
|
||||
(local {: view} (require :fennel))
|
||||
(local {: create-client-with-files} (require :test.utils))
|
||||
|
||||
(fn check [file-contents]
|
||||
(let [{: self : uri :locations [range]} (create-client-with-files file-contents)
|
||||
response (self:code-action uri range.range)]
|
||||
;; (print "hello" (view response))))
|
||||
nil))
|
||||
|
||||
(fn test-thing []
|
||||
(check "(+====)"
|
||||
"op-with-no-arguments"))
|
||||
|
||||
{: test-thing}
|
||||
@ -21,5 +21,6 @@
|
||||
:test.completion
|
||||
:test.references
|
||||
:test.diagnostic
|
||||
:test.code-action
|
||||
:test.rename
|
||||
:test.misc])
|
||||
|
||||
@ -86,6 +86,13 @@
|
||||
:textDocument {:uri file}
|
||||
: newName})))
|
||||
|
||||
(fn code-action [self file range]
|
||||
(dispatch.handle* self.server
|
||||
(message.create-request (next-id! self) :textDocument/codeAction
|
||||
{: range
|
||||
:textDocument {:uri file}
|
||||
:context {:diagnostics []}})))
|
||||
|
||||
(set mt.__index
|
||||
{: open-file!
|
||||
: pretend-this-file-exists!
|
||||
@ -93,7 +100,8 @@
|
||||
: definition
|
||||
: hover
|
||||
: references
|
||||
: rename})
|
||||
: rename
|
||||
: code-action})
|
||||
|
||||
{: create-client
|
||||
: default-encoding
|
||||
|
||||
Loading…
Reference in New Issue
Block a user