It's now working all the way, but supports nothing
This commit is contained in:
parent
5deb5e1ed0
commit
f7b7b64d38
159
fennel-ls.fnl
159
fennel-ls.fnl
@ -1,86 +1,87 @@
|
|||||||
(local {: encode : decode} (require :json.json))
|
(local requests [])
|
||||||
(local {: split} (require :pl.stringx))
|
(local notifications [])
|
||||||
|
|
||||||
(local capabilities {:positionEncoding nil ; "utf-8"
|
(local capabilities
|
||||||
:textDocumentSync nil
|
{:positionEncoding "utf-8"
|
||||||
:notepookDocumentSync nil
|
:textDocumentSync nil
|
||||||
:completionProvider nil
|
:notebookDocumentSync nil
|
||||||
:hoverProvider nil
|
:completionProvider nil
|
||||||
:signatureHelpProvider nil
|
:hoverProvider nil
|
||||||
:declarationProvider nil
|
:signatureHelpProvider nil
|
||||||
:definitionProvider nil
|
:declarationProvider nil
|
||||||
:typeDefinitionProvider nil
|
:definitionProvider nil
|
||||||
:implementationProvider nil
|
:typeDefinitionProvider nil
|
||||||
:referencesProvider nil
|
:implementationProvider nil
|
||||||
:documentHighlightProvider nil
|
:referencesProvider nil
|
||||||
:documentSymbolProvider nil
|
:documentHighlightProvider nil
|
||||||
:codeActionProvider nil
|
:documentSymbolProvider nil
|
||||||
:codeLensProvider nil
|
:codeActionProvider nil
|
||||||
:documentLinkProvider nil
|
:codeLensProvider nil
|
||||||
:colorProvider nil
|
:documentLinkProvider nil
|
||||||
:documentFormattingProvider nil
|
:colorProvider nil
|
||||||
:documentRangeFormattingProvider nil
|
:documentFormattingProvider nil
|
||||||
:documentOnTypeFormattingProvider nil
|
:documentRangeFormattingProvider nil
|
||||||
:renameProvider nil
|
:documentOnTypeFormattingProvider nil
|
||||||
:foldingRangeProvider nil
|
:renameProvider nil
|
||||||
:executeCommandProvider nil
|
:foldingRangeProvider nil
|
||||||
:selectionRangeProvider nil
|
:executeCommandProvider nil
|
||||||
:linkedEditingRangeProvider nil
|
:selectionRangeProvider nil
|
||||||
:callHierarchyProvider nil
|
:linkedEditingRangeProvider nil
|
||||||
:semanticTokensProvider nil
|
:callHierarchyProvider nil
|
||||||
:monikerProvider nil
|
:semanticTokensProvider nil
|
||||||
:typeHierarchyProvider nil
|
:monikerProvider nil
|
||||||
:inlineValueProvider nil
|
:typeHierarchyProvider nil
|
||||||
:inlayHintProvider nil
|
:inlineValueProvider nil
|
||||||
:diagnosticProvider nil
|
:inlayHintProvider nil
|
||||||
:workspaceSymbolProvider nil
|
:diagnosticProvider nil
|
||||||
:workspace nil})
|
:workspaceSymbolProvider nil
|
||||||
|
:workspace {:workspaceFolders nil
|
||||||
|
:fileOperations {:didCreate nil
|
||||||
|
:willCreate nil
|
||||||
|
:didRename nil
|
||||||
|
:willRename nil
|
||||||
|
:didDelete nil
|
||||||
|
:willDelete nil}}})
|
||||||
|
|
||||||
(local handlers {})
|
(λ requests.initialize [params]
|
||||||
|
{:capabilities capabilities
|
||||||
|
:serverInfo {:name "fennel-ls" :version "0.0.0"}})
|
||||||
|
|
||||||
(λ handlers.initialize [params]
|
(λ requests.shutdown [])
|
||||||
{:capabilities capabilities
|
;; no op
|
||||||
:serverInfo {:name "fennel-ls" :version "0.0.0"}})
|
|
||||||
|
|
||||||
(λ receive-message [in]
|
(λ notifications.exit []
|
||||||
(local header {})
|
(os.exit 0))
|
||||||
(while
|
|
||||||
(match (in:read)
|
|
||||||
"\r" false
|
|
||||||
header-line
|
|
||||||
(let [[k v] (split header-line ": " 2)]
|
|
||||||
(tset header k v)
|
|
||||||
true)
|
|
||||||
_ nil))
|
|
||||||
(let [len (tonumber header.Content-Length)
|
|
||||||
buffer []]
|
|
||||||
(var sofar 0)
|
|
||||||
(while (< sofar len)
|
|
||||||
(let [r (in:read (- len sofar))]
|
|
||||||
(set sofar (+ sofar (length r)))
|
|
||||||
(table.insert buffer r)))
|
|
||||||
(decode (table.concat buffer))))
|
|
||||||
|
|
||||||
(λ send-message [out msg]
|
(λ handle-request [id method ?params]
|
||||||
(let [content (encode msg)
|
(let [callback (. requests method)
|
||||||
msg-stringified (.. "Content-Length: " (length content) "\r\n\r\n" content)]
|
result {: id :jsonrpc "2.0"}]
|
||||||
(out:write msg-stringified)))
|
(if callback
|
||||||
|
(tset result :result (callback ?params))
|
||||||
|
(tset result :error (.. "Unknown message type: " method)))
|
||||||
|
result))
|
||||||
|
|
||||||
(λ handle [{: jsonrpc : method : params : id &as msg}]
|
(λ handle-response [id result])
|
||||||
(assert (= jsonrpc "2.0"))
|
;; Do nothing
|
||||||
;; Right now, if the callback crashes, the whole server does.
|
|
||||||
;; It would be nice to turn this into an error message
|
|
||||||
(match (. handlers method)
|
|
||||||
callback
|
|
||||||
(let [result (callback params)]
|
|
||||||
{: id
|
|
||||||
: method
|
|
||||||
:params result
|
|
||||||
:jsonrpc "2.0"})
|
|
||||||
_ {: id
|
|
||||||
: method
|
|
||||||
:error (.. "unknown method " msg.method)}))
|
|
||||||
|
|
||||||
{: receive-message
|
(λ handle-bad-response [id err]
|
||||||
: send-message
|
(error (.. "oopsie: " err.code)))
|
||||||
: handle}
|
|
||||||
|
(λ handle-notification [method ?params]
|
||||||
|
(let [callback (. notifications method)]
|
||||||
|
(if callback
|
||||||
|
(callback ?params))))
|
||||||
|
|
||||||
|
(λ handle [msg]
|
||||||
|
"The entry point for all messages."
|
||||||
|
(assert (= msg.jsonrpc "2.0") "Aha! You forgot to repeat that jsonrpc is version 2.0!")
|
||||||
|
(match msg
|
||||||
|
{: id : method :params ?params} (handle-request id method ?params)
|
||||||
|
{: method :params ?params} (handle-notification method ?params)
|
||||||
|
{: id : result} (handle-response id result)
|
||||||
|
{: id :error err} (handle-bad-response id err)
|
||||||
|
_ {:id msg.id
|
||||||
|
:error "I just received a message that doesn't make sense to me"
|
||||||
|
:jsonrpc "2.0"}))
|
||||||
|
|
||||||
|
{: handle}
|
||||||
|
|||||||
44
lsp-io.fnl
Normal file
44
lsp-io.fnl
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
(local {: encode : decode} (require :json.json))
|
||||||
|
(local {: split} (require :pl.stringx))
|
||||||
|
|
||||||
|
(λ read-header [in ?header]
|
||||||
|
(let [header (or ?header {})]
|
||||||
|
(match (in:read)
|
||||||
|
;; base cases
|
||||||
|
"\r" header
|
||||||
|
nil nil
|
||||||
|
;; reading an actual line
|
||||||
|
header-line
|
||||||
|
(let [[k v] (split header-line ": " 2)]
|
||||||
|
(tset header k (string.sub v 1 -2))
|
||||||
|
(read-header in header)))))
|
||||||
|
|
||||||
|
(λ read-content [in header]
|
||||||
|
(let [len (tonumber header.Content-Length)
|
||||||
|
buffer []]
|
||||||
|
;; TODO make this code as tolerable as possible
|
||||||
|
(var sofar 0)
|
||||||
|
(var currently-read-bytes nil)
|
||||||
|
(while
|
||||||
|
(and (< sofar len)
|
||||||
|
(do (set currently-read-bytes (in:read (- len sofar)))
|
||||||
|
currently-read-bytes))
|
||||||
|
(set sofar (+ sofar (length currently-read-bytes)))
|
||||||
|
(table.insert buffer currently-read-bytes))
|
||||||
|
(decode (table.concat buffer))))
|
||||||
|
|
||||||
|
|
||||||
|
(λ read-message [in]
|
||||||
|
(match (read-header in)
|
||||||
|
header (read-content in header)
|
||||||
|
nil nil))
|
||||||
|
|
||||||
|
(λ write-message [out msg]
|
||||||
|
(let [content (encode msg)
|
||||||
|
msg-stringified (.. "Content-Length: " (length content) "\r\n\r\n" content)]
|
||||||
|
(out:write msg-stringified)
|
||||||
|
(when out.flush
|
||||||
|
(out:flush))))
|
||||||
|
|
||||||
|
{: read-message
|
||||||
|
: write-message}
|
||||||
28
main.fnl
28
main.fnl
@ -1,20 +1,16 @@
|
|||||||
(local
|
(local fennel (require :fennel))
|
||||||
{: handle
|
(local {: read-message : write-message} (require :lsp-io))
|
||||||
: send-message
|
(local {: handle} (require :fennel-ls))
|
||||||
: receive-message}
|
|
||||||
(require "fennel-ls"))
|
(λ main-loop [in out]
|
||||||
|
(let [msg (read-message in)]
|
||||||
|
(when msg
|
||||||
|
(let [response (handle msg)]
|
||||||
|
(when response
|
||||||
|
(write-message out response))
|
||||||
|
(main-loop in out)))))
|
||||||
|
|
||||||
(λ main []
|
(λ main []
|
||||||
(local in (io.input))
|
(main-loop (io.input) (io.output)))
|
||||||
(local out (io.output))
|
|
||||||
(while
|
|
||||||
(match (receive-message in)
|
|
||||||
msg
|
|
||||||
(do
|
|
||||||
(let [response (handle msg)]
|
|
||||||
(if msg.id
|
|
||||||
(send-message out response)))
|
|
||||||
true)
|
|
||||||
_ nil)))
|
|
||||||
|
|
||||||
(main)
|
(main)
|
||||||
|
|||||||
94
test.fnl
94
test.fnl
@ -1,93 +1,43 @@
|
|||||||
(local fennel-ls (require :fennel-ls))
|
(local {: handle} (require :fennel-ls))
|
||||||
|
(local {: read-message : write-message} (require :lsp-io))
|
||||||
(local stringio (require :pl.stringio))
|
(local stringio (require :pl.stringio))
|
||||||
(local {: view} (require :fennel))
|
(local {: view} (require :fennel))
|
||||||
(local busted (require :busted))
|
(local busted (require :busted))
|
||||||
|
|
||||||
((require :busted.runner))
|
((require :busted.runner))
|
||||||
|
|
||||||
(macro describe! [title ...]
|
(macro it! [title ...] `(busted.it ,title (fn [] ,...)))
|
||||||
`(busted.describe ,title (fn [] ,...)))
|
(macro describe! [title ...] `(busted.describe ,title (fn [] ,...)))
|
||||||
|
|
||||||
(macro it! [title ...]
|
|
||||||
`(busted.it ,title (fn [] ,...)))
|
|
||||||
|
|
||||||
(describe! "fennel-ls"
|
(describe! "fennel-ls"
|
||||||
|
|
||||||
(it! "parses incoming messages"
|
(it! "parses incoming messages"
|
||||||
(let [out (stringio.open "Content-Length: 29\r\n\r\n{\"my json content\":\"is cool\"}")]
|
(let [out (stringio.open "Content-Length: 29\r\n\r\n{\"my json content\":\"is cool\"}")]
|
||||||
(assert.same {"my json content" "is cool"}
|
(assert.same {"my json content" "is cool"}
|
||||||
(fennel-ls.recieve-message out))))
|
(read-message out))))
|
||||||
|
|
||||||
(it! "serializes outgoing messages"
|
(it! "serializes outgoing messages"
|
||||||
(let [in (stringio.create)]
|
(let [in (stringio.create)]
|
||||||
(fennel-ls.send-message in {"my json content" "is cool"})
|
(write-message in {"my json content" "is cool"})
|
||||||
(assert.same "Content-Length: 29\r\n\r\n{\"my json content\":\"is cool\"}"
|
(assert.same "Content-Length: 29\r\n\r\n{\"my json content\":\"is cool\"}"
|
||||||
(in:value))))
|
(in:value))))
|
||||||
|
|
||||||
|
(it! "can read multiple messages"
|
||||||
|
(let [out (stringio.open "Content-Length: 29\r\n\r\n{\"my json content\":\"is cool\"}Content-Length: 29\r\n\r\n{\"my json content\":\"is cool\"}")]
|
||||||
|
(assert.same {"my json content" "is cool"}
|
||||||
|
(read-message out))
|
||||||
|
(assert.same {"my json content" "is cool"}
|
||||||
|
(read-message out))
|
||||||
|
(assert.same nil
|
||||||
|
(read-message out))))
|
||||||
|
|
||||||
(it! "responds to initialize"
|
(it! "responds to initialize"
|
||||||
(local initialize-message
|
(local initialize-message
|
||||||
{:id 1
|
{:id 1
|
||||||
:jsonrpc "2.0"
|
:jsonrpc "2.0"
|
||||||
:method "initialize"
|
:method "initialize"
|
||||||
:params
|
:params
|
||||||
{:capabilities {:callHierarchy {:dynamicRegistration false}
|
{:capabilities {}
|
||||||
:textDocument {:codeAction {:codeActionLiteralSupport
|
|
||||||
{:codeActionKind
|
|
||||||
{:valueSet
|
|
||||||
["" "Empty" "QuickFix" "Refactor"
|
|
||||||
"RefactorExtract" "RefactorInline"
|
|
||||||
"RefactorRewrite" "Source"
|
|
||||||
"SourceOrganizeImports" "quickfix"
|
|
||||||
"refactor" "refactor.extract"
|
|
||||||
"refactor.inline" "refactor.rewrite"
|
|
||||||
"source" "source.organizeImports"]}}
|
|
||||||
:dataSupport true
|
|
||||||
:dynamicRegistration false
|
|
||||||
:resolveSupport {:properties ["edit"]}}
|
|
||||||
:completion {:completionItem {:commitCharactersSupport true
|
|
||||||
:deprecatedSupport true
|
|
||||||
:documentationFormat ["markdown" "plaintext"]
|
|
||||||
:insertReplaceSupport true
|
|
||||||
:labelDetailsSupport true
|
|
||||||
:preselectSupport true
|
|
||||||
:resolveSupport {:properties ["documentation" "detail" "additionalTextEdits"]}
|
|
||||||
:snippetSupport true
|
|
||||||
:tagSupport {:valueSet [1]}}
|
|
||||||
:completionItemKind {:valueSet [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25]}
|
|
||||||
:contextSupport false
|
|
||||||
:dynamicRegistration false}
|
|
||||||
:declaration {:linkSupport true}
|
|
||||||
:definition {:linkSupport true}
|
|
||||||
:documentHighlight {:dynamicRegistration false}
|
|
||||||
:documentSymbol {:dynamicRegistration false
|
|
||||||
:hierarchicalDocumentSymbolSupport true
|
|
||||||
:symbolKind {:valueSet [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]}}
|
|
||||||
:hover {:contentFormat ["markdown" "plaintext"]
|
|
||||||
:dynamicRegistration false}
|
|
||||||
:implementation {:linkSupport true}
|
|
||||||
:publishDiagnostics {:relatedInformation true
|
|
||||||
:tagSupport {:valueSet [1 2]}}
|
|
||||||
:references {:dynamicRegistration false}
|
|
||||||
:rename {:dynamicRegistration false
|
|
||||||
:prepareSupport true}
|
|
||||||
:signatureHelp {:dynamicRegistration false
|
|
||||||
:signatureInformation {:activeParameterSupport true
|
|
||||||
:documentationFormat ["markdown" "plaintext"]
|
|
||||||
:parameterInformation {:labelOffsetSupport true}}}
|
|
||||||
:synchronization {:didSave true
|
|
||||||
:dynamicRegistration false
|
|
||||||
:willSave false
|
|
||||||
:willSaveWaitUntil false}
|
|
||||||
:typeDefinition {:linkSupport true}}
|
|
||||||
:window {:showDocument {:support false}
|
|
||||||
:showMessage {:messageActionItem {:additionalPropertiesSupport false}}
|
|
||||||
:workDoneProgress true}
|
|
||||||
:workspace {:applyEdit true
|
|
||||||
:configuration true
|
|
||||||
:symbol {:dynamicRegistration false
|
|
||||||
:hierarchicalWorkspaceSymbolSupport true
|
|
||||||
:symbolKind {:valueSet [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]}}
|
|
||||||
:workspaceEdit {:resourceOperations ["rename" "create" "delete"]}
|
|
||||||
:workspaceFolders true}}
|
|
||||||
:clientInfo {:name "Neovim" :version "0.7.2"}
|
:clientInfo {:name "Neovim" :version "0.7.2"}
|
||||||
:initializationOptions {}
|
:initializationOptions {}
|
||||||
:processId 16245
|
:processId 16245
|
||||||
@ -96,4 +46,12 @@
|
|||||||
:trace "off"
|
:trace "off"
|
||||||
:workspaceFolders [{:name "/home/xerool/Documents/projects/fennel-ls"
|
:workspaceFolders [{:name "/home/xerool/Documents/projects/fennel-ls"
|
||||||
:uri "file:///home/xerool/Documents/projects/fennel-ls"}]}})
|
:uri "file:///home/xerool/Documents/projects/fennel-ls"}]}})
|
||||||
(local result (fennel-ls.handle initialize-message))))
|
(assert
|
||||||
|
(match (handle initialize-message)
|
||||||
|
{:id 1
|
||||||
|
:jsonrpc "2.0"
|
||||||
|
:result {:capabilities {}
|
||||||
|
:serverInfo {:name "fennel-ls" : version}}}
|
||||||
|
true
|
||||||
|
otherwise (values false (view otherwise))))))
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user