rewrite diagnostics to support pull diagnostics
This commit is contained in:
parent
c127c54937
commit
a366e5996d
@ -42,6 +42,7 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find
|
||||
(local {: get-ast-info &as utils} (require :fennel-ls.utils))
|
||||
(local files (require :fennel-ls.files))
|
||||
(local docs (require :fennel-ls.docs))
|
||||
(local compiler (require :fennel-ls.compiler))
|
||||
|
||||
(var search-multival nil) ;; all of the search functions are mutually recursive
|
||||
|
||||
@ -142,6 +143,7 @@ find the definition `10`, but if `opts.stop-early?` is set, it would find
|
||||
(when (= :string (type mod))
|
||||
(let [newfile (files.get-by-module server mod file.macro-file?)]
|
||||
(when newfile
|
||||
(compiler.compile server newfile)
|
||||
(let [newitem (. newfile.ast (length newfile.ast))]
|
||||
(when (= (length stack) 1)
|
||||
(set opts.searched-through-require-with-stack-size-1 true))
|
||||
@ -267,9 +269,10 @@ initialization-opts: {:stack ?list[ast]
|
||||
byte
|
||||
(+ 1 (get-ast-info ?ast :byteend))))))
|
||||
|
||||
(λ find-symbol [ast byte]
|
||||
(λ find-symbol [server file byte]
|
||||
"tries to find a sym, and a list of all of its parents/grandparents"
|
||||
(local parents [ast])
|
||||
(compiler.compile server file)
|
||||
(local parents [file.ast])
|
||||
(λ recurse [ast]
|
||||
(if
|
||||
(sym? ast)
|
||||
@ -292,40 +295,34 @@ initialization-opts: {:stack ?list[ast]
|
||||
(contains? value byte)
|
||||
(recurse value)))))))
|
||||
(values
|
||||
(accumulate [result nil _ top-level-form (ipairs ast) &until result]
|
||||
(accumulate [result nil _ top-level-form (ipairs file.ast) &until result]
|
||||
(if (contains? top-level-form byte)
|
||||
(recurse top-level-form)))
|
||||
(fcollect [i 1 (length parents)]
|
||||
(. parents (- (length parents) i -1)))))
|
||||
|
||||
(λ find-nearest-call [_server file byte]
|
||||
(λ find-nearest-call [server file byte]
|
||||
"Find the nearest call
|
||||
|
||||
returns the called symbol and the number of the argument closest to byte"
|
||||
(λ find-list [[call & parents]]
|
||||
(if (. file.calls call)
|
||||
call
|
||||
(next parents)
|
||||
(find-list parents)))
|
||||
(case-try (find-symbol server file byte)
|
||||
(_symbol parents) (accumulate [result nil _ v (ipairs parents) &until result]
|
||||
(if (. file.calls v)
|
||||
v))
|
||||
[callee &as call] (values callee ;; TODO: special handling for binding forms so we can point to the
|
||||
;; individual arguments in an each or accumulate call.
|
||||
;; Also need to split them up in formatter.fnl
|
||||
(faccumulate [index nil
|
||||
i (length call) 1 -1 &until index]
|
||||
(if (contains? (. call i) byte)
|
||||
; -2 because this is the 3rd element of the list, but
|
||||
; the 2nd argument to the call, and LSP is 0-indexed
|
||||
(- i 2)
|
||||
(past? (. call i) byte)
|
||||
; this means we are either at the end of the list or
|
||||
; inserting between two arguments
|
||||
(- i 1))))
|
||||
|
||||
(λ arg-index [call byte]
|
||||
;; TODO: special handling for binding forms so we can point to the
|
||||
;; individual arguments in an each or accumulate call.
|
||||
;; Also need to split them up in formatter.fnl
|
||||
(faccumulate [index nil
|
||||
i (length call) 1 -1 &until index]
|
||||
(if (contains? (. call i) byte)
|
||||
; -2 because this is the 3rd element of the list, but
|
||||
; the 2nd argument to the call, and LSP is 0-indexed
|
||||
(- i 2)
|
||||
(past? (. call i) byte)
|
||||
; this means we are either at the end of the list or
|
||||
; inserting between two arguments
|
||||
(- i 1))))
|
||||
|
||||
(case-try (find-symbol file.ast byte)
|
||||
(_symbol parents) (find-list parents)
|
||||
[callee &as call] (values callee (arg-index call byte))
|
||||
(catch _ nil)))
|
||||
|
||||
(λ find-definition [server file symbol ?byte]
|
||||
|
||||
@ -62,12 +62,12 @@ identifiers are declared / referenced in which places."
|
||||
position (utils.pos->position file.text line byte server.position-encoding)]
|
||||
{:start position :end position}))
|
||||
|
||||
(λ compile [{:configuration {: macro-path} :root-uri ?root-uri &as server} file]
|
||||
(λ do-compile [{:configuration {: macro-path} :root-uri ?root-uri &as server} file]
|
||||
"Compile the file, and record all the useful information from the compiler into the file object"
|
||||
;; The useful information being recorded:
|
||||
(let [definitions-by-scope (doto {} (setmetatable has-tables-mt))
|
||||
definitions {} ; symbol -> binding
|
||||
diagnostics {} ; [diagnostic]
|
||||
compile-diagnostics {} ; [diagnostic]
|
||||
references {} ; symbol -> references
|
||||
macro-refs {} ; symbol -> macro
|
||||
scopes {} ; ast -> scope
|
||||
@ -307,7 +307,7 @@ identifiers are declared / referenced in which places."
|
||||
(λ on-compile-error [_ msg ast call-me-to-reset-the-compiler]
|
||||
(let [range (or (message.ast->range server file ast)
|
||||
message.unknown-range)]
|
||||
(table.insert diagnostics
|
||||
(table.insert compile-diagnostics
|
||||
{:range range
|
||||
:message msg
|
||||
:severity message.severity.ERROR
|
||||
@ -321,7 +321,7 @@ identifiers are declared / referenced in which places."
|
||||
(λ on-parse-error [msg _filename line byte _source call-me-to-reset-the-compiler]
|
||||
(let [line (if (= line "?") 1 line)
|
||||
range (line+byte->range server file line byte)]
|
||||
(table.insert diagnostics
|
||||
(table.insert compile-diagnostics
|
||||
{:range range
|
||||
:message msg
|
||||
:severity message.severity.ERROR
|
||||
@ -334,7 +334,7 @@ identifiers are declared / referenced in which places."
|
||||
|
||||
(λ warn [msg ?ast _file ?line ?col]
|
||||
(let [range (or (message.ast->range server file ?ast) (line+byte->range server file (or ?line 1) (or ?col 0)))]
|
||||
(table.insert diagnostics
|
||||
(table.insert compile-diagnostics
|
||||
{:range range
|
||||
:message msg
|
||||
:severity message.severity.WARN
|
||||
@ -380,7 +380,7 @@ identifiers are declared / referenced in which places."
|
||||
(if (os.getenv :DEV)
|
||||
(error (.. "\nYou have crashed fennel-ls (or the fennel " component ") with the following message\n:" err
|
||||
"\n\n^^^ the error message above here is the root problem\n\n"))
|
||||
(table.insert diagnostics
|
||||
(table.insert compile-diagnostics
|
||||
{:range (line+byte->range server file 1 1)
|
||||
:message (.. "unrecoverable " component " error: " err)}))))
|
||||
|
||||
@ -434,7 +434,7 @@ identifiers are declared / referenced in which places."
|
||||
(fn hook []
|
||||
(debug.sethook nil nil)
|
||||
(if _G.jit (_G.jit.on))
|
||||
(table.insert diagnostics
|
||||
(table.insert compile-diagnostics
|
||||
{:range message.unknown-range
|
||||
:message "instruction limit reached"
|
||||
:severity message.severity.ERROR})
|
||||
@ -459,11 +459,15 @@ identifiers are declared / referenced in which places."
|
||||
(set file.scopes scopes)
|
||||
(set file.definitions definitions)
|
||||
(set file.definitions-by-scope definitions-by-scope)
|
||||
(set file.diagnostics diagnostics)
|
||||
(set file.compile-errors compile-diagnostics)
|
||||
(set file.references references)
|
||||
(set file.require-calls require-calls)
|
||||
(set file.allowed-globals allowed-globals)
|
||||
(set file.macro-refs macro-refs)
|
||||
(set file.macro-calls macro-calls))))
|
||||
|
||||
(fn compile [server file]
|
||||
(when (not file.ast)
|
||||
(do-compile server file)))
|
||||
|
||||
{: compile}
|
||||
|
||||
@ -25,8 +25,9 @@ actually recover the original completion. If the client supports both
|
||||
CompletionClientCapabilites.completionList.itemDefaults.editRange and
|
||||
CompletionClientCapabilites.completionList.itemDefaults.data, then we can ask
|
||||
the client to forward information to the resolve request by setting the `data`
|
||||
to {: uri : byte}. When this capability exists, `server.can-do-good-completions?`
|
||||
is set to true and we report that we support completionItem/resolve."
|
||||
to {: uri : byte}. When this capability exists,
|
||||
`server.client-capable-of-good-completions?` is set to true and we report that
|
||||
we support completionItem/resolve."
|
||||
|
||||
(local fennel (require :fennel))
|
||||
(local {:metadata METADATA} (require :fennel.compiler))
|
||||
@ -49,7 +50,7 @@ is set to true and we report that we support completionItem/resolve."
|
||||
file {:text (.. (file.text:sub 1 (- byte 1)) "|" (file.text:sub byte)) :uri file.uri}
|
||||
_ (compiler.compile server file)
|
||||
;; find what ast objects are under the cursor
|
||||
(symbol parents) (analyzer.find-symbol file.ast byte)
|
||||
(symbol parents) (analyzer.find-symbol server file byte)
|
||||
;; check what context I'm in
|
||||
in-call-position? (or (and (fennel.list? (. parents 1))
|
||||
(= symbol (. parents 1 1)))
|
||||
@ -136,7 +137,7 @@ is set to true and we report that we support completionItem/resolve."
|
||||
|
||||
(fn binding-completions []
|
||||
"completions when you're writing a destructure pattern. We suggest identifiers which are unknown"
|
||||
(each [_ {: message} (ipairs file.diagnostics)]
|
||||
(each [_ {: message} (ipairs file.compile-errors)]
|
||||
(case (message:match "unknown identifier: ([a-zA-Z0-9_-]+)")
|
||||
identifier (add-completion! identifier {} :Variable))))
|
||||
|
||||
@ -145,8 +146,8 @@ is set to true and we report that we support completionItem/resolve."
|
||||
(binding-completions)
|
||||
(expression-completions)))
|
||||
|
||||
(if server.can-do-good-completions?
|
||||
{:itemDefaults {:editRange (if server.can-do-insert-replace-completions?
|
||||
(if server.client-capable-of-good-completions?
|
||||
{:itemDefaults {:editRange (if server.client-capable-of-insert-replace-completions?
|
||||
{:insert {:start range.start :end position}
|
||||
:replace range}
|
||||
range)
|
||||
@ -157,7 +158,7 @@ is set to true and we report that we support completionItem/resolve."
|
||||
(fn completionItem/resolve [server _send completion-item]
|
||||
(let [{: uri : byte} completion-item.data
|
||||
file (files.get-by-uri server uri)
|
||||
(_symbol parents) (analyzer.find-symbol file.ast byte)
|
||||
(_symbol parents) (analyzer.find-symbol server file byte)
|
||||
scope (or (. file.scopes _symbol)
|
||||
(accumulate [?find nil _ parent (ipairs parents) &until ?find]
|
||||
(. file.scopes parent))
|
||||
|
||||
@ -126,13 +126,15 @@ However, when not an option, fennel-ls will fall back to positionEncoding=\"utf-
|
||||
(set server.macro-modules {})
|
||||
(set server.root-uri params.rootUri)
|
||||
(set server.position-encoding (choose-position-encoding params))
|
||||
(set server.can-do-good-completions?
|
||||
(set server.client-capable-of-good-completions?
|
||||
;; if client supports CompletionClientCapabilites.completionList.itemDefaults.editRange
|
||||
;; and CompletionClientCapabilites.completionList.itemDefaults.data
|
||||
(case (?. params :capabilities :textDocument :completion :completionList :itemDefaults)
|
||||
completion-item-defaults (and (accumulate [found nil _ v (ipairs completion-item-defaults) &until found] (= v :editRange))
|
||||
(accumulate [found nil _ v (ipairs completion-item-defaults) &until found] (= v :data)))))
|
||||
(set server.can-do-insert-replace-completions? (?. params :capabilities :textDocument :completion :completionItem :insertReplaceSupport))
|
||||
(set server.client-capable-of-insert-replace-completions? (?. params :capabilities :textDocument :completion :completionItem :insertReplaceSupport))
|
||||
(set server.client-capable-of-pull-diagnostics? (or (?. params :capabilities :textDocument :diagnostic)
|
||||
(not (?. params :capabilities :textDocument :publishDiagnostics))))
|
||||
(reload server))
|
||||
|
||||
{: initialize
|
||||
|
||||
@ -3,7 +3,6 @@ This module has high level helpers for creating/getting \"file\" objects."
|
||||
|
||||
(local searcher (require :fennel-ls.searcher))
|
||||
(local utils (require :fennel-ls.utils))
|
||||
(local {: compile} (require :fennel-ls.compiler))
|
||||
|
||||
(λ read-file [server uri]
|
||||
;; preload is here so that tests can inject files
|
||||
@ -23,7 +22,6 @@ This module has high level helpers for creating/getting \"file\" objects."
|
||||
(or (. server.files uri)
|
||||
(case (read-file server uri)
|
||||
file (do
|
||||
(compile server file)
|
||||
(tset server.files uri file)
|
||||
file))))
|
||||
|
||||
@ -44,21 +42,9 @@ This module has high level helpers for creating/getting \"file\" objects."
|
||||
(get-by-uri server uri))))))
|
||||
|
||||
(λ set-uri-contents [server uri text]
|
||||
(case (. server.files uri)
|
||||
;; modify existing file
|
||||
file
|
||||
(do
|
||||
(when (not= text file.text)
|
||||
(set file.text text)
|
||||
(compile server file))
|
||||
file)
|
||||
|
||||
;; create new file
|
||||
nil
|
||||
(let [file {: uri : text}]
|
||||
(tset server.files uri file)
|
||||
(compile server file)
|
||||
file)))
|
||||
(let [file {: uri : text}]
|
||||
(tset server.files uri file)
|
||||
file))
|
||||
|
||||
(λ flush-uri [server uri]
|
||||
"get rid of data about a file, in case it changed in some way"
|
||||
|
||||
@ -209,9 +209,9 @@ fntype is one of fn or λ or lambda"
|
||||
metadata (?. kinds metadata.fls/itemKind))
|
||||
(if (name:find "%.") kinds.Field kinds.Value))]
|
||||
{:label name
|
||||
:documentation (when (not server.can-do-good-completions?) (hover-format server name definition))
|
||||
:documentation (when (not server.client-capable-of-good-completions?) (hover-format server name definition))
|
||||
:sortText (sort-text name kind)
|
||||
:textEdit (when (not server.can-do-good-completions?) {:newText name : range})
|
||||
:textEdit (when (not server.client-capable-of-good-completions?) {:newText name : range})
|
||||
: kind}))
|
||||
|
||||
{: signature-help-format
|
||||
|
||||
@ -26,7 +26,7 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
:save true}
|
||||
;; :notebookDocumentSync nil
|
||||
:completionProvider {:workDoneProgress false
|
||||
:resolveProvider server.can-do-good-completions?
|
||||
:resolveProvider server.client-capable-of-good-completions?
|
||||
:triggerCharacters ["(" "[" "{"
|
||||
;; The LSP spec claims that "characters that make up identifiers don't need to be listed here"
|
||||
;; > https://github.com/microsoft/language-server-protocol/blob/4a4ff53db00d8c5e57630ff364dedf1918cbb612/_specifications/lsp/3.18/language/completion.md?plain=1#L205
|
||||
@ -51,7 +51,7 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
;; :documentFormattingProvider {:workDoneProgress false}
|
||||
;; :documentRangeFormattingProvider nil
|
||||
;; :documentOnTypeFormattingProvider nil
|
||||
:renameProvider {:workDoneProgress false}}]
|
||||
:renameProvider {:workDoneProgress false}
|
||||
;; :foldingRangeProvider nil
|
||||
;; :executeCommandProvider nil
|
||||
;; :selectionRangeProvider nil
|
||||
@ -62,8 +62,7 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
;; :typeHierarchyProvider nil
|
||||
;; :inlineValueProvider nil
|
||||
;; :inlayHintProvider nil
|
||||
;; ;; this is for PULL diagnostics, but fennel-ls currently does PUSH diagnostics
|
||||
;; :diagnosticProvider {:workDoneProgress false}})
|
||||
:diagnosticProvider {:workDoneProgress false}}]
|
||||
;; :workspaceSymbolProvider nil
|
||||
;; :workspace {:workspaceFolders nil
|
||||
;; :fileOperations {:didCreate nil
|
||||
@ -79,7 +78,7 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
(λ requests.textDocument/definition [server _send {: position :textDocument {: uri}}]
|
||||
(let [file (files.get-by-uri server uri)
|
||||
byte (utils.position->byte file.text position server.position-encoding)]
|
||||
(case-try (analyzer.find-symbol file.ast byte)
|
||||
(case-try (analyzer.find-symbol server file byte)
|
||||
(symbol [parent])
|
||||
(let [search-target (if (. file.require-calls parent) parent symbol)]
|
||||
(analyzer.search server file search-target {:stop-early? true} {: byte}))
|
||||
@ -95,7 +94,7 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
:textDocument {: uri}}]
|
||||
(let [this-file (files.get-by-uri server uri)
|
||||
byte (utils.position->byte this-file.text position server.position-encoding)]
|
||||
(match-try (analyzer.find-symbol this-file.ast byte)
|
||||
(match-try (analyzer.find-symbol server this-file byte)
|
||||
symbol
|
||||
(analyzer.find-nearest-definition server this-file symbol byte)
|
||||
{: referenced-by :file {:uri this-file.uri &as file} : binding}
|
||||
@ -113,7 +112,7 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
include-declaration?}}]
|
||||
(let [file (files.get-by-uri server uri)
|
||||
byte (utils.position->byte file.text position server.position-encoding)]
|
||||
(case-try (analyzer.find-symbol file.ast byte)
|
||||
(case-try (analyzer.find-symbol server file byte)
|
||||
symbol
|
||||
(analyzer.find-nearest-definition server file symbol byte)
|
||||
{: referenced-by : file : binding}
|
||||
@ -146,7 +145,7 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
(λ requests.textDocument/hover [server _send {: position :textDocument {: uri}}]
|
||||
(let [file (files.get-by-uri server uri)
|
||||
byte (utils.position->byte file.text position server.position-encoding)]
|
||||
(case-try (analyzer.find-symbol file.ast byte)
|
||||
(case-try (analyzer.find-symbol server file byte)
|
||||
(symbol parents) (analyzer.search server file symbol {} {: byte})
|
||||
{:indeterminate nil &as result}
|
||||
(let [opts {:macroexpansion (case-try parents
|
||||
@ -166,7 +165,7 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
(λ requests.textDocument/rename [server _send {: position :textDocument {: uri} :newName new-name}]
|
||||
(let [file (files.get-by-uri server uri)
|
||||
byte (utils.position->byte file.text position server.position-encoding)]
|
||||
(case-try (analyzer.find-symbol file.ast byte)
|
||||
(case-try (analyzer.find-symbol server file byte)
|
||||
symbol
|
||||
(analyzer.find-nearest-definition server file symbol symbol.bytestart)
|
||||
;; TODO we are assuming that every reference is in the same file
|
||||
@ -206,7 +205,8 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
(let [file (files.get-by-uri server uri)
|
||||
byte (utils.position->byte file.text range.start server.position-encoding)
|
||||
results []]
|
||||
(case-try (analyzer.find-symbol file.ast byte)
|
||||
(lint.add-lint-diagnostics server file)
|
||||
(case-try (analyzer.find-symbol server file byte)
|
||||
(symbol_ [[symbol_ &as parent]]) file.macro-calls
|
||||
{parent expansion} (table.insert results
|
||||
{:title "Expand macro"
|
||||
@ -216,16 +216,25 @@ Every time the client sends a message, it gets handled by a function in the corr
|
||||
(if (overlap? diagnostic.range range)
|
||||
(message.diagnostic->code-action server file diagnostic :quickfix)))))
|
||||
|
||||
(λ requests.textDocument/diagnostic [server _send {:textDocument {: uri}}]
|
||||
(let [file (files.get-by-uri server uri)]
|
||||
(lint.add-lint-diagnostics server file)
|
||||
{:kind "full"
|
||||
:items file.diagnostics}))
|
||||
|
||||
(fn push-diagnostics [server file send]
|
||||
(when (not server.client-capable-of-pull-diagnostics?)
|
||||
(lint.add-lint-diagnostics server file)
|
||||
(send (message.diagnostics file))))
|
||||
|
||||
(λ notifications.textDocument/didChange [server send {: contentChanges :textDocument {: uri}}]
|
||||
(local file (files.get-by-uri server uri))
|
||||
(files.set-uri-contents server uri (utils.apply-changes file.text contentChanges server.position-encoding))
|
||||
(lint.add-lint-diagnostics server file)
|
||||
(send (message.diagnostics file)))
|
||||
(push-diagnostics server file send))
|
||||
|
||||
(λ notifications.textDocument/didOpen [server send {:textDocument {: text : uri}}]
|
||||
(local file (files.set-uri-contents server uri text))
|
||||
(lint.add-lint-diagnostics server file)
|
||||
(send (message.diagnostics file))
|
||||
(push-diagnostics server file send)
|
||||
(set file.open? true))
|
||||
|
||||
(λ notifications.textDocument/didSave [server _send {:textDocument {: uri}}]
|
||||
|
||||
@ -14,6 +14,7 @@ You can read more about how to add lints in docs/linting.md"
|
||||
(local navigate (require :fennel-ls.navigate))
|
||||
(local docs (require :fennel-ls.docs))
|
||||
(local dkjson (require :dkjson))
|
||||
(local compiler (require :fennel-ls.compiler))
|
||||
|
||||
|
||||
(fn special? [item]
|
||||
@ -855,30 +856,33 @@ You can read more about how to add lints in docs/linting.md"
|
||||
(setmetatable {: self : fix} lint-mt)))
|
||||
|
||||
(λ add-lint-diagnostics [server file]
|
||||
(fn run [lints ...]
|
||||
(each [_ lint (ipairs lints)]
|
||||
(when (. server.configuration.lints lint.name)
|
||||
(case (lint.impl ...)
|
||||
diagnostic
|
||||
(table.insert file.diagnostics
|
||||
(wrap (doto diagnostic
|
||||
(tset :code lint.name))))))))
|
||||
(icollect [diagnostic (coroutine.wrap #(run lints.other server file)) &into file.diagnostics]
|
||||
(wrap diagnostic))
|
||||
(each [symbol definition (pairs file.definitions)]
|
||||
(when (. file.lexical symbol)
|
||||
(run lints.definition server file symbol definition)))
|
||||
(each [symbol (pairs file.references)]
|
||||
(when (. file.lexical symbol)
|
||||
(run lints.reference server file symbol)))
|
||||
(each [[head &as ast] (pairs file.calls)]
|
||||
(when (and (. file.lexical ast) (not= nil head))
|
||||
(run (if (special? head) lints.special-call lints.function-call)
|
||||
server file ast)))
|
||||
(each [ast macroexpanded (pairs file.macro-calls)]
|
||||
(when (. file.lexical ast)
|
||||
(run lints.macro-call
|
||||
server file ast macroexpanded))))
|
||||
(when (not file.diagnostics)
|
||||
(compiler.compile server file)
|
||||
(set file.diagnostics file.compile-errors)
|
||||
(fn run [lints ...]
|
||||
(each [_ lint (ipairs lints)]
|
||||
(when (. server.configuration.lints lint.name)
|
||||
(case (lint.impl ...)
|
||||
diagnostic
|
||||
(table.insert file.diagnostics
|
||||
(wrap (doto diagnostic
|
||||
(tset :code lint.name))))))))
|
||||
(icollect [diagnostic (coroutine.wrap #(run lints.other server file)) &into file.diagnostics]
|
||||
(wrap diagnostic))
|
||||
(each [symbol definition (pairs file.definitions)]
|
||||
(when (. file.lexical symbol)
|
||||
(run lints.definition server file symbol definition)))
|
||||
(each [symbol (pairs file.references)]
|
||||
(when (. file.lexical symbol)
|
||||
(run lints.reference server file symbol)))
|
||||
(each [[head &as ast] (pairs file.calls)]
|
||||
(when (and (. file.lexical ast) (not= nil head))
|
||||
(run (if (special? head) lints.special-call lints.function-call)
|
||||
server file ast)))
|
||||
(each [ast macroexpanded (pairs file.macro-calls)]
|
||||
(when (. file.lexical ast)
|
||||
(run lints.macro-call
|
||||
server file ast macroexpanded)))))
|
||||
|
||||
{: add-lint-diagnostics
|
||||
:list all-lints}
|
||||
|
||||
@ -21,7 +21,8 @@
|
||||
i)))
|
||||
|
||||
(fn check [file-contents expected unexpected ?opts]
|
||||
(let [{: diagnostics} (create-client file-contents nil ?opts)]
|
||||
(let [{: uri : client} (create-client file-contents nil ?opts)
|
||||
[{:result {:items diagnostics}}] (client:diagnostic uri)]
|
||||
(each [_ e (ipairs unexpected)]
|
||||
(let [i (find diagnostics e)]
|
||||
(faith.= nil i (.. "Lint matching " (view e) "\n"
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
i))))
|
||||
|
||||
(fn check [file-contents expected ?unexpected]
|
||||
(let [{: diagnostics} (create-client file-contents)]
|
||||
(let [{: uri : client} (create-client file-contents)
|
||||
[{:result {:items diagnostics}}] (client:diagnostic uri)]
|
||||
(each [_ e (ipairs (or ?unexpected []))]
|
||||
(let [i (find diagnostics e)]
|
||||
(faith.= nil i (.. "Lint matching " (view e) "\n"
|
||||
@ -38,7 +39,8 @@
|
||||
(table.remove diagnostics i)))))
|
||||
|
||||
(fn assert-ok [file-contents]
|
||||
(let [{: diagnostics} (create-client file-contents)]
|
||||
(let [{: uri : client} (create-client file-contents)
|
||||
[{:result {:items diagnostics}}] (client:diagnostic uri)]
|
||||
(faith.= nil (next diagnostics) (view diagnostics))))
|
||||
|
||||
(fn test-unused []
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
(fn test-find-symbol []
|
||||
(let [{: server : uri} (create-client "(match [1 2 4] [1 2 sym-one] sym-one)")
|
||||
file (. server.files uri)
|
||||
(symbol parents) (analyzer.find-symbol file.ast 23)]
|
||||
(symbol parents) (analyzer.find-symbol server file 23)]
|
||||
(faith.= symbol (fennel.sym :sym-one))
|
||||
(faith.=
|
||||
"[[1 2 sym-one] (match [1 2 4] [1 2 sym-one] sym-one) [(match [1 2 4] [1 2 sym-one] sym-one)]]"
|
||||
@ -28,7 +28,7 @@
|
||||
|
||||
(let [{: server : uri} (create-client "(match [1 2 4] [1 2 sym-one] sym-one)")
|
||||
file (. server.files uri)
|
||||
(symbol parents) (analyzer.find-symbol file.ast 18)]
|
||||
(symbol parents) (analyzer.find-symbol server file 18)]
|
||||
(faith.= symbol nil)
|
||||
(faith.=
|
||||
"[[1 2 sym-one] (match [1 2 4] [1 2 sym-one] sym-one) [(match [1 2 4] [1 2 sym-one] sym-one)]]"
|
||||
|
||||
@ -12,6 +12,10 @@
|
||||
(faith.= location response.result
|
||||
"error message")))
|
||||
|
||||
(fn get-diagnostics [file-contents]
|
||||
(let [{: uri : client} (create-client file-contents)]
|
||||
(. (client:diagnostic uri) 1 :result :items)))
|
||||
|
||||
;; TODO fix macros to use a custom searcher
|
||||
; (let [{: diagnostics}
|
||||
; (create-client
|
||||
@ -29,10 +33,10 @@
|
||||
;; (local client (doto [] ({:settings {:fennel-ls {:fennel-path "./?/?/?/?.fnl"}}))))
|
||||
|
||||
(fn test-extra-globals []
|
||||
(let [{:diagnostics good} (create-client {:main.fnl "(foo-100 bar :baz)"
|
||||
:flsproject.fnl "{:extra-globals \"foo-100 bar\"}"})
|
||||
{:diagnostics bad} (create-client {:main.fnl "(foo-100 bar :baz)"
|
||||
:flsproject.fnl "{}"})]
|
||||
(let [good (get-diagnostics {:main.fnl "(foo-100 bar :baz)"
|
||||
:flsproject.fnl "{:extra-globals \"foo-100 bar\"}"})
|
||||
bad (get-diagnostics {:main.fnl "(foo-100 bar :baz)"
|
||||
:flsproject.fnl "{}"})]
|
||||
(faith.= [] good)
|
||||
(faith.not= [] bad))
|
||||
nil)
|
||||
@ -44,10 +48,10 @@
|
||||
;; (local client (doto [] (setup-server {:fennel-ls {:diagnostics {:E202 "warning"}}})))))
|
||||
|
||||
(fn test-lints []
|
||||
(let [{:diagnostics good} (create-client {:main.fnl "(local x 10)"
|
||||
:flsproject.fnl "{:lints {:unused-definition false}}"})
|
||||
{:diagnostics bad} (create-client {:main.fnl "(local x 10)"
|
||||
:flsproject.fnl "{}"})]
|
||||
(let [good (get-diagnostics {:main.fnl "(local x 10)"
|
||||
:flsproject.fnl "{:lints {:unused-definition false}}"})
|
||||
bad (get-diagnostics {:main.fnl "(local x 10)"
|
||||
:flsproject.fnl "{}"})]
|
||||
(faith.= [] good)
|
||||
(faith.not= [] bad))
|
||||
nil)
|
||||
@ -76,20 +80,20 @@
|
||||
(faith.match "Could not find docset for library nasilemak"
|
||||
show.params.message)
|
||||
;; diagnostic
|
||||
(let [[diagnostics] (client:open-file! (.. client.server.root-uri "/" :flsproject.fnl) "{:libraries {:nasilemak true}}")]
|
||||
(faith.= "textDocument/publishDiagnostics" diagnostics.method)
|
||||
(faith.match "Could not find docset for library nasilemak" (. diagnostics.params.diagnostics 1 :message))))
|
||||
(client:open-file! (.. client.server.root-uri "/" :flsproject.fnl) "{:libraries {:nasilemak true}}")
|
||||
(let [[diagnostics] (client:diagnostic (.. client.server.root-uri "/" :flsproject.fnl))]
|
||||
(faith.match "Could not find docset for library nasilemak" (. diagnostics.result.items 1 :message))))
|
||||
nil)
|
||||
|
||||
(fn test-infinite-macro []
|
||||
(when (not debug.debug)
|
||||
(faith.skip))
|
||||
(let [{: diagnostics} (create-client {:main.fnl "(macro infinite [] (while true nil))\n(infinite)\n"
|
||||
:flsproject.fnl "{:compiler-instruction-limit 25000}"})]
|
||||
(let [diagnostics (get-diagnostics {:main.fnl "(macro infinite [] (while true nil))\n(infinite)\n"
|
||||
:flsproject.fnl "{:compiler-instruction-limit 25000}"})]
|
||||
(faith.= "instruction limit reached"
|
||||
(?. diagnostics 1 :message)))
|
||||
(let [{: diagnostics} (create-client {:main.fnl "(macro finite [] (while false nil))\n(finite)\n"
|
||||
:flsproject.fnl "{:compiler-instruction-limit 25000}"})]
|
||||
(let [diagnostics (get-diagnostics {:main.fnl "(macro finite [] (while false nil))\n(finite)\n"
|
||||
:flsproject.fnl "{:compiler-instruction-limit 25000}"})]
|
||||
(faith.= [] diagnostics))
|
||||
nil)
|
||||
|
||||
|
||||
@ -75,6 +75,14 @@
|
||||
:textDocument {:uri file}
|
||||
: newName})))
|
||||
|
||||
(fn diagnostic [self file ?previousResultId]
|
||||
(dispatch.handle* self.server
|
||||
(message.create-request (next-id! self) :textDocument/diagnostic
|
||||
(let [params {:textDocument {:uri file}}]
|
||||
(when ?previousResultId
|
||||
(set params.previousResultId ?previousResultId))
|
||||
params))))
|
||||
|
||||
(fn code-action [self file range]
|
||||
(dispatch.handle* self.server
|
||||
(message.create-request (next-id! self) :textDocument/codeAction
|
||||
@ -100,6 +108,7 @@
|
||||
: signature-help
|
||||
: rename
|
||||
: code-action
|
||||
: diagnostic
|
||||
: did-save}})
|
||||
|
||||
{: client-mt
|
||||
|
||||
@ -89,10 +89,10 @@
|
||||
: params})
|
||||
_ (each [k v (pairs (or ?config []))]
|
||||
(tset server.configuration k v))
|
||||
[{:params {: diagnostics}}] (client:open-file! uri text)]
|
||||
?diagnostics (?. (client:open-file! uri text) 1 :params :diagnostics)]
|
||||
{: client
|
||||
: server
|
||||
: diagnostics
|
||||
:diagnostics ?diagnostics
|
||||
: cursor
|
||||
: locations
|
||||
: highlights
|
||||
|
||||
Loading…
Reference in New Issue
Block a user