mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-07 18:35:34 +00:00
widget: test and document seekPosition
This commit adds a test for the seekPosition helper, a function which can be used to move a combinedPos forward through a body of text until it approaches a position. Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
This commit is contained in:
@@ -1093,6 +1093,8 @@ func (e *Editor) closestPosition(pos combinedPos) combinedPos {
|
|||||||
|
|
||||||
// seekPosition seeks to the position closest to needle, starting at start and returns true.
|
// seekPosition seeks to the position closest to needle, starting at start and returns true.
|
||||||
// If limit is non-zero, seekPosition stops seeks after limit runes and returns false.
|
// If limit is non-zero, seekPosition stops seeks after limit runes and returns false.
|
||||||
|
// Start must have all fields valid, and needle must have at least one of runes, lineCol,
|
||||||
|
// or x+y valid. Start must be known to be before needle.
|
||||||
func seekPosition(lines []text.Line, alignment text.Alignment, width int, start, needle combinedPos, limit int) (combinedPos, bool) {
|
func seekPosition(lines []text.Line, alignment text.Alignment, width int, start, needle combinedPos, limit int) (combinedPos, bool) {
|
||||||
count := 0
|
count := 0
|
||||||
// Advance until start is greater than or equal to needle.
|
// Advance until start is greater than or equal to needle.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package widget
|
package widget
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -440,3 +441,88 @@ func TestPositionGreaterOrEqual(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSeekPosition(t *testing.T) {
|
||||||
|
fontSize := 16
|
||||||
|
lineWidth := fontSize * 10
|
||||||
|
// Be careful tuning the runeLimit here. This test case's complexity
|
||||||
|
// is O(N^2) where N=runeLimit. It's easy to make this test take a stupid
|
||||||
|
// amount of time accidentally.
|
||||||
|
ltrText, rtlText := makeTestText(fontSize, lineWidth, 15)
|
||||||
|
type testcase struct {
|
||||||
|
name string
|
||||||
|
lines []text.Line
|
||||||
|
align text.Alignment
|
||||||
|
width int
|
||||||
|
}
|
||||||
|
for _, tc := range []testcase{
|
||||||
|
{
|
||||||
|
name: "ltr",
|
||||||
|
lines: ltrText,
|
||||||
|
align: text.Start,
|
||||||
|
width: lineWidth,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rtl",
|
||||||
|
lines: rtlText,
|
||||||
|
align: text.Start,
|
||||||
|
width: lineWidth,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
finalLineRunes := tc.lines[len(tc.lines)-1].Layout.Runes
|
||||||
|
// Statically generate all valid positions.
|
||||||
|
positions := make([]combinedPos, 1, finalLineRunes.Offset+finalLineRunes.Count+1)
|
||||||
|
positions[0] = firstPos(tc.lines[0], tc.align, tc.width)
|
||||||
|
for i := 1; ; i++ {
|
||||||
|
pos, eof := incrementPosition(tc.lines, tc.align, tc.width, positions[i-1])
|
||||||
|
positions = append(positions, pos)
|
||||||
|
if eof {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, start := range positions {
|
||||||
|
for k, needle := range positions {
|
||||||
|
if k < i {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Run(tc.name+" "+strconv.Itoa(i)+"->"+strconv.Itoa(k), func(t *testing.T) {
|
||||||
|
for kind := 0; kind < 3; kind++ {
|
||||||
|
p2 := needle
|
||||||
|
transform := ""
|
||||||
|
switch kind {
|
||||||
|
case 0: // only runes populated
|
||||||
|
transform = "runes only"
|
||||||
|
p2.lineCol = screenPos{}
|
||||||
|
p2.x = 0
|
||||||
|
p2.y = 0
|
||||||
|
case 1: // only lineCol populated
|
||||||
|
transform = "lineCol only"
|
||||||
|
p2.runes = 0
|
||||||
|
p2.x = 0
|
||||||
|
p2.y = 0
|
||||||
|
case 2: // only x and y populated
|
||||||
|
transform = "x,y only"
|
||||||
|
p2.runes = 0
|
||||||
|
p2.lineCol = screenPos{}
|
||||||
|
}
|
||||||
|
for limit := 0; limit <= 10; limit += 5 {
|
||||||
|
result, found := seekPosition(tc.lines, tc.align, tc.width, start, p2, limit)
|
||||||
|
if (found && result != needle) || (!found && needle.runes-start.runes < limit) {
|
||||||
|
t.Errorf("unexpected result seeking p[%d] -> p[%d](%s) (limit %d, found %v) = %#+v\np1: %#+v\np2:%#+v", i, k, transform, limit, found, result, start, p2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result, found := seekPosition(tc.lines, tc.align, tc.width, positions[0], combinedPos{runes: math.MaxInt}, 0)
|
||||||
|
if !found {
|
||||||
|
t.Errorf("reported hit limit on max int")
|
||||||
|
}
|
||||||
|
if expected := positions[len(positions)-1]; result != expected {
|
||||||
|
t.Errorf("expected maximum int position to equal final position.\nexpected %#+v\nactual %#+v", expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user