From 00777a24d5190a4fea467411e71dc361d061488c Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Wed, 9 Apr 2025 23:48:44 +0200 Subject: [PATCH] text editor --- src/core/core.c | 1 - src/core/core_array.c | 158 --------------- src/core/core_string.c | 2 +- src/core/core_string16.c | 15 ++ src/testing/testing.gen.c | 2 - src/text_editor/core_array.c | 185 +++++++++++++++++ src/text_editor/text_editor.gen.c | 4 +- src/text_editor/text_editor_buffer.c | 293 ++++++++++++++++++++------- src/text_editor/text_editor_main.c | 1 + src/wasm_app/wasm_app.gen.c | 1 - 10 files changed, 429 insertions(+), 233 deletions(-) delete mode 100644 src/core/core_array.c create mode 100644 src/text_editor/core_array.c diff --git a/src/core/core.c b/src/core/core.c index 87a96d5..a4ced41 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -62,7 +62,6 @@ #include "core_type_info.c" #include "core_hash.c" #include "core_hash_table.c" -#include "core_array.c" #ifndef DONT_INCLUDE_GENERATED_MATH #include "core_math.gen.c" #endif diff --git a/src/core/core_array.c b/src/core/core_array.c deleted file mode 100644 index 9306e7e..0000000 --- a/src/core/core_array.c +++ /dev/null @@ -1,158 +0,0 @@ -typedef struct arr_header_t arr_header_t; -struct arr_header_t { - i64 len; - i64 cap; - ma_arena_t *arena; -}; - -#define arr_header(arr) ((arr_header_t *)(arr) - 1) -#define arr_len(arr) (arr_header(arr)->len) -#define arr_cap(arr) (arr_header(arr)->cap) -#define arr_add(arr, x) (arr_grow(&(arr), sizeof(*(arr)), 1), (arr)[arr_len(arr)++] = (x)) -#define arr_addn(arr, count) (arr_grow(&(arr), sizeof(*(arr)), (count)), arr_len(arr) += (count), &(arr)[arr_len(arr) - (count)]) -#define arr_set_len(arr, x) (arr_len(arr) = (x)) -#define arr_set_cap(arr, x) (arr__set_cap(&(arr), sizeof(*(arr)), (x))) -#define arr_pop(arr) ((arr)[--arr_len(arr)]) -#define arr_swapdel(arr, i) (assert_expr((i) < arr_len(arr)), (arr)[(i)] = (arr)[--arr_len(arr)]) -#define arr_del(arr, i) arr__del((arr), sizeof(*(arr)), (i), 1) -#define arr_deln(arr, i, count) arr__del((arr), sizeof(*(arr)), (i), (count)) -#define arr_insert(arr, i, x) (arr__insert(&arr, sizeof(*(arr)), (i)), (arr)[(i)] = (x)) -#define arr_create(arena, type, count) (type *)(arr__create((arena), sizeof(type), (count)) + 1) -#define arr_copy(arena, arr) arr__copy((arena), (arr), sizeof(*(arr))) - -fn arr_header_t *arr__create(ma_arena_t *arena, i64 item_size, i64 item_count) { - arr_header_t *hdr = (arr_header_t *)ma_push_size(arena, item_size * item_count + sizeof(arr_header_t)); - hdr->arena = arena; - hdr->cap = item_count; - return hdr; -} - -fn void *arr__copy(ma_arena_t *arena, void *arr, i64 item_size) { - arr_header_t *hdr = arr_header(arr); - arr_header_t *new_hdr = arr__create(arena, item_size, hdr->cap); - new_hdr->len = hdr->len; - memory_copy(new_hdr + 1, hdr + 1, new_hdr->len * item_size); - return new_hdr + 1; -} - -fn void arr__set_cap(void **arr, i64 item_size, i64 item_count) { - assert(item_count >= 0); - arr_header_t *hdr = arr_header(*arr); - arr_header_t *new_hdr = arr__create(hdr->arena, item_size, item_count); - new_hdr->len = CLAMP(hdr->len, 0, item_count); - memory_copy(new_hdr + 1, hdr + 1, new_hdr->len * item_size); - *arr = new_hdr + 1; -} - -fn void arr_grow(void **arr, i64 item_size, i64 increment) { - assert(increment >= 1); - arr_header_t *hdr = arr_header(*arr); - if (hdr->len + increment > hdr->cap) { - arr_header_t *new_hdr = arr__create(hdr->arena, item_size, (hdr->cap + increment - 1) * 2); - memory_copy(new_hdr + 1, hdr + 1, hdr->len * item_size); - new_hdr->len = hdr->len; - *arr = new_hdr + 1; - // dealloc(hdr); - } -} - -fn void arr__insert(void **arr, i64 item_size, i64 idx) { - arr_grow(arr, item_size, 1); - arr_header_t *hdr = arr_header(*arr); - assert(idx <= hdr->len); - assert(idx >= 0); - - u8 *arr8 = (u8 *)*arr; - i64 right_len = hdr->len - idx; - memory_move(arr8 + (idx + 1)*item_size, arr8 + idx*item_size, item_size * right_len); - hdr->len += 1; -} - -fn void arr__del(void *arr, i64 item_size, i64 idx, i64 count) { - arr_header_t *hdr = arr_header(arr); - assert(idx >= 0 && idx < hdr->len); - assert((idx + count) > 0 && (idx + count) <= hdr->len); - i64 right_len = hdr->len - idx - count; - u8 *arr8 = (u8 *)arr; - memmove(arr8+idx*item_size, arr8+(idx+count)*item_size, right_len*item_size); - hdr->len -= count; -} - -fn_test void test_arr(void) { - ma_temp_t scratch = ma_begin_scratch(); - int *arr = arr_create(scratch.arena, int, 2); - for (int i = 0; i < 512; i += 1) { - arr_add(arr, i); - } - for (int i = 0; i < 512; i += 1) { - assert(arr[i] == i); - } - assert(arr_len(arr) == 512); - assert(arr_cap(arr) == 512); - for (int i = 511; i >= 0; i -= 1) { - int a = arr_pop(arr); - assert(a == i); - } - assert(arr_len(arr) == 0); - - { - int *a = arr_addn(arr, 4); - assert(arr == a); - assert(arr_header(arr)->len == 4); - - int *b = arr_addn(arr, 12); - assert(arr + 4 == b); - assert(arr_header(arr)->len == 16); - - arr_set_len(arr, 0); - } - - { - for (int i = 0; i < 512; i += 1) { - arr_add(arr, i); - } - - arr_swapdel(arr, 3); - assert(arr[3] == 511); - assert(arr_len(arr) == 511); - - arr_set_len(arr, 0); - } - - { - for (int i = 0; i < 512; i += 1) { - arr_add(arr, i); - } - arr_set_cap(arr, 256); - assert(arr_len(arr) == 256); - assert(arr_cap(arr) == 256); - - arr_insert(arr, 256, 111); - assert(arr_len(arr) == 257); - assert(arr_cap(arr) > 256); - assert(arr[256] == 111); - - arr_insert(arr, 4, 222); - assert(arr_len(arr) == 258); - assert(arr_cap(arr) > 256); - assert(arr[4] == 222); - assert(arr[3] == 3); - assert(arr[5] == 4); - - arr_set_len(arr, 0); - } - - { - for (int i = 0; i < 512; i += 1) { - arr_add(arr, i); - } - arr_deln(arr, 1, 10); - assert(arr[0] == 0); - assert(arr[1] == 11); - assert(arr_len(arr) == (512 - 10)); - arr_del(arr, 0); - assert(arr[0] == 11); - } - - ma_end_scratch(scratch); -} diff --git a/src/core/core_string.c b/src/core/core_string.c index 4c2404f..b26f2b4 100644 --- a/src/core/core_string.c +++ b/src/core/core_string.c @@ -56,7 +56,7 @@ fn b32 char_is_non_word(char w) { } fn b32 char_is_word(char w) { - b32 result = !char16_is_non_word(w); + b32 result = !char_is_non_word(w); return result; } diff --git a/src/core/core_string16.c b/src/core/core_string16.c index 91e34fe..e718632 100644 --- a/src/core/core_string16.c +++ b/src/core/core_string16.c @@ -35,6 +35,21 @@ fn b32 char16_is_digit(u16 a) { return result; } +fn b32 char16_is_symbol(u16 w) { + b32 result = (w >= '!' && w <= '/') || (w >= ':' && w <= '@') || (w >= '[' && w <= '`') || (w >= '{' && w <= '~'); + return result; +} + +fn b32 char16_is_non_word(u16 w) { + b32 result = w != '_' && (char16_is_symbol(w) || char16_is_whitespace(w)); + return result; +} + +fn b32 char16_is_word(u16 w) { + b32 result = !char16_is_non_word(w); + return result; +} + fn b32 char16_is_alphanumeric(u16 a) { b32 result = char16_is_digit(a) || char16_is_alphabetic(a); return result; diff --git a/src/testing/testing.gen.c b/src/testing/testing.gen.c index b6b6a85..1149d7c 100644 --- a/src/testing/testing.gen.c +++ b/src/testing/testing.gen.c @@ -1,10 +1,8 @@ void test_string16(void); void test_hash_table(void); void test_intern_table(void); -void test_arr(void); fn void run_tests(void) { test_string16(); test_hash_table(); test_intern_table(); - test_arr(); } diff --git a/src/text_editor/core_array.c b/src/text_editor/core_array.c new file mode 100644 index 0000000..ece17ea --- /dev/null +++ b/src/text_editor/core_array.c @@ -0,0 +1,185 @@ +typedef enum { + alokind_alloc, + alokind_dealloc, +} alokind_t; + +typedef struct alo_t alo_t; +struct alo_t { + void *object; + void *(*proc)(alo_t alo, alokind_t kind, void *ptr, size_t size); +}; + +fn void *arena_alo_proc(alo_t alo, alokind_t kind, void *ptr, size_t size) { + ma_arena_t *ma = alo.object; + if (kind == alokind_alloc) { + return ma_push_size(alo.object, size); + } else if (kind == alokind_dealloc) { + return NULL; + } else_is_invalid; + return NULL; +} + +fn alo_t ma_alo(ma_arena_t *arena) { + return (alo_t){arena, arena_alo_proc}; +} + +fn alo_t ma_temp_alo(ma_temp_t temp) { + return ma_alo(temp.arena); +} + +fn void dealloc(alo_t alo, void *ptr) { + alo.proc(alo, alokind_dealloc, ptr, 0); +} + +fn void *alloc_size(alo_t alo, size_t size) { + return alo.proc(alo, alokind_alloc, NULL, size); +} + +#define alloc_type(alo, type) (type *)alloc_size((aloc), sizeof(type)) +#define alloc_array(alo, type, count) (type *)alloc_size((alo), sizeof(type) * (count)) + +#define array(type) struct { alo_t alo; type *data; i64 len; i64 cap; } +typedef array(void) array_void_t; +typedef array(i64) array_i64_t; + +#define arrisz(x) sizeof((x)->data[0]) +#define arrcst(x) ((array_void_t *)(x)) +#define array_init(a, this, count) (array__init((a), arrcst(this), arrisz(this), (count))) +#define array_add(this, item) (array__grow(arrcst(this), arrisz(this), 1), (this)->data[(this)->len++] = (item)) +#define array_pop(this) (assert_expr((this)->len > 0), (this)->data[--(this)->len]) +#define array_dealloc(this) (dealloc((this)->alo, (this)->data)) +#define array_addn(this, n) (array__grow(arrcst(this), arrisz(this), (n)), (this)->len += (n), &(this)->data[(this)->len - (n)]) +#define array_insert(this, i, item) (array__insert(arrcst(this), arrisz(this), (i)), (this)->data[(i)] = (item)) +#define array_swapdel(this, i) (assert_expr((i) < (this)->len), (this)->data[(i)] = (this)->data[--(this)->len]) +#define array_del(this, i) (array__del(arrcst(this), arrisz(this), (i), 1)) +#define array_deln(this, i, count) (array__del(arrcst(this), arrisz(this), (i), (count))) +#define array_copy(alo, dst, src) (array__copy((alo), arrcst(dst), (array_void_t *)(src), arrisz(dst))) +#define array_last(x) ((x)->data[(x)->len - 1]) + +#define array_for(type, it, array) for (type *it = (array)->data; it < (array)->data + (array)->len; it += 1) + +fn void array__init(alo_t alo, array_void_t *this, size_t item_size, i64 count) { + assert(this->data == NULL); + this->data = alloc_size(alo, item_size * count); + this->cap = count; + this->len = 0; + this->alo = alo; +} + +fn void array__copy(alo_t alo, array_void_t *dst, array_void_t *src, size_t item_size) { + dst->data = alloc_size(alo, item_size * src->cap); + memory_copy(dst->data, src->data, item_size * src->len); + dst->cap = src->cap; + dst->len = src->len; + dst->alo = alo; +} + +fn void array__grow(array_void_t *this, size_t item_size, i64 item_count) { + if (this->len + 1 > this->cap) { + i64 new_cap = this->cap == 0 ? 16 : (this->cap + item_count - 1) * 2; + void *new_data = alloc_size(this->alo, new_cap * item_size); + if (this->data) { + memory_copy(new_data, this->data, this->len*item_size); + dealloc(this->alo, this->data); + } + this->data = new_data; + this->cap = new_cap; + } +} + +fn void array__del(array_void_t *this, size_t item_size, i64 idx, i64 count) { + if (count == 0) return; + assert(idx >= 0 && idx < this->len); + assert((idx + count) > 0 && (idx + count) <= this->len); + i64 right_len = this->len - idx - count; + u8 *data = (u8 *)this->data; + memory_move(data + idx * item_size, data + (idx + count) * item_size, right_len * item_size); + this->len -= count; +} + +fn void array__insert(array_void_t *this, size_t item_size, i64 idx) { + array__grow(this, item_size, 1); + assert(idx >= 0); + assert(idx <= this->len); + assert(this->alo.object); + + u8 *data = (u8 *)this->data; + i64 right_len = this->len - idx; + memory_move(data + (idx + 1) * item_size, data + idx * item_size, item_size * right_len); + this->len += 1; +} + +fn_test void test_array(void) { + ma_temp_t scratch = ma_begin_scratch(); + { + array_i64_t arr = (array_i64_t){.alo = ma_temp_alo(scratch)}; + for (int i = 0; i < 512; i += 1) { + array_add(&arr, i); + } + for (int i = 0; i < 512; i += 1) { + assert(arr.data[i] == i); + } + assert(arr.len == 512); + assert(arr.cap == 512); + for (int i = 511; i >= 0; i -= 1) { + i64 a = array_pop(&arr); + assert(a == i); + } + assert(arr.len == 0); + array_dealloc(&arr); + } + + { + array_i64_t arr = (array_i64_t){.alo = ma_temp_alo(scratch)}; + i64 *i = array_addn(&arr, 10); + assert(arr.len == 10 && arr.cap == 16); + i64 *j = array_addn(&arr, 2); + assert(i == arr.data); + assert(j == arr.data + 10); + array_dealloc(&arr); + } + + { + array_i64_t arr = (array_i64_t){.alo = ma_temp_alo(scratch)}; + for (int i = 0; i < 512; i += 1) { + array_add(&arr, i); + } + + array_insert(&arr, 256, 111); + assert(arr.data[256] == 111); + assert(arr.data[255] == 255); + assert(arr.data[257] == 256); + assert(arr.len == 513); + + array_insert(&arr, 4, 222); + assert(arr.len == 514); + assert(arr.data[4] == 222); + assert(arr.data[3] == 3); + assert(arr.data[5] == 4); + + array_swapdel(&arr, 2); + assert(arr.len == 513); + assert(arr.data[2] == 511); + assert(arr.data[1] == 1); + assert(arr.data[3] == 3); + + array_i64_t copy = {0}; + array_copy(ma_temp_alo(scratch), ©, &arr); + for (i64 i = 0; i < arr.len; i += 1) { + assert(arr.data[i] == copy.data[i]); + } + assert(copy.len == arr.len); + + } + + ma_end_scratch(scratch); +} + +fn s16_t s16_alo_copy(alo_t ma, s16_t string) { + i64 byte_size = sizeof(u16) * string.len; + u16 *copy = (u16 *)alloc_size(ma, byte_size + sizeof(u16)); + memory_copy(copy, string.str, byte_size); + copy[string.len] = 0; + s16_t result = s16_make(copy, string.len); + return result; +} diff --git a/src/text_editor/text_editor.gen.c b/src/text_editor/text_editor.gen.c index 502f2e3..9fddced 100644 --- a/src/text_editor/text_editor.gen.c +++ b/src/text_editor/text_editor.gen.c @@ -10,6 +10,6 @@ void run_all_tests(void) { test_string16(); test_hash_table(); test_intern_table(); - test_arr(); - buffer16_raw_test(); + test_array(); + buffer16_test(); }// run_all_tests() diff --git a/src/text_editor/text_editor_buffer.c b/src/text_editor/text_editor_buffer.c index 6b3aeed..7122330 100644 --- a/src/text_editor/text_editor_buffer.c +++ b/src/text_editor/text_editor_buffer.c @@ -22,12 +22,15 @@ struct edit16_t { }; // @todo: redo tree +typedef array(edit16_t) array_edit16_t; +typedef array(caret_t) array_caret_t; typedef struct history16_t history16_t; struct history16_t { - edit16_t *edits; // @array - caret_t *carets; // @array + array_edit16_t edits; + array_caret_t carets; }; +typedef array(history16_t) array_history16_t; typedef struct buffer16_id_t buffer16_id_t; struct buffer16_id_t { i64 e; }; @@ -54,33 +57,35 @@ struct buffer16_t { }; }; i64 cap; - i64 *line_starts; // @array + array_i64_t line_starts; - history16_t *undo_stack; // @array - history16_t *redo_stack; // @array - ma_arena_t *arena; + array_history16_t undo_stack; + array_history16_t redo_stack; + alo_t alo; }; gb i64 buffer_raw_ids; /////////////////////////////// // buffer helpers +/////////////////////////////// + fn i64 buffer16_pos_to_line(buffer16_t *buffer, i64 pos) { - // @todo: rewrite this without modifying the line_starts + // @todo: refactor this to not modify line_starts assert(buffer->flags.line_starts); - arr_add(buffer->line_starts, buffer->len + 1); + array_add(&buffer->line_starts, buffer->len + 1); // binary search i64 low = 0; // -2 here because we use 2 indices and combine them into one line range so we // don't want to access last item since that would index past array. - i64 high = arr_len(buffer->line_starts) - 2; + i64 high = buffer->line_starts.len - 2; i64 result = 0; while (low <= high) { i64 mid = low + (high - low) / 2; - r1i64_t range = {buffer->line_starts[mid], buffer->line_starts[mid + 1]}; + r1i64_t range = {buffer->line_starts.data[mid], buffer->line_starts.data[mid + 1]}; if (pos >= range.min && pos < range.max) { result = mid; break; @@ -93,15 +98,15 @@ fn i64 buffer16_pos_to_line(buffer16_t *buffer, i64 pos) { } } - arr_pop(buffer->line_starts); + array_pop(&buffer->line_starts); return result; } fn r1i64_t buffer16_get_line_range_full(buffer16_t *buffer, i64 line, i64 *eof) { - assert(line < arr_len(buffer->line_starts)); - r1i64_t result = {buffer->line_starts[line], buffer->len}; - if (line + 1 < arr_len(buffer->line_starts)) { - result.max = buffer->line_starts[line + 1]; + assert(line < buffer->line_starts.len); + r1i64_t result = {buffer->line_starts.data[line], buffer->len}; + if (line + 1 < buffer->line_starts.len) { + result.max = buffer->line_starts.data[line + 1]; } else { *eof = 1; } @@ -125,7 +130,7 @@ fn xy_t buffer16_pos_to_xy(buffer16_t *buffer, i64 pos) { } fn i64 buffer16_xy_to_pos(buffer16_t *buffer, xy_t xy) { - xy.line = CLAMP(xy.line, 0, arr_len(buffer->line_starts) - 1); + xy.line = CLAMP(xy.line, 0, buffer->line_starts.len - 1); i64 eof = 0; r1i64_t line_range = buffer16_get_line_range_full(buffer, xy.line, &eof); i64 pos = CLAMP(xy.col + line_range.min, line_range.min, line_range.max); @@ -133,7 +138,7 @@ fn i64 buffer16_xy_to_pos(buffer16_t *buffer, xy_t xy) { } fn i64 buffer16_xy_to_pos_without_new_line(buffer16_t *buffer, xy_t xy) { - xy.line = CLAMP(xy.line, 0, arr_len(buffer->line_starts) - 1); + xy.line = CLAMP(xy.line, 0, buffer->line_starts.len - 1); i64 eof = 0; r1i64_t line_range = buffer16_get_line_range_full(buffer, xy.line, &eof); i64 pos = CLAMP(xy.col + line_range.min, line_range.min, line_range.max - 1 + eof); @@ -273,19 +278,17 @@ fn i64 buffer16_get_prev_word_start(buffer16_t *buffer, i64 pos) { return result; } -fn i64 buffer16_get_line_start(buffer16_t *buffer, i64 pos) { - i64 eof = 0; +fn i64 buffer16_get_line_start(buffer16_t *buffer, i64 pos, i64 *eof) { pos = CLAMP(pos, (i64)0, buffer->len); i64 line = buffer16_pos_to_line(buffer, pos); - r1i64_t range = buffer16_get_line_range_full(buffer, line, &eof); + r1i64_t range = buffer16_get_line_range_full(buffer, line, eof); return range.min; } -fn i64 buffer16_get_line_end(buffer16_t *buffer, i64 pos) { - i64 eof = 0; +fn i64 buffer16_get_line_end(buffer16_t *buffer, i64 pos, i64 *eof) { pos = CLAMP(pos, (i64)0, buffer->len); i64 line = buffer16_pos_to_line(buffer, pos); - r1i64_t range = buffer16_get_line_range_full(buffer, line, &eof); + r1i64_t range = buffer16_get_line_range_full(buffer, line, eof); return range.max; } @@ -315,32 +318,38 @@ fn u16 buffer16_get_char(buffer16_t *buffer, i64 pos) { return 0; } +fn i64 buffer16_offset_pos_by_line(buffer16_t *buffer, i64 pos, i64 line_offset) { + xy_t xy = buffer16_pos_to_xy(buffer, pos); + i64 result = buffer16_xy_to_pos_without_new_line(buffer, (xy_t){xy.col, xy.line + line_offset}); + return result; +} + /////////////////////////////// // raw buffer operations -fn void buffer16_raw_init(ma_arena_t *arena, buffer16_t *buffer, s8_t name, i64 size) { +/////////////////////////////// + +fn void buffer16_raw_init(alo_t alo, buffer16_t *buffer, s8_t name, i64 size) { buffer->id = (buffer16_id_t){++buffer_raw_ids}; buffer->name = name; - buffer->arena = arena; + buffer->alo = alo; buffer->cap = size; - buffer->data = ma_push_array(arena, u16, size); - buffer->line_starts = arr_create(arena, i64, 128); - buffer->undo_stack = arr_create(arena, history16_t, 128); - buffer->redo_stack = arr_create(arena, history16_t, 128); + buffer->data = alloc_array(alo, u16, size); + array_init(alo, &buffer->line_starts, 128); + array_init(alo, &buffer->undo_stack, 128); + array_init(alo, &buffer->redo_stack, 128); buffer->flags.line_starts = true; buffer->flags.history = true; - arr_add(buffer->line_starts, 0); + array_add(&buffer->line_starts, 0); } fn void buffer16_raw_grow(buffer16_t *buffer, i64 change_size) { - // @todo: add memory block management, also for arrays i64 new_size = buffer->len + change_size; if (new_size > buffer->cap) { - // Allocator alo = buffer->line_starts.allocator; - i64 outside = new_size - buffer->cap; - i64 new_cap = (buffer->cap + outside) * 2; - u16 *new_array = ma_push_array(buffer->arena, u16, new_cap); + i64 outside = new_size - buffer->cap; + i64 new_cap = (buffer->cap + outside) * 2; + u16 *new_array = alloc_array(buffer->alo, u16, new_cap); memory_copy(new_array, buffer->data, buffer->len * sizeof(u16)); - // Dealloc(alo, &buffer->data); + dealloc(buffer->alo, buffer->data); buffer->cap = new_cap; buffer->data = new_array; } @@ -351,8 +360,8 @@ fn void buffer16_raw_offset_all_lines_forward(buffer16_t *buffer, i64 line, i64 *_offset = 0; if (offset == 0) return; - for (i64 i = line; i < arr_len(buffer->line_starts); i += 1) { - buffer->line_starts[i] += offset; + for (i64 i = line; i < buffer->line_starts.len; i += 1) { + buffer->line_starts.data[i] += offset; } } @@ -362,7 +371,7 @@ fn void buffer16_raw_update_lines(buffer16_t *buffer, r1i64_t range, s16_t strin } i64 min_line_number = buffer16_pos_to_line(buffer, range.min); - assert(min_line_number < arr_len(buffer->line_starts)); + assert(min_line_number < buffer->line_starts.len); // Update lines remove { i64 line_offset = 0; @@ -375,7 +384,7 @@ fn void buffer16_raw_update_lines(buffer16_t *buffer, r1i64_t range, s16_t strin } line_offset -= 1; } - arr_deln(buffer->line_starts, lines_to_remove, lines_to_remove_count); + array_deln(&buffer->line_starts, lines_to_remove, lines_to_remove_count); buffer16_raw_offset_all_lines_forward(buffer, min_line_number + 1, &line_offset); } @@ -383,14 +392,14 @@ fn void buffer16_raw_update_lines(buffer16_t *buffer, r1i64_t range, s16_t strin i64 line_offset = 0; i64 nl = min_line_number + 1; for (i64 i = 0; i < string.len; i += 1) { - nl = min_line_number + 1; - b32 next_line_valid = nl < arr_len(buffer->line_starts); + nl = min_line_number + 1; + b32 next_line_valid = nl < buffer->line_starts.len; if (string.str[i] == L'\n') { i64 new_line_pos = range.min + i + 1; line_offset += 1; - arr_insert(buffer->line_starts, nl, new_line_pos); + array_insert(&buffer->line_starts, nl, new_line_pos); buffer16_raw_offset_all_lines_forward(buffer, nl + 1, &line_offset); min_line_number = nl; } else if (next_line_valid) { @@ -440,39 +449,187 @@ fn void buffer16_raw_replace_text(buffer16_t *buffer, r1i64_t range, s16_t strin #endif } -fn_test void buffer16_raw_test(void) { +fn void buffer16_save_history_before_merge_cursor(buffer16_t *buffer, array_history16_t *stack, array_caret_t *carets) { + if (!buffer->flags.history) { + return; + } + + history16_t node = {0}; + array_copy(carets->alo, &node.carets, carets); + array_add(stack, node); +} + +fn void buffer16_save_history_before_apply_edits(buffer16_t *buffer, array_history16_t *stack, array_edit16_t *edits) { + if (!buffer->flags.history) { + return; + } + + history16_t *entry = &array_last(stack); + array_copy(buffer->alo, &entry->edits, edits); + + // make reverse edits + for (i64 i = 0; i < edits->len; i += 1) { + edit16_t *it = edits->data + i; + it->range = (r1i64_t){it->range.min, it->range.min + it->string.len}; + it->string = s16_alo_copy(buffer->alo, buffer16_get_string(buffer, it->range)); + } + + ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object); + array_edit16_t temp_edit = {0}; + array_copy(ma_temp_alo(scratch), &temp_edit, &entry->edits); + + // fix reverse edits + for (int i = 0; i < edits->len; i += 1) { + edit16_t *it = edits->data + i; + i64 remove_size = r1i64_size(it->range); + i64 insert_size = it->string.len; + i64 offset = insert_size - remove_size; + for (int j = 0; j < entry->edits.len; j += 1) { + edit16_t *new_edit = entry->edits.data + i; + edit16_t *old_edit = temp_edit.data + i; + if (old_edit->range.min > it->range.min) { + new_edit->range.min += offset; + new_edit->range.max += offset; + } + } + } + + ma_end_scratch(scratch); +} + +fn void buffer16_redo_edit(buffer16_t *buffer, array_caret_t *carets) { + if (!buffer->flags.history || buffer->redo_stack.len <= 0) { + return; + } + + history16_t entry = array_pop(&buffer->redo_stack); + buffer16_save_history_before_merge_cursor(buffer, &buffer->undo_stack, carets); + buffer16_save_history_before_apply_edits(buffer, &buffer->undo_stack, &entry.edits); + // buffer16_multi_cursor_apply_edits(buffer, entry.edits); // @todo + + array_dealloc(carets); + *carets = entry.carets; + + array_for(edit16_t, it, &entry.edits) { + dealloc(buffer->alo, it->string.str); + } + array_dealloc(&entry.edits); + +} + +fn void buffer16_apply_edits(buffer16_t *buffer, array_edit16_t *edits) { + assert(buffer->edit_phase == 1); + buffer->edit_phase += 1; + buffer16_save_history_before_apply_edits(buffer, &buffer->undo_stack, edits); + // buffer16_multi_cursor_apply_edits(buffer, entry.edits); // @todo + // @todo: ... +} + +fn void buffer16_dealloc_history_entries(buffer16_t *buffer, array_history16_t *entries) { + for (history16_t *it = entries->data; it < entries->data + entries->len; it += 1) { + array_dealloc(&it->carets); + for (edit16_t *edit_it = it->edits.data; edit_it < it->edits.data + it->edits.len; edit_it += 1) { + dealloc(buffer->alo, edit_it->string.str); + } + array_dealloc(&it->edits); + } + entries->len = 0; +} + + +fn void buffer16_clear_redo_stack(buffer16_t *buffer) { + buffer16_dealloc_history_entries(buffer, &buffer->redo_stack); +} + +fn void buffer16_dealloc_history_array(buffer16_t *buffer, array_history16_t *entries) { + buffer16_dealloc_history_entries(buffer, entries); + array_dealloc(entries); +} + +// @note: !! +// We can invoke this before caret altering commands to save caret history +// and then call some editing command to edit which is not going to save carets +fn array_edit16_t buffer16_begin_edit(alo_t alo, buffer16_t *buffer, array_caret_t *carets) { + assert(buffer->edit_phase == 0 || buffer->edit_phase == 1); + if (buffer->edit_phase == 0) { + buffer->edit_phase += 1; + assert(carets->len); + buffer16_save_history_before_merge_cursor(buffer, &buffer->undo_stack, carets); + buffer16_clear_redo_stack(buffer); + } + array_edit16_t edits = (array_edit16_t){.alo = alo}; + return edits; +} + +fn void buffer16_pre_begin_edit_save_caret_history(buffer16_t *buffer, array_caret_t *carets) { + buffer16_begin_edit(buffer->alo, buffer, carets); +} + +fn void caret_assert_ranges(array_caret_t carets) { + array_for(caret_t, it, &carets) { + assert(it->range.max >= it->range.min); + } +} + +fn_test void buffer16_test(void) { ma_temp_t scratch = ma_begin_scratch(); - s16_t string = s16("thing itself"); - buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t); - buffer16_raw_init(scratch.arena, buffer, S8_FILE_AND_LINE, 16); - buffer16_raw_replace_text(buffer, r1i64_null, string); - assert(s16_are_equal(buffer->string, string)); - assert(buffer->cap == 16); - assert(arr_len(buffer->line_starts) == 1); + { + s16_t string = s16("thing itself"); + buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t); + buffer16_raw_init(ma_temp_alo(scratch), buffer, S8_FILE_AND_LINE, 16); + buffer16_raw_replace_text(buffer, r1i64_null, string); + assert(s16_are_equal(buffer->string, string)); + assert(buffer->cap == 16); + assert(buffer->line_starts.len == 1); - buffer16_raw_replace_text(buffer, r1i64(5, 6), s16("|||")); - assert(s16_are_equal(buffer->string, s16("thing|||itself"))); - assert(arr_len(buffer->line_starts) == 1); + buffer16_raw_replace_text(buffer, r1i64(5, 6), s16("|||")); + assert(s16_are_equal(buffer->string, s16("thing|||itself"))); + assert(buffer->line_starts.len == 1); - buffer16_raw_replace_text(buffer, buffer16_get_range_end(buffer), s16("|||MEMES|||AndStuff")); - assert(s16_are_equal(buffer->string, s16("thing|||itself|||MEMES|||AndStuff"))); - assert(buffer->cap > 16); - assert(arr_len(buffer->line_starts) == 1); + buffer16_raw_replace_text(buffer, buffer16_get_range_end(buffer), s16("|||MEMES|||AndStuff")); + assert(s16_are_equal(buffer->string, s16("thing|||itself|||MEMES|||AndStuff"))); + assert(buffer->cap > 16); + assert(buffer->line_starts.len == 1); - buffer16_raw_replace_text(buffer, buffer16_get_range_end(buffer), s16("\nnext line")); - assert(arr_len(buffer->line_starts) == 2); + buffer16_raw_replace_text(buffer, buffer16_get_range_end(buffer), s16("\nnext line")); + assert(buffer->line_starts.len == 2); - buffer16_raw_replace_text(buffer, r1i64(4, 4), s16("\nnext line")); - buffer16_raw_replace_text(buffer, r1i64(20, 20), s16("\nnext line")); - buffer16_raw_replace_text(buffer, r1i64(14, 15), s16("\nnext line")); - buffer16_raw_replace_text(buffer, r1i64(0, 0), s16("\nnext line")); - buffer16_raw_replace_text(buffer, r1i64(9, 10), s16("\nnext line")); - assert(arr_len(buffer->line_starts) == 7); + buffer16_raw_replace_text(buffer, r1i64(4, 4), s16("\nnext line")); + buffer16_raw_replace_text(buffer, r1i64(20, 20), s16("\nnext line")); + buffer16_raw_replace_text(buffer, r1i64(14, 15), s16("\nnext line")); + buffer16_raw_replace_text(buffer, r1i64(0, 0), s16("\nnext line")); + buffer16_raw_replace_text(buffer, r1i64(9, 10), s16("\nnext line")); + assert(buffer->line_starts.len == 7); - buffer16_raw_replace_text(buffer, r1i64(8, 12), s16_null); - assert(arr_len(buffer->line_starts) == 6); + buffer16_raw_replace_text(buffer, r1i64(8, 12), s16_null); + assert(buffer->line_starts.len == 6); + } + { + s16_t string = s16("thing itself_and meme"); + buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t); + buffer16_raw_init(ma_temp_alo(scratch), buffer, S8_FILE_AND_LINE, 10); + buffer16_raw_replace_text(buffer, r1i64_null, string); + r1i64_t range = buffer16_enclose_word(buffer, 18); + buffer16_raw_replace_text(buffer, range, s16("cat")); + assert(s16_are_equal(buffer->string, s16("thing itself_and cat"))); + + i64 eof = 0; + r1i64_t rng = buffer16_get_line_range_full(buffer, 0, &eof); + s16_t string_of_range = buffer16_get_string(buffer, rng); + assert(eof == 1); + assert(s16_are_equal(string_of_range, s16("thing itself_and cat"))); + } + + { + buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t); + buffer16_raw_init(ma_temp_alo(scratch), buffer, S8_FILE_AND_LINE, 16); + for (int i = 0; i < 16; i += 1) { + buffer16_raw_replace_text(buffer, r1i64_null, s16("line of memes\n")); + } + + } ma_end_scratch(scratch); diff --git a/src/text_editor/text_editor_main.c b/src/text_editor/text_editor_main.c index 23f601a..5e20589 100644 --- a/src/text_editor/text_editor_main.c +++ b/src/text_editor/text_editor_main.c @@ -10,6 +10,7 @@ #include "app/app.c" #include "profiler/profiler.c" #include "render/render.c" +#include "core_array.c" #include "text_editor_buffer.c" // #include "ui/ui.c" diff --git a/src/wasm_app/wasm_app.gen.c b/src/wasm_app/wasm_app.gen.c index ae50798..4d1fab2 100644 --- a/src/wasm_app/wasm_app.gen.c +++ b/src/wasm_app/wasm_app.gen.c @@ -10,6 +10,5 @@ void run_all_tests(void) { test_string16(); test_hash_table(); test_intern_table(); - test_arr(); ui_test_text_replace(); }// run_all_tests()