buffer16 api work

This commit is contained in:
Krzosa Karol
2025-04-10 22:07:14 +02:00
parent 13295a2fcd
commit c8a4d19a72
13 changed files with 342 additions and 240 deletions

View File

@@ -62,6 +62,7 @@
#include "core_type_info.c" #include "core_type_info.c"
#include "core_hash.c" #include "core_hash.c"
#include "core_hash_table.c" #include "core_hash_table.c"
#include "core_array.c"
#ifndef DONT_INCLUDE_GENERATED_MATH #ifndef DONT_INCLUDE_GENERATED_MATH
#include "core_math.gen.c" #include "core_math.gen.c"
#endif #endif

View File

@@ -15,3 +15,4 @@
#include "core_platform.h" #include "core_platform.h"
#include "core_hash_table.h" #include "core_hash_table.h"
#include "core_ctx.h" #include "core_ctx.h"
#include "core_array.h"

View File

@@ -1,63 +1,3 @@
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, ...) (array__grow(arrcst(this), arrisz(this), 1), (this)->data[(this)->len++] = __VA_ARGS__)
#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) { fn void array__init(alo_t alo, array_void_t *this, size_t item_size, i64 count) {
assert(this->data == NULL); assert(this->data == NULL);
this->data = alloc_size(alo, item_size * count); this->data = alloc_size(alo, item_size * count);
@@ -112,7 +52,7 @@ fn void array__insert(array_void_t *this, size_t item_size, i64 idx) {
fn_test void test_array(void) { fn_test void test_array(void) {
ma_temp_t scratch = ma_begin_scratch(); ma_temp_t scratch = ma_begin_scratch();
{ {
array_i64_t arr = (array_i64_t){.alo = ma_temp_alo(scratch)}; array_i64_t arr = (array_i64_t){.alo = malot(scratch)};
for (int i = 0; i < 512; i += 1) { for (int i = 0; i < 512; i += 1) {
array_add(&arr, i); array_add(&arr, i);
} }
@@ -130,7 +70,7 @@ fn_test void test_array(void) {
} }
{ {
array_i64_t arr = (array_i64_t){.alo = ma_temp_alo(scratch)}; array_i64_t arr = (array_i64_t){.alo = malot(scratch)};
i64 *i = array_addn(&arr, 10); i64 *i = array_addn(&arr, 10);
assert(arr.len == 10 && arr.cap == 16); assert(arr.len == 10 && arr.cap == 16);
i64 *j = array_addn(&arr, 2); i64 *j = array_addn(&arr, 2);
@@ -140,7 +80,7 @@ fn_test void test_array(void) {
} }
{ {
array_i64_t arr = (array_i64_t){.alo = ma_temp_alo(scratch)}; array_i64_t arr = (array_i64_t){.alo = malot(scratch)};
for (int i = 0; i < 512; i += 1) { for (int i = 0; i < 512; i += 1) {
array_add(&arr, i); array_add(&arr, i);
} }
@@ -164,7 +104,7 @@ fn_test void test_array(void) {
assert(arr.data[3] == 3); assert(arr.data[3] == 3);
array_i64_t copy = {0}; array_i64_t copy = {0};
array_copy(ma_temp_alo(scratch), &copy, &arr); array_copy(malot(scratch), &copy, &arr);
for (i64 i = 0; i < arr.len; i += 1) { for (i64 i = 0; i < arr.len; i += 1) {
assert(arr.data[i] == copy.data[i]); assert(arr.data[i] == copy.data[i]);
} }
@@ -174,12 +114,3 @@ fn_test void test_array(void) {
ma_end_scratch(scratch); 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;
}

24
src/core/core_array.h Normal file
View File

@@ -0,0 +1,24 @@
#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, ...) (array__grow(arrcst(this), arrisz(this), 1), (this)->data[(this)->len++] = __VA_ARGS__)
#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);
fn void array__copy(alo_t alo, array_void_t *dst, array_void_t *src, size_t item_size);
fn void array__grow(array_void_t *this, size_t item_size, i64 item_count);
fn void array__del(array_void_t *this, size_t item_size, i64 idx, i64 count);
fn void array__insert(array_void_t *this, size_t item_size, i64 idx);

View File

@@ -12,3 +12,29 @@ fn void core_init(void) {
tcx = &global_thread_context; tcx = &global_thread_context;
os_core_init(); os_core_init();
} }
fn void *ma_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 malo(ma_arena_t *arena) {
return (alo_t){arena, ma_arena_alo_proc};
}
fn alo_t malot(ma_temp_t temp) {
return malo(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);
}

View File

@@ -19,4 +19,22 @@ enum {
tcx_slot_app, tcx_slot_app,
}; };
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);
};
#define alloc_type(alo, type) (type *)alloc_size((aloc), sizeof(type))
#define alloc_array(alo, type, count) (type *)alloc_size((alo), sizeof(type) * (count))
fn alo_t malo(ma_arena_t *arena);
fn alo_t malot(ma_temp_t temp);
fn void dealloc(alo_t alo, void *ptr);
fn void *alloc_size(alo_t alo, size_t size);
gb_thread thread_ctx_t *tcx; gb_thread thread_ctx_t *tcx;

View File

@@ -489,6 +489,15 @@ fn sb16_t s16_split(ma_arena_t *ma, s16_t string, s16_t find, s16_split_t flags)
return result; return result;
} }
fn s16_t s16_copy_ex(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;
}
fn_test void test_string16(void) { fn_test void test_string16(void) {
ma_temp_t scratch = ma_begin_scratch(); ma_temp_t scratch = ma_begin_scratch();

View File

@@ -1,8 +1,10 @@
void test_string16(void); void test_string16(void);
void test_hash_table(void); void test_hash_table(void);
void test_intern_table(void); void test_intern_table(void);
void test_array(void);
fn void run_tests(void) { fn void run_tests(void) {
test_string16(); test_string16();
test_hash_table(); test_hash_table();
test_intern_table(); test_intern_table();
test_array();
} }

View File

@@ -1,74 +1,57 @@
#define BUFFER_DEBUG 1 #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 array(edit16_t) array_edit16_t;
typedef array(caret_t) array_caret_t;
typedef struct history16_t history16_t;
struct history16_t {
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; };
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;
array_i64_t line_starts;
array_history16_t undo_stack;
array_history16_t redo_stack;
alo_t alo;
};
gb i64 buffer_raw_ids; gb i64 buffer_raw_ids;
const b32 dont_kill_selection = false;
const b32 kill_selection = true;
fn void buffer16_multi_cursor_apply_edits(buffer16_t *buffer, array_edit16_t edits);
///////////////////////////////
// caret helpers
///////////////////////////////
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 b32 carets_overlap(caret_t a, caret_t b) {
b32 result = r1i64_overlap(a.range, b.range);
return result;
}
/////////////////////////////// ///////////////////////////////
// buffer helpers // buffer helpers
@@ -181,47 +164,6 @@ fn r1i64_t buffer16_clamp_range(buffer16_t *buffer, r1i64_t range) {
return result; 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) { fn i64 buffer16_get_word_start(buffer16_t *buffer, i64 pos) {
pos = CLAMP(pos, (i64)0, buffer->len); pos = CLAMP(pos, (i64)0, buffer->len);
for (i64 i = pos - 1; i >= 0; i -= 1) { for (i64 i = pos - 1; i >= 0; i -= 1) {
@@ -257,7 +199,7 @@ fn i64 buffer16_get_next_word_end(buffer16_t *buffer, i64 pos) {
// semantics - proper max is one past last index // semantics - proper max is one past last index
if (!(i < buffer->len)) if (!(i < buffer->len))
break; break;
fn if (prev == L'\n' || (prev && prev != buffer->str[i]) || char16_is_word(buffer->str[i])) { if (prev == L'\n' || (prev && prev != buffer->str[i]) || char16_is_word(buffer->str[i])) {
break; break;
} }
prev = buffer->str[i]; prev = buffer->str[i];
@@ -271,7 +213,7 @@ fn i64 buffer16_get_prev_word_start(buffer16_t *buffer, i64 pos) {
u16 prev = 0; u16 prev = 0;
i64 i = pos - 1; i64 i = pos - 1;
for (; i >= 0; i -= 1) { for (; i >= 0; i -= 1) {
fn if (prev == L'\n' || (prev && prev != buffer->str[i]) || char16_is_word(buffer->str[i])) { if (prev == L'\n' || (prev && prev != buffer->str[i]) || char16_is_word(buffer->str[i])) {
break; break;
} }
pos = i; pos = i;
@@ -332,20 +274,6 @@ fn i64 buffer16_offset_pos_by_line(buffer16_t *buffer, i64 pos, i64 line_offset)
// raw buffer operations // raw buffer operations
/////////////////////////////// ///////////////////////////////
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->alo = alo;
buffer->cap = size;
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;
array_add(&buffer->line_starts, 0);
}
fn void buffer16_raw_grow(buffer16_t *buffer, i64 change_size) { fn void buffer16_raw_grow(buffer16_t *buffer, i64 change_size) {
i64 new_size = buffer->len + change_size; i64 new_size = buffer->len + change_size;
if (new_size > buffer->cap) { if (new_size > buffer->cap) {
@@ -453,6 +381,32 @@ fn void buffer16_raw_replace_text(buffer16_t *buffer, r1i64_t range, s16_t strin
#endif #endif
} }
///////////////////////////////
// buffer multicursor + history
///////////////////////////////
fn void buffer16_init(alo_t alo, buffer16_t *buffer, s8_t name, i64 size) {
buffer->id = (buffer16_id_t){++buffer_raw_ids};
buffer->name = name;
buffer->alo = alo;
buffer->cap = size;
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;
array_add(&buffer->line_starts, 0);
}
fn void buffer16_deinit(buffer16_t *buffer) {
// @todo: verify this works
array_dealloc(&buffer->line_starts);
dealloc(buffer->alo, buffer->data);
buffer16_dealloc_history_array(buffer, &buffer->undo_stack);
buffer16_dealloc_history_array(buffer, &buffer->redo_stack);
}
fn void buffer16_save_history_before_merge_cursor(buffer16_t *buffer, array_history16_t *stack, array_caret_t *carets) { fn void buffer16_save_history_before_merge_cursor(buffer16_t *buffer, array_history16_t *stack, array_caret_t *carets) {
if (!buffer->flags.history) { if (!buffer->flags.history) {
return; return;
@@ -473,24 +427,25 @@ fn void buffer16_save_history_before_apply_edits(buffer16_t *buffer, array_histo
// make reverse edits // make reverse edits
array_for(edit16_t, it, &entry->edits) { array_for(edit16_t, it, &entry->edits) {
it->range = (r1i64_t){it->range.min, it->range.min + it->string.len}; r1i64_t new_range = {it->range.min, it->range.min + it->string.len};
it->string = s16_alo_copy(buffer->alo, buffer16_get_string(buffer, it->range)); s16_t string = buffer16_get_string(buffer, it->range);
it->string = s16_copy_ex(buffer->alo, string);
it->range = new_range;
} }
ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object); ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object);
array_edit16_t temp_edit = {0}; array_edit16_t temp_edit = {0};
array_copy(ma_temp_alo(scratch), &temp_edit, &entry->edits); array_copy(malot(scratch), &temp_edit, &entry->edits);
// fix reverse edits // fix reverse edits
for (int i = 0; i < edits->len; i += 1) { array_for(edit16_t, edit, edits) {
edit16_t *it = edits->data + i; i64 remove_size = r1i64_size(edit->range);
i64 remove_size = r1i64_size(it->range); i64 insert_size = edit->string.len;
i64 insert_size = it->string.len;
i64 offset = insert_size - remove_size; i64 offset = insert_size - remove_size;
for (int j = 0; j < entry->edits.len; j += 1) { for (i64 i = 0; i < entry->edits.len; i += 1) {
edit16_t *new_edit = entry->edits.data + i; edit16_t *new_edit = entry->edits.data + i;
edit16_t *old_edit = temp_edit.data + i; edit16_t *old_edit = temp_edit.data + i;
if (old_edit->range.min > it->range.min) { if (old_edit->range.min > edit->range.min) {
new_edit->range.min += offset; new_edit->range.min += offset;
new_edit->range.max += offset; new_edit->range.max += offset;
} }
@@ -520,11 +475,12 @@ fn void buffer16_redo_edit(buffer16_t *buffer, array_caret_t *carets) {
} }
fn void buffer16_undo_edit(buffer16_t *buffer, array_caret_t *carets) { fn void buffer16_undo_edit(buffer16_t *buffer, array_caret_t *carets) {
if (!buffer->flags.history || buffer->redo_stack.len <= 0) { if (!buffer->flags.history || buffer->undo_stack.len <= 0) {
return; return;
} }
history16_t entry = array_pop(&buffer->undo_stack); history16_t entry = array_pop(&buffer->undo_stack);
buffer16_save_history_before_merge_cursor(buffer, &buffer->redo_stack, carets); buffer16_save_history_before_merge_cursor(buffer, &buffer->redo_stack, carets);
buffer16_save_history_before_apply_edits(buffer, &buffer->redo_stack, &entry.edits); buffer16_save_history_before_apply_edits(buffer, &buffer->redo_stack, &entry.edits);
buffer16_multi_cursor_apply_edits(buffer, entry.edits); buffer16_multi_cursor_apply_edits(buffer, entry.edits);
@@ -557,10 +513,6 @@ fn void buffer16_dealloc_history_entries(buffer16_t *buffer, array_history16_t *
} }
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) { fn void buffer16_dealloc_history_array(buffer16_t *buffer, array_history16_t *entries) {
buffer16_dealloc_history_entries(buffer, entries); buffer16_dealloc_history_entries(buffer, entries);
array_dealloc(entries); array_dealloc(entries);
@@ -575,7 +527,7 @@ fn array_edit16_t buffer16_begin_edit(alo_t alo, buffer16_t *buffer, array_caret
buffer->edit_phase += 1; buffer->edit_phase += 1;
assert(carets->len); assert(carets->len);
buffer16_save_history_before_merge_cursor(buffer, &buffer->undo_stack, carets); buffer16_save_history_before_merge_cursor(buffer, &buffer->undo_stack, carets);
buffer16_clear_redo_stack(buffer); buffer16_dealloc_history_entries(buffer, &buffer->redo_stack);
} }
array_edit16_t edits = (array_edit16_t){.alo = alo}; array_edit16_t edits = (array_edit16_t){.alo = alo};
return edits; return edits;
@@ -596,7 +548,7 @@ fn void buffer16_adjust_carets(array_edit16_t *edits, array_caret_t *carets) {
assert(edits->alo.object == carets->alo.object); assert(edits->alo.object == carets->alo.object);
array_caret_t new_carets = {0}; array_caret_t new_carets = {0};
array_copy(ma_temp_alo(scratch), &new_carets, carets); array_copy(malot(scratch), &new_carets, carets);
array_for(edit16_t, it, edits) { array_for(edit16_t, it, edits) {
i64 remove_size = r1i64_size(it->range); i64 remove_size = r1i64_size(it->range);
i64 insert_size = it->string.len; i64 insert_size = it->string.len;
@@ -643,7 +595,7 @@ fn void buffer16_end_edit(buffer16_t *buffer, array_edit16_t *edits, array_caret
assert(buffer->alo.object == edits->alo.object); assert(buffer->alo.object == edits->alo.object);
array_caret_t new_carets = {0}; array_caret_t new_carets = {0};
array_copy(ma_temp_alo(scratch), &new_carets, carets); array_copy(malot(scratch), &new_carets, carets);
array_for(edit16_t, it, edits) { array_for(edit16_t, it, edits) {
i64 remove_size = r1i64_size(it->range); i64 remove_size = r1i64_size(it->range);
i64 insert_size = it->string.len; i64 insert_size = it->string.len;
@@ -679,7 +631,7 @@ fn void buffer16_end_edit(buffer16_t *buffer, array_edit16_t *edits, array_caret
ma_end_scratch(scratch); ma_end_scratch(scratch);
} }
void caret_merge_sort(i64 Count, caret_t *First, caret_t *Temp) { fn void caret_merge_sort(i64 Count, caret_t *First, caret_t *Temp) {
// SortKey = range.min // SortKey = range.min
if (Count == 1) { if (Count == 1) {
// NOTE(casey): No work to do. // NOTE(casey): No work to do.
@@ -733,7 +685,7 @@ void caret_merge_sort(i64 Count, caret_t *First, caret_t *Temp) {
} }
} }
void edit16_merge_sort(i64 Count, edit16_t *First, edit16_t *Temp) { fn void edit16_merge_sort(i64 Count, edit16_t *First, edit16_t *Temp) {
// SortKey = range.min // SortKey = range.min
if (Count == 1) { if (Count == 1) {
// NOTE(casey): No work to do. // NOTE(casey): No work to do.
@@ -814,7 +766,7 @@ fn void buffer16_multi_cursor_apply_edits(buffer16_t *buffer, array_edit16_t edi
{ {
ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object); ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object);
array_edit16_t edits_copy = {0}; array_edit16_t edits_copy = {0};
array_copy(ma_temp_alo(scratch), &edits_copy, &edits); array_copy(malot(scratch), &edits_copy, &edits);
if (edits.len > 1) edit16_merge_sort(edits.len, edits_copy.data, edits.data); if (edits.len > 1) edit16_merge_sort(edits.len, edits_copy.data, edits.data);
edits = edits_copy; edits = edits_copy;
ma_end_scratch(scratch); ma_end_scratch(scratch);
@@ -854,7 +806,7 @@ fn void buffer16_merge_carets(buffer16_t *buffer, array_caret_t *carets) {
ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object); ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object);
array_caret_t c1 = {0}; array_caret_t c1 = {0};
array_copy(ma_temp_alo(scratch), &c1, carets); array_copy(malot(scratch), &c1, carets);
if (carets->len > 1) { if (carets->len > 1) {
caret_merge_sort(carets->len, c1.data, carets->data); caret_merge_sort(carets->len, c1.data, carets->data);
} }
@@ -887,7 +839,7 @@ fn_test void buffer16_test(void) {
{ {
s16_t string = s16("thing itself"); s16_t string = s16("thing itself");
buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t); buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t);
buffer16_raw_init(ma_temp_alo(scratch), buffer, S8_FILE_AND_LINE, 16); buffer16_init(malot(scratch), buffer, S8_FILE_AND_LINE, 16);
buffer16_raw_replace_text(buffer, r1i64_null, string); buffer16_raw_replace_text(buffer, r1i64_null, string);
assert(s16_are_equal(buffer->string, string)); assert(s16_are_equal(buffer->string, string));
assert(buffer->cap == 16); assert(buffer->cap == 16);
@@ -919,7 +871,7 @@ fn_test void buffer16_test(void) {
{ {
s16_t string = s16("thing itself_and meme"); s16_t string = s16("thing itself_and meme");
buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t); buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t);
buffer16_raw_init(ma_temp_alo(scratch), buffer, S8_FILE_AND_LINE, 10); buffer16_init(malot(scratch), buffer, S8_FILE_AND_LINE, 10);
buffer16_raw_replace_text(buffer, r1i64_null, string); buffer16_raw_replace_text(buffer, r1i64_null, string);
r1i64_t range = buffer16_enclose_word(buffer, 18); r1i64_t range = buffer16_enclose_word(buffer, 18);
buffer16_raw_replace_text(buffer, range, s16("cat")); buffer16_raw_replace_text(buffer, range, s16("cat"));
@@ -934,7 +886,7 @@ fn_test void buffer16_test(void) {
{ {
buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t); buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t);
buffer16_raw_init(ma_temp_alo(scratch), buffer, S8_FILE_AND_LINE, 16); buffer16_init(malot(scratch), buffer, S8_FILE_AND_LINE, 16);
for (int i = 0; i < 16; i += 1) { for (int i = 0; i < 16; i += 1) {
buffer16_raw_replace_text(buffer, r1i64_null, s16("line of memes\n")); buffer16_raw_replace_text(buffer, r1i64_null, s16("line of memes\n"));
} }
@@ -942,14 +894,15 @@ fn_test void buffer16_test(void) {
} }
{ {
s16_t string = s16("some thing or another and stuff like that");
s16_t meme_string = s16("meme meme or another and stuff like that");
buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t); buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t);
buffer16_raw_init(ma_temp_alo(scratch), buffer, S8_FILE_AND_LINE, 128); buffer16_init(malot(scratch), buffer, S8_FILE_AND_LINE, 128);
buffer16_raw_replace_text(buffer, r1i64_null, s16("some thing or another and stuff like that")); buffer16_raw_replace_text(buffer, r1i64_null, string);
{ {
array_caret_t carets = {0}; array_caret_t carets = {0};
array_init(ma_temp_alo(scratch), &carets, 32); array_init(malot(scratch), &carets, 32);
array_add(&carets, caret_make(0, 4)); array_add(&carets, caret_make(0, 4));
array_add(&carets, caret_make(3, 8)); array_add(&carets, caret_make(3, 8));
array_add(&carets, caret_make(3, 8)); array_add(&carets, caret_make(3, 8));
@@ -964,12 +917,12 @@ fn_test void buffer16_test(void) {
{ {
array_caret_t carets = {0}; array_caret_t carets = {0};
array_init(ma_temp_alo(scratch), &carets, 32); array_init(malot(scratch), &carets, 32);
array_add(&carets, caret_make(0, 4)); array_add(&carets, caret_make(0, 4));
array_add(&carets, caret_make(0, 3)); array_add(&carets, caret_make(0, 3));
array_add(&carets, caret_make(5, 10)); array_add(&carets, caret_make(5, 10));
array_edit16_t edits = buffer16_begin_edit(ma_temp_alo(scratch), buffer, &carets); array_edit16_t edits = buffer16_begin_edit(malot(scratch), buffer, &carets);
buffer16_merge_carets(buffer, &carets); buffer16_merge_carets(buffer, &carets);
assert(carets.len == 2); assert(carets.len == 2);
@@ -979,7 +932,14 @@ fn_test void buffer16_test(void) {
} }
buffer16_end_edit(buffer, &edits, &carets, kill_selection); buffer16_end_edit(buffer, &edits, &carets, kill_selection);
assert(s16_are_equal(buffer->string, s16("meme meme or another and stuff like that"))); assert(s16_are_equal(buffer->string, meme_string));
for (i32 i = 0; i < 32; i += 1) {
buffer16_undo_edit(buffer, &carets);
assert(s16_are_equal(buffer->string, string));
buffer16_redo_edit(buffer, &carets);
assert(s16_are_equal(buffer->string, meme_string));
}
} }
} }

129
src/text_editor/buffer16.h Normal file
View File

@@ -0,0 +1,129 @@
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 array(edit16_t) array_edit16_t;
typedef array(caret_t) array_caret_t;
typedef struct history16_t history16_t;
struct history16_t {
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; };
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;
array_i64_t line_starts;
array_history16_t undo_stack;
array_history16_t redo_stack;
alo_t alo;
};
const b32 dont_kill_selection = false;
const b32 kill_selection = true;
///////////////////////////////
// caret helpers
fn caret_t caret_make(i64 front, i64 back);
fn i64 caret_get_front(caret_t caret);
fn i64 caret_get_back(caret_t caret);
fn caret_t caret_set_front(caret_t caret, i64 pos);
fn caret_t caret_set_back(caret_t caret, i64 pos);
fn b32 carets_are_equal(caret_t a, caret_t b);
fn b32 carets_overlap(caret_t a, caret_t b);
///////////////////////////////
// buffer helpers
fn i64 buffer16_pos_to_line(buffer16_t *buffer, i64 pos);
fn r1i64_t buffer16_get_line_range(buffer16_t *buffer, i64 line);
fn r1i64_t buffer16_get_line_range_full(buffer16_t *buffer, i64 line, i64 *eof);
fn xy_t buffer16_pos_to_xy(buffer16_t *buffer, i64 pos);
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);
fn s16_t buffer16_get_string(buffer16_t *buffer, r1i64_t range);
fn s16_t buffer16_get_line_string(buffer16_t *buffer, i64 line);
fn r1i64_t buffer16_get_range_end(buffer16_t *buffer);
fn r1i64_t buffer16_get_range(buffer16_t *buffer);
fn i64 buffer16_clamp_pos(buffer16_t *buffer, i64 pos);
fn r1i64_t buffer16_clamp_range(buffer16_t *buffer, r1i64_t range);
fn i64 buffer16_get_word_start(buffer16_t *buffer, i64 pos);
fn i64 buffer16_get_word_end(buffer16_t *buffer, i64 pos);
fn i64 buffer16_get_next_word_end(buffer16_t *buffer, i64 pos);
fn i64 buffer16_get_prev_word_start(buffer16_t *buffer, i64 pos);
fn i64 buffer16_get_line_start(buffer16_t *buffer, i64 pos, i64 *eof);
fn i64 buffer16_get_line_end(buffer16_t *buffer, i64 pos, i64 *eof);
fn i64 buffer16_get_line_start_full(buffer16_t *buffer, i64 pos);
fn i64 buffer16_get_line_end_full(buffer16_t *buffer, i64 pos);
fn r1i64_t buffer16_enclose_word(buffer16_t *buffer, i64 pos);
fn u16 buffer16_get_char(buffer16_t *buffer, i64 pos);
fn i64 buffer16_offset_pos_by_line(buffer16_t *buffer, i64 pos, i64 line_offset);
///////////////////////////////
// buffer raw textural operations. no history
fn void buffer16_raw_grow(buffer16_t *buffer, i64 change_size);
fn void buffer16_raw_replace_text(buffer16_t *buffer, r1i64_t range, s16_t string);
///////////////////////////////
// buffer multicursor + history
fn void buffer16_init(alo_t alo, buffer16_t *buffer, s8_t name, i64 size);
fn void buffer16_deinit(buffer16_t *buffer);
fn void buffer16_add_edit(array_edit16_t *edits, r1i64_t range, s16_t string);
fn array_edit16_t buffer16_begin_edit(alo_t alo, buffer16_t *buffer, array_caret_t *carets);
fn void buffer16_end_edit(buffer16_t *buffer, array_edit16_t *edits, array_caret_t *carets, b32 kill_selection);
fn void buffer16_merge_carets(buffer16_t *buffer, array_caret_t *carets);
fn void buffer16_adjust_carets(array_edit16_t *edits, array_caret_t *carets);
fn void buffer16_redo_edit(buffer16_t *buffer, array_caret_t *carets);
fn void buffer16_undo_edit(buffer16_t *buffer, array_caret_t *carets);
fn void buffer16_save_history_before_merge_cursor(buffer16_t *buffer, array_history16_t *stack, array_caret_t *carets);
fn void buffer16_save_history_before_apply_edits(buffer16_t *buffer, array_history16_t *stack, array_edit16_t *edits);
fn void buffer16_pre_begin_edit_save_caret_history(buffer16_t *buffer, array_caret_t *carets);
fn void buffer16_dealloc_history_entries(buffer16_t *buffer, array_history16_t *entries);
fn void buffer16_dealloc_history_array(buffer16_t *buffer, array_history16_t *entries);
fn void buffer16_multi_cursor_apply_edits(buffer16_t *buffer, array_edit16_t edits);

View File

@@ -2,6 +2,7 @@
#include "os/os.h" #include "os/os.h"
#include "app/app.h" #include "app/app.h"
#include "profiler/profiler.h" #include "profiler/profiler.h"
#include "buffer16.h"
// #include "ui/ui.h" // #include "ui/ui.h"
@@ -10,8 +11,7 @@
#include "app/app.c" #include "app/app.c"
#include "profiler/profiler.c" #include "profiler/profiler.c"
#include "render/render.c" #include "render/render.c"
#include "core_array.c" #include "buffer16.c"
#include "text_editor_buffer.c"
// #include "ui/ui.c" // #include "ui/ui.c"
#include "text_editor.gen.c" #include "text_editor.gen.c"

View File

@@ -367,7 +367,7 @@ fn ui_signal_t ui_signal_from_box(ui_box_t *box) {
} }
fn v2f32_t ui_aligned_text_pos(f32 offset, ui_text_align_t text_align, r2f32_t rect, s8_t string, v2f32_t string_size) { fn v2f32_t ui_aligned_text_pos(f32 offset, ui_text_align_t text_align, r2f32_t rect, s8_t string, v2f32_t string_size) {
v2f32_t rect_size = r2f32_get_size(rect); v2f32_t rect_size = r2f32_size(rect);
v2f32_t rect_string_diff = v2f32_sub(rect_size, string_size); v2f32_t rect_string_diff = v2f32_sub(rect_size, string_size);
v2f32_t center_pos = v2f32_divs(rect_string_diff, 2); v2f32_t center_pos = v2f32_divs(rect_string_diff, 2);
v2f32_t pos_in_rect = v2f32(0, center_pos.y); v2f32_t pos_in_rect = v2f32(0, center_pos.y);
@@ -390,7 +390,7 @@ struct ui_draw_compute_t {
fn r2f32_t ui_get_appear_rect(ui_box_t *box) { fn r2f32_t ui_get_appear_rect(ui_box_t *box) {
r2f32_t result = box->rect; r2f32_t result = box->rect;
if (box->flags.animate_appear && !ui_id_is_null(box->id)) { if (box->flags.animate_appear && !ui_id_is_null(box->id)) {
v2f32_t size = v2f32_muls(r2f32_get_size(result), 0.15f); v2f32_t size = v2f32_muls(r2f32_size(result), 0.15f);
r2f32_t smaller_rect = r2f32_shrink(result, size); r2f32_t smaller_rect = r2f32_shrink(result, size);
f32 appear_t = f32_ease_out_n(f32_clamp01(box->appear_t * 2), 10); f32 appear_t = f32_ease_out_n(f32_clamp01(box->appear_t * 2), 10);
result = r2f32_lerp(smaller_rect, result, appear_t); result = r2f32_lerp(smaller_rect, result, appear_t);
@@ -634,7 +634,7 @@ fn void ui_tree_table_begin(ui_code_loc_t loc) {
fn void ui_tree_table_end(void) { fn void ui_tree_table_end(void) {
ui_box_t *box = ui_pop_top(); ui_box_t *box = ui_pop_top();
ui_set_children_sums(box); ui_set_children_sums(box);
ui_next_rect(ui_top_lop(), ui_top_rectp(), r2f32_get_size(box->rect)); ui_next_rect(ui_top_lop(), ui_top_rectp(), r2f32_size(box->rect));
} }
#define ui_tree_table_expandable(...) defer_if (ui_tree_table_push_expandable(UILOC, __VA_ARGS__).clicked, ui_tree_table_pop_expandable()) #define ui_tree_table_expandable(...) defer_if (ui_tree_table_push_expandable(UILOC, __VA_ARGS__).clicked, ui_tree_table_pop_expandable())
@@ -998,7 +998,7 @@ struct ui_scroller_t {
fn void ui_scroller_calc_vertical(ui_scroller_t s) { fn void ui_scroller_calc_vertical(ui_scroller_t s) {
ui_set_top(s.verti.box) { ui_set_top(s.verti.box) {
f32 scroller_rect_pixels = r2f32_get_size(s.verti.box->rect).y; f32 scroller_rect_pixels = r2f32_size(s.verti.box->rect).y;
f32 all_items_size = s.p.verti.max_size; f32 all_items_size = s.p.verti.max_size;
f32 scroller_size = f32_clamp01(s.verti.item_box_pixels / (all_items_size + s.verti.item_box_pixels)); f32 scroller_size = f32_clamp01(s.verti.item_box_pixels / (all_items_size + s.verti.item_box_pixels));
f32 scrollable_space = (1 - scroller_size); f32 scrollable_space = (1 - scroller_size);
@@ -1023,7 +1023,7 @@ fn void ui_scroller_calc_vertical(ui_scroller_t s) {
} }
if (upper_box_signal.dragging || down_box_signal.dragging) { if (upper_box_signal.dragging || down_box_signal.dragging) {
s.p.verti.value[0] = (ev->mouse_pos.y - upper_box->rect.min.y) * coef; s.p.verti.value[0] = (ev->mouse_pos.y - upper_box->rect.min.y) * coef;
s.p.verti.value[0] -= (r2f32_get_size(slider_box->rect).y / 2) * coef; s.p.verti.value[0] -= (r2f32_size(slider_box->rect).y / 2) * coef;
} }
if (ev->kind == app_event_kind_mouse_wheel) { if (ev->kind == app_event_kind_mouse_wheel) {
s.p.verti.value[0] -= ev->mouse_wheel_delta.y; s.p.verti.value[0] -= ev->mouse_wheel_delta.y;
@@ -1038,7 +1038,7 @@ fn ui_scroller_t ui_begin_scroller(ui_code_loc_t loc, ui_scroller_params_t p) {
if (p.verti.enabled) { if (p.verti.enabled) {
s.verti.box = ui_box(.loc = loc, .rect = r2f32_cut_right(&p.parent->rect, ui_dm(0.5f)), .flags = {.draw_rect = true}); s.verti.box = ui_box(.loc = loc, .rect = r2f32_cut_right(&p.parent->rect, ui_dm(0.5f)), .flags = {.draw_rect = true});
r2f32_cut_bottom(&s.verti.box->rect, ui_dm(0.5f)); r2f32_cut_bottom(&s.verti.box->rect, ui_dm(0.5f));
s.verti.item_box_pixels = r2f32_get_size(p.parent->rect).y; s.verti.item_box_pixels = r2f32_size(p.parent->rect).y;
if (p.verti.item_count) { if (p.verti.item_count) {
s.p.verti.max_size = s.p.verti.item_pixels * s.p.verti.item_count; s.p.verti.max_size = s.p.verti.item_pixels * s.p.verti.item_count;
ui_scroller_calc_vertical(s); ui_scroller_calc_vertical(s);
@@ -1077,7 +1077,7 @@ fn void ui_end_scroller(ui_scroller_t s) {
if (s.p.hori.enabled) { if (s.p.hori.enabled) {
ui_set_top(s.hori.box) { ui_set_top(s.hori.box) {
f32 scroller_rect_pixels = r2f32_get_size(s.hori.box->rect).x; f32 scroller_rect_pixels = r2f32_size(s.hori.box->rect).x;
f32 scroller_button_size_norm = f32_clamp01(scroller_rect_pixels / s.p.hori.max_size); f32 scroller_button_size_norm = f32_clamp01(scroller_rect_pixels / s.p.hori.max_size);
f32 scrollable_space = (1.f - scroller_button_size_norm); f32 scrollable_space = (1.f - scroller_button_size_norm);
@@ -1101,7 +1101,7 @@ fn void ui_end_scroller(ui_scroller_t s) {
} }
if (left_box_sig.dragging || right_box_sig.dragging) { if (left_box_sig.dragging || right_box_sig.dragging) {
s.p.hori.value[0] = (ev->mouse_pos.x - left_box->rect.min.x) * coef; s.p.hori.value[0] = (ev->mouse_pos.x - left_box->rect.min.x) * coef;
s.p.hori.value[0] -= (r2f32_get_size(slider_box->rect).x / 2) * coef; s.p.hori.value[0] -= (r2f32_size(slider_box->rect).x / 2) * coef;
} }
if (ev->kind == app_event_kind_mouse_wheel) { if (ev->kind == app_event_kind_mouse_wheel) {
s.p.hori.value[0] -= ev->mouse_wheel_delta.x; s.p.hori.value[0] -= ev->mouse_wheel_delta.x;
@@ -1399,7 +1399,7 @@ fn void ui_demo_update(app_frame_t *frame, mt_tweak_t *tweak_table, i32 tweak_co
ui_serial_type(UILOC, &ui_test_event, type(app_event_t)); ui_serial_type(UILOC, &ui_test_event, type(app_event_t));
r2f32_t scroll_rect = r2f32_fix(ui_top_rect()); r2f32_t scroll_rect = r2f32_fix(ui_top_rect());
scroller.p.verti.max_size = r2f32_get_size(scroll_rect).y; scroller.p.verti.max_size = r2f32_size(scroll_rect).y;
} }
ui_end_scroller(scroller); ui_end_scroller(scroller);

View File

@@ -10,5 +10,6 @@ void run_all_tests(void) {
test_string16(); test_string16();
test_hash_table(); test_hash_table();
test_intern_table(); test_intern_table();
test_array();
ui_test_text_replace(); ui_test_text_replace();
}// run_all_tests() }// run_all_tests()