summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2017-01-27 17:46:56 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2017-01-27 18:56:41 +0900
commit421b9b271ac263057c81eb58f98d5a96c04ce941 (patch)
treea79c584958acc6272699a48510c4a7ad3948d00e /src
parented57dcb924112192636260fb31d0db54352c517a (diff)
downloadfzf-421b9b271ac263057c81eb58f98d5a96c04ce941.tar.gz
Add execute-silent action
Close #823
Diffstat (limited to 'src')
-rw-r--r--src/options.go58
-rw-r--r--src/terminal.go33
-rw-r--r--src/terminal_test.go46
3 files changed, 85 insertions, 52 deletions
diff --git a/src/options.go b/src/options.go
index 9d8bb898..d29f8bfe 100644
--- a/src/options.go
+++ b/src/options.go
@@ -581,18 +581,25 @@ const (
escapedPlus = 2
)
+func init() {
+ // Backreferences are not supported.
+ // "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
+ executeRegexp = regexp.MustCompile(
+ "(?si):(execute(?:-multi|-silent)?):.+|:(execute(?:-multi|-silent)?)(\\([^)]*\\)|\\[[^\\]]*\\]|~[^~]*~|![^!]*!|@[^@]*@|\\#[^\\#]*\\#|\\$[^\\$]*\\$|%[^%]*%|\\^[^\\^]*\\^|&[^&]*&|\\*[^\\*]*\\*|;[^;]*;|/[^/]*/|\\|[^\\|]*\\|)")
+}
+
func parseKeymap(keymap map[int][]action, str string) {
- if executeRegexp == nil {
- // Backreferences are not supported.
- // "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
- executeRegexp = regexp.MustCompile(
- "(?si):execute(-multi)?:.+|:execute(-multi)?(\\([^)]*\\)|\\[[^\\]]*\\]|~[^~]*~|![^!]*!|@[^@]*@|\\#[^\\#]*\\#|\\$[^\\$]*\\$|%[^%]*%|\\^[^\\^]*\\^|&[^&]*&|\\*[^\\*]*\\*|;[^;]*;|/[^/]*/|\\|[^\\|]*\\|)")
- }
masked := executeRegexp.ReplaceAllStringFunc(str, func(src string) string {
- if src[len(":execute")] == '-' {
- return ":execute-multi(" + strings.Repeat(" ", len(src)-len(":execute-multi()")) + ")"
+ prefix := ":execute"
+ if src[len(prefix)] == '-' {
+ c := src[len(prefix)+1]
+ if c == 's' || c == 'S' {
+ prefix += "-silent"
+ } else {
+ prefix += "-multi"
+ }
}
- return ":execute(" + strings.Repeat(" ", len(src)-len(":execute()")) + ")"
+ return prefix + "(" + strings.Repeat(" ", len(src)-len(prefix)-2) + ")"
})
masked = strings.Replace(masked, "::", string([]rune{escapedColon, ':'}), -1)
masked = strings.Replace(masked, ",:", string([]rune{escapedComma, ':'}), -1)
@@ -728,9 +735,12 @@ func parseKeymap(keymap map[int][]action, str string) {
errorExit("unknown action: " + spec)
} else {
var offset int
- if t == actExecuteMulti {
+ switch t {
+ case actExecuteSilent:
+ offset = len("execute-silent")
+ case actExecuteMulti:
offset = len("execute-multi")
- } else {
+ default:
offset = len("execute")
}
if spec[offset] == ':' {
@@ -752,23 +762,21 @@ func parseKeymap(keymap map[int][]action, str string) {
}
func isExecuteAction(str string) actionType {
- t := actExecute
- if !strings.HasPrefix(str, "execute") || len(str) < len("execute(") {
+ matches := executeRegexp.FindAllStringSubmatch(":"+str, -1)
+ if matches == nil || len(matches) != 1 {
return actIgnore
}
-
- b := str[len("execute")]
- if strings.HasPrefix(str, "execute-multi") {
- if len(str) < len("execute-multi(") {
- return actIgnore
- }
- t = actExecuteMulti
- b = str[len("execute-multi")]
+ prefix := matches[0][1]
+ if len(prefix) == 0 {
+ prefix = matches[0][2]
}
- e := str[len(str)-1]
- if b == ':' || b == '(' && e == ')' || b == '[' && e == ']' ||
- b == e && strings.ContainsAny(string(b), "~!@#$%^&*;/|") {
- return t
+ switch prefix {
+ case "execute":
+ return actExecute
+ case "execute-silent":
+ return actExecuteSilent
+ case "execute-multi":
+ return actExecuteMulti
}
return actIgnore
}
diff --git a/src/terminal.go b/src/terminal.go
index 43d21d88..ee678f5c 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -204,7 +204,8 @@ const (
actPreviousHistory
actNextHistory
actExecute
- actExecuteMulti
+ actExecuteSilent
+ actExecuteMulti // Deprecated
)
func toActions(types ...actionType) []action {
@@ -1126,22 +1127,26 @@ func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, fo
})
}
-func (t *Terminal) executeCommand(template string, forcePlus bool) {
+func (t *Terminal) executeCommand(template string, forcePlus bool, background bool) {
valid, list := t.buildPlusList(template, forcePlus)
if !valid {
return
}
command := replacePlaceholder(template, t.ansi, t.delimiter, forcePlus, string(t.input), list)
cmd := util.ExecCommand(command)
- cmd.Stdin = os.Stdin
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- t.tui.Pause()
- cmd.Run()
- if t.tui.Resume() {
- t.printAll()
- }
- t.refresh()
+ if !background {
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ t.tui.Pause()
+ cmd.Run()
+ if t.tui.Resume() {
+ t.printAll()
+ }
+ t.refresh()
+ } else {
+ cmd.Run()
+ }
}
func (t *Terminal) hasPreviewer() bool {
@@ -1390,10 +1395,10 @@ func (t *Terminal) Loop() {
doAction = func(a action, mapkey int) bool {
switch a.t {
case actIgnore:
- case actExecute:
- t.executeCommand(a.a, false)
+ case actExecute, actExecuteSilent:
+ t.executeCommand(a.a, false, a.t == actExecuteSilent)
case actExecuteMulti:
- t.executeCommand(a.a, true)
+ t.executeCommand(a.a, true, false)
case actInvalid:
t.mutex.Unlock()
return false
diff --git a/src/terminal_test.go b/src/terminal_test.go
index 5afafaa2..41941eeb 100644
--- a/src/terminal_test.go
+++ b/src/terminal_test.go
@@ -14,9 +14,11 @@ func newItem(str string) *Item {
}
func TestReplacePlaceholder(t *testing.T) {
- items1 := []*Item{newItem(" foo'bar \x1b[31mbaz\x1b[m")}
+ item1 := newItem(" foo'bar \x1b[31mbaz\x1b[m")
+ items1 := []*Item{item1, item1}
items2 := []*Item{
newItem("foo'bar \x1b[31mbaz\x1b[m"),
+ newItem("foo'bar \x1b[31mbaz\x1b[m"),
newItem("FOO'BAR \x1b[31mBAZ\x1b[m")}
var result string
@@ -27,47 +29,65 @@ func TestReplacePlaceholder(t *testing.T) {
}
// {}, preserve ansi
- result = replacePlaceholder("echo {}", false, Delimiter{}, "query", items1)
+ result = replacePlaceholder("echo {}", false, Delimiter{}, false, "query", items1)
check("echo ' foo'\\''bar \x1b[31mbaz\x1b[m'")
// {}, strip ansi
- result = replacePlaceholder("echo {}", true, Delimiter{}, "query", items1)
+ result = replacePlaceholder("echo {}", true, Delimiter{}, false, "query", items1)
check("echo ' foo'\\''bar baz'")
// {}, with multiple items
- result = replacePlaceholder("echo {}", true, Delimiter{}, "query", items2)
- check("echo 'foo'\\''bar baz' 'FOO'\\''BAR BAZ'")
+ result = replacePlaceholder("echo {}", true, Delimiter{}, false, "query", items2)
+ check("echo 'foo'\\''bar baz'")
// {..}, strip leading whitespaces, preserve ansi
- result = replacePlaceholder("echo {..}", false, Delimiter{}, "query", items1)
+ result = replacePlaceholder("echo {..}", false, Delimiter{}, false, "query", items1)
check("echo 'foo'\\''bar \x1b[31mbaz\x1b[m'")
// {..}, strip leading whitespaces, strip ansi
- result = replacePlaceholder("echo {..}", true, Delimiter{}, "query", items1)
+ result = replacePlaceholder("echo {..}", true, Delimiter{}, false, "query", items1)
check("echo 'foo'\\''bar baz'")
// {q}
- result = replacePlaceholder("echo {} {q}", true, Delimiter{}, "query", items1)
+ result = replacePlaceholder("echo {} {q}", true, Delimiter{}, false, "query", items1)
check("echo ' foo'\\''bar baz' 'query'")
// {q}, multiple items
- result = replacePlaceholder("echo {}{q}{}", true, Delimiter{}, "query 'string'", items2)
+ result = replacePlaceholder("echo {+}{q}{+}", true, Delimiter{}, false, "query 'string'", items2)
check("echo 'foo'\\''bar baz' 'FOO'\\''BAR BAZ''query '\\''string'\\''''foo'\\''bar baz' 'FOO'\\''BAR BAZ'")
- result = replacePlaceholder("echo {1}/{2}/{2,1}/{-1}/{-2}/{}/{..}/{n.t}/\\{}/\\{1}/\\{q}/{3}", true, Delimiter{}, "query", items1)
+ result = replacePlaceholder("echo {}{q}{}", true, Delimiter{}, 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)
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{}, "query", items2)
+ result = replacePlaceholder("echo {1}/{2}/{-1}/{-2}/{..}/{n.t}/\\{}/\\{1}/\\{q}/{3}", true, Delimiter{}, 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)
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)
+ check("echo 'foo'\\''bar' 'FOO'\\''BAR'/'baz' 'BAZ'/'baz' 'BAZ'/'foo'\\''bar' 'FOO'\\''BAR'/'foo'\\''bar baz' 'FOO'\\''BAR BAZ'/{n.t}/{}/{1}/{q}/'' ''")
+
+ // No match
+ result = replacePlaceholder("echo {}/{+}", true, Delimiter{}, false, "query", []*Item{nil, nil})
+ check("echo /")
+
+ // No match, but with selections
+ result = replacePlaceholder("echo {}/{+}", true, Delimiter{}, false, "query", []*Item{nil, item1})
+ check("echo /' foo'\\''bar baz'")
+
// String delimiter
delim := "'"
- result = replacePlaceholder("echo {}/{1}/{2}", true, Delimiter{str: &delim}, "query", items1)
+ result = replacePlaceholder("echo {}/{1}/{2}", true, Delimiter{str: &delim}, 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}, "query", items1)
+ result = replacePlaceholder("echo {}/{1}/{3}/{2..3}", true, Delimiter{regex: regex}, false, "query", items1)
check("echo ' foo'\\''bar baz'/'f'/'r b'/''\\''bar b'")
}