-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprotocol.go
More file actions
151 lines (125 loc) · 4.05 KB
/
protocol.go
File metadata and controls
151 lines (125 loc) · 4.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// 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 lsp
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"time"
"golang.org/x/exp/jsonrpc2"
)
var (
// RequestCancelledError should be used when a request is cancelled early.
RequestCancelledError = jsonrpc2.NewError(-32800, "JSON RPC cancelled")
)
// detach returns a context that keeps all the values of its parent context
// but detaches from the cancellation and error handling.
func detach(ctx context.Context) context.Context { return detachedContext{ctx} }
type detachedContext struct{ parent context.Context }
func (v detachedContext) Deadline() (time.Time, bool) { return time.Time{}, false }
func (v detachedContext) Done() <-chan struct{} { return nil }
func (v detachedContext) Err() error { return nil }
func (v detachedContext) Value(key any) any { return v.parent.Value(key) }
type ClientCloser interface {
Client
io.Closer
}
type connSender interface {
io.Closer
Notify(ctx context.Context, method string, params any) error
Call(ctx context.Context, method string, params, result any) error
}
type clientDispatcher struct {
sender connSender
}
func (c *clientDispatcher) Close() error {
return c.sender.Close()
}
// ClientDispatcher returns a Client that dispatches LSP requests across the
// given jsonrpc2 connection.
func ClientDispatcher(conn *jsonrpc2.Connection) ClientCloser {
return &clientDispatcher{sender: clientConn{conn}}
}
type clientConn struct {
conn *jsonrpc2.Connection
}
func (c clientConn) Close() error {
return c.conn.Close()
}
func (c clientConn) Notify(ctx context.Context, method string, params any) error {
return c.conn.Notify(ctx, method, params)
}
func (c clientConn) Call(ctx context.Context, method string, params any, result any) error {
call := c.conn.Call(ctx, method, params)
err := call.Await(ctx, result)
if ctx.Err() != nil {
detached := detach(ctx)
_ = c.conn.Notify(detached, "$/cancelRequest", &CancelParams{ID: call.ID().Raw()})
}
return err
}
// ServerDispatcher returns a Server that dispatches LSP requests across the
// given jsonrpc2 connection.
func ServerDispatcher(conn *jsonrpc2.Connection) Server {
return &serverDispatcher{sender: clientConn{conn}}
}
type serverDispatcher struct {
sender connSender
}
func ClientHandler(client Client) jsonrpc2.HandlerFunc {
return func(ctx context.Context, req *jsonrpc2.Request) (any, error) {
if ctx.Err() != nil {
return nil, RequestCancelledError
}
return clientDispatch(ctx, client, req)
}
}
func ServerHandler(server Server) jsonrpc2.HandlerFunc {
return func(ctx context.Context, req *jsonrpc2.Request) (any, error) {
if ctx.Err() != nil {
return nil, RequestCancelledError
}
return serverDispatch(ctx, server, req)
}
}
func Call(ctx context.Context, conn *jsonrpc2.Connection, method string, params any, result any) error {
call := conn.Call(ctx, method, params)
err := call.Await(ctx, result)
if ctx.Err() != nil {
_ = conn.Notify(detach(ctx), "$/cancelRequest", &CancelParams{ID: call.ID().Raw()})
}
return err
}
// UnmarshalJSON unmarshals msg into the variable pointed to by
// params. In JSONRPC, optional messages may be
// "null", in which case it is a no-op.
func UnmarshalJSON(msg json.RawMessage, v any) error {
if len(msg) == 0 || bytes.Equal(msg, []byte("null")) {
return nil
}
return json.Unmarshal(msg, v)
}
// NonNilSlice returns x, or an empty slice if x was nil.
//
// (Many slice fields of protocol structs must be non-nil
// to avoid being encoded as JSON "null".)
func NonNilSlice[T comparable](x []T) []T {
if x == nil {
return []T{}
}
return x
}
// EncodeMessage is a utility to encode LSP protocol messages to JSON.
func EncodeMessage(msg any) ([]byte, error) {
data, err := json.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("failed to marshal message: %w", err)
}
var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("Content-Length: %d\r\n\r\n", len(data)))
buf.Write(data)
return buf.Bytes(), nil
}