package buffer import ( "io" "log/slog" "reflect" "testing" "code.chimeric.al/dwrz/wen/internal/editor/buffer/line" ) func newTestBuffer(lines ...string) *Buffer { var b = &Buffer{ cursor: &Cursor{}, lines: make([]*line.Line, 0, len(lines)), log: slog.New(slog.NewTextHandler(io.Discard, nil)), offset: &offset{}, } for _, s := range lines { b.lines = append(b.lines, line.New(s)) } if len(b.lines) == 0 { b.lines = append(b.lines, &line.Line{}) } return b } func bufferLines(b *Buffer) []string { var out = make([]string, len(b.lines)) for i, l := range b.lines { out[i] = l.String() } return out } type CursorMoveTest struct { lines []string action func(b *Buffer) want Cursor } func TestCursorMovement(t *testing.T) { var tests = []CursorMoveTest{ { lines: []string{"ab", "cd"}, action: func(b *Buffer) { b.CursorRight() }, want: Cursor{line: 0, index: 1, glyph: 1}, }, { lines: []string{"a", "b"}, action: func(b *Buffer) { b.CursorRight() b.CursorRight() }, want: Cursor{line: 1, index: 0, glyph: 0}, }, { lines: []string{"ab", "cd"}, action: func(b *Buffer) { b.CursorDown() b.CursorLeft() }, want: Cursor{line: 0, index: 2, glyph: 2}, }, { lines: []string{"hello", "hi"}, action: func(b *Buffer) { b.CursorEnd() b.CursorDown() }, want: Cursor{line: 1, index: 2, glyph: 2}, }, { lines: []string{"abc", "世界"}, action: func(b *Buffer) { b.CursorRight() b.CursorDown() }, want: Cursor{line: 1, index: 0, glyph: 0}, }, { lines: []string{"abc", "世界"}, action: func(b *Buffer) { b.CursorEnd() b.CursorDown() }, want: Cursor{line: 1, index: 3, glyph: 2}, }, } for n, test := range tests { b := newTestBuffer(test.lines...) test.action(b) if got := b.Cursor(); got != test.want { t.Errorf( "#%d: expected cursor %+v, but got %+v", n, test.want, got, ) } } } type EditTest struct { lines []string action func(b *Buffer) wantLines []string wantCursor Cursor } func TestEdit(t *testing.T) { var tests = []EditTest{ { lines: []string{""}, action: func(b *Buffer) { b.Insert('a') b.Insert('世') }, wantLines: []string{"a世"}, wantCursor: Cursor{line: 0, index: 4, glyph: 3}, }, { lines: []string{"hello"}, action: func(b *Buffer) { b.Newline() }, wantLines: []string{"", "hello"}, wantCursor: Cursor{line: 1, index: 0, glyph: 0}, }, { lines: []string{"abcd"}, action: func(b *Buffer) { b.CursorRight() b.CursorRight() b.Newline() }, wantLines: []string{"ab", "cd"}, wantCursor: Cursor{line: 1, index: 0, glyph: 0}, }, { lines: []string{"abcd"}, action: func(b *Buffer) { b.CursorEnd() b.Newline() }, wantLines: []string{"abcd", ""}, wantCursor: Cursor{line: 1, index: 0, glyph: 0}, }, { lines: []string{"ab", ""}, action: func(b *Buffer) { b.CursorDown() b.Backspace() }, wantLines: []string{"ab"}, wantCursor: Cursor{line: 0, index: 2, glyph: 2}, }, { lines: []string{"ab", "cd"}, action: func(b *Buffer) { b.CursorDown() b.Backspace() }, wantLines: []string{"abcd"}, wantCursor: Cursor{line: 0, index: 2, glyph: 2}, }, } for n, test := range tests { b := newTestBuffer(test.lines...) test.action(b) if got := bufferLines(b); !reflect.DeepEqual(got, test.wantLines) { t.Errorf( "#%d: expected lines %q, but got %q", n, test.wantLines, got, ) } if got := b.Cursor(); got != test.wantCursor { t.Errorf( "#%d: expected cursor %+v, but got %+v", n, test.wantCursor, got, ) } } }