1
0
mirror of https://github.com/golang/go synced 2024-11-18 11:04:42 -07:00

internal/jsonrpc2: split reply from request

reply is now passed to handlers separately from request, which allows it to be
substituted by handlers.
This also makes the handler signature much closer to http (which has
ResponseWriter)

Change-Id: I12be2e8e8b9bd508982ba43c9092709429284eaf
Reviewed-on: https://go-review.googlesource.com/c/tools/+/227839
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Ian Cottrell 2020-04-09 23:54:23 -04:00
parent b854406a88
commit 1f08ef6002
9 changed files with 236 additions and 237 deletions

View File

@ -13,23 +13,25 @@ import (
)
// Handler is invoked to handle incoming requests.
// If the request returns false from IsNotify then the Handler must eventually
// call Reply on the Conn with the supplied request.
// The handler should return ErrNotHandled if it could not handle the request.
type Handler func(context.Context, *Request) error
// The Replier sends a reply to the request and must be called exactly once.
type Handler func(ctx context.Context, reply Replier, req *Request) error
// Replier is passed to handlers to allow them to reply to the request.
// If err is set then result will be ignored.
type Replier func(ctx context.Context, result interface{}, err error) error
// MethodNotFound is a Handler that replies to all call requests with the
// standard method not found response.
// This should normally be the final handler in a chain.
func MethodNotFound(ctx context.Context, r *Request) error {
return r.Reply(ctx, nil, fmt.Errorf("%w: %q", ErrMethodNotFound, r.Method))
func MethodNotFound(ctx context.Context, reply Replier, r *Request) error {
return reply(ctx, nil, fmt.Errorf("%w: %q", ErrMethodNotFound, r.Method))
}
// MustReply creates a Handler that panics if the wrapped handler does
// MustReplyHandler creates a Handler that panics if the wrapped handler does
// not call Reply for every request that it is passed.
func MustReply(handler Handler) Handler {
return func(ctx context.Context, req *Request) error {
err := handler(ctx, req)
func MustReplyHandler(handler Handler) Handler {
return func(ctx context.Context, reply Replier, req *Request) error {
err := handler(ctx, reply, req)
if req.done != nil {
panic(fmt.Errorf("request %q was never replied to", req.Method))
}
@ -42,7 +44,7 @@ func MustReply(handler Handler) Handler {
func CancelHandler(handler Handler) (Handler, func(id ID)) {
var mu sync.Mutex
handling := make(map[ID]context.CancelFunc)
wrapped := func(ctx context.Context, req *Request) error {
wrapped := func(ctx context.Context, reply Replier, req *Request) error {
if req.ID != nil {
cancelCtx, cancel := context.WithCancel(ctx)
ctx = cancelCtx
@ -55,7 +57,7 @@ func CancelHandler(handler Handler) (Handler, func(id ID)) {
mu.Unlock()
})
}
return handler(ctx, req)
return handler(ctx, reply, req)
}
return wrapped, func(id ID) {
mu.Lock()
@ -76,7 +78,7 @@ func CancelHandler(handler Handler) (Handler, func(id ID)) {
func AsyncHandler(handler Handler) Handler {
nextRequest := make(chan struct{})
close(nextRequest)
return func(ctx context.Context, req *Request) error {
return func(ctx context.Context, reply Replier, req *Request) error {
waitForPrevious := nextRequest
nextRequest = make(chan struct{})
unlockNext := nextRequest
@ -85,7 +87,7 @@ func AsyncHandler(handler Handler) Handler {
go func() {
<-waitForPrevious
queueDone()
if err := handler(ctx, req); err != nil {
if err := handler(ctx, reply, req); err != nil {
event.Error(ctx, "jsonrpc2 async message delivery failed", err)
}
}()

View File

@ -170,62 +170,59 @@ func (r *Request) IsNotify() bool {
return r.ID == nil
}
// Reply sends a reply to the given request.
// You must call this exactly once for any given request.
// If err is set then result will be ignored.
// This will mark the request as done, triggering any done
// handlers
func (r *Request) Reply(ctx context.Context, result interface{}, err error) error {
if r.done == nil {
return fmt.Errorf("reply invoked more than once")
}
defer func() {
recordStatus(ctx, err)
for i := len(r.done); i > 0; i-- {
r.done[i-1]()
func replier(r *Request) Replier {
return func(ctx context.Context, result interface{}, err error) error {
if r.done == nil {
return fmt.Errorf("reply invoked more than once")
}
r.done = nil
}()
if r.IsNotify() {
return nil
}
defer func() {
recordStatus(ctx, err)
for i := len(r.done); i > 0; i-- {
r.done[i-1]()
}
r.done = nil
}()
var raw *json.RawMessage
if err == nil {
raw, err = marshalToRaw(result)
}
response := &wireResponse{
Result: raw,
ID: r.ID,
}
if err != nil {
if callErr, ok := err.(*wireError); ok {
response.Error = callErr
} else {
response.Error = &wireError{Message: err.Error()}
var wrapped *wireError
if errors.As(err, &wrapped) {
// if we wrapped a wire error, keep the code from the wrapped error
// but the message from the outer error
response.Error.Code = wrapped.Code
if r.IsNotify() {
return nil
}
var raw *json.RawMessage
if err == nil {
raw, err = marshalToRaw(result)
}
response := &wireResponse{
Result: raw,
ID: r.ID,
}
if err != nil {
if callErr, ok := err.(*wireError); ok {
response.Error = callErr
} else {
response.Error = &wireError{Message: err.Error()}
var wrapped *wireError
if errors.As(err, &wrapped) {
// if we wrapped a wire error, keep the code from the wrapped error
// but the message from the outer error
response.Error.Code = wrapped.Code
}
}
}
}
data, err := json.Marshal(response)
if err != nil {
return err
}
n, err := r.conn.stream.Write(ctx, data)
event.Record(ctx, tag.SentBytes.Of(n))
data, err := json.Marshal(response)
if err != nil {
return err
}
n, err := r.conn.stream.Write(ctx, data)
event.Record(ctx, tag.SentBytes.Of(n))
if err != nil {
// TODO(iancottrell): if a stream write fails, we really need to shut down
// the whole stream
return err
if err != nil {
// TODO(iancottrell): if a stream write fails, we really need to shut down
// the whole stream
return err
}
return nil
}
return nil
}
// OnReply adds a done callback to the request.
@ -283,7 +280,7 @@ func (c *Conn) Run(runCtx context.Context, handler Handler) error {
}
req.OnReply(func() { spanDone() })
if err := handler(reqCtx, req); err != nil {
if err := handler(reqCtx, replier(req), req); err != nil {
// delivery failed, not much we can do
event.Error(reqCtx, "jsonrpc2 message delivery failed", err)
}

View File

@ -119,33 +119,33 @@ func run(ctx context.Context, t *testing.T, withHeaders bool, r io.ReadCloser, w
}
func testHandler(log bool) jsonrpc2.Handler {
return func(ctx context.Context, r *jsonrpc2.Request) error {
switch r.Method {
return func(ctx context.Context, reply jsonrpc2.Replier, req *jsonrpc2.Request) error {
switch req.Method {
case "no_args":
if r.Params != nil {
return r.Reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
if req.Params != nil {
return reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
}
return r.Reply(ctx, true, nil)
return reply(ctx, true, nil)
case "one_string":
var v string
if err := json.Unmarshal(*r.Params, &v); err != nil {
return r.Reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err))
if err := json.Unmarshal(*req.Params, &v); err != nil {
return reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err))
}
return r.Reply(ctx, "got:"+v, nil)
return reply(ctx, "got:"+v, nil)
case "one_number":
var v int
if err := json.Unmarshal(*r.Params, &v); err != nil {
return r.Reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err))
if err := json.Unmarshal(*req.Params, &v); err != nil {
return reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err))
}
return r.Reply(ctx, fmt.Sprintf("got:%d", v), nil)
return reply(ctx, fmt.Sprintf("got:%d", v), nil)
case "join":
var v []string
if err := json.Unmarshal(*r.Params, &v); err != nil {
return r.Reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err))
if err := json.Unmarshal(*req.Params, &v); err != nil {
return reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err))
}
return r.Reply(ctx, path.Join(v...), nil)
return reply(ctx, path.Join(v...), nil)
default:
return jsonrpc2.MethodNotFound(ctx, r)
return jsonrpc2.MethodNotFound(ctx, reply, req)
}
}
}

View File

@ -16,8 +16,8 @@ type msg struct {
Msg string
}
func fakeHandler(ctx context.Context, r *jsonrpc2.Request) error {
return r.Reply(ctx, &msg{"pong"}, nil)
func fakeHandler(ctx context.Context, reply jsonrpc2.Replier, req *jsonrpc2.Request) error {
return reply(ctx, &msg{"pong"}, nil)
}
func TestTestServer(t *testing.T) {

View File

@ -416,7 +416,7 @@ func OverrideExitFuncsForTest() func() {
// instance from exiting. In the future it may also intercept 'shutdown' to
// provide more graceful shutdown of the client connection.
func forwarderHandler(handler jsonrpc2.Handler) jsonrpc2.Handler {
return func(ctx context.Context, r *jsonrpc2.Request) error {
return func(ctx context.Context, reply jsonrpc2.Replier, r *jsonrpc2.Request) error {
// TODO(golang.org/issues/34111): we should more gracefully disconnect here,
// once that process exists.
if r.Method == "exit" {
@ -424,9 +424,9 @@ func forwarderHandler(handler jsonrpc2.Handler) jsonrpc2.Handler {
// reply nil here to consume the message: in
// tests, ForwarderExitFunc may be overridden to something that doesn't
// exit the process.
return r.Reply(ctx, nil, nil)
return reply(ctx, nil, nil)
}
return handler(ctx, r)
return handler(ctx, reply, r)
}
}
@ -486,12 +486,12 @@ const (
)
func handshaker(client *debugClient, goplsPath string, handler jsonrpc2.Handler) jsonrpc2.Handler {
return func(ctx context.Context, r *jsonrpc2.Request) error {
return func(ctx context.Context, reply jsonrpc2.Replier, r *jsonrpc2.Request) error {
switch r.Method {
case handshakeMethod:
var req handshakeRequest
if err := json.Unmarshal(*r.Params, &req); err != nil {
sendError(ctx, r, err)
sendError(ctx, reply, r, err)
return nil
}
client.debugAddress = req.DebugAddr
@ -508,7 +508,7 @@ func handshaker(client *debugClient, goplsPath string, handler jsonrpc2.Handler)
resp.DebugAddr = di.ListenedDebugAddress
}
return r.Reply(ctx, resp, nil)
return reply(ctx, resp, nil)
case sessionsMethod:
resp := ServerState{
GoplsPath: goplsPath,
@ -526,15 +526,15 @@ func handshaker(client *debugClient, goplsPath string, handler jsonrpc2.Handler)
})
}
}
return r.Reply(ctx, resp, nil)
return reply(ctx, resp, nil)
}
return handler(ctx, r)
return handler(ctx, reply, r)
}
}
func sendError(ctx context.Context, req *jsonrpc2.Request, err error) {
func sendError(ctx context.Context, reply jsonrpc2.Replier, req *jsonrpc2.Request, err error) {
err = fmt.Errorf("%w: %v", jsonrpc2.ErrParse, err)
if err := req.Reply(ctx, nil, err); err != nil {
if err := reply(ctx, nil, err); err != nil {
event.Error(ctx, "", err)
}
}

View File

@ -35,27 +35,27 @@ func Handlers(handler jsonrpc2.Handler) jsonrpc2.Handler {
return CancelHandler(
CancelHandler(
jsonrpc2.AsyncHandler(
jsonrpc2.MustReply(handler))))
jsonrpc2.MustReplyHandler(handler))))
}
func CancelHandler(handler jsonrpc2.Handler) jsonrpc2.Handler {
handler, canceller := jsonrpc2.CancelHandler(handler)
return func(ctx context.Context, req *jsonrpc2.Request) error {
return func(ctx context.Context, reply jsonrpc2.Replier, req *jsonrpc2.Request) error {
if req.Method != "$/cancelRequest" {
return handler(ctx, req)
return handler(ctx, reply, req)
}
var params CancelParams
if err := json.Unmarshal(*req.Params, &params); err != nil {
return sendParseError(ctx, req, err)
return sendParseError(ctx, reply, req, err)
}
if n, ok := params.ID.(float64); ok {
canceller(*jsonrpc2.NewIntID(int64(n)))
} else if s, ok := params.ID.(string); ok {
canceller(*jsonrpc2.NewStringID(s))
} else {
return sendParseError(ctx, req, fmt.Errorf("request ID %v malformed", params.ID))
return sendParseError(ctx, reply, req, fmt.Errorf("request ID %v malformed", params.ID))
}
return req.Reply(ctx, nil, nil)
return reply(ctx, nil, nil)
}
}
@ -75,6 +75,6 @@ func cancelCall(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID) {
conn.Notify(ctx, "$/cancelRequest", &CancelParams{ID: &id})
}
func sendParseError(ctx context.Context, req *jsonrpc2.Request, err error) error {
return req.Reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err))
func sendParseError(ctx context.Context, reply jsonrpc2.Replier, req *jsonrpc2.Request, err error) error {
return reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err))
}

View File

@ -32,97 +32,97 @@ type Client interface {
}
func ClientHandler(client Client, handler jsonrpc2.Handler) jsonrpc2.Handler {
return func(ctx context.Context, r *jsonrpc2.Request) error {
return func(ctx context.Context, reply jsonrpc2.Replier, r *jsonrpc2.Request) error {
if ctx.Err() != nil {
ctx := xcontext.Detach(ctx)
return r.Reply(ctx, nil, RequestCancelledError)
return reply(ctx, nil, RequestCancelledError)
}
switch r.Method {
case "window/showMessage": // notif
var params ShowMessageParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := client.ShowMessage(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "window/logMessage": // notif
var params LogMessageParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := client.LogMessage(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "telemetry/event": // notif
var params interface{}
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := client.Event(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "textDocument/publishDiagnostics": // notif
var params PublishDiagnosticsParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := client.PublishDiagnostics(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "$/progress": // notif
var params ProgressParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := client.Progress(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "workspace/workspaceFolders": // req
if r.Params != nil {
return r.Reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
return reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
}
resp, err := client.WorkspaceFolders(ctx)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "workspace/configuration": // req
var params ParamConfiguration
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := client.Configuration(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "window/workDoneProgress/create": // req
var params WorkDoneProgressCreateParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := client.WorkDoneProgressCreate(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "client/registerCapability": // req
var params RegistrationParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := client.RegisterCapability(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "client/unregisterCapability": // req
var params UnregistrationParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := client.UnregisterCapability(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "window/showMessageRequest": // req
var params ShowMessageRequestParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := client.ShowMessageRequest(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "workspace/applyEdit": // req
var params ApplyWorkspaceEditParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := client.ApplyEdit(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
default:
return handler(ctx, r)
return handler(ctx, reply, r)
}
}

View File

@ -70,356 +70,356 @@ type Server interface {
}
func ServerHandler(server Server, handler jsonrpc2.Handler) jsonrpc2.Handler {
return func(ctx context.Context, r *jsonrpc2.Request) error {
return func(ctx context.Context, reply jsonrpc2.Replier, r *jsonrpc2.Request) error {
if ctx.Err() != nil {
ctx := xcontext.Detach(ctx)
return r.Reply(ctx, nil, RequestCancelledError)
return reply(ctx, nil, RequestCancelledError)
}
switch r.Method {
case "workspace/didChangeWorkspaceFolders": // notif
var params DidChangeWorkspaceFoldersParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := server.DidChangeWorkspaceFolders(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "window/workDoneProgress/cancel": // notif
var params WorkDoneProgressCancelParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := server.WorkDoneProgressCancel(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "initialized": // notif
var params InitializedParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := server.Initialized(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "exit": // notif
return server.Exit(ctx)
case "workspace/didChangeConfiguration": // notif
var params DidChangeConfigurationParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := server.DidChangeConfiguration(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "textDocument/didOpen": // notif
var params DidOpenTextDocumentParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := server.DidOpen(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "textDocument/didChange": // notif
var params DidChangeTextDocumentParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := server.DidChange(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "textDocument/didClose": // notif
var params DidCloseTextDocumentParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := server.DidClose(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "textDocument/didSave": // notif
var params DidSaveTextDocumentParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := server.DidSave(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "textDocument/willSave": // notif
var params WillSaveTextDocumentParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := server.WillSave(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "workspace/didChangeWatchedFiles": // notif
var params DidChangeWatchedFilesParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := server.DidChangeWatchedFiles(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "$/setTraceNotification": // notif
var params SetTraceParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := server.SetTraceNotification(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "$/logTraceNotification": // notif
var params LogTraceParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err := server.LogTraceNotification(ctx, &params)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "textDocument/implementation": // req
var params ImplementationParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.Implementation(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/typeDefinition": // req
var params TypeDefinitionParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.TypeDefinition(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/documentColor": // req
var params DocumentColorParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.DocumentColor(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/colorPresentation": // req
var params ColorPresentationParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.ColorPresentation(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/foldingRange": // req
var params FoldingRangeParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.FoldingRange(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/declaration": // req
var params DeclarationParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.Declaration(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/selectionRange": // req
var params SelectionRangeParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.SelectionRange(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "initialize": // req
var params ParamInitialize
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.Initialize(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "shutdown": // req
if r.Params != nil {
return r.Reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
return reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
}
err := server.Shutdown(ctx)
return r.Reply(ctx, nil, err)
return reply(ctx, nil, err)
case "textDocument/willSaveWaitUntil": // req
var params WillSaveTextDocumentParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.WillSaveWaitUntil(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/completion": // req
var params CompletionParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.Completion(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "completionItem/resolve": // req
var params CompletionItem
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.Resolve(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/hover": // req
var params HoverParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.Hover(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/signatureHelp": // req
var params SignatureHelpParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.SignatureHelp(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/definition": // req
var params DefinitionParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.Definition(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/references": // req
var params ReferenceParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.References(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/documentHighlight": // req
var params DocumentHighlightParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.DocumentHighlight(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/documentSymbol": // req
var params DocumentSymbolParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.DocumentSymbol(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/codeAction": // req
var params CodeActionParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.CodeAction(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "workspace/symbol": // req
var params WorkspaceSymbolParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.Symbol(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/codeLens": // req
var params CodeLensParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.CodeLens(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "codeLens/resolve": // req
var params CodeLens
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.ResolveCodeLens(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/documentLink": // req
var params DocumentLinkParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.DocumentLink(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "documentLink/resolve": // req
var params DocumentLink
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.ResolveDocumentLink(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/formatting": // req
var params DocumentFormattingParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.Formatting(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/rangeFormatting": // req
var params DocumentRangeFormattingParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.RangeFormatting(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/onTypeFormatting": // req
var params DocumentOnTypeFormattingParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.OnTypeFormatting(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/rename": // req
var params RenameParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.Rename(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/prepareRename": // req
var params PrepareRenameParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.PrepareRename(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "workspace/executeCommand": // req
var params ExecuteCommandParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.ExecuteCommand(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/prepareCallHierarchy": // req
var params CallHierarchyPrepareParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.PrepareCallHierarchy(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "callHierarchy/incomingCalls": // req
var params CallHierarchyIncomingCallsParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.IncomingCalls(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "callHierarchy/outgoingCalls": // req
var params CallHierarchyOutgoingCallsParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.OutgoingCalls(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/semanticTokens": // req
var params SemanticTokensParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.SemanticTokens(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/semanticTokens/edits": // req
var params SemanticTokensEditsParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.SemanticTokensEdits(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
case "textDocument/semanticTokens/range": // req
var params SemanticTokensRangeParams
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.SemanticTokensRange(ctx, &params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
default:
var params interface{}
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.NonstandardRequest(ctx, r.Method, params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
}
}

View File

@ -908,7 +908,7 @@ let server: side = {
// commonly used output
const notNil = `if r.Params != nil {
return r.Reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.CodeInvalidParams))
return reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
}`;
// Go code for notifications. Side is client or server, m is the request
@ -924,10 +924,10 @@ function goNot(side: side, m: string) {
if (a != '' && a != 'void') {
case1 = `var params ${a}
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
err:= ${side.name}.${nm}(ctx, &params)
return r.Reply(ctx, nil, err)`
return reply(ctx, nil, err)`
} else {
case1 = `return ${side.name}.${nm}(ctx)`;
}
@ -959,7 +959,7 @@ function goReq(side: side, m: string) {
if (extraTypes.has('Param' + nm)) a = 'Param' + nm
case1 = `var params ${a}
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}`;
}
const arg2 = a == '' ? '' : ', &params';
@ -968,10 +968,10 @@ function goReq(side: side, m: string) {
}`;
if (b != '' && b != 'void') {
case2 = `resp, err := ${side.name}.${nm}(ctx${arg2})
return r.Reply(ctx, resp, err)`;
return reply(ctx, resp, err)`;
} else { // response is nil
case2 = `err := ${side.name}.${nm}(ctx${arg2})
return r.Reply(ctx, nil, err)`
return reply(ctx, nil, err)`
}
side.cases.push(`${caseHdr}\n${case1}\n${case2}`);
@ -1082,10 +1082,10 @@ function output(side: side) {
side.methods.forEach((v) => {f(v)});
f('}\n');
f(`func ${a}Handler(${side.name} ${a}, handler jsonrpc2.Handler) jsonrpc2.Handler {
return func(ctx context.Context, r *jsonrpc2.Request) error {
return func(ctx context.Context, reply jsonrpc2.Replier, r *jsonrpc2.Request) error {
if ctx.Err() != nil {
ctx := xcontext.Detach(ctx)
return r.Reply(ctx, nil, RequestCancelledError)
return reply(ctx, nil, RequestCancelledError)
}
switch r.Method {`);
side.cases.forEach((v) => {f(v)});
@ -1115,14 +1115,14 @@ function nonstandardRequests() {
}
`)
client.cases.push(`default:
return handler(ctx, r)`)
return handler(ctx, reply, r)`)
server.cases.push(`default:
var params interface{}
if err := json.Unmarshal(*r.Params, &params); err != nil {
return sendParseError(ctx, r, err)
return sendParseError(ctx, reply, r, err)
}
resp, err := server.NonstandardRequest(ctx, r.Method, params)
return r.Reply(ctx, resp, err)
return reply(ctx, resp, err)
`)
}