Package fake provides fake implementations of a text editor, LSP client plugin, and Sandbox environment for use in tests.

The Editor type provides a high level API for text editor operations (open/modify/save/close a buffer, jump to definition, etc.), and the Client type exposes an LSP client for the editor that can be connected to a language server. By default, the Editor and Client should be compliant with the LSP spec: their intended use is to verify server compliance with the spec in a variety of environment. Possible future enhancements of these types may allow them to misbehave in configurable ways, but that is not their primary use.

The Sandbox type provides a facility for executing tests with a temporary directory, module proxy, and GOPATH.



This section is empty.


View Source
var (
	ErrNoMatch       = errors.New("no match")
	ErrUnknownBuffer = errors.New("unknown buffer")

ErrNoMatch is returned if a regexp search fails.


func Tempdir

func Tempdir(files map[string][]byte) (string, error)

Tempdir creates a new temp directory with the given txtar-encoded files. It is the responsibility of the caller to call os.RemoveAll on the returned file path when it is no longer needed.

func UnpackTxt

func UnpackTxt(txt string) map[string][]byte

func WriteFileData

func WriteFileData(path string, content []byte, rel RelativeTo) error

WriteFileData writes content to the relative path, replacing the special token $SANDBOX_WORKDIR with the relative root given by rel.

func WriteProxy

func WriteProxy(tmpdir string, files map[string][]byte) (string, error)

WriteProxy creates a new proxy file tree using the txtar-encoded content, and returns its URL.


type CallCounts

type CallCounts struct {
	DidOpen, DidChange, DidSave, DidChangeWatchedFiles, DidClose uint64

type Client

type Client struct {
	// contains filtered or unexported fields

Client is an adapter that converts an *Editor into an LSP Client. It mosly delegates functionality to hooks that can be configured by tests.

func (*Client) ApplyEdit

ApplyEdit applies edits sent from the server.

func (*Client) Configuration

func (c *Client) Configuration(_ context.Context, p *protocol.ParamConfiguration) ([]interface{}, error)

func (*Client) Event

func (c *Client) Event(ctx context.Context, event *interface{}) error

func (*Client) LogMessage

func (c *Client) LogMessage(ctx context.Context, params *protocol.LogMessageParams) error

func (*Client) Progress

func (c *Client) Progress(ctx context.Context, params *protocol.ProgressParams) error

func (*Client) PublishDiagnostics

func (c *Client) PublishDiagnostics(ctx context.Context, params *protocol.PublishDiagnosticsParams) error

func (*Client) RegisterCapability

func (c *Client) RegisterCapability(ctx context.Context, params *protocol.RegistrationParams) error

func (*Client) ShowMessage

func (c *Client) ShowMessage(ctx context.Context, params *protocol.ShowMessageParams) error

func (*Client) ShowMessageRequest

func (c *Client) ShowMessageRequest(ctx context.Context, params *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error)

func (*Client) UnregisterCapability

func (c *Client) UnregisterCapability(ctx context.Context, params *protocol.UnregistrationParams) error

func (*Client) WorkDoneProgressCreate

func (c *Client) WorkDoneProgressCreate(ctx context.Context, params *protocol.WorkDoneProgressCreateParams) error

func (*Client) WorkspaceFolders

func (c *Client) WorkspaceFolders(context.Context) ([]protocol.WorkspaceFolder, error)

type ClientHooks

type ClientHooks struct {
	OnLogMessage             func(context.Context, *protocol.LogMessageParams) error
	OnDiagnostics            func(context.Context, *protocol.PublishDiagnosticsParams) error
	OnWorkDoneProgressCreate func(context.Context, *protocol.WorkDoneProgressCreateParams) error
	OnProgress               func(context.Context, *protocol.ProgressParams) error
	OnShowMessage            func(context.Context, *protocol.ShowMessageParams) error
	OnShowMessageRequest     func(context.Context, *protocol.ShowMessageRequestParams) error
	OnRegistration           func(context.Context, *protocol.RegistrationParams) error
	OnUnregistration         func(context.Context, *protocol.UnregistrationParams) error

ClientHooks are called to handle the corresponding client LSP method.

type Edit

type Edit struct {
	Start, End Pos
	Text       string

Edit represents a single (contiguous) buffer edit.

func NewEdit

func NewEdit(startLine, startColumn, endLine, endColumn int, text string) Edit

NewEdit creates an edit replacing all content between (startLine, startColumn) and (endLine, endColumn) with text.

type Editor

type Editor struct {
	Config EditorConfig

	// Server, client, and sandbox are concurrency safe and written only
	// at construction time, so do not require synchronization.
	Server protocol.Server
	// contains filtered or unexported fields

Editor is a fake editor client. It keeps track of client state and can be used for writing LSP tests.

func NewEditor

func NewEditor(sandbox *Sandbox, config EditorConfig) *Editor

NewEditor Creates a new Editor.

func (*Editor) AcceptCompletion

func (e *Editor) AcceptCompletion(ctx context.Context, path string, pos Pos, item protocol.CompletionItem) error

AcceptCompletion accepts a completion for the given item at the given position.

func (*Editor) ApplyCodeAction

func (e *Editor) ApplyCodeAction(ctx context.Context, action protocol.CodeAction) error

ApplyCodeAction applies the given code action.

func (*Editor) ApplyQuickFixes

func (e *Editor) ApplyQuickFixes(ctx context.Context, path string, rng *protocol.Range, diagnostics []protocol.Diagnostic) error

ApplyQuickFixes requests and performs the quickfix codeAction.

func (*Editor) BufferText

func (e *Editor) BufferText(name string) string

BufferText returns the content of the buffer with the given name.

func (*Editor) BufferVersion

func (e *Editor) BufferVersion(name string) int

BufferVersion returns the current version of the buffer corresponding to name (or 0 if it is not being edited).

func (*Editor) Client

func (e *Editor) Client() *Client

Client returns the LSP client for this editor.

func (*Editor) Close

func (e *Editor) Close(ctx context.Context) error

Close issues the shutdown and exit sequence an editor should.

func (*Editor) CloseBuffer

func (e *Editor) CloseBuffer(ctx context.Context, path string) error

CloseBuffer removes the current buffer (regardless of whether it is saved).

func (*Editor) CodeAction

func (e *Editor) CodeAction(ctx context.Context, path string, rng *protocol.Range, diagnostics []protocol.Diagnostic) ([]protocol.CodeAction, error)

CodeAction executes a codeAction request on the server.

func (*Editor) CodeLens

func (e *Editor) CodeLens(ctx context.Context, path string) ([]protocol.CodeLens, error)

CodeLens executes a codelens request on the server.

func (*Editor) Completion

func (e *Editor) Completion(ctx context.Context, path string, pos Pos) (*protocol.CompletionList, error)

Completion executes a completion request on the server.

func (*Editor) Connect

func (e *Editor) Connect(ctx context.Context, conn jsonrpc2.Conn, hooks ClientHooks) (*Editor, error)

Connect configures the editor to communicate with an LSP server on conn. It is not concurrency safe, and should be called at most once, before using the editor.

It returns the editor, so that it may be called as follows:

editor, err := NewEditor(s).Connect(ctx, conn)

func (*Editor) CreateBuffer

func (e *Editor) CreateBuffer(ctx context.Context, path, content string) error

CreateBuffer creates a new unsaved buffer corresponding to the workdir path, containing the given textual content.

func (*Editor) DocumentHighlight

func (e *Editor) DocumentHighlight(ctx context.Context, path string, pos Pos) ([]protocol.DocumentHighlight, error)
func (e *Editor) DocumentLink(ctx context.Context, path string) ([]protocol.DocumentLink, error)

func (*Editor) EditBuffer

func (e *Editor) EditBuffer(ctx context.Context, path string, edits []Edit) error

EditBuffer applies the given test edits to the buffer identified by path.

func (*Editor) ExecuteCommand

func (e *Editor) ExecuteCommand(ctx context.Context, params *protocol.ExecuteCommandParams) (interface{}, error)

func (*Editor) Exit

func (e *Editor) Exit(ctx context.Context) error

Exit issues the 'exit' LSP notification.

func (*Editor) FormatBuffer

func (e *Editor) FormatBuffer(ctx context.Context, path string) error

FormatBuffer gofmts a Go file.

func (*Editor) GetQuickFixes

func (e *Editor) GetQuickFixes(ctx context.Context, path string, rng *protocol.Range, diagnostics []protocol.Diagnostic) ([]protocol.CodeAction, error)

GetQuickFixes returns the available quick fix code actions.

func (*Editor) GoToDefinition

func (e *Editor) GoToDefinition(ctx context.Context, path string, pos Pos) (string, Pos, error)

GoToDefinition jumps to the definition of the symbol at the given position in an open buffer. It returns the path and position of the resulting jump.

func (*Editor) GoToTypeDefinition

func (e *Editor) GoToTypeDefinition(ctx context.Context, path string, pos Pos) (string, Pos, error)

GoToTypeDefinition jumps to the type definition of the symbol at the given position in an open buffer.

func (*Editor) HasBuffer

func (e *Editor) HasBuffer(name string) bool

HasBuffer reports whether the file name is open in the editor.

func (*Editor) Hover

func (e *Editor) Hover(ctx context.Context, path string, pos Pos) (*protocol.MarkupContent, Pos, error)

Hover triggers a hover at the given position in an open buffer.

func (*Editor) OpenFile

func (e *Editor) OpenFile(ctx context.Context, path string) error

OpenFile creates a buffer for the given workdir-relative file.

func (*Editor) OrganizeImports

func (e *Editor) OrganizeImports(ctx context.Context, path string) error

OrganizeImports requests and performs the source.organizeImports codeAction.

func (*Editor) RefactorRewrite

func (e *Editor) RefactorRewrite(ctx context.Context, path string, rng *protocol.Range) error

RefactorRewrite requests and performs the source.refactorRewrite codeAction.

func (*Editor) References

func (e *Editor) References(ctx context.Context, path string, pos Pos) ([]protocol.Location, error)

References executes a reference request on the server.

func (*Editor) RegexpRange

func (e *Editor) RegexpRange(bufName, re string) (Pos, Pos, error)

RegexpRange returns the first range in the buffer bufName matching re. See RegexpSearch for more information on matching.

func (*Editor) RegexpReplace

func (e *Editor) RegexpReplace(ctx context.Context, path, re, replace string) error

RegexpReplace edits the buffer corresponding to path by replacing the first instance of re, or its first subgroup, with the replace text. See RegexpSearch for more explanation of these two modes. It returns an error if re is invalid, has more than one subgroup, or doesn't match the buffer.

func (*Editor) RegexpSearch

func (e *Editor) RegexpSearch(bufName, re string) (Pos, error)

RegexpSearch returns the position of the first match for re in the buffer bufName. For convenience, RegexpSearch supports the following two modes:

1. If re has no subgroups, return the position of the match for re itself.
2. If re has one subgroup, return the position of the first subgroup.

It returns an error re is invalid, has more than one subgroup, or doesn't match the buffer.

func (*Editor) RunGenerate

func (e *Editor) RunGenerate(ctx context.Context, dir string) error

RunGenerate runs `go generate` non-recursively in the workdir-relative dir path. It does not report any resulting file changes as a watched file change, so must be followed by a call to Workdir.CheckForFileChanges once the generate command has completed. TODO(rFindley): this shouldn't be necessary anymore. Delete it.

func (*Editor) SaveBuffer

func (e *Editor) SaveBuffer(ctx context.Context, path string) error

SaveBuffer writes the content of the buffer specified by the given path to the filesystem.

func (*Editor) SaveBufferWithoutActions

func (e *Editor) SaveBufferWithoutActions(ctx context.Context, path string) error

func (*Editor) SetBufferContent

func (e *Editor) SetBufferContent(ctx context.Context, path, content string) error

func (*Editor) Shutdown

func (e *Editor) Shutdown(ctx context.Context) error

Shutdown issues the 'shutdown' LSP notification.

func (*Editor) Stats

func (e *Editor) Stats() CallCounts

func (*Editor) Symbol

func (e *Editor) Symbol(ctx context.Context, query string) ([]SymbolInformation, error)

Symbol performs a workspace symbol search using query

type EditorConfig

type EditorConfig struct {
	Env        map[string]string
	BuildFlags []string

	// CodeLenses is a map defining whether codelens are enabled, keyed by the
	// codeLens command. CodeLenses which are not present in this map are left in
	// their default state.
	CodeLenses map[string]bool

	// SymbolMatcher is the config associated with the "symbolMatcher" gopls
	// config option.
	SymbolMatcher, SymbolStyle *string

	// LimitWorkspaceScope is true if the user does not want to expand their
	// workspace scope to the entire module.
	LimitWorkspaceScope bool

	// WorkspaceFolders is the workspace folders to configure on the LSP server,
	// relative to the sandbox workdir.
	// As a special case, if WorkspaceFolders is nil the editor defaults to
	// configuring a single workspace folder corresponding to the workdir root.
	// To explicitly send no workspace folders, use an empty (non-nil) slice.
	WorkspaceFolders []string

	// EnableStaticcheck enables staticcheck analyzers.
	EnableStaticcheck bool

	// AllExperiments sets the "allExperiments" configuration, which enables
	// all of gopls's opt-in settings.
	AllExperiments bool

	// Whether to send the current process ID, for testing data that is joined to
	// the PID. This can only be set by one test.
	SendPID bool

	// Whether to edit files with windows line endings.
	WindowsLineEndings bool

	DirectoryFilters []string

	VerboseOutput bool

	ImportShortcut string

EditorConfig configures the editor's LSP session. This is similar to source.UserOptions, but we use a separate type here so that we expose only that configuration which we support.

The zero value for EditorConfig should correspond to its defaults.

type FileEvent

type FileEvent struct {
	Path, Content string
	ProtocolEvent protocol.FileEvent

FileEvent wraps the protocol.FileEvent so that it can be associated with a workdir-relative path.

type Location

type Location struct {
	Path  string
	Range Range

Location is the editor friendly equivalent of protocol.Location

type Pos

type Pos struct {
	Line, Column int

Pos represents a position in a text buffer. Both Line and Column are 0-indexed.

func (Pos) ToProtocolPosition

func (p Pos) ToProtocolPosition() protocol.Position

type Range

type Range struct {
	Start Pos
	End   Pos

Range corresponds to protocol.Range, but uses the editor friend Pos instead of UTF-16 oriented protocol.Position

type RelativeTo

type RelativeTo string

RelativeTo is a helper for operations relative to a given directory.

func (RelativeTo) AbsPath

func (r RelativeTo) AbsPath(path string) string

AbsPath returns an absolute filesystem path for the workdir-relative path.

func (RelativeTo) RelPath

func (r RelativeTo) RelPath(fp string) string

RelPath returns a '/'-encoded path relative to the working directory (or an absolute path if the file is outside of workdir)

type Sandbox

type Sandbox struct {
	Workdir *Workdir
	// contains filtered or unexported fields

Sandbox holds a collection of temporary resources to use for working with Go code in tests.

func NewSandbox

func NewSandbox(config *SandboxConfig) (_ *Sandbox, err error)

NewSandbox creates a collection of named temporary resources, with a working directory populated by the txtar-encoded content in srctxt, and a file-based module proxy populated with the txtar-encoded content in proxytxt.

If rootDir is non-empty, it will be used as the root of temporary directories created for the sandbox. Otherwise, a new temporary directory will be used as root.

func (*Sandbox) Close

func (sb *Sandbox) Close() error

Close removes all state associated with the sandbox.

func (*Sandbox) GOPATH

func (sb *Sandbox) GOPATH() string

GOPATH returns the value of the Sandbox GOPATH.

func (*Sandbox) GoEnv

func (sb *Sandbox) GoEnv() map[string]string

GoEnv returns the default environment variables that can be used for invoking Go commands in the sandbox.

func (*Sandbox) RootDir

func (sb *Sandbox) RootDir() string

func (*Sandbox) RunGoCommand

func (sb *Sandbox) RunGoCommand(ctx context.Context, dir, verb string, args []string, checkForFileChanges bool) error

RunGoCommand executes a go command in the sandbox. If checkForFileChanges is true, the sandbox scans the working directory and emits file change events for any file changes it finds.

type SandboxConfig

type SandboxConfig struct {
	// RootDir sets the base directory to use when creating temporary
	// directories. If not specified, defaults to a new temporary directory.
	RootDir string
	// Files holds a txtar-encoded archive of files to populate the initial state
	// of the working directory.
	// For convenience, the special substring "$SANDBOX_WORKDIR" is replaced with
	// the sandbox's resolved working directory before writing files.
	Files map[string][]byte
	// InGoPath specifies that the working directory should be within the
	// temporary GOPATH.
	InGoPath bool
	// Workdir configures the working directory of the Sandbox. It behaves as
	// follows:
	//  - if set to an absolute path, use that path as the working directory.
	//  - if set to a relative path, create and use that path relative to the
	//    sandbox.
	//  - if unset, default to a the 'work' subdirectory of the sandbox.
	// This option is incompatible with InGoPath or Files.
	Workdir string
	// ProxyFiles holds a txtar-encoded archive of files to populate a file-based
	// Go proxy.
	ProxyFiles map[string][]byte
	// GOPROXY is the explicit GOPROXY value that should be used for the sandbox.
	// This option is incompatible with ProxyFiles.
	GOPROXY string

SandboxConfig controls the behavior of a test sandbox. The zero value defines a reasonable default.

type SymbolInformation

type SymbolInformation struct {
	Name     string
	Kind     protocol.SymbolKind
	Location Location

SymbolInformation is an editor friendly version of protocol.SymbolInformation, with location information transformed to byte offsets. Field names correspond to the protocol type.

type Workdir

type Workdir struct {
	// contains filtered or unexported fields

Workdir is a temporary working directory for tests. It exposes file operations in terms of relative paths, and fakes file watching by triggering events on file operations.

func NewWorkdir

func NewWorkdir(dir string) *Workdir

NewWorkdir writes the txtar-encoded file data in txt to dir, and returns a Workir for operating on these files using

func (*Workdir) AddWatcher

func (w *Workdir) AddWatcher(watcher func(context.Context, []FileEvent))

AddWatcher registers the given func to be called on any file change.

func (*Workdir) ChangeFilesOnDisk

func (w *Workdir) ChangeFilesOnDisk(ctx context.Context, events []FileEvent) error

ChangeFilesOnDisk executes the given on-disk file changes in a batch, simulating the action of changing branches outside of an editor.

func (*Workdir) CheckForFileChanges

func (w *Workdir) CheckForFileChanges(ctx context.Context) error

CheckForFileChanges walks the working directory and checks for any files that have changed since the last poll.

func (*Workdir) ReadFile

func (w *Workdir) ReadFile(path string) (string, error)

ReadFile reads a text file specified by a workdir-relative path.

func (*Workdir) RegexpRange

func (w *Workdir) RegexpRange(path, re string) (Pos, Pos, error)

func (*Workdir) RegexpSearch

func (w *Workdir) RegexpSearch(path string, re string) (Pos, error)

RegexpSearch searches the file corresponding to path for the first position matching re.

func (*Workdir) RemoveFile

func (w *Workdir) RemoveFile(ctx context.Context, path string) error

RemoveFile removes a workdir-relative file path.

func (*Workdir) RootURI

func (w *Workdir) RootURI() protocol.DocumentURI

RootURI returns the root URI for this working directory of this scratch environment.

func (*Workdir) URI

func (w *Workdir) URI(path string) protocol.DocumentURI

URI returns the URI to a the workdir-relative path.

func (*Workdir) URIToPath

func (w *Workdir) URIToPath(uri protocol.DocumentURI) string

URIToPath converts a uri to a workdir-relative path (or an absolute path, if the uri is outside of the workdir).

func (*Workdir) WriteFile

func (w *Workdir) WriteFile(ctx context.Context, path, content string) error

WriteFile writes text file content to a workdir-relative path.

func (*Workdir) WriteFiles

func (w *Workdir) WriteFiles(ctx context.Context, files map[string]string) error

WriteFiles writes the text file content to workdir-relative paths. It batches notifications rather than sending them consecutively.