Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

preselector option, issue #395 #2440

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const usage = `usage: fzf [options]
-1, --select-1 Automatically select the only match
-0, --exit-0 Exit immediately when there's no match
-f, --filter=STR Filter mode. Do not start interactive finder.
--preselector=FILE File including the pointed and selected items
--print-query Print query as the first line
--expect=KEYS Comma-separated list of keys to complete fzf
--read0 Read input delimited by ASCII NUL characters
Expand Down Expand Up @@ -207,6 +208,7 @@ type Options struct {
Pointer string
Marker string
Query string
Preselector *Preselector
Select1 bool
Exit0 bool
Filter *string
Expand Down Expand Up @@ -268,6 +270,7 @@ func defaultOptions() *Options {
Pointer: ">",
Marker: ">",
Query: "",
Preselector: nil,
Select1: false,
Exit0: false,
Filter: nil,
Expand Down Expand Up @@ -1220,6 +1223,13 @@ func parseOptions(opts *Options, allArgs []string) {
case "-f", "--filter":
filter := nextString(allArgs, &i, "query string required")
opts.Filter = &filter
case "--preselector":
path := nextString(allArgs, &i, "preselector file required")
p, e := NewPreselector(path)
if e != nil {
errorExit(e.Error())
}
opts.Preselector = p
case "--literal":
opts.Normalize = false
case "--no-literal":
Expand Down Expand Up @@ -1428,6 +1438,12 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Filter = &value
} else if match, value := optString(arg, "-d", "--delimiter="); match {
opts.Delimiter = delimiterRegexp(value)
} else if match, value := optString(arg, "--preselector="); match {
p, e := NewPreselector(value)
if e != nil {
errorExit(e.Error())
}
opts.Preselector = p
} else if match, value := optString(arg, "--border="); match {
opts.BorderShape = parseBorder(value, false)
} else if match, value := optString(arg, "--prompt="); match {
Expand Down
83 changes: 83 additions & 0 deletions src/preselector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package fzf

import (
"errors"
"io/ioutil"
"os"
"strings"
)

type Preselector struct {
path string
prepointed string
preselected []string
}

func fileFmtError(path string, e error) error {
if os.IsPermission(e) {
return errors.New("permission denied: " + path)
}
return errors.New("invalid preselector file: " + e.Error())
}

func NewPreselector(path string) (*Preselector, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
// If it doesn't exist, check if we can create a file with the name
if os.IsNotExist(err) {
data = []byte{}
if err := ioutil.WriteFile(path, data, 0600); err != nil {
return nil, fileFmtError(path, err)
}
} else {
return nil, fileFmtError(path, err)
}
}
// Split lines and limit the maximum number of lines
lines := strings.Split(strings.Trim(string(data), "\n"), "\n")
return &Preselector{
path: path,
prepointed: lines[0],
preselected: lines[1:]}, nil
}

func (p *Preselector) apply(t *Terminal) {
var itemRes Result
for i := 0; i < t.merger.Length(); i++ {
itemRes = t.merger.Get(i)
// find prepointed if any
if p.prepointed != "" &&
itemRes.item.AsString(true) == p.prepointed {
t.vset(int(itemRes.item.Index()))
p.prepointed = ""
}
// find preselected if any
if p.preselected != nil {
for j := 0; j < len(p.preselected); j++ {
if itemRes.item.AsString(true) == p.preselected[j] {
t.selectItem(itemRes.item)
p.preselected[j] = "" // TODO: remove deleted ones
}
}
}
}
}

func (p *Preselector) save(t *Terminal) error {
current := t.currentItem()
if current == nil {
return errors.New("current item not found")
}
res := make([]string, 0, len(t.selected)+1)
res = append(res, current.AsString(true))
for _, sel := range t.selected {
res = append(res, sel.item.AsString(true))
}

data := strings.Join(res, "\n")
if err := ioutil.WriteFile(p.path, []byte(data), 0600); err != nil {
return fileFmtError(p.path, err)
}

return nil
}
16 changes: 15 additions & 1 deletion src/terminal.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ type Terminal struct {
theme *tui.ColorTheme
tui tui.Renderer
executing *util.AtomicBool
preselector *Preselector
}

type selectedItem struct {
Expand Down Expand Up @@ -531,7 +532,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
killChan: make(chan int),
tui: renderer,
initFunc: func() { renderer.Init() },
executing: util.NewAtomicBool(false)}
executing: util.NewAtomicBool(false),
preselector: opts.Preselector}
t.prompt, t.promptLen = t.parsePrompt(opts.Prompt)
t.pointer, t.pointerLen = t.processTabs([]rune(opts.Pointer), 0)
t.marker, t.markerLen = t.processTabs([]rune(opts.Marker), 0)
Expand Down Expand Up @@ -636,6 +638,9 @@ func (t *Terminal) UpdateList(merger *Merger, reset bool) {
if reset {
t.selected = make(map[int32]selectedItem)
}
if t.preselector != nil {
t.preselector.apply(t)
}
t.mutex.Unlock()
t.reqBox.Set(reqInfo, nil)
t.reqBox.Set(reqList, nil)
Expand Down Expand Up @@ -2150,6 +2155,9 @@ func (t *Terminal) Loop() {
case reqRedraw:
t.redraw()
case reqClose:
if t.preselector != nil {
t.preselector.save(t)
}
exit(func() int {
if t.output() {
return exitOk
Expand Down Expand Up @@ -2177,12 +2185,18 @@ func (t *Terminal) Loop() {
t.previewer.version = value.(int64)
t.printPreviewDelayed()
case reqPrintQuery:
if t.preselector != nil {
t.preselector.save(t)
}
exit(func() int {
t.printer(string(t.input))
return exitOk
})
return
case reqQuit:
if t.preselector != nil {
t.preselector.save(t)
}
exit(func() int { return exitInterrupt })
return
}
Expand Down