summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2018-06-10 01:40:22 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2018-06-10 01:40:22 +0900
commit2c26f02f5ce263820a339595517152b70ed63b80 (patch)
tree2440ac305ef78bb9d96065ed61eda8aa7c146c08
parentaf87650bc42ec8b821a3502857ca28692d6525d4 (diff)
Improve preview window update events
- Update preview window even if there is no match for the query string if any of the placeholder expressions evaluates to a non-empty string. - Also, if the command template contains {q}, preview window will be updated if the query string changes even though the focus remains on the same item. An example: git log --oneline --color=always | fzf --reverse --ansi --preview \ '[ -n {1} ] && git show --color=always {1} || git show --color=always {q}' Close #1307
-rw-r--r--man/man1/fzf.13
-rw-r--r--src/terminal.go43
-rw-r--r--test/test_go.rb29
3 files changed, 67 insertions, 8 deletions
diff --git a/man/man1/fzf.1 b/man/man1/fzf.1
index 8f79c468..5dd00e49 100644
--- a/man/man1/fzf.1
+++ b/man/man1/fzf.1
@@ -291,6 +291,9 @@ from the replacement string. To preserve the whitespace, use the \fBs\fR flag.
Also, \fB{q}\fR is replaced to the current query string.
Note that you can escape a placeholder pattern by prepending a backslash.
+
+Preview window will be updated even when there is no match for the current
+query if any of the placeholder expressions evaluates to a non-empty string.
.RE
.TP
.BI "--preview-window=" "[POSITION][:SIZE[%]][:wrap][:hidden]"
diff --git a/src/terminal.go b/src/terminal.go
index 2daad273..664b7d47 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -224,6 +224,7 @@ const (
type placeholderFlags struct {
plus bool
preserveSpace bool
+ query bool
}
func toActions(types ...actionType) []action {
@@ -1152,6 +1153,8 @@ func parsePlaceholder(match string) (bool, string, placeholderFlags) {
case 's':
flags.preserveSpace = true
skipChars++
+ case 'q':
+ flags.query = true
default:
break
}
@@ -1162,14 +1165,17 @@ func parsePlaceholder(match string) (bool, string, placeholderFlags) {
return false, matchWithoutFlags, flags
}
-func hasPlusFlag(template string) bool {
+func hasPreviewFlags(template string) (plus bool, query bool) {
for _, match := range placeholder.FindAllString(template, -1) {
_, _, flags := parsePlaceholder(match)
if flags.plus {
- return true
+ plus = true
+ }
+ if flags.query {
+ query = true
}
}
- return false
+ return
}
func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, forcePlus bool, query string, allItems []*Item) string {
@@ -1288,13 +1294,28 @@ func (t *Terminal) currentItem() *Item {
func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, []*Item) {
current := t.currentItem()
- if !forcePlus && !hasPlusFlag(template) || len(t.selected) == 0 {
+ plus, query := hasPreviewFlags(template)
+ if !(query && len(t.input) > 0 || (forcePlus || plus) && len(t.selected) > 0) {
return current != nil, []*Item{current, current}
}
- sels := make([]*Item, len(t.selected)+1)
- sels[0] = current
- for i, sel := range t.sortSelected() {
- sels[i+1] = sel.item
+
+ // We would still want to update preview window even if there is no match if
+ // 1. command template contains {q} and the query string is not empty
+ // 2. or it contains {+} and we have more than one item already selected.
+ // To do so, we pass an empty Item instead of nil to trigger an update.
+ if current == nil {
+ current = &Item{}
+ }
+
+ var sels []*Item
+ if len(t.selected) == 0 {
+ sels = []*Item{current, current}
+ } else {
+ sels = make([]*Item, len(t.selected)+1)
+ sels[0] = current
+ for i, sel := range t.sortSelected() {
+ sels[i+1] = sel.item
+ }
}
return true, sels
}
@@ -1861,6 +1882,12 @@ func (t *Terminal) Loop() {
t.mutex.Unlock() // Must be unlocked before touching reqBox
if changed {
+ if t.isPreviewEnabled() {
+ _, q := hasPreviewFlags(t.preview.command)
+ if q {
+ t.version++
+ }
+ }
t.eventBox.Set(EvtSearchNew, t.sort)
}
for _, event := range events {
diff --git a/test/test_go.rb b/test/test_go.rb
index 3c487b4b..4f4f94e1 100644
--- a/test/test_go.rb
+++ b/test/test_go.rb
@@ -1355,6 +1355,35 @@ class TestGoFZF < TestBase
tmux.until { |_| %w[1 2 3] == File.readlines(tempname).map(&:chomp) }
end
+ def test_preview_flags
+ tmux.send_keys %(seq 10 | sed 's/^/:: /; s/$/ /' |
+ #{FZF} --multi --preview 'echo {{2}/{s2}/{+2}/{+s2}/{q}}'), :Enter
+ tmux.until { |lines| lines[1].include?('{1/1 /1/1 /}') }
+ tmux.send_keys '123'
+ tmux.until { |lines| lines[1].include?('{////123}') }
+ tmux.send_keys 'C-u', '1'
+ tmux.until { |lines| lines.match_count == 2 }
+ tmux.until { |lines| lines[1].include?('{1/1 /1/1 /1}') }
+ tmux.send_keys :BTab
+ tmux.until { |lines| lines[1].include?('{10/10 /1/1 /1}') }
+ tmux.send_keys :BTab
+ tmux.until { |lines| lines[1].include?('{10/10 /1 10/1 10 /1}') }
+ tmux.send_keys '2'
+ tmux.until { |lines| lines[1].include?('{//1 10/1 10 /12}') }
+ tmux.send_keys '3'
+ tmux.until { |lines| lines[1].include?('{//1 10/1 10 /123}') }
+ end
+
+ def test_preview_q_no_match
+ tmux.send_keys %(: | #{FZF} --preview 'echo foo {q}'), :Enter
+ tmux.until { |lines| lines.match_count == 0 }
+ tmux.until { |lines| !lines[1].include?('foo') }
+ tmux.send_keys 'bar'
+ tmux.until { |lines| lines[1].include?('foo bar') }
+ tmux.send_keys 'C-u'
+ tmux.until { |lines| !lines[1].include?('foo') }
+ end
+
def test_no_clear
tmux.send_keys "seq 10 | fzf --no-clear --inline-info --height 5 > #{tempname}", :Enter
prompt = '> < 10/10'