Fix --scroll-off for multi-line mode

This commit is contained in:
Junegunn Choi 2024-05-24 19:23:36 +09:00
parent 996abb2831
commit 7a97532547
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627

View File

@ -4489,26 +4489,26 @@ func (t *Terminal) Loop() error {
func (t *Terminal) constrain() { func (t *Terminal) constrain() {
// count of items to display allowed by filtering // count of items to display allowed by filtering
count := t.merger.Length() count := t.merger.Length()
maxItems := t.maxItems() maxLines := t.maxItems()
// May need to try again after adjusting the offset // May need to try again after adjusting the offset
for tries := 0; tries < maxItems; tries++ { for tries := 0; tries < maxLines; tries++ {
height := maxItems numItems := maxLines
// How many items can be fit on screen including the current item? // How many items can be fit on screen including the current item?
if t.multiLine && t.merger.Length() > 0 { if t.multiLine && t.merger.Length() > 0 {
actualHeight := 0 numItemsFound := 0
linesSum := 0 linesSum := 0
add := func(i int) bool { add := func(i int) bool {
lines := t.merger.Get(i).item.text.NumLines(height - linesSum) lines := t.merger.Get(i).item.text.NumLines(numItems - linesSum)
linesSum += lines linesSum += lines
if linesSum >= height { if linesSum >= numItems {
if actualHeight == 0 { if numItemsFound == 0 {
actualHeight = 1 numItemsFound = 1
} }
return false return false
} }
actualHeight++ numItemsFound++
return true return true
} }
@ -4519,7 +4519,7 @@ func (t *Terminal) constrain() {
} }
// We can possibly fit more items "before" the offset on screen // We can possibly fit more items "before" the offset on screen
if linesSum < height { if linesSum < numItems {
for i := t.offset - 1; i >= 0; i-- { for i := t.offset - 1; i >= 0; i-- {
if !add(i) { if !add(i) {
break break
@ -4527,27 +4527,50 @@ func (t *Terminal) constrain() {
} }
} }
height = actualHeight numItems = numItemsFound
} }
t.cy = util.Constrain(t.cy, 0, util.Max(0, count-1)) t.cy = util.Constrain(t.cy, 0, util.Max(0, count-1))
minOffset := util.Max(t.cy-height+1, 0) minOffset := util.Max(t.cy-numItems+1, 0)
maxOffset := util.Max(util.Min(count-height, t.cy), 0) maxOffset := util.Max(util.Min(count-numItems, t.cy), 0)
prevOffset := t.offset prevOffset := t.offset
t.offset = util.Constrain(t.offset, minOffset, maxOffset) t.offset = util.Constrain(t.offset, minOffset, maxOffset)
if t.scrollOff > 0 { if t.scrollOff > 0 {
scrollOff := util.Min(height/2, t.scrollOff) 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 { for {
prevOffset := t.offset prevOffset := newOffset
if t.cy-t.offset < scrollOff { numItems := t.merger.Length()
t.offset = util.Max(minOffset, t.offset-1) itemLines := 1
if t.multiLine && t.cy < numItems {
itemLines = t.merger.Get(t.cy).item.text.NumLines(maxLines)
} }
if t.cy-t.offset >= height-scrollOff { linesBefore := t.cy - newOffset
t.offset = util.Min(maxOffset, t.offset+1) if t.multiLine {
linesBefore = 0
for i := newOffset; i < t.cy && i < numItems; i++ {
linesBefore += t.merger.Get(i).item.text.NumLines(maxLines - linesBefore - itemLines)
} }
if t.offset == prevOffset { }
linesAfter := maxLines - (linesBefore + itemLines)
// Stuck in the middle, nothing to do
if linesBefore < scrollOff && linesAfter < scrollOff {
break 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 { if t.offset == prevOffset {