Closed mickael-menu closed 3 years ago
Thank you! I think the README mentions the supported features.
The main difference between this and go-lsp is also mentioned in the README: much more comprehensive coverage. Also, the code is documented with links to every struct and message in the LSP specification, so you can double-check as well as get instruction on how to use each message. What else do you think should be listed?
I started this project because go-lsp was insufficient.
Thank you! I think the README mentions the supported features.
Ha I assumed it was more a goal than the actual state of the project, because of the disclaimer. Then that sounds great!
This is an early release. Some features are not yet fully implemented.
Also, the code is documented with links to every struct and message in the LSP specification, so you can double-check as well as get instruction on how to use each message. What else do you think should be listed?
The code is well documented and clear. Maybe a minimal implementation example could help to get started. I played a bit with glsp
but didn't manage to make it work with coc.nvim yet.
First, I got the following crash: panic: logging not configured [recovered]
, which I solved by adding:
_ "github.com/tliron/kutil/logging/simple"
But it is not very obvious how to actually get the log messages.
At the moment my test LSP server seems to be stuck in the "starting" phase of coc.nvim. I couldn't figure out how to make it run despite checking your Puccini LS. Do you have any idea what could be missing?
package cmd
import (
"fmt"
"log"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
"github.com/tliron/glsp/server"
_ "github.com/tliron/kutil/logging/simple"
)
// LSP starts a server implementing the Language Server Protocol.
type LSP struct{}
var handler = protocol.Handler{}
var logger *log.Logger
func (cmd *LSP) Run() error {
handler.Initialize = initialize
handler.Initialized = initialized
handler.Shutdown = shutdown
handler.LogTrace = logTrace
handler.SetTrace = setTrace
handler.TextDocumentCompletion = textDocumentCompletion
debug := true
server := server.NewServer(&handler, "zk-lsp", debug)
fmt.Println("STARTING")
return server.RunTCP(":4230")
}
// initialize implements protocol.InitializeFunc
// Returns: InitializeResult | InitializeError
func initialize(context *glsp.Context, params *protocol.InitializeParams) (interface{}, error) {
log.Println("INITIALIZE")
// clientCapabilities = ¶ms.Capabilities
if params.Trace != nil {
protocol.SetTraceValue(*params.Trace)
}
resolveProvider := true
serverCapabilities := handler.CreateServerCapabilities()
serverCapabilities.TextDocumentSync = protocol.TextDocumentSyncKindIncremental
serverCapabilities.CompletionProvider = &protocol.CompletionOptions{
ResolveProvider: &resolveProvider,
TriggerCharacters: []string{"#"},
}
version := "1.0"
return &protocol.InitializeResult{
Capabilities: serverCapabilities,
ServerInfo: &protocol.InitializeResultServerInfo{
Name: "zk-lsp",
Version: &version,
},
}, nil
}
// protocol.TextDocumentCompletionFunc signature
// Returns: []CompletionItem | CompletionList | nil
func textDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (interface{}, error) {
var completionItems []protocol.CompletionItem
completionItems = append(completionItems, protocol.CompletionItem{
Label: "server",
})
log.Println("COMPLETION")
return completionItems, nil
}
// protocol.InitializedFunc signature
func initialized(context *glsp.Context, params *protocol.InitializedParams) error {
log.Println("INITIALIZED")
return nil
}
// protocol.ShutdownFunc signature
func shutdown(context *glsp.Context) error {
protocol.SetTraceValue(protocol.TraceValueOff)
return nil
}
// protocol.LogTraceFunc signature
func logTrace(context *glsp.Context, params *protocol.LogTraceParams) error {
return nil
}
// protocol.SetTraceFunc signature
func setTrace(context *glsp.Context, params *protocol.SetTraceParams) error {
protocol.SetTraceValue(params.Value)
return nil
}
Some of the pain points you are experiencing are why I put a "early release" warning. The problem is that indeed I did not do testing in many environments, for example no testing with coc.vim. Also I'm not sure how correct my TCP code is. Any help in debugging this would be very appreciated! I will assist in whatever way I can.
Logging can help a lot, of course. :) To enable logging you need to call logging.Configure
with the verbosity (max is 2). See this example.
Thanks, that did the trick for the logging 👍 There's some progress!
I figured that using stdio might be simpler than TCP, however I get the following error from Vim:
2021-03-27T21:08:28.109 INFO (pid:30131) [services] - registered service "languageserver.zk"
2021-03-27T21:08:28.113 INFO (pid:30131) [services] - zk state change: stopped => starting
2021-03-27T21:08:28.117 INFO (pid:30131) [plugin] - coc.nvim 0.0.79-6fe357fc97 initialized with node: v15.1.0 after 54ms
2021-03-27T21:08:28.123 INFO (pid:30131) [language-client-index] - Language server "languageserver.zk" started with 30133
2021-03-27T21:08:28.501 ERROR (pid:30131) [server] - uncaughtException Error: Header must provide a Content-Length property.
at StreamMessageReader.onData (/Users/mickael/.vim/plugged/coc.nvim/build/index.js:18138:27)
at Socket.<anonymous> (/Users/mickael/.vim/plugged/coc.nvim/build/index.js:18123:18)
at Socket.emit (node:events:327:20)
at addChunk (node:internal/streams/readable:304:12)
at readableAddChunk (node:internal/streams/readable:279:9)
at Socket.Readable.push (node:internal/streams/readable:218:10)
at Pipe.onStreamRead (node:internal/stream_base_commons:192:23)
And here are the logs from glsp, the connection seems to have been successful:
2021/03/27 21:08:28.497 INFO [zk-lsp.server] reading from stdin, writing to stdout
2021/03/27 21:08:28.499 DEBUG [zk-lsp.rpc] jsonrpc2: --> request #0: initialize: {"processId":30131,"rootPath":"/Users/mickael/Dropbox/Notes","rootUri":"file:///Users/mickael/Dropbox/Notes","capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional"},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":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]},"tagSupport":{"valueSet":[1]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]}},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true},"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]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true}}},"definition":{"dynamicRegistration":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":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]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]}},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true},"implementation":{"dynamicRegistration":true},"declaration":{"dynamicRegistration":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true},"selectionRange":{"dynamicRegistration":true}},"window":{"workDoneProgress":true}},"initializationOptions":{},"trace":"verbose","workspaceFolders":[{"uri":"file:///Users/mickael/Dropbox/Notes","name":"Notes"}],"clientInfo":{"name":"coc.nvim","version":"0.0.79"},"workDoneToken":"9a61d353-d957-4adb-9cec-fd1ddf994a0b"}
2021/03/27 21:08:28.499 DEBUG [zk-lsp.rpc] jsonrpc2: <-- result #0: initialize: {"capabilities":{"textDocumentSync":2,"completionProvider":{"triggerCharacters":["#"],"resolveProvider":true}},"serverInfo":{"name":"zk-lsp","version":"1.0"}}
It might be that coc.nvim is more demanding, because I managed to run my LS with Sublime Text:
:: --> zk initialize(1): {'initializationOptions': {}, 'rootPath': None, 'clientInfo': {'version': '0.14.3', 'name': 'Sublime Text LSP'}, 'capabilities': {'workspace': {'workspaceEdit': {'documentChanges': True, 'failureHandling': 'abort'}, 'didChangeConfiguration': {'dynamicRegistration': True}, 'applyEdit': True, 'executeCommand': {}, 'configuration': True, 'symbol': {'dynamicRegistration': 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]}}, 'workspaceFolders': True}, 'window': {'showMessage': {'messageActionItem': {'additionalPropertiesSupport': True}}, 'workDoneProgress': True}, 'experimental': {}, 'textDocument': {'hover': {'contentFormat': ['markdown', 'plaintext'], 'dynamicRegistration': True}, 'declaration': {'dynamicRegistration': True, 'linkSupport': True}, 'completion': {'completionItem': {'snippetSupport': True}, 'dynamicRegistration': True, '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]}}, 'rangeFormatting': {'dynamicRegistration': True}, 'references': {'dynamicRegistration': True}, 'documentSymbol': {'dynamicRegistration': 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]}, 'hierarchicalDocumentSymbolSupport': True}, 'definition': {'dynamicRegistration': True, 'linkSupport': True}, 'synchronization': {'didSave': True, 'willSave': True, 'dynamicRegistration': True, 'willSaveWaitUntil': True}, 'documentHighlight': {'dynamicRegistration': True}, 'colorProvider': {'dynamicRegistration': True}, 'signatureHelp': {'signatureInformation': {'documentationFormat': ['markdown', 'plaintext'], 'parameterInformation': {'labelOffsetSupport': True}}, 'dynamicRegistration': True}, 'publishDiagnostics': {'relatedInformation': True}, 'rename': {'dynamicRegistration': True}, 'typeDefinition': {'dynamicRegistration': True, 'linkSupport': True}, 'implementation': {'dynamicRegistration': True, 'linkSupport': True}, 'formatting': {'dynamicRegistration': True}, 'codeAction': {'codeActionLiteralSupport': {'codeActionKind': {'valueSet': []}}, 'dynamicRegistration': True}}}, 'rootUri': None, 'processId': 31466, 'workspaceFolders': None}
zk: 2021/03/27 21:45:08 INITIALIZE
:: <<< zk 1: {'capabilities': {'completionProvider': {'triggerCharacters': ['#'], 'resolveProvider': True}, 'textDocumentSync': 2}, 'serverInfo': {'version': '1.0', 'name': 'zk-lsp'}}
zk: 2021/03/27 21:45:08 INITIALIZED
:: -> zk initialized: {}
2021/03/27 21:45:08.263 INFO [zk-lsp.server] reading from stdin, writing to stdout
2021/03/27 21:45:08.264 DEBUG [zk-lsp.rpc] jsonrpc2: --> request #1: initialize: {"initializationOptions":{},"rootPath":null,"clientInfo":{"version":"0.14.3","name":"Sublime Text LSP"},"capabilities":{"workspace":{"workspaceEdit":{"documentChanges":true,"failureHandling":"abort"},"didChangeConfiguration":{"dynamicRegistration":true},"applyEdit":true,"executeCommand":{},"configuration":true,"symbol":{"dynamicRegistration":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]}},"workspaceFolders":true},"window":{"showMessage":{"messageActionItem":{"additionalPropertiesSupport":true}},"workDoneProgress":true},"experimental":{},"textDocument":{"hover":{"contentFormat":["markdown","plaintext"],"dynamicRegistration":true},"declaration":{"dynamicRegistration":true,"linkSupport":true},"completion":{"completionItem":{"snippetSupport":true},"dynamicRegistration":true,"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]}},"rangeFormatting":{"dynamicRegistration":true},"references":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":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]},"hierarchicalDocumentSymbolSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"synchronization":{"didSave":true,"willSave":true,"dynamicRegistration":true,"willSaveWaitUntil":true},"documentHighlight":{"dynamicRegistration":true},"colorProvider":{"dynamicRegistration":true},"signatureHelp":{"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true}},"dynamicRegistration":true},"publishDiagnostics":{"relatedInformation":true},"rename":{"dynamicRegistration":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"formatting":{"dynamicRegistration":true},"codeAction":{"codeActionLiteralSupport":{"codeActionKind":{"valueSet":[]}},"dynamicRegistration":true}}},"rootUri":null,"processId":31466,"workspaceFolders":null}
2021/03/27 21:45:08.265 DEBUG [zk-lsp.rpc] jsonrpc2: <-- result #1: initialize: {"capabilities":{"textDocumentSync":2,"completionProvider":{"triggerCharacters":["#"],"resolveProvider":true}},"serverInfo":{"name":"zk-lsp","version":"1.0"}}
2021/03/27 21:45:08.265 DEBUG [zk-lsp.rpc] jsonrpc2: --> notif: initialized: {}
Is there a way to debug the actual JSON-RPC messages sent with glsp, to see if the Content-Length
header is really missing?
I was able to run a similar LS made with go-lsp, so I think my coc.nvim setup is fine.
So, it was really dumb...
I figured out a way to print the output from glsp, and it seemed correct:
2021/03/28 11:29:48.590 INFO [zk-lsp.server] WRITING: "Content-Length: 192\r\n\r\n{\"id\":0,\"result\":{\"capabilities\":{\"textDocumentSync\":2,\"completionProvider\":{\"triggerCharacters\":[\"#\"],\"resolveProvider\":true}},\"serverInfo\":{\"name\":\"zk-lsp\",\"version\":\"1.0\"}},\"jsonrpc\":\"2.0\"}"
The issue was that I added some stdout log in my cli app, which of course was messing up the protocol communication when using the stdio mode. After removing it, it's working properly and I get some basic completion 👍
Closing this issue, thanks and keep up the good work!
Ha, that might have happened to me once or twice, too...
By the way, one quirk of GLSP is that it absolutely requires the Content-Length header. It is not optional.
Albeit in early stage, your project is looking very promising!
It would be helpful to have a list of supported features to know if
glsp
could be a good fit for a given project. I'm trying to figure out if I should go withglsp
or https://github.com/sourcegraph/go-lsp.I would be willing to contribute as well if something I need is missing.