Code Monkey home page Code Monkey logo

protocol's Introduction

protocol

CircleCI pkg.go.dev Go module codecov.io GA

Package protocol implements Language Server Protocol specification in Go.

protocol's People

Contributors

a-h avatar hiroebe avatar ja-he avatar markdumay avatar micnncim avatar zchee avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

protocol's Issues

Client Usage Examples

Does anyone have a small getting started example of using this library as a LSP client? I see the NewClient/NewServer but trying to understand what a most basic implementation would look like.

LSP 3.17 support

Is there a plan to support LSP 3.17?
I want to use Inlay Hint API.

Protocol Updates

Is there a long term plan to continue to maintain this package? There are new features in protocol version 3.18 that would be useful to have integrated here.

How to implement

How do you implement a language server using this repository?

I tried the following and it creates nilpointer exceptions because "Server" is empty

func NewLangServer(logger *zap.Logger) *LangServer {
	server := new(LangServer)

	return server
}
type LangServer struct {
	protocol.Server
}

Initialized like this:

f, _ := os.Create("lsp-log.txt")
logr, _ := zap.NewDevelopment(zap.ErrorOutput(f))
srv := NewLangServer(logr)
conn, cl := protocol.NewServer(ctx,
                                srv,
                                jsonrpc2.NewStream(os.Stdin, os.Stdout),
                                logr,
                                jsonrpc2.WithLogger(logr))
if err := conn.Run(ctx); err != nil {
	// ...
}
if err := cl.Run(ctx); err != nil {
	// ...
}

produces

2019-06-22T14:57:45.363+0200	DEBUG	jsonrpc2/jsonrpc2.go:346	deliver
panic: runtime error: invalid memory address or nil pointer dereference

which happens in server_json.go

func ServerHandler(ctx context.Context, server ServerInterface, logger *zap.Logger) jsonrpc2.Handler {
	return func(ctx context.Context, conn *jsonrpc2.Conn, r *jsonrpc2.Request) {
		dec := json.NewDecoder(bytes.NewReader(*r.Params))

		switch r.Method {
		case MethodInitialize:
			var params InitializeParams
			if err := dec.Decode(&params); err != nil {
				ReplyError(ctx, err, conn, r, logger)
				return
			}
			// error here: server....
			resp, err := server.Initialize(ctx, &params)
			if err := conn.Reply(ctx, r, resp, err); err != nil {
				logger.Error(MethodInitialize, zap.Error(err))
			}
			// .... more
		}
	}
}

francoispqt/gojay is not actively maintained which causes dependabot to fail

gojay hasn't had a release since 2019. francoispqt/gojay#150

Since the 2019 gojay release, thrift has moved the repository to github.com/apache/thrift which has been fixed in newer versions of go.opencensus.io and cloud.google.com/go but since gojay is unmaintained those new dependencies have not been pulled into gojay or this project which means Dependabot fails when trying to update skaffold.

Reference: census-instrumentation/opencensus-go#1158 (comment)

Dependabot encountered the following error:

go: github.com/GoogleContainerTools/[email protected] requires
	go.lsp.dev/[email protected] requires
	github.com/francoispqt/[email protected] requires
	cloud.google.com/[email protected] requires
	[email protected] requires
	git.apache.org/[email protected]: invalid version: unknown revisio

v0.10.0 compatibility issue with jsonrpc2 v0.9.0

At handler_json.go:47, id is cast using int64(id) instead of int32(id), causing an error since jsonrpc2 is expecting int32 at wire.go:60.

Not sure which package is correct. Happy to create a PR to fix this, but didn't want to be presumptuous.

CompletionItemKind can not be deserialized

Server can not deserialize capabilities request because TextDocumentClientCapabilitiesCompletion.CompletionItemKind is now an object instead of a plain number.

Before:

// TextDocumentClientCapabilitiesCompletion Capabilities specific to the `textDocument/completion`
type TextDocumentClientCapabilitiesCompletion struct {
// ...
	CompletionItem // ...
        // This here is not a plain number anymore
	CompletionItemKind CompletionItemKind `json:"completionItemKind,omitempty"`
// ...
}

After:

