mirror of
https://github.com/bloeys/nterm.git
synced 2025-12-29 14:38:19 +00:00
Scroll by lines that respects ring buf+iterator prevN/nextN+it.Len
This commit is contained in:
59
main.go
59
main.go
@ -284,11 +284,9 @@ func (p *program) MainUpdate() {
|
||||
var newPosNewLines int64
|
||||
w, _ := p.GridSize()
|
||||
if mouseWheelYNorm < 0 {
|
||||
// @TODO This is wrong because it deals with data as a normal array while its a ring buffer
|
||||
|
||||
newPosNewLines, _ = find_n_lines_index(p.textBuf.Data, p.scrollPos, p.scrollSpd*mouseWheelYNorm-1, int64(w))
|
||||
newPosNewLines, _ = find_n_lines_index_iterator(p.textBuf.Iterator(), p.scrollPos, p.scrollSpd*mouseWheelYNorm-1, int64(w))
|
||||
} else {
|
||||
newPosNewLines, _ = find_n_lines_index(p.textBuf.Data, p.scrollPos, p.scrollSpd*mouseWheelYNorm, int64(w))
|
||||
newPosNewLines, _ = find_n_lines_index_iterator(p.textBuf.Iterator(), p.scrollPos, p.scrollSpd*mouseWheelYNorm, int64(w))
|
||||
}
|
||||
|
||||
p.scrollPos = clamp(newPosNewLines, 0, p.textBuf.Len)
|
||||
@ -831,22 +829,36 @@ func FindNthOrLastIndex[T comparable](arr []T, x T, startIndex, n int64) (lastIn
|
||||
//
|
||||
// Note: When moving backwards from the start of the line, the first char will be a new line (e.g. \n), so the first counted line is not a full line
|
||||
// but only a single rune. So in most cases to get '-n' lines backwards you should request '-n-1' lines.
|
||||
func find_n_lines_index(arr []byte, startIndex, n, charsPerLine int64) (lastIndex, lastSize int64) {
|
||||
func find_n_lines_index_iterator(it ring.Iterator[byte], startIndex, n, charsPerLine int64) (newIndex, newSize int64) {
|
||||
|
||||
// If nothing changes (e.g. already at end of iterator) then we will stay at the same place
|
||||
|
||||
done := false
|
||||
read := 0
|
||||
bytesToKeep := 0
|
||||
buf := make([]byte, 4)
|
||||
it.GotoIndex(startIndex)
|
||||
|
||||
lastIndex = -1
|
||||
lastSize = 0
|
||||
if n >= 0 {
|
||||
// @Note we should ignore zero width glyphs
|
||||
// @Note is this better in glyphs package?
|
||||
bytesSeen := int64(0)
|
||||
arrSize := int64(len(arr))
|
||||
charsSeenThisLine := int64(0)
|
||||
for startIndex+bytesSeen < arrSize {
|
||||
|
||||
r, size := utf8.DecodeRune(arr[startIndex+bytesSeen:])
|
||||
if n >= 0 {
|
||||
|
||||
// If nothing changes (e.g. already at end of iterator) then we will stay at the same place
|
||||
newIndex = startIndex
|
||||
|
||||
for !done || bytesToKeep > 0 {
|
||||
|
||||
read, done = it.NextN(buf[bytesToKeep:], 4)
|
||||
|
||||
r, size := utf8.DecodeRune(buf[:bytesToKeep+read])
|
||||
if r == utf8.RuneError {
|
||||
break
|
||||
}
|
||||
bytesToKeep += read - size
|
||||
copy(buf, buf[size:size+bytesToKeep])
|
||||
|
||||
charsSeenThisLine++
|
||||
bytesSeen += int64(size)
|
||||
@ -855,8 +867,8 @@ func find_n_lines_index(arr []byte, startIndex, n, charsPerLine int64) (lastInde
|
||||
if charsSeenThisLine == charsPerLine || r == '\n' {
|
||||
|
||||
charsSeenThisLine = 0
|
||||
lastSize = int64(size)
|
||||
lastIndex = startIndex + bytesSeen
|
||||
newSize = int64(size)
|
||||
newIndex = startIndex + bytesSeen
|
||||
|
||||
n--
|
||||
if n <= 0 {
|
||||
@ -867,14 +879,16 @@ func find_n_lines_index(arr []byte, startIndex, n, charsPerLine int64) (lastInde
|
||||
|
||||
} else {
|
||||
|
||||
bytesSeen := int64(0)
|
||||
charsSeenThisLine := int64(0)
|
||||
for startIndex-bytesSeen > 0 {
|
||||
for !done || bytesToKeep > 0 {
|
||||
|
||||
r, size := utf8.DecodeLastRune(arr[:startIndex-bytesSeen+1])
|
||||
read, done = it.PrevN(buf[bytesToKeep:], 4)
|
||||
|
||||
r, size := utf8.DecodeRune(buf[:bytesToKeep+read])
|
||||
if r == utf8.RuneError {
|
||||
break
|
||||
}
|
||||
bytesToKeep += read - size
|
||||
copy(buf, buf[size:size+bytesToKeep])
|
||||
|
||||
charsSeenThisLine++
|
||||
bytesSeen += int64(size)
|
||||
@ -883,8 +897,8 @@ func find_n_lines_index(arr []byte, startIndex, n, charsPerLine int64) (lastInde
|
||||
if charsSeenThisLine == charsPerLine || r == '\n' {
|
||||
|
||||
charsSeenThisLine = 0
|
||||
lastSize = int64(size)
|
||||
lastIndex = startIndex - bytesSeen + 1 + lastSize
|
||||
newSize = int64(size)
|
||||
newIndex = startIndex - bytesSeen + newSize
|
||||
|
||||
n++
|
||||
if n >= 0 {
|
||||
@ -893,11 +907,12 @@ func find_n_lines_index(arr []byte, startIndex, n, charsPerLine int64) (lastInde
|
||||
}
|
||||
}
|
||||
|
||||
// Handle reaching beginning before finding nth line
|
||||
// If we reached beginning of buffer before finding a new line then newIndex is zero
|
||||
if startIndex-bytesSeen == 0 {
|
||||
return 0, 0
|
||||
newIndex = 0
|
||||
newSize = 0
|
||||
}
|
||||
}
|
||||
|
||||
return lastIndex, lastSize
|
||||
return newIndex, newSize
|
||||
}
|
||||
|
||||
61
ring/ring.go
61
ring/ring.go
@ -143,6 +143,10 @@ type Iterator[T any] struct {
|
||||
InV1 bool
|
||||
}
|
||||
|
||||
func (it *Iterator[T]) Len() int64 {
|
||||
return int64(len(it.V1) + len(it.V2))
|
||||
}
|
||||
|
||||
// Next returns the value at Iterator.Curr and done=false
|
||||
//
|
||||
// If there are no more values to return the default value is returned for v and done=true
|
||||
@ -198,6 +202,63 @@ func (it *Iterator[T]) Prev() (v T, done bool) {
|
||||
return v, false
|
||||
}
|
||||
|
||||
// NextN calls Next() up to n times and places the result in the passed buffer.
|
||||
// 'read' is the actual number of elements put in the buffer.
|
||||
// We might not be able to put 'n' elements because the buffer is too small or because there aren't enough remaining elements
|
||||
func (it *Iterator[T]) NextN(buf []T, n int) (read int, done bool) {
|
||||
|
||||
if n > len(buf) {
|
||||
n = len(buf)
|
||||
}
|
||||
|
||||
var v T
|
||||
for v, done = it.Next(); !done; v, done = it.Next() {
|
||||
|
||||
buf[read] = v
|
||||
read++
|
||||
|
||||
// We must break inside the loop not in the 'for' check because
|
||||
// if we check part of the loop and break before done=true we will waste the last
|
||||
// value
|
||||
n--
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return read, done
|
||||
}
|
||||
|
||||
// PrevN calls Prev() up to n times and places the result in the passed buffer in the order they are read from Prev().
|
||||
// That is, the first Prev() call is put into index 0, second Prev() call is in index 1, and so on,
|
||||
// similar to if you were doing a reverse loop on an array.
|
||||
//
|
||||
// 'read' is the actual number of elements put in the buffer.
|
||||
// We might not be able to put 'n' elements because the buffer is too small or because there aren't enough remaining elements
|
||||
func (it *Iterator[T]) PrevN(buf []T, n int) (read int, done bool) {
|
||||
|
||||
if n > len(buf) {
|
||||
n = len(buf)
|
||||
}
|
||||
|
||||
var v T
|
||||
for v, done = it.Prev(); !done; v, done = it.Prev() {
|
||||
|
||||
buf[read] = v
|
||||
read++
|
||||
|
||||
// We must break inside the loop not in the 'for' check because
|
||||
// if we check part of the loop and break before done=true we will waste the last
|
||||
// value
|
||||
n--
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return read, done
|
||||
}
|
||||
|
||||
// GotoStart adjusts the iterator such that the following Next() call returns the value at index=0
|
||||
// and the next Prev() call returns done=true
|
||||
func (it *Iterator[T]) GotoStart() {
|
||||
|
||||
@ -176,6 +176,28 @@ func TestIterator(t *testing.T) {
|
||||
got = append(got, v)
|
||||
}
|
||||
CheckArr(t, ans, got)
|
||||
|
||||
// NextN
|
||||
got = make([]int, 2)
|
||||
ans = []int{3, 4}
|
||||
it.GotoStart()
|
||||
it.NextN(got, 2)
|
||||
CheckArr(t, ans, got)
|
||||
|
||||
ans = []int{5, 6}
|
||||
it.NextN(got, 2)
|
||||
CheckArr(t, ans, got)
|
||||
|
||||
// PrevN
|
||||
got = make([]int, 2)
|
||||
ans = []int{6, 5}
|
||||
it.GotoEnd()
|
||||
it.PrevN(got, 2)
|
||||
CheckArr(t, ans, got)
|
||||
|
||||
ans = []int{4, 3}
|
||||
it.PrevN(got, 2)
|
||||
CheckArr(t, ans, got)
|
||||
}
|
||||
|
||||
func Check[T comparable](t *testing.T, expected, got T) {
|
||||
|
||||
Reference in New Issue
Block a user