Support pinning directories using $le:loc-pinned.

This fixes #342.
This commit is contained in:
Qi Xiao 2017-05-18 01:56:07 +01:00
parent d2c58c34cf
commit 1097ad5891
4 changed files with 71 additions and 23 deletions

View File

@ -35,14 +35,17 @@ type Editor struct {
evaler *eval.Evaler evaler *eval.Evaler
cmdSeq int cmdSeq int
prompt eval.Variable prompt eval.Variable
rprompt eval.Variable rprompt eval.Variable
rpromptPersistent eval.Variable
abbreviations map[string]string abbreviations map[string]string
locationHidden eval.Variable locationHidden eval.Variable
rpromptPersistent eval.Variable locationPinned eval.Variable
beforeReadLine eval.Variable
afterReadLine eval.Variable beforeReadLine eval.Variable
afterReadLine eval.Variable
historyMutex sync.RWMutex historyMutex sync.RWMutex
@ -104,20 +107,22 @@ func NewEditor(file *os.File, sigs chan os.Signal, ev *eval.Evaler, st *store.St
prompt, rprompt := defaultPrompts() prompt, rprompt := defaultPrompts()
ed := &Editor{ ed := &Editor{
file: file, file: file,
writer: newWriter(file), writer: newWriter(file),
reader: tty.NewReader(file), reader: tty.NewReader(file),
sigs: sigs, sigs: sigs,
store: st, store: st,
evaler: ev, evaler: ev,
cmdSeq: seq, cmdSeq: seq,
prompt: eval.NewPtrVariableWithValidator(prompt, eval.ShouldBeFn),
rprompt: eval.NewPtrVariableWithValidator(rprompt, eval.ShouldBeFn), prompt: eval.NewPtrVariableWithValidator(prompt, eval.ShouldBeFn),
rprompt: eval.NewPtrVariableWithValidator(rprompt, eval.ShouldBeFn),
rpromptPersistent: eval.NewPtrVariableWithValidator(eval.Bool(false), eval.ShouldBeBool),
abbreviations: make(map[string]string), abbreviations: make(map[string]string),
locationHidden: eval.NewPtrVariableWithValidator(eval.NewList(), eval.ShouldBeList), locationHidden: eval.NewPtrVariableWithValidator(eval.NewList(), eval.ShouldBeList),
rpromptPersistent: eval.NewPtrVariableWithValidator(eval.Bool(false), eval.ShouldBeBool), locationPinned: eval.NewPtrVariableWithValidator(eval.NewList(), eval.ShouldBeList),
beforeReadLine: eval.NewPtrVariableWithValidator(eval.NewList(), eval.ShouldBeList), beforeReadLine: eval.NewPtrVariableWithValidator(eval.NewList(), eval.ShouldBeList),
afterReadLine: eval.NewPtrVariableWithValidator(eval.NewList(), eval.ShouldBeList), afterReadLine: eval.NewPtrVariableWithValidator(eval.NewList(), eval.ShouldBeList),

View File

@ -3,6 +3,7 @@ package edit
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"math"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
@ -16,6 +17,10 @@ import (
// Location mode. // Location mode.
// PinnedScore is a special value of Score in store.Dir to represent that the
// directory is pinned.
var PinnedScore = math.Inf(1)
type location struct { type location struct {
listing listing
home string // The home directory; leave empty if unknown. home string // The home directory; leave empty if unknown.
@ -42,7 +47,13 @@ func (loc *location) Len() int {
} }
func (loc *location) Show(i int) (string, styled) { func (loc *location) Show(i int) (string, styled) {
header := fmt.Sprintf("%.0f", loc.filtered[i].Score) var header string
score := loc.filtered[i].Score
if score == PinnedScore {
header = "*"
} else {
header = fmt.Sprintf("%.0f", score)
}
return header, unstyled(showPath(loc.filtered[i].Path, loc.home)) return header, unstyled(showPath(loc.filtered[i].Path, loc.home))
} }
@ -126,13 +137,28 @@ func startLocation(ed *Editor) {
ed.Notify("%v", ErrStoreOffline) ed.Notify("%v", ErrStoreOffline)
return return
} }
black := convertBlacklist(ed.locationHidden.Get().(eval.List)) black := convertListToSet(ed.locationHidden.Get().(eval.List))
dirs, err := ed.store.GetDirs(black) dirs, err := ed.store.GetDirs(black)
if err != nil { if err != nil {
ed.Notify("store error: %v", err) ed.Notify("store error: %v", err)
return return
} }
pinnedValue := ed.locationPinned.Get().(eval.List)
pinned := convertListToDirs(pinnedValue)
pinnedSet := convertListToSet(pinnedValue)
// TODO(xiaq): Optimize this by changing GetDirs to a callback API, and
// build dirs by first putting pinned directories and then appending those
// from store.
for _, d := range dirs {
_, inPinned := pinnedSet[d.Path]
if !inPinned {
pinned = append(pinned, d)
}
}
dirs = pinned
// Drop the error. When there is an error, home is "", which is used to // Drop the error. When there is an error, home is "", which is used to
// signify "no home known" in location. // signify "no home known" in location.
home, _ := util.GetHome("") home, _ := util.GetHome("")
@ -140,14 +166,28 @@ func startLocation(ed *Editor) {
ed.mode = ed.location ed.mode = ed.location
} }
func convertBlacklist(li eval.List) map[string]struct{} { // convertListToDirs converts a list of strings to []store.Dir. It uses the
black := make(map[string]struct{}) // special score of PinnedScore to signify that the directory is pinned.
func convertListToDirs(li eval.List) []store.Dir {
pinned := make([]store.Dir, 0, li.Len())
// XXX(xiaq): silently drops non-string items. // XXX(xiaq): silently drops non-string items.
li.Iterate(func(v eval.Value) bool { li.Iterate(func(v eval.Value) bool {
if s, ok := v.(eval.String); ok { if s, ok := v.(eval.String); ok {
black[string(s)] = struct{}{} pinned = append(pinned, store.Dir{string(s), PinnedScore})
} }
return true return true
}) })
return black return pinned
}
func convertListToSet(li eval.List) map[string]struct{} {
set := make(map[string]struct{})
// XXX(xiaq): silently drops non-string items.
li.Iterate(func(v eval.Value) bool {
if s, ok := v.(eval.String); ok {
set[string(s)] = struct{}{}
}
return true
})
return set
} }

View File

@ -8,6 +8,7 @@ import (
var ( var (
theLocation = newLocation([]store.Dir{ theLocation = newLocation([]store.Dir{
{"/pinned", PinnedScore},
{"/src/github.com/elves/elvish", 300}, {"/src/github.com/elves/elvish", 300},
{"/src/home/xyz", 233}, {"/src/home/xyz", 233},
{"/home/dir", 100}, {"/home/dir", 100},
@ -17,6 +18,7 @@ var (
locationFilterTests = []listingFilterTestCases{ locationFilterTests = []listingFilterTestCases{
{"", []shown{ {"", []shown{
{"*", unstyled("/pinned")},
{"300", unstyled("/src/github.com/elves/elvish")}, {"300", unstyled("/src/github.com/elves/elvish")},
{"233", unstyled("/src/home/xyz")}, {"233", unstyled("/src/home/xyz")},
{"100", unstyled("~/dir")}, // home is abbreviated {"100", unstyled("~/dir")}, // home is abbreviated

View File

@ -78,6 +78,7 @@ func makeModule(ed *Editor) eval.Namespace {
ns["abbr"] = eval.NewRoVariable(eval.MapStringString(ed.abbreviations)) ns["abbr"] = eval.NewRoVariable(eval.MapStringString(ed.abbreviations))
ns["loc-pinned"] = ed.locationPinned
ns["loc-hidden"] = ed.locationHidden ns["loc-hidden"] = ed.locationHidden
ns["before-readline"] = ed.beforeReadLine ns["before-readline"] = ed.beforeReadLine