mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2025-01-23 15:18:29 +00:00
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
This commit is contained in:
parent
af87650bc4
commit
2c26f02f5c
@ -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.
|
Also, \fB{q}\fR is replaced to the current query string.
|
||||||
|
|
||||||
Note that you can escape a placeholder pattern by prepending a backslash.
|
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
|
.RE
|
||||||
.TP
|
.TP
|
||||||
.BI "--preview-window=" "[POSITION][:SIZE[%]][:wrap][:hidden]"
|
.BI "--preview-window=" "[POSITION][:SIZE[%]][:wrap][:hidden]"
|
||||||
|
@ -224,6 +224,7 @@ const (
|
|||||||
type placeholderFlags struct {
|
type placeholderFlags struct {
|
||||||
plus bool
|
plus bool
|
||||||
preserveSpace bool
|
preserveSpace bool
|
||||||
|
query bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func toActions(types ...actionType) []action {
|
func toActions(types ...actionType) []action {
|
||||||
@ -1152,6 +1153,8 @@ func parsePlaceholder(match string) (bool, string, placeholderFlags) {
|
|||||||
case 's':
|
case 's':
|
||||||
flags.preserveSpace = true
|
flags.preserveSpace = true
|
||||||
skipChars++
|
skipChars++
|
||||||
|
case 'q':
|
||||||
|
flags.query = true
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1162,14 +1165,17 @@ func parsePlaceholder(match string) (bool, string, placeholderFlags) {
|
|||||||
return false, matchWithoutFlags, flags
|
return false, matchWithoutFlags, flags
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasPlusFlag(template string) bool {
|
func hasPreviewFlags(template string) (plus bool, query bool) {
|
||||||
for _, match := range placeholder.FindAllString(template, -1) {
|
for _, match := range placeholder.FindAllString(template, -1) {
|
||||||
_, _, flags := parsePlaceholder(match)
|
_, _, flags := parsePlaceholder(match)
|
||||||
if flags.plus {
|
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 {
|
func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, forcePlus bool, query string, allItems []*Item) string {
|
||||||
@ -1288,14 +1294,29 @@ func (t *Terminal) currentItem() *Item {
|
|||||||
|
|
||||||
func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, []*Item) {
|
func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, []*Item) {
|
||||||
current := t.currentItem()
|
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}
|
return current != nil, []*Item{current, current}
|
||||||
}
|
}
|
||||||
sels := make([]*Item, len(t.selected)+1)
|
|
||||||
|
// 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
|
sels[0] = current
|
||||||
for i, sel := range t.sortSelected() {
|
for i, sel := range t.sortSelected() {
|
||||||
sels[i+1] = sel.item
|
sels[i+1] = sel.item
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true, sels
|
return true, sels
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1861,6 +1882,12 @@ func (t *Terminal) Loop() {
|
|||||||
t.mutex.Unlock() // Must be unlocked before touching reqBox
|
t.mutex.Unlock() // Must be unlocked before touching reqBox
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
|
if t.isPreviewEnabled() {
|
||||||
|
_, q := hasPreviewFlags(t.preview.command)
|
||||||
|
if q {
|
||||||
|
t.version++
|
||||||
|
}
|
||||||
|
}
|
||||||
t.eventBox.Set(EvtSearchNew, t.sort)
|
t.eventBox.Set(EvtSearchNew, t.sort)
|
||||||
}
|
}
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
|
@ -1355,6 +1355,35 @@ class TestGoFZF < TestBase
|
|||||||
tmux.until { |_| %w[1 2 3] == File.readlines(tempname).map(&:chomp) }
|
tmux.until { |_| %w[1 2 3] == File.readlines(tempname).map(&:chomp) }
|
||||||
end
|
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
|
def test_no_clear
|
||||||
tmux.send_keys "seq 10 | fzf --no-clear --inline-info --height 5 > #{tempname}", :Enter
|
tmux.send_keys "seq 10 | fzf --no-clear --inline-info --height 5 > #{tempname}", :Enter
|
||||||
prompt = '> < 10/10'
|
prompt = '> < 10/10'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user