mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-13 09:57:51 +08:00
newedit: Implement a basic location mode.
This commit is contained in:
parent
5e41a4cd40
commit
5eaec24f11
65
newedit/location/location.go
Normal file
65
newedit/location/location.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package location
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/elves/elvish/edit/ui"
|
||||
"github.com/elves/elvish/newedit/listing"
|
||||
"github.com/elves/elvish/newedit/types"
|
||||
"github.com/elves/elvish/store/storedefs"
|
||||
"github.com/elves/elvish/styled"
|
||||
)
|
||||
|
||||
// Mode represents the location mode. It implements the types.Mode interface by
|
||||
// embedding a *listing.Mode.
|
||||
type Mode struct {
|
||||
*listing.Mode
|
||||
KeyHandler func(ui.Key) types.HandlerAction
|
||||
Cd func(string) error
|
||||
}
|
||||
|
||||
// Start starts the location mode.
|
||||
func (m *Mode) Start(dirs []storedefs.Dir) {
|
||||
m.Mode.Start(listing.StartConfig{
|
||||
Name: "LOCATION",
|
||||
KeyHandler: m.KeyHandler,
|
||||
ItemsGetter: func(p string) listing.Items {
|
||||
return getItems(dirs, p, m.Cd)
|
||||
},
|
||||
StartFilter: true,
|
||||
})
|
||||
}
|
||||
|
||||
func getItems(dirs []storedefs.Dir, p string, cd func(string) error) items {
|
||||
var entries []storedefs.Dir
|
||||
for _, dir := range dirs {
|
||||
if strings.Contains(dir.Path, p) {
|
||||
entries = append(entries, dir)
|
||||
}
|
||||
}
|
||||
return items{entries, cd}
|
||||
}
|
||||
|
||||
// A slice of entries plus a cd callback, implementing the listing.Items
|
||||
// interface.
|
||||
type items struct {
|
||||
entries []storedefs.Dir
|
||||
cd func(string) error
|
||||
}
|
||||
|
||||
func (it items) Len() int {
|
||||
return len(it.entries)
|
||||
}
|
||||
|
||||
func (it items) Show(i int) styled.Text {
|
||||
return styled.Unstyled(
|
||||
fmt.Sprintf("%3.0f %s", it.entries[i].Score, it.entries[i].Path))
|
||||
}
|
||||
|
||||
func (it items) Accept(i int, st *types.State) {
|
||||
err := it.cd(it.entries[i].Path)
|
||||
if err != nil {
|
||||
st.AddNote(err.Error())
|
||||
}
|
||||
}
|
63
newedit/location/location_test.go
Normal file
63
newedit/location/location_test.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
package location
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/elves/elvish/newedit/listing"
|
||||
"github.com/elves/elvish/newedit/types"
|
||||
"github.com/elves/elvish/store/storedefs"
|
||||
"github.com/elves/elvish/styled"
|
||||
"github.com/elves/elvish/tt"
|
||||
)
|
||||
|
||||
var Args = tt.Args
|
||||
|
||||
func TestGetEntries(t *testing.T) {
|
||||
dirs := []storedefs.Dir{
|
||||
{Path: "/home/elf", Score: 20},
|
||||
{Path: "/usr/bin", Score: 10},
|
||||
}
|
||||
dummyCd := func(string) error { return nil }
|
||||
tt.Test(t, tt.Fn("getItems", getItems), tt.Table{
|
||||
Args(dirs, "", dummyCd).Rets(listing.MatchItems(
|
||||
styled.Unstyled(" 20 /home/elf"),
|
||||
styled.Unstyled(" 10 /usr/bin"),
|
||||
)),
|
||||
Args(dirs, "/usr", dummyCd).Rets(listing.MatchItems(
|
||||
styled.Unstyled(" 10 /usr/bin"),
|
||||
))})
|
||||
}
|
||||
|
||||
func TestAccept_OK(t *testing.T) {
|
||||
dirs := []storedefs.Dir{
|
||||
{Path: "/home/elf", Score: 20},
|
||||
{Path: "/usr/bin", Score: 10},
|
||||
}
|
||||
calledDir := ""
|
||||
cd := func(dir string) error {
|
||||
calledDir = dir
|
||||
return nil
|
||||
}
|
||||
getItems(dirs, "", cd).Accept(0, &types.State{})
|
||||
if calledDir != "/home/elf" {
|
||||
t.Errorf("Accept did not call cd")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccept_Error(t *testing.T) {
|
||||
dirs := []storedefs.Dir{
|
||||
{Path: "/home/elf", Score: 20},
|
||||
{Path: "/usr/bin", Score: 10},
|
||||
}
|
||||
cd := func(string) error { return errors.New("cannot cd") }
|
||||
state := types.State{}
|
||||
|
||||
getItems(dirs, "", cd).Accept(0, &state)
|
||||
|
||||
wantNotes := []string{"cannot cd"}
|
||||
if !reflect.DeepEqual(state.Raw.Notes, wantNotes) {
|
||||
t.Errorf("cd errors not added to notes")
|
||||
}
|
||||
}
|
31
newedit/location_api.go
Normal file
31
newedit/location_api.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package newedit
|
||||
|
||||
import (
|
||||
"github.com/elves/elvish/eval"
|
||||
"github.com/elves/elvish/newedit/listing"
|
||||
"github.com/elves/elvish/newedit/location"
|
||||
"github.com/elves/elvish/store/storedefs"
|
||||
)
|
||||
|
||||
func initLocation(ed editor, ev *eval.Evaler, getDirs func() ([]storedefs.Dir, error), cd func(string) error, lsMode *listing.Mode, lsBinding *bindingMap) eval.Ns {
|
||||
binding := emptyBindingMap
|
||||
mode := location.Mode{
|
||||
Mode: lsMode,
|
||||
KeyHandler: keyHandlerFromBindings(ed, ev, &binding, lsBinding),
|
||||
Cd: cd,
|
||||
}
|
||||
ns := eval.Ns{}.
|
||||
AddGoFn("<edit:location>", "start", func() {
|
||||
startLocation(ed, getDirs, &mode)
|
||||
})
|
||||
return ns
|
||||
}
|
||||
|
||||
func startLocation(ed editor, getDirs func() ([]storedefs.Dir, error), mode *location.Mode) {
|
||||
dirs, err := getDirs()
|
||||
if err != nil {
|
||||
ed.Notify("db error: " + err.Error())
|
||||
}
|
||||
mode.Start(dirs)
|
||||
ed.State().SetMode(mode)
|
||||
}
|
47
newedit/location_api_test.go
Normal file
47
newedit/location_api_test.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package newedit
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/elves/elvish/edit/ui"
|
||||
|
||||
"github.com/elves/elvish/eval"
|
||||
"github.com/elves/elvish/newedit/listing"
|
||||
"github.com/elves/elvish/newedit/types"
|
||||
"github.com/elves/elvish/store/storedefs"
|
||||
)
|
||||
|
||||
func TestLocation_Start(t *testing.T) {
|
||||
ed := &fakeEditor{}
|
||||
ev := eval.NewEvaler()
|
||||
lsMode := listing.Mode{}
|
||||
lsBinding := emptyBindingMap
|
||||
getDirs := func() ([]storedefs.Dir, error) {
|
||||
return []storedefs.Dir{
|
||||
{Path: "/usr/bin", Score: 20},
|
||||
{Path: "/home/elf", Score: 10},
|
||||
}, nil
|
||||
}
|
||||
cd := func(string) error { return nil }
|
||||
|
||||
ns := initLocation(ed, ev, getDirs, cd, &lsMode, &lsBinding)
|
||||
|
||||
// Call <edit:location>:start.
|
||||
fm := eval.NewTopFrame(ev, eval.NewInternalSource("[test]"), nil)
|
||||
fm.Call(getFn(ns, "start"), eval.NoArgs, eval.NoOpts)
|
||||
|
||||
// Verify that the current mode supports listing.
|
||||
lister, ok := ed.state.Mode().(types.Lister)
|
||||
if !ok {
|
||||
t.Errorf("Mode is not Lister after <edit:location>:start")
|
||||
}
|
||||
// Verify the actual listing.
|
||||
buf := ui.Render(lister.List(10), 30)
|
||||
wantBuf := ui.NewBufferBuilder(30).
|
||||
WriteString(" 20 /usr/bin", "7").Newline().
|
||||
WriteString(" 10 /home/elf", "").Buffer()
|
||||
if !reflect.DeepEqual(buf, wantBuf) {
|
||||
t.Errorf("Rendered listing is %v, want %v", buf, wantBuf)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user