Skip to content

Commit 43f0d0c

Browse files
committed
change-preview-window to take multiple option sets separated by '|'
So you can "rotate" through the different options with a single binding. fzf --preview 'cat {}' \ --bind 'ctrl-/:change-preview-window(70%|down,40%,border-horizontal|hidden|)' Close junegunn#2376
1 parent 20b4e69 commit 43f0d0c

File tree

5 files changed

+87
-37
lines changed

5 files changed

+87
-37
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ CHANGELOG
77
- cf. `preview(...)` is a one-off action that doesn't change the default
88
preview command
99
- Added `change-preview-window(...)` action
10+
- You can rotate through the different options separated by `|`
11+
```sh
12+
fzf --preview 'cat {}' --bind 'ctrl-/:change-preview-window(right,70%|down,40%,border-horizontal|hidden|right)'
13+
```
1014

1115
0.28.0
1216
------

man/man1/fzf.1

+17-2
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ A key or an event can be bound to one or more of the following actions.
821821
\fBbeginning-of-line\fR \fIctrl-a home\fR
822822
\fBcancel\fR (clear query string if not empty, abort fzf otherwise)
823823
\fBchange-preview(...)\fR (change \fB--preview\fR option)
824-
\fBchange-preview-window(...)\fR (change \fB--preview-window\fR option)
824+
\fBchange-preview-window(...)\fR (change \fB--preview-window\fR option; rotate through the multiple option sets separated by '|')
825825
\fBchange-prompt(...)\fR (change prompt to the given string)
826826
\fBclear-screen\fR \fIctrl-l\fR
827827
\fBclear-selection\fR (clear multi-selection)
@@ -972,7 +972,6 @@ commands in addition to the default preview command given by \fB--preview\fR
972972
option.
973973

974974
e.g.
975-
976975
# Default preview command with an extra preview binding
977976
fzf --preview 'file {}' --bind '?:preview:cat {}'
978977

@@ -983,6 +982,22 @@ e.g.
983982
# Preview window hidden by default, it appears when you first hit '?'
984983
fzf --bind '?:preview:cat {}' --preview-window hidden
985984

985+
.SS CHANGE PREVIEW WINDOW ATTRIBUTES
986+
987+
\fBchange-preview-window\fR action can be used to change the properties of the
988+
preview window. Unlike the \fB--preview-window\fR option, you can specify
989+
multiple sets of options separated by '|' characters.
990+
991+
e.g.
992+
# Rotate through the options using CTRL-/
993+
fzf --preview 'cat {}' --bind 'ctrl-/:change-preview-window(right,70%|down,40%,border-horizontal|hidden|right)'
994+
995+
# The default properties given by `--preview-window` are inherited, so an empty string in the list is interpreted as the default
996+
fzf --preview 'cat {}' --preview-window 'right,40%,border-left' --bind 'ctrl-/:change-preview-window(70%|down,border-top|hidden|)'
997+
998+
# This is equivalent to toggle-preview action
999+
fzf --preview 'cat {}' --bind 'ctrl-/:change-preview-window(hidden|)'
1000+
9861001
.SH AUTHOR
9871002
Junegunn Choi (\fI[email protected]\fR)
9881003

src/options.go

