func (w *Workdir) ChangeFilesOnDisk(ctx context.Context, events []FileEvent) error {
	for _, e := range events {
		switch e.ProtocolEvent.Type {
		case protocol.Deleted:
			fp := w.AbsPath(e.Path)
			if err := os.Remove(fp); err != nil {
				return errors.Errorf("removing %q: %w", e.Path, err)
			}
		case protocol.Changed, protocol.Created:
			if _, err := w.writeFile(ctx, e.Path, e.Content); err != nil {
				return err
			}
		}
	}
	w.sendEvents(ctx, events)
	return nil
}
ChangeFilesOnDisk executes the given on-disk file changes in a batch,
simulating the action of changing branches outside of an editor.
package fake
import (
	"bytes"
	"context"
	"crypto/sha256"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/lsp/protocol"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/span"
	errors "golang.org/x/xerrors"
)
type FileEvent struct {
	Path, Content string
	ProtocolEvent protocol.FileEvent
}
type RelativeTo string
func (r RelativeTo) AbsPath(path string) string {
	fp := filepath.FromSlash(path)
	if filepath.IsAbs(fp) {
		return fp
	}
	return filepath.Join(string(r), filepath.FromSlash(path))
}
func (r RelativeTo) RelPath(fp string) string {
	root := string(r)
	if rel, err := filepath.Rel(root, fp); err == nil && !strings.HasPrefix(rel, "..") {
		return filepath.ToSlash(rel)
	}
	return filepath.ToSlash(fp)
}
func writeTxtar(txt string, rel RelativeTo) error {
	files := UnpackTxt(txt)
	for name, data := range files {
		if err := WriteFileData(name, data, rel); err != nil {
			return errors.Errorf("writing to workdir: %w", err)
		}
	}
	return nil
}
func WriteFileData(path string, content []byte, rel RelativeTo) error {
	content = bytes.ReplaceAll(content, []byte("$SANDBOX_WORKDIR"), []byte(rel))
	fp := rel.AbsPath(path)
	if err := os.MkdirAll(filepath.Dir(fp), 0755); err != nil {
		return errors.Errorf("creating nested directory: %w", err)
	}
	if err := ioutil.WriteFile(fp, []byte(content), 0644); err != nil {
		return errors.Errorf("writing %q: %w", path, err)
	}
	return nil
}
type Workdir struct {
	RelativeTo
	watcherMu sync.Mutex
	watchers  []func(context.Context, []FileEvent)
	fileMu sync.Mutex
	files  map[string]string
}
func NewWorkdir(dir string) *Workdir {
	return &Workdir{RelativeTo: RelativeTo(dir)}
}
func hashFile(data []byte) string {
	return fmt.Sprintf("%x", sha256.Sum256(data))
}
func (w *Workdir) writeInitialFiles(files map[string][]byte) error {
	w.files = map[string]string{}
	for name, data := range files {
		w.files[name] = hashFile(data)
		if err := WriteFileData(name, data, w.RelativeTo); err != nil {
			return errors.Errorf("writing to workdir: %w", err)
		}
	}
	return nil
}
func (w *Workdir) RootURI() protocol.DocumentURI {
	return toURI(string(w.RelativeTo))
}
func (w *Workdir) AddWatcher(watcher func(context.Context, []FileEvent)) {
	w.watcherMu.Lock()
	w.watchers = append(w.watchers, watcher)
	w.watcherMu.Unlock()
}
func (w *Workdir) URI(path string) protocol.DocumentURI {
	return toURI(w.AbsPath(path))
}
func (w *Workdir) URIToPath(uri protocol.DocumentURI) string {
	fp := uri.SpanURI().Filename()
	return w.RelPath(fp)
}
func toURI(fp string) protocol.DocumentURI {
	return protocol.DocumentURI(span.URIFromPath(fp))
}
func (w *Workdir) ReadFile(path string) (string, error) {
	b, err := ioutil.ReadFile(w.AbsPath(path))
	if err != nil {
		return "", err
	}
	return string(b), nil
}
func (w *Workdir) RegexpRange(path, re string) (Pos, Pos, error) {
	content, err := w.ReadFile(path)
	if err != nil {
		return Pos{}, Pos{}, err
	}
	return regexpRange(content, re)
}
func (w *Workdir) RegexpSearch(path string, re string) (Pos, error) {
	content, err := w.ReadFile(path)
	if err != nil {
		return Pos{}, err
	}
	start, _, err := regexpRange(content, re)
	return start, err
}