// TextDocumentClientCapabilitiesCompletion Capabilities specific to the `textDocument/completion`
type TextDocumentClientCapabilitiesCompletion struct {
// ... 
	CompletionItem ...
// ...
        // better would be an additional struct instead of an inline one
	CompletionItemKind struct {
		ValueSet []CompletionItemKind `json:"valueSet,omitempty"`
	} `json:"completionItemKind,omitempty"`

// ...
}

Error unmarshalling `ApplyEdit` response

When testing against vscode, using client.ApplyEdit fails when unmarshalling the response. It looks like the response structure should be struct{Applied bool `json:"applied"`}, but it's unmarshalled directly into the boolean result. I was able to confirm this by creating my own client that implements a patched ApplyEdit.
It's probably worth taking into account the other optional fields declared by the spec[1].

[1] https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#applyWorkspaceEditResult

Response to `applyEdit` never received

I encountered an issue with my language server stalling after sending an applyEdit request to the client from inside of the processing of an executeCommand request. I believe this is a common pattern (code actions give the client command names, client calls executeCommand for the selected action, in the executeCommand we compute the edit and call applyEdit).

The server ends up infinitely waiting in jsonrpc2/conn.go:118 on the response to the applyEdit.
Thus, this issue might actually belong to jsonrpc2 (or of course be me not using either of the packages right).

I am testing mostly using Neovim, and in Neovim's log (~/.cache/nvim/lsp.log) the requests and responses all look appropriate (until my stalled language server stops participating, after sending applyEdit).

I have implemented a minimal working example (see below) that shows the behavior.
I have also implemented a similar small example in Rust using tower-lsp, which works as expected.

Repro

  • compile the code below, of course
  • set up with editor (I used Neovim) as client
  • open any document with the filetype that starts the server, etc.
  • invoke code action (you should see Foo)
  • select Foo code action
  • it properly (calls executeCommand, which calls applyEdit, which) inserts 'FOO'
  • the client's response to the applyEdit is never received by the server, forever waiting on case resp := <-rchan

MWE

full main.go
package main

import (
	"context"
	"fmt"
	"io"
	"os"

	"go.lsp.dev/jsonrpc2"
	lsp "go.lsp.dev/protocol"
	"go.uber.org/zap"
)

type Backend struct {
	client              lsp.Client
}

func (b *Backend) Initialize(ctx context.Context, params *lsp.InitializeParams) (result *lsp.InitializeResult, err error) {
	return &lsp.InitializeResult{
		Capabilities: lsp.ServerCapabilities{
			CodeActionProvider:     true,
			ExecuteCommandProvider: &lsp.ExecuteCommandOptions{Commands: []string{"foo"}},
		},
		ServerInfo: &lsp.ServerInfo{
			Name:    "mycoolserver",
			Version: "0.1.0",
		},
	}, nil
}
func (b *Backend) Initialized(ctx context.Context, params *lsp.InitializedParams) (err error) {
	return nil
}

func (b *Backend) Shutdown(ctx context.Context) (err error) { return nil }
func (b *Backend) Exit(ctx context.Context) (err error)     { return nil }

