初始化仓库
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
package copilot
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// jsonRPCRequest is a JSON-RPC 2.0 request.
|
||||
type jsonRPCRequest struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
ID int64 `json:"id"`
|
||||
Method string `json:"method"`
|
||||
Params any `json:"params,omitempty"`
|
||||
}
|
||||
|
||||
// jsonRPCNotification is a JSON-RPC 2.0 notification (server push, no id).
|
||||
type jsonRPCNotification struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
Params json.RawMessage `json:"params,omitempty"`
|
||||
}
|
||||
|
||||
// jsonRPCResponse is a JSON-RPC 2.0 response.
|
||||
type jsonRPCResponse struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
ID json.RawMessage `json:"id"`
|
||||
Result json.RawMessage `json:"result,omitempty"`
|
||||
Error *jsonRPCError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// jsonRPCError is the error object in a JSON-RPC 2.0 response.
|
||||
type jsonRPCError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data any `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (e *jsonRPCError) Error() string {
|
||||
return fmt.Sprintf("JSON-RPC error %d: %s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
// lspWriter writes Content-Length framed JSON-RPC messages.
|
||||
type lspWriter struct {
|
||||
w io.Writer
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newLSPWriter(w io.Writer) *lspWriter {
|
||||
return &lspWriter{w: w}
|
||||
}
|
||||
|
||||
func (lw *lspWriter) writeMessage(v any) error {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal: %w", err)
|
||||
}
|
||||
|
||||
header := fmt.Sprintf("Content-Length: %d\r\n\r\n", len(data))
|
||||
|
||||
lw.mu.Lock()
|
||||
defer lw.mu.Unlock()
|
||||
|
||||
if _, err := io.WriteString(lw.w, header); err != nil {
|
||||
return fmt.Errorf("write header: %w", err)
|
||||
}
|
||||
if _, err := lw.w.Write(data); err != nil {
|
||||
return fmt.Errorf("write body: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// lspReader reads Content-Length framed JSON-RPC messages.
|
||||
type lspReader struct {
|
||||
reader *bufio.Reader
|
||||
}
|
||||
|
||||
func newLSPReader(r io.Reader) *lspReader {
|
||||
return &lspReader{reader: bufio.NewReaderSize(r, 64*1024)}
|
||||
}
|
||||
|
||||
// readMessage reads one Content-Length framed message and returns the raw JSON body.
|
||||
func (lr *lspReader) readMessage() ([]byte, error) {
|
||||
contentLength := -1
|
||||
for {
|
||||
line, err := lr.reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read header: %w", err)
|
||||
}
|
||||
line = strings.TrimRight(line, "\r\n")
|
||||
if line == "" {
|
||||
// End of headers
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(line, "Content-Length: ") {
|
||||
val := strings.TrimPrefix(line, "Content-Length: ")
|
||||
n, err := strconv.Atoi(strings.TrimSpace(val))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid Content-Length: %w", err)
|
||||
}
|
||||
contentLength = n
|
||||
}
|
||||
// Ignore other headers (Content-Type, etc.)
|
||||
}
|
||||
|
||||
if contentLength < 0 {
|
||||
return nil, fmt.Errorf("missing Content-Length header")
|
||||
}
|
||||
if contentLength > 10*1024*1024 {
|
||||
return nil, fmt.Errorf("Content-Length too large: %d", contentLength)
|
||||
}
|
||||
|
||||
body := make([]byte, contentLength)
|
||||
if _, err := io.ReadFull(lr.reader, body); err != nil {
|
||||
return nil, fmt.Errorf("read body: %w", err)
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// rpcClient manages JSON-RPC request IDs and pending responses.
|
||||
type rpcClient struct {
|
||||
writer *lspWriter
|
||||
nextID atomic.Int64
|
||||
|
||||
pendingMu sync.Mutex
|
||||
pending map[int64]chan *jsonRPCResponse
|
||||
}
|
||||
|
||||
func newRPCClient(w io.Writer) *rpcClient {
|
||||
c := &rpcClient{
|
||||
writer: newLSPWriter(w),
|
||||
pending: make(map[int64]chan *jsonRPCResponse),
|
||||
}
|
||||
c.nextID.Store(1)
|
||||
return c
|
||||
}
|
||||
|
||||
// call sends a JSON-RPC request and returns the response channel.
|
||||
func (c *rpcClient) call(method string, params any) (int64, <-chan *jsonRPCResponse) {
|
||||
id := c.nextID.Add(1) - 1
|
||||
ch := make(chan *jsonRPCResponse, 1)
|
||||
|
||||
c.pendingMu.Lock()
|
||||
c.pending[id] = ch
|
||||
c.pendingMu.Unlock()
|
||||
|
||||
req := jsonRPCRequest{
|
||||
JSONRPC: "2.0",
|
||||
ID: id,
|
||||
Method: method,
|
||||
Params: params,
|
||||
}
|
||||
if err := c.writer.writeMessage(req); err != nil {
|
||||
c.pendingMu.Lock()
|
||||
delete(c.pending, id)
|
||||
c.pendingMu.Unlock()
|
||||
ch <- &jsonRPCResponse{Error: &jsonRPCError{Code: -1, Message: err.Error()}}
|
||||
return id, ch
|
||||
}
|
||||
return id, ch
|
||||
}
|
||||
|
||||
// notify sends a JSON-RPC notification (no response expected).
|
||||
func (c *rpcClient) notify(method string, params any) error {
|
||||
// Notification: no ID field. We use a separate struct to omit it.
|
||||
type notification struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
Params any `json:"params,omitempty"`
|
||||
}
|
||||
return c.writer.writeMessage(notification{
|
||||
JSONRPC: "2.0",
|
||||
Method: method,
|
||||
Params: params,
|
||||
})
|
||||
}
|
||||
|
||||
// dispatch routes a response to the pending caller.
|
||||
func (c *rpcClient) dispatch(resp *jsonRPCResponse) bool {
|
||||
var id int64
|
||||
if err := json.Unmarshal(resp.ID, &id); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
c.pendingMu.Lock()
|
||||
ch, ok := c.pending[id]
|
||||
if ok {
|
||||
delete(c.pending, id)
|
||||
}
|
||||
c.pendingMu.Unlock()
|
||||
|
||||
if ok {
|
||||
ch <- resp
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// respond sends a JSON-RPC 2.0 response to a server-to-client request.
|
||||
func (c *rpcClient) respond(id json.RawMessage, result any, rpcErr *jsonRPCError) error {
|
||||
type response struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
ID json.RawMessage `json:"id"`
|
||||
Result any `json:"result,omitempty"`
|
||||
Error *jsonRPCError `json:"error,omitempty"`
|
||||
}
|
||||
return c.writer.writeMessage(response{
|
||||
JSONRPC: "2.0",
|
||||
ID: id,
|
||||
Result: result,
|
||||
Error: rpcErr,
|
||||
})
|
||||
}
|
||||
|
||||
// cancelAll cancels all pending requests with the given error.
|
||||
func (c *rpcClient) cancelAll(err error) {
|
||||
c.pendingMu.Lock()
|
||||
pending := c.pending
|
||||
c.pending = make(map[int64]chan *jsonRPCResponse)
|
||||
c.pendingMu.Unlock()
|
||||
|
||||
resp := &jsonRPCResponse{
|
||||
Error: &jsonRPCError{Code: -1, Message: err.Error()},
|
||||
}
|
||||
for _, ch := range pending {
|
||||
ch <- resp
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user