diff --git a/src/text_editor/buffer.cpp b/src/text_editor/buffer.cpp index 40eab7a..6ee36f7 100644 --- a/src/text_editor/buffer.cpp +++ b/src/text_editor/buffer.cpp @@ -211,6 +211,8 @@ void MergeSort(int64_t Count, Edit *First, Edit *Temp) { void ApplyEdits(Buffer *buffer, Array edits) { Scratch scratch((Arena *)buffer->allocator.object); + // Figure out how much we insert and how much we delete so + // we can resize buffers properly if necessary Assert(edits.len); int64_t size_to_delete = 0; int64_t size_to_insert = 0; @@ -241,6 +243,7 @@ void ApplyEdits(Buffer *buffer, Array edits) { Array edits_copy = edits.copy(scratch); MergeSort(edits.len, edits_copy.data, edits.data); + // Try resizing the buffers 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) { @@ -257,6 +260,7 @@ void ApplyEdits(Buffer *buffer, Array edits) { buffer->cap = new_cap; } + // Figure out what we need to write to the second buffer int srci = buffer->bi; int dsti = (buffer->bi + 1) % 2; @@ -293,12 +297,14 @@ void ApplyEdits(Buffer *buffer, Array edits) { writes.add({dest_range, source_string}); } + // Make sure there are no gaps between ranges #if DEBUG_BUILD for (int64_t i = 0; i < writes.len - 1; i += 1) { Assert(writes[i].range.max == writes[i + 1].range.min); } #endif + // Write to the second buffer int64_t new_buffer_len = 0; For(writes) { Assert(it.range.min >= 0); diff --git a/src/text_editor/main.cpp b/src/text_editor/main.cpp index cccb411..47631a5 100644 --- a/src/text_editor/main.cpp +++ b/src/text_editor/main.cpp @@ -103,7 +103,26 @@ int64_t MoveUp(Buffer &buffer, int64_t pos) { return new_pos; } -void UpdateCursorsAfterEdit(Window *window, Array edits) { +void BeforeEdit(Window *window) { + // Merge cursors that overlap, this needs to be handled before any edits to + // make sure overlapping edits won't happen. + for (int64_t cursor_i = 0; cursor_i < window->cursors.len; cursor_i += 1) { + Cursor &cursor = window->cursors[cursor_i]; + IterRemove(window->cursors) { + IterRemovePrepare(window->cursors); + if (&cursor == &it) continue; + + if (cursor.range.max >= it.range.min && cursor.range.max <= it.range.max) { + remove_item = true; + cursor.range.max = it.range.max; + break; + } + } + } +} + +void AfterEdit(Window *window, Array edits) { + // First offset all cursors by edits ForItem(edit, edits) { int64_t remove_size = GetRangeSize(edit.range); int64_t insert_size = edit.string.len; @@ -194,7 +213,13 @@ int main() { if (IsKeyPressed(KEY_LEFT) || IsKeyPressedRepeat(KEY_LEFT)) { For(focused_window->cursors) { if (IsKeyDown(KEY_LEFT_CONTROL)) { - it.range.max = it.range.min = Seek(focused_window->buffer, it.range.min, ITERATE_BACKWARD); + if (IsKeyDown(KEY_LEFT_SHIFT)) { + int64_t front = GetFront(it); + front = Seek(focused_window->buffer, front, ITERATE_BACKWARD); + it = ChangeFront(it, front); + } else { + it.range.max = it.range.min = Seek(focused_window->buffer, it.range.min, ITERATE_BACKWARD); + } } else { if (IsKeyDown(KEY_LEFT_SHIFT)) { int64_t front = GetFront(it); @@ -213,7 +238,13 @@ int main() { if (IsKeyPressed(KEY_RIGHT) || IsKeyPressedRepeat(KEY_RIGHT)) { For(focused_window->cursors) { if (IsKeyDown(KEY_LEFT_CONTROL)) { - it.range.max = it.range.min = Seek(focused_window->buffer, it.range.min, ITERATE_FORWARD); + if (IsKeyDown(KEY_LEFT_SHIFT)) { + int64_t front = GetFront(it); + front = Seek(focused_window->buffer, front, ITERATE_FORWARD); + it = ChangeFront(it, front); + } else { + it.range.max = it.range.min = Seek(focused_window->buffer, it.range.min, ITERATE_FORWARD); + } } else { if (IsKeyDown(KEY_LEFT_SHIFT)) { int64_t front = GetFront(it); @@ -230,45 +261,75 @@ int main() { } } if (IsKeyPressed(KEY_DOWN) || IsKeyPressedRepeat(KEY_DOWN)) { - For(focused_window->cursors) { - if (IsKeyDown(KEY_LEFT_SHIFT)) { + if (IsKeyDown(KEY_LEFT_SHIFT) && IsKeyDown(KEY_LEFT_ALT)) { // Default in VSCode seems to be Ctrl + Alt + down + For(focused_window->cursors) { + int64_t front = GetFront(it); + front = MoveDown(focused_window->buffer, front); + focused_window->cursors.add({front, front}); + } + } else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyDown(KEY_LEFT_ALT)) { // Default in VSCode seems to be Shift + Alt + down + BeforeEdit(focused_window); + Array edits = {FrameArena}; + For(focused_window->cursors) { + int64_t front = GetFront(it); + Line line = FindLine(focused_window->buffer, front); + String string = GetString(focused_window->buffer, {line.range.min, line.range.max + 1}); + AddEdit(&edits, {line.range.max + 1, line.range.max + 1}, string); + } + ApplyEdits(&focused_window->buffer, edits); + AfterEdit(focused_window, edits); + For(focused_window->cursors) { + it.range.max = it.range.min = MoveDown(focused_window->buffer, it.range.min); + } + } else if (IsKeyDown(KEY_LEFT_SHIFT)) { + For(focused_window->cursors) { int64_t front = GetFront(it); front = MoveDown(focused_window->buffer, front); it = ChangeFront(it, front); - } else { + } + } else { + For(focused_window->cursors) { it.range.max = it.range.min = MoveDown(focused_window->buffer, it.range.min); } } } + if (IsKeyPressed(KEY_UP) || IsKeyPressedRepeat(KEY_UP)) { - For(focused_window->cursors) { - if (IsKeyDown(KEY_LEFT_SHIFT)) { + if (IsKeyDown(KEY_LEFT_SHIFT) && IsKeyDown(KEY_LEFT_ALT)) { // Default in VSCode seems to be Ctrl + Alt + up + For(focused_window->cursors) { + int64_t front = GetFront(it); + front = MoveUp(focused_window->buffer, front); + focused_window->cursors.add({front, front}); + } + } else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyDown(KEY_LEFT_ALT)) { // Default in VSCode seems to be Shift + Alt + up + BeforeEdit(focused_window); + Array edits = {FrameArena}; + For(focused_window->cursors) { + int64_t front = GetFront(it); + Line line = FindLine(focused_window->buffer, front); + String string = GetString(focused_window->buffer, {line.range.min, line.range.max + 1}); + AddEdit(&edits, {line.range.min, line.range.min}, string); + } + ApplyEdits(&focused_window->buffer, edits); + AfterEdit(focused_window, edits); + For(focused_window->cursors) { + it.range.max = it.range.min = MoveUp(focused_window->buffer, it.range.min); + } + } else if (IsKeyDown(KEY_LEFT_SHIFT)) { + For(focused_window->cursors) { int64_t front = GetFront(it); front = MoveUp(focused_window->buffer, front); it = ChangeFront(it, front); - } else { + } + } else { + For(focused_window->cursors) { it.range.max = it.range.min = MoveUp(focused_window->buffer, it.range.min); } } } - // Merge cursors that overlap, this needs to be handled before any edits to - // make sure overlapping edits won't happen. - for (int64_t cursor_i = 0; cursor_i < focused_window->cursors.len; cursor_i += 1) { - Cursor &cursor = focused_window->cursors[cursor_i]; - IterRemove(focused_window->cursors) { - IterRemovePrepare(focused_window->cursors); - if (&cursor == &it) continue; - - if (cursor.range.max >= it.range.min && cursor.range.max <= it.range.max) { - remove_item = true; - cursor.range.max = it.range.max; - break; - } - } - } - if (IsKeyPressed(KEY_BACKSPACE) || IsKeyPressedRepeat(KEY_BACKSPACE)) { + BeforeEdit(focused_window); Array edits = {FrameArena}; String string = {}; For(focused_window->cursors) { @@ -276,7 +337,7 @@ int main() { AddEdit(&edits, {pos, it.range.min}, string); } ApplyEdits(&focused_window->buffer, edits); - UpdateCursorsAfterEdit(focused_window, edits); + AfterEdit(focused_window, edits); } // Handle user input @@ -290,12 +351,13 @@ int main() { string = {(char *)utf8.out_str, (int64_t)utf8.len}; } + BeforeEdit(focused_window); Array edits = {FrameArena}; For(focused_window->cursors) { AddEdit(&edits, it.range, string); } ApplyEdits(&focused_window->buffer, edits); - UpdateCursorsAfterEdit(focused_window, edits); + AfterEdit(focused_window, edits); } }