From c82eccc02abfe55408281cf30f5615391c4c6ff1 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Mon, 7 Apr 2025 08:37:06 +0200 Subject: [PATCH] porting text editor --- src/core/core_array.c | 142 +++++--- src/core/core_math.gen.c | 48 ++- src/core/core_math_gen.py | 12 +- src/core/core_string.c | 15 + src/core/core_string16.c | 38 +-- src/os/os.h | 2 + src/os/os_unix.c | 4 +- src/os/os_win32.c | 17 +- src/testing/testing.gen.c | 4 +- src/text_editor/text_editor.gen.c | 3 +- src/text_editor/text_editor_buffer.c | 480 +++++++++++++++++++++++++++ src/text_editor/text_editor_main.c | 40 +-- src/wasm_app/wasm_app.gen.c | 2 +- 13 files changed, 690 insertions(+), 117 deletions(-) create mode 100644 src/text_editor/text_editor_buffer.c diff --git a/src/core/core_array.c b/src/core/core_array.c index 455d24a..9306e7e 100644 --- a/src/core/core_array.c +++ b/src/core/core_array.c @@ -1,41 +1,54 @@ -typedef struct array_header_t array_header_t; -struct array_header_t { - ma_arena_t *arena; +typedef struct arr_header_t arr_header_t; +struct arr_header_t { i64 len; i64 cap; + ma_arena_t *arena; }; -#define array_header(arr) ((array_header_t *)(arr) - 1) -#define array_len(arr) (array_header(arr)->len) -#define array_cap(arr) (array_header(arr)->cap) -#define array_add(arr, x) (array_grow(&(arr), sizeof(*(arr)), 1), (arr)[array_len(arr)++] = (x)) -#define array_addn(arr, count) (array_grow(&(arr), sizeof(*(arr)), (count)), array_len(arr) += (count), &(arr)[array_len(arr) - (count)]) -#define array_set_len(arr, x) (array_len(arr) = (x)) -#define array_set_cap(arr, x) (array__set_cap(&(arr), sizeof(*(arr)), (x))) -#define array_pop(arr) ((arr)[--array_len(arr)]) -#define array_swapdel(arr, i) (assert_expr((i) < array_len(arr)), (arr)[(i)] = (arr)[--array_len(arr)]) -#define array_create(arena, type, count) (type *)(array__create((arena), sizeof(type), (count)) + 1) +#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 array_header_t *array__create(ma_arena_t *arena, i64 item_size, i64 item_count) { - array_header_t *hdr = (array_header_t *)ma_push_size(arena, item_size * item_count + sizeof(array_header_t)); +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 array__set_cap(void **arr, i64 item_size, i64 item_count) { - array_header_t *hdr = array_header(*arr); - array_header_t *new_hdr = array__create(hdr->arena, item_size, item_count); +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 array_grow(void **arr, i64 item_size, i64 increment) { +fn void arr_grow(void **arr, i64 item_size, i64 increment) { assert(increment >= 1); - array_header_t *hdr = array_header(*arr); + arr_header_t *hdr = arr_header(*arr); if (hdr->len + increment > hdr->cap) { - array_header_t *new_hdr = array__create(hdr->arena, item_size, (hdr->cap + increment - 1) * 2); + 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; @@ -43,56 +56,103 @@ fn void array_grow(void **arr, i64 item_size, i64 increment) { } } -fn_test void test_array(void) { +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 = array_create(scratch.arena, int, 2); + int *arr = arr_create(scratch.arena, int, 2); for (int i = 0; i < 512; i += 1) { - array_add(arr, i); + arr_add(arr, i); } for (int i = 0; i < 512; i += 1) { assert(arr[i] == i); } - assert(array_len(arr) == 512); - assert(array_cap(arr) == 512); + assert(arr_len(arr) == 512); + assert(arr_cap(arr) == 512); for (int i = 511; i >= 0; i -= 1) { - int a = array_pop(arr); + int a = arr_pop(arr); assert(a == i); } - assert(array_len(arr) == 0); + assert(arr_len(arr) == 0); { - int *a = array_addn(arr, 4); + int *a = arr_addn(arr, 4); assert(arr == a); - assert(array_header(arr)->len == 4); + assert(arr_header(arr)->len == 4); - int *b = array_addn(arr, 12); + int *b = arr_addn(arr, 12); assert(arr + 4 == b); - assert(array_header(arr)->len == 16); + assert(arr_header(arr)->len == 16); - array_set_len(arr, 0); + arr_set_len(arr, 0); } { for (int i = 0; i < 512; i += 1) { - array_add(arr, i); + arr_add(arr, i); } - array_swapdel(arr, 3); + arr_swapdel(arr, 3); assert(arr[3] == 511); - assert(array_len(arr) == 511); + assert(arr_len(arr) == 511); - array_set_len(arr, 0); + arr_set_len(arr, 0); } { for (int i = 0; i < 512; i += 1) { - array_add(arr, i); + arr_add(arr, i); } - array_set_cap(arr, 256); - assert(array_len(arr) == 256); - assert(array_cap(arr) == 256); + 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_math.gen.c b/src/core/core_math.gen.c index b2f8526..fc48ad4 100644 --- a/src/core/core_math.gen.c +++ b/src/core/core_math.gen.c @@ -370,8 +370,8 @@ fn r2f32_t r2f32_get_bottom(const r2f32_t *r, f32 value) { /* Y is down */ fn_inline r2f32_t r2f32_expand(r2f32_t rect, v2f32_t value) { return (r2f32_t){ v2f32_sub(rect.min, value), v2f32_add(rect.max, value) }; } fn_inline r2f32_t r2f32_shrink(r2f32_t rect, v2f32_t value) { return (r2f32_t){ v2f32_add(rect.min, value), v2f32_sub(rect.max, value) }; } fn_inline r2f32_t r2f32_shrinks(r2f32_t rect, f32 value) { return (r2f32_t){ v2f32_adds(rect.min, value), v2f32_subs(rect.max, value) }; } -fn_inline v2f32_t r2f32_get_size(r2f32_t r) { return (v2f32_t){r.max.x - r.min.x, r.max.y - r.min.y}; } -fn_inline v2f32_t r2f32_get_mid(r2f32_t r) { return v2f32_add(r.min, v2f32_divs(r2f32_get_size(r), 2)); } +fn_inline v2f32_t r2f32_size(r2f32_t r) { return (v2f32_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +fn_inline v2f32_t r2f32_get_mid(r2f32_t r) { return v2f32_add(r.min, v2f32_divs(r2f32_size(r), 2)); } fn_inline b32 r2f32_contains(r2f32_t rec, v2f32_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } fn_inline r2f32_t r2f32_intersect(r2f32_t a, r2f32_t b) { return (r2f32_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; } fn_inline r2f32_t r2f32_union(r2f32_t a, r2f32_t b) { return (r2f32_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; } @@ -381,8 +381,16 @@ fn_inline r1f32_t r1f32(f32 min, f32 max) { return (r1f32_t){min,max}; } fn_inline r1f32_t r1f32_auto(f32 a, f32 b) { return (r1f32_t){MIN(a,b),MAX(a,b)}; } fn_inline r1f32_t r1f32s(f32 a) { return (r1f32_t){a,a}; } fn_inline r1f32_t r1f32_clamp(r1f32_t a, f32 min, f32 max) { return (r1f32_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; } +fn_inline b32 r1f32_contains(r1f32_t rec, f32 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); } +fn_inline b32 r1f32_are_equal(r1f32_t a, r1f32_t b) { return a.min == b.min && a.max == b.max; } gb_read_only r1f32_t r1f32_null; +fn b32 r1f32_overlap(r1f32_t a, r1f32_t b) { + b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max); + b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max); + return r1 || r2; +} + fn v2f32_t v2f32_clamps(v2f32_t v, f32 min, f32 max) { v2f32_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) }; return result; @@ -515,8 +523,8 @@ fn r2f64_t r2f64_get_bottom(const r2f64_t *r, f64 value) { /* Y is down */ fn_inline r2f64_t r2f64_expand(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_sub(rect.min, value), v2f64_add(rect.max, value) }; } fn_inline r2f64_t r2f64_shrink(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_add(rect.min, value), v2f64_sub(rect.max, value) }; } fn_inline r2f64_t r2f64_shrinks(r2f64_t rect, f64 value) { return (r2f64_t){ v2f64_adds(rect.min, value), v2f64_subs(rect.max, value) }; } -fn_inline v2f64_t r2f64_get_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; } -fn_inline v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_get_size(r), 2)); } +fn_inline v2f64_t r2f64_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +fn_inline v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_size(r), 2)); } fn_inline b32 r2f64_contains(r2f64_t rec, v2f64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } fn_inline r2f64_t r2f64_intersect(r2f64_t a, r2f64_t b) { return (r2f64_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; } fn_inline r2f64_t r2f64_union(r2f64_t a, r2f64_t b) { return (r2f64_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; } @@ -526,8 +534,16 @@ fn_inline r1f64_t r1f64(f64 min, f64 max) { return (r1f64_t){min,max}; } fn_inline r1f64_t r1f64_auto(f64 a, f64 b) { return (r1f64_t){MIN(a,b),MAX(a,b)}; } fn_inline r1f64_t r1f64s(f64 a) { return (r1f64_t){a,a}; } fn_inline r1f64_t r1f64_clamp(r1f64_t a, f64 min, f64 max) { return (r1f64_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; } +fn_inline b32 r1f64_contains(r1f64_t rec, f64 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); } +fn_inline b32 r1f64_are_equal(r1f64_t a, r1f64_t b) { return a.min == b.min && a.max == b.max; } gb_read_only r1f64_t r1f64_null; +fn b32 r1f64_overlap(r1f64_t a, r1f64_t b) { + b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max); + b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max); + return r1 || r2; +} + fn v2f64_t v2f64_clamps(v2f64_t v, f64 min, f64 max) { v2f64_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) }; return result; @@ -660,8 +676,8 @@ fn r2i32_t r2i32_get_bottom(const r2i32_t *r, i32 value) { /* Y is down */ fn_inline r2i32_t r2i32_expand(r2i32_t rect, v2i32_t value) { return (r2i32_t){ v2i32_sub(rect.min, value), v2i32_add(rect.max, value) }; } fn_inline r2i32_t r2i32_shrink(r2i32_t rect, v2i32_t value) { return (r2i32_t){ v2i32_add(rect.min, value), v2i32_sub(rect.max, value) }; } fn_inline r2i32_t r2i32_shrinks(r2i32_t rect, i32 value) { return (r2i32_t){ v2i32_adds(rect.min, value), v2i32_subs(rect.max, value) }; } -fn_inline v2i32_t r2i32_get_size(r2i32_t r) { return (v2i32_t){r.max.x - r.min.x, r.max.y - r.min.y}; } -fn_inline v2i32_t r2i32_get_mid(r2i32_t r) { return v2i32_add(r.min, v2i32_divs(r2i32_get_size(r), 2)); } +fn_inline v2i32_t r2i32_size(r2i32_t r) { return (v2i32_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +fn_inline v2i32_t r2i32_get_mid(r2i32_t r) { return v2i32_add(r.min, v2i32_divs(r2i32_size(r), 2)); } fn_inline b32 r2i32_contains(r2i32_t rec, v2i32_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } fn_inline r2i32_t r2i32_intersect(r2i32_t a, r2i32_t b) { return (r2i32_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; } fn_inline r2i32_t r2i32_union(r2i32_t a, r2i32_t b) { return (r2i32_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; } @@ -671,8 +687,16 @@ fn_inline r1i32_t r1i32(i32 min, i32 max) { return (r1i32_t){min,max}; } fn_inline r1i32_t r1i32_auto(i32 a, i32 b) { return (r1i32_t){MIN(a,b),MAX(a,b)}; } fn_inline r1i32_t r1i32s(i32 a) { return (r1i32_t){a,a}; } fn_inline r1i32_t r1i32_clamp(r1i32_t a, i32 min, i32 max) { return (r1i32_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; } +fn_inline b32 r1i32_contains(r1i32_t rec, i32 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); } +fn_inline b32 r1i32_are_equal(r1i32_t a, r1i32_t b) { return a.min == b.min && a.max == b.max; } gb_read_only r1i32_t r1i32_null; +fn b32 r1i32_overlap(r1i32_t a, r1i32_t b) { + b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max); + b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max); + return r1 || r2; +} + fn v2i32_t v2i32_clamps(v2i32_t v, i32 min, i32 max) { v2i32_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) }; return result; @@ -805,8 +829,8 @@ fn r2i64_t r2i64_get_bottom(const r2i64_t *r, i64 value) { /* Y is down */ fn_inline r2i64_t r2i64_expand(r2i64_t rect, v2i64_t value) { return (r2i64_t){ v2i64_sub(rect.min, value), v2i64_add(rect.max, value) }; } fn_inline r2i64_t r2i64_shrink(r2i64_t rect, v2i64_t value) { return (r2i64_t){ v2i64_add(rect.min, value), v2i64_sub(rect.max, value) }; } fn_inline r2i64_t r2i64_shrinks(r2i64_t rect, i64 value) { return (r2i64_t){ v2i64_adds(rect.min, value), v2i64_subs(rect.max, value) }; } -fn_inline v2i64_t r2i64_get_size(r2i64_t r) { return (v2i64_t){r.max.x - r.min.x, r.max.y - r.min.y}; } -fn_inline v2i64_t r2i64_get_mid(r2i64_t r) { return v2i64_add(r.min, v2i64_divs(r2i64_get_size(r), 2)); } +fn_inline v2i64_t r2i64_size(r2i64_t r) { return (v2i64_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +fn_inline v2i64_t r2i64_get_mid(r2i64_t r) { return v2i64_add(r.min, v2i64_divs(r2i64_size(r), 2)); } fn_inline b32 r2i64_contains(r2i64_t rec, v2i64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } fn_inline r2i64_t r2i64_intersect(r2i64_t a, r2i64_t b) { return (r2i64_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; } fn_inline r2i64_t r2i64_union(r2i64_t a, r2i64_t b) { return (r2i64_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; } @@ -816,8 +840,16 @@ fn_inline r1i64_t r1i64(i64 min, i64 max) { return (r1i64_t){min,max}; } fn_inline r1i64_t r1i64_auto(i64 a, i64 b) { return (r1i64_t){MIN(a,b),MAX(a,b)}; } fn_inline r1i64_t r1i64s(i64 a) { return (r1i64_t){a,a}; } fn_inline r1i64_t r1i64_clamp(r1i64_t a, i64 min, i64 max) { return (r1i64_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; } +fn_inline b32 r1i64_contains(r1i64_t rec, i64 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); } +fn_inline b32 r1i64_are_equal(r1i64_t a, r1i64_t b) { return a.min == b.min && a.max == b.max; } gb_read_only r1i64_t r1i64_null; +fn b32 r1i64_overlap(r1i64_t a, r1i64_t b) { + b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max); + b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max); + return r1 || r2; +} + fn v2i64_t v2i64_clamps(v2i64_t v, i64 min, i64 max) { v2i64_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) }; return result; diff --git a/src/core/core_math_gen.py b/src/core/core_math_gen.py index 8cb09ea..e0f4d6b 100644 --- a/src/core/core_math_gen.py +++ b/src/core/core_math_gen.py @@ -318,8 +318,8 @@ fn r2f64_t r2f64_get_bottom(const r2f64_t *r, f64 value) { /* Y is down */ fn_inline r2f64_t r2f64_expand(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_sub(rect.min, value), v2f64_add(rect.max, value) }; } fn_inline r2f64_t r2f64_shrink(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_add(rect.min, value), v2f64_sub(rect.max, value) }; } fn_inline r2f64_t r2f64_shrinks(r2f64_t rect, f64 value) { return (r2f64_t){ v2f64_adds(rect.min, value), v2f64_subs(rect.max, value) }; } -fn_inline v2f64_t r2f64_get_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; } -fn_inline v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_get_size(r), 2)); } +fn_inline v2f64_t r2f64_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +fn_inline v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_size(r), 2)); } fn_inline b32 r2f64_contains(r2f64_t rec, v2f64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } fn_inline r2f64_t r2f64_intersect(r2f64_t a, r2f64_t b) { return (r2f64_t){ .min.x = MAX(a.min.x, b.min.x), .min.y = MAX(a.min.y, b.min.y), .max.x = MIN(a.max.x, b.max.x), .max.y = MIN(a.max.y, b.max.y) }; } fn_inline r2f64_t r2f64_union(r2f64_t a, r2f64_t b) { return (r2f64_t){ .min.x = MIN(a.min.x, b.min.x), .min.y = MIN(a.min.y, b.min.y), .max.x = MAX(a.max.x, b.max.x), .max.y = MAX(a.max.y, b.max.y) }; } @@ -329,8 +329,16 @@ fn_inline r1f64_t r1f64(f64 min, f64 max) { return (r1f64_t){min,max}; } fn_inline r1f64_t r1f64_auto(f64 a, f64 b) { return (r1f64_t){MIN(a,b),MAX(a,b)}; } fn_inline r1f64_t r1f64s(f64 a) { return (r1f64_t){a,a}; } fn_inline r1f64_t r1f64_clamp(r1f64_t a, f64 min, f64 max) { return (r1f64_t){ .min = CLAMP(a.min, min, max), .max = CLAMP(a.max, min, max) }; } +fn_inline b32 r1f64_contains(r1f64_t rec, f64 point) { return (point >= rec.min) && (point < rec.max) && (point >= rec.min) && (point < rec.max); } +fn_inline b32 r1f64_are_equal(r1f64_t a, r1f64_t b) { return a.min == b.min && a.max == b.max; } gb_read_only r1f64_t r1f64_null; +fn b32 r1f64_overlap(r1f64_t a, r1f64_t b) { + b32 r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max); + b32 r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max); + return r1 || r2; +} + fn v2f64_t v2f64_clamps(v2f64_t v, f64 min, f64 max) { v2f64_t result = { CLAMP(v.x, min, max), CLAMP(v.y, min, max) }; return result; diff --git a/src/core/core_string.c b/src/core/core_string.c index 0ff4c38..4c2404f 100644 --- a/src/core/core_string.c +++ b/src/core/core_string.c @@ -45,6 +45,21 @@ fn b32 char_is_alphanumeric(char a) { return result; } +fn b32 char_is_symbol(char w) { + b32 result = (w >= '!' && w <= '/') || (w >= ':' && w <= '@') || (w >= '[' && w <= '`') || (w >= '{' && w <= '~'); + return result; +} + +fn b32 char_is_non_word(char w) { + b32 result = w != '_' && (char_is_symbol(w) || char_is_whitespace(w)); + return result; +} + +fn b32 char_is_word(char w) { + b32 result = !char16_is_non_word(w); + return result; +} + fn s8_t s8_from_range(char *begin, char *end) { assert(end >= begin); intptr_t size = (intptr_t)end - (intptr_t)begin; diff --git a/src/core/core_string16.c b/src/core/core_string16.c index f7837da..91e34fe 100644 --- a/src/core/core_string16.c +++ b/src/core/core_string16.c @@ -5,38 +5,38 @@ fn i64 str16_len(u16 *string) { return len; } -fn u16 u16_to_lower_case(u16 a) { +fn u16 char16_to_lower_case(u16 a) { if (a >= 'A' && a <= 'Z') a += 32; return a; } -fn u16 u16_to_upper_case(u16 a) { +fn u16 char16_to_upper_case(u16 a) { if (a >= 'a' && a <= 'z') a -= 32; return a; } -fn b32 u16_is_whitespace(u16 w) { +fn b32 char16_is_whitespace(u16 w) { b32 result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; return result; } -fn b32 u16_is_alphabetic(u16 a) { +fn b32 char16_is_alphabetic(u16 a) { b32 result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); return result; } -fn b32 u16_is_ident(u16 a) { +fn b32 char16_is_ident(u16 a) { b32 result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_'; return result; } -fn b32 u16_is_digit(u16 a) { +fn b32 char16_is_digit(u16 a) { b32 result = a >= '0' && a <= '9'; return result; } -fn b32 u16_is_alphanumeric(u16 a) { - b32 result = u16_is_digit(a) || u16_is_alphabetic(a); +fn b32 char16_is_alphanumeric(u16 a) { + b32 result = char16_is_digit(a) || char16_is_alphabetic(a); return result; } @@ -73,8 +73,8 @@ fn b32 s16_are_equal_ex(s16_t a, s16_t b, b32 ignore_case) { u16 A = a.str[i]; u16 B = b.str[i]; if (ignore_case) { - A = u16_to_lower_case(A); - B = u16_to_lower_case(B); + A = char16_to_lower_case(A); + B = char16_to_lower_case(B); } if (A != B) return false; @@ -206,14 +206,14 @@ fn s16_t s16_trim(s16_t string) { i64 whitespace_begin = 0; for (; whitespace_begin < string.len; whitespace_begin++) { - if (!u16_is_whitespace(string.str[whitespace_begin])) { + if (!char16_is_whitespace(string.str[whitespace_begin])) { break; } } i64 whitespace_end = string.len; for (; whitespace_end != whitespace_begin; whitespace_end--) { - if (!u16_is_whitespace(string.str[whitespace_end - 1])) { + if (!char16_is_whitespace(string.str[whitespace_end - 1])) { break; } } @@ -231,7 +231,7 @@ fn s16_t s16_trim(s16_t string) { fn s16_t s16_trim_end(s16_t string) { i64 whitespace_end = string.len; for (; whitespace_end != 0; whitespace_end--) { - if (!u16_is_whitespace(string.str[whitespace_end - 1])) { + if (!char16_is_whitespace(string.str[whitespace_end - 1])) { break; } } @@ -314,7 +314,7 @@ fn s16_t s16_get_name_no_ext(s16_t s) { fn s16_t s16_to_lower_case(ma_arena_t *ma, s16_t s) { s16_t copy = s16_copy(ma, s); for (int64_t i = 0; i < copy.len; i++) { - copy.str[i] = u16_to_lower_case(copy.str[i]); + copy.str[i] = char16_to_lower_case(copy.str[i]); } return copy; } @@ -322,7 +322,7 @@ fn s16_t s16_to_lower_case(ma_arena_t *ma, s16_t s) { fn s16_t s16_to_upper_case(ma_arena_t *ma, s16_t s) { s16_t copy = s16_copy(ma, s); for (int64_t i = 0; i < copy.len; i++) { - copy.str[i] = u16_to_upper_case(copy.str[i]); + copy.str[i] = char16_to_upper_case(copy.str[i]); } return copy; } @@ -491,10 +491,10 @@ fn_test void test_string16(void) { } { - assert(u16_to_upper_case('a') == char16('A')); - assert(u16_to_upper_case('a') == 'A'); - assert(u16_to_upper_case(char16('a')) == 'A'); - assert(u16_is_digit('3')); + assert(char16_to_upper_case('a') == char16('A')); + assert(char16_to_upper_case('a') == 'A'); + assert(char16_to_upper_case(char16('a')) == 'A'); + assert(char16_is_digit('3')); } { diff --git a/src/os/os.h b/src/os/os.h index 47b786a..7dd30ec 100644 --- a/src/os/os.h +++ b/src/os/os.h @@ -44,6 +44,8 @@ fn os_date_t os_universal_time(void); fn s8_t os_format_date(ma_arena_t *arena, os_date_t date); fn f64 os_seconds(void); fn f64 os_milliseconds(void); +fn f64 os_microseconds(void); + fn b32 os_copy(s8_t from, s8_t to, b32 overwrite); fn b32 os_delete(s8_t path); diff --git a/src/os/os_unix.c b/src/os/os_unix.c index 347c1b3..3840fdf 100644 --- a/src/os/os_unix.c +++ b/src/os/os_unix.c @@ -25,11 +25,11 @@ fn os_date_t os_universal_time(void) { return result; } -fn u64 os_get_microseconds(void) { +fn f64 os_microseconds(void) { struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); u64 result = t.tv_sec*million(1) + (t.tv_nsec/thousand(1)); - return result; + return (f64)result; } fn f64 os_get_milliseconds(void) { diff --git a/src/os/os_win32.c b/src/os/os_win32.c index 22abd69..a1af442 100644 --- a/src/os/os_win32.c +++ b/src/os/os_win32.c @@ -28,23 +28,30 @@ fn os_date_t os_universal_time(void) { return result; } +static int64_t win32_counts_per_second; fn f64 os_seconds(void) { - static int64_t counts_per_second; - if (counts_per_second == 0) { + if (win32_counts_per_second == 0) { LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); - counts_per_second = freq.QuadPart; + win32_counts_per_second = freq.QuadPart; } LARGE_INTEGER time; QueryPerformanceCounter(&time); - f64 result = (f64)time.QuadPart / (f64)counts_per_second; + f64 result = (f64)time.QuadPart / (f64)win32_counts_per_second; + return result; +} + +// @todo: revise these conversions +fn f64 os_microseconds(void) { + f64 secs = os_seconds(); + f64 result = secs * 1000000.0; return result; } fn f64 os_milliseconds(void) { f64 secs = os_seconds(); - f64 result = secs * 1000; + f64 result = secs * 1000.0; return result; } diff --git a/src/testing/testing.gen.c b/src/testing/testing.gen.c index d58ccd4..b6b6a85 100644 --- a/src/testing/testing.gen.c +++ b/src/testing/testing.gen.c @@ -1,10 +1,10 @@ void test_string16(void); void test_hash_table(void); void test_intern_table(void); -void test_array(void); +void test_arr(void); fn void run_tests(void) { test_string16(); test_hash_table(); test_intern_table(); - test_array(); + test_arr(); } diff --git a/src/text_editor/text_editor.gen.c b/src/text_editor/text_editor.gen.c index a6666fc..502f2e3 100644 --- a/src/text_editor/text_editor.gen.c +++ b/src/text_editor/text_editor.gen.c @@ -10,5 +10,6 @@ void run_all_tests(void) { test_string16(); test_hash_table(); test_intern_table(); - test_array(); + test_arr(); + buffer16_raw_test(); }// run_all_tests() diff --git a/src/text_editor/text_editor_buffer.c b/src/text_editor/text_editor_buffer.c new file mode 100644 index 0000000..6b3aeed --- /dev/null +++ b/src/text_editor/text_editor_buffer.c @@ -0,0 +1,480 @@ +#define BUFFER_DEBUG 1 + +typedef struct caret_t caret_t; +struct caret_t { + i64 ifront; + union { + r1i64_t range; + i64 pos[2]; + }; +}; + +typedef struct xy_t xy_t; +struct xy_t { + i64 col; + i64 line; +}; + +typedef struct edit16_t edit16_t; +struct edit16_t { + r1i64_t range; + s16_t string; +}; + +// @todo: redo tree +typedef struct history16_t history16_t; +struct history16_t { + edit16_t *edits; // @array + caret_t *carets; // @array +}; + +typedef struct buffer16_id_t buffer16_id_t; +struct buffer16_id_t { i64 e; }; + +typedef struct buffer16_t buffer16_t; +struct buffer16_t { + s8_t name; + buffer16_id_t id; + + i32 change_id; + i8 edit_phase; + + struct { + b8 dirty: 1; + b8 history: 1; + b8 line_starts: 1; + } flags; + + union { + s16_t string; + u16 *data; + struct { + u16 *str; + i64 len; + }; + }; + i64 cap; + i64 *line_starts; // @array + + history16_t *undo_stack; // @array + history16_t *redo_stack; // @array + ma_arena_t *arena; +}; + +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 + assert(buffer->flags.line_starts); + arr_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 result = 0; + + while (low <= high) { + i64 mid = low + (high - low) / 2; + r1i64_t range = {buffer->line_starts[mid], buffer->line_starts[mid + 1]}; + if (pos >= range.min && pos < range.max) { + result = mid; + break; + } + + if (range.min < pos) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + arr_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]; + } else { + *eof = 1; + } + return result; +} + +fn r1i64_t buffer16_get_line_range(buffer16_t *buffer, i64 line) { + i64 eof = 0; + r1i64_t line_range = buffer16_get_line_range_full(buffer, line, &eof); + line_range.max = line_range.max - 1 + eof; + return line_range; +} + +fn xy_t buffer16_pos_to_xy(buffer16_t *buffer, i64 pos) { + i64 eof = 0; + i64 line = buffer16_pos_to_line(buffer, pos); + r1i64_t line_range = buffer16_get_line_range_full(buffer, line, &eof); + i64 col = pos - line_range.min; + xy_t result = {col, line}; + return result; +} + +fn i64 buffer16_xy_to_pos(buffer16_t *buffer, xy_t xy) { + xy.line = CLAMP(xy.line, 0, arr_len(buffer->line_starts) - 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); + return pos; +} + +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); + 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); + return pos; +} + +fn s16_t buffer16_get_string(buffer16_t *buffer, r1i64_t range) { + range = r1i64_clamp(range, 0, buffer->len); + s16_t result = s16_make(buffer->str + range.min, r1i64_size(range)); + return result; +} + +fn s16_t buffer16_get_line_string(buffer16_t *buffer, i64 line) { + r1i64_t range = buffer16_get_line_range(buffer, line); + s16_t string = buffer16_get_string(buffer, range); + return string; +} + +fn r1i64_t buffer16_get_range_end(buffer16_t *buffer) { + r1i64_t result = {buffer->len, buffer->len}; + return result; +} + +fn r1i64_t buffer16_get_range(buffer16_t *buffer) { + r1i64_t result = {0, buffer->len}; + return result; +} + +fn i64 buffer16_clamp_pos(buffer16_t *buffer, i64 pos) { + i64 result = CLAMP(pos, 0, buffer->len); + return result; +} + +fn r1i64_t buffer16_clamp_range(buffer16_t *buffer, r1i64_t range) { + r1i64_t result = {buffer16_clamp_pos(buffer, range.min), buffer16_clamp_pos(buffer, range.max)}; + return result; +} + +fn i64 caret_get_front(caret_t caret) { + i64 result = caret.pos[caret.ifront]; + return result; +} + +fn i64 caret_get_back(caret_t caret) { + i64 result = caret.pos[(caret.ifront + 1) % 2]; + return result; +} + +fn caret_t caret_make(i64 front, i64 back) { + caret_t result = {0}; + if (front >= back) { + result.range.min = back; + result.range.max = front; + result.ifront = 1; + } else { + result.range.min = front; + result.range.max = back; + result.ifront = 0; + } + return result; +} + +fn caret_t caret_set_front(caret_t caret, i64 pos) { + i64 back = caret_get_back(caret); + caret_t result = caret_make(pos, back); + return result; +} + +fn caret_t caret_set_back(caret_t caret, i64 pos) { + i64 front = caret_get_front(caret); + caret_t result = caret_make(front, pos); + return result; +} + +fn b32 carets_are_equal(caret_t a, caret_t b) { + b32 result = r1i64_are_equal(a.range, b.range) && a.ifront == b.ifront; + return result; +} + +fn i64 buffer16_get_word_start(buffer16_t *buffer, i64 pos) { + pos = CLAMP(pos, (i64)0, buffer->len); + for (i64 i = pos - 1; i >= 0; i -= 1) { + if (!char16_is_word(buffer->str[i])) + break; + pos = i; + } + return pos; +} + +fn i64 buffer16_get_word_end(buffer16_t *buffer, i64 pos) { + pos = CLAMP(pos, (i64)0, buffer->len); + for (i64 i = pos;; i += 1) { + pos = i; + // this is because buffer end terminates the loop + // too early and we cannot establish the proper range + // semantics - proper max is one past last index + if (!(i < buffer->len)) + break; + if (!char16_is_word(buffer->str[i])) + break; + } + return pos; +} + +fn i64 buffer16_get_next_word_end(buffer16_t *buffer, i64 pos) { + pos = CLAMP(pos, (i64)0, buffer->len); + u16 prev = 0; + for (i64 i = pos;; i += 1) { + pos = i; + // this is because buffer end terminates the loop + // too early and we cannot establish the proper range + // semantics - proper max is one past last index + if (!(i < buffer->len)) + break; + fn if (prev == L'\n' || (prev && prev != buffer->str[i]) || char16_is_word(buffer->str[i])) { + break; + } + prev = buffer->str[i]; + } + i64 result = prev == L'\n' ? pos : buffer16_get_word_end(buffer, pos); + return result; +} + +fn i64 buffer16_get_prev_word_start(buffer16_t *buffer, i64 pos) { + pos = CLAMP(pos, (i64)0, buffer->len); + u16 prev = 0; + i64 i = pos - 1; + for (; i >= 0; i -= 1) { + fn if (prev == L'\n' || (prev && prev != buffer->str[i]) || char16_is_word(buffer->str[i])) { + break; + } + pos = i; + prev = buffer->str[i]; + } + b32 new_line = prev == L'\n'; + i64 result = new_line ? pos : buffer16_get_word_start(buffer, pos); + return result; +} + +fn i64 buffer16_get_line_start(buffer16_t *buffer, i64 pos) { + i64 eof = 0; + 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); + return range.min; +} + +fn i64 buffer16_get_line_end(buffer16_t *buffer, i64 pos) { + i64 eof = 0; + 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); + return range.max; +} + +fn i64 buffer16_get_line_start_full(buffer16_t *buffer, i64 pos) { + pos = CLAMP(pos, (i64)0, buffer->len); + i64 line = buffer16_pos_to_line(buffer, pos); + r1i64_t range = buffer16_get_line_range(buffer, line); + return range.min; +} + +fn i64 buffer16_get_line_end_full(buffer16_t *buffer, i64 pos) { + pos = CLAMP(pos, (i64)0, buffer->len); + i64 line = buffer16_pos_to_line(buffer, pos); + r1i64_t range = buffer16_get_line_range(buffer, line); + return range.max; +} + +fn r1i64_t buffer16_enclose_word(buffer16_t *buffer, i64 pos) { + r1i64_t result = {buffer16_get_word_start(buffer, pos), buffer16_get_word_end(buffer, pos)}; + return result; +} + +fn u16 buffer16_get_char(buffer16_t *buffer, i64 pos) { + if (pos >= 0 && pos < buffer->len) { + return buffer->str[pos]; + } + return 0; +} + +/////////////////////////////// +// raw buffer operations +fn void buffer16_raw_init(ma_arena_t *arena, buffer16_t *buffer, s8_t name, i64 size) { + buffer->id = (buffer16_id_t){++buffer_raw_ids}; + buffer->name = name; + buffer->arena = arena; + 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->flags.line_starts = true; + buffer->flags.history = true; + arr_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); + memory_copy(new_array, buffer->data, buffer->len * sizeof(u16)); + // Dealloc(alo, &buffer->data); + buffer->cap = new_cap; + buffer->data = new_array; + } +} + +fn void buffer16_raw_offset_all_lines_forward(buffer16_t *buffer, i64 line, i64 *_offset) { + i64 offset = *_offset; + *_offset = 0; + if (offset == 0) return; + + for (i64 i = line; i < arr_len(buffer->line_starts); i += 1) { + buffer->line_starts[i] += offset; + } +} + +fn void buffer16_raw_update_lines(buffer16_t *buffer, r1i64_t range, s16_t string) { + if (buffer->flags.line_starts == false) { + return; + } + + i64 min_line_number = buffer16_pos_to_line(buffer, range.min); + assert(min_line_number < arr_len(buffer->line_starts)); + // Update lines remove + { + i64 line_offset = 0; + i64 lines_to_remove = min_line_number + 1; + i64 lines_to_remove_count = 0; + for (i64 i = range.min; i < range.max; i += 1) { + u16 c = buffer->data[i]; + if (c == '\n') { + lines_to_remove_count += 1; + } + line_offset -= 1; + } + arr_deln(buffer->line_starts, lines_to_remove, lines_to_remove_count); + buffer16_raw_offset_all_lines_forward(buffer, min_line_number + 1, &line_offset); + } + + // Update lines add + 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); + + 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); + buffer16_raw_offset_all_lines_forward(buffer, nl + 1, &line_offset); + min_line_number = nl; + } else if (next_line_valid) { + line_offset += 1; + } + } + buffer16_raw_offset_all_lines_forward(buffer, nl, &line_offset); +} + +fn void buffer16_raw_validate_line_starts(buffer16_t *buffer) { + i64 line = 0; + for (i64 i = 0; i < buffer->len; i += 1) { + i64 l = buffer16_pos_to_line(buffer, i); + assert(l == line); + if (buffer->data[i] == L'\n') line += 1; + } +} + +fn void buffer16_raw_replace_text(buffer16_t *buffer, r1i64_t range, s16_t string) { + assert(range.max >= range.min); + assert(range.max >= 0 && range.max <= buffer->len); + assert(range.min >= 0 && range.min <= buffer->len); + buffer->flags.dirty = true; + buffer->change_id += 1; + + i64 size_to_remove = range.max - range.min; + i64 size_to_add = string.len; + i64 change_size = size_to_add - size_to_remove; + assert(change_size + buffer->len >= 0); + buffer16_raw_grow(buffer, change_size); + + i64 range_size = range.max - range.min; + u16 *begin_remove = buffer->data + range.min; + u16 *end_remove = begin_remove + range_size; + i64 remain_len = buffer->len - (range.min + range_size); + + buffer16_raw_update_lines(buffer, range, string); + + u16 *begin_add = begin_remove; + u16 *end_add = begin_add + string.len; + memory_move(end_add, end_remove, remain_len * sizeof(u16)); + memory_copy(begin_add, string.str, string.len * sizeof(u16)); + buffer->len = buffer->len + change_size; + +#if BUFFER_DEBUG + buffer16_raw_validate_line_starts(buffer); +#endif +} + +fn_test void buffer16_raw_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); + + 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, 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("\nnext line")); + assert(arr_len(buffer->line_starts) == 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(8, 12), s16_null); + assert(arr_len(buffer->line_starts) == 6); + + + ma_end_scratch(scratch); + + exit(0); +} \ No newline at end of file diff --git a/src/text_editor/text_editor_main.c b/src/text_editor/text_editor_main.c index 4bf643c..23f601a 100644 --- a/src/text_editor/text_editor_main.c +++ b/src/text_editor/text_editor_main.c @@ -1,12 +1,16 @@ #include "core/core.h" #include "os/os.h" #include "app/app.h" +#include "profiler/profiler.h" // #include "ui/ui.h" + #include "core/core.c" #include "os/os.c" #include "app/app.c" +#include "profiler/profiler.c" #include "render/render.c" +#include "text_editor_buffer.c" // #include "ui/ui.c" #include "text_editor.gen.c" @@ -21,42 +25,6 @@ * Win32 upload icon **/ -typedef struct caret_t caret_t; -struct caret_t { - i64 ifront; - union { - r1i64_t range; - i64 pos[2]; - }; -}; - -typedef struct xy_t xy_t; -struct xy_t { - i64 col; - i64 line; -}; - -typedef struct buffer_id_t buffer_id_t; -struct buffer_id_t { i64 e; }; - -typedef struct buffer_t buffer_t; -struct buffer_t { - buffer_id_t id; - - union { - s16_t string; - struct { - u16 *data; - i64 len; - }; - }; - i64 cap; - - i64 *line_starts; - i64 line_count; -}; - - fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) { tcx = thread_ctx; if (frame->first_event->kind == app_event_kind_init) { diff --git a/src/wasm_app/wasm_app.gen.c b/src/wasm_app/wasm_app.gen.c index 37bc685..ae50798 100644 --- a/src/wasm_app/wasm_app.gen.c +++ b/src/wasm_app/wasm_app.gen.c @@ -10,6 +10,6 @@ void run_all_tests(void) { test_string16(); test_hash_table(); test_intern_table(); - test_array(); + test_arr(); ui_test_text_replace(); }// run_all_tests()