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  }