Scroll by lines that respects ring buf+iterator prevN/nextN+it.Len

This commit is contained in:
bloeys
2022-07-20 02:18:36 +04:00
parent b4f06bda54
commit 90b0bc0ae9
3 changed files with 123 additions and 25 deletions

65
main.go
View File

@ -284,11 +284,9 @@ func (p *program) MainUpdate() {
var newPosNewLines int64 var newPosNewLines int64
w, _ := p.GridSize() w, _ := p.GridSize()
if mouseWheelYNorm < 0 { 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_iterator(p.textBuf.Iterator(), p.scrollPos, p.scrollSpd*mouseWheelYNorm-1, int64(w))
newPosNewLines, _ = find_n_lines_index(p.textBuf.Data, p.scrollPos, p.scrollSpd*mouseWheelYNorm-1, int64(w))
} else { } 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) 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 // 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. // 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)
// @Note we should ignore zero width glyphs
// @Note is this better in glyphs package?
bytesSeen := int64(0)
charsSeenThisLine := int64(0)
lastIndex = -1
lastSize = 0
if n >= 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 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 { if r == utf8.RuneError {
break break
} }
bytesToKeep += read - size
copy(buf, buf[size:size+bytesToKeep])
charsSeenThisLine++ charsSeenThisLine++
bytesSeen += int64(size) bytesSeen += int64(size)
@ -855,8 +867,8 @@ func find_n_lines_index(arr []byte, startIndex, n, charsPerLine int64) (lastInde
if charsSeenThisLine == charsPerLine || r == '\n' { if charsSeenThisLine == charsPerLine || r == '\n' {
charsSeenThisLine = 0 charsSeenThisLine = 0
lastSize = int64(size) newSize = int64(size)
lastIndex = startIndex + bytesSeen newIndex = startIndex + bytesSeen
n-- n--
if n <= 0 { if n <= 0 {
@ -867,14 +879,16 @@ func find_n_lines_index(arr []byte, startIndex, n, charsPerLine int64) (lastInde
} else { } else {
bytesSeen := int64(0) for !done || bytesToKeep > 0 {
charsSeenThisLine := int64(0)
for startIndex-bytesSeen > 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 { if r == utf8.RuneError {
break break
} }
bytesToKeep += read - size
copy(buf, buf[size:size+bytesToKeep])
charsSeenThisLine++ charsSeenThisLine++
bytesSeen += int64(size) bytesSeen += int64(size)
@ -883,8 +897,8 @@ func find_n_lines_index(arr []byte, startIndex, n, charsPerLine int64) (lastInde
if charsSeenThisLine == charsPerLine || r == '\n' { if charsSeenThisLine == charsPerLine || r == '\n' {
charsSeenThisLine = 0 charsSeenThisLine = 0
lastSize = int64(size) newSize = int64(size)
lastIndex = startIndex - bytesSeen + 1 + lastSize newIndex = startIndex - bytesSeen + newSize
n++ n++
if n >= 0 { 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 { if startIndex-bytesSeen == 0 {
return 0, 0 newIndex = 0
newSize = 0
} }
} }
return lastIndex, lastSize return newIndex, newSize
} }

View File

@ -143,6 +143,10 @@ type Iterator[T any] struct {
InV1 bool 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 // 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 // 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 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 // GotoStart adjusts the iterator such that the following Next() call returns the value at index=0
// and the next Prev() call returns done=true // and the next Prev() call returns done=true
func (it *Iterator[T]) GotoStart() { func (it *Iterator[T]) GotoStart() {

View File

@ -176,6 +176,28 @@ func TestIterator(t *testing.T) {
got = append(got, v) got = append(got, v)
} }
CheckArr(t, ans, got) 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) { func Check[T comparable](t *testing.T, expected, got T) {