text editor
This commit is contained in:
185
src/text_editor/core_array.c
Normal file
185
src/text_editor/core_array.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user