first iteration of naive undo/redo

memory-refactor
Patrick Cleaveliln 2025-07-15 23:57:09 +00:00
parent 7e128d08cc
commit aae0c24504
8 changed files with 219 additions and 108 deletions

View File

@ -144,7 +144,7 @@ yank_whole_line :: proc(state: ^State) {
}
if buffer := current_buffer(state); buffer != nil {
selection := new_selection(buffer, buffer.cursor)
selection := new_selection(buffer, buffer.history.cursor)
length := selection_length(buffer, selection)
state.yank_register.whole_line = true

View File

@ -46,7 +46,7 @@ FileBuffer :: struct {
extension: string,
top_line: int,
cursor: Cursor,
// cursor: Cursor,
selection: Maybe(Selection),
history: FileHistory,
@ -64,7 +64,7 @@ FileBufferIter :: struct {
// TODO: don't make this panic on nil snapshot
buffer_piece_table :: proc(file_buffer: ^FileBuffer) -> ^PieceTable {
return &file_buffer.history.snapshots[file_buffer.history.current].(PieceTable)
return &file_buffer.history.piece_table
}
new_file_buffer_iter_from_beginning :: proc(file_buffer: ^FileBuffer) -> FileBufferIter {
@ -343,7 +343,7 @@ update_file_buffer_index_from_cursor :: proc(buffer: ^FileBuffer) {
rendered_line := 0;
for character in iterate_file_buffer(&it) {
if line_length == buffer.cursor.col && rendered_line == buffer.cursor.line {
if line_length == buffer.history.cursor.col && rendered_line == buffer.history.cursor.line {
break;
}
@ -358,7 +358,7 @@ update_file_buffer_index_from_cursor :: proc(buffer: ^FileBuffer) {
}
// FIXME: just swap cursors
buffer.cursor.index = before_it.cursor.index;
buffer.history.cursor.index = before_it.cursor.index;
update_file_buffer_scroll(buffer);
}
@ -402,7 +402,7 @@ move_cursor_start_of_line :: proc(buffer: ^FileBuffer, cursor: Maybe(^Cursor) =
cursor := cursor;
if cursor == nil {
cursor = &buffer.cursor;
cursor = &buffer.history.cursor;
}
if cursor.?.col > 0 {
@ -421,7 +421,7 @@ move_cursor_end_of_line :: proc(buffer: ^FileBuffer, stop_at_end: bool = true, c
cursor := cursor;
if cursor == nil {
cursor = &buffer.cursor;
cursor = &buffer.history.cursor;
}
it := new_file_buffer_iter_with_cursor(buffer, cursor.?^);
@ -445,7 +445,7 @@ move_cursor_up :: proc(buffer: ^FileBuffer, amount: int = 1, cursor: Maybe(^Curs
cursor := cursor;
if cursor == nil {
cursor = &buffer.cursor;
cursor = &buffer.history.cursor;
}
if cursor.?.line > 0 {
@ -479,7 +479,7 @@ move_cursor_down :: proc(buffer: ^FileBuffer, amount: int = 1, cursor: Maybe(^Cu
cursor := cursor;
if cursor == nil {
cursor = &buffer.cursor;
cursor = &buffer.history.cursor;
}
current_line := cursor.?.line;
@ -512,7 +512,7 @@ move_cursor_left :: proc(buffer: ^FileBuffer, cursor: Maybe(^Cursor) = nil) {
cursor := cursor;
if cursor == nil {
cursor = &buffer.cursor;
cursor = &buffer.history.cursor;
}
if cursor.?.col > 0 {
@ -526,7 +526,7 @@ move_cursor_right :: proc(buffer: ^FileBuffer, stop_at_end: bool = true, amt: in
cursor := cursor;
if cursor == nil {
cursor = &buffer.cursor;
cursor = &buffer.history.cursor;
}
it := new_file_buffer_iter_with_cursor(buffer, cursor.?^);
@ -544,7 +544,7 @@ move_cursor_forward_start_of_word :: proc(buffer: ^FileBuffer, cursor: Maybe(^Cu
cursor := cursor;
if cursor == nil {
cursor = &buffer.cursor;
cursor = &buffer.history.cursor;
}
it := new_file_buffer_iter_with_cursor(buffer, cursor.?^);
@ -558,7 +558,7 @@ move_cursor_forward_end_of_word :: proc(buffer: ^FileBuffer, cursor: Maybe(^Curs
cursor := cursor;
if cursor == nil {
cursor = &buffer.cursor;
cursor = &buffer.history.cursor;
}
it := new_file_buffer_iter_with_cursor(buffer, cursor.?^);
@ -572,7 +572,7 @@ move_cursor_backward_start_of_word :: proc(buffer: ^FileBuffer, cursor: Maybe(^C
cursor := cursor;
if cursor == nil {
cursor = &buffer.cursor;
cursor = &buffer.history.cursor;
}
it := new_file_buffer_iter_with_cursor(buffer, cursor.?^);
@ -587,7 +587,7 @@ move_cursor_backward_end_of_word :: proc(buffer: ^FileBuffer, cursor: Maybe(^Cur
cursor := cursor;
if cursor == nil {
cursor = &buffer.cursor;
cursor = &buffer.history.cursor;
}
it := new_file_buffer_iter_with_cursor(buffer, cursor.?^);
@ -811,8 +811,8 @@ draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, sho
}
begin := buffer.top_line;
cursor_x := x + padding + buffer.cursor.col * state.source_font_width;
cursor_y := y + buffer.cursor.line * state.source_font_height;
cursor_x := x + padding + buffer.history.cursor.col * state.source_font_width;
cursor_y := y + buffer.history.cursor.line * state.source_font_height;
cursor_y -= begin * state.source_font_height;
@ -908,7 +908,7 @@ draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, sho
update_file_buffer_scroll :: proc(buffer: ^FileBuffer, cursor: Maybe(^Cursor) = nil) {
cursor := cursor;
if cursor == nil {
cursor = &buffer.cursor;
cursor = &buffer.history.cursor;
}
if cursor.?.line > (buffer.top_line + buffer.glyphs.height - 5) {
@ -917,10 +917,10 @@ update_file_buffer_scroll :: proc(buffer: ^FileBuffer, cursor: Maybe(^Cursor) =
buffer.top_line = math.max(cursor.?.line - 5, 0);
}
// if buffer.cursor.line > (buffer.top_line + buffer.glyphs.height - 5) {
// buffer.top_line = math.max(buffer.cursor.line - buffer.glyphs.height + 5, 0);
// } else if buffer.cursor.line < (buffer.top_line + 5) {
// buffer.top_line = math.max(buffer.cursor.line - 5, 0);
// if buffer.history.cursor.line > (buffer.top_line + buffer.glyphs.height - 5) {
// buffer.top_line = math.max(buffer.history.cursor.line - buffer.glyphs.height + 5, 0);
// } else if buffer.history.cursor.line < (buffer.top_line + 5) {
// buffer.top_line = math.max(buffer.history.cursor.line - 5, 0);
// }
}
@ -944,9 +944,9 @@ insert_content :: proc(buffer: ^FileBuffer, to_be_inserted: []u8, append_to_end:
return;
}
index := buffer.cursor.index if !append_to_end else new_piece_table_index_from_end(buffer_piece_table(buffer))
index := buffer.history.cursor.index if !append_to_end else new_piece_table_index_from_end(buffer_piece_table(buffer))
insert_text(buffer_piece_table(buffer), to_be_inserted, buffer.cursor.index)
insert_text(buffer_piece_table(buffer), to_be_inserted, buffer.history.cursor.index)
if !append_to_end {
update_file_buffer_index_from_cursor(buffer);
@ -962,13 +962,13 @@ delete_content_from_buffer_cursor :: proc(buffer: ^FileBuffer, amount: int) {
runtime.clear(&buffer.input_buffer);
// Calculate proper line/col values
it := new_file_buffer_iter_with_cursor(buffer, buffer.cursor);
it := new_file_buffer_iter_with_cursor(buffer, buffer.history.cursor);
iterate_file_buffer_reverse(&it)
delete_text(buffer_piece_table(buffer), &buffer.cursor.index)
delete_text(buffer_piece_table(buffer), &buffer.history.cursor.index)
buffer.cursor.line = it.cursor.line
buffer.cursor.col = it.cursor.col
buffer.history.cursor.line = it.cursor.line
buffer.history.cursor.col = it.cursor.col
}
}
@ -976,7 +976,7 @@ delete_content_from_selection :: proc(buffer: ^FileBuffer, selection: ^Selection
selection^ = swap_selections(selection^)
delete_text_in_span(buffer_piece_table(buffer), &selection.start.index, &selection.end.index)
buffer.cursor.index = selection.start.index
buffer.history.cursor.index = selection.start.index
}
delete_content :: proc{delete_content_from_buffer_cursor, delete_content_from_selection};

View File

@ -43,7 +43,7 @@ update_glyph_buffer_from_file_buffer :: proc(buffer: ^FileBuffer) {
if rendered_line >= begin && screen_line >= buffer.glyphs.height { break; }
// render INSERT mode text into glyph buffer
if len(buffer.input_buffer) > 0 && rendered_line == buffer.cursor.line && rendered_col >= buffer.cursor.col && rendered_col < buffer.cursor.col + len(buffer.input_buffer) {
if len(buffer.input_buffer) > 0 && rendered_line == buffer.history.cursor.line && rendered_col >= buffer.history.cursor.col && rendered_col < buffer.history.cursor.col + len(buffer.input_buffer) {
for k in 0..<len(buffer.input_buffer) {
screen_line = rendered_line - begin;

View File

@ -1,35 +1,40 @@
package core
import "core:log"
FileHistory :: struct {
snapshots: []Snapshot,
current: int
piece_table: PieceTable,
cursor: Cursor,
snapshots: []Snapshot,
next: int,
first: int
}
Snapshot :: union {
PieceTable,
Snapshot :: struct {
chunks: [dynamic][]u8,
cursor: Cursor,
}
make_history_with_data :: proc(initial_data: []u8, starting_capacity: int = 1024, allocator := context.allocator) -> FileHistory {
context.allocator = allocator
snapshots := make([]Snapshot, starting_capacity)
snapshots[0] = make_piece_table_from_bytes(initial_data, starting_capacity)
return FileHistory {
snapshots = snapshots,
current = 0
piece_table = make_piece_table(initial_data, starting_capacity = starting_capacity),
snapshots = make([]Snapshot, starting_capacity),
next = 0,
first = 0,
}
}
make_history_empty :: proc(starting_capacity: int = 1024, allocator := context.allocator) -> FileHistory {
context.allocator = allocator
snapshots := make([]Snapshot, starting_capacity)
snapshots[0] = make_piece_table(starting_capacity = starting_capacity)
return FileHistory {
snapshots = snapshots,
current = 0
piece_table = make_piece_table(starting_capacity = starting_capacity),
snapshots = make([]Snapshot, starting_capacity),
next = 0,
first = 0,
}
}
@ -37,12 +42,94 @@ make_history :: proc{make_history_with_data, make_history_empty}
free_history :: proc(history: ^FileHistory) {
for snapshot in &history.snapshots {
if piece_table, ok := snapshot.(PieceTable); ok {
delete(piece_table.original_content);
delete(piece_table.added_content);
delete(piece_table.chunks);
if snapshot.chunks != nil {
delete(snapshot.chunks);
}
}
delete(history.snapshots)
delete(history.piece_table.original_content)
delete(history.piece_table.added_content)
delete(history.piece_table.chunks)
}
push_new_snapshot :: proc(history: ^FileHistory) {
if history.snapshots[history.next].chunks != nil {
delete(history.snapshots[history.next].chunks)
}
history.snapshots[history.next].chunks = clone_chunk(history.piece_table.chunks)
history.snapshots[history.next].cursor = history.cursor
history.next, history.first = next_indexes(history)
}
pop_snapshot :: proc(history: ^FileHistory, make_new_snapshot: bool = false) {
new_next, _ := next_indexes(history, backward = true)
if new_next == history.next do return
if make_new_snapshot {
push_new_snapshot(history)
}
history.next = new_next
delete(history.piece_table.chunks)
history.piece_table.chunks = clone_chunk(history.snapshots[history.next].chunks)
history.cursor = history.snapshots[history.next].cursor
}
recover_snapshot :: proc(history: ^FileHistory) {
new_next, _ := next_indexes(history)
if history.snapshots[new_next].chunks == nil do return
history.next = new_next
delete(history.piece_table.chunks)
history.piece_table.chunks = clone_chunk(history.snapshots[history.next].chunks)
history.cursor = history.snapshots[history.next].cursor
}
clone_chunk :: proc(chunks: [dynamic][]u8) -> [dynamic][]u8 {
new_chunks := make([dynamic][]u8, len(chunks), len(chunks))
for ptr, i in chunks {
new_chunks[i] = ptr
}
return new_chunks
}
next_indexes :: proc(history: ^FileHistory, backward: bool = false) -> (next: int, first: int) {
next = history.next
first = history.first
if backward {
if history.next == history.first {
return
}
next = history.next - 1
if next < 0 {
next = len(history.snapshots) - 1
}
} else {
next = history.next + 1
if next >= len(history.snapshots) {
next = 0
}
if next == first {
first += 1
}
if first >= len(history.snapshots) {
first = 0
}
}
delete(history.snapshots)
}
return
}

View File

@ -89,7 +89,7 @@ register_default_input_actions :: proc(input_map: ^core.InputActions) {
state.mode = .Visual;
core.reset_input_map(state)
core.current_buffer(state).selection = core.new_selection(core.current_buffer(state).cursor);
core.current_buffer(state).selection = core.new_selection(core.current_buffer(state).history.cursor);
}, "enter visual mode");
}
@ -158,6 +158,8 @@ register_default_visual_actions :: proc(input_map: ^core.InputActions) {
// Text Modification
{
core.register_key_action(input_map, .D, proc(state: ^State) {
core.push_new_snapshot(&core.current_buffer(state).history)
sel_cur := &(core.current_buffer(state).selection.?);
core.delete_content(core.current_buffer(state), sel_cur);
@ -169,6 +171,8 @@ register_default_visual_actions :: proc(input_map: ^core.InputActions) {
}, "delete selection");
core.register_key_action(input_map, .C, proc(state: ^State) {
core.push_new_snapshot(&core.current_buffer(state).history)
sel_cur := &(core.current_buffer(state).selection.?);
core.delete_content(core.current_buffer(state), sel_cur);
@ -194,6 +198,8 @@ register_default_visual_actions :: proc(input_map: ^core.InputActions) {
}, "Yank Line");
core.register_key_action(input_map, .P, proc(state: ^State) {
core.push_new_snapshot(&core.current_buffer(state).history)
if state.yank_register.whole_line {
core.insert_content(core.current_buffer(state), []u8{'\n'});
core.paste_register(state, state.yank_register)
@ -209,18 +215,32 @@ register_default_visual_actions :: proc(input_map: ^core.InputActions) {
register_default_text_input_actions :: proc(input_map: ^core.InputActions) {
core.register_key_action(input_map, .I, proc(state: ^State) {
core.push_new_snapshot(&core.current_buffer(state).history)
state.mode = .Insert;
sdl2.StartTextInput();
}, "enter insert mode");
core.register_key_action(input_map, .A, proc(state: ^State) {
core.push_new_snapshot(&core.current_buffer(state).history)
core.move_cursor_right(core.current_buffer(state), false);
state.mode = .Insert;
sdl2.StartTextInput();
}, "enter insert mode after character (append)");
core.register_key_action(input_map, .U, proc(state: ^State) {
core.pop_snapshot(&core.current_buffer(state).history, true)
}, "Undo");
core.register_ctrl_key_action(input_map, .R, proc(state: ^State) {
core.recover_snapshot(&core.current_buffer(state).history)
}, "Redo");
// TODO: add shift+o to insert newline above current one
core.register_key_action(input_map, .O, proc(state: ^State) {
core.push_new_snapshot(&core.current_buffer(state).history)
if buffer := core.current_buffer(state); buffer != nil {
core.move_cursor_end_of_line(buffer, false);
runtime.clear(&buffer.input_buffer)
@ -247,6 +267,8 @@ register_default_text_input_actions :: proc(input_map: ^core.InputActions) {
}
core.register_key_action(input_map, .P, proc(state: ^State) {
core.push_new_snapshot(&core.current_buffer(state).history)
if state.yank_register.whole_line {
core.move_cursor_end_of_line(core.current_buffer(state), false);
core.insert_content(core.current_buffer(state), []u8{'\n'});

View File

@ -389,6 +389,8 @@ main :: proc() {
sdl2.AddEventWatch(expose_event_watcher, &state);
core.push_new_snapshot(&core.current_buffer(&state).history)
control_key_pressed: bool;
for !state.should_close {
{

View File

@ -135,9 +135,9 @@ open_file_buffer_in_new_panel :: proc(state: ^core.State, file_path: string, lin
return;
}
buffer.cursor.line = line
buffer.cursor.col = col
buffer.top_line = buffer.cursor.line
buffer.history.cursor.line = line
buffer.history.cursor.col = col
buffer.top_line = buffer.history.cursor.line
core.update_file_buffer_index_from_cursor(&buffer)
buffer_index = len(state.buffers)
@ -183,15 +183,15 @@ render_file_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileB
ui.open_element(s, nil, { kind = {ui.Grow{}, ui.Grow{}}})
ui.close_element(s)
it := core.new_file_buffer_iter_with_cursor(buffer, buffer.cursor)
it := core.new_file_buffer_iter_with_cursor(buffer, buffer.history.cursor)
ui.open_element(
s,
fmt.tprintf(
"%v:%v - Slice %v:%v - Char: %v",
buffer.cursor.line + 1,
buffer.cursor.col + 1,
buffer.cursor.index.chunk_index,
buffer.cursor.index.char_index,
buffer.history.cursor.line + 1,
buffer.history.cursor.col + 1,
buffer.history.cursor.index.chunk_index,
buffer.history.cursor.index.char_index,
core.get_character_at_iter(it)
),
{}

View File

@ -142,8 +142,8 @@ insert_from_empty_no_newlines :: proc(t: ^testing.T) {
expected_text := fmt.aprintf("%v\n", inputted_text)
run_text_insertion(&e, inputted_text)
expect_line_col(t, buffer.cursor, 0, 12)
expect_cursor_index(t, buffer.cursor, 0, 12)
expect_line_col(t, buffer.history.cursor, 0, 12)
expect_cursor_index(t, buffer.history.cursor, 0, 12)
contents := buffer_to_string(core.current_buffer(&e))
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
@ -160,8 +160,8 @@ insert_from_empty_with_newline :: proc(t: ^testing.T) {
expected_text := fmt.aprintf("%v\n", inputted_text)
run_text_insertion(&e, inputted_text)
expect_line_col(t, buffer.cursor, 1, 17)
expect_cursor_index(t, buffer.cursor, 0, 31)
expect_line_col(t, buffer.history.cursor, 1, 17)
expect_cursor_index(t, buffer.history.cursor, 0, 31)
contents := buffer_to_string(core.current_buffer(&e))
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
@ -184,8 +184,8 @@ insert_in_between_text :: proc(t: ^testing.T) {
run_text_insertion(&e, " beautiful")
expect_line_col(t, buffer.cursor, 0, 15)
expect_cursor_index(t, buffer.cursor, 1, 9)
expect_line_col(t, buffer.history.cursor, 0, 15)
expect_cursor_index(t, buffer.history.cursor, 1, 9)
contents := buffer_to_string(core.current_buffer(&e))
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
@ -211,8 +211,8 @@ insert_before_slice_at_beginning_of_file :: proc(t: ^testing.T) {
run_inputs(&e, []ArtificialInput{ press_key(.G), press_key(.H)})
run_text_insertion(&e, "Well, ")
expect_line_col(t, buffer.cursor, 0, 5)
expect_cursor_index(t, buffer.cursor, 0, 5)
expect_line_col(t, buffer.history.cursor, 0, 5)
expect_cursor_index(t, buffer.history.cursor, 0, 5)
contents := buffer_to_string(core.current_buffer(&e))
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
@ -239,8 +239,8 @@ insert_before_slice :: proc(t: ^testing.T) {
run_text_insertion(&e, " rich")
expect_line_col(t, buffer.cursor, 0, 20)
expect_cursor_index(t, buffer.cursor, 2, 4)
expect_line_col(t, buffer.history.cursor, 0, 20)
expect_cursor_index(t, buffer.history.cursor, 2, 4)
contents := buffer_to_string(core.current_buffer(&e))
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
@ -259,14 +259,14 @@ delete_last_content_slice_beginning_of_file :: proc(t: ^testing.T) {
run_input_multiple(&e, press_key(.I), 1)
run_input_multiple(&e, press_key(.BACKSPACE), 13)
expect_line_col(t, buffer.cursor, 0, 0)
expect_cursor_index(t, buffer.cursor, 0, 0)
expect_line_col(t, buffer.history.cursor, 0, 0)
expect_cursor_index(t, buffer.history.cursor, 0, 0)
// Try to delete when there is no text
run_input_multiple(&e, press_key(.BACKSPACE), 1)
expect_line_col(t, buffer.cursor, 0, 0)
expect_cursor_index(t, buffer.cursor, 0, 0)
expect_line_col(t, buffer.history.cursor, 0, 0)
expect_cursor_index(t, buffer.history.cursor, 0, 0)
testing.expect(t, len(core.buffer_piece_table(buffer).chunks) > 0, "BACKSPACE deleted final content slice in buffer")
// "commit" insert mode changes, then re-enter insert mode and try to delete again
@ -274,8 +274,8 @@ delete_last_content_slice_beginning_of_file :: proc(t: ^testing.T) {
run_input_multiple(&e, press_key(.I), 1)
run_input_multiple(&e, press_key(.BACKSPACE), 1)
expect_line_col(t, buffer.cursor, 0, 0)
expect_cursor_index(t, buffer.cursor, 0, 0)
expect_line_col(t, buffer.history.cursor, 0, 0)
expect_cursor_index(t, buffer.history.cursor, 0, 0)
testing.expect(t, len(core.buffer_piece_table(buffer).chunks) > 0, "BACKSPACE deleted final content slice in buffer")
}
@ -306,8 +306,8 @@ delete_in_slice :: proc(t: ^testing.T) {
run_input_multiple(&e, press_key(.BACKSPACE), 3)
run_input_multiple(&e, press_key(.ESCAPE), 1)
expect_line_col(t, buffer.cursor, 0, 17)
expect_cursor_index(t, buffer.cursor, 3, 0)
expect_line_col(t, buffer.history.cursor, 0, 17)
expect_cursor_index(t, buffer.history.cursor, 3, 0)
contents := buffer_to_string(core.current_buffer(&e))
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
@ -348,8 +348,8 @@ delete_across_slices :: proc(t: ^testing.T) {
run_input_multiple(&e, press_key(.BACKSPACE), 2)
run_input_multiple(&e, press_key(.ESCAPE), 1)
expect_line_col(t, buffer.cursor, 0, 16)
expect_cursor_index(t, buffer.cursor, 2, 0)
expect_line_col(t, buffer.history.cursor, 0, 16)
expect_cursor_index(t, buffer.history.cursor, 2, 0)
contents := buffer_to_string(core.current_buffer(&e))
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
@ -375,8 +375,8 @@ move_down_next_line_has_shorter_length :: proc(t: ^testing.T) {
// Move down to the second line
run_input_multiple(&e, press_key(.J), 1)
expect_line_col(t, buffer.cursor, 1, 0)
expect_cursor_index(t, buffer.cursor, 0, 10)
expect_line_col(t, buffer.history.cursor, 1, 0)
expect_cursor_index(t, buffer.history.cursor, 0, 10)
}
@(test)
@ -394,8 +394,8 @@ move_down_on_last_line :: proc(t: ^testing.T) {
run_input_multiple(&e, press_key(.J), 1)
// Cursor should stay where it is
expect_line_col(t, buffer.cursor, 0, 8)
expect_cursor_index(t, buffer.cursor, 0, 8)
expect_line_col(t, buffer.history.cursor, 0, 8)
expect_cursor_index(t, buffer.history.cursor, 0, 8)
}
@(test)
@ -410,15 +410,15 @@ move_left_at_beginning_of_file :: proc(t: ^testing.T) {
// to ------------------^
run_input_multiple(&e, press_key(.H), 4)
expect_line_col(t, buffer.cursor, 0, 0)
expect_cursor_index(t, buffer.cursor, 0, 0)
expect_line_col(t, buffer.history.cursor, 0, 0)
expect_cursor_index(t, buffer.history.cursor, 0, 0)
// Try to move before the beginning of the file
run_input_multiple(&e, press_key(.H), 1)
// Should stay the same
expect_line_col(t, buffer.cursor, 0, 0)
expect_cursor_index(t, buffer.cursor, 0, 0)
expect_line_col(t, buffer.history.cursor, 0, 0)
expect_cursor_index(t, buffer.history.cursor, 0, 0)
}
@(test)
@ -432,15 +432,15 @@ move_right_at_end_of_file :: proc(t: ^testing.T) {
run_text_insertion(&e, "01234")
expect_line_col(t, buffer.cursor, 0, 4)
expect_cursor_index(t, buffer.cursor, 0, 4)
expect_line_col(t, buffer.history.cursor, 0, 4)
expect_cursor_index(t, buffer.history.cursor, 0, 4)
// Try to move after the end of the file
run_input_multiple(&e, press_key(.L), 1)
// Should stay the same
expect_line_col(t, buffer.cursor, 0, 4)
expect_cursor_index(t, buffer.cursor, 0, 4)
expect_line_col(t, buffer.history.cursor, 0, 4)
expect_cursor_index(t, buffer.history.cursor, 0, 4)
}
@(test)
@ -460,8 +460,8 @@ move_to_end_of_line_from_end :: proc(t: ^testing.T) {
// Move to the end of the line
run_inputs(&e, []ArtificialInput{ press_key(.G), press_key(.L)})
expect_line_col(t, buffer.cursor, 0, 4)
expect_cursor_index(t, buffer.cursor, 0, 4)
expect_line_col(t, buffer.history.cursor, 0, 4)
expect_cursor_index(t, buffer.history.cursor, 0, 4)
}
@(test)
@ -484,8 +484,8 @@ move_to_end_of_line_from_middle :: proc(t: ^testing.T) {
// Move to the end of the line
run_inputs(&e, []ArtificialInput{ press_key(.G), press_key(.L)})
expect_line_col(t, buffer.cursor, 0, 4)
expect_cursor_index(t, buffer.cursor, 0, 4)
expect_line_col(t, buffer.history.cursor, 0, 4)
expect_cursor_index(t, buffer.history.cursor, 0, 4)
}
@(test)
@ -508,8 +508,8 @@ move_to_beginning_of_line_from_middle :: proc(t: ^testing.T) {
// Move to the beginning of the line
run_inputs(&e, []ArtificialInput{ press_key(.G), press_key(.H)})
expect_line_col(t, buffer.cursor, 0, 0)
expect_cursor_index(t, buffer.cursor, 0, 0)
expect_line_col(t, buffer.history.cursor, 0, 0)
expect_cursor_index(t, buffer.history.cursor, 0, 0)
}
@(test)
@ -532,8 +532,8 @@ move_to_beginning_of_line_from_start :: proc(t: ^testing.T) {
// Move to the beginning of the line
run_inputs(&e, []ArtificialInput{ press_key(.G), press_key(.H)})
expect_line_col(t, buffer.cursor, 0, 0)
expect_cursor_index(t, buffer.cursor, 0, 0)
expect_line_col(t, buffer.history.cursor, 0, 0)
expect_cursor_index(t, buffer.history.cursor, 0, 0)
}
@(test)
@ -548,14 +548,14 @@ append_end_of_line :: proc(t: ^testing.T) {
run_input_multiple(&e, press_key(.A), 1)
run_input_multiple(&e, press_key(.ESCAPE), 1)
expect_line_col(t, buffer.cursor, 0, 5)
expect_cursor_index(t, buffer.cursor, 1, 0)
expect_line_col(t, buffer.history.cursor, 0, 5)
expect_cursor_index(t, buffer.history.cursor, 1, 0)
run_input_multiple(&e, press_key(.A), 1)
run_input_multiple(&e, press_key(.ESCAPE), 1)
expect_line_col(t, buffer.cursor, 0, 5)
expect_cursor_index(t, buffer.cursor, 1, 0)
expect_line_col(t, buffer.history.cursor, 0, 5)
expect_cursor_index(t, buffer.history.cursor, 1, 0)
}
@(test)
@ -581,13 +581,13 @@ insert_line_under_current :: proc(t: ^testing.T) {
// Technically the cursor is still on the first line, because the `input_buffer`
// has been modified but not the actual contents of the filebuffer
expect_line_col(t, buffer.cursor, 0, 13)
expect_cursor_index(t, buffer.cursor, 0, 13)
expect_line_col(t, buffer.history.cursor, 0, 13)
expect_cursor_index(t, buffer.history.cursor, 0, 13)
run_text_insertion(&e, "This is the second line")
expect_line_col(t, buffer.cursor, 1, 22)
expect_cursor_index(t, buffer.cursor, 1, 23)
expect_line_col(t, buffer.history.cursor, 1, 22)
expect_cursor_index(t, buffer.history.cursor, 1, 23)
contents := buffer_to_string(core.current_buffer(&e))
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
@ -614,7 +614,7 @@ yank_and_paste_whole_line :: proc(t: ^testing.T) {
// Paste it below current one
run_input_multiple(&e, press_key(.P), 1)
expect_line_col(t, buffer.cursor, 1, 0)
expect_line_col(t, buffer.history.cursor, 1, 0)
contents := buffer_to_string(core.current_buffer(&e))
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)