github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/lsp/lsprpc/binder.go (about)

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package lsprpc
     6  
     7  import (
     8  	"context"
     9  	"encoding/json"
    10  	"fmt"
    11  
    12  	"github.com/powerman/golang-tools/internal/event"
    13  	jsonrpc2_v2 "github.com/powerman/golang-tools/internal/jsonrpc2_v2"
    14  	"github.com/powerman/golang-tools/internal/lsp/protocol"
    15  	"github.com/powerman/golang-tools/internal/xcontext"
    16  	errors "golang.org/x/xerrors"
    17  )
    18  
    19  // The BinderFunc type adapts a bind function to implement the jsonrpc2.Binder
    20  // interface.
    21  type BinderFunc func(ctx context.Context, conn *jsonrpc2_v2.Connection) (jsonrpc2_v2.ConnectionOptions, error)
    22  
    23  func (f BinderFunc) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) (jsonrpc2_v2.ConnectionOptions, error) {
    24  	return f(ctx, conn)
    25  }
    26  
    27  // Middleware defines a transformation of jsonrpc2 Binders, that may be
    28  // composed to build jsonrpc2 servers.
    29  type Middleware func(jsonrpc2_v2.Binder) jsonrpc2_v2.Binder
    30  
    31  // A ServerFunc is used to construct an LSP server for a given client.
    32  type ServerFunc func(context.Context, protocol.ClientCloser) protocol.Server
    33  
    34  // ServerBinder binds incoming connections to a new server.
    35  type ServerBinder struct {
    36  	newServer ServerFunc
    37  }
    38  
    39  func NewServerBinder(newServer ServerFunc) *ServerBinder {
    40  	return &ServerBinder{newServer: newServer}
    41  }
    42  
    43  func (b *ServerBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) (jsonrpc2_v2.ConnectionOptions, error) {
    44  	client := protocol.ClientDispatcherV2(conn)
    45  	server := b.newServer(ctx, client)
    46  	serverHandler := protocol.ServerHandlerV2(server)
    47  	// Wrap the server handler to inject the client into each request context, so
    48  	// that log events are reflected back to the client.
    49  	wrapped := jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) {
    50  		ctx = protocol.WithClient(ctx, client)
    51  		return serverHandler.Handle(ctx, req)
    52  	})
    53  	preempter := &canceler{
    54  		conn: conn,
    55  	}
    56  	return jsonrpc2_v2.ConnectionOptions{
    57  		Handler:   wrapped,
    58  		Preempter: preempter,
    59  	}, nil
    60  }
    61  
    62  type canceler struct {
    63  	conn *jsonrpc2_v2.Connection
    64  }
    65  
    66  func (c *canceler) Preempt(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) {
    67  	if req.Method != "$/cancelRequest" {
    68  		return nil, jsonrpc2_v2.ErrNotHandled
    69  	}
    70  	var params protocol.CancelParams
    71  	if err := json.Unmarshal(req.Params, &params); err != nil {
    72  		return nil, errors.Errorf("%w: %v", jsonrpc2_v2.ErrParse, err)
    73  	}
    74  	var id jsonrpc2_v2.ID
    75  	switch raw := params.ID.(type) {
    76  	case float64:
    77  		id = jsonrpc2_v2.Int64ID(int64(raw))
    78  	case string:
    79  		id = jsonrpc2_v2.StringID(raw)
    80  	default:
    81  		return nil, errors.Errorf("%w: invalid ID type %T", jsonrpc2_v2.ErrParse, params.ID)
    82  	}
    83  	c.conn.Cancel(id)
    84  	return nil, nil
    85  }
    86  
    87  type ForwardBinder struct {
    88  	dialer jsonrpc2_v2.Dialer
    89  	onBind func(*jsonrpc2_v2.Connection)
    90  }
    91  
    92  func NewForwardBinder(dialer jsonrpc2_v2.Dialer) *ForwardBinder {
    93  	return &ForwardBinder{
    94  		dialer: dialer,
    95  	}
    96  }
    97  
    98  func (b *ForwardBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) (opts jsonrpc2_v2.ConnectionOptions, _ error) {
    99  	client := protocol.ClientDispatcherV2(conn)
   100  	clientBinder := NewClientBinder(func(context.Context, protocol.Server) protocol.Client { return client })
   101  	serverConn, err := jsonrpc2_v2.Dial(context.Background(), b.dialer, clientBinder)
   102  	if err != nil {
   103  		return opts, err
   104  	}
   105  	if b.onBind != nil {
   106  		b.onBind(serverConn)
   107  	}
   108  	server := protocol.ServerDispatcherV2(serverConn)
   109  	preempter := &canceler{
   110  		conn: conn,
   111  	}
   112  	detached := xcontext.Detach(ctx)
   113  	go func() {
   114  		conn.Wait()
   115  		if err := serverConn.Close(); err != nil {
   116  			event.Log(detached, fmt.Sprintf("closing remote connection: %v", err))
   117  		}
   118  	}()
   119  	return jsonrpc2_v2.ConnectionOptions{
   120  		Handler:   protocol.ServerHandlerV2(server),
   121  		Preempter: preempter,
   122  	}, nil
   123  }
   124  
   125  // A ClientFunc is used to construct an LSP client for a given server.
   126  type ClientFunc func(context.Context, protocol.Server) protocol.Client
   127  
   128  // ClientBinder binds an LSP client to an incoming connection.
   129  type ClientBinder struct {
   130  	newClient ClientFunc
   131  }
   132  
   133  func NewClientBinder(newClient ClientFunc) *ClientBinder {
   134  	return &ClientBinder{newClient}
   135  }
   136  
   137  func (b *ClientBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) (jsonrpc2_v2.ConnectionOptions, error) {
   138  	server := protocol.ServerDispatcherV2(conn)
   139  	client := b.newClient(ctx, server)
   140  	return jsonrpc2_v2.ConnectionOptions{
   141  		Handler: protocol.ClientHandlerV2(client),
   142  	}, nil
   143  }