move file buffer contents into history struct

memory-refactor
Patrick Cleaveliln 2025-07-15 22:28:56 +00:00
parent cf21773e6f
commit 7e128d08cc
3 changed files with 76 additions and 25 deletions

View File

@ -49,7 +49,7 @@ FileBuffer :: struct {
cursor: Cursor,
selection: Maybe(Selection),
piece_table: PieceTable,
history: FileHistory,
glyphs: GlyphBuffer,
input_buffer: [dynamic]u8,
@ -62,17 +62,22 @@ FileBufferIter :: struct {
hit_end: bool,
}
// 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)
}
new_file_buffer_iter_from_beginning :: proc(file_buffer: ^FileBuffer) -> FileBufferIter {
return FileBufferIter {
buffer = file_buffer,
piter = new_piece_table_iter(&file_buffer.piece_table)
piter = new_piece_table_iter(buffer_piece_table(file_buffer))
};
}
new_file_buffer_iter_with_cursor :: proc(file_buffer: ^FileBuffer, cursor: Cursor) -> FileBufferIter {
return FileBufferIter {
buffer = file_buffer,
cursor = cursor,
piter = new_piece_table_iter_from_index(&file_buffer.piece_table, cursor.index)
piter = new_piece_table_iter_from_index(buffer_piece_table(file_buffer), cursor.index)
};
}
new_file_buffer_iter :: proc{new_file_buffer_iter_from_beginning, new_file_buffer_iter_with_cursor};
@ -81,7 +86,7 @@ file_buffer_end :: proc(buffer: ^FileBuffer) -> Cursor {
return Cursor {
col = 0,
line = 0,
index = new_piece_table_index_from_end(&buffer.piece_table)
index = new_piece_table_index_from_end(buffer_piece_table(buffer))
};
}
@ -126,7 +131,7 @@ iterate_file_buffer_reverse :: proc(it: ^FileBufferIter) -> (character: u8, idx:
}
get_character_at_iter :: proc(it: FileBufferIter) -> u8 {
return get_character_at_piece_table_index(&it.buffer.piece_table, it.cursor.index);
return get_character_at_piece_table_index(buffer_piece_table(it.buffer), it.cursor.index);
}
IterProc :: proc(it: ^FileBufferIter) -> (character: u8, idx: PieceTableIndex, cond: bool);
@ -322,7 +327,7 @@ until_single_quote :: proc(it: ^FileBufferIter, iter_proc: IterProc) -> bool {
}
until_line_break :: proc(it: ^FileBufferIter, iter_proc: IterProc) -> (cond: bool) {
if get_character_at_piece_table_index(&it.buffer.piece_table, it.cursor.index) == '\n' {
if get_character_at_piece_table_index(buffer_piece_table(it.buffer), it.cursor.index) == '\n' {
return false;
}
@ -362,8 +367,8 @@ file_buffer_line_length :: proc(buffer: ^FileBuffer, index: PieceTableIndex) ->
line_length := 0;
// if len(buffer.content_slices) <= 0 do return line_length
first_character := get_character_at_piece_table_index(&buffer.piece_table, index);
left_it := new_piece_table_iter_from_index(&buffer.piece_table, index);
first_character := get_character_at_piece_table_index(buffer_piece_table(buffer), index);
left_it := new_piece_table_iter_from_index(buffer_piece_table(buffer), index);
if first_character == '\n' {
iterate_piece_table_iter_reverse(&left_it);
@ -377,7 +382,7 @@ file_buffer_line_length :: proc(buffer: ^FileBuffer, index: PieceTableIndex) ->
line_length += 1;
}
right_it := new_piece_table_iter_from_index(&buffer.piece_table, index);
right_it := new_piece_table_iter_from_index(buffer_piece_table(buffer), index);
first := true;
for character in iterate_piece_table_iter(&right_it) {
if character == '\n' {
@ -664,7 +669,7 @@ new_virtual_file_buffer :: proc(allocator: mem.Allocator) -> FileBuffer {
allocator = allocator,
file_path = "virtual_buffer",
piece_table = make_piece_table(),
history = make_history(),
glyphs = make_glyph_buffer(width, height),
input_buffer = make([dynamic]u8, 0, 1024),
@ -712,7 +717,7 @@ new_file_buffer :: proc(allocator: mem.Allocator, file_path: string, base_dir: s
// file_path = fi.fullpath[4:],
extension = extension,
piece_table = make_piece_table(original_content),
history = make_history(original_content),
glyphs = make_glyph_buffer(width, height),
input_buffer = make([dynamic]u8, 0, 1024),
@ -729,7 +734,7 @@ save_buffer_to_disk :: proc(state: ^State, buffer: ^FileBuffer) -> (error: os.Er
defer os.close(fd);
offset: i64 = 0
for chunk in buffer.piece_table.chunks {
for chunk in buffer_piece_table(buffer).chunks {
os.write(fd, chunk) or_return
offset += i64(len(chunk))
@ -755,11 +760,9 @@ next_buffer :: proc(state: ^State, prev_buffer: ^int) -> int {
// TODO: replace this with arena for the file buffer
free_file_buffer :: proc(buffer: ^FileBuffer) {
delete(buffer.piece_table.original_content);
delete(buffer.piece_table.added_content);
delete(buffer.piece_table.chunks);
delete(buffer.glyphs.buffer);
delete(buffer.input_buffer);
free_history(&buffer.history)
delete(buffer.glyphs.buffer)
delete(buffer.input_buffer)
}
color_character :: proc(buffer: ^FileBuffer, start: Cursor, end: Cursor, palette_index: theme.PaletteColor) {
@ -941,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)
index := buffer.cursor.index if !append_to_end else new_piece_table_index_from_end(buffer_piece_table(buffer))
insert_text(&buffer.piece_table, to_be_inserted, buffer.cursor.index)
insert_text(buffer_piece_table(buffer), to_be_inserted, buffer.cursor.index)
if !append_to_end {
update_file_buffer_index_from_cursor(buffer);
@ -962,7 +965,7 @@ delete_content_from_buffer_cursor :: proc(buffer: ^FileBuffer, amount: int) {
it := new_file_buffer_iter_with_cursor(buffer, buffer.cursor);
iterate_file_buffer_reverse(&it)
delete_text(&buffer.piece_table, &buffer.cursor.index)
delete_text(buffer_piece_table(buffer), &buffer.cursor.index)
buffer.cursor.line = it.cursor.line
buffer.cursor.col = it.cursor.col
@ -971,7 +974,7 @@ delete_content_from_buffer_cursor :: proc(buffer: ^FileBuffer, amount: int) {
delete_content_from_selection :: proc(buffer: ^FileBuffer, selection: ^Selection) {
selection^ = swap_selections(selection^)
delete_text_in_span(&buffer.piece_table, &selection.start.index, &selection.end.index)
delete_text_in_span(buffer_piece_table(buffer), &selection.start.index, &selection.end.index)
buffer.cursor.index = selection.start.index
}

48
src/core/history.odin Normal file
View File

@ -0,0 +1,48 @@
package core
FileHistory :: struct {
snapshots: []Snapshot,
current: int
}
Snapshot :: union {
PieceTable,
}
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
}
}
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
}
}
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);
}
}
delete(history.snapshots)
}

View File

@ -32,14 +32,14 @@ buffer_to_string :: proc(buffer: ^core.FileBuffer) -> string {
}
length := 0
for chunk in buffer.piece_table.chunks {
for chunk in core.buffer_piece_table(buffer).chunks {
length += len(chunk)
}
buffer_contents := make([]u8, length)
offset := 0
for chunk in buffer.piece_table.chunks {
for chunk in core.buffer_piece_table(buffer).chunks {
for c in chunk {
buffer_contents[offset] = c
offset += 1
@ -267,7 +267,7 @@ delete_last_content_slice_beginning_of_file :: proc(t: ^testing.T) {
expect_line_col(t, buffer.cursor, 0, 0)
expect_cursor_index(t, buffer.cursor, 0, 0)
testing.expect(t, len(buffer.piece_table.chunks) > 0, "BACKSPACE deleted final content slice in buffer")
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
run_input_multiple(&e, press_key(.ESCAPE), 1)
@ -276,7 +276,7 @@ delete_last_content_slice_beginning_of_file :: proc(t: ^testing.T) {
expect_line_col(t, buffer.cursor, 0, 0)
expect_cursor_index(t, buffer.cursor, 0, 0)
testing.expect(t, len(buffer.piece_table.chunks) > 0, "BACKSPACE deleted final content slice in buffer")
testing.expect(t, len(core.buffer_piece_table(buffer).chunks) > 0, "BACKSPACE deleted final content slice in buffer")
}
@(test)