diff --git a/src/text_editor/buffer.cpp b/src/text_editor/buffer.cpp index 448c8eb..686d905 100644 --- a/src/text_editor/buffer.cpp +++ b/src/text_editor/buffer.cpp @@ -225,7 +225,7 @@ void MergeSort(int64_t Count, Edit *First, Edit *Temp) { } } -void ApplyEdits(Buffer *buffer, Array edits) { +void _ApplyEdits(Buffer *buffer, Array edits) { Scratch scratch((Arena *)buffer->allocator.object); Assert(buffer->data[0]); Assert(buffer->allocator.proc); diff --git a/src/text_editor/event_recording.cpp b/src/text_editor/event_recording.cpp index 9f0cc7b..878f7bd 100644 --- a/src/text_editor/event_recording.cpp +++ b/src/text_editor/event_recording.cpp @@ -101,7 +101,7 @@ void EventRecording_SimulateGetCharPressed(Window *focused_window) { For(focused_window->cursors) { AddEdit(&edits, it.range, string); } - ApplyEdits(&focused_window->buffer, edits); + ApplyEdits(focused_window, edits); AfterEdit(focused_window, edits); } } diff --git a/src/text_editor/layout.cpp b/src/text_editor/layout.cpp index 833af44..f113327 100644 --- a/src/text_editor/layout.cpp +++ b/src/text_editor/layout.cpp @@ -14,6 +14,16 @@ struct Layout { Vec2 buffer_world_pixel_size; }; +struct HistoryEntry { + Array cursors; + Array edits; +}; + +struct History { + Array entries; + int debug_edit_phase; +}; + struct Window { Font font; float font_size; @@ -25,6 +35,7 @@ struct Window { Cursor main_cursor_begin_frame; Array cursors; Buffer buffer; + History history; Arena layout_arena; Layout layout; @@ -246,7 +257,7 @@ Array CalculateVisibleColumns(Arena *arena, Window &window) { return r; } -void BeforeEdit(Window *window) { +void MergeCursors(Window *window) { Scratch scratch; // Merge cursors that overlap, this needs to be handled before any edits to // make sure overlapping edits won't happen. @@ -280,7 +291,67 @@ void BeforeEdit(Window *window) { window->cursors = new_cursors; } +void UndoEdit(Window *window) { + if (window->history.entries.len <= 0) return; + + HistoryEntry entry = window->history.entries.pop(); + _ApplyEdits(&window->buffer, entry.edits); + + window->cursors.dealloc(); + window->cursors = entry.cursors; + + Allocator sys_allocator = GetSystemAllocator(); + For(entry.edits) Dealloc(sys_allocator, &it.string.data); + entry.edits.dealloc(); + + // Generate layout + Clear(&window->layout_arena); + window->layout = CalculateLayout(&window->layout_arena, window->buffer, window->font, window->font_size, window->font_spacing); +} + +void BeforeEdit(Window *window) { + Assert(window->history.debug_edit_phase == 0); + window->history.debug_edit_phase += 1; + + { + History &h = window->history; + HistoryEntry *entry = h.entries.alloc(); + Allocator sys_allocator = GetSystemAllocator(); + Assert(window->cursors.len); + entry->cursors = window->cursors.tight_copy(sys_allocator); + } + + MergeCursors(window); +} + +void ApplyEdits(Window *window, Array edits) { + Assert(window->history.debug_edit_phase == 1); + window->history.debug_edit_phase += 1; + + { + HistoryEntry *entry = window->history.entries.last(); + Allocator sys_allocator = GetSystemAllocator(); + entry->edits = edits.tight_copy(sys_allocator); + For(entry->edits) { + // Need to compile reverse 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; + } + } + + _ApplyEdits(&window->buffer, edits); +} + void AfterEdit(Window *window, Array edits) { + Assert(window->history.debug_edit_phase == 2); + window->history.debug_edit_phase -= 2; + + HistoryEntry *entry = window->history.entries.last(); + Assert(entry->cursors.len); + Assert(entry->edits.len); + // Offset all cursors by edits ForItem(edit, edits) { int64_t remove_size = GetRangeSize(edit.range); @@ -308,6 +379,7 @@ void AfterEdit(Window *window, Array edits) { // Make sure all cursors are in range For(window->cursors) it.range = Clamp(window->buffer, it.range); + // Generate layout Clear(&window->layout_arena); window->layout = CalculateLayout(&window->layout_arena, window->buffer, window->font, window->font_size, window->font_spacing); } diff --git a/src/text_editor/main.cpp b/src/text_editor/main.cpp index 97ecab4..1b42a06 100644 --- a/src/text_editor/main.cpp +++ b/src/text_editor/main.cpp @@ -63,27 +63,26 @@ int main() { window.font_size = font_size; window.font_spacing; InitArena(&window.layout_arena); - InitBuffer(GetSystemAllocator(), &window.buffer); - if (1) { - // Array edits = {FrameArena}; - // AddEdit(&edits, GetEnd(window.buffer), Format(FrameArena, "line number: 1")); - // ApplyEdits(&window.buffer, edits); - for (int i = 0; i < 50; i += 1) { - Array edits = {FrameArena}; - AddEdit(&edits, GetEnd(window.buffer), Format(FrameArena, "line number: %d\n", i)); - ApplyEdits(&window.buffer, edits); - } - } window.cursors.allocator = GetSystemAllocator(); window.cursors.add({}); - AfterEdit(&window, {}); + if (1) { + for (int i = 0; i < 50; i += 1) { + BeforeEdit(&window); + Array edits = {FrameArena}; + AddEdit(&edits, GetEnd(window.buffer), Format(FrameArena, "line number: %d\n", i)); + ApplyEdits(&window, edits); + AfterEdit(&window, edits); + } + } + + *window.cursors.last() = {}; windows.add(window); } - // EnableEventWaiting(); + int64_t frame = -1; InitEventRecording(); while (!WindowShouldClose()) { For(windows) { @@ -91,6 +90,9 @@ int main() { it.main_cursor_begin_frame = it.cursors[0]; } + frame += 1; + Dbg("%lld", (long long)frame); + UpdateEventRecording(); { @@ -100,7 +102,7 @@ int main() { focused_window->scroll.y -= mouse_wheel; focused_window->scroll.y = ClampBottom(focused_window->scroll.y, 0.f); - BeforeEdit(focused_window); // Merge cursors + MergeCursors(focused_window); if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_A)) { focused_window->cursors.clear(); focused_window->cursors.add(MakeCursor(0, focused_window->buffer.len)); @@ -167,7 +169,7 @@ int main() { cursors_to_add.add({front, front}); } For(cursors_to_add) focused_window->cursors.add(it); - BeforeEdit(focused_window); // Merge cursors + MergeCursors(focused_window); } 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}; @@ -177,7 +179,7 @@ int main() { String string = GetString(focused_window->buffer, line.range); AddEdit(&edits, {line.range.max, line.range.max}, string); } - ApplyEdits(&focused_window->buffer, edits); + ApplyEdits(focused_window, edits); AfterEdit(focused_window, edits); For(focused_window->cursors) { it.range.max = it.range.min = MoveDown(focused_window->buffer, it.range.min); @@ -204,7 +206,7 @@ int main() { cursors_to_add.add({front, front}); } For(cursors_to_add) focused_window->cursors.add(it); - BeforeEdit(focused_window); // Merge cursors + MergeCursors(focused_window); } 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}; @@ -214,7 +216,7 @@ int main() { String string = GetString(focused_window->buffer, line.range); AddEdit(&edits, {line.range.min, line.range.min}, string); } - ApplyEdits(&focused_window->buffer, edits); + ApplyEdits(focused_window, edits); AfterEdit(focused_window, edits); For(focused_window->cursors) { it.range.max = it.range.min = MoveUp(focused_window->buffer, it.range.min); @@ -240,7 +242,7 @@ int main() { if (Seek(to_seek, needle, &found_index, SeekFlag_IgnoreCase)) { found_index += cursor.range.max; focused_window->cursors.add(MakeCursor(found_index + needle.len, found_index)); - BeforeEdit(focused_window); // Merge cursors + MergeCursors(focused_window); } } @@ -288,7 +290,7 @@ int main() { For(focused_window->cursors) { AddEdit(&edits, it.range, string); } - ApplyEdits(&focused_window->buffer, edits); + ApplyEdits(focused_window, edits); AfterEdit(focused_window, edits); } if (IsKeyDown(KEY_LEFT_CONTROL) && (IsKeyPressed(KEY_X) || IsKeyPressedRepeat(KEY_X))) { @@ -310,10 +312,14 @@ int main() { } String to_save = Merge(FrameArena, strings, "\n"); SetClipboardText(to_save.data); - ApplyEdits(&focused_window->buffer, edits); + ApplyEdits(focused_window, edits); AfterEdit(focused_window, edits); } + if (IsKeyDown(KEY_LEFT_CONTROL) && (IsKeyPressed(KEY_Z) || IsKeyPressedRepeat(KEY_Z))) { + UndoEdit(focused_window); + } + if (IsKeyPressed(KEY_DELETE) || IsKeyPressedRepeat(KEY_DELETE)) { // Select things to delete For(focused_window->cursors) { @@ -334,7 +340,7 @@ int main() { Array edits = {FrameArena}; String string = {}; For(focused_window->cursors) AddEdit(&edits, it.range, string); - ApplyEdits(&focused_window->buffer, edits); + ApplyEdits(focused_window, edits); AfterEdit(focused_window, edits); } @@ -358,7 +364,7 @@ int main() { Array edits = {FrameArena}; String string = {}; For(focused_window->cursors) AddEdit(&edits, it.range, string); - ApplyEdits(&focused_window->buffer, edits); + ApplyEdits(focused_window, edits); AfterEdit(focused_window, edits); } @@ -366,7 +372,7 @@ int main() { BeforeEdit(focused_window); Array edits = {FrameArena}; For(focused_window->cursors) AddEdit(&edits, it.range, " "); - ApplyEdits(&focused_window->buffer, edits); + ApplyEdits(focused_window, edits); AfterEdit(focused_window, edits); } @@ -382,7 +388,7 @@ int main() { BeforeEdit(focused_window); Array edits = {FrameArena}; For(focused_window->cursors) AddEdit(&edits, it.range, "\n"); - ApplyEdits(&focused_window->buffer, edits); + ApplyEdits(focused_window, edits); AfterEdit(focused_window, edits); } @@ -402,7 +408,7 @@ int main() { For(focused_window->cursors) { AddEdit(&edits, it.range, string); } - ApplyEdits(&focused_window->buffer, edits); + ApplyEdits(focused_window, edits); AfterEdit(focused_window, edits); } EventRecording_SimulateGetCharPressed(focused_window); @@ -457,7 +463,7 @@ int main() { window.cursors.clear(); } window.cursors.add({col->pos, col->pos}); - BeforeEdit(&window); // Merge cursors + MergeCursors(&window); window.mouse_selecting = true; } @@ -572,6 +578,14 @@ int main() { Dbg_Draw(); EventRecording_Draw(); + + Window *focused_window = &windows[0]; + if (focused_window->mouse_selecting || EventPlaying) { + DisableEventWaiting(); + } else { + EnableEventWaiting(); + } + EndDrawing(); } } \ No newline at end of file