mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2025-02-03 12:38:39 +00:00
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 #2376
This commit is contained in:
parent
20b4e6953e
commit
43f0d0cacd
@ -7,6 +7,10 @@ CHANGELOG
|
|||||||
- cf. `preview(...)` is a one-off action that doesn't change the default
|
- cf. `preview(...)` is a one-off action that doesn't change the default
|
||||||
preview command
|
preview command
|
||||||
- Added `change-preview-window(...)` action
|
- Added `change-preview-window(...)` action
|
||||||
|
- You can rotate through the different options separated by `|`
|
||||||
|
```sh
|
||||||
|
fzf --preview 'cat {}' --bind 'ctrl-/:change-preview-window(right,70%|down,40%,border-horizontal|hidden|right)'
|
||||||
|
```
|
||||||
|
|
||||||
0.28.0
|
0.28.0
|
||||||
------
|
------
|
||||||
|
@ -821,7 +821,7 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBbeginning-of-line\fR \fIctrl-a home\fR
|
\fBbeginning-of-line\fR \fIctrl-a home\fR
|
||||||
\fBcancel\fR (clear query string if not empty, abort fzf otherwise)
|
\fBcancel\fR (clear query string if not empty, abort fzf otherwise)
|
||||||
\fBchange-preview(...)\fR (change \fB--preview\fR option)
|
\fBchange-preview(...)\fR (change \fB--preview\fR option)
|
||||||
\fBchange-preview-window(...)\fR (change \fB--preview-window\fR option)
|
\fBchange-preview-window(...)\fR (change \fB--preview-window\fR option; rotate through the multiple option sets separated by '|')
|
||||||
\fBchange-prompt(...)\fR (change prompt to the given string)
|
\fBchange-prompt(...)\fR (change prompt to the given string)
|
||||||
\fBclear-screen\fR \fIctrl-l\fR
|
\fBclear-screen\fR \fIctrl-l\fR
|
||||||
\fBclear-selection\fR (clear multi-selection)
|
\fBclear-selection\fR (clear multi-selection)
|
||||||
@ -972,7 +972,6 @@ commands in addition to the default preview command given by \fB--preview\fR
|
|||||||
option.
|
option.
|
||||||
|
|
||||||
e.g.
|
e.g.
|
||||||
|
|
||||||
# Default preview command with an extra preview binding
|
# Default preview command with an extra preview binding
|
||||||
fzf --preview 'file {}' --bind '?:preview:cat {}'
|
fzf --preview 'file {}' --bind '?:preview:cat {}'
|
||||||
|
|
||||||
@ -983,6 +982,22 @@ e.g.
|
|||||||
# Preview window hidden by default, it appears when you first hit '?'
|
# Preview window hidden by default, it appears when you first hit '?'
|
||||||
fzf --bind '?:preview:cat {}' --preview-window hidden
|
fzf --bind '?:preview:cat {}' --preview-window hidden
|
||||||
|
|
||||||
|
.SS CHANGE PREVIEW WINDOW ATTRIBUTES
|
||||||
|
|
||||||
|
\fBchange-preview-window\fR action can be used to change the properties of the
|
||||||
|
preview window. Unlike the \fB--preview-window\fR option, you can specify
|
||||||
|
multiple sets of options separated by '|' characters.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
# Rotate through the options using CTRL-/
|
||||||
|
fzf --preview 'cat {}' --bind 'ctrl-/:change-preview-window(right,70%|down,40%,border-horizontal|hidden|right)'
|
||||||
|
|
||||||
|
# The default properties given by `--preview-window` are inherited, so an empty string in the list is interpreted as the default
|
||||||
|
fzf --preview 'cat {}' --preview-window 'right,40%,border-left' --bind 'ctrl-/:change-preview-window(70%|down,border-top|hidden|)'
|
||||||
|
|
||||||
|
# This is equivalent to toggle-preview action
|
||||||
|
fzf --preview 'cat {}' --bind 'ctrl-/:change-preview-window(hidden|)'
|
||||||
|
|
||||||
.SH AUTHOR
|
.SH AUTHOR
|
||||||
Junegunn Choi (\fIjunegunn.c@gmail.com\fR)
|
Junegunn Choi (\fIjunegunn.c@gmail.com\fR)
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ type Options struct {
|
|||||||
Filter *string
|
Filter *string
|
||||||
ToggleSort bool
|
ToggleSort bool
|
||||||
Expect map[tui.Event]string
|
Expect map[tui.Event]string
|
||||||
Keymap map[tui.Event][]action
|
Keymap map[tui.Event][]*action
|
||||||
Preview previewOpts
|
Preview previewOpts
|
||||||
PrintQuery bool
|
PrintQuery bool
|
||||||
ReadZero bool
|
ReadZero bool
|
||||||
@ -287,7 +287,7 @@ func defaultOptions() *Options {
|
|||||||
Filter: nil,
|
Filter: nil,
|
||||||
ToggleSort: false,
|
ToggleSort: false,
|
||||||
Expect: make(map[tui.Event]string),
|
Expect: make(map[tui.Event]string),
|
||||||
Keymap: make(map[tui.Event][]action),
|
Keymap: make(map[tui.Event][]*action),
|
||||||
Preview: defaultPreviewOpts(""),
|
Preview: defaultPreviewOpts(""),
|
||||||
PrintQuery: false,
|
PrintQuery: false,
|
||||||
ReadZero: false,
|
ReadZero: false,
|
||||||
@ -798,7 +798,7 @@ func init() {
|
|||||||
`(?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)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
|
`(?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)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseKeymap(keymap map[tui.Event][]action, str string) {
|
func parseKeymap(keymap map[tui.Event][]*action, str string) {
|
||||||
masked := executeRegexp.ReplaceAllStringFunc(str, func(src string) string {
|
masked := executeRegexp.ReplaceAllStringFunc(str, func(src string) string {
|
||||||
symbol := ":"
|
symbol := ":"
|
||||||
if strings.HasPrefix(src, "+") {
|
if strings.HasPrefix(src, "+") {
|
||||||
@ -854,7 +854,7 @@ func parseKeymap(keymap map[tui.Event][]action, str string) {
|
|||||||
|
|
||||||
idx2 := len(pair[0]) + 1
|
idx2 := len(pair[0]) + 1
|
||||||
specs := strings.Split(pair[1], "+")
|
specs := strings.Split(pair[1], "+")
|
||||||
actions := make([]action, 0, len(specs))
|
actions := make([]*action, 0, len(specs))
|
||||||
appendAction := func(types ...actionType) {
|
appendAction := func(types ...actionType) {
|
||||||
actions = append(actions, toActions(types...)...)
|
actions = append(actions, toActions(types...)...)
|
||||||
}
|
}
|
||||||
@ -1033,20 +1033,22 @@ func parseKeymap(keymap map[tui.Event][]action, str string) {
|
|||||||
if spec[offset] == ':' {
|
if spec[offset] == ':' {
|
||||||
if specIndex == len(specs)-1 {
|
if specIndex == len(specs)-1 {
|
||||||
actionArg = spec[offset+1:]
|
actionArg = spec[offset+1:]
|
||||||
actions = append(actions, action{t: t, a: actionArg})
|
actions = append(actions, &action{t: t, a: actionArg})
|
||||||
} else {
|
} else {
|
||||||
prevSpec = spec + "+"
|
prevSpec = spec + "+"
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
actionArg = spec[offset+1 : len(spec)-1]
|
actionArg = spec[offset+1 : len(spec)-1]
|
||||||
actions = append(actions, action{t: t, a: actionArg})
|
actions = append(actions, &action{t: t, a: actionArg})
|
||||||
}
|
}
|
||||||
if t == actUnbind {
|
if t == actUnbind {
|
||||||
parseKeyChords(actionArg, "unbind target required")
|
parseKeyChords(actionArg, "unbind target required")
|
||||||
} else if t == actChangePreviewWindow {
|
} else if t == actChangePreviewWindow {
|
||||||
opts := previewOpts{}
|
opts := previewOpts{}
|
||||||
parsePreviewWindow(&opts, actionArg)
|
for _, arg := range strings.Split(actionArg, "|") {
|
||||||
|
parsePreviewWindow(&opts, arg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1088,7 +1090,7 @@ func isExecuteAction(str string) actionType {
|
|||||||
return actIgnore
|
return actIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseToggleSort(keymap map[tui.Event][]action, str string) {
|
func parseToggleSort(keymap map[tui.Event][]*action, str string) {
|
||||||
keys := parseKeyChords(str, "key name required")
|
keys := parseKeyChords(str, "key name required")
|
||||||
if len(keys) != 1 {
|
if len(keys) != 1 {
|
||||||
errorExit("multiple keys specified")
|
errorExit("multiple keys specified")
|
||||||
@ -1656,7 +1658,7 @@ func postProcessOptions(opts *Options) {
|
|||||||
// Extend the default key map
|
// Extend the default key map
|
||||||
keymap := defaultKeymap()
|
keymap := defaultKeymap()
|
||||||
for key, actions := range opts.Keymap {
|
for key, actions := range opts.Keymap {
|
||||||
lastChangePreviewWindow := action{t: actIgnore}
|
var lastChangePreviewWindow *action
|
||||||
for _, act := range actions {
|
for _, act := range actions {
|
||||||
switch act.t {
|
switch act.t {
|
||||||
case actToggleSort:
|
case actToggleSort:
|
||||||
@ -1670,8 +1672,8 @@ func postProcessOptions(opts *Options) {
|
|||||||
// and it comes first in the list.
|
// and it comes first in the list.
|
||||||
// * change-preview-window(up,+10)+preview(sleep 3; cat {})+change-preview-window(up,+20)
|
// * change-preview-window(up,+10)+preview(sleep 3; cat {})+change-preview-window(up,+20)
|
||||||
// -> change-preview-window(up,+20)+preview(sleep 3; cat {})
|
// -> change-preview-window(up,+20)+preview(sleep 3; cat {})
|
||||||
if lastChangePreviewWindow.t == actChangePreviewWindow {
|
if lastChangePreviewWindow != nil {
|
||||||
reordered := []action{lastChangePreviewWindow}
|
reordered := []*action{lastChangePreviewWindow}
|
||||||
for _, act := range actions {
|
for _, act := range actions {
|
||||||
if act.t != actChangePreviewWindow {
|
if act.t != actChangePreviewWindow {
|
||||||
reordered = append(reordered, act)
|
reordered = append(reordered, act)
|
||||||
|
@ -135,7 +135,7 @@ type Terminal struct {
|
|||||||
toggleSort bool
|
toggleSort bool
|
||||||
delimiter Delimiter
|
delimiter Delimiter
|
||||||
expect map[tui.Event]string
|
expect map[tui.Event]string
|
||||||
keymap map[tui.Event][]action
|
keymap map[tui.Event][]*action
|
||||||
pressed string
|
pressed string
|
||||||
printQuery bool
|
printQuery bool
|
||||||
history *History
|
history *History
|
||||||
@ -217,6 +217,7 @@ const (
|
|||||||
reqRefresh
|
reqRefresh
|
||||||
reqReinit
|
reqReinit
|
||||||
reqRedraw
|
reqRedraw
|
||||||
|
reqFullRedraw
|
||||||
reqClose
|
reqClose
|
||||||
reqPrintQuery
|
reqPrintQuery
|
||||||
reqPreviewEnqueue
|
reqPreviewEnqueue
|
||||||
@ -229,6 +230,7 @@ const (
|
|||||||
type action struct {
|
type action struct {
|
||||||
t actionType
|
t actionType
|
||||||
a string
|
a string
|
||||||
|
c int
|
||||||
}
|
}
|
||||||
|
|
||||||
type actionType int
|
type actionType int
|
||||||
@ -340,16 +342,16 @@ type previewResult struct {
|
|||||||
spinner string
|
spinner string
|
||||||
}
|
}
|
||||||
|
|
||||||
func toActions(types ...actionType) []action {
|
func toActions(types ...actionType) []*action {
|
||||||
actions := make([]action, len(types))
|
actions := make([]*action, len(types))
|
||||||
for idx, t := range types {
|
for idx, t := range types {
|
||||||
actions[idx] = action{t: t, a: ""}
|
actions[idx] = &action{t: t, a: ""}
|
||||||
}
|
}
|
||||||
return actions
|
return actions
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultKeymap() map[tui.Event][]action {
|
func defaultKeymap() map[tui.Event][]*action {
|
||||||
keymap := make(map[tui.Event][]action)
|
keymap := make(map[tui.Event][]*action)
|
||||||
add := func(e tui.EventType, a actionType) {
|
add := func(e tui.EventType, a actionType) {
|
||||||
keymap[e.AsEvent()] = toActions(a)
|
keymap[e.AsEvent()] = toActions(a)
|
||||||
}
|
}
|
||||||
@ -1778,8 +1780,10 @@ func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, pr
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) redraw() {
|
func (t *Terminal) redraw(clear bool) {
|
||||||
|
if clear {
|
||||||
t.tui.Clear()
|
t.tui.Clear()
|
||||||
|
}
|
||||||
t.tui.Refresh()
|
t.tui.Refresh()
|
||||||
t.printAll()
|
t.printAll()
|
||||||
}
|
}
|
||||||
@ -1799,7 +1803,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
|
|||||||
t.tui.Pause(true)
|
t.tui.Pause(true)
|
||||||
cmd.Run()
|
cmd.Run()
|
||||||
t.tui.Resume(true, false)
|
t.tui.Resume(true, false)
|
||||||
t.redraw()
|
t.redraw(true)
|
||||||
t.refresh()
|
t.refresh()
|
||||||
} else {
|
} else {
|
||||||
t.tui.Pause(false)
|
t.tui.Pause(false)
|
||||||
@ -1944,7 +1948,7 @@ func (t *Terminal) Loop() {
|
|||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
<-resizeChan
|
<-resizeChan
|
||||||
t.reqBox.Set(reqRedraw, nil)
|
t.reqBox.Set(reqFullRedraw, nil)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -2194,9 +2198,11 @@ func (t *Terminal) Loop() {
|
|||||||
t.suppress = false
|
t.suppress = false
|
||||||
case reqReinit:
|
case reqReinit:
|
||||||
t.tui.Resume(t.fullscreen, t.sigstop)
|
t.tui.Resume(t.fullscreen, t.sigstop)
|
||||||
t.redraw()
|
t.redraw(true)
|
||||||
case reqRedraw:
|
case reqRedraw:
|
||||||
t.redraw()
|
t.redraw(false)
|
||||||
|
case reqFullRedraw:
|
||||||
|
t.redraw(true)
|
||||||
case reqClose:
|
case reqClose:
|
||||||
exit(func() int {
|
exit(func() int {
|
||||||
if t.output() {
|
if t.output() {
|
||||||
@ -2268,7 +2274,6 @@ func (t *Terminal) Loop() {
|
|||||||
if t.previewer.enabled != enabled {
|
if t.previewer.enabled != enabled {
|
||||||
t.previewer.enabled = enabled
|
t.previewer.enabled = enabled
|
||||||
// We need to immediately update t.pwindow so we don't use reqRedraw
|
// We need to immediately update t.pwindow so we don't use reqRedraw
|
||||||
t.tui.Clear()
|
|
||||||
t.resizeWindows()
|
t.resizeWindows()
|
||||||
req(reqPrompt, reqList, reqInfo, reqHeader)
|
req(reqPrompt, reqList, reqInfo, reqHeader)
|
||||||
return true
|
return true
|
||||||
@ -2310,12 +2315,12 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actionsFor := func(eventType tui.EventType) []action {
|
actionsFor := func(eventType tui.EventType) []*action {
|
||||||
return t.keymap[eventType.AsEvent()]
|
return t.keymap[eventType.AsEvent()]
|
||||||
}
|
}
|
||||||
|
|
||||||
var doAction func(action) bool
|
var doAction func(*action) bool
|
||||||
doActions := func(actions []action) bool {
|
doActions := func(actions []*action) bool {
|
||||||
for _, action := range actions {
|
for _, action := range actions {
|
||||||
if !doAction(action) {
|
if !doAction(action) {
|
||||||
return false
|
return false
|
||||||
@ -2323,7 +2328,7 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
doAction = func(a action) bool {
|
doAction = func(a *action) bool {
|
||||||
switch a.t {
|
switch a.t {
|
||||||
case actIgnore:
|
case actIgnore:
|
||||||
case actExecute, actExecuteSilent:
|
case actExecute, actExecuteSilent:
|
||||||
@ -2503,14 +2508,14 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
case actToggleIn:
|
case actToggleIn:
|
||||||
if t.layout != layoutDefault {
|
if t.layout != layoutDefault {
|
||||||
return doAction(action{t: actToggleUp})
|
return doAction(&action{t: actToggleUp})
|
||||||
}
|
}
|
||||||
return doAction(action{t: actToggleDown})
|
return doAction(&action{t: actToggleDown})
|
||||||
case actToggleOut:
|
case actToggleOut:
|
||||||
if t.layout != layoutDefault {
|
if t.layout != layoutDefault {
|
||||||
return doAction(action{t: actToggleDown})
|
return doAction(&action{t: actToggleDown})
|
||||||
}
|
}
|
||||||
return doAction(action{t: actToggleUp})
|
return doAction(&action{t: actToggleUp})
|
||||||
case actToggleDown:
|
case actToggleDown:
|
||||||
if t.multi > 0 && t.merger.Length() > 0 && toggle() {
|
if t.multi > 0 && t.merger.Length() > 0 && toggle() {
|
||||||
t.vmove(-1, true)
|
t.vmove(-1, true)
|
||||||
@ -2534,7 +2539,7 @@ func (t *Terminal) Loop() {
|
|||||||
req(reqClose)
|
req(reqClose)
|
||||||
}
|
}
|
||||||
case actClearScreen:
|
case actClearScreen:
|
||||||
req(reqRedraw)
|
req(reqFullRedraw)
|
||||||
case actClearQuery:
|
case actClearQuery:
|
||||||
t.input = []rune{}
|
t.input = []rune{}
|
||||||
t.cx = 0
|
t.cx = 0
|
||||||
@ -2732,7 +2737,14 @@ func (t *Terminal) Loop() {
|
|||||||
|
|
||||||
// Reset preview options and apply the additional options
|
// Reset preview options and apply the additional options
|
||||||
t.previewOpts = t.initialPreviewOpts
|
t.previewOpts = t.initialPreviewOpts
|
||||||
parsePreviewWindow(&t.previewOpts, a.a)
|
|
||||||
|
// Split window options
|
||||||
|
tokens := strings.Split(a.a, "|")
|
||||||
|
parsePreviewWindow(&t.previewOpts, tokens[0])
|
||||||
|
if len(tokens) > 1 {
|
||||||
|
a.a = strings.Join(append(tokens[1:], tokens[0]), "|")
|
||||||
|
a.c++
|
||||||
|
}
|
||||||
|
|
||||||
if t.previewOpts.hidden {
|
if t.previewOpts.hidden {
|
||||||
togglePreview(false)
|
togglePreview(false)
|
||||||
@ -2761,7 +2773,7 @@ func (t *Terminal) Loop() {
|
|||||||
if t.jumping == jumpDisabled {
|
if t.jumping == jumpDisabled {
|
||||||
actions := t.keymap[event.Comparable()]
|
actions := t.keymap[event.Comparable()]
|
||||||
if len(actions) == 0 && event.Type == tui.Rune {
|
if len(actions) == 0 && event.Type == tui.Rune {
|
||||||
doAction(action{t: actRune})
|
doAction(&action{t: actRune})
|
||||||
} else if !doActions(actions) {
|
} else if !doActions(actions) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -2191,6 +2191,23 @@ class TestGoFZF < TestBase
|
|||||||
assert_equal ' 3', lines[1]
|
assert_equal ' 3', lines[1]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_change_preview_window_rotate
|
||||||
|
tmux.send_keys "seq 100 | #{FZF} --preview-window left,border-none --preview 'echo hello' --bind '" \
|
||||||
|
"a:change-preview-window(right|down|up|hidden|)'", :Enter
|
||||||
|
3.times do
|
||||||
|
tmux.until { |lines| lines[0].start_with?('hello') }
|
||||||
|
tmux.send_keys 'a'
|
||||||
|
tmux.until { |lines| lines[0].end_with?('hello') }
|
||||||
|
tmux.send_keys 'a'
|
||||||
|
tmux.until { |lines| lines[-1].start_with?('hello') }
|
||||||
|
tmux.send_keys 'a'
|
||||||
|
tmux.until { |lines| assert_equal 'hello', lines[0] }
|
||||||
|
tmux.send_keys 'a'
|
||||||
|
tmux.until { |lines| refute_includes lines[0], 'hello' }
|
||||||
|
tmux.send_keys 'a'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module TestShell
|
module TestShell
|
||||||
|
Loading…
x
Reference in New Issue
Block a user