func (b *Backend) WorkDoneProgressCancel(ctx context.Context, params *lsp.WorkDoneProgressCancelParams) (err error)                                                              { panic("Unimplemented: WorkDoneProgressCancel") }
func (b *Backend) LogTrace(ctx context.Context, params *lsp.LogTraceParams) (err error)                                                                                          { panic("Unimplemented: LogTrace") }
func (b *Backend) SetTrace(ctx context.Context, params *lsp.SetTraceParams) (err error)                                                                                          { panic("Unimplemented: SetTrace") }
func (b *Backend) CodeLens(ctx context.Context, params *lsp.CodeLensParams) (result []lsp.CodeLens, err error)                                                                   { panic("Unimplemented: CodeLens") }
func (b *Backend) CodeLensResolve(ctx context.Context, params *lsp.CodeLens) (result *lsp.CodeLens, err error)                                                                   { panic("Unimplemented: CodeLensResolve") }
func (b *Backend) ColorPresentation(ctx context.Context, params *lsp.ColorPresentationParams) (result []lsp.ColorPresentation, err error)                                        { panic("Unimplemented: ColorPresentation") }
func (b *Backend) Completion(ctx context.Context, params *lsp.CompletionParams) (result *lsp.CompletionList, err error)                                                          { panic("Unimplemented: Completion") }
func (b *Backend) CompletionResolve(ctx context.Context, params *lsp.CompletionItem) (result *lsp.CompletionItem, err error)                                                     { panic("Unimplemented: CompletionResolve") }
func (b *Backend) Declaration(ctx context.Context, params *lsp.DeclarationParams) (result []lsp.Location /* Declaration | DeclarationLink[] | null */, err error)                { panic("Unimplemented: Declaration") }
func (b *Backend) Definition(ctx context.Context, params *lsp.DefinitionParams) (result []lsp.Location /* Definition | DefinitionLink[] | null */, err error)                    { panic("Unimplemented: Definition") }
func (b *Backend) DidChange(ctx context.Context, params *lsp.DidChangeTextDocumentParams) (err error)                                                                            { panic("Unimplemented: DidChange") }
func (b *Backend) DidChangeConfiguration(ctx context.Context, params *lsp.DidChangeConfigurationParams) (err error)                                                              { panic("Unimplemented: DidChangeConfiguration") }
func (b *Backend) DidChangeWatchedFiles(ctx context.Context, params *lsp.DidChangeWatchedFilesParams) (err error)                                                                { panic("Unimplemented: DidChangeWatchedFiles") }
func (b *Backend) DidChangeWorkspaceFolders(ctx context.Context, params *lsp.DidChangeWorkspaceFoldersParams) (err error)                                                        { panic("Unimplemented: DidChangeWorkspaceFolders") }
func (b *Backend) DidClose(ctx context.Context, params *lsp.DidCloseTextDocumentParams) (err error)                                                                              { panic("Unimplemented: DidClose") }
func (b *Backend) DidOpen(ctx context.Context, params *lsp.DidOpenTextDocumentParams) (err error)                                                                                { panic("Unimplemented: DidOpen") }
func (b *Backend) DidSave(ctx context.Context, params *lsp.DidSaveTextDocumentParams) (err error)                                                                                { panic("Unimplemented: DidSave") }
func (b *Backend) DocumentColor(ctx context.Context, params *lsp.DocumentColorParams) (result []lsp.ColorInformation, err error)                                                 { panic("Unimplemented: DocumentColor") }
func (b *Backend) DocumentHighlight(ctx context.Context, params *lsp.DocumentHighlightParams) (result []lsp.DocumentHighlight, err error)                                        { panic("Unimplemented: DocumentHighlight") }
func (b *Backend) DocumentLink(ctx context.Context, params *lsp.DocumentLinkParams) (result []lsp.DocumentLink, err error)                                                       { panic("Unimplemented: DocumentLink") }
func (b *Backend) DocumentLinkResolve(ctx context.Context, params *lsp.DocumentLink) (result *lsp.DocumentLink, err error)                                                       { panic("Unimplemented: DocumentLinkResolve") }
func (b *Backend) DocumentSymbol(ctx context.Context, params *lsp.DocumentSymbolParams) (result []interface{} /* []SymbolInformation | []DocumentSymbol */, err error)           { panic("Unimplemented: DocumentSymbol") }
func (b *Backend) FoldingRanges(ctx context.Context, params *lsp.FoldingRangeParams) (result []lsp.FoldingRange, err error)                                                      { panic("Unimplemented: FoldingRanges") }
func (b *Backend) Formatting(ctx context.Context, params *lsp.DocumentFormattingParams) (result []lsp.TextEdit, err error)                                                       { panic("Unimplemented: Formatting") }
func (b *Backend) Hover(ctx context.Context, params *lsp.HoverParams) (result *lsp.Hover, err error)                                                                             { panic("Unimplemented: Hover") }
func (b *Backend) Implementation(ctx context.Context, params *lsp.ImplementationParams) (result []lsp.Location, err error)                                                       { panic("Unimplemented: Implementation") }
func (b *Backend) OnTypeFormatting(ctx context.Context, params *lsp.DocumentOnTypeFormattingParams) (result []lsp.TextEdit, err error)                                           { panic("Unimplemented: OnTypeFormatting") }
func (b *Backend) PrepareRename(ctx context.Context, params *lsp.PrepareRenameParams) (result *lsp.Range, err error)                                                             { panic("Unimplemented: PrepareRename") }
func (b *Backend) RangeFormatting(ctx context.Context, params *lsp.DocumentRangeFormattingParams) (result []lsp.TextEdit, err error)                                             { panic("Unimplemented: RangeFormatting") }
func (b *Backend) References(ctx context.Context, params *lsp.ReferenceParams) (result []lsp.Location, err error)                                                                { panic("Unimplemented: References") }
func (b *Backend) Rename(ctx context.Context, params *lsp.RenameParams) (result *lsp.WorkspaceEdit, err error)                                                                   { panic("Unimplemented: Rename") }
func (b *Backend) SignatureHelp(ctx context.Context, params *lsp.SignatureHelpParams) (result *lsp.SignatureHelp, err error)                                                     { panic("Unimplemented: SignatureHelp") }
func (b *Backend) Symbols(ctx context.Context, params *lsp.WorkspaceSymbolParams) (result []lsp.SymbolInformation, err error)                                                    { panic("Unimplemented: Symbols") }
func (b *Backend) TypeDefinition(ctx context.Context, params *lsp.TypeDefinitionParams) (result []lsp.Location, err error)                                                       { panic("Unimplemented: TypeDefinition") }
func (b *Backend) WillSave(ctx context.Context, params *lsp.WillSaveTextDocumentParams) (err error)                                                                              { panic("Unimplemented: WillSave") }
func (b *Backend) WillSaveWaitUntil(ctx context.Context, params *lsp.WillSaveTextDocumentParams) (result []lsp.TextEdit, err error)                                              { panic("Unimplemented: WillSaveWaitUntil") }
func (b *Backend) ShowDocument(ctx context.Context, params *lsp.ShowDocumentParams) (result *lsp.ShowDocumentResult, err error)                                                  { panic("Unimplemented: ShowDocument") }
func (b *Backend) WillCreateFiles(ctx context.Context, params *lsp.CreateFilesParams) (result *lsp.WorkspaceEdit, err error)                                                     { panic("Unimplemented: WillCreateFiles") }
func (b *Backend) DidCreateFiles(ctx context.Context, params *lsp.CreateFilesParams) (err error)                                                                                 { panic("Unimplemented: DidCreateFiles") }
func (b *Backend) WillRenameFiles(ctx context.Context, params *lsp.RenameFilesParams) (result *lsp.WorkspaceEdit, err error)                                                     { panic("Unimplemented: WillRenameFiles") }
func (b *Backend) DidRenameFiles(ctx context.Context, params *lsp.RenameFilesParams) (err error)                                                                                 { panic("Unimplemented: DidRenameFiles") }
func (b *Backend) WillDeleteFiles(ctx context.Context, params *lsp.DeleteFilesParams) (result *lsp.WorkspaceEdit, err error)                                                     { panic("Unimplemented: WillDeleteFiles") }
func (b *Backend) DidDeleteFiles(ctx context.Context, params *lsp.DeleteFilesParams) (err error)                                                                                 { panic("Unimplemented: DidDeleteFiles") }
func (b *Backend) CodeLensRefresh(ctx context.Context) (err error)                                                                                                               { panic("Unimplemented: CodeLensRefresh") }
func (b *Backend) PrepareCallHierarchy(ctx context.Context, params *lsp.CallHierarchyPrepareParams) (result []lsp.CallHierarchyItem, err error)                                  { panic("Unimplemented: PrepareCallHierarchy") }
func (b *Backend) IncomingCalls(ctx context.Context, params *lsp.CallHierarchyIncomingCallsParams) (result []lsp.CallHierarchyIncomingCall, err error)                           { panic("Unimplemented: IncomingCalls") }
func (b *Backend) OutgoingCalls(ctx context.Context, params *lsp.CallHierarchyOutgoingCallsParams) (result []lsp.CallHierarchyOutgoingCall, err error)                           { panic("Unimplemented: OutgoingCalls") }
func (b *Backend) SemanticTokensFull(ctx context.Context, params *lsp.SemanticTokensParams) (result *lsp.SemanticTokens, err error)                                              { panic("Unimplemented: SemanticTokensFull") }
func (b *Backend) SemanticTokensFullDelta(ctx context.Context, params *lsp.SemanticTokensDeltaParams) (result interface{} /* SemanticTokens | SemanticTokensDelta */, err error) { panic("Unimplemented: SemanticTokensFullDelta") }
func (b *Backend) SemanticTokensRange(ctx context.Context, params *lsp.SemanticTokensRangeParams) (result *lsp.SemanticTokens, err error)                                        { panic("Unimplemented: SemanticTokensRange") }
func (b *Backend) SemanticTokensRefresh(ctx context.Context) (err error)                                                                                                         { panic("Unimplemented: SemanticTokensRefresh") }
func (b *Backend) LinkedEditingRange(ctx context.Context, params *lsp.LinkedEditingRangeParams) (result *lsp.LinkedEditingRanges, err error)                                     { panic("Unimplemented: LinkedEditingRange") }
func (b *Backend) Moniker(ctx context.Context, params *lsp.MonikerParams) (result []lsp.Moniker, err error)                                                                      { panic("Unimplemented: Moniker") }
func (b *Backend) Request(ctx context.Context, method string, params interface{}) (result interface{}, err error)                                                                { panic("Unimplemented: Request") }

