golang.org/x/tools/gopls@v0.15.3/internal/test/integration/fake/client.go (about) 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 5 package fake 6 7 import ( 8 "context" 9 "encoding/json" 10 "fmt" 11 "path" 12 "path/filepath" 13 14 "golang.org/x/tools/gopls/internal/protocol" 15 "golang.org/x/tools/gopls/internal/test/integration/fake/glob" 16 ) 17 18 // ClientHooks are a set of optional hooks called during handling of 19 // the corresponding client method (see protocol.Client for the 20 // LSP server-to-client RPCs) in order to make test expectations 21 // awaitable. 22 type ClientHooks struct { 23 OnLogMessage func(context.Context, *protocol.LogMessageParams) error 24 OnDiagnostics func(context.Context, *protocol.PublishDiagnosticsParams) error 25 OnWorkDoneProgressCreate func(context.Context, *protocol.WorkDoneProgressCreateParams) error 26 OnProgress func(context.Context, *protocol.ProgressParams) error 27 OnShowDocument func(context.Context, *protocol.ShowDocumentParams) error 28 OnShowMessage func(context.Context, *protocol.ShowMessageParams) error 29 OnShowMessageRequest func(context.Context, *protocol.ShowMessageRequestParams) error 30 OnRegisterCapability func(context.Context, *protocol.RegistrationParams) error 31 OnUnregisterCapability func(context.Context, *protocol.UnregistrationParams) error 32 OnApplyEdit func(context.Context, *protocol.ApplyWorkspaceEditParams) error 33 } 34 35 // Client is an implementation of the [protocol.Client] interface 36 // based on the test's fake [Editor]. It mostly delegates 37 // functionality to hooks that can be configured by tests. 38 type Client struct { 39 editor *Editor 40 hooks ClientHooks 41 skipApplyEdits bool // don't apply edits from ApplyEdit downcalls to Editor 42 } 43 44 func (c *Client) CodeLensRefresh(context.Context) error { return nil } 45 46 func (c *Client) InlayHintRefresh(context.Context) error { return nil } 47 48 func (c *Client) DiagnosticRefresh(context.Context) error { return nil } 49 50 func (c *Client) FoldingRangeRefresh(context.Context) error { return nil } 51 52 func (c *Client) InlineValueRefresh(context.Context) error { return nil } 53 54 func (c *Client) SemanticTokensRefresh(context.Context) error { return nil } 55 56 func (c *Client) LogTrace(context.Context, *protocol.LogTraceParams) error { return nil } 57 58 func (c *Client) ShowMessage(ctx context.Context, params *protocol.ShowMessageParams) error { 59 if c.hooks.OnShowMessage != nil { 60 return c.hooks.OnShowMessage(ctx, params) 61 } 62 return nil 63 } 64 65 func (c *Client) ShowMessageRequest(ctx context.Context, params *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) { 66 if c.hooks.OnShowMessageRequest != nil { 67 if err := c.hooks.OnShowMessageRequest(ctx, params); err != nil { 68 return nil, err 69 } 70 } 71 if c.editor.config.MessageResponder != nil { 72 return c.editor.config.MessageResponder(params) 73 } 74 return nil, nil // don't choose, which is effectively dismissing the message 75 } 76 77 func (c *Client) LogMessage(ctx context.Context, params *protocol.LogMessageParams) error { 78 if c.hooks.OnLogMessage != nil { 79 return c.hooks.OnLogMessage(ctx, params) 80 } 81 return nil 82 } 83 84 func (c *Client) Event(ctx context.Context, event *interface{}) error { 85 return nil 86 } 87 88 func (c *Client) PublishDiagnostics(ctx context.Context, params *protocol.PublishDiagnosticsParams) error { 89 if c.hooks.OnDiagnostics != nil { 90 return c.hooks.OnDiagnostics(ctx, params) 91 } 92 return nil 93 } 94 95 func (c *Client) WorkspaceFolders(context.Context) ([]protocol.WorkspaceFolder, error) { 96 return []protocol.WorkspaceFolder{}, nil 97 } 98 99 func (c *Client) Configuration(_ context.Context, p *protocol.ParamConfiguration) ([]interface{}, error) { 100 results := make([]interface{}, len(p.Items)) 101 for i, item := range p.Items { 102 if item.ScopeURI != nil && *item.ScopeURI == "" { 103 return nil, fmt.Errorf(`malformed ScopeURI ""`) 104 } 105 if item.Section == "gopls" { 106 config := c.editor.Config() 107 results[i] = makeSettings(c.editor.sandbox, config, item.ScopeURI) 108 } 109 } 110 return results, nil 111 } 112 113 func (c *Client) RegisterCapability(ctx context.Context, params *protocol.RegistrationParams) error { 114 if c.hooks.OnRegisterCapability != nil { 115 if err := c.hooks.OnRegisterCapability(ctx, params); err != nil { 116 return err 117 } 118 } 119 // Update file watching patterns. 120 // 121 // TODO(rfindley): We could verify more here, like verify that the 122 // registration ID is distinct, and that the capability is not currently 123 // registered. 124 for _, registration := range params.Registrations { 125 if registration.Method == "workspace/didChangeWatchedFiles" { 126 // Marshal and unmarshal to interpret RegisterOptions as 127 // DidChangeWatchedFilesRegistrationOptions. 128 raw, err := json.Marshal(registration.RegisterOptions) 129 if err != nil { 130 return fmt.Errorf("marshaling registration options: %v", err) 131 } 132 var opts protocol.DidChangeWatchedFilesRegistrationOptions 133 if err := json.Unmarshal(raw, &opts); err != nil { 134 return fmt.Errorf("unmarshaling registration options: %v", err) 135 } 136 var globs []*glob.Glob 137 for _, watcher := range opts.Watchers { 138 var globPattern string 139 switch pattern := watcher.GlobPattern.Value.(type) { 140 case protocol.Pattern: 141 globPattern = pattern 142 case protocol.RelativePattern: 143 globPattern = path.Join(filepath.ToSlash(pattern.BaseURI.Path()), pattern.Pattern) 144 } 145 // TODO(rfindley): honor the watch kind. 146 g, err := glob.Parse(globPattern) 147 if err != nil { 148 return fmt.Errorf("error parsing glob pattern %q: %v", watcher.GlobPattern, err) 149 } 150 globs = append(globs, g) 151 } 152 c.editor.mu.Lock() 153 c.editor.watchPatterns = globs 154 c.editor.mu.Unlock() 155 } 156 } 157 return nil 158 } 159 160 func (c *Client) UnregisterCapability(ctx context.Context, params *protocol.UnregistrationParams) error { 161 if c.hooks.OnUnregisterCapability != nil { 162 return c.hooks.OnUnregisterCapability(ctx, params) 163 } 164 return nil 165 } 166 167 func (c *Client) Progress(ctx context.Context, params *protocol.ProgressParams) error { 168 if c.hooks.OnProgress != nil { 169 return c.hooks.OnProgress(ctx, params) 170 } 171 return nil 172 } 173 174 func (c *Client) WorkDoneProgressCreate(ctx context.Context, params *protocol.WorkDoneProgressCreateParams) error { 175 if c.hooks.OnWorkDoneProgressCreate != nil { 176 return c.hooks.OnWorkDoneProgressCreate(ctx, params) 177 } 178 return nil 179 } 180 181 func (c *Client) ShowDocument(ctx context.Context, params *protocol.ShowDocumentParams) (*protocol.ShowDocumentResult, error) { 182 if c.hooks.OnShowDocument != nil { 183 if err := c.hooks.OnShowDocument(ctx, params); err != nil { 184 return nil, err 185 } 186 return &protocol.ShowDocumentResult{Success: true}, nil 187 } 188 return nil, nil 189 } 190 191 func (c *Client) ApplyEdit(ctx context.Context, params *protocol.ApplyWorkspaceEditParams) (*protocol.ApplyWorkspaceEditResult, error) { 192 if len(params.Edit.Changes) != 0 { 193 return &protocol.ApplyWorkspaceEditResult{FailureReason: "Edit.Changes is unsupported"}, nil 194 } 195 if c.hooks.OnApplyEdit != nil { 196 if err := c.hooks.OnApplyEdit(ctx, params); err != nil { 197 return nil, err 198 } 199 } 200 if !c.skipApplyEdits { 201 for _, change := range params.Edit.DocumentChanges { 202 if err := c.editor.applyDocumentChange(ctx, change); err != nil { 203 return nil, err 204 } 205 } 206 } 207 return &protocol.ApplyWorkspaceEditResult{Applied: true}, nil 208 }