clean up quickfix lint title system
This commit is contained in:
parent
623700e1a2
commit
fc57612fea
@ -108,7 +108,7 @@ Every time the client sends a message, it gets handled by a function in the corr
|
|||||||
symbol
|
symbol
|
||||||
(analyzer.find-nearest-definition server this-file symbol byte)
|
(analyzer.find-nearest-definition server this-file symbol byte)
|
||||||
{: referenced-by :file {:uri this-file.uri &as file} : binding}
|
{: referenced-by :file {:uri this-file.uri &as file} : binding}
|
||||||
(let [result (icollect [_ {:symbol reference} (ipairs referenced-by)]
|
(let [result (icollect [_ {:symbol reference} (ipairs referenced-by) &into (message.array)]
|
||||||
{:range (message.ast->range server file reference)
|
{:range (message.ast->range server file reference)
|
||||||
:kind documentHighlightKind.Read})]
|
:kind documentHighlightKind.Read})]
|
||||||
(table.insert result {:range (message.ast->range server file binding)
|
(table.insert result {:range (message.ast->range server file binding)
|
||||||
@ -142,12 +142,12 @@ Every time the client sends a message, it gets handled by a function in the corr
|
|||||||
(let [file (files.get-by-uri server uri)
|
(let [file (files.get-by-uri server uri)
|
||||||
byte (utils.position->byte file.text position server.position-encoding)]
|
byte (utils.position->byte file.text position server.position-encoding)]
|
||||||
(case-try (analyzer.find-nearest-call server file byte)
|
(case-try (analyzer.find-nearest-call server file byte)
|
||||||
(symbol active-parameter)
|
(call active-parameter)
|
||||||
(analyzer.find-definition server file symbol)
|
(analyzer.find-definition server file call)
|
||||||
{:indeterminate nil &as definition}
|
{:indeterminate nil &as definition}
|
||||||
(formatter.signature-help-format definition)
|
(formatter.signature-help-format definition)
|
||||||
signature
|
signature
|
||||||
(message.symbol->signature-help server file symbol
|
(message.call->signature-help server file call
|
||||||
signature
|
signature
|
||||||
active-parameter)
|
active-parameter)
|
||||||
(catch _ nil))))
|
(catch _ nil))))
|
||||||
@ -207,9 +207,8 @@ Every time the client sends a message, it gets handled by a function in the corr
|
|||||||
|
|
||||||
(λ requests.textDocument/codeAction [server _send {: range :textDocument {: uri}}]
|
(λ requests.textDocument/codeAction [server _send {: range :textDocument {: uri}}]
|
||||||
(let [file (files.get-by-uri server uri)]
|
(let [file (files.get-by-uri server uri)]
|
||||||
(icollect [_ diagnostic (ipairs file.diagnostics)]
|
(icollect [_ diagnostic (ipairs file.diagnostics) &into (message.array)]
|
||||||
(if (and (overlap? diagnostic.range range)
|
(if (overlap? diagnostic.range range)
|
||||||
diagnostic.quickfix)
|
|
||||||
(message.diagnostic->code-action server file diagnostic :quickfix)))))
|
(message.diagnostic->code-action server file diagnostic :quickfix)))))
|
||||||
|
|
||||||
(λ notifications.textDocument/didChange [server send {: contentChanges :textDocument {: uri}}]
|
(λ notifications.textDocument/didChange [server send {: contentChanges :textDocument {: uri}}]
|
||||||
|
|||||||
@ -13,8 +13,10 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
(local diagnostic-mt {:__tojson (fn [{: self} state] (dkjson.encode self state))
|
(local diagnostic-mt {:__tojson (fn [{: self} state] (dkjson.encode self state))
|
||||||
:__index #(. $1 :self $2)})
|
:__index #(. $1 :self $2)})
|
||||||
|
|
||||||
(fn diagnostic [self quickfix]
|
(fn diagnostic [self]
|
||||||
(setmetatable {: self : quickfix} diagnostic-mt))
|
(let [fix self.fix]
|
||||||
|
(set self.fix nil)
|
||||||
|
(setmetatable {: self : fix} diagnostic-mt)))
|
||||||
|
|
||||||
(fn could-be-rewritten-as-sym? [str]
|
(fn could-be-rewritten-as-sym? [str]
|
||||||
(and (= :string (type str)) (not (str:find "^%d"))
|
(and (= :string (type str)) (not (str:find "^%d"))
|
||||||
@ -33,9 +35,10 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
{:range (message.ast->range server file symbol)
|
{:range (message.ast->range server file symbol)
|
||||||
:message (.. "unused definition: " (tostring symbol))
|
:message (.. "unused definition: " (tostring symbol))
|
||||||
:severity message.severity.WARN
|
:severity message.severity.WARN
|
||||||
:code :unused-definition}
|
:code :unused-definition
|
||||||
#[{:range (message.ast->range server file symbol)
|
:fix #{:title (.. "Replace " (tostring symbol) " with _" (tostring symbol))
|
||||||
:newText (.. "_" (tostring symbol))}])))
|
:changes [{:range (message.ast->range server file symbol)
|
||||||
|
:newText (.. "_" (tostring symbol))}]}})))
|
||||||
|
|
||||||
;; this is way too specific; it's also safe to do this inside an `if` or `case`
|
;; this is way too specific; it's also safe to do this inside an `if` or `case`
|
||||||
(fn in-or? [calls symbol]
|
(fn in-or? [calls symbol]
|
||||||
@ -107,9 +110,10 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
(diagnostic {:range (message.ast->range server file call)
|
(diagnostic {:range (message.ast->range server file call)
|
||||||
:message (.. "unnecessary " (tostring head))
|
:message (.. "unnecessary " (tostring head))
|
||||||
:severity message.severity.WARN
|
:severity message.severity.WARN
|
||||||
:code :unnecessary-tset}
|
:code :unnecessary-tset
|
||||||
#[{:range (message.ast->range server file call)
|
:fix #{:title "Replace tset with set"
|
||||||
:newText (make-new-text call)}])))
|
:changes [{:range (message.ast->range server file call)
|
||||||
|
:newText (make-new-text call)}]}})))
|
||||||
|
|
||||||
(λ unnecessary-do-values [server file head call]
|
(λ unnecessary-do-values [server file head call]
|
||||||
(if (and (or (sym? head :do) (sym? head :values))
|
(if (and (or (sym? head :do) (sym? head :values))
|
||||||
@ -117,26 +121,31 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
(diagnostic {:range (message.ast->range server file call)
|
(diagnostic {:range (message.ast->range server file call)
|
||||||
:message (.. "unnecessary " (tostring head))
|
:message (.. "unnecessary " (tostring head))
|
||||||
:severity message.severity.WARN
|
:severity message.severity.WARN
|
||||||
:code :unnecessary-do-values}
|
:code :unnecessary-do-values
|
||||||
#[{:range (message.ast->range server file call)
|
:fix #{:title "Unwrap the expression"
|
||||||
:newText (view (. call 2))}])))
|
:changes [{:range (message.ast->range server file call)
|
||||||
|
:newText (view (. call 2))}]}})))
|
||||||
|
|
||||||
(local implicit-do-forms (collect [form {: body-form?} (pairs (fennel.syntax))]
|
(local implicit-do-forms (collect [form {: body-form?} (pairs (fennel.syntax))]
|
||||||
(values form body-form?)))
|
(values form body-form?)))
|
||||||
|
|
||||||
(λ redundant-do [server file head call]
|
(λ redundant-do [server file head call]
|
||||||
(let [last-body (. call (length call))]
|
(let [last-body (. call (length call))]
|
||||||
(if (and (. implicit-do-forms (tostring head)) (. file.lexical call)
|
(if (and (. implicit-do-forms (tostring head))
|
||||||
(list? last-body) (sym? (. last-body 1) :do))
|
(. file.lexical call)
|
||||||
|
(list? last-body)
|
||||||
|
(sym? (. last-body 1) :do)
|
||||||
|
(not (unnecessary-do-values server file head call))) ;; we don't want two lints to trigger for same call
|
||||||
(diagnostic {:range (message.ast->range server file last-body)
|
(diagnostic {:range (message.ast->range server file last-body)
|
||||||
:message "redundant do"
|
:message "redundant do"
|
||||||
:severity message.severity.WARN
|
:severity message.severity.WARN
|
||||||
:code :redundant-do}
|
:code :redundant-do
|
||||||
#[{:range (message.ast->range server file last-body)
|
:fix #{:title "Unwrap the expression"
|
||||||
:newText (table.concat
|
:changes [{:range (message.ast->range server file last-body)
|
||||||
(fcollect [i 2 (length last-body)]
|
:newText (table.concat
|
||||||
(view (. last-body i)))
|
(fcollect [i 2 (length last-body)]
|
||||||
" ")}]))))
|
(view (. last-body i)))
|
||||||
|
" ")}]}}))))
|
||||||
|
|
||||||
(λ bad-unpack [server file op call]
|
(λ bad-unpack [server file op call]
|
||||||
"an unpack call leading into an operator"
|
"an unpack call leading into an operator"
|
||||||
@ -160,12 +169,12 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
(.. " Use a loop when you have a dynamic number of "
|
(.. " Use a loop when you have a dynamic number of "
|
||||||
"arguments to (" (tostring op) ")")))
|
"arguments to (" (tostring op) ")")))
|
||||||
:severity message.severity.WARN
|
:severity message.severity.WARN
|
||||||
:code :bad-unpack}
|
:code :bad-unpack
|
||||||
(if (and (= (length call) 2)
|
:fix (if (and (= (length last-item) 2)
|
||||||
(= (length (. call 2)) 2)
|
(sym? op ".."))
|
||||||
(sym? op ".."))
|
#{:title "Replace with a call to table.concat"
|
||||||
#[{:range (message.ast->range server file call)
|
:changes [{:range (message.ast->range server file (if (= 2 (length call)) call last-item))
|
||||||
:newText (.. "(table.concat " (view (. call 2 2)) ")")}])))))
|
:newText (.. "(table.concat " (view (. last-item 2)) ")")}]})}))))
|
||||||
|
|
||||||
(λ var-never-set [server file symbol definition]
|
(λ var-never-set [server file symbol definition]
|
||||||
(if (and definition.var? (not definition.var-set) (. file.lexical symbol))
|
(if (and definition.var? (not definition.var-set) (. file.lexical symbol))
|
||||||
@ -189,9 +198,10 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
{:range (message.ast->range server file call)
|
{:range (message.ast->range server file call)
|
||||||
:message (.. "write " (view identity) " instead of (" (tostring op) ")")
|
:message (.. "write " (view identity) " instead of (" (tostring op) ")")
|
||||||
:severity message.severity.WARN
|
:severity message.severity.WARN
|
||||||
:code :op-with-no-arguments}
|
:code :op-with-no-arguments
|
||||||
#[{:range (message.ast->range server file call)
|
:fix #{:title (.. "Replace (" (tostring op) ") with " (view identity))
|
||||||
:newText (view identity)}]))))
|
:changes [{:range (message.ast->range server file call)
|
||||||
|
:newText (view identity)}]}}))))
|
||||||
|
|
||||||
(λ no-decreasing-comparison [server file op call]
|
(λ no-decreasing-comparison [server file op call]
|
||||||
(if (or (sym? op :>) (sym? op :>=))
|
(if (or (sym? op :>) (sym? op :>=))
|
||||||
@ -199,13 +209,14 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
{:range (message.ast->range server file call)
|
{:range (message.ast->range server file call)
|
||||||
:message "Use increasing operator instead of decreasing"
|
:message "Use increasing operator instead of decreasing"
|
||||||
:severity message.severity.WARN
|
:severity message.severity.WARN
|
||||||
:code :no-decreasing-comparison}
|
:code :no-decreasing-comparison
|
||||||
#[{:range (message.ast->range server file call)
|
:fix #{:title "Reverse the comparison"
|
||||||
:newText (let [new (if (sym? op :>=) (fennel.sym :<=) (fennel.sym :<))
|
:changes [{:range (message.ast->range server file call)
|
||||||
reversed (fcollect [i (length call) 2 -1
|
:newText (let [new (if (sym? op :>=) (fennel.sym :<=) (fennel.sym :<))
|
||||||
&into (list (sym new))]
|
reversed (fcollect [i (length call) 2 -1
|
||||||
(. call i))]
|
&into (list (sym new))]
|
||||||
(view reversed))}])))
|
(. call i))]
|
||||||
|
(view reversed))}]}})))
|
||||||
|
|
||||||
(λ match-reference? [ast references]
|
(λ match-reference? [ast references]
|
||||||
(if (sym? ast) (?. references ast :target)
|
(if (sym? ast) (?. references ast :target)
|
||||||
@ -221,9 +232,10 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
(diagnostic {:range (message.ast->range server file (. ast 1))
|
(diagnostic {:range (message.ast->range server file (. ast 1))
|
||||||
:message "no pinned patterns; use case instead of match"
|
:message "no pinned patterns; use case instead of match"
|
||||||
:severity message.severity.WARN
|
:severity message.severity.WARN
|
||||||
:code :match-should-case}
|
:code :match-should-case
|
||||||
#[{:range (message.ast->range server file (. ast 1))
|
:fix #{:title "Replace match with case"
|
||||||
:newText "case"}])))
|
:changes [{:range (message.ast->range server file (. ast 1))
|
||||||
|
:newText "case"}]}})))
|
||||||
|
|
||||||
(λ multival-in-middle-of-call [server file fun call arg index]
|
(λ multival-in-middle-of-call [server file fun call arg index]
|
||||||
"generally, values and unpack are signs that the user is trying to do
|
"generally, values and unpack are signs that the user is trying to do
|
||||||
@ -252,11 +264,12 @@ the `file.diagnostics` field, filling it with diagnostics."
|
|||||||
(diagnostic {:range (message.ast->range server file binding)
|
(diagnostic {:range (message.ast->range server file binding)
|
||||||
:message "use do instead of let with no bindings"
|
:message "use do instead of let with no bindings"
|
||||||
:severity message.severity.WARN
|
:severity message.severity.WARN
|
||||||
:code :empty-let}
|
:code :empty-let
|
||||||
#[(let [{: start} (message.ast->range server file let*)
|
:fix #{:title "Replace (let [] ...) with (do ...)"
|
||||||
{: end} (message.ast->range server file binding)]
|
:changes [(let [{: start} (message.ast->range server file let*)
|
||||||
{:range {: start : end}
|
{: end} (message.ast->range server file binding)]
|
||||||
:newText "do"})])))
|
{:range {: start : end}
|
||||||
|
:newText "do"})]}})))
|
||||||
|
|
||||||
(λ add-lint-diagnostics [server file]
|
(λ add-lint-diagnostics [server file]
|
||||||
"fill up the file.diagnostics table with linting things"
|
"fill up the file.diagnostics table with linting things"
|
||||||
|
|||||||
@ -9,6 +9,8 @@ LSP json objects."
|
|||||||
(local utils (require :fennel-ls.utils))
|
(local utils (require :fennel-ls.utils))
|
||||||
(local json (require :dkjson))
|
(local json (require :dkjson))
|
||||||
|
|
||||||
|
(local json-array-mt {:__jsontype :array})
|
||||||
|
|
||||||
(λ nullify [?value]
|
(λ nullify [?value]
|
||||||
(case ?value
|
(case ?value
|
||||||
nil json.null
|
nil json.null
|
||||||
@ -73,25 +75,18 @@ LSP json objects."
|
|||||||
:end (utils.byte->position file.text (+ byteend 1)
|
:end (utils.byte->position file.text (+ byteend 1)
|
||||||
server.position-encoding)}))
|
server.position-encoding)}))
|
||||||
|
|
||||||
(λ code-action-title [diag]
|
(λ array [?t]
|
||||||
(let [titles {:match-should-case "Replace match with case"
|
(setmetatable (or ?t []) json-array-mt))
|
||||||
:unused-definition "Prefix with _ to silence warning"
|
|
||||||
:op-with-no-arguments "Replace with the corresponding literal"
|
|
||||||
:no-decreasing-comparison "Reverse comparison"
|
|
||||||
:unnecessary-tset "Replace with set"
|
|
||||||
:unnecessary-do-values "Remove unnecessary do or values"
|
|
||||||
:redundant-do "Remove redundant do"
|
|
||||||
:bad-unpack "Replace with table.concat call"}]
|
|
||||||
(or (. titles diag.code)
|
|
||||||
(.. "Action title missing - " diag.code))))
|
|
||||||
|
|
||||||
(λ diagnostic->code-action [_server file diagnostic ?kind]
|
(λ diagnostic->code-action [_server {: uri} diagnostic ?kind]
|
||||||
(let [{: uri} file]
|
(case-try diagnostic.fix
|
||||||
{:title (code-action-title diagnostic)
|
fix (fix)
|
||||||
:kind ?kind
|
{: title : changes} {: title
|
||||||
:edit {:changes {uri (diagnostic.quickfix)}}}))
|
:kind ?kind
|
||||||
|
:diagnostics [diagnostic]
|
||||||
|
:edit {:changes {uri (array changes)}}}))
|
||||||
|
|
||||||
(λ symbol->signature-help [_server _file _call signature active-parameter]
|
(λ call->signature-help [_server _file _call signature active-parameter]
|
||||||
(let [params-count (length signature.parameters)]
|
(let [params-count (length signature.parameters)]
|
||||||
{:signatures [signature]
|
{:signatures [signature]
|
||||||
:activeSignature 0 ; we only ever have one signature
|
:activeSignature 0 ; we only ever have one signature
|
||||||
@ -101,8 +96,7 @@ LSP json objects."
|
|||||||
active-parameter)}))
|
active-parameter)}))
|
||||||
|
|
||||||
(λ multisym->range [server file ast n]
|
(λ multisym->range [server file ast n]
|
||||||
(let [spl (utils.multi-sym-split ast)
|
(let [spl (utils.multi-sym-split ast)]
|
||||||
n (if (< n 0) (+ n 1 (length spl)) n)]
|
|
||||||
(case (values (utils.get-ast-info ast :bytestart)
|
(case (values (utils.get-ast-info ast :bytestart)
|
||||||
(utils.get-ast-info ast :byteend))
|
(utils.get-ast-info ast :byteend))
|
||||||
(bytestart byteend)
|
(bytestart byteend)
|
||||||
@ -138,10 +132,11 @@ LSP json objects."
|
|||||||
: create-error
|
: create-error
|
||||||
: ast->range
|
: ast->range
|
||||||
: diagnostic->code-action
|
: diagnostic->code-action
|
||||||
: symbol->signature-help
|
: call->signature-help
|
||||||
: multisym->range
|
: multisym->range
|
||||||
: range-and-uri
|
: range-and-uri
|
||||||
: diagnostics
|
: diagnostics
|
||||||
: severity
|
: severity
|
||||||
: severity->string
|
: severity->string
|
||||||
: show-message}
|
: show-message
|
||||||
|
: array}
|
||||||
|
|||||||
@ -33,32 +33,47 @@
|
|||||||
(fn test-fix-op-no-arguments []
|
(fn test-fix-op-no-arguments []
|
||||||
(check "(let [x (+====)]
|
(check "(let [x (+====)]
|
||||||
(print x))"
|
(print x))"
|
||||||
"Replace with the corresponding literal"
|
"Replace (+) with 0"
|
||||||
"(let [x 0]
|
"(let [x 0]
|
||||||
(print x))"))
|
(print x))")
|
||||||
|
nil)
|
||||||
|
|
||||||
(fn test-fix-unused-definition []
|
(fn test-fix-unused-definition []
|
||||||
(check "(local x==== 10)"
|
(check "(local x==== 10)"
|
||||||
"Prefix with _ to silence warning"
|
"Replace x with _x"
|
||||||
"(local _x 10)"))
|
"(local _x 10)")
|
||||||
|
nil)
|
||||||
|
|
||||||
(fn test-unnecessary-tset []
|
(fn test-unnecessary-tset []
|
||||||
(check "==(tset state :mouse 496)=="
|
(check "==(tset state :mouse 496)=="
|
||||||
"Replace with set"
|
"Replace tset with set"
|
||||||
"(set state.mouse 496)")
|
"(set state.mouse 496)")
|
||||||
|
|
||||||
(check "==(tset state :mouse :cursor 496)=="
|
(check "==(tset state :mouse :cursor 496)=="
|
||||||
"Replace with set"
|
"Replace tset with set"
|
||||||
"(set state.mouse.cursor 496)")
|
"(set state.mouse.cursor 496)")
|
||||||
|
|
||||||
(check "==(tset state :mouse :cursor {:x 4 :y 7})=="
|
(check "==(tset state :mouse :cursor {:x 4 :y 7})=="
|
||||||
"Replace with set"
|
"Replace tset with set"
|
||||||
"(set state.mouse.cursor {:x 4 :y 7})")
|
"(set state.mouse.cursor {:x 4 :y 7})")
|
||||||
|
|
||||||
(check "==(tset state :mouse :cursor :x 496)=="
|
(check "==(tset state :mouse :cursor :x 496)=="
|
||||||
"Replace with set"
|
"Replace tset with set"
|
||||||
"(set state.mouse.cursor.x 496)"))
|
"(set state.mouse.cursor.x 496)")
|
||||||
|
nil)
|
||||||
|
|
||||||
|
(fn test-fix-unpack []
|
||||||
|
(check "(.. (table.unpack my-ta====ble))"
|
||||||
|
"Replace with a call to table.concat"
|
||||||
|
"(table.concat my-table)")
|
||||||
|
(check "(.. 1 2 3 (table.unpa====ck my-table))"
|
||||||
|
"Replace with a call to table.concat"
|
||||||
|
"(.. 1 2 3 (table.concat my-table))")
|
||||||
|
(check-negative "(+ 1 2 3 (table.unpa====ck my-table))"
|
||||||
|
"Replace with a call to table.concat")
|
||||||
|
nil)
|
||||||
|
|
||||||
{: test-fix-op-no-arguments
|
{: test-fix-op-no-arguments
|
||||||
: test-fix-unused-definition
|
: test-fix-unused-definition
|
||||||
: test-unnecessary-tset}
|
: test-unnecessary-tset
|
||||||
|
: test-fix-unpack}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user