Basic multiline buffer concept

This commit is contained in:
Krzosa Karol
2024-06-20 09:40:05 +02:00
parent b919678913
commit 5539bbd3b9
3 changed files with 253 additions and 4 deletions

160
src/text_editor/buffer.cpp Normal file
View File

@@ -0,0 +1,160 @@
struct Buffer {
Allocator allocator;
char *data[2];
int64_t cap;
int64_t len;
int bi; // current buffer index
};
struct Range {
int64_t a;
int64_t b; // one past last index
// <0,4> = 0,1,2,3
};
int64_t GetRangeSize(Range range) {
int64_t result = range.b - range.a;
return result;
}
Range GetRange(const Buffer &buffer) {
Range result = {0, buffer.len};
return result;
}
struct Edit {
Range range;
String string;
};
void ApplyEdits(Buffer *buffer, Array<Edit> edits) {
int64_t size_to_delete = 0;
int64_t size_to_insert = 0;
int64_t end_of_buffer = Max((int64_t)0, buffer->len - 1);
For(edits) {
it.range.a = Clamp(it.range.a, (int64_t)0, end_of_buffer);
it.range.b = Clamp(it.range.b, (int64_t)0, buffer->len);
size_to_delete += GetRangeSize(it.range);
size_to_insert += it.string.len;
}
#if DEBUG_BUILD
ForItem(it1, edits) {
ForItem(it2, edits) {
if (&it1 == &it2) continue;
bool a2_inside = it2.range.a >= it1.range.a && it2.range.a < it1.range.b;
Assert(!a2_inside);
bool b2_inside = it2.range.b > it1.range.a && it2.range.b <= it1.range.b;
Assert(!b2_inside);
}
}
#endif
int64_t len_offset = size_to_insert - size_to_delete;
int64_t allocated_size_required = Max((int64_t)0, len_offset);
if (buffer->len + allocated_size_required > buffer->cap) {
int64_t new_cap = AlignUp(buffer->cap + allocated_size_required, 4096);
if (buffer->allocator.proc == NULL) buffer->allocator = GetSystemAllocator();
{
char *data = AllocArray(buffer->allocator, char, new_cap);
Assert(data);
memcpy(data, buffer->data[0], buffer->len);
Dealloc(buffer->allocator, &buffer->data[0]);
buffer->data[0] = data;
}
{
char *data = AllocArray(buffer->allocator, char, new_cap);
Assert(data);
memcpy(data, buffer->data[1], buffer->len);
Dealloc(buffer->allocator, &buffer->data[1]);
buffer->data[1] = data;
}
buffer->cap = new_cap;
}
Scratch scratch;
Array<Edit> writes = {scratch};
int64_t prev_source = 0;
int64_t prev_dest = 0;
For(edits) {
Range source_range = {prev_source, it.range.a};
if (GetRangeSize(source_range) != 0) {
String source_string = {};
source_string.data = buffer->data[buffer->bi] + source_range.a;
source_string.len = GetRangeSize(source_range);
Range dest_range = {prev_dest, prev_dest + source_string.len};
writes.add({dest_range, source_string});
prev_dest = dest_range.b;
}
Range dest_range = {prev_dest, prev_dest + it.string.len};
writes.add({dest_range, it.string});
prev_dest = dest_range.b;
prev_source = it.range.b;
}
// Add remaining range
Range source_range = {prev_source, buffer->len};
if (GetRangeSize(source_range)) {
String source_string = {};
source_string.data = buffer->data[buffer->bi] + source_range.a;
source_string.len = GetRangeSize(source_range);
Range dest_range = {prev_dest, prev_dest + source_string.len};
writes.add({dest_range, source_string});
}
#if DEBUG_BUILD
for (int64_t i = 0; i < writes.len - 1; i += 1) {
Assert(writes[i].range.b == writes[i + 1].range.a);
}
#endif
int64_t new_buffer_len = 0;
int dsti = (buffer->bi + 1) % 2;
For(writes) {
memcpy(buffer->data[dsti] + new_buffer_len, it.string.data, it.string.len);
new_buffer_len += it.string.len;
}
buffer->bi = dsti;
Assert(new_buffer_len == buffer->len + len_offset);
buffer->len = new_buffer_len;
}
void AddEdit(Array<Edit> *edits, Range range, String string) {
edits->add({range, string});
}
void RunBufferTests() {
Scratch scratch;
{
Buffer buffer = {scratch};
Array<Edit> edits = {scratch};
AddEdit(&edits, {0, 0}, "Things and other things");
ApplyEdits(&buffer, edits);
String string = {buffer.data[buffer.bi], buffer.len};
Assert(string == "Things and other things");
}
{
Buffer buffer = {scratch};
Array<Edit> edits = {scratch};
edits.add({
{0, 0},
"Things and other things"
});
ApplyEdits(&buffer, edits);
edits.clear();
AddEdit(&edits, {0, 6}, "Memes");
AddEdit(&edits, {7, 10}, "dna");
AddEdit(&edits, {11, 16}, "BigOther");
ApplyEdits(&buffer, edits);
String string = {buffer.data[buffer.bi], buffer.len};
Assert(string == "Memes dna BigOther things");
}
}