Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

--preview-window wrap breaks preview scrolling in some condition. #4258

Open
5 of 10 tasks
fsc0 opened this issue Feb 19, 2025 · 1 comment
Open
5 of 10 tasks

--preview-window wrap breaks preview scrolling in some condition. #4258

fsc0 opened this issue Feb 19, 2025 · 1 comment
Labels

Comments

@fsc0
Copy link

fsc0 commented Feb 19, 2025

Checklist

  • I have read through the manual page (man fzf)
  • I have searched through the existing issues
  • For bug reports, I have checked if the bug is reproducible in the latest version of fzf

Output of fzf --version

0.60.0 (6fa8295)

OS

  • Linux
  • macOS
  • Windows
  • Etc.

Shell

  • bash
  • zsh
  • fish

Problem / Steps to reproduce

I have a text file named preview-scroll-test.txt

Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
Line 11
Line 12
Line 13
Line 14
Line 15
Line 16
Line 17
Line 18
Line 19
Line 20
Line 21
Line 22
Line 23 ######################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Line 24
Line 25

And I saved below script like fzf-preview-scroll-test.sh

#!/usr/bin/env bash
: | FZF_DEFAULT_OPTS= fzf \
	--preview-window wrap \
	--preview 'cat /tmp/preview-scroll-test.txt' \
	--bind 'up:preview-page-up,down:preview-page-down'

When I execute the script.
Line 24~ is hidden cause of very precise wrapping.
And up and down bindings not working.

Image

Let's remove those X phrase on Line 23.

Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
Line 11
Line 12
Line 13
Line 14
Line 15
Line 16
Line 17
Line 18
Line 19
Line 20
Line 21
Line 22
Line 23 ######################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
Line 24
Line 25

Then execute the script again.
Line 24 shows up but scrolling still not working.

Image

Now restore those X phase and put a single # at the end of Line 23.

Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
Line 11
Line 12
Line 13
Line 14
Line 15
Line 16
Line 17
Line 18
Line 19
Line 20
Line 21
Line 22
Line 23 ######################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#
Line 24
Line 25

Scrolling is back to work, but Line 24 is gone.

1more-result.mp4

As you can see, to reproducing this issue, user need to prepare a precise text file for their terminal setting.
Like increasing Line N and # and X. (Or using fzf's own setting.)

Note. When you adjust the terminal screen size, issue may gone.

EDIT: At the last case, Line 24 wasn't exactly gone, but only can be found with preview-down.

@fsc0 fsc0 changed the title --preview-window wrap broken preview scrolling in some condition. --preview-window wrap breaks preview scrolling in some condition. Feb 19, 2025
@junegunn junegunn added the bug label Feb 19, 2025
junegunn added a commit that referenced this issue Feb 19, 2025
When the last rendered line was wrapped, fzf would incorrectly determine
the scrollability of the window.
@junegunn
Copy link
Owner

junegunn commented Feb 19, 2025

Thanks. 0ba0661 will fix the scrollability problem. Please test it and let me know.

fzf --preview-window wrap --preview 'seq $((FZF_PREVIEW_LINES - 2)); yes | head -$((FZF_PREVIEW_COLUMNS + 1)) | xargs | sed "s/ //g"; echo more'

However, 24 not showing on preview-page-down action is not trivial to fix. When we have wrapped lines, we can't no longer just add or subtract the height of the preview window to the scroll offset. Getting it right requires a lot more code as you can see in constrain() function which is for handling that in the list section.

fzf/src/terminal.go

Lines 6078 to 6178 in 0ba0661

func (t *Terminal) constrain() {
// count of items to display allowed by filtering
count := t.merger.Length()
maxLines := t.maxItems()
// May need to try again after adjusting the offset
t.offset = util.Constrain(t.offset, 0, count)
for tries := 0; tries < maxLines; tries++ {
numItems := maxLines
// How many items can be fit on screen including the current item?
if t.canSpanMultiLines() && t.merger.Length() > 0 {
numItemsFound := 0
linesSum := 0
add := func(i int) bool {
lines, overflow := t.numItemLines(t.merger.Get(i).item, numItems-linesSum)
linesSum += lines
if linesSum >= numItems {
/*
# Should show all 3 items
printf "file1\0file2\0file3\0" | fzf --height=5 --read0 --bind load:last --reverse
# Should not truncate the last item
printf "file\n1\0file\n2\0file\n3\0" | fzf --height=5 --read0 --bind load:last --reverse
*/
if numItemsFound == 0 || !overflow {
numItemsFound++
}
return false
}
numItemsFound++
return true
}
for i := t.offset; i < t.merger.Length(); i++ {
if !add(i) {
break
}
}
// We can possibly fit more items "before" the offset on screen
if linesSum < numItems {
for i := t.offset - 1; i >= 0; i-- {
if !add(i) {
break
}
}
}
numItems = numItemsFound
}
t.cy = util.Constrain(t.cy, 0, util.Max(0, count-1))
minOffset := util.Max(t.cy-numItems+1, 0)
maxOffset := util.Max(util.Min(count-numItems, t.cy), 0)
prevOffset := t.offset
t.offset = util.Constrain(t.offset, minOffset, maxOffset)
if t.scrollOff > 0 {
scrollOff := util.Min(maxLines/2, t.scrollOff)
newOffset := t.offset
// 2-phase adjustment to avoid infinite loop of alternating between moving up and down
for phase := 0; phase < 2; phase++ {
for {
prevOffset := newOffset
numItems := t.merger.Length()
itemLines := 1 + t.gap
if t.canSpanMultiLines() && t.cy < numItems {
itemLines, _ = t.numItemLines(t.merger.Get(t.cy).item, maxLines)
}
linesBefore := t.cy - newOffset
if t.canSpanMultiLines() {
linesBefore = 0
for i := newOffset; i < t.cy && i < numItems; i++ {
lines, _ := t.numItemLines(t.merger.Get(i).item, maxLines-linesBefore-itemLines)
linesBefore += lines
}
}
linesAfter := maxLines - (linesBefore + itemLines)
// Stuck in the middle, nothing to do
if linesBefore < scrollOff && linesAfter < scrollOff {
break
}
if phase == 0 && linesBefore < scrollOff {
newOffset = util.Max(minOffset, newOffset-1)
} else if phase == 1 && linesAfter < scrollOff {
newOffset = util.Min(maxOffset, newOffset+1)
}
if newOffset == prevOffset {
break
}
}
t.offset = newOffset
}
}
if t.offset == prevOffset {
break
}
}
}

So I'm going to leave it as an open issue (tentatively wontfix).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants