fix: update tests and fix SetCursorFromPoint panic
- Rewrite cursor_test.go to use the current GlyphLayout-based API instead of the deleted byteOffsetToLineCol function - Add guard for empty/missing layout slices in SetCursorFromPoint to prevent index-out-of-range panic on empty documents - Add missing Advance field to e2e test GlyphLayout initialization - Add cmd/pad/pad binary to .gitignore
This commit is contained in:
parent
dbe822d3f6
commit
bef3929f40
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -8,6 +8,7 @@
|
||||||
# build files
|
# build files
|
||||||
*.apk
|
*.apk
|
||||||
*.idsig
|
*.idsig
|
||||||
|
cmd/pad/pad
|
||||||
|
|
||||||
# IDE
|
# IDE
|
||||||
.idea/
|
.idea/
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,90 @@ package editor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"pad/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestByteOffsetToLineCol_Logical verifies that the current logic
|
// TestCursorPositioning_Basic verifies cursor positioning via GlyphLayout.
|
||||||
// only handles logical lines (newline-delimited).
|
func TestCursorPositioning_Basic(t *testing.T) {
|
||||||
func TestByteOffsetToLineCol_Logical(t *testing.T) {
|
TheState = NewState()
|
||||||
buf := "hello\nworld"
|
TheState.Editor.Buffer = "hello\nworld"
|
||||||
// 012345 67890
|
|
||||||
|
// Two lines: "hello\n" (bytes 0-5) and "world" (bytes 6-10)
|
||||||
// Logical line 0: "hello" (0-4), pos 5 is '\n', line 1 starts at 6
|
// Line 0 glyphs at Y=70, Line 1 glyphs at Y=140
|
||||||
line, col := byteOffsetToLineCol(buf, 5)
|
TheState.Editor.GlyphLayout = ui.GlyphLayout{
|
||||||
if line != 0 || col != 5 {
|
ByteOffsets: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||||
t.Errorf("expected (0, 5) for pos 5, got (%d, %d)", line, col)
|
X: []ui.Dp{10, 20, 30, 40, 50, 10, 20, 30, 40, 50, 60},
|
||||||
|
Y: []ui.Dp{70, 70, 70, 70, 70, 70, 140, 140, 140, 140, 140},
|
||||||
|
Advance: []ui.Dp{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
|
||||||
}
|
}
|
||||||
|
|
||||||
line, col = byteOffsetToLineCol(buf, 6)
|
// Test tapping on first line near X=30 (glyph index 2, byteOffset 2)
|
||||||
if line != 1 || col != 0 {
|
SetCursorFromPoint(25, 70)
|
||||||
t.Errorf("expected (1, 0) for pos 6, got (%d, %d)", line, col)
|
if TheState.Editor.CursorPosition < 0 || TheState.Editor.CursorPosition > len(TheState.Editor.Buffer) {
|
||||||
|
t.Errorf("cursor position %d out of bounds after first tap", TheState.Editor.CursorPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test tapping on second line
|
||||||
|
SetCursorFromPoint(35, 140)
|
||||||
|
if TheState.Editor.CursorPosition < 0 || TheState.Editor.CursorPosition > len(TheState.Editor.Buffer) {
|
||||||
|
t.Errorf("cursor position %d out of bounds after second tap", TheState.Editor.CursorPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCursorPositioning_EmptyDocument verifies cursor positioning handles empty documents.
|
||||||
|
func TestCursorPositioning_EmptyDocument(t *testing.T) {
|
||||||
|
TheState = NewState()
|
||||||
|
TheState.Editor.Buffer = ""
|
||||||
|
TheState.Editor.GlyphLayout = ui.GlyphLayout{}
|
||||||
|
|
||||||
|
// Should not panic on empty layout
|
||||||
|
SetCursorFromPoint(50, 50)
|
||||||
|
|
||||||
|
if TheState.Editor.CursorPosition != 0 {
|
||||||
|
t.Errorf("expected cursor at 0 for empty document, got %d", TheState.Editor.CursorPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCursorPositioning_IncompleteLayout verifies cursor positioning handles missing layout slices.
|
||||||
|
func TestCursorPositioning_IncompleteLayout(t *testing.T) {
|
||||||
|
TheState = NewState()
|
||||||
|
TheState.Editor.Buffer = "test"
|
||||||
|
// GlyphLayout with ByteOffsets and X but no Advance (partial data)
|
||||||
|
TheState.Editor.GlyphLayout = ui.GlyphLayout{
|
||||||
|
ByteOffsets: []int{0, 1, 2, 3},
|
||||||
|
X: []ui.Dp{10, 20, 30, 40},
|
||||||
|
Y: []ui.Dp{70, 70, 70, 70},
|
||||||
|
// Advance intentionally omitted
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not panic when Advance is missing
|
||||||
|
SetCursorFromPoint(25, 70)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHandleCursorMove_Bounds verifies cursor movement stays within buffer bounds.
|
||||||
|
func TestHandleCursorMove_Bounds(t *testing.T) {
|
||||||
|
TheState = NewState()
|
||||||
|
TheState.Editor.Buffer = "hello"
|
||||||
|
TheState.Editor.CursorPosition = 0
|
||||||
|
|
||||||
|
// Move right 3 times
|
||||||
|
HandleCursorMove(1)
|
||||||
|
HandleCursorMove(1)
|
||||||
|
HandleCursorMove(1)
|
||||||
|
if TheState.Editor.CursorPosition != 3 {
|
||||||
|
t.Errorf("expected cursor at 3, got %d", TheState.Editor.CursorPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move right past end — should clamp to buffer length
|
||||||
|
HandleCursorMove(10)
|
||||||
|
if TheState.Editor.CursorPosition != len(TheState.Editor.Buffer) {
|
||||||
|
t.Errorf("expected cursor at %d (end of buffer), got %d", len(TheState.Editor.Buffer), TheState.Editor.CursorPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move left past start — should clamp to 0
|
||||||
|
HandleCursorMove(-100)
|
||||||
|
if TheState.Editor.CursorPosition != 0 {
|
||||||
|
t.Errorf("expected cursor at 0, got %d", TheState.Editor.CursorPosition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -453,7 +453,7 @@ func EditorLayout(screenWidth, screenHeight ui.Dp, wordWrap bool) []ui.Element {
|
||||||
// SetCursorFromPoint updates the cursor position based on screen coordinates (Dp).
|
// SetCursorFromPoint updates the cursor position based on screen coordinates (Dp).
|
||||||
func SetCursorFromPoint(x, y float64) {
|
func SetCursorFromPoint(x, y float64) {
|
||||||
layout := TheState.Editor.GlyphLayout
|
layout := TheState.Editor.GlyphLayout
|
||||||
if len(layout.ByteOffsets) == 0 {
|
if len(layout.ByteOffsets) == 0 || len(layout.X) == 0 || len(layout.Advance) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ func TestEditorClickToMoveCursor(t *testing.T) {
|
||||||
ByteOffsets: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
|
ByteOffsets: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
|
||||||
X: []ui.Dp{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120},
|
X: []ui.Dp{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120},
|
||||||
Y: []ui.Dp{70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70},
|
Y: []ui.Dp{70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70},
|
||||||
|
Advance: []ui.Dp{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial cursor position should be 0 (or end of text, depending on implementation)
|
// Initial cursor position should be 0 (or end of text, depending on implementation)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user