func (b *Backend) CodeAction(ctx context.Context, params *lsp.CodeActionParams) (result []lsp.CodeAction, err error) {
	return []lsp.CodeAction{
		{
			Title: "Foo",
			Kind:  "source",
			Edit:  &lsp.WorkspaceEdit{},
			Command: &lsp.Command{
				Title:     "Foo",
				Command:   "foo",
				Arguments: []interface{}{params.TextDocument.URI},
			},
			Data: nil,
		},
	}, nil
}

func (b *Backend) ExecuteCommand(ctx context.Context, params *lsp.ExecuteCommandParams) (result interface{}, err error) {
	switch params.Command {
	case "foo":
		uri := lsp.URI(params.Arguments[0].(string))
		b.client.ApplyEdit(
			ctx,
			&lsp.ApplyWorkspaceEditParams{
				Label: "insert foo at the top of the document",
				Edit: lsp.WorkspaceEdit{
					Changes: map[lsp.URI][]lsp.TextEdit{
						uri: {
							{
								Range:   lsp.Range{Start: lsp.Position{0, 0}, End: lsp.Position{0, 0}},
								NewText: "FOO\n",
							},
						},
					},
					DocumentChanges:   []lsp.TextDocumentEdit{},
					ChangeAnnotations: map[lsp.ChangeAnnotationIdentifier]lsp.ChangeAnnotation{},
				},
			},
		)
		return nil, nil
	default:
		panic(fmt.Sprint("unimplemented command:", params.Command))
	}
}