+13-11
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ type Options struct {
224224
Filter *string
225225
ToggleSort bool
226226
Expect map[tui.Event]string
227-
Keymap map[tui.Event][]action
227+
Keymap map[tui.Event][]*action
228228
Preview previewOpts
229229
PrintQuery bool
230230
ReadZero bool
@@ -287,7 +287,7 @@ func defaultOptions() *Options {
287287
Filter: nil,
288288
ToggleSort: false,
289289
Expect: make(map[tui.Event]string),
290-
Keymap: make(map[tui.Event][]action),
290+
Keymap: make(map[tui.Event][]*action),
291291
Preview: defaultPreviewOpts(""),
292292
PrintQuery: false,
293293
ReadZero: false,
@@ -798,7 +798,7 @@ func init() {
798798
`(?si)[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|unbind):.+|[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|unbind)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
799799
}
800800

801-
func parseKeymap(keymap map[tui.Event][]action, str string) {
801+
func parseKeymap(keymap map[tui.Event][]*action, str string) {
802802
masked := executeRegexp.ReplaceAllStringFunc(str, func(src string) string {
803803
symbol := ":"
804804
if strings.HasPrefix(src, "+") {
@@ -854,7 +854,7 @@ func parseKeymap(keymap map[tui.Event][]action, str string) {
854854

855855
idx2 := len(pair[0]) + 1
856856
specs := strings.Split(pair[1], "+")
857-
actions := make([]action, 0, len(specs))
857+
actions := make([]*action, 0, len(specs))
858858
appendAction := func(types ...actionType) {
859859
actions = append(actions, toActions(types...)...)
860860
}
@@ -1033,20 +1033,22 @@ func parseKeymap(keymap map[tui.Event][]action, str string) {
10331033
if spec[offset] == ':' {
10341034
if specIndex == len(specs)-1 {
10351035
actionArg = spec[offset+1:]
1036-
actions = append(actions, action{t: t, a: actionArg})
1036+
actions = append(actions, &action{t: t, a: actionArg})
10371037
} else {
10381038
prevSpec = spec + "+"
10391039
continue
10401040
}
10411041
} else {
10421042
actionArg = spec[offset+1 : len(spec)-1]
1043-
actions = append(actions, action{t: t, a: actionArg})
1043+
actions = append(actions, &action{t: t, a: actionArg})
10441044
}
10451045
if t == actUnbind {
10461046
parseKeyChords(actionArg, "unbind target required")
10471047
} else if t == actChangePreviewWindow {
10481048
opts := previewOpts{}
1049-
parsePreviewWindow(&opts, actionArg)
1049+
for _, arg := range strings.Split(actionArg, "|") {
1050+
parsePreviewWindow(&opts, arg)
1051+
}
10501052
}
10511053
}
10521054
}
@@ -1088,7 +1090,7 @@ func isExecuteAction(str string) actionType {
10881090
return actIgnore
10891091
}
10901092

1091-
func parseToggleSort(keymap map[tui.Event][]action, str string) {
1093+
func parseToggleSort(keymap map[tui.Event][]*action, str string) {
10921094
keys := parseKeyChords(str, "key name required")
10931095
if len(keys) != 1 {
10941096
errorExit("multiple keys specified")
@@ -1656,7 +1658,7 @@ func postProcessOptions(opts *Options) {
16561658
// Extend the default key map
16571659
keymap := defaultKeymap()
16581660
for key, actions := range opts.Keymap {
1659-
lastChangePreviewWindow := action{t: actIgnore}
1661+
var lastChangePreviewWindow *action
16601662
for _, act := range actions {
16611663
switch act.t {
16621664
case actToggleSort:
@@ -1670,8 +1672,8 @@ func postProcessOptions(opts *Options) {
16701672
// and it comes first in the list.
16711673
// * change-preview-window(up,+10)+preview(sleep 3; cat {})+change-preview-window(up,+20)
16721674
// -> change-preview-window(up,+20)+preview(sleep 3; cat {})
1673-
if lastChangePreviewWindow.t == actChangePreviewWindow {
1674-
reordered := []action{lastChangePreviewWindow}
1675+
if lastChangePreviewWindow != nil {
1676+
reordered := []*action{lastChangePreviewWindow}
16751677
for _, act := range actions {
16761678
if act.t != actChangePreviewWindow {
16771679
reordered = append(reordered, act)

src/terminal.go

+36-24
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ type Terminal struct {
135135
toggleSort bool
136136
delimiter Delimiter
137137
expect map[tui.Event]string
138-
keymap map[tui.Event][]action
138+
keymap map[tui.Event][]*action
139139
pressed string
140140
printQuery bool
141141
history *History
@@ -217,6 +217,7 @@ const (
217217
reqRefresh
218218
reqReinit
219219
reqRedraw
220+
reqFullRedraw
220221
reqClose
221222
reqPrintQuery
222223
reqPreviewEnqueue
@@ -229,6 +230,7 @@ const (
229230
type action struct {
230231
t actionType
231232
a string
233+
c int
232234
}
233235

234236
type actionType int
@@ -340,16 +342,16 @@ type previewResult struct {
340342
spinner string
341343
}
342344

343-
func toActions(types ...actionType) []action {
344-
actions := make([]action, len(types))
345+
func toActions(types ...actionType) []*action {
346+
actions := make([]*action, len(types))
345347
for idx, t := range types {
346-
actions[idx] = action{t: t, a: ""}
348+
actions[idx] = &action{t: t, a: ""}
347349
}
348350
return actions
349351
}
350352

351-
func defaultKeymap() map[tui.Event][]action {
352-
keymap := make(map[tui.Event][]action)
353+
func defaultKeymap() map[tui.Event][]*action {
354+
keymap := make(map[tui.Event][]*action)
353355
add := func(e tui.EventType, a actionType) {
354356
keymap[e.AsEvent()] = toActions(a)
355357
}
@@ -1778,8 +1780,10 @@ func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, pr
17781780
})
17791781
}
17801782

1781-
func (t *Terminal) redraw() {
1782-
t.tui.Clear()
1783+
func (t *Terminal) redraw(clear bool) {
1784+
if clear {
1785+
t.tui.Clear()
1786+
}
17831787
t.tui.Refresh()
17841788
t.printAll()
17851789
}
@@ -1799,7 +1803,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
17991803
t.tui.Pause(true)
18001804
cmd.Run()
18011805
t.tui.Resume(true, false)
1802-
t.redraw()
1806+
t.redraw(true)
18031807
t.refresh()
18041808
} else {
18051809
t.tui.Pause(false)
@@ -1944,7 +1948,7 @@ func (t *Terminal) Loop() {
19441948
go func() {
19451949
for {
19461950
<-resizeChan
1947-
t.reqBox.Set(reqRedraw, nil)
1951+
t.reqBox.Set(reqFullRedraw, nil)
19481952
}
19491953
}()
19501954

@@ -2194,9 +2198,11 @@ func (t *Terminal) Loop() {
21942198
t.suppress = false
21952199
case reqReinit:
21962200
t.tui.Resume(t.fullscreen, t.sigstop)
2197-
t.redraw()
2201+
t.redraw(true)
21982202
case reqRedraw:
2199-
t.redraw()
2203+
t.redraw(false)
2204+
case reqFullRedraw:
2205+
t.redraw(true)
22002206
case reqClose:
22012207
exit(func() int {
22022208
if t.output() {
@@ -2268,7 +2274,6 @@ func (t *Terminal) Loop() {
22682274
if t.previewer.enabled != enabled {
22692275
t.previewer.enabled = enabled
22702276
// We need to immediately update t.pwindow so we don't use reqRedraw
2271-
t.tui.Clear()
22722277
t.resizeWindows()
22732278
req(reqPrompt, reqList, reqInfo, reqHeader)
22742279
return true
@@ -2310,20 +2315,20 @@ func (t *Terminal) Loop() {
23102315
}
23112316
}
23122317

2313-
actionsFor := func(eventType tui.EventType) []action {
2318+
actionsFor := func(eventType tui.EventType) []*action {
23142319
return t.keymap[eventType.AsEvent()]
23152320
}
23162321

2317-
var doAction func(action) bool
2318-
doActions := func(actions []action) bool {
2322+
var doAction func(*action) bool
2323+
doActions := func(actions []*action) bool {
23192324
for _, action := range actions {
23202325
if !doAction(action) {
23212326
return false
23222327
}
23232328
}
23242329
return true
23252330
}
2326-
doAction = func(a action) bool {
2331+
doAction = func(a *action) bool {
23272332
switch a.t {
23282333
case actIgnore:
23292334
case actExecute, actExecuteSilent:
@@ -2503,14 +2508,14 @@ func (t *Terminal) Loop() {
25032508
}
25042509
case actToggleIn:
25052510
if t.layout != layoutDefault {
2506-
return doAction(action{t: actToggleUp})
2511+
return doAction(&action{t: actToggleUp})
25072512
}
2508-
return doAction(action{t: actToggleDown})
2513+
return doAction(&action{t: actToggleDown})
25092514
case actToggleOut:
25102515
if t.layout != layoutDefault {
2511-
return doAction(action{t: actToggleDown})
2516+
return doAction(&action{t: actToggleDown})
25122517
}
2513-
return doAction(action{t: actToggleUp})
2518+
return doAction(&action{t: actToggleUp})
25142519
case actToggleDown:
25152520
if t.multi > 0 && t.merger.Length() > 0 && toggle() {
25162521
t.vmove(-1, true)
@@ -2534,7 +2539,7 @@ func (t *Terminal) Loop() {
25342539
req(reqClose)
25352540
}
25362541
case actClearScreen:
2537-
req(reqRedraw)
2542+
req(reqFullRedraw)
25382543
case actClearQuery:
25392544
t.input = []rune{}
25402545
t.cx = 0
@@ -2732,7 +2737,14 @@ func (t *Terminal) Loop() {
27322737

27332738
// Reset preview options and apply the additional options
27342739
t.previewOpts = t.initialPreviewOpts
2735-
parsePreviewWindow(&t.previewOpts, a.a)
2740+
2741+
// Split window options
2742+
tokens := strings.Split(a.a, "|")
2743+
parsePreviewWindow(&t.previewOpts, tokens[0])
2744+
if len(tokens) > 1 {
2745+
a.a = strings.Join(append(tokens[1:], tokens[0]), "|")
2746+
a.c++
2747+
}
27362748

27372749
if t.previewOpts.hidden {
27382750
togglePreview(false)
@@ -2761,7 +2773,7 @@ func (t *Terminal) Loop() {
27612773
if t.jumping == jumpDisabled {
27622774
actions := t.keymap[event.Comparable()]
27632775
if len(actions) == 0 && event.Type == tui.Rune {
2764-
doAction(action{t: actRune})
2776+
doAction(&action{t: actRune})
27652777
} else if !doActions(actions) {
27662778
continue
27672779
}

test/test_go.rb

+17
Original file line numberDiff line numberDiff line change
@@ -2191,6 +2191,23 @@ def test_change_preview_window
21912191
assert_equal ' 3', lines[1]
21922192
end
21932193
end
2194+
2195+
def test_change_preview_window_rotate
2196+
tmux.send_keys "seq 100 | #{FZF} --preview-window left,border-none --preview 'echo hello' --bind '" \
2197+
"a:change-preview-window(right|down|up|hidden|)'", :Enter
2198+
3.times do
2199+
tmux.until { |lines| lines[0].start_with?('hello') }
2200+
tmux.send_keys 'a'
2201+
tmux.until { |lines| lines[0].end_with?('hello') }
2202+
tmux.send_keys 'a'
2203+
tmux.until { |lines| lines[-1].start_with?('hello') }
2204+
tmux.send_keys 'a'
2205+
tmux.until { |lines| assert_equal 'hello', lines[0] }
2206+
tmux.send_keys 'a'
2207+
tmux.until { |lines| refute_includes lines[0], 'hello' }
2208+
tmux.send_keys 'a'
2209+
end
2210+
end
21942211
end
21952212

21962213
module TestShell

0 commit comments

Comments
 (0)