summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2023-01-07 11:21:52 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2023-01-07 15:12:31 +0900
commit23d8b78ce12a6d9182a3e576de96123eb4ad176d (patch)
tree755778678bf737bbe08a84c43fce2cb9d604bbc8
parent3b2244077d3e7d299943077b33e0564ffcd1f384 (diff)
Allow toggling of alternative preview window layout that is hidden
Fix #3113
-rw-r--r--src/options.go8
-rw-r--r--src/terminal.go138
-rwxr-xr-xtest/test_go.rb24
3 files changed, 103 insertions, 67 deletions
diff --git a/src/options.go b/src/options.go
index e4a6aa39..350ab94d 100644
--- a/src/options.go
+++ b/src/options.go
@@ -207,6 +207,14 @@ type previewOpts struct {
alternative *previewOpts
}
+func (o *previewOpts) Visible() bool {
+ return o.size.size > 0 || o.alternative != nil && o.alternative.size.size > 0
+}
+
+func (o *previewOpts) Toggle() {
+ o.hidden = !o.hidden
+}
+
func parseLabelPosition(opts *labelOpts, arg string) {
opts.column = 0
opts.bottom = false
diff --git a/src/terminal.go b/src/terminal.go
index 28823675..53175d96 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -195,6 +195,7 @@ type Terminal struct {
reqBox *util.EventBox
initialPreviewOpts previewOpts
previewOpts previewOpts
+ activePreviewOpts *previewOpts
previewer previewer
previewed previewed
previewBox *util.EventBox
@@ -241,7 +242,6 @@ const (
reqJump
reqRefresh
reqReinit
- reqRedraw
reqFullRedraw
reqClose
reqPrintQuery
@@ -494,7 +494,6 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
delay = initialDelay
}
var previewBox *util.EventBox
- showPreviewWindow := len(opts.Preview.command) > 0 && !opts.Preview.hidden
// We need to start previewer if HTTP server is enabled even when --preview option is not specified
if len(opts.Preview.command) > 0 || hasPreviewAction(opts) || opts.ListenPort > 0 {
previewBox = util.NewEventBox()
@@ -602,7 +601,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
reqBox: util.NewEventBox(),
initialPreviewOpts: opts.Preview,
previewOpts: opts.Preview,
- previewer: previewer{0, []string{}, 0, showPreviewWindow, false, true, false, "", []bool{}},
+ previewer: previewer{0, []string{}, 0, len(opts.Preview.command) > 0, false, true, false, "", []bool{}},
previewed: previewed{0, 0, 0, false},
previewBox: previewBox,
eventBox: eventBox,
@@ -985,7 +984,7 @@ func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
if t.noInfoLine() {
minAreaHeight -= 1
}
- if t.isPreviewVisible() {
+ if t.mayNeedPreviewWindow() {
minPreviewHeight := 1 + borderLines(t.previewOpts.border)
minPreviewWidth := 5
switch t.previewOpts.position {
@@ -1003,7 +1002,7 @@ func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
return screenWidth, screenHeight, marginInt, paddingInt
}
-func (t *Terminal) resizeWindows() {
+func (t *Terminal) resizeWindows(forcePreview bool) {
screenWidth, screenHeight, marginInt, paddingInt := t.adjustMarginAndPadding()
width := screenWidth - marginInt[1] - marginInt[3]
height := screenHeight - marginInt[0] - marginInt[2]
@@ -1067,9 +1066,13 @@ func (t *Terminal) resizeWindows() {
// Set up preview window
noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode)
- if t.isPreviewVisible() {
- var resizePreviewWindows func(previewOpts previewOpts)
- resizePreviewWindows = func(previewOpts previewOpts) {
+ if t.mayNeedPreviewWindow() {
+ var resizePreviewWindows func(previewOpts *previewOpts)
+ resizePreviewWindows = func(previewOpts *previewOpts) {
+ t.activePreviewOpts = previewOpts
+ if previewOpts.size.size == 0 {
+ return
+ }
hasThreshold := previewOpts.threshold > 0 && previewOpts.alternative != nil
createPreviewWindow := func(y int, x int, w int, h int) {
pwidth := w
@@ -1124,11 +1127,21 @@ func (t *Terminal) resizeWindows() {
case posUp, posDown:
pheight := calculateSize(height, previewOpts.size, minHeight, minPreviewHeight, verticalPad)
if hasThreshold && pheight < previewOpts.threshold {
+ t.activePreviewOpts = previewOpts.alternative
+ if forcePreview {
+ previewOpts.alternative.hidden = false
+ }
if !previewOpts.alternative.hidden {
- resizePreviewWindows(*previewOpts.alternative)
+ resizePreviewWindows(previewOpts.alternative)
}
return
}
+ if forcePreview {
+ previewOpts.hidden = false
+ }
+ if previewOpts.hidden {
+ return
+ }
// Put scrollbar closer to the right border for consistent look
if t.borderShape.HasRight() {
width++
@@ -1145,11 +1158,21 @@ func (t *Terminal) resizeWindows() {
case posLeft, posRight:
pwidth := calculateSize(width, previewOpts.size, minWidth, 5, 4)
if hasThreshold && pwidth < previewOpts.threshold {
+ t.activePreviewOpts = previewOpts.alternative
+ if forcePreview {
+ previewOpts.alternative.hidden = false
+ }
if !previewOpts.alternative.hidden {
- resizePreviewWindows(*previewOpts.alternative)
+ resizePreviewWindows(previewOpts.alternative)
}
return
}
+ if forcePreview {
+ previewOpts.hidden = false
+ }
+ if previewOpts.hidden {
+ return
+ }
if previewOpts.position == posLeft {
// Put scrollbar closer to the right border for consistent look
if t.borderShape.HasRight() {
@@ -1171,7 +1194,7 @@ func (t *Terminal) resizeWindows() {
}
}
}
- resizePreviewWindows(t.previewOpts)
+ resizePreviewWindows(&t.previewOpts)
}
// Without preview window
@@ -1857,7 +1880,7 @@ func (t *Terminal) processTabs(runes []rune, prefixWidth int) (string, int) {
}
func (t *Terminal) printAll() {
- t.resizeWindows()
+ t.resizeWindows(false)
t.printList()
t.printPrompt()
t.printInfo()
@@ -2142,10 +2165,8 @@ func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, pr
})
}
-func (t *Terminal) redraw(clear bool) {
- if clear {
- t.tui.Clear()
- }
+func (t *Terminal) redraw() {
+ t.tui.Clear()
t.tui.Refresh()
t.printAll()
}
@@ -2168,7 +2189,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
t.tui.Pause(true)
cmd.Run()
t.tui.Resume(true, false)
- t.redraw(true)
+ t.redraw()
t.refresh()
} else {
t.tui.Pause(false)
@@ -2193,16 +2214,17 @@ func (t *Terminal) hasPreviewer() bool {
return t.previewBox != nil
}
-func (t *Terminal) isPreviewEnabled() bool {
- return t.hasPreviewer() && t.previewer.enabled
+func (t *Terminal) mayNeedPreviewWindow() bool {
+ return t.hasPreviewer() && t.previewer.enabled && t.previewOpts.Visible()
}
-func (t *Terminal) isPreviewVisible() bool {
- return t.isPreviewEnabled() && t.previewOpts.size.size > 0
+// Check if previewer is currently in action (invisible previewer with size 0 or visible previewer)
+func (t *Terminal) isPreviewEnabled() bool {
+ return t.hasPreviewer() && t.previewer.enabled && (!t.previewOpts.Visible() || t.pwindow != nil)
}
func (t *Terminal) hasPreviewWindow() bool {
- return t.pwindow != nil && t.isPreviewEnabled()
+ return t.pwindow != nil
}
func (t *Terminal) currentItem() *Item {
@@ -2306,7 +2328,7 @@ func (t *Terminal) Loop() {
pad := fitpad.pad
t.tui.Resize(func(termHeight int) int {
contentHeight := fit + t.extraLines()
- if t.hasPreviewer() {
+ if t.mayNeedPreviewWindow() {
if t.previewOpts.aboveOrBelow() {
if t.previewOpts.size.percent {
newContentHeight := int(float64(contentHeight) * 100. / (100. - t.previewOpts.size.size))
@@ -2354,7 +2376,7 @@ func (t *Terminal) Loop() {
t.mutex.Lock()
t.initFunc()
- t.resizeWindows()
+ t.resizeWindows(false)
t.printPrompt()
t.printInfo()
t.printHeader()
@@ -2598,11 +2620,9 @@ func (t *Terminal) Loop() {
t.suppress = false
case reqReinit:
t.tui.Resume(t.fullscreen, t.sigstop)
- t.redraw(true)
- case reqRedraw:
- t.redraw(false)
+ t.redraw()
case reqFullRedraw:
- t.redraw(true)
+ t.redraw()
case reqClose:
exit(func() int {
if t.output() {
@@ -2701,15 +2721,9 @@ func (t *Terminal) Loop() {
}
}
}
- togglePreview := func(enabled bool) bool {
- if t.previewer.enabled != enabled {
- t.previewer.enabled = enabled
- // We need to immediately update t.pwindow so we don't use reqRedraw
- t.resizeWindows()
- req(reqPrompt, reqList, reqInfo, reqHeader)
- return true
- }
- return false
+ updatePreviewWindow := func(forcePreview bool) {
+ t.resizeWindows(forcePreview)
+ req(reqPrompt, reqList, reqInfo, reqHeader)
}
toggle := func() bool {
current := t.currentItem()
@@ -2773,8 +2787,13 @@ func (t *Terminal) Loop() {
return false
case actTogglePreview:
if t.hasPreviewer() {
- togglePreview(!t.previewer.enabled)
- if t.previewer.enabled {
+ if t.activePreviewOpts != nil {
+ t.activePreviewOpts.Toggle()
+ } else if !t.previewOpts.Visible() {
+ t.previewer.enabled = !t.previewer.enabled
+ }
+ updatePreviewWindow(false)
+ if t.isPreviewEnabled() {
valid, list := t.buildPlusList(t.previewOpts.command, false, false)
if valid {
t.cancelPreview()
@@ -2848,7 +2867,8 @@ func (t *Terminal) Loop() {
t.prompt, t.promptLen = t.parsePrompt(a.a)
req(reqPrompt)
case actPreview:
- togglePreview(true)
+ t.previewer.enabled = true
+ updatePreviewWindow(true)
refreshPreview(a.a)
case actRefreshPreview:
refreshPreview(t.previewOpts.command)
@@ -2910,8 +2930,9 @@ func (t *Terminal) Loop() {
req(reqList, reqInfo)
}
case actClose:
- if t.isPreviewEnabled() {
- togglePreview(false)
+ if t.hasPreviewWindow() {
+ t.activePreviewOpts.Toggle()
+ updatePreviewWindow(false)
} else {
req(reqQuit)
}
@@ -3258,7 +3279,8 @@ func (t *Terminal) Loop() {
}
case actChangePreview:
if t.previewOpts.command != a.a {
- togglePreview(len(a.a) > 0)
+ t.previewer.enabled = len(a.a) > 0
+ updatePreviewWindow(false)
t.previewOpts.command = a.a
refreshPreview(t.previewOpts.command)
}
@@ -3275,25 +3297,23 @@ func (t *Terminal) Loop() {
a.a = strings.Join(append(tokens[1:], tokens[0]), "|")
}
- if t.previewOpts.hidden {
- togglePreview(false)
- } else {
- // Full redraw
- if !currentPreviewOpts.sameLayout(t.previewOpts) {
- if togglePreview(true) {
- refreshPreview(t.previewOpts.command)
- } else {
- req(reqRedraw)
- }
- } else if !currentPreviewOpts.sameContentLayout(t.previewOpts) {
- t.previewed.version = 0
+ // Full redraw
+ if !currentPreviewOpts.sameLayout(t.previewOpts) {
+ wasHidden := t.pwindow == nil
+ updatePreviewWindow(false)
+ if wasHidden && t.pwindow != nil {
+ refreshPreview(t.previewOpts.command)
+ } else {
req(reqPreviewRefresh)
}
+ } else if !currentPreviewOpts.sameContentLayout(t.previewOpts) {
+ t.previewed.version = 0
+ req(reqPreviewRefresh)
+ }
- // Adjust scroll offset
- if t.hasPreviewWindow() && currentPreviewOpts.scroll != t.previewOpts.scroll {
- scrollPreviewTo(t.evaluateScrollOffset())
- }
+ // Adjust scroll offset
+ if t.hasPreviewWindow() && currentPreviewOpts.scroll != t.previewOpts.scroll {
+ scrollPreviewTo(t.evaluateScrollOffset())
}
case actNextSelected, actPrevSelected:
if len(t.selected) > 0 {
diff --git a/test/test_go.rb b/test/test_go.rb
index 142df094..8031e81e 100755
--- a/test/test_go.rb
+++ b/test/test_go.rb
@@ -1502,7 +1502,7 @@ class TestGoFZF < TestBase
rescue StandardError
nil
end
- tmux.send_keys %(seq 100 | #{FZF} --reverse --preview 'echo {} >> #{tempname}; echo ' --preview-window 0), :Enter
+ tmux.send_keys %(seq 100 | #{FZF} --reverse --preview 'echo {} >> #{tempname}; echo ' --preview-window 0 --bind space:toggle-preview), :Enter
tmux.until do |lines|
assert_equal 100, lines.item_count
assert_equal ' 100/100', lines[1]
@@ -1512,17 +1512,17 @@ class TestGoFZF < TestBase
assert_path_exists tempname
assert_equal %w[1], File.readlines(tempname, chomp: true)
end
- tmux.send_keys :Down
- tmux.until { |lines| assert_equal '> 2', lines[3] }
+ tmux.send_keys :Space, :Down, :Down
+ tmux.until { |lines| assert_equal '> 3', lines[4] }
wait do
assert_path_exists tempname
- assert_equal %w[1 2], File.readlines(tempname, chomp: true)
+ assert_equal %w[1], File.readlines(tempname, chomp: true)
end
- tmux.send_keys :Down
- tmux.until { |lines| assert_equal '> 3', lines[4] }
+ tmux.send_keys :Space, :Down
+ tmux.until { |lines| assert_equal '> 4', lines[5] }
wait do
assert_path_exists tempname
- assert_equal %w[1 2 3], File.readlines(tempname, chomp: true)
+ assert_equal %w[1 3 4], File.readlines(tempname, chomp: true)
end
end
@@ -2405,7 +2405,7 @@ class TestGoFZF < TestBase
end
def test_start_event
- tmux.send_keys 'seq 100 | fzf --multi --sync --preview-window border-none --bind "start:select-all+last+preview(echo welcome)"', :Enter
+ tmux.send_keys 'seq 100 | fzf --multi --sync --preview-window hidden:border-none --bind "start:select-all+last+preview(echo welcome)"', :Enter
tmux.until do |lines|
assert_match(/>100.*welcome/, lines[0])
assert_includes(lines[-2], '100/100 (100)')
@@ -2495,6 +2495,14 @@ class TestGoFZF < TestBase
tmux.until { |lines| assert_equal 100, lines.item_count }
tmux.until { |lines| assert_equal 'hundred> yo', lines[-1] }
end
+
+ def test_toggle_alternative_preview_window
+ tmux.send_keys "seq 10 | #{FZF} --bind space:toggle-preview --preview-window '<100000(hidden,up,border-none)' --preview 'echo /{}/{}/'", :Enter
+ tmux.until { |lines| assert_equal 10, lines.item_count }
+ tmux.until { |lines| refute_includes lines, '/1/1/' }
+ tmux.send_keys :Space
+ tmux.until { |lines| assert_includes lines, '/1/1/' }
+ end
end
module TestShell