summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSimon Fraser <srfraser@users.noreply.github.com>2019-10-27 14:50:12 +0000
committerJunegunn Choi <junegunn.c@gmail.com>2019-10-27 23:50:12 +0900
commit391669a451c4eedd74dd6e1794601d2ca793340c (patch)
treef028d0e3dc22d1ea036807e330d60e5bf87389c2 /src
parent0c6c76e08115d0ba160e2c95c8692e4174aa5368 (diff)
downloadfzf-391669a451c4eedd74dd6e1794601d2ca793340c.tar.gz
Add 'f' flag for placeholder expression (#1733)
If present the contents of the selection will be placed in a temporary file, and the filename will be placed into the string instead.
Diffstat (limited to 'src')
-rw-r--r--src/options.go4
-rw-r--r--src/terminal.go52
-rw-r--r--src/terminal_test.go48
3 files changed, 75 insertions, 29 deletions
diff --git a/src/options.go b/src/options.go
index 3449f3dc..bd2a038e 100644
--- a/src/options.go
+++ b/src/options.go
@@ -189,6 +189,7 @@ type Options struct {
PrintQuery bool
ReadZero bool
Printer func(string)
+ PrintSep string
Sync bool
History *History
Header []string
@@ -240,6 +241,7 @@ func defaultOptions() *Options {
PrintQuery: false,
ReadZero: false,
Printer: func(str string) { fmt.Println(str) },
+ PrintSep: "\n",
Sync: false,
History: nil,
Header: make([]string, 0),
@@ -1106,8 +1108,10 @@ func parseOptions(opts *Options, allArgs []string) {
opts.ReadZero = false
case "--print0":
opts.Printer = func(str string) { fmt.Print(str, "\x00") }
+ opts.PrintSep = "\x00"
case "--no-print0":
opts.Printer = func(str string) { fmt.Println(str) }
+ opts.PrintSep = "\n"
case "--print-query":
opts.PrintQuery = true
case "--no-print-query":
diff --git a/src/terminal.go b/src/terminal.go
index 1b44e141..bb29d1d5 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -5,6 +5,7 @@ import (
"bytes"
"fmt"
"io"
+ "io/ioutil"
"os"
"os/signal"
"regexp"
@@ -22,9 +23,11 @@ import (
// import "github.com/pkg/profile"
var placeholder *regexp.Regexp
+var activeTempFiles []string
func init() {
- placeholder = regexp.MustCompile("\\\\?(?:{[+s]*[0-9,-.]*}|{q}|{\\+?n})")
+ placeholder = regexp.MustCompile("\\\\?(?:{[+sf]*[0-9,-.]*}|{q}|{\\+?f?nf?})")
+ activeTempFiles = []string{}
}
type jumpMode int
@@ -103,6 +106,7 @@ type Terminal struct {
jumping jumpMode
jumpLabels string
printer func(string)
+ printsep string
merger *Merger
selected map[int32]selectedItem
version int64
@@ -231,6 +235,7 @@ type placeholderFlags struct {
preserveSpace bool
number bool
query bool
+ file bool
}
func toActions(types ...actionType) []action {
@@ -407,6 +412,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
jumping: jumpDisabled,
jumpLabels: opts.JumpLabels,
printer: opts.Printer,
+ printsep: opts.PrintSep,
merger: EmptyMerger,
selected: make(map[int32]selectedItem),
reqBox: util.NewEventBox(),
@@ -1207,6 +1213,9 @@ func parsePlaceholder(match string) (bool, string, placeholderFlags) {
case 'n':
flags.number = true
skipChars++
+ case 'f':
+ flags.file = true
+ skipChars++
case 'q':
flags.query = true
default:
@@ -1232,7 +1241,27 @@ func hasPreviewFlags(template string) (plus bool, query bool) {
return
}
-func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, forcePlus bool, query string, allItems []*Item) string {
+func writeTemporaryFile(data []string, printSep string) string {
+ f, err := ioutil.TempFile("", "fzf-preview-*")
+ if err != nil {
+ errorExit("Unable to create temporary file")
+ }
+ defer f.Close()
+
+ f.WriteString(strings.Join(data, printSep))
+ f.WriteString(printSep)
+ activeTempFiles = append(activeTempFiles, f.Name())
+ return f.Name()
+}
+
+func cleanTemporaryFiles() {
+ for _, filename := range activeTempFiles {
+ os.Remove(filename)
+ }
+ activeTempFiles = []string{}
+}
+
+func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, printsep string, forcePlus bool, query string, allItems []*Item) string {
current := allItems[:1]
selected := allItems[1:]
if current[0] == nil {
@@ -1269,10 +1298,15 @@ func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, fo
} else {
replacements[idx] = strconv.Itoa(n)
}
+ } else if flags.file {
+ replacements[idx] = item.AsString(stripAnsi)
} else {
replacements[idx] = quoteEntry(item.AsString(stripAnsi))
}
}
+ if flags.file {
+ return writeTemporaryFile(replacements, printsep)
+ }
return strings.Join(replacements, " ")
}
@@ -1302,7 +1336,13 @@ func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, fo
if !flags.preserveSpace {
str = strings.TrimSpace(str)
}
- replacements[idx] = quoteEntry(str)
+ if !flags.file {
+ str = quoteEntry(str)
+ }
+ replacements[idx] = str
+ }
+ if flags.file {
+ return writeTemporaryFile(replacements, printsep)
}
return strings.Join(replacements, " ")
})
@@ -1319,7 +1359,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
if !valid {
return
}
- command := replacePlaceholder(template, t.ansi, t.delimiter, forcePlus, string(t.input), list)
+ command := replacePlaceholder(template, t.ansi, t.delimiter, t.printsep, forcePlus, string(t.input), list)
cmd := util.ExecCommand(command, false)
if !background {
cmd.Stdin = os.Stdin
@@ -1335,6 +1375,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
cmd.Run()
t.tui.Resume(false)
}
+ cleanTemporaryFiles()
}
func (t *Terminal) hasPreviewer() bool {
@@ -1492,7 +1533,7 @@ func (t *Terminal) Loop() {
// We don't display preview window if no match
if request[0] != nil {
command := replacePlaceholder(t.preview.command,
- t.ansi, t.delimiter, false, string(t.input), request)
+ t.ansi, t.delimiter, t.printsep, false, string(t.input), request)
cmd := util.ExecCommand(command, true)
if t.pwindow != nil {
env := os.Environ()
@@ -1534,6 +1575,7 @@ func (t *Terminal) Loop() {
if out.Len() > 0 || !<-updateChan {
t.reqBox.Set(reqPreviewDisplay, out.String())
}
+ cleanTemporaryFiles()
} else {
t.reqBox.Set(reqPreviewDisplay, "")
}
diff --git a/src/terminal_test.go b/src/terminal_test.go
index 62b20c45..8b828af4 100644
--- a/src/terminal_test.go
+++ b/src/terminal_test.go
@@ -30,92 +30,92 @@ func TestReplacePlaceholder(t *testing.T) {
t.Errorf("expected: %s, actual: %s", expected, result)
}
}
-
+ printsep := "\n"
// {}, preserve ansi
- result = replacePlaceholder("echo {}", false, Delimiter{}, false, "query", items1)
+ result = replacePlaceholder("echo {}", false, Delimiter{}, printsep, false, "query", items1)
check("echo ' foo'\\''bar \x1b[31mbaz\x1b[m'")
// {}, strip ansi
- result = replacePlaceholder("echo {}", true, Delimiter{}, false, "query", items1)
+ result = replacePlaceholder("echo {}", true, Delimiter{}, printsep, false, "query", items1)
check("echo ' foo'\\''bar baz'")
// {}, with multiple items
- result = replacePlaceholder("echo {}", true, Delimiter{}, false, "query", items2)
+ result = replacePlaceholder("echo {}", true, Delimiter{}, printsep, false, "query", items2)
check("echo 'foo'\\''bar baz'")
// {..}, strip leading whitespaces, preserve ansi
- result = replacePlaceholder("echo {..}", false, Delimiter{}, false, "query", items1)
+ result = replacePlaceholder("echo {..}", false, Delimiter{}, printsep, false, "query", items1)
check("echo 'foo'\\''bar \x1b[31mbaz\x1b[m'")
// {..}, strip leading whitespaces, strip ansi
- result = replacePlaceholder("echo {..}", true, Delimiter{}, false, "query", items1)
+ result = replacePlaceholder("echo {..}", true, Delimiter{}, printsep, false, "query", items1)
check("echo 'foo'\\''bar baz'")
// {q}
- result = replacePlaceholder("echo {} {q}", true, Delimiter{}, false, "query", items1)
+ result = replacePlaceholder("echo {} {q}", true, Delimiter{}, printsep, false, "query", items1)
check("echo ' foo'\\''bar baz' 'query'")
// {q}, multiple items
- result = replacePlaceholder("echo {+}{q}{+}", true, Delimiter{}, false, "query 'string'", items2)
+ result = replacePlaceholder("echo {+}{q}{+}", true, Delimiter{}, printsep, false, "query 'string'", items2)
check("echo 'foo'\\''bar baz' 'FOO'\\''BAR BAZ''query '\\''string'\\''''foo'\\''bar baz' 'FOO'\\''BAR BAZ'")
- result = replacePlaceholder("echo {}{q}{}", true, Delimiter{}, false, "query 'string'", items2)
+ result = replacePlaceholder("echo {}{q}{}", true, Delimiter{}, printsep, false, "query 'string'", items2)
check("echo 'foo'\\''bar baz''query '\\''string'\\''''foo'\\''bar baz'")
- result = replacePlaceholder("echo {1}/{2}/{2,1}/{-1}/{-2}/{}/{..}/{n.t}/\\{}/\\{1}/\\{q}/{3}", true, Delimiter{}, false, "query", items1)
+ result = replacePlaceholder("echo {1}/{2}/{2,1}/{-1}/{-2}/{}/{..}/{n.t}/\\{}/\\{1}/\\{q}/{3}", true, Delimiter{}, printsep, false, "query", items1)
check("echo 'foo'\\''bar'/'baz'/'bazfoo'\\''bar'/'baz'/'foo'\\''bar'/' foo'\\''bar baz'/'foo'\\''bar baz'/{n.t}/{}/{1}/{q}/''")
- result = replacePlaceholder("echo {1}/{2}/{-1}/{-2}/{..}/{n.t}/\\{}/\\{1}/\\{q}/{3}", true, Delimiter{}, false, "query", items2)
+ result = replacePlaceholder("echo {1}/{2}/{-1}/{-2}/{..}/{n.t}/\\{}/\\{1}/\\{q}/{3}", true, Delimiter{}, printsep, false, "query", items2)
check("echo 'foo'\\''bar'/'baz'/'baz'/'foo'\\''bar'/'foo'\\''bar baz'/{n.t}/{}/{1}/{q}/''")
- result = replacePlaceholder("echo {+1}/{+2}/{+-1}/{+-2}/{+..}/{n.t}/\\{}/\\{1}/\\{q}/{+3}", true, Delimiter{}, false, "query", items2)
+ result = replacePlaceholder("echo {+1}/{+2}/{+-1}/{+-2}/{+..}/{n.t}/\\{}/\\{1}/\\{q}/{+3}", true, Delimiter{}, printsep, false, "query", items2)
check("echo 'foo'\\''bar' 'FOO'\\''BAR'/'baz' 'BAZ'/'baz' 'BAZ'/'foo'\\''bar' 'FOO'\\''BAR'/'foo'\\''bar baz' 'FOO'\\''BAR BAZ'/{n.t}/{}/{1}/{q}/'' ''")
// forcePlus
- result = replacePlaceholder("echo {1}/{2}/{-1}/{-2}/{..}/{n.t}/\\{}/\\{1}/\\{q}/{3}", true, Delimiter{}, true, "query", items2)
+ result = replacePlaceholder("echo {1}/{2}/{-1}/{-2}/{..}/{n.t}/\\{}/\\{1}/\\{q}/{3}", true, Delimiter{}, printsep, true, "query", items2)
check("echo 'foo'\\''bar' 'FOO'\\''BAR'/'baz' 'BAZ'/'baz' 'BAZ'/'foo'\\''bar' 'FOO'\\''BAR'/'foo'\\''bar baz' 'FOO'\\''BAR BAZ'/{n.t}/{}/{1}/{q}/'' ''")
// Whitespace preserving flag with "'" delimiter
- result = replacePlaceholder("echo {s1}", true, Delimiter{str: &delim}, false, "query", items1)
+ result = replacePlaceholder("echo {s1}", true, Delimiter{str: &delim}, printsep, false, "query", items1)
check("echo ' foo'")
- result = replacePlaceholder("echo {s2}", true, Delimiter{str: &delim}, false, "query", items1)
+ result = replacePlaceholder("echo {s2}", true, Delimiter{str: &delim}, printsep, false, "query", items1)
check("echo 'bar baz'")
- result = replacePlaceholder("echo {s}", true, Delimiter{str: &delim}, false, "query", items1)
+ result = replacePlaceholder("echo {s}", true, Delimiter{str: &delim}, printsep, false, "query", items1)
check("echo ' foo'\\''bar baz'")
- result = replacePlaceholder("echo {s..}", true, Delimiter{str: &delim}, false, "query", items1)
+ result = replacePlaceholder("echo {s..}", true, Delimiter{str: &delim}, printsep, false, "query", items1)
check("echo ' foo'\\''bar baz'")
// Whitespace preserving flag with regex delimiter
regex = regexp.MustCompile("\\w+")
- result = replacePlaceholder("echo {s1}", true, Delimiter{regex: regex}, false, "query", items1)
+ result = replacePlaceholder("echo {s1}", true, Delimiter{regex: regex}, printsep, false, "query", items1)
check("echo ' '")
- result = replacePlaceholder("echo {s2}", true, Delimiter{regex: regex}, false, "query", items1)
+ result = replacePlaceholder("echo {s2}", true, Delimiter{regex: regex}, printsep, false, "query", items1)
check("echo ''\\'''")
- result = replacePlaceholder("echo {s3}", true, Delimiter{regex: regex}, false, "query", items1)
+ result = replacePlaceholder("echo {s3}", true, Delimiter{regex: regex}, printsep, false, "query", items1)
check("echo ' '")
// No match
- result = replacePlaceholder("echo {}/{+}", true, Delimiter{}, false, "query", []*Item{nil, nil})
+ result = replacePlaceholder("echo {}/{+}", true, Delimiter{}, printsep, false, "query", []*Item{nil, nil})
check("echo /")
// No match, but with selections
- result = replacePlaceholder("echo {}/{+}", true, Delimiter{}, false, "query", []*Item{nil, item1})
+ result = replacePlaceholder("echo {}/{+}", true, Delimiter{}, printsep, false, "query", []*Item{nil, item1})
check("echo /' foo'\\''bar baz'")
// String delimiter
- result = replacePlaceholder("echo {}/{1}/{2}", true, Delimiter{str: &delim}, false, "query", items1)
+ result = replacePlaceholder("echo {}/{1}/{2}", true, Delimiter{str: &delim}, printsep, false, "query", items1)
check("echo ' foo'\\''bar baz'/'foo'/'bar baz'")
// Regex delimiter
regex = regexp.MustCompile("[oa]+")
// foo'bar baz
- result = replacePlaceholder("echo {}/{1}/{3}/{2..3}", true, Delimiter{regex: regex}, false, "query", items1)
+ result = replacePlaceholder("echo {}/{1}/{3}/{2..3}", true, Delimiter{regex: regex}, printsep, false, "query", items1)
check("echo ' foo'\\''bar baz'/'f'/'r b'/''\\''bar b'")
}