| // Copyright 2024 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 lsprpc |
| |
| // This file defines things (and opens backdoors) needed only by tests. |
| |
| import ( |
| "context" |
| "encoding/json" |
| "fmt" |
| |
| "golang.org/x/tools/gopls/internal/protocol" |
| "golang.org/x/tools/internal/event" |
| jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" |
| "golang.org/x/tools/internal/xcontext" |
| ) |
| |
| const HandshakeMethod = handshakeMethod |
| |
| // A ServerFunc is used to construct an LSP server for a given client. |
| type ServerFunc func(context.Context, protocol.ClientCloser) protocol.Server |
| |
| type Canceler struct { |
| Conn *jsonrpc2_v2.Connection |
| } |
| |
| func (c *Canceler) Preempt(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { |
| if req.Method != "$/cancelRequest" { |
| return nil, jsonrpc2_v2.ErrNotHandled |
| } |
| var params protocol.CancelParams |
| if err := json.Unmarshal(req.Params, ¶ms); err != nil { |
| return nil, fmt.Errorf("%w: %v", jsonrpc2_v2.ErrParse, err) |
| } |
| var id jsonrpc2_v2.ID |
| switch raw := params.ID.(type) { |
| case float64: |
| id = jsonrpc2_v2.Int64ID(int64(raw)) |
| case string: |
| id = jsonrpc2_v2.StringID(raw) |
| default: |
| return nil, fmt.Errorf("%w: invalid ID type %T", jsonrpc2_v2.ErrParse, params.ID) |
| } |
| c.Conn.Cancel(id) |
| return nil, nil |
| } |
| |
| type ForwardBinder struct { |
| dialer jsonrpc2_v2.Dialer |
| onBind func(*jsonrpc2_v2.Connection) |
| } |
| |
| func NewForwardBinder(dialer jsonrpc2_v2.Dialer) *ForwardBinder { |
| return &ForwardBinder{ |
| dialer: dialer, |
| } |
| } |
| |
| func (b *ForwardBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) (opts jsonrpc2_v2.ConnectionOptions) { |
| client := protocol.ClientDispatcherV2(conn) |
| clientBinder := NewClientBinder(func(context.Context, protocol.Server) protocol.Client { return client }) |
| |
| serverConn, err := jsonrpc2_v2.Dial(context.Background(), b.dialer, clientBinder) |
| if err != nil { |
| return jsonrpc2_v2.ConnectionOptions{ |
| Handler: jsonrpc2_v2.HandlerFunc(func(context.Context, *jsonrpc2_v2.Request) (interface{}, error) { |
| return nil, fmt.Errorf("%w: %v", jsonrpc2_v2.ErrInternal, err) |
| }), |
| } |
| } |
| |
| if b.onBind != nil { |
| b.onBind(serverConn) |
| } |
| server := protocol.ServerDispatcherV2(serverConn) |
| preempter := &Canceler{ |
| Conn: conn, |
| } |
| detached := xcontext.Detach(ctx) |
| go func() { |
| conn.Wait() |
| if err := serverConn.Close(); err != nil { |
| event.Log(detached, fmt.Sprintf("closing remote connection: %v", err)) |
| } |
| }() |
| return jsonrpc2_v2.ConnectionOptions{ |
| Handler: protocol.ServerHandlerV2(server), |
| Preempter: preempter, |
| } |
| } |
| |
| func NewClientBinder(newClient ClientFunc) *clientBinder { |
| return &clientBinder{newClient} |
| } |
| |
| // A ClientFunc is used to construct an LSP client for a given server. |
| type ClientFunc func(context.Context, protocol.Server) protocol.Client |
| |
| // clientBinder binds an LSP client to an incoming connection. |
| type clientBinder struct { |
| newClient ClientFunc |
| } |
| |
| func (b *clientBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { |
| server := protocol.ServerDispatcherV2(conn) |
| client := b.newClient(ctx, server) |
| return jsonrpc2_v2.ConnectionOptions{ |
| Handler: protocol.ClientHandlerV2(client), |
| } |
| } |
| |
| // HandlerMiddleware is a middleware that only modifies the jsonrpc2 handler. |
| type HandlerMiddleware func(jsonrpc2_v2.Handler) jsonrpc2_v2.Handler |
| |
| // BindHandler transforms a HandlerMiddleware into a Middleware. |
| func BindHandler(hmw HandlerMiddleware) Middleware { |
| return Middleware(func(binder jsonrpc2_v2.Binder) jsonrpc2_v2.Binder { |
| return BinderFunc(func(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { |
| opts := binder.Bind(ctx, conn) |
| opts.Handler = hmw(opts.Handler) |
| return opts |
| }) |
| }) |
| } |
| |
| // The BinderFunc type adapts a bind function to implement the jsonrpc2.Binder |
| // interface. |
| type BinderFunc func(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions |
| |
| func (f BinderFunc) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { |
| return f(ctx, conn) |
| } |
| |
| // Middleware defines a transformation of jsonrpc2 Binders, that may be |
| // composed to build jsonrpc2 servers. |
| type Middleware func(jsonrpc2_v2.Binder) jsonrpc2_v2.Binder |
| |
| var GetGoEnv = getGoEnv |
| |
| type StreamServer = streamServer |