func main() {
	logger, err := zap.NewDevelopment()
	if err != nil {
		panic(err)
	}

	ctx := lsp.WithLogger(context.Background(), logger)

	stdio := struct {
		io.ReadCloser
		io.Writer
	}{
		os.Stdin, os.Stdout,
	}
	conn := jsonrpc2.NewConn(jsonrpc2.NewStream(stdio))

	client := lsp.ClientDispatcher(conn, logger)
	server := &Backend{client}
	handler := lsp.ServerHandler(server, jsonrpc2.MethodNotFoundHandler)

	conn.Go(ctx, handler)
	select {
	case <-ctx.Done():
		fmt.Fprintf(os.Stderr, "context done")
		conn.Close()
	case <-conn.Done():
		fmt.Fprintf(os.Stderr, "jsonrpc conn done")
	}
}

Log

lsp.log snippet
[DEBUG][2022-03-24 15:07:24] .../lua/vim/lsp.lua:962	"LSP[spdx_lsp]"	"client.request"	1	"textDocument/codeAction"	{  context = {    diagnostics = {}  },  range = {    end = <1>{      character = 0,      line = 0    },    start = <table 1>  },  textDocument = {    uri = "file:[redacted path]"  }}	<function 1>	1
[DEBUG][2022-03-24 15:07:24] .../vim/lsp/rpc.lua:347	"rpc.send"	{  id = 2,  jsonrpc = "2.0",  method = "textDocument/codeAction",  params = {    context = {      diagnostics = {}    },    range = {      end = <1>{        character = 0,        line = 0      },      start = <table 1>    },    textDocument = {      uri = "file:[redacted path]"    }  }}
[DEBUG][2022-03-24 15:07:24] .../vim/lsp/rpc.lua:454	"rpc.receive"	{  id = 2,  jsonrpc = "2.0",  result = { {      command = {        arguments = { "file:[redacted path]" },        command = "foo",        title = "Foo"      },      edit = vim.empty_dict(),      kind = "source",      title = "Foo"    } }}
[ERROR][2022-03-24 15:07:24] .../vim/lsp/rpc.lua:420	"rpc"	"[redacted binary path]"	"stderr"	"2022-03-24T15:07:24.997+0100\tDEBUG\[email protected]/server.go:160\ttextDocument/codeAction\n"
[DEBUG][2022-03-24 15:07:29] .../lua/vim/lsp.lua:962	"LSP[spdx_lsp]"	"client.request"	1	"workspace/executeCommand"	{  arguments = { "file:[redacted path]" },  command = "foo",  title = "Foo"}	<function 1>	1
[DEBUG][2022-03-24 15:07:29] .../vim/lsp/rpc.lua:347	"rpc.send"	{  id = 3,  jsonrpc = "2.0",  method = "workspace/executeCommand",  params = {    arguments = { "file:[redacted path]" },    command = "foo",    title = "Foo"  }}
[ERROR][2022-03-24 15:07:29] .../vim/lsp/rpc.lua:420	"rpc"	"[redacted binary path]"	"stderr"	"2022-03-24T15:07:29.161+0100\tDEBUG\[email protected]/client.go:371\tcall workspace/applyEdit\n"
[DEBUG][2022-03-24 15:07:29] .../vim/lsp/rpc.lua:454	"rpc.receive"	{  id = 1,  jsonrpc = "2.0",  method = "workspace/applyEdit",  params = {    edit = {      changes = {        ["file:[redacted path]"] = { {            newText = "FOO\n",            range = {              end = {                character = 0,                line = 0              },              start = {                character = 0,                line = 0              }            }          } }      }    },    label = "insert foo at the top of the document"  }}
[DEBUG][2022-03-24 15:07:29] .../vim/lsp/rpc.lua:464	"server_request: callback result"	{  result = {    applied = true  },  status = true}
[DEBUG][2022-03-24 15:07:29] .../vim/lsp/rpc.lua:347	"rpc.send"	{  id = 1,  jsonrpc = "2.0",  result = {    applied = true  }}
[nothing more happens here; if we invoke an action, the client sends the proper data, but the server remains stalled]

SemanticTokensOptions definition

SemanticTokensOptions is defined as follow in Semantic Token

export interface SemanticTokensOptions extends WorkDoneProgressOptions {
	/**
	 * The legend used by the server
	 */
	legend: SemanticTokensLegend;

	/**
	 * Server supports providing semantic tokens for a specific range
	 * of a document.
	 */
	range?: boolean | {
	};

	/**
	 * Server supports providing semantic tokens for a full document.
	 */
	full?: boolean | {
		/**
		 * The server supports deltas for full documents.
		 */
		delta?: boolean;
	};
}

but in this repo, SemanticTokensOptions lack of legend, range and full fields

// SemanticTokensOptions option of semantic tokens provider server capabilities.
//
// @since 3.16.0.
type SemanticTokensOptions struct {
	WorkDoneProgressOptions
}

fails to go get go.lsp.dev/protocol

Fails to get the module on my mac.

% go get -u go.lsp.dev/protocol                   
go: downloading go.lsp.dev/protocol v0.11.2
go: downloading github.com/francoispqt/gojay v1.2.13
go: downloading go.lsp.dev/pkg v0.0.0-20210323044036-f7deec69b52e
go: downloading go.lsp.dev/uri v0.3.0
go: downloading go.uber.org/zap v1.17.0
go.lsp.dev/protocol imports
        go.lsp.dev/jsonrpc2 imports
        go.lsp.dev/pkg/event/label: cannot find module providing package go.lsp.dev/pkg/event/label
go.lsp.dev/protocol imports
        go.lsp.dev/jsonrpc2 imports
        go.lsp.dev/pkg/event/tag: cannot find module providing package go.lsp.dev/pkg/event/tag

my go toolchain version is:


% go version
go version go1.17.5 darwin/amd64

% go env GOPROXY
https://goproxy.cn,direct

Support InsertReplaceEdit in CompletionItem

Since LSP 3.16.0, CompletionItem supports either *TextEdit or *InsertReplaceEdit for the field TextEdit. The current implementation in Go supports *TextEdit only.

After a quick review, the following changes seem to be needed to cater for this additional type. Most likely language_gojay_test.go and language_test.go need to be modified too, but the needed code changes are less obvious to me. I'm more than happy to submit an initial PR too, let me know what you prefer.

language.go

diff --git a/language.go b/language.go
index 221d72a..f5be379 100644
--- a/language.go
+++ b/language.go
@@ -257,7 +257,7 @@ type CompletionItem struct {
        // contained and starting at the same position.
        //
        // @since 3.16.0 additional type "InsertReplaceEdit".
-       TextEdit *TextEdit `json:"textEdit,omitempty"` // *TextEdit | *InsertReplaceEdit
+       TextEdit interface{} `json:"textEdit,omitempty"`
 }
 
 // CompletionItemKind is the completion item kind values the client supports. When this

language_gojay.go

diff --git a/language_gojay.go b/language_gojay.go
index 2e9e38a..f6a992b 100644
--- a/language_gojay.go
+++ b/language_gojay.go
@@ -188,7 +188,7 @@ func (v *CompletionItem) MarshalJSONObject(enc *gojay.Encoder) {
        enc.StringKeyOmitEmpty(keyLabel, v.Label)
        enc.BoolKeyOmitEmpty(keyPreselect, v.Preselect)
        enc.StringKeyOmitEmpty(keySortText, v.SortText)
-       enc.ObjectKeyOmitEmpty(keyTextEdit, v.TextEdit)
+       enc.AddInterfaceKeyOmitEmpty(keyTextEdit, v.TextEdit)
 }
 
 // NKeys returns the number of keys to unmarshal.
@@ -233,10 +233,7 @@ func (v *CompletionItem) UnmarshalJSONObject(dec *gojay.Decoder, k string) error
        case keySortText:
                return dec.String(&v.SortText)
        case keyTextEdit:
-               if v.TextEdit == nil {
-                       v.TextEdit = &TextEdit{}
-               }
-               return dec.Object(v.TextEdit)
+               return dec.Interface(&v.TextEdit)
        }
        return nil
 }

Problem with Range in TextDocumentContentChangeEvent

When generating a textDocument/didChange notification from a client you run into the following problem if you want to indicate that the content you are sending to the server is the entire document.

As you note in the comments: "If range and rangeLength are omitted the new text is considered to be the full content of the document."

However, you can't omit range in straightforward way unless you make it a pointer and add "omitempty"

So the TextDocumentContentChangeEvent type's Range field (in text.go) would become:

Range *Range `json:"range,omitempty"`

With that change providing no Range produces the correct json (no Range information at all) whereas without the change you get a Range type struct with start and end type structs containing line and character fields set to zero and that is not interpreted by gopls (I haven't checked other lsps) as having omitted the range.

Can't access client from server methods

I've just started fiddling around with this package, and this is also my first venture into LSPs.
If I understand correctly, in order to send diagnostics about a file in response to a message (e.g. textDocument/didOpen), I need to access the client created by protocol.NewServer.
I see that the client is embedded into the context, but the key used to embed it is private and there appears to be no method to extract it from the context (as opposed to the logger which has LoggerFromContext in context.go).
Am I missing anything?

Dependabot can't resolve your Go dependency files

Dependabot can't resolve your Go dependency files.

As a result, Dependabot couldn't update your dependencies.

The error Dependabot encountered was:

go: golang.org/x/[email protected]: unknown revision 8115aa61538e

If you think the above is an error on Dependabot's side please don't hesitate to get in touch - we'll do whatever we can to fix it.

You can mention @dependabot in the comments below to contact the Dependabot team.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.