text editor
This commit is contained in:
@@ -170,3 +170,8 @@ fn ma_temp_t ma_begin_scratch1(ma_arena_t *conflict) {
|
|||||||
ma_arena_t *conflicts[] = {conflict};
|
ma_arena_t *conflicts[] = {conflict};
|
||||||
return ma_begin_scratch_ex(conflicts, 1);
|
return ma_begin_scratch_ex(conflicts, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ma_temp_t ma_begin_scratch2(ma_arena_t *a, ma_arena_t *b) {
|
||||||
|
ma_arena_t *conflicts[] = {a, b};
|
||||||
|
return ma_begin_scratch_ex(conflicts, 2);
|
||||||
|
}
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ union convert_f32_i32_t { f32 f; i32 i; };
|
|||||||
#define PASTE_(a, b) a##b
|
#define PASTE_(a, b) a##b
|
||||||
#define PASTE(a, b) PASTE_(a, b)
|
#define PASTE(a, b) PASTE_(a, b)
|
||||||
#define SWAP(t, a, b) do { t PASTE(temp__, __LINE__) = a; a = b; b = PASTE(temp__, __LINE__); } while(0)
|
#define SWAP(t, a, b) do { t PASTE(temp__, __LINE__) = a; a = b; b = PASTE(temp__, __LINE__); } while(0)
|
||||||
|
#define SWAP_PTR(t, a, b) do { t PASTE(temp__, __LINE__) = *a; *a = *b; *b = PASTE(temp__, __LINE__); } while(0)
|
||||||
#define CODE(...) #__VA_ARGS__
|
#define CODE(...) #__VA_ARGS__
|
||||||
|
|
||||||
#if PLATFORM_CL || (PLATFORM_CLANG && PLATFORM_WINDOWS)
|
#if PLATFORM_CL || (PLATFORM_CLANG && PLATFORM_WINDOWS)
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ typedef array(i64) array_i64_t;
|
|||||||
#define arrisz(x) sizeof((x)->data[0])
|
#define arrisz(x) sizeof((x)->data[0])
|
||||||
#define arrcst(x) ((array_void_t *)(x))
|
#define arrcst(x) ((array_void_t *)(x))
|
||||||
#define array_init(a, this, count) (array__init((a), arrcst(this), arrisz(this), (count)))
|
#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_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_pop(this) (assert_expr((this)->len > 0), (this)->data[--(this)->len])
|
||||||
#define array_dealloc(this) (dealloc((this)->alo, (this)->data))
|
#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_addn(this, n) (array__grow(arrcst(this), arrisz(this), (n)), (this)->len += (n), &(this)->data[(this)->len - (n)])
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ struct buffer16_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
// buffer helpers
|
// buffer helpers
|
||||||
@@ -468,8 +472,7 @@ fn void buffer16_save_history_before_apply_edits(buffer16_t *buffer, array_histo
|
|||||||
array_copy(buffer->alo, &entry->edits, edits);
|
array_copy(buffer->alo, &entry->edits, edits);
|
||||||
|
|
||||||
// make reverse edits
|
// make reverse edits
|
||||||
for (i64 i = 0; i < edits->len; i += 1) {
|
array_for(edit16_t, it, &entry->edits) {
|
||||||
edit16_t *it = edits->data + i;
|
|
||||||
it->range = (r1i64_t){it->range.min, it->range.min + it->string.len};
|
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));
|
it->string = s16_alo_copy(buffer->alo, buffer16_get_string(buffer, it->range));
|
||||||
}
|
}
|
||||||
@@ -505,7 +508,7 @@ fn void buffer16_redo_edit(buffer16_t *buffer, array_caret_t *carets) {
|
|||||||
history16_t entry = array_pop(&buffer->redo_stack);
|
history16_t entry = array_pop(&buffer->redo_stack);
|
||||||
buffer16_save_history_before_merge_cursor(buffer, &buffer->undo_stack, carets);
|
buffer16_save_history_before_merge_cursor(buffer, &buffer->undo_stack, carets);
|
||||||
buffer16_save_history_before_apply_edits(buffer, &buffer->undo_stack, &entry.edits);
|
buffer16_save_history_before_apply_edits(buffer, &buffer->undo_stack, &entry.edits);
|
||||||
// buffer16_multi_cursor_apply_edits(buffer, entry.edits); // @todo
|
buffer16_multi_cursor_apply_edits(buffer, entry.edits);
|
||||||
|
|
||||||
array_dealloc(carets);
|
array_dealloc(carets);
|
||||||
*carets = entry.carets;
|
*carets = entry.carets;
|
||||||
@@ -514,15 +517,32 @@ fn void buffer16_redo_edit(buffer16_t *buffer, array_caret_t *carets) {
|
|||||||
dealloc(buffer->alo, it->string.str);
|
dealloc(buffer->alo, it->string.str);
|
||||||
}
|
}
|
||||||
array_dealloc(&entry.edits);
|
array_dealloc(&entry.edits);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void buffer16_apply_edits(buffer16_t *buffer, array_edit16_t *edits) {
|
fn void buffer16_undo_edit(buffer16_t *buffer, array_caret_t *carets) {
|
||||||
|
if (!buffer->flags.history || buffer->redo_stack.len <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
history16_t entry = array_pop(&buffer->undo_stack);
|
||||||
|
buffer16_save_history_before_merge_cursor(buffer, &buffer->redo_stack, carets);
|
||||||
|
buffer16_save_history_before_apply_edits(buffer, &buffer->redo_stack, &entry.edits);
|
||||||
|
buffer16_multi_cursor_apply_edits(buffer, entry.edits);
|
||||||
|
|
||||||
|
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);
|
assert(buffer->edit_phase == 1);
|
||||||
buffer->edit_phase += 1;
|
buffer->edit_phase += 1;
|
||||||
buffer16_save_history_before_apply_edits(buffer, &buffer->undo_stack, edits);
|
buffer16_save_history_before_apply_edits(buffer, &buffer->undo_stack, edits);
|
||||||
// buffer16_multi_cursor_apply_edits(buffer, entry.edits); // @todo
|
buffer16_multi_cursor_apply_edits(buffer, *edits);
|
||||||
// @todo: ...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void buffer16_dealloc_history_entries(buffer16_t *buffer, array_history16_t *entries) {
|
fn void buffer16_dealloc_history_entries(buffer16_t *buffer, array_history16_t *entries) {
|
||||||
@@ -571,6 +591,296 @@ fn void caret_assert_ranges(array_caret_t carets) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn void buffer16_adjust_carets(array_edit16_t *edits, array_caret_t *carets) {
|
||||||
|
ma_temp_t scratch = ma_begin_scratch1(edits->alo.object);
|
||||||
|
assert(edits->alo.object == carets->alo.object);
|
||||||
|
|
||||||
|
array_caret_t new_carets = {0};
|
||||||
|
array_copy(ma_temp_alo(scratch), &new_carets, carets);
|
||||||
|
array_for(edit16_t, it, edits) {
|
||||||
|
i64 remove_size = r1i64_size(it->range);
|
||||||
|
i64 insert_size = it->string.len;
|
||||||
|
i64 offset = insert_size - remove_size;
|
||||||
|
for (i64 i = 0; i < carets->len; i += 1) {
|
||||||
|
caret_t *old_caret = carets->data + i;
|
||||||
|
caret_t *new_caret = new_carets.data + i;
|
||||||
|
|
||||||
|
if (old_caret->range.min > it->range.min) {
|
||||||
|
new_caret->range.min += offset;
|
||||||
|
new_caret->range.max += offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i64 i = 0; i < carets->len; i += 1) {
|
||||||
|
carets->data[i] = new_carets.data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_end_scratch(scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void buffer16_end_edit(buffer16_t *buffer, array_edit16_t *edits, array_caret_t *carets, b32 kill_selection) {
|
||||||
|
buffer16__apply_edits(buffer, edits);
|
||||||
|
|
||||||
|
assert(buffer->edit_phase == 2);
|
||||||
|
buffer->edit_phase -= 1;
|
||||||
|
|
||||||
|
#if BUFFER_DEBUG
|
||||||
|
if (buffer->flags.history) {
|
||||||
|
history16_t *entry = &array_last(&buffer->undo_stack);
|
||||||
|
assert(entry->carets.len);
|
||||||
|
assert(entry->edits.len);
|
||||||
|
for (i64 i = 0; i < edits->len - 1; i += 1) {
|
||||||
|
assert(edits->data[i].range.min <= edits->data[i + 1].range.min);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Adjust carets
|
||||||
|
// this one also moves the carets forward if they are aligned with edit
|
||||||
|
ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object);
|
||||||
|
assert(buffer->alo.object == carets->alo.object);
|
||||||
|
assert(buffer->alo.object == edits->alo.object);
|
||||||
|
|
||||||
|
array_caret_t new_carets = {0};
|
||||||
|
array_copy(ma_temp_alo(scratch), &new_carets, carets);
|
||||||
|
array_for(edit16_t, it, edits) {
|
||||||
|
i64 remove_size = r1i64_size(it->range);
|
||||||
|
i64 insert_size = it->string.len;
|
||||||
|
i64 offset = insert_size - remove_size;
|
||||||
|
|
||||||
|
for (i64 i = 0; i < carets->len; i += 1) {
|
||||||
|
caret_t *old_caret = carets->data + i;
|
||||||
|
caret_t *new_caret = new_carets.data + i;
|
||||||
|
|
||||||
|
if (old_caret->range.min == it->range.min) {
|
||||||
|
new_caret->range.min += insert_size;
|
||||||
|
} else if (old_caret->range.min > it->range.min) {
|
||||||
|
new_caret->range.min += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_caret->range.max == it->range.max) {
|
||||||
|
new_caret->range.max += insert_size;
|
||||||
|
} else if (old_caret->range.max > it->range.max) {
|
||||||
|
new_caret->range.max += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(new_caret->range.max >= new_caret->range.min);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i64 i = 0; i < carets->len; i += 1) {
|
||||||
|
carets->data[i] = new_carets.data[i];
|
||||||
|
if (kill_selection) {
|
||||||
|
carets->data[i].range.max = carets->data[i].range.min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_end_scratch(scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void caret_merge_sort(i64 Count, caret_t *First, caret_t *Temp) {
|
||||||
|
// SortKey = range.min
|
||||||
|
if (Count == 1) {
|
||||||
|
// NOTE(casey): No work to do.
|
||||||
|
} else if (Count == 2) {
|
||||||
|
caret_t *EntryA = First;
|
||||||
|
caret_t *EntryB = First + 1;
|
||||||
|
if (EntryA->range.min > EntryB->range.min) {
|
||||||
|
SWAP_PTR(caret_t, EntryA, EntryB);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i64 Half0 = Count / 2;
|
||||||
|
i64 Half1 = Count - Half0;
|
||||||
|
|
||||||
|
assert(Half0 >= 1);
|
||||||
|
assert(Half1 >= 1);
|
||||||
|
|
||||||
|
caret_t *InHalf0 = First;
|
||||||
|
caret_t *InHalf1 = First + Half0;
|
||||||
|
caret_t *End = First + Count;
|
||||||
|
|
||||||
|
caret_merge_sort(Half0, InHalf0, Temp);
|
||||||
|
caret_merge_sort(Half1, InHalf1, Temp);
|
||||||
|
|
||||||
|
caret_t *ReadHalf0 = InHalf0;
|
||||||
|
caret_t *ReadHalf1 = InHalf1;
|
||||||
|
|
||||||
|
caret_t *Out = Temp;
|
||||||
|
for (i64 Index = 0;
|
||||||
|
Index < Count;
|
||||||
|
++Index) {
|
||||||
|
if (ReadHalf0 == InHalf1) {
|
||||||
|
*Out++ = *ReadHalf1++;
|
||||||
|
} else if (ReadHalf1 == End) {
|
||||||
|
*Out++ = *ReadHalf0++;
|
||||||
|
} else if (ReadHalf0->range.min < ReadHalf1->range.min) {
|
||||||
|
*Out++ = *ReadHalf0++;
|
||||||
|
} else {
|
||||||
|
*Out++ = *ReadHalf1++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(Out == (Temp + Count));
|
||||||
|
assert(ReadHalf0 == InHalf1);
|
||||||
|
assert(ReadHalf1 == End);
|
||||||
|
|
||||||
|
// TODO(casey): Not really necessary if we ping-pong
|
||||||
|
for (i64 Index = 0;
|
||||||
|
Index < Count;
|
||||||
|
++Index) {
|
||||||
|
First[Index] = Temp[Index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void edit16_merge_sort(i64 Count, edit16_t *First, edit16_t *Temp) {
|
||||||
|
// SortKey = range.min
|
||||||
|
if (Count == 1) {
|
||||||
|
// NOTE(casey): No work to do.
|
||||||
|
} else if (Count == 2) {
|
||||||
|
edit16_t *EntryA = First;
|
||||||
|
edit16_t *EntryB = First + 1;
|
||||||
|
if (EntryA->range.min > EntryB->range.min) {
|
||||||
|
SWAP_PTR(edit16_t, EntryA, EntryB);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i64 Half0 = Count / 2;
|
||||||
|
i64 Half1 = Count - Half0;
|
||||||
|
|
||||||
|
assert(Half0 >= 1);
|
||||||
|
assert(Half1 >= 1);
|
||||||
|
|
||||||
|
edit16_t *InHalf0 = First;
|
||||||
|
edit16_t *InHalf1 = First + Half0;
|
||||||
|
edit16_t *End = First + Count;
|
||||||
|
|
||||||
|
edit16_merge_sort(Half0, InHalf0, Temp);
|
||||||
|
edit16_merge_sort(Half1, InHalf1, Temp);
|
||||||
|
|
||||||
|
edit16_t *ReadHalf0 = InHalf0;
|
||||||
|
edit16_t *ReadHalf1 = InHalf1;
|
||||||
|
|
||||||
|
edit16_t *Out = Temp;
|
||||||
|
for (i64 Index = 0;
|
||||||
|
Index < Count;
|
||||||
|
++Index) {
|
||||||
|
if (ReadHalf0 == InHalf1) {
|
||||||
|
*Out++ = *ReadHalf1++;
|
||||||
|
} else if (ReadHalf1 == End) {
|
||||||
|
*Out++ = *ReadHalf0++;
|
||||||
|
} else if (ReadHalf0->range.min < ReadHalf1->range.min) {
|
||||||
|
*Out++ = *ReadHalf0++;
|
||||||
|
} else {
|
||||||
|
*Out++ = *ReadHalf1++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(Out == (Temp + Count));
|
||||||
|
assert(ReadHalf0 == InHalf1);
|
||||||
|
assert(ReadHalf1 == End);
|
||||||
|
|
||||||
|
// TODO(casey): Not really necessary if we ping-pong
|
||||||
|
for (i64 Index = 0;
|
||||||
|
Index < Count;
|
||||||
|
++Index) {
|
||||||
|
First[Index] = Temp[Index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void buffer16_multi_cursor_apply_edits(buffer16_t *buffer, array_edit16_t edits) {
|
||||||
|
#if BUFFER_DEBUG
|
||||||
|
assert(buffer->line_starts.len);
|
||||||
|
assert(edits.len);
|
||||||
|
array_for(edit16_t, it, &edits) {
|
||||||
|
assert(it->range.min >= 0);
|
||||||
|
assert(it->range.max >= it->range.min);
|
||||||
|
assert(it->range.max <= buffer->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
array_for(edit16_t, it1, &edits) {
|
||||||
|
array_for(edit16_t, it2, &edits) {
|
||||||
|
if (it1 == it2) continue;
|
||||||
|
|
||||||
|
b32 a2_inside = it2->range.min >= it1->range.min && it2->range.min < it1->range.max;
|
||||||
|
assert(!a2_inside);
|
||||||
|
|
||||||
|
b32 b2_inside = it2->range.max > it1->range.min && it2->range.max <= it1->range.max;
|
||||||
|
assert(!b2_inside);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// We need to sort from lowest to highest based on range.min
|
||||||
|
{
|
||||||
|
ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object);
|
||||||
|
array_edit16_t edits_copy = {0};
|
||||||
|
array_copy(ma_temp_alo(scratch), &edits_copy, &edits);
|
||||||
|
if (edits.len > 1) edit16_merge_sort(edits.len, edits_copy.data, edits.data);
|
||||||
|
edits = edits_copy;
|
||||||
|
ma_end_scratch(scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if BUFFER_DEBUG
|
||||||
|
for (i64 i = 0; i < edits.len - 1; i += 1) {
|
||||||
|
assert(edits.data[i].range.min <= edits.data[i + 1].range.min);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// @optimize: we can do all edits in one go with less memory copies probably
|
||||||
|
// or something else entirely
|
||||||
|
i64 offset = 0;
|
||||||
|
array_for(edit16_t, it, &edits) {
|
||||||
|
it->range.min += offset;
|
||||||
|
it->range.max += offset;
|
||||||
|
offset += it->string.len - r1i64_size(it->range);
|
||||||
|
buffer16_raw_replace_text(buffer, it->range, it->string);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void buffer16_add_edit(array_edit16_t *edits, r1i64_t range, s16_t string) {
|
||||||
|
array_add(edits, (edit16_t){range, string});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge carets that overlap, this needs to be handled before any edits to
|
||||||
|
// make sure overlapping edits won't happen.
|
||||||
|
//
|
||||||
|
// mouse_selection_anchor is special case for mouse handling !
|
||||||
|
fn void buffer16_merge_carets(buffer16_t *buffer, array_caret_t *carets) {
|
||||||
|
array_for(caret_t, it, carets) {
|
||||||
|
it->range = buffer16_clamp_range(buffer, it->range);
|
||||||
|
}
|
||||||
|
caret_t first_caret = carets->data[0];
|
||||||
|
|
||||||
|
ma_temp_t scratch = ma_begin_scratch1(buffer->alo.object);
|
||||||
|
array_caret_t c1 = {0};
|
||||||
|
array_copy(ma_temp_alo(scratch), &c1, carets);
|
||||||
|
if (carets->len > 1) {
|
||||||
|
caret_merge_sort(carets->len, c1.data, carets->data);
|
||||||
|
}
|
||||||
|
carets->len = 0;
|
||||||
|
|
||||||
|
i64 first_caret_index = 0;
|
||||||
|
array_add(carets, c1.data[0]);
|
||||||
|
for (i64 i = 1; i < c1.len; i += 1) {
|
||||||
|
caret_t *it = c1.data + i;
|
||||||
|
caret_t *last = &array_last(carets);
|
||||||
|
|
||||||
|
if (r1i64_overlap(it->range, last->range)) {
|
||||||
|
last->range.max = MAX(last->range.max, it->range.max);
|
||||||
|
} else {
|
||||||
|
array_add(carets, *it);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (carets_are_equal(*it, first_caret)) {
|
||||||
|
first_caret_index = carets->len - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SWAP(caret_t, carets->data[first_caret_index], carets->data[0]);
|
||||||
|
ma_end_scratch(scratch);
|
||||||
|
}
|
||||||
|
|
||||||
fn_test void buffer16_test(void) {
|
fn_test void buffer16_test(void) {
|
||||||
ma_temp_t scratch = ma_begin_scratch();
|
ma_temp_t scratch = ma_begin_scratch();
|
||||||
|
|
||||||
@@ -631,6 +941,48 @@ fn_test void buffer16_test(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
buffer16_t *buffer = ma_push_type(scratch.arena, buffer16_t);
|
||||||
|
buffer16_raw_init(ma_temp_alo(scratch), buffer, S8_FILE_AND_LINE, 128);
|
||||||
|
buffer16_raw_replace_text(buffer, r1i64_null, s16("some thing or another and stuff like that"));
|
||||||
|
|
||||||
|
{
|
||||||
|
array_caret_t carets = {0};
|
||||||
|
array_init(ma_temp_alo(scratch), &carets, 32);
|
||||||
|
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));
|
||||||
|
array_add(&carets, caret_make(3, 6));
|
||||||
|
array_add(&carets, caret_make(3, 6));
|
||||||
|
buffer16_merge_carets(buffer, &carets);
|
||||||
|
assert(carets.len == 1);
|
||||||
|
assert(carets.data[0].range.min == 0 && carets.data[0].range.max == 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
array_caret_t carets = {0};
|
||||||
|
array_init(ma_temp_alo(scratch), &carets, 32);
|
||||||
|
array_add(&carets, caret_make(0, 4));
|
||||||
|
array_add(&carets, caret_make(0, 3));
|
||||||
|
array_add(&carets, caret_make(5, 10));
|
||||||
|
|
||||||
|
array_edit16_t edits = buffer16_begin_edit(ma_temp_alo(scratch), buffer, &carets);
|
||||||
|
|
||||||
|
buffer16_merge_carets(buffer, &carets);
|
||||||
|
assert(carets.len == 2);
|
||||||
|
|
||||||
|
array_for(caret_t, it, &carets) {
|
||||||
|
buffer16_add_edit(&edits, it->range, s16("meme"));
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer16_end_edit(buffer, &edits, &carets, kill_selection);
|
||||||
|
assert(s16_are_equal(buffer->string, s16("meme meme or another and stuff like that")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ma_end_scratch(scratch);
|
ma_end_scratch(scratch);
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|||||||
Reference in New Issue
Block a user