зеркало из https://github.com/golang/tools.git
internal/jsonrpc2: add concrete types for Call, Notify and Response
The previous implementation was exposing the details of the wire format and resulted in non idomatic go, detecting the presence of absence of values in fields to deterimine the message type. Now the messages are distinct types and we use type switches instead. Request still exists as an interface to expose the shared behaviour of Call and Notification, as this is the type accepted by handlers. The set of messages is deliberately closed by using a private methods on the interfaces. Change-Id: I2cf15ee3923ef4688670c62896f81f760c77fe04 Reviewed-on: https://go-review.googlesource.com/c/tools/+/228719 Run-TryBot: Ian Cottrell <iancottrell@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Родитель
98173f2f69
Коммит
434f7a8fef
|
@ -14,7 +14,7 @@ import (
|
|||
|
||||
// Handler is invoked to handle incoming requests.
|
||||
// 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
|
||||
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.
|
||||
|
@ -23,24 +23,24 @@ 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, reply Replier, r *Request) error {
|
||||
return reply(ctx, nil, fmt.Errorf("%w: %q", ErrMethodNotFound, r.Method))
|
||||
func MethodNotFound(ctx context.Context, reply Replier, req Request) error {
|
||||
return reply(ctx, nil, fmt.Errorf("%w: %q", ErrMethodNotFound, req.Method()))
|
||||
}
|
||||
|
||||
// MustReplyHandler creates a Handler that panics if the wrapped handler does
|
||||
// not call Reply for every request that it is passed.
|
||||
func MustReplyHandler(handler Handler) Handler {
|
||||
return func(ctx context.Context, reply Replier, req *Request) error {
|
||||
return func(ctx context.Context, reply Replier, req Request) error {
|
||||
called := false
|
||||
err := handler(ctx, func(ctx context.Context, result interface{}, err error) error {
|
||||
if called {
|
||||
panic(fmt.Errorf("request %q replied to more than once", req.Method))
|
||||
panic(fmt.Errorf("request %q replied to more than once", req.Method()))
|
||||
}
|
||||
called = true
|
||||
return reply(ctx, result, err)
|
||||
}, req)
|
||||
if !called {
|
||||
panic(fmt.Errorf("request %q was never replied to", req.Method))
|
||||
panic(fmt.Errorf("request %q was never replied to", req.Method()))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -51,17 +51,17 @@ func MustReplyHandler(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, reply Replier, req *Request) error {
|
||||
if req.ID != nil {
|
||||
wrapped := func(ctx context.Context, reply Replier, req Request) error {
|
||||
if call, ok := req.(*Call); ok {
|
||||
cancelCtx, cancel := context.WithCancel(ctx)
|
||||
ctx = cancelCtx
|
||||
mu.Lock()
|
||||
handling[*req.ID] = cancel
|
||||
handling[call.ID()] = cancel
|
||||
mu.Unlock()
|
||||
innerReply := reply
|
||||
reply = func(ctx context.Context, result interface{}, err error) error {
|
||||
mu.Lock()
|
||||
delete(handling, *req.ID)
|
||||
delete(handling, call.ID())
|
||||
mu.Unlock()
|
||||
return innerReply(ctx, result, err)
|
||||
}
|
||||
|
@ -87,7 +87,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, reply Replier, req *Request) error {
|
||||
return func(ctx context.Context, reply Replier, req Request) error {
|
||||
waitForPrevious := nextRequest
|
||||
nextRequest = make(chan struct{})
|
||||
unlockNext := nextRequest
|
||||
|
|
|
@ -10,7 +10,6 @@ package jsonrpc2
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
@ -33,20 +32,7 @@ type Conn struct {
|
|||
seq int64 // must only be accessed using atomic operations
|
||||
stream Stream
|
||||
pendingMu sync.Mutex // protects the pending map
|
||||
pending map[ID]chan *wireResponse
|
||||
}
|
||||
|
||||
// Request is sent to a server to represent a Call or Notify operaton.
|
||||
type Request struct {
|
||||
// The Wire values of the request.
|
||||
// Method is a string containing the method name to invoke.
|
||||
Method string
|
||||
// Params is either a struct or an array with the parameters of the method.
|
||||
Params *json.RawMessage
|
||||
// The id of this request, used to tie the Response back to the request.
|
||||
// Will be either a string or a number. If not set, the Request is a notify,
|
||||
// and no response is possible.
|
||||
ID *ID
|
||||
pending map[ID]chan *Response
|
||||
}
|
||||
|
||||
type constError string
|
||||
|
@ -58,7 +44,7 @@ func (e constError) Error() string { return string(e) }
|
|||
func NewConn(s Stream) *Conn {
|
||||
conn := &Conn{
|
||||
stream: s,
|
||||
pending: make(map[ID]chan *wireResponse),
|
||||
pending: make(map[ID]chan *Response),
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
@ -67,22 +53,17 @@ func NewConn(s Stream) *Conn {
|
|||
// It will return as soon as the notification has been sent, as no response is
|
||||
// possible.
|
||||
func (c *Conn) Notify(ctx context.Context, method string, params interface{}) (err error) {
|
||||
jsonParams, err := marshalToRaw(params)
|
||||
notify, err := NewNotification(method, params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshaling notify parameters: %v", err)
|
||||
}
|
||||
request := &wireRequest{
|
||||
Method: method,
|
||||
Params: jsonParams,
|
||||
}
|
||||
data, err := json.Marshal(request)
|
||||
data, err := json.Marshal(notify)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshaling notify request: %v", err)
|
||||
}
|
||||
ctx, done := event.StartSpan(ctx, request.Method,
|
||||
tag.Method.Of(request.Method),
|
||||
ctx, done := event.StartSpan(ctx, method,
|
||||
tag.Method.Of(method),
|
||||
tag.RPCDirection.Of(tag.Outbound),
|
||||
tag.RPCID.Of(fmt.Sprintf("%q", request.ID)),
|
||||
)
|
||||
defer func() {
|
||||
recordStatus(ctx, err)
|
||||
|
@ -101,24 +82,19 @@ func (c *Conn) Notify(ctx context.Context, method string, params interface{}) (e
|
|||
func (c *Conn) Call(ctx context.Context, method string, params, result interface{}) (_ ID, err error) {
|
||||
// generate a new request identifier
|
||||
id := ID{number: atomic.AddInt64(&c.seq, 1)}
|
||||
jsonParams, err := marshalToRaw(params)
|
||||
call, err := NewCall(id, method, params)
|
||||
if err != nil {
|
||||
return id, fmt.Errorf("marshaling call parameters: %v", err)
|
||||
}
|
||||
request := &wireRequest{
|
||||
ID: &id,
|
||||
Method: method,
|
||||
Params: jsonParams,
|
||||
}
|
||||
// marshal the request now it is complete
|
||||
data, err := json.Marshal(request)
|
||||
data, err := json.Marshal(call)
|
||||
if err != nil {
|
||||
return id, fmt.Errorf("marshaling call request: %v", err)
|
||||
}
|
||||
ctx, done := event.StartSpan(ctx, request.Method,
|
||||
tag.Method.Of(request.Method),
|
||||
ctx, done := event.StartSpan(ctx, method,
|
||||
tag.Method.Of(method),
|
||||
tag.RPCDirection.Of(tag.Outbound),
|
||||
tag.RPCID.Of(fmt.Sprintf("%q", request.ID)),
|
||||
tag.RPCID.Of(fmt.Sprintf("%q", id)),
|
||||
)
|
||||
defer func() {
|
||||
recordStatus(ctx, err)
|
||||
|
@ -129,7 +105,7 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface
|
|||
// are racing the response. Also add a buffer to rchan, so that if we get a
|
||||
// wire response between the time this call is cancelled and id is deleted
|
||||
// from c.pending, the send to rchan will not block.
|
||||
rchan := make(chan *wireResponse, 1)
|
||||
rchan := make(chan *Response, 1)
|
||||
c.pendingMu.Lock()
|
||||
c.pending[id] = rchan
|
||||
c.pendingMu.Unlock()
|
||||
|
@ -149,13 +125,13 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface
|
|||
select {
|
||||
case response := <-rchan:
|
||||
// is it an error response?
|
||||
if response.Error != nil {
|
||||
return id, response.Error
|
||||
if response.err != nil {
|
||||
return id, response.err
|
||||
}
|
||||
if result == nil || response.Result == nil {
|
||||
if result == nil || len(response.result) == 0 {
|
||||
return id, nil
|
||||
}
|
||||
if err := json.Unmarshal(*response.Result, result); err != nil {
|
||||
if err := json.Unmarshal(response.result, result); err != nil {
|
||||
return id, fmt.Errorf("unmarshaling result: %v", err)
|
||||
}
|
||||
return id, nil
|
||||
|
@ -164,43 +140,22 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface
|
|||
}
|
||||
}
|
||||
|
||||
func replier(conn *Conn, r *Request, spanDone func()) Replier {
|
||||
func replier(conn *Conn, req Request, spanDone func()) Replier {
|
||||
return func(ctx context.Context, result interface{}, err error) error {
|
||||
defer func() {
|
||||
recordStatus(ctx, err)
|
||||
spanDone()
|
||||
}()
|
||||
|
||||
if r.ID == nil {
|
||||
call, ok := req.(*Call)
|
||||
if !ok {
|
||||
// request was a notify, no need to respond
|
||||
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)
|
||||
response, err := NewResponse(call.id, result, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := json.Marshal(response)
|
||||
n, err := conn.stream.Write(ctx, data)
|
||||
event.Record(ctx, tag.SentBytes.Of(n))
|
||||
|
||||
|
@ -227,61 +182,51 @@ func (c *Conn) Run(runCtx context.Context, handler Handler) error {
|
|||
return err
|
||||
}
|
||||
// read a combined message
|
||||
msg := &wireCombined{}
|
||||
if err := json.Unmarshal(data, msg); err != nil {
|
||||
msg, err := DecodeMessage(data)
|
||||
if err != nil {
|
||||
// a badly formed message arrived, log it and continue
|
||||
// we trust the stream to have isolated the error to just this message
|
||||
continue
|
||||
}
|
||||
// Work out whether this is a request or response.
|
||||
switch {
|
||||
case msg.Method != "":
|
||||
// If method is set it must be a request.
|
||||
reqCtx, spanDone := event.StartSpan(runCtx, msg.Method,
|
||||
tag.Method.Of(msg.Method),
|
||||
switch msg := msg.(type) {
|
||||
case Request:
|
||||
tags := []event.Tag{
|
||||
tag.Method.Of(msg.Method()),
|
||||
tag.RPCDirection.Of(tag.Inbound),
|
||||
tag.RPCID.Of(fmt.Sprintf("%q", msg.ID)),
|
||||
)
|
||||
{}, // reserved for ID if present
|
||||
}
|
||||
if call, ok := msg.(*Call); ok {
|
||||
tags[len(tags)-1] = tag.RPCID.Of(fmt.Sprintf("%q", call.ID()))
|
||||
} else {
|
||||
tags = tags[:len(tags)-1]
|
||||
}
|
||||
reqCtx, spanDone := event.StartSpan(runCtx, msg.Method(), tags...)
|
||||
event.Record(reqCtx,
|
||||
tag.Started.Of(1),
|
||||
tag.ReceivedBytes.Of(n))
|
||||
|
||||
req := &Request{
|
||||
Method: msg.Method,
|
||||
Params: msg.Params,
|
||||
ID: msg.ID,
|
||||
}
|
||||
|
||||
if err := handler(reqCtx, replier(c, req, spanDone), req); err != nil {
|
||||
if err := handler(reqCtx, replier(c, msg, spanDone), msg); err != nil {
|
||||
// delivery failed, not much we can do
|
||||
event.Error(reqCtx, "jsonrpc2 message delivery failed", err)
|
||||
}
|
||||
case msg.ID != nil:
|
||||
case *Response:
|
||||
// If method is not set, this should be a response, in which case we must
|
||||
// have an id to send the response back to the caller.
|
||||
c.pendingMu.Lock()
|
||||
rchan, ok := c.pending[*msg.ID]
|
||||
rchan, ok := c.pending[msg.id]
|
||||
c.pendingMu.Unlock()
|
||||
if ok {
|
||||
response := &wireResponse{
|
||||
Result: msg.Result,
|
||||
Error: msg.Error,
|
||||
ID: msg.ID,
|
||||
}
|
||||
rchan <- response
|
||||
rchan <- msg
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func marshalToRaw(obj interface{}) (*json.RawMessage, error) {
|
||||
func marshalToRaw(obj interface{}) (json.RawMessage, error) {
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return json.RawMessage{}, err
|
||||
}
|
||||
raw := json.RawMessage(data)
|
||||
return &raw, nil
|
||||
return json.RawMessage(data), nil
|
||||
}
|
||||
|
||||
func recordStatus(ctx context.Context, err error) {
|
||||
|
|
|
@ -134,28 +134,28 @@ 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, reply jsonrpc2.Replier, req *jsonrpc2.Request) error {
|
||||
switch req.Method {
|
||||
return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error {
|
||||
switch req.Method() {
|
||||
case "no_args":
|
||||
if req.Params != nil {
|
||||
if len(req.Params()) > 0 {
|
||||
return reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
|
||||
}
|
||||
return reply(ctx, true, nil)
|
||||
case "one_string":
|
||||
var v string
|
||||
if err := json.Unmarshal(*req.Params, &v); err != nil {
|
||||
if err := json.Unmarshal(req.Params(), &v); err != nil {
|
||||
return reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err))
|
||||
}
|
||||
return reply(ctx, "got:"+v, nil)
|
||||
case "one_number":
|
||||
var v int
|
||||
if err := json.Unmarshal(*req.Params, &v); err != nil {
|
||||
if err := json.Unmarshal(req.Params(), &v); err != nil {
|
||||
return reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err))
|
||||
}
|
||||
return reply(ctx, fmt.Sprintf("got:%d", v), nil)
|
||||
case "join":
|
||||
var v []string
|
||||
if err := json.Unmarshal(*req.Params, &v); err != nil {
|
||||
if err := json.Unmarshal(req.Params(), &v); err != nil {
|
||||
return reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err))
|
||||
}
|
||||
return reply(ctx, path.Join(v...), nil)
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package jsonrpc2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Message is the interface to all jsonrpc2 message types.
|
||||
// They share no common functionality, but are a closed set of concrete types
|
||||
// that are allowed to implement this interface. The message types are *Call,
|
||||
// *Notification and *Response.
|
||||
type Message interface {
|
||||
// isJSONRPC2Message is used to make the set of message implementations a
|
||||
// closed set.
|
||||
isJSONRPC2Message()
|
||||
}
|
||||
|
||||
// Request is the shared interface to jsonrpc2 messages that request
|
||||
// a method be invoked.
|
||||
// The request types are a closed set of *Call and *Notification.
|
||||
type Request interface {
|
||||
Message
|
||||
// Method is a string containing the method name to invoke.
|
||||
Method() string
|
||||
// Params is either a struct or an array with the parameters of the method.
|
||||
Params() json.RawMessage
|
||||
// isJSONRPC2Request is used to make the set of request implementations closed.
|
||||
isJSONRPC2Request()
|
||||
}
|
||||
|
||||
// Notification is a request for which a response cannot occur, and as such
|
||||
// it has not ID.
|
||||
type Notification struct {
|
||||
// Method is a string containing the method name to invoke.
|
||||
method string
|
||||
params json.RawMessage
|
||||
}
|
||||
|
||||
// Call is a request that expects a response.
|
||||
// The response will have a matching ID.
|
||||
type Call struct {
|
||||
// Method is a string containing the method name to invoke.
|
||||
method string
|
||||
// Params is either a struct or an array with the parameters of the method.
|
||||
params json.RawMessage
|
||||
// id of this request, used to tie the Response back to the request.
|
||||
id ID
|
||||
}
|
||||
|
||||
// Response is a reply to a Call.
|
||||
// It will have the same ID as the call it is a response to.
|
||||
type Response struct {
|
||||
// result is the content of the response.
|
||||
result json.RawMessage
|
||||
// err is set only if the call failed.
|
||||
err error
|
||||
// ID of the request this is a response to.
|
||||
id ID
|
||||
}
|
||||
|
||||
// NewNotification constructs a new Notification message for the supplied
|
||||
// method and parameters.
|
||||
func NewNotification(method string, params interface{}) (*Notification, error) {
|
||||
p, merr := marshalToRaw(params)
|
||||
return &Notification{method: method, params: p}, merr
|
||||
}
|
||||
|
||||
func (msg *Notification) Method() string { return msg.method }
|
||||
func (msg *Notification) Params() json.RawMessage { return msg.params }
|
||||
func (msg *Notification) isJSONRPC2Message() {}
|
||||
func (msg *Notification) isJSONRPC2Request() {}
|
||||
|
||||
func (n *Notification) MarshalJSON() ([]byte, error) {
|
||||
msg := wireRequest{Method: n.method, Params: &n.params}
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return data, fmt.Errorf("marshaling notification: %w", err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (n *Notification) UnmarshalJSON(data []byte) error {
|
||||
msg := wireRequest{}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return fmt.Errorf("unmarshaling notification: %w", err)
|
||||
}
|
||||
n.method = msg.Method
|
||||
if msg.Params != nil {
|
||||
n.params = *msg.Params
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCall constructs a new Call message for the supplied ID, method and
|
||||
// parameters.
|
||||
func NewCall(id ID, method string, params interface{}) (*Call, error) {
|
||||
p, merr := marshalToRaw(params)
|
||||
return &Call{id: id, method: method, params: p}, merr
|
||||
}
|
||||
|
||||
func (msg *Call) Method() string { return msg.method }
|
||||
func (msg *Call) Params() json.RawMessage { return msg.params }
|
||||
func (msg *Call) ID() ID { return msg.id }
|
||||
func (msg *Call) isJSONRPC2Message() {}
|
||||
func (msg *Call) isJSONRPC2Request() {}
|
||||
|
||||
func (c *Call) MarshalJSON() ([]byte, error) {
|
||||
msg := wireRequest{Method: c.method, Params: &c.params, ID: &c.id}
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return data, fmt.Errorf("marshaling call: %w", err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (c *Call) UnmarshalJSON(data []byte) error {
|
||||
msg := wireRequest{}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return fmt.Errorf("unmarshaling call: %w", err)
|
||||
}
|
||||
c.method = msg.Method
|
||||
if msg.Params != nil {
|
||||
c.params = *msg.Params
|
||||
}
|
||||
if msg.ID != nil {
|
||||
c.id = *msg.ID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewResponse constructs a new Response message that is a reply to the
|
||||
// supplied. If err is set result may be ignored.
|
||||
func NewResponse(id ID, result interface{}, err error) (*Response, error) {
|
||||
r, merr := marshalToRaw(result)
|
||||
return &Response{id: id, result: r, err: err}, merr
|
||||
}
|
||||
|
||||
func (msg *Response) ID() ID { return msg.id }
|
||||
func (msg *Response) Result() json.RawMessage { return msg.result }
|
||||
func (msg *Response) Err() error { return msg.err }
|
||||
func (msg *Response) isJSONRPC2Message() {}
|
||||
|
||||
func (r *Response) MarshalJSON() ([]byte, error) {
|
||||
msg := &wireResponse{Result: &r.result, Error: toWireError(r.err), ID: &r.id}
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return data, fmt.Errorf("marshaling notification: %w", err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func toWireError(err error) *wireError {
|
||||
if err == nil {
|
||||
// no error, the response is complete
|
||||
return nil
|
||||
}
|
||||
if err, ok := err.(*wireError); ok {
|
||||
// already a wire error, just use it
|
||||
return err
|
||||
}
|
||||
result := &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
|
||||
result.Code = wrapped.Code
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (r *Response) UnmarshalJSON(data []byte) error {
|
||||
msg := wireResponse{}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return fmt.Errorf("unmarshaling jsonrpc response: %w", err)
|
||||
}
|
||||
if msg.Result != nil {
|
||||
r.result = *msg.Result
|
||||
}
|
||||
if msg.Error != nil {
|
||||
r.err = msg.Error
|
||||
}
|
||||
if msg.ID != nil {
|
||||
r.id = *msg.ID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeMessage(data []byte) (Message, error) {
|
||||
msg := wireCombined{}
|
||||
if err := json.Unmarshal(data, &msg); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling jsonrpc message: %w", err)
|
||||
}
|
||||
if msg.Method == "" {
|
||||
// no method, should be a response
|
||||
if msg.ID == nil {
|
||||
return nil, ErrInvalidRequest
|
||||
}
|
||||
response := &Response{id: *msg.ID}
|
||||
if msg.Error != nil {
|
||||
response.err = msg.Error
|
||||
}
|
||||
if msg.Result != nil {
|
||||
response.result = *msg.Result
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
// has a method, must be a request
|
||||
if msg.ID == nil {
|
||||
// request with no ID is a notify
|
||||
notify := &Notification{method: msg.Method}
|
||||
if msg.Params != nil {
|
||||
notify.params = *msg.Params
|
||||
}
|
||||
return notify, nil
|
||||
}
|
||||
// request with an ID, must be a call
|
||||
call := &Call{method: msg.Method, id: *msg.ID}
|
||||
if msg.Params != nil {
|
||||
call.params = *msg.Params
|
||||
}
|
||||
return call, nil
|
||||
}
|
|
@ -16,7 +16,7 @@ type msg struct {
|
|||
Msg string
|
||||
}
|
||||
|
||||
func fakeHandler(ctx context.Context, reply jsonrpc2.Replier, req *jsonrpc2.Request) error {
|
||||
func fakeHandler(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error {
|
||||
return reply(ctx, &msg{"pong"}, nil)
|
||||
}
|
||||
|
||||
|
|
|
@ -416,10 +416,10 @@ 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, reply jsonrpc2.Replier, 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" {
|
||||
if r.Method() == "exit" {
|
||||
ForwarderExitFunc(0)
|
||||
// reply nil here to consume the message: in
|
||||
// tests, ForwarderExitFunc may be overridden to something that doesn't
|
||||
|
@ -486,12 +486,12 @@ const (
|
|||
)
|
||||
|
||||
func handshaker(client *debugClient, goplsPath string, handler jsonrpc2.Handler) jsonrpc2.Handler {
|
||||
return func(ctx context.Context, reply jsonrpc2.Replier, r *jsonrpc2.Request) error {
|
||||
switch r.Method {
|
||||
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, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), &req); err != nil {
|
||||
sendError(ctx, reply, err)
|
||||
return nil
|
||||
}
|
||||
client.debugAddress = req.DebugAddr
|
||||
|
@ -532,7 +532,7 @@ func handshaker(client *debugClient, goplsPath string, handler jsonrpc2.Handler)
|
|||
}
|
||||
}
|
||||
|
||||
func sendError(ctx context.Context, reply jsonrpc2.Replier, req *jsonrpc2.Request, err error) {
|
||||
func sendError(ctx context.Context, reply jsonrpc2.Replier, err error) {
|
||||
err = fmt.Errorf("%w: %v", jsonrpc2.ErrParse, err)
|
||||
if err := reply(ctx, nil, err); err != nil {
|
||||
event.Error(ctx, "", err)
|
||||
|
|
|
@ -40,20 +40,20 @@ func Handlers(handler jsonrpc2.Handler) jsonrpc2.Handler {
|
|||
|
||||
func CancelHandler(handler jsonrpc2.Handler) jsonrpc2.Handler {
|
||||
handler, canceller := jsonrpc2.CancelHandler(handler)
|
||||
return func(ctx context.Context, reply jsonrpc2.Replier, req *jsonrpc2.Request) error {
|
||||
if req.Method != "$/cancelRequest" {
|
||||
return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error {
|
||||
if req.Method() != "$/cancelRequest" {
|
||||
return handler(ctx, reply, req)
|
||||
}
|
||||
var params CancelParams
|
||||
if err := json.Unmarshal(*req.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, req, err)
|
||||
if err := json.Unmarshal(req.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, 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, reply, req, fmt.Errorf("request ID %v malformed", params.ID))
|
||||
return sendParseError(ctx, reply, fmt.Errorf("request ID %v malformed", params.ID))
|
||||
}
|
||||
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, reply jsonrpc2.Replier, req *jsonrpc2.Request, err error) error {
|
||||
func sendParseError(ctx context.Context, reply jsonrpc2.Replier, err error) error {
|
||||
return reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err))
|
||||
}
|
||||
|
|
|
@ -32,92 +32,92 @@ type Client interface {
|
|||
}
|
||||
|
||||
func ClientHandler(client Client, handler jsonrpc2.Handler) jsonrpc2.Handler {
|
||||
return func(ctx context.Context, reply jsonrpc2.Replier, 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 reply(ctx, nil, RequestCancelledError)
|
||||
}
|
||||
switch r.Method {
|
||||
switch r.Method() {
|
||||
case "window/showMessage": // notif
|
||||
var params ShowMessageParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := client.ShowMessage(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "window/logMessage": // notif
|
||||
var params LogMessageParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := client.LogMessage(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "telemetry/event": // notif
|
||||
var params interface{}
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := client.Event(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "textDocument/publishDiagnostics": // notif
|
||||
var params PublishDiagnosticsParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := client.PublishDiagnostics(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "$/progress": // notif
|
||||
var params ProgressParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := client.Progress(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "workspace/workspaceFolders": // req
|
||||
if r.Params != nil {
|
||||
if len(r.Params()) > 0 {
|
||||
return reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
|
||||
}
|
||||
resp, err := client.WorkspaceFolders(ctx)
|
||||
return reply(ctx, resp, err)
|
||||
case "workspace/configuration": // req
|
||||
var params ParamConfiguration
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := client.Configuration(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "window/workDoneProgress/create": // req
|
||||
var params WorkDoneProgressCreateParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := client.WorkDoneProgressCreate(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "client/registerCapability": // req
|
||||
var params RegistrationParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := client.RegisterCapability(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "client/unregisterCapability": // req
|
||||
var params UnregistrationParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := client.UnregisterCapability(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "window/showMessageRequest": // req
|
||||
var params ShowMessageRequestParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := client.ShowMessageRequest(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "workspace/applyEdit": // req
|
||||
var params ApplyWorkspaceEditParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := client.ApplyEdit(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
|
|
|
@ -70,30 +70,30 @@ type Server interface {
|
|||
}
|
||||
|
||||
func ServerHandler(server Server, handler jsonrpc2.Handler) jsonrpc2.Handler {
|
||||
return func(ctx context.Context, reply jsonrpc2.Replier, 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 reply(ctx, nil, RequestCancelledError)
|
||||
}
|
||||
switch r.Method {
|
||||
switch r.Method() {
|
||||
case "workspace/didChangeWorkspaceFolders": // notif
|
||||
var params DidChangeWorkspaceFoldersParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := server.DidChangeWorkspaceFolders(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "window/workDoneProgress/cancel": // notif
|
||||
var params WorkDoneProgressCancelParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := server.WorkDoneProgressCancel(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "initialized": // notif
|
||||
var params InitializedParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := server.Initialized(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
|
@ -101,324 +101,324 @@ func ServerHandler(server Server, handler jsonrpc2.Handler) jsonrpc2.Handler {
|
|||
return server.Exit(ctx)
|
||||
case "workspace/didChangeConfiguration": // notif
|
||||
var params DidChangeConfigurationParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := server.DidChangeConfiguration(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "textDocument/didOpen": // notif
|
||||
var params DidOpenTextDocumentParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := server.DidOpen(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "textDocument/didChange": // notif
|
||||
var params DidChangeTextDocumentParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := server.DidChange(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "textDocument/didClose": // notif
|
||||
var params DidCloseTextDocumentParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := server.DidClose(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "textDocument/didSave": // notif
|
||||
var params DidSaveTextDocumentParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := server.DidSave(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "textDocument/willSave": // notif
|
||||
var params WillSaveTextDocumentParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := server.WillSave(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "workspace/didChangeWatchedFiles": // notif
|
||||
var params DidChangeWatchedFilesParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := server.DidChangeWatchedFiles(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "$/setTraceNotification": // notif
|
||||
var params SetTraceParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := server.SetTraceNotification(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "$/logTraceNotification": // notif
|
||||
var params LogTraceParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err := server.LogTraceNotification(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)
|
||||
case "textDocument/implementation": // req
|
||||
var params ImplementationParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.Implementation(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/typeDefinition": // req
|
||||
var params TypeDefinitionParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.TypeDefinition(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/documentColor": // req
|
||||
var params DocumentColorParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.DocumentColor(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/colorPresentation": // req
|
||||
var params ColorPresentationParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.ColorPresentation(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/foldingRange": // req
|
||||
var params FoldingRangeParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.FoldingRange(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/declaration": // req
|
||||
var params DeclarationParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.Declaration(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/selectionRange": // req
|
||||
var params SelectionRangeParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.SelectionRange(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "initialize": // req
|
||||
var params ParamInitialize
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.Initialize(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "shutdown": // req
|
||||
if r.Params != nil {
|
||||
if len(r.Params()) > 0 {
|
||||
return reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
|
||||
}
|
||||
err := server.Shutdown(ctx)
|
||||
return reply(ctx, nil, err)
|
||||
case "textDocument/willSaveWaitUntil": // req
|
||||
var params WillSaveTextDocumentParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.WillSaveWaitUntil(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/completion": // req
|
||||
var params CompletionParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.Completion(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "completionItem/resolve": // req
|
||||
var params CompletionItem
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.Resolve(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/hover": // req
|
||||
var params HoverParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.Hover(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/signatureHelp": // req
|
||||
var params SignatureHelpParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.SignatureHelp(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/definition": // req
|
||||
var params DefinitionParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.Definition(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/references": // req
|
||||
var params ReferenceParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.References(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/documentHighlight": // req
|
||||
var params DocumentHighlightParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.DocumentHighlight(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/documentSymbol": // req
|
||||
var params DocumentSymbolParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.DocumentSymbol(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/codeAction": // req
|
||||
var params CodeActionParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.CodeAction(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "workspace/symbol": // req
|
||||
var params WorkspaceSymbolParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.Symbol(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/codeLens": // req
|
||||
var params CodeLensParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.CodeLens(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "codeLens/resolve": // req
|
||||
var params CodeLens
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.ResolveCodeLens(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/documentLink": // req
|
||||
var params DocumentLinkParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.DocumentLink(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "documentLink/resolve": // req
|
||||
var params DocumentLink
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.ResolveDocumentLink(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/formatting": // req
|
||||
var params DocumentFormattingParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.Formatting(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/rangeFormatting": // req
|
||||
var params DocumentRangeFormattingParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.RangeFormatting(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/onTypeFormatting": // req
|
||||
var params DocumentOnTypeFormattingParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.OnTypeFormatting(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/rename": // req
|
||||
var params RenameParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.Rename(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/prepareRename": // req
|
||||
var params PrepareRenameParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.PrepareRename(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "workspace/executeCommand": // req
|
||||
var params ExecuteCommandParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.ExecuteCommand(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/prepareCallHierarchy": // req
|
||||
var params CallHierarchyPrepareParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.PrepareCallHierarchy(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "callHierarchy/incomingCalls": // req
|
||||
var params CallHierarchyIncomingCallsParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.IncomingCalls(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "callHierarchy/outgoingCalls": // req
|
||||
var params CallHierarchyOutgoingCallsParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.OutgoingCalls(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/semanticTokens": // req
|
||||
var params SemanticTokensParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.SemanticTokens(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/semanticTokens/edits": // req
|
||||
var params SemanticTokensEditsParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.SemanticTokensEdits(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
case "textDocument/semanticTokens/range": // req
|
||||
var params SemanticTokensRangeParams
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.SemanticTokensRange(ctx, ¶ms)
|
||||
return reply(ctx, resp, err)
|
||||
default:
|
||||
var params interface{}
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.NonstandardRequest(ctx, r.Method, params)
|
||||
resp, err := server.NonstandardRequest(ctx, r.Method(), params)
|
||||
return reply(ctx, resp, err)
|
||||
|
||||
}
|
||||
|
|
|
@ -907,7 +907,7 @@ let server: side = {
|
|||
};
|
||||
|
||||
// commonly used output
|
||||
const notNil = `if r.Params != nil {
|
||||
const notNil = `if len(r.Params()) > 0 {
|
||||
return reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
|
||||
}`;
|
||||
|
||||
|
@ -923,8 +923,8 @@ function goNot(side: side, m: string) {
|
|||
let case1 = notNil;
|
||||
if (a != '' && a != 'void') {
|
||||
case1 = `var params ${a}
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
err:= ${side.name}.${nm}(ctx, ¶ms)
|
||||
return reply(ctx, nil, err)`
|
||||
|
@ -958,8 +958,8 @@ function goReq(side: side, m: string) {
|
|||
if (a != '') {
|
||||
if (extraTypes.has('Param' + nm)) a = 'Param' + nm
|
||||
case1 = `var params ${a}
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}`;
|
||||
}
|
||||
const arg2 = a == '' ? '' : ', ¶ms';
|
||||
|
@ -1082,12 +1082,12 @@ 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, reply jsonrpc2.Replier, 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 reply(ctx, nil, RequestCancelledError)
|
||||
}
|
||||
switch r.Method {`);
|
||||
switch r.Method() {`);
|
||||
side.cases.forEach((v) => {f(v)});
|
||||
f(`
|
||||
}
|
||||
|
@ -1118,10 +1118,10 @@ function nonstandardRequests() {
|
|||
return handler(ctx, reply, r)`)
|
||||
server.cases.push(`default:
|
||||
var params interface{}
|
||||
if err := json.Unmarshal(*r.Params, ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, r, err)
|
||||
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
|
||||
return sendParseError(ctx, reply, err)
|
||||
}
|
||||
resp, err := server.NonstandardRequest(ctx, r.Method, params)
|
||||
resp, err := server.NonstandardRequest(ctx, r.Method(), params)
|
||||
return reply(ctx, resp, err)
|
||||
`)
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче