summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2023-01-12 23:12:26 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2023-01-12 23:18:41 +0900
commite97e925efb0470dfafedc034f38eb37f70848de2 (patch)
treed94690268549fc2d90af2ff2584c392b9df79c4b
parent0f032235cf8f2d788a76c412f66012b49cdb759c (diff)
Resume preview following if the user scrolls the window to the bottom
-rw-r--r--CHANGELOG.md5
-rw-r--r--src/terminal.go51
-rwxr-xr-xtest/test_go.rb65
3 files changed, 111 insertions, 10 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 718c9326..04b276f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -109,6 +109,11 @@ CHANGELOG
color for `border`. Same holds true for `scrollbar`. This is to reduce
the number of configuration items required to achieve a consistent color
scheme.
+ - If `follow` flag is specified in `--preview-window` option, fzf will
+ automatically scroll to the bottom of the streaming preview output. But
+ when the user manually scrolls the window, the following stops. With
+ this version, fzf will resume following if the user scrolls the window
+ to the bottom.
- Added color name `preview-label` for `--preview-label` (defaults to `label`
for `--border-label`)
- Minor bug fixes and improvements
diff --git a/src/terminal.go b/src/terminal.go
index 80c9e1a1..8cd9a143 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -68,6 +68,38 @@ const (
jumpAcceptEnabled
)
+type resumableState int
+
+const (
+ disabledState resumableState = iota
+ pausedState
+ enabledState
+)
+
+func (s resumableState) Enabled() bool {
+ return s == enabledState
+}
+
+func (s *resumableState) Force(flag bool) {
+ if flag {
+ *s = enabledState
+ } else {
+ *s = disabledState
+ }
+}
+
+func (s *resumableState) Set(flag bool) {
+ if *s == disabledState {
+ return
+ }
+
+ if flag {
+ *s = enabledState
+ } else {
+ *s = pausedState
+ }
+}
+
type previewer struct {
version int64
lines []string
@@ -75,7 +107,7 @@ type previewer struct {
enabled bool
scrollable bool
final bool
- following bool
+ following resumableState
spinner string
bar []bool
}
@@ -601,7 +633,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
reqBox: util.NewEventBox(),
initialPreviewOpts: opts.Preview,
previewOpts: opts.Preview,
- previewer: previewer{0, []string{}, 0, len(opts.Preview.command) > 0, false, true, false, "", []bool{}},
+ previewer: previewer{0, []string{}, 0, len(opts.Preview.command) > 0, false, true, disabledState, "", []bool{}},
previewed: previewed{0, 0, 0, false},
previewBox: previewBox,
eventBox: eventBox,
@@ -2639,12 +2671,15 @@ func (t *Terminal) Loop() {
result := value.(previewResult)
if t.previewer.version != result.version {
t.previewer.version = result.version
- t.previewer.following = t.previewOpts.follow
+ t.previewer.following.Force(t.previewOpts.follow)
+ if t.previewer.following.Enabled() {
+ t.previewer.offset = 0
+ }
}
t.previewer.lines = result.lines
t.previewer.spinner = result.spinner
- if t.previewer.following {
- t.previewer.offset = len(t.previewer.lines) - t.pwindow.Height()
+ if t.previewer.following.Enabled() {
+ t.previewer.offset = util.Max(t.previewer.offset, len(t.previewer.lines)-(t.pwindow.Height()-t.previewOpts.headerLines))
} else if result.offset >= 0 {
t.previewer.offset = util.Constrain(result.offset, t.previewOpts.headerLines, len(t.previewer.lines)-1)
}
@@ -2741,7 +2776,6 @@ func (t *Terminal) Loop() {
if !t.previewer.scrollable {
return
}
- t.previewer.following = false
numLines := len(t.previewer.lines)
headerLines := t.previewOpts.headerLines
if t.previewOpts.cycle {
@@ -2751,6 +2785,7 @@ func (t *Terminal) Loop() {
newOffset = util.Constrain(newOffset, headerLines, numLines-1)
if t.previewer.offset != newOffset {
t.previewer.offset = newOffset
+ t.previewer.following.Set(t.previewer.offset >= numLines-(t.pwindow.Height()-headerLines))
req(reqPreviewRefresh)
}
}
@@ -3177,7 +3212,7 @@ func (t *Terminal) Loop() {
y = util.Constrain(y, 0, effectiveHeight-barLength)
// offset = (total - maxItems) * barStart / (maxItems - barLength)
t.previewer.offset = headerLines + int(math.Ceil(float64(y)*float64(numLines-effectiveHeight)/float64(effectiveHeight-barLength)))
- t.previewer.following = false
+ t.previewer.following.Set(t.previewer.offset >= numLines-effectiveHeight)
req(reqPreviewRefresh)
}
break
@@ -3322,7 +3357,7 @@ func (t *Terminal) Loop() {
}
// Resume following
- t.previewer.following = t.previewOpts.follow
+ t.previewer.following.Force(t.previewOpts.follow)
case actNextSelected, actPrevSelected:
if len(t.selected) > 0 {
total := t.merger.Length()
diff --git a/test/test_go.rb b/test/test_go.rb
index 8031e81e..60322e15 100755
--- a/test/test_go.rb
+++ b/test/test_go.rb
@@ -1943,8 +1943,69 @@ class TestGoFZF < TestBase
end
def test_preview_window_follow
- tmux.send_keys "#{FZF} --preview 'seq 1000 | nl' --preview-window down:noborder:follow", :Enter
- tmux.until { |lines| assert_equal '1000 1000', lines[-1].strip }
+ file = Tempfile.new('fzf-follow')
+ file.sync = true
+
+ tmux.send_keys %[seq 100 | #{FZF} --preview 'tail -f "#{file.path}"' --preview-window follow --bind 'up:preview-up,down:preview-down,space:change-preview-window:follow|nofollow' --preview-window '~3'], :Enter
+ tmux.until { |lines| lines.item_count == 100 }
+
+ # Write to the temporary file, and check if the preview window is showing
+ # the last line of the file
+ 3.times { file.puts _1 } # header lines
+ 1000.times { file.puts _1 }
+ tmux.until { |lines| assert_includes lines[1], '/1003' }
+ tmux.until { |lines| assert_includes lines[-2], '999' }
+
+ # Scroll the preview window and fzf should stop following the file content
+ tmux.send_keys :Up
+ tmux.until { |lines| assert_includes lines[-2], '998' }
+ file.puts 'foo', 'bar'
+ tmux.until do |lines|
+ assert_includes lines[1], '/1005'
+ assert_includes lines[-2], '998'
+ end
+
+ # Scroll back to the bottom and fzf should start following the file again
+ %w[999 foo bar].each do |item|
+ wait do
+ tmux.send_keys :Down
+ tmux.until { |lines| assert_includes lines[-2], item }
+ end
+ end
+ file.puts 'baz'
+ tmux.until do |lines|
+ assert_includes lines[1], '/1006'
+ assert_includes lines[-2], 'baz'
+ end
+
+ # Scroll upwards to stop following
+ tmux.send_keys :Up
+ wait { |line| assert_includes lines[-2], 'bar' }
+ file.puts 'aaa'
+ tmux.until do |lines|
+ assert_includes lines[1], '/1007'
+ assert_includes lines[-2], 'bar'
+ end
+
+ # Manually enable following
+ tmux.send_keys :Space
+ tmux.until { |lines| assert_includes lines[-2], 'aaa' }
+ file.puts 'bbb'
+ tmux.until do |lines|
+ assert_includes lines[1], '/1008'
+ assert_includes lines[-2], 'bbb'
+ end
+
+ # Disable following
+ tmux.send_keys :Space
+ file.puts 'ccc', 'ddd'
+ tmux.until do |lines|
+ assert_includes lines[1], '/1010'
+ assert_includes lines[-2], 'bbb'
+ end
+ rescue
+ file.close
+ file.unlink
end
def test_toggle_preview_wrap