diff --git a/tools/tui/readline/actions.go b/tools/tui/readline/actions.go index 71478f921..2a662b9a7 100644 --- a/tools/tui/readline/actions.go +++ b/tools/tui/readline/actions.go @@ -183,7 +183,7 @@ func (self *Readline) move_cursor_vertically(amt int) (ans int) { break } } - target_line_num := (cursor_line_num + amt + len(screen_lines)) % len(screen_lines) + target_line_num := utils.Min(utils.Max(0, cursor_line_num+amt), len(screen_lines)-1) ans = target_line_num - cursor_line_num if ans != 0 { self.move_cursor_to_target_line(screen_lines[cursor_line_num], screen_lines[target_line_num]) diff --git a/tools/tui/readline/actions_test.go b/tools/tui/readline/actions_test.go index 3f3ab5e00..02c4a3d61 100644 --- a/tools/tui/readline/actions_test.go +++ b/tools/tui/readline/actions_test.go @@ -176,6 +176,31 @@ func TestCursorMovement(t *testing.T) { dt("àb", func(rl *Readline) { right(rl, 1, 1, false) }, "à", "b") + + lp, _ := loop.New() + rl := New(lp, RlInit{Prompt: "$$ "}) + rl.screen_width = 10 + + vert := func(amt int, moved_amt int, text_upto_cursor_pos string, initials ...Position) { + initial := Position{} + if len(initials) > 0 { + initial = initials[0] + } + rl.cursor = initial + actual := rl.move_cursor_vertically(amt) + if actual != moved_amt { + t.Fatalf("Failed to move cursor by %#v for: %#v \nactual != expected: %#v != %#v", amt, rl.AllText(), actual, moved_amt) + } + if diff := cmp.Diff(text_upto_cursor_pos, rl.text_upto_cursor_pos()); diff != "" { + t.Fatalf("Did not get expected screen lines for: %#v and cursor: %+v\n%s", rl.AllText(), initial, diff) + } + } + + rl.ResetText() + rl.add_text("1234567xy\nabcd\n123") + vert(-1, -1, "1234567xy\nabc", Position{X: 3, Y: 2}) + vert(-2, -2, "1234567xy", Position{X: 3, Y: 2}) + vert(-30, -3, "123", Position{X: 3, Y: 2}) } func TestEraseChars(t *testing.T) { diff --git a/tools/utils/misc.go b/tools/utils/misc.go index dd945967e..cb7c3eae7 100644 --- a/tools/utils/misc.go +++ b/tools/utils/misc.go @@ -53,3 +53,23 @@ func StableSortWithKey[T any, C constraints.Ordered](s []T, key func(a T) C) []T sort.SliceStable(s, func(i, j int) bool { return mem[i] < mem[j] }) return s } + +func Max[T constraints.Ordered](a T, items ...T) (ans T) { + ans = a + for _, q := range items { + if q > ans { + ans = q + } + } + return ans +} + +func Min[T constraints.Ordered](a T, items ...T) (ans T) { + ans = a + for _, q := range items { + if q < ans { + ans = q + } + } + return ans +}