1// Copyright 2020 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 fake
6
7import (
8	"context"
9	"fmt"
10
11	"golang.org/x/tools/internal/lsp/protocol"
12)
13
14// ClientHooks are called to handle the corresponding client LSP method.
15type ClientHooks struct {
16	OnLogMessage             func(context.Context, *protocol.LogMessageParams) error
17	OnDiagnostics            func(context.Context, *protocol.PublishDiagnosticsParams) error
18	OnWorkDoneProgressCreate func(context.Context, *protocol.WorkDoneProgressCreateParams) error
19	OnProgress               func(context.Context, *protocol.ProgressParams) error
20	OnShowMessage            func(context.Context, *protocol.ShowMessageParams) error
21	OnShowMessageRequest     func(context.Context, *protocol.ShowMessageRequestParams) error
22	OnRegistration           func(context.Context, *protocol.RegistrationParams) error
23	OnUnregistration         func(context.Context, *protocol.UnregistrationParams) error
24}
25
26// Client is an adapter that converts an *Editor into an LSP Client. It mosly
27// delegates functionality to hooks that can be configured by tests.
28type Client struct {
29	editor *Editor
30	hooks  ClientHooks
31}
32
33func (c *Client) ShowMessage(ctx context.Context, params *protocol.ShowMessageParams) error {
34	if c.hooks.OnShowMessage != nil {
35		return c.hooks.OnShowMessage(ctx, params)
36	}
37	return nil
38}
39
40func (c *Client) ShowMessageRequest(ctx context.Context, params *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) {
41	if c.hooks.OnShowMessageRequest != nil {
42		if err := c.hooks.OnShowMessageRequest(ctx, params); err != nil {
43			return nil, err
44		}
45	}
46	if len(params.Actions) == 0 || len(params.Actions) > 1 {
47		return nil, fmt.Errorf("fake editor cannot handle multiple action items")
48	}
49	return &params.Actions[0], nil
50}
51
52func (c *Client) LogMessage(ctx context.Context, params *protocol.LogMessageParams) error {
53	if c.hooks.OnLogMessage != nil {
54		return c.hooks.OnLogMessage(ctx, params)
55	}
56	return nil
57}
58
59func (c *Client) Event(ctx context.Context, event *interface{}) error {
60	return nil
61}
62
63func (c *Client) PublishDiagnostics(ctx context.Context, params *protocol.PublishDiagnosticsParams) error {
64	if c.hooks.OnDiagnostics != nil {
65		return c.hooks.OnDiagnostics(ctx, params)
66	}
67	return nil
68}
69
70func (c *Client) WorkspaceFolders(context.Context) ([]protocol.WorkspaceFolder, error) {
71	return []protocol.WorkspaceFolder{}, nil
72}
73
74func (c *Client) Configuration(_ context.Context, p *protocol.ParamConfiguration) ([]interface{}, error) {
75	results := make([]interface{}, len(p.Items))
76	for i, item := range p.Items {
77		if item.Section != "gopls" {
78			continue
79		}
80		results[i] = c.editor.configuration()
81	}
82	return results, nil
83}
84
85func (c *Client) RegisterCapability(ctx context.Context, params *protocol.RegistrationParams) error {
86	if c.hooks.OnRegistration != nil {
87		return c.hooks.OnRegistration(ctx, params)
88	}
89	return nil
90}
91
92func (c *Client) UnregisterCapability(ctx context.Context, params *protocol.UnregistrationParams) error {
93	if c.hooks.OnUnregistration != nil {
94		return c.hooks.OnUnregistration(ctx, params)
95	}
96	return nil
97}
98
99func (c *Client) Progress(ctx context.Context, params *protocol.ProgressParams) error {
100	if c.hooks.OnProgress != nil {
101		return c.hooks.OnProgress(ctx, params)
102	}
103	return nil
104}
105
106func (c *Client) WorkDoneProgressCreate(ctx context.Context, params *protocol.WorkDoneProgressCreateParams) error {
107	if c.hooks.OnWorkDoneProgressCreate != nil {
108		return c.hooks.OnWorkDoneProgressCreate(ctx, params)
109	}
110	return nil
111}
112
113// ApplyEdit applies edits sent from the server. Note that as of writing gopls
114// doesn't use this feature, so it is untested.
115func (c *Client) ApplyEdit(ctx context.Context, params *protocol.ApplyWorkspaceEditParams) (*protocol.ApplyWorkspaceEditResponse, error) {
116	if len(params.Edit.Changes) != 0 {
117		return &protocol.ApplyWorkspaceEditResponse{FailureReason: "Edit.Changes is unsupported"}, nil
118	}
119	for _, change := range params.Edit.DocumentChanges {
120		path := c.editor.sandbox.Workdir.URIToPath(change.TextDocument.URI)
121		edits := convertEdits(change.Edits)
122		if err := c.editor.EditBuffer(ctx, path, edits); err != nil {
123			return nil, err
124		}
125	}
126	return &protocol.ApplyWorkspaceEditResponse{Applied: true}, nil
127}
128