diff --git a/src/basic/basic.h b/src/basic/basic.h index 9117a44..8d5eee8 100644 --- a/src/basic/basic.h +++ b/src/basic/basic.h @@ -95,6 +95,13 @@ T Clamp(T value, T min, T max) { return value; } +template +void Swap(T *a, T *b) { + T temp = *a; + *a = *b; + *b = temp; +} + inline bool IsPowerOf2(size_t x) { size_t result = (((x) & ((x)-1)) == 0); return result; diff --git a/src/text_editor/buffer.cpp b/src/text_editor/buffer.cpp index 202f350..40eab7a 100644 --- a/src/text_editor/buffer.cpp +++ b/src/text_editor/buffer.cpp @@ -131,6 +131,7 @@ Cursor ChangeFront(Cursor cursor, int64_t front) { void AddEdit(Array *edits, Range range, String string) { edits->add({range, string}); } +// SortKey = range.min bool InBounds(const Buffer &buffer, int64_t pos) { bool result = pos >= 0 && pos < buffer.len; @@ -153,7 +154,63 @@ String GetString(const Buffer &buffer, Range range = {0, INT64_MAX}) { return result; } +void MergeSort(int64_t Count, Edit *First, Edit *Temp) { + // SortKey = range.min + if (Count == 1) { + // NOTE(casey): No work to do. + } else if (Count == 2) { + Edit *EntryA = First; + Edit *EntryB = First + 1; + if (EntryA->range.min > EntryB->range.min) { + Swap(EntryA, EntryB); + } + } else { + int64_t Half0 = Count / 2; + int64_t Half1 = Count - Half0; + + Assert(Half0 >= 1); + Assert(Half1 >= 1); + + Edit *InHalf0 = First; + Edit *InHalf1 = First + Half0; + Edit *End = First + Count; + + MergeSort(Half0, InHalf0, Temp); + MergeSort(Half1, InHalf1, Temp); + + Edit *ReadHalf0 = InHalf0; + Edit *ReadHalf1 = InHalf1; + + Edit *Out = Temp; + for (int64_t 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 (int64_t Index = 0; + Index < Count; + ++Index) { + First[Index] = Temp[Index]; + } + } +} + void ApplyEdits(Buffer *buffer, Array edits) { + Scratch scratch((Arena *)buffer->allocator.object); + Assert(edits.len); int64_t size_to_delete = 0; int64_t size_to_insert = 0; @@ -180,6 +237,10 @@ void ApplyEdits(Buffer *buffer, Array edits) { } #endif + // We need to sort from lowest to higest based on range.min + Array edits_copy = edits.copy(scratch); + MergeSort(edits.len, edits_copy.data, edits.data); + 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) { @@ -199,13 +260,12 @@ void ApplyEdits(Buffer *buffer, Array edits) { int srci = buffer->bi; int dsti = (buffer->bi + 1) % 2; - Scratch scratch((Arena *)buffer->allocator.object); Array writes = {scratch}; int64_t prev_source = 0; int64_t prev_dest = 0; For(edits) { - TraceLog(LOG_DEBUG, "edit dsti: %d, srci: %d, range: %lld to %lld, string: '%.*s'\n", dsti, srci, (long long)it.range.min, (long long)it.range.max, FmtString(it.string)); + TraceLog(LOG_DEBUG, "edit dsti: %d, srci: %d, range: %lld to %lld, string: '%.*s'", dsti, srci, (long long)it.range.min, (long long)it.range.max, Min(5, (int)it.string.len), it.string.data); Range source_range = {prev_source, it.range.min}; if (GetRangeSize(source_range) != 0) { String source_string = {}; @@ -241,7 +301,9 @@ void ApplyEdits(Buffer *buffer, Array edits) { int64_t new_buffer_len = 0; For(writes) { - TraceLog(LOG_DEBUG, "write dsti: %d, srci: %d, range: %lld to %lld, string: '%.*s'\n", dsti, srci, (long long)it.range.min, (long long)it.range.max, FmtString(it.string)); + Assert(it.range.min >= 0); + Assert(it.range.max >= 0); + TraceLog(LOG_DEBUG, "write dsti: %d, srci: %d, range: %lld to %lld, string: '%.*s'", dsti, srci, (long long)it.range.min, (long long)it.range.max, Min(5, (int)it.string.len), it.string.data); memcpy(buffer->data[dsti] + new_buffer_len, it.string.data, it.string.len); new_buffer_len += it.string.len; } diff --git a/src/text_editor/main.cpp b/src/text_editor/main.cpp index 0da8c72..cccb411 100644 --- a/src/text_editor/main.cpp +++ b/src/text_editor/main.cpp @@ -231,12 +231,24 @@ int main() { } if (IsKeyPressed(KEY_DOWN) || IsKeyPressedRepeat(KEY_DOWN)) { For(focused_window->cursors) { - it.range.max = it.range.min = MoveDown(focused_window->buffer, it.range.min); + if (IsKeyDown(KEY_LEFT_SHIFT)) { + int64_t front = GetFront(it); + front = MoveDown(focused_window->buffer, front); + it = ChangeFront(it, front); + } else { + it.range.max = it.range.min = MoveDown(focused_window->buffer, it.range.min); + } } } if (IsKeyPressed(KEY_UP) || IsKeyPressedRepeat(KEY_UP)) { For(focused_window->cursors) { - it.range.max = it.range.min = MoveUp(focused_window->buffer, it.range.min); + if (IsKeyDown(KEY_LEFT_SHIFT)) { + int64_t front = GetFront(it); + front = MoveUp(focused_window->buffer, front); + it = ChangeFront(it, front); + } else { + it.range.max = it.range.min = MoveUp(focused_window->buffer, it.range.min); + } } } @@ -426,10 +438,15 @@ int main() { if (CheckCollisionPointRec(mouse_in_render_units, ToRectangle(cell.rect))) { DrawRectangleRec(ToRectangle(cell.rect), {0, 0, 255, 40}); if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - window.cursors.clear(); - window.cursors.add({cell.pos, cell.pos}); + if (IsKeyDown(KEY_LEFT_CONTROL)) { + window.cursors.add({cell.pos, cell.pos}); + } else { + window.cursors.clear(); + window.cursors.add({cell.pos, cell.pos}); + } } else if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - window.cursors[0] = ChangeBack(window.cursors[0], cell.pos); + Cursor *c = window.cursors.last(); + *c = ChangeBack(*c, cell.pos); } } }