2021-09-04 03:44:04 +08:00
|
|
|
package modes
|
2019-08-31 21:21:06 +08:00
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2021-02-12 02:49:00 +08:00
|
|
|
"fmt"
|
2019-11-14 06:42:33 +08:00
|
|
|
"path/filepath"
|
2019-11-17 05:17:11 +08:00
|
|
|
"runtime"
|
|
|
|
"strings"
|
2019-08-31 21:21:06 +08:00
|
|
|
"testing"
|
2021-03-27 09:34:31 +08:00
|
|
|
"time"
|
2019-08-31 21:21:06 +08:00
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
"src.elv.sh/pkg/cli"
|
2021-01-27 09:28:38 +08:00
|
|
|
. "src.elv.sh/pkg/cli/clitest"
|
|
|
|
"src.elv.sh/pkg/cli/term"
|
2021-06-19 08:52:51 +08:00
|
|
|
"src.elv.sh/pkg/store/storedefs"
|
2021-01-27 09:28:38 +08:00
|
|
|
"src.elv.sh/pkg/testutil"
|
|
|
|
"src.elv.sh/pkg/ui"
|
2019-08-31 21:21:06 +08:00
|
|
|
)
|
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
type locationStore struct {
|
2021-06-19 08:52:51 +08:00
|
|
|
storedDirs []storedefs.Dir
|
2019-11-15 02:48:24 +08:00
|
|
|
dirsError error
|
|
|
|
chdir func(dir string) error
|
2019-11-16 18:42:54 +08:00
|
|
|
wd string
|
2019-08-31 21:21:06 +08:00
|
|
|
}
|
|
|
|
|
2021-06-19 08:52:51 +08:00
|
|
|
func (ts locationStore) Dirs(blacklist map[string]struct{}) ([]storedefs.Dir, error) {
|
|
|
|
dirs := []storedefs.Dir{}
|
2019-11-15 02:48:24 +08:00
|
|
|
for _, dir := range ts.storedDirs {
|
|
|
|
if _, ok := blacklist[dir.Path]; ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
dirs = append(dirs, dir)
|
2019-08-31 21:21:06 +08:00
|
|
|
}
|
2019-11-15 02:48:24 +08:00
|
|
|
return dirs, ts.dirsError
|
2019-08-31 21:21:06 +08:00
|
|
|
}
|
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
func (ts locationStore) Chdir(dir string) error {
|
2019-08-31 21:21:06 +08:00
|
|
|
if ts.chdir == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return ts.chdir(dir)
|
|
|
|
}
|
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
func (ts locationStore) Getwd() (string, error) {
|
2019-11-16 18:42:54 +08:00
|
|
|
return ts.wd, nil
|
|
|
|
}
|
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
func TestNewLocation_NoStore(t *testing.T) {
|
2019-12-06 18:12:33 +08:00
|
|
|
f := Setup()
|
|
|
|
defer f.Stop()
|
2019-08-31 21:21:06 +08:00
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
_, err := NewLocation(f.App, LocationSpec{})
|
|
|
|
if err != errNoDirectoryHistoryStore {
|
|
|
|
t.Error("want errNoDirectoryHistoryStore")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNewLocation_StoreError(t *testing.T) {
|
|
|
|
f := Setup()
|
|
|
|
defer f.Stop()
|
2019-08-31 21:21:06 +08:00
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
_, err := NewLocation(f.App,
|
|
|
|
LocationSpec{Store: locationStore{dirsError: errors.New("ERROR")}})
|
|
|
|
if err.Error() != "db error: ERROR" {
|
|
|
|
t.Error("want db error")
|
|
|
|
}
|
2019-08-31 21:21:06 +08:00
|
|
|
}
|
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
func TestLocation_FullWorkflow(t *testing.T) {
|
2021-08-07 05:18:09 +08:00
|
|
|
home := testutil.InTempHome(t)
|
2019-12-06 18:12:33 +08:00
|
|
|
f := Setup()
|
|
|
|
defer f.Stop()
|
2019-08-31 21:21:06 +08:00
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
errChdir := errors.New("mock chdir error")
|
|
|
|
chdirCh := make(chan string, 100)
|
2021-06-19 08:52:51 +08:00
|
|
|
dirs := []storedefs.Dir{
|
2021-02-13 09:25:05 +08:00
|
|
|
{Path: filepath.Join(home, "go"), Score: 200},
|
|
|
|
{Path: home, Score: 100},
|
|
|
|
{Path: fixPath("/tmp/foo/bar/lorem/ipsum"), Score: 50},
|
|
|
|
}
|
|
|
|
startLocation(f.App, LocationSpec{Store: locationStore{
|
|
|
|
storedDirs: dirs,
|
|
|
|
chdir: func(dir string) error { chdirCh <- dir; return errChdir },
|
|
|
|
}})
|
2019-08-31 21:21:06 +08:00
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
// Test UI.
|
|
|
|
wantBuf := locationBuf(
|
|
|
|
"",
|
|
|
|
"200 "+filepath.Join("~", "go"),
|
|
|
|
"100 ~",
|
|
|
|
" 50 "+fixPath("/tmp/foo/bar/lorem/ipsum"))
|
|
|
|
f.TTY.TestBuffer(t, wantBuf)
|
|
|
|
|
|
|
|
// Test filtering.
|
2021-03-27 09:34:31 +08:00
|
|
|
f.TTY.Inject(term.K('f'), term.K('o'))
|
2021-02-13 09:25:05 +08:00
|
|
|
|
|
|
|
wantBuf = locationBuf(
|
2021-03-27 09:34:31 +08:00
|
|
|
"fo",
|
2021-02-13 09:25:05 +08:00
|
|
|
" 50 "+fixPath("/tmp/foo/bar/lorem/ipsum"))
|
|
|
|
f.TTY.TestBuffer(t, wantBuf)
|
|
|
|
|
|
|
|
// Test accepting.
|
|
|
|
f.TTY.Inject(term.K(ui.Enter))
|
|
|
|
// There should be no change to codearea after accepting.
|
|
|
|
f.TestTTY(t /* nothing */)
|
|
|
|
// Error from Chdir should be sent to notes.
|
2021-12-08 08:26:17 +08:00
|
|
|
f.TestTTYNotes(t,
|
|
|
|
"error: mock chdir error", Styles,
|
|
|
|
"!!!!!!")
|
2021-02-13 09:25:05 +08:00
|
|
|
// Chdir should be called.
|
|
|
|
wantChdir := fixPath("/tmp/foo/bar/lorem/ipsum")
|
2021-03-27 09:34:31 +08:00
|
|
|
select {
|
|
|
|
case got := <-chdirCh:
|
|
|
|
if got != wantChdir {
|
|
|
|
t.Errorf("Chdir called with %s, want %s", got, wantChdir)
|
|
|
|
}
|
2021-10-05 08:02:54 +08:00
|
|
|
case <-time.After(testutil.Scaled(time.Second)):
|
2021-03-27 09:34:31 +08:00
|
|
|
t.Errorf("Chdir not called")
|
2021-02-13 09:25:05 +08:00
|
|
|
}
|
2019-08-31 21:21:06 +08:00
|
|
|
}
|
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
func TestLocation_Hidden(t *testing.T) {
|
2019-12-06 18:12:33 +08:00
|
|
|
f := Setup()
|
|
|
|
defer f.Stop()
|
2019-11-15 05:19:38 +08:00
|
|
|
|
2021-06-19 08:52:51 +08:00
|
|
|
dirs := []storedefs.Dir{
|
2021-02-13 09:25:05 +08:00
|
|
|
{Path: fixPath("/usr/bin"), Score: 200},
|
|
|
|
{Path: fixPath("/usr"), Score: 100},
|
|
|
|
{Path: fixPath("/tmp"), Score: 50},
|
2019-11-15 05:19:38 +08:00
|
|
|
}
|
2021-02-13 09:25:05 +08:00
|
|
|
startLocation(f.App, LocationSpec{
|
|
|
|
Store: locationStore{storedDirs: dirs},
|
|
|
|
IterateHidden: func(f func(string)) { f(fixPath("/usr")) },
|
2019-11-15 05:19:38 +08:00
|
|
|
})
|
|
|
|
// Test UI.
|
2021-02-13 09:25:05 +08:00
|
|
|
wantBuf := locationBuf(
|
2019-11-16 18:38:35 +08:00
|
|
|
"",
|
2021-02-13 09:25:05 +08:00
|
|
|
"200 "+fixPath("/usr/bin"),
|
|
|
|
" 50 "+fixPath("/tmp"))
|
2019-12-06 18:12:33 +08:00
|
|
|
f.TTY.TestBuffer(t, wantBuf)
|
2019-11-15 05:19:38 +08:00
|
|
|
}
|
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
func TestLocation_Pinned(t *testing.T) {
|
2019-12-06 18:12:33 +08:00
|
|
|
f := Setup()
|
|
|
|
defer f.Stop()
|
2019-11-15 05:19:38 +08:00
|
|
|
|
2021-06-19 08:52:51 +08:00
|
|
|
dirs := []storedefs.Dir{
|
2021-02-13 09:25:05 +08:00
|
|
|
{Path: fixPath("/usr/bin"), Score: 200},
|
|
|
|
{Path: fixPath("/usr"), Score: 100},
|
|
|
|
{Path: fixPath("/tmp"), Score: 50},
|
2019-11-15 05:19:38 +08:00
|
|
|
}
|
2021-02-13 09:25:05 +08:00
|
|
|
startLocation(f.App, LocationSpec{
|
|
|
|
Store: locationStore{storedDirs: dirs},
|
|
|
|
IteratePinned: func(f func(string)) { f(fixPath("/home")); f(fixPath("/usr")) },
|
2019-11-15 05:19:38 +08:00
|
|
|
})
|
|
|
|
// Test UI.
|
2021-02-13 09:25:05 +08:00
|
|
|
wantBuf := locationBuf(
|
2019-11-16 18:38:35 +08:00
|
|
|
"",
|
2021-02-13 09:25:05 +08:00
|
|
|
" * "+fixPath("/home"),
|
|
|
|
" * "+fixPath("/usr"),
|
|
|
|
"200 "+fixPath("/usr/bin"),
|
|
|
|
" 50 "+fixPath("/tmp"))
|
2019-12-06 18:12:33 +08:00
|
|
|
f.TTY.TestBuffer(t, wantBuf)
|
2019-11-15 05:19:38 +08:00
|
|
|
}
|
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
func TestLocation_HideWd(t *testing.T) {
|
2019-12-06 18:12:33 +08:00
|
|
|
f := Setup()
|
|
|
|
defer f.Stop()
|
2019-11-16 18:42:54 +08:00
|
|
|
|
2021-06-19 08:52:51 +08:00
|
|
|
dirs := []storedefs.Dir{
|
2021-02-13 09:25:05 +08:00
|
|
|
{Path: fixPath("/home"), Score: 200},
|
|
|
|
{Path: fixPath("/tmp"), Score: 50},
|
2019-11-16 18:42:54 +08:00
|
|
|
}
|
2021-02-13 09:25:05 +08:00
|
|
|
startLocation(f.App, LocationSpec{Store: locationStore{storedDirs: dirs, wd: fixPath("/home")}})
|
2019-11-16 18:42:54 +08:00
|
|
|
// Test UI.
|
2021-02-13 09:25:05 +08:00
|
|
|
wantBuf := locationBuf(
|
2019-11-16 18:42:54 +08:00
|
|
|
"",
|
2021-02-13 09:25:05 +08:00
|
|
|
" 50 "+fixPath("/tmp"))
|
2019-12-06 18:12:33 +08:00
|
|
|
f.TTY.TestBuffer(t, wantBuf)
|
2019-11-16 18:42:54 +08:00
|
|
|
}
|
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
func TestLocation_Workspace(t *testing.T) {
|
2019-12-06 18:12:33 +08:00
|
|
|
f := Setup()
|
|
|
|
defer f.Stop()
|
2019-11-17 03:49:05 +08:00
|
|
|
|
|
|
|
chdir := ""
|
2021-06-19 08:52:51 +08:00
|
|
|
dirs := []storedefs.Dir{
|
2021-02-13 09:25:05 +08:00
|
|
|
{Path: fixPath("home/src"), Score: 200},
|
|
|
|
{Path: fixPath("ws1/src"), Score: 150},
|
|
|
|
{Path: fixPath("ws2/bin"), Score: 100},
|
|
|
|
{Path: fixPath("/tmp"), Score: 50},
|
2019-11-17 03:49:05 +08:00
|
|
|
}
|
2021-02-13 09:25:05 +08:00
|
|
|
startLocation(f.App, LocationSpec{
|
|
|
|
Store: locationStore{
|
2019-11-17 03:49:05 +08:00
|
|
|
storedDirs: dirs,
|
2021-02-13 09:25:05 +08:00
|
|
|
wd: fixPath("/home/elf/bin"),
|
2019-11-17 03:49:05 +08:00
|
|
|
chdir: func(dir string) error {
|
|
|
|
chdir = dir
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
IterateWorkspaces: func(f func(kind, pattern string) bool) {
|
2019-11-17 05:17:11 +08:00
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
// Invalid patterns are ignored.
|
|
|
|
f("ws1", `C:\\usr\\[^\\+`)
|
|
|
|
f("home", `C:\\home\\[^\\]+`)
|
|
|
|
f("ws2", `C:\\tmp\[^\]+`)
|
|
|
|
} else {
|
|
|
|
// Invalid patterns are ignored.
|
|
|
|
f("ws1", "/usr/[^/+")
|
|
|
|
f("home", "/home/[^/]+")
|
|
|
|
f("ws2", "/tmp/[^/]+")
|
|
|
|
}
|
2019-11-17 03:49:05 +08:00
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
wantBuf := locationBuf(
|
2019-11-17 03:49:05 +08:00
|
|
|
"",
|
2021-02-13 09:25:05 +08:00
|
|
|
"200 "+fixPath("home/src"),
|
|
|
|
" 50 "+fixPath("/tmp"))
|
2019-12-06 18:12:33 +08:00
|
|
|
f.TTY.TestBuffer(t, wantBuf)
|
2019-11-17 03:49:05 +08:00
|
|
|
|
2019-12-06 18:12:33 +08:00
|
|
|
f.TTY.Inject(term.K(ui.Enter))
|
|
|
|
f.TestTTY(t /* nothing */)
|
2021-02-13 09:25:05 +08:00
|
|
|
wantChdir := fixPath("/home/elf/src")
|
2019-11-17 03:49:05 +08:00
|
|
|
if chdir != wantChdir {
|
|
|
|
t.Errorf("got chdir %q, want %q", chdir, wantChdir)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
func locationBuf(filter string, lines ...string) *term.Buffer {
|
2021-02-12 02:49:00 +08:00
|
|
|
b := term.NewBufferBuilder(50).
|
|
|
|
Newline(). // empty code area
|
2021-03-15 11:42:43 +08:00
|
|
|
WriteStyled(modeLine(" LOCATION ", true)).
|
2021-02-12 02:49:00 +08:00
|
|
|
Write(filter).SetDotHere()
|
|
|
|
for i, line := range lines {
|
|
|
|
b.Newline()
|
|
|
|
if i == 0 {
|
|
|
|
b.WriteStyled(ui.T(fmt.Sprintf("%-50s", line), ui.Inverse))
|
|
|
|
} else {
|
|
|
|
b.Write(line)
|
|
|
|
}
|
|
|
|
}
|
2019-11-16 18:38:35 +08:00
|
|
|
return b.Buffer()
|
|
|
|
}
|
2019-11-17 05:17:11 +08:00
|
|
|
|
2021-02-13 09:25:05 +08:00
|
|
|
func fixPath(path string) string {
|
2019-11-17 05:17:11 +08:00
|
|
|
if runtime.GOOS != "windows" {
|
|
|
|
return path
|
|
|
|
}
|
|
|
|
if path[0] == '/' {
|
|
|
|
path = "C:" + path
|
|
|
|
}
|
|
|
|
return strings.ReplaceAll(path, "/", "\\")
|
|
|
|
}
|
2021-02-13 09:25:05 +08:00
|
|
|
|
|
|
|
func startLocation(app cli.App, spec LocationSpec) {
|
|
|
|
w, err := NewLocation(app, spec)
|
2021-02-14 05:34:32 +08:00
|
|
|
startMode(app, w, err)
|
2021-02-13 09:25:05 +08:00
|
|
|
}
|