1// Copyright 2018 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
5package protocol
6
7import (
8	"context"
9	"encoding/json"
10	"fmt"
11
12	"golang.org/x/tools/internal/jsonrpc2"
13	"golang.org/x/tools/internal/telemetry/log"
14	"golang.org/x/tools/internal/telemetry/trace"
15	"golang.org/x/tools/internal/xcontext"
16)
17
18const (
19	// RequestCancelledError should be used when a request is cancelled early.
20	RequestCancelledError = -32800
21)
22
23type clientHandler struct {
24	jsonrpc2.EmptyHandler
25	client Client
26}
27
28// ClientHandler returns a jsonrpc2.Handler that handles the LSP client
29// protocol.
30func ClientHandler(client Client) jsonrpc2.Handler {
31	return &clientHandler{client: client}
32}
33
34type serverHandler struct {
35	jsonrpc2.EmptyHandler
36	server Server
37}
38
39// ServerHandler returns a jsonrpc2.Handler that handles the LSP server
40// protocol.
41func ServerHandler(server Server) jsonrpc2.Handler {
42	return &serverHandler{server: server}
43}
44
45// ClientDispatcher returns a Client that dispatches LSP requests across the
46// given jsonrpc2 connection.
47func ClientDispatcher(conn *jsonrpc2.Conn) Client {
48	return &clientDispatcher{Conn: conn}
49}
50
51// ServerDispatcher returns a Server that dispatches LSP requests across the
52// given jsonrpc2 connection.
53func ServerDispatcher(conn *jsonrpc2.Conn) Server {
54	return &serverDispatcher{Conn: conn}
55}
56
57// Canceller is a jsonrpc2.Handler that handles LSP request cancellation.
58type Canceller struct{ jsonrpc2.EmptyHandler }
59
60func (Canceller) Request(ctx context.Context, conn *jsonrpc2.Conn, direction jsonrpc2.Direction, r *jsonrpc2.WireRequest) context.Context {
61	if direction == jsonrpc2.Receive && r.Method == "$/cancelRequest" {
62		var params CancelParams
63		if err := json.Unmarshal(*r.Params, &params); err != nil {
64			log.Error(ctx, "", err)
65		} else {
66			v := jsonrpc2.ID{}
67			if n, ok := params.ID.(float64); ok {
68				v.Number = int64(n)
69			} else if s, ok := params.ID.(string); ok {
70				v.Name = s
71			} else {
72				log.Error(ctx, fmt.Sprintf("Request ID %v malformed", params.ID), nil)
73				return ctx
74			}
75			conn.Cancel(v)
76		}
77	}
78	return ctx
79}
80
81func (Canceller) Cancel(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID, cancelled bool) bool {
82	if cancelled {
83		return false
84	}
85	ctx = xcontext.Detach(ctx)
86	ctx, done := trace.StartSpan(ctx, "protocol.canceller")
87	defer done()
88	// Note that only *jsonrpc2.ID implements json.Marshaler.
89	conn.Notify(ctx, "$/cancelRequest", &CancelParams{ID: &id})
90	return true
91}
92
93func (Canceller) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
94	// Hide cancellations from downstream handlers.
95	return r.Method == "$/cancelRequest"
96}
97
98func sendParseError(ctx context.Context, req *jsonrpc2.Request, err error) {
99	if _, ok := err.(*jsonrpc2.Error); !ok {
100		err = jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err)
101	}
102	if err := req.Reply(ctx, nil, err); err != nil {
103		log.Error(ctx, "", err)
104	}
105}
106