Split buffer into multiple files

This commit is contained in:
Krzosa Karol
2024-07-18 14:19:48 +02:00
parent e0ac9654c3
commit cd48094a18
6 changed files with 327 additions and 169 deletions

View File

@@ -2,149 +2,35 @@
https://code.visualstudio.com/blogs/2018/03/23/text-buffer-reimplementation
*/
struct Range {
Int min;
Int max;
};
struct Cursor {
union {
Range range;
Int pos[2];
};
Int ifront;
};
struct Pos {
Int line;
Int col;
};
struct Edit {
Range range;
String16 string;
};
struct Buffer {
union {
U16 *data;
wchar_t *str;
};
Int len;
Int cap;
Array<Int> line_starts;
};
struct HistoryEntry {
Array<Edit> edits;
Array<Cursor> cursors;
};
Int GetSize(Range range) {
Assert(range.max >= range.min);
Int result = range.max - range.min;
return result;
void InitBuffer(Allocator allocator, Buffer *buffer) {
buffer->cap = 4096;
buffer->data = AllocArray(allocator, U16, buffer->cap);
buffer->line_starts.allocator = allocator;
Add(&buffer->line_starts, (Int)0);
}
Range MakeRange(Int a, Int b) {
Range result = {Min(a, b), Max(a, b)};
return result;
}
Range MakeRange(Int a) {
Range result = {a, a};
return result;
}
String16 GetString(Buffer &buffer, Range range = {0, INT64_MAX}) {
range.min = Clamp(range.min, (Int)0, buffer.len);
range.max = Clamp(range.max, (Int)0, buffer.len);
String16 result = {(wchar_t *)buffer.data + range.min, GetSize(range)};
return result;
}
Int Clamp(const Buffer &buffer, Int pos) {
Int result = Clamp(pos, (Int)0, buffer.len);
return result;
}
Range Clamp(const Buffer &buffer, Range range) {
Range result = {};
result.min = Clamp(buffer, range.min);
result.max = Clamp(buffer, range.max);
return result;
}
Range GetEndAsRange(Buffer &buffer) {
Range result = {buffer.len, buffer.len};
return result;
}
Range GetBeginAsRange(Buffer &buffer) {
Range result = {0, 0};
return result;
}
Range GetRange(Buffer &buffer) {
Range result = {0, buffer.len};
return result;
}
Int GetFront(Cursor cursor) {
Int result = cursor.pos[cursor.ifront];
return result;
}
Int GetBack(Cursor cursor) {
Int index = (cursor.ifront + 1) % 2;
Int result = cursor.pos[index];
return result;
}
Cursor MakeCursor(Int front, Int back) {
Cursor result = {};
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;
void Grow(Buffer *buffer, Int change_size) {
Int new_size = buffer->len + change_size;
if (new_size > buffer->cap) {
Allocator alo = buffer->line_starts.allocator;
Int outside = new_size - buffer->cap;
Int new_cap = (buffer->cap + outside) * 2;
U16 *new_array = AllocArray(alo, U16, new_cap);
MemoryCopy(new_array, buffer->data, buffer->len * sizeof(U16));
Dealloc(alo, &buffer->data);
buffer->cap = new_cap;
buffer->data = new_array;
}
return result;
}
Cursor ChangeBack(Cursor cursor, Int back) {
Int front = GetFront(cursor);
Cursor result = MakeCursor(front, back);
return result;
}
void OffsetAllLinesForward(Buffer *buffer, Int line, Int *_offset) {
Int offset = *_offset;
*_offset = 0;
if (offset == 0) return;
Cursor ChangeFront(Cursor cursor, Int front) {
Int back = GetBack(cursor);
Cursor result = MakeCursor(front, back);
return result;
}
bool InBounds(const Buffer &buffer, Int pos) {
bool result = pos >= 0 && pos < buffer.len;
return result;
}
bool AreEqual(Range a, Range b) {
bool result = a.min == b.min && a.max == b.max;
return result;
}
bool AreEqual(Cursor a, Cursor b) {
bool result = AreEqual(a.range, b.range) && a.ifront == b.ifront;
return result;
}
bool InRange(Int a, Range b) {
bool result = a >= b.min && a < b.max;
return result;
for (Int i = line; i < buffer->line_starts.len; i += 1) {
buffer->line_starts[i] += offset;
}
}
Range GetLine(Buffer &buffer, Int line) {
@@ -183,37 +69,6 @@ Int GetLineNumber(Buffer &buffer, Int pos) {
return result;
}
void InitBuffer(Allocator allocator, Buffer *buffer) {
buffer->cap = 4096;
buffer->data = AllocArray(allocator, U16, buffer->cap);
buffer->line_starts.allocator = allocator;
Add(&buffer->line_starts, (Int)0);
}
void Grow(Buffer *buffer, Int change_size) {
Int new_size = buffer->len + change_size;
if (new_size > buffer->cap) {
Allocator alo = buffer->line_starts.allocator;
Int outside = new_size - buffer->cap;
Int new_cap = (buffer->cap + outside) * 2;
U16 *new_array = AllocArray(alo, U16, new_cap);
MemoryCopy(new_array, buffer->data, buffer->len * sizeof(U16));
Dealloc(alo, &buffer->data);
buffer->cap = new_cap;
buffer->data = new_array;
}
}
void OffsetAllLinesForward(Buffer *buffer, Int line, Int *_offset) {
Int offset = *_offset;
*_offset = 0;
if (offset == 0) return;
for (Int i = line; i < buffer->line_starts.len; i += 1) {
buffer->line_starts[i] += offset;
}
}
void ValidateLineStarts(Buffer *buffer) {
Int line = 0;
for (Int i = 0; i < buffer->len; i += 1) {

32
src/text_editor/buffer.h Normal file
View File

@@ -0,0 +1,32 @@
struct Buffer {
union {
U16 *data;
wchar_t *str;
};
Int len;
Int cap;
Array<Int> line_starts;
};
struct Range {
Int min;
Int max;
};
struct Cursor {
union {
Range range;
Int pos[2];
};
Int ifront;
};
struct Pos {
Int line;
Int col;
};
struct Edit {
Range range;
String16 string;
};

View File

@@ -0,0 +1,106 @@
Int GetSize(Range range) {
Assert(range.max >= range.min);
Int result = range.max - range.min;
return result;
}
Range MakeRange(Int a, Int b) {
Range result = {Min(a, b), Max(a, b)};
return result;
}
Range MakeRange(Int a) {
Range result = {a, a};
return result;
}
String16 GetString(Buffer &buffer, Range range = {0, INT64_MAX}) {
range.min = Clamp(range.min, (Int)0, buffer.len);
range.max = Clamp(range.max, (Int)0, buffer.len);
String16 result = {(wchar_t *)buffer.data + range.min, GetSize(range)};
return result;
}
Int Clamp(const Buffer &buffer, Int pos) {
Int result = Clamp(pos, (Int)0, buffer.len);
return result;
}
Range Clamp(const Buffer &buffer, Range range) {
Range result = {};
result.min = Clamp(buffer, range.min);
result.max = Clamp(buffer, range.max);
return result;
}
Range GetEndAsRange(Buffer &buffer) {
Range result = {buffer.len, buffer.len};
return result;
}
Range GetBeginAsRange(Buffer &buffer) {
Range result = {0, 0};
return result;
}
Range GetRange(Buffer &buffer) {
Range result = {0, buffer.len};
return result;
}
Int GetFront(Cursor cursor) {
Int result = cursor.pos[cursor.ifront];
return result;
}
Int GetBack(Cursor cursor) {
Int index = (cursor.ifront + 1) % 2;
Int result = cursor.pos[index];
return result;
}
Cursor MakeCursor(Int front, Int back) {
Cursor result = {};
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;
}
Cursor ChangeBack(Cursor cursor, Int back) {
Int front = GetFront(cursor);
Cursor result = MakeCursor(front, back);
return result;
}
Cursor ChangeFront(Cursor cursor, Int front) {
Int back = GetBack(cursor);
Cursor result = MakeCursor(front, back);
return result;
}
bool InBounds(const Buffer &buffer, Int pos) {
bool result = pos >= 0 && pos < buffer.len;
return result;
}
bool AreEqual(Range a, Range b) {
bool result = a.min == b.min && a.max == b.max;
return result;
}
bool AreEqual(Cursor a, Cursor b) {
bool result = AreEqual(a.range, b.range) && a.ifront == b.ifront;
return result;
}
bool InRange(Int a, Range b) {
bool result = a >= b.min && a < b.max;
return result;
}

View File

@@ -0,0 +1,160 @@
// @todo:
// @idea: maybe redo tree?
struct HistoryEntry {
Array<Edit> edits;
Array<Cursor> cursors;
};
struct History {
Array<HistoryEntry> undo_stack;
Array<HistoryEntry> redo_stack;
int debug_edit_phase;
};
#if 0
void SaveHistoryBeforeMergeCursor(Window *window, Array<HistoryEntry> *stack) {
ProfileFunction();
HistoryEntry *entry = stack->alloc();
Allocator sys_allocator = GetSystemAllocator();
entry->cursors = window->cursors.tight_copy(sys_allocator);
}
void SaveHistoryBeforeApplyEdits(Window *window, Array<HistoryEntry> *stack, Array<Edit> edits) {
ProfileFunction();
HistoryEntry *entry = stack->last();
Allocator sys_allocator = GetSystemAllocator();
entry->edits = edits.tight_copy(sys_allocator);
// Make reverse edits
For(entry->edits) {
Range new_range = {it.range.min, it.range.min + it.string.len};
String string = GetString(window->buffer, it.range);
it.string = Copy(sys_allocator, string);
it.range = new_range;
}
Array<Edit> temp_edits = entry->edits.tight_copy(sys_allocator);
defer { temp_edits.dealloc(); };
// Fix reverse edits
ForItem(edit, edits) {
int64_t remove_size = GetRangeSize(edit.range);
int64_t insert_size = edit.string.len;
int64_t offset = insert_size - remove_size;
for (int64_t i = 0; i < entry->edits.len; i += 1) {
Edit &new_edit = entry->edits.data[i];
Edit &old_edit = temp_edits.data[i];
if (old_edit.range.min > edit.range.min) {
new_edit.range.min += offset;
new_edit.range.max += offset;
}
}
}
}
void RedoEdit(Window *window) {
ProfileFunction();
if (window->history.redo_stack.len <= 0) return;
HistoryEntry entry = window->history.redo_stack.pop();
SaveHistoryBeforeMergeCursor(window, &window->history.undo_stack);
SaveHistoryBeforeApplyEdits(window, &window->history.undo_stack, entry.edits);
_ApplyEdits(&window->buffer, entry.edits);
window->cursors.dealloc();
window->cursors = entry.cursors;
window->not_regen_layout = false;
Allocator sys_allocator = GetSystemAllocator();
For(entry.edits) Dealloc(sys_allocator, &it.string.data);
entry.edits.dealloc();
}
void UndoEdit(Window *window) {
ProfileFunction();
if (window->history.undo_stack.len <= 0) return;
HistoryEntry entry = window->history.undo_stack.pop();
SaveHistoryBeforeMergeCursor(window, &window->history.redo_stack);
SaveHistoryBeforeApplyEdits(window, &window->history.redo_stack, entry.edits);
_ApplyEdits(&window->buffer, entry.edits);
window->cursors.dealloc();
window->cursors = entry.cursors;
window->not_regen_layout = false;
Allocator sys_allocator = GetSystemAllocator();
For(entry.edits) Dealloc(sys_allocator, &it.string.data);
entry.edits.dealloc();
}
void BeforeEdit(Window *window) {
Assert(window->history.debug_edit_phase == 0);
window->history.debug_edit_phase += 1;
Assert(window->cursors.len);
SaveHistoryBeforeMergeCursor(window, &window->history.undo_stack);
// Clear redo stack
{
For(window->history.redo_stack) {
it.cursors.dealloc();
ForItem(edit, it.edits) Dealloc(it.edits.allocator, &edit.string.data);
it.edits.dealloc();
}
window->history.redo_stack.dealloc();
}
MergeCursors(window);
}
void ApplyEdits(Window *window, Array<Edit> edits) {
ProfileFunction();
Assert(window->history.debug_edit_phase == 1);
window->history.debug_edit_phase += 1;
SaveHistoryBeforeApplyEdits(window, &window->history.undo_stack, edits);
_ApplyEdits(&window->buffer, edits);
}
void AfterEdit(Window *window, Array<Edit> edits) {
ProfileFunction();
Assert(window->history.debug_edit_phase == 2);
window->history.debug_edit_phase -= 2;
HistoryEntry *entry = window->history.undo_stack.last();
Assert(entry->cursors.len);
Assert(entry->edits.len);
for (int64_t i = 0; i < edits.len - 1; i += 1) {
Assert(edits[i].range.min <= edits[i + 1].range.min);
}
//
// Offset all cursors by edits
//
Scratch scratch;
Array<Cursor> new_cursors = window->cursors.tight_copy(scratch);
ForItem(edit, edits) {
int64_t remove_size = GetRangeSize(edit.range);
int64_t insert_size = edit.string.len;
int64_t offset = insert_size - remove_size;
for (int64_t i = 0; i < window->cursors.len; i += 1) {
Cursor &old_cursor = window->cursors.data[i];
Cursor &new_cursor = new_cursors.data[i];
if (old_cursor.range.min == edit.range.min) {
new_cursor.range.min = new_cursor.range.max = new_cursor.range.min + insert_size;
} else if (old_cursor.range.min > edit.range.min) {
new_cursor.range.min = new_cursor.range.max = new_cursor.range.min + offset;
}
}
}
for (int64_t i = 0; i < window->cursors.len; i += 1) window->cursors[i] = new_cursors[i];
// Make sure all cursors are in range
For(window->cursors) it.range = Clamp(window->buffer, it.range);
window->not_regen_layout = false;
}
#endif

View File

@@ -119,7 +119,8 @@ void ApplyEdits(Buffer *buffer, Array<Edit> edits) {
edits = edits_copy;
}
// @todo: optimize
// @optimize: we can do all edits in one go with less memory copies probably
// or something else entirely
Int offset = 0;
For(edits) {
it.range.min += offset;

View File

@@ -3,8 +3,12 @@
#include "new_basic.cpp"
#include "string16.cpp"
#include "buffer.h"
#include "buffer_helpers.cpp"
#include "buffer.cpp"
#include "buffer_multi_cursor.cpp"
#include "buffer_history.cpp"
#include "raylib.h"
int main(void) {