From d7908bee545eb3e4fed89729646212a5efe04afe Mon Sep 17 00:00:00 2001 From: krzosa Date: Fri, 2 May 2025 12:02:13 +0200 Subject: [PATCH] Improving basic commands --- src/basic/string16.cpp | 9 +++ src/text_editor/buffer.cpp | 26 ++++--- src/text_editor/buffer.h | 2 +- src/text_editor/commands.cpp | 99 +++++++++++++++----------- src/text_editor/commands_bindings.cpp | 23 ++---- src/text_editor/commands_clipboard.cpp | 6 +- src/text_editor/lua_api.cpp | 44 ++++++++---- src/text_editor/management.cpp | 4 ++ src/text_editor/text_editor.h | 3 + 9 files changed, 126 insertions(+), 90 deletions(-) diff --git a/src/basic/string16.cpp b/src/basic/string16.cpp index 2bac1dc..29aeeb3 100644 --- a/src/basic/string16.cpp +++ b/src/basic/string16.cpp @@ -249,6 +249,14 @@ String16 Copy16(Allocator allocator, char16_t *string) { return Copy(allocator, s); } +String16 Concat(Allocator allocator, String16 a, String16 b) { + char16_t *str = AllocArray(allocator, char16_t, a.len + b.len + 1); + MemoryCopy(str, a.data, a.len * 2); + MemoryCopy(str + a.len, b.data, b.len * 2); + str[a.len + b.len] = 0; + return {str, a.len + b.len}; +} + void NormalizePathInPlace(String16 s) { for (int64_t i = 0; i < s.len; i++) { if (s.data[i] == u'\\') @@ -304,3 +312,4 @@ String16 SkipWhitespace(String16 *string) { } return begin; } + diff --git a/src/text_editor/buffer.cpp b/src/text_editor/buffer.cpp index 4d175d2..a1c14ca 100644 --- a/src/text_editor/buffer.cpp +++ b/src/text_editor/buffer.cpp @@ -404,10 +404,10 @@ Int GetFullLineStart(Buffer *buffer, Int pos) { return range.min; } -Int GetFullLineEnd(Buffer *buffer, Int pos) { +Int GetFullLineEnd(Buffer *buffer, Int pos, Int *eof = NULL) { pos = Clamp(pos, (Int)0, buffer->len); Int line = PosToLine(buffer, pos); - Range range = GetLineRange(buffer, line); + Range range = GetLineRange(buffer, line, eof); return range.max; } @@ -1018,14 +1018,6 @@ void UndoEdit(Buffer *buffer, Array *carets) { Dealloc(&entry.edits); } -void RawApplyEdits(Buffer *buffer, Array &edits) { - ProfileFunction(); - Assert(buffer->edit_phase == 1); - buffer->edit_phase += 1; - SaveHistoryBeforeApplyEdits(buffer, &buffer->undo_stack, edits); - ApplyEditsMultiCursor(buffer, edits); -} - void DeallocHistoryEntries(Array *entries) { For(*entries) { Dealloc(&it.carets); @@ -1093,7 +1085,19 @@ void AdjustCarets(Array edits, Array *carets) { void EndEdit(Buffer *buffer, Array *edits, Array *carets, bool kill_selection) { ProfileFunction(); - RawApplyEdits(buffer, *edits); + + { + Assert(buffer->edit_phase == 1); + buffer->edit_phase += 1; + if (edits->len) { + SaveHistoryBeforeApplyEdits(buffer, &buffer->undo_stack, *edits); + ApplyEditsMultiCursor(buffer, *edits); + } else { + HistoryEntry entry = Pop(&buffer->undo_stack); + Dealloc(&entry.carets); + Assert(entry.edits.len == 0); + } + } Assert(buffer->edit_phase == 2); buffer->edit_phase -= 2; diff --git a/src/text_editor/buffer.h b/src/text_editor/buffer.h index ad33617..bc066ee 100644 --- a/src/text_editor/buffer.h +++ b/src/text_editor/buffer.h @@ -52,7 +52,7 @@ enum { void SaveCaretHistoryBeforeBeginEdit(Buffer *buffer, Array &carets); Array BeginEdit(Allocator allocator, Buffer *buffer, Array &carets); -void EndEdit(Buffer *buffer, Array *edits, Array *carets, bool kill_selection = true); +void EndEdit(Buffer *buffer, Array *edits, Array *carets, bool kill_selection); void AddEdit(Array *e, Range range, String16 string); // Merge carets that overlap, this needs to be handled before any edits to diff --git a/src/text_editor/commands.cpp b/src/text_editor/commands.cpp index 93491d8..cb81805 100644 --- a/src/text_editor/commands.cpp +++ b/src/text_editor/commands.cpp @@ -234,6 +234,8 @@ void ReportWarningf(const char *fmt, ...) { void Command_MoveCursorsByPageSize(Window *window, int direction, bool shift = false) { Assert(direction == DIR_UP || direction == DIR_DOWN); BSet set = GetBSet(window); + CheckpointBeforeGoto(window); + Rect2I visible_cells_rect = GetVisibleCells(window); Int y = GetSize(visible_cells_rect).y - 2; @@ -388,6 +390,7 @@ void Command_MoveLine(View *view, int direction) { Assert(direction == DIR_DOWN || direction == DIR_UP); Scratch scratch; + // @todo: this doesn't work well at the end of buffer struct XYPair { XY front; XY back; @@ -398,26 +401,36 @@ void Command_MoveLine(View *view, int direction) { MergeCarets(buffer, &view->carets); Array saved_xy = {scratch}; For(view->carets) { - Add(&saved_xy, {PosToXY(buffer, GetFront(it)), PosToXY(buffer, GetBack(it))}); - Range lines_to_move_range = {GetFullLineStart(buffer, it.range.min), GetFullLineEnd(buffer, it.range.max)}; + Int eof_current = 0; + Range lines_to_move_range = {GetFullLineStart(buffer, it.range.min), GetFullLineEnd(buffer, it.range.max, &eof_current)}; + if (lines_to_move_range.min == 0 && direction == DIR_UP) { + continue; + } + + Int eof = 0; + Int next_line_start = lines_to_move_range.max; + Int next_line_end = GetFullLineEnd(buffer, next_line_start, &eof); + Int prev_line_end = lines_to_move_range.min - 1; + Int prev_line_start = GetFullLineStart(buffer, prev_line_end); + + if (direction == DIR_DOWN && eof) { + continue; + } else if (direction == DIR_UP && eof_current) { + continue; + } + + String16 string = Copy(scratch, GetString(buffer, lines_to_move_range)); - String16 string = GetString(buffer, lines_to_move_range); - string = Copy(scratch, string); AddEdit(&edits, lines_to_move_range, {}); - - // @todo: GetPrevLine, GetNextLine, GetNextFull - // GetPrevLineStart, GetNextLineEnd if (direction == DIR_DOWN) { - Int next_line_start = lines_to_move_range.max; - Int next_line_end = GetFullLineEnd(buffer, next_line_start); AddEdit(&edits, Rng(next_line_end), string); } else { - Int prev_line_end = lines_to_move_range.min - 1; - Int prev_line_start = GetFullLineStart(buffer, prev_line_end); AddEdit(&edits, Rng(prev_line_start), string); } + + Add(&saved_xy, {PosToXY(buffer, GetFront(it)), PosToXY(buffer, GetBack(it))}); } - EndEdit(buffer, &edits, &view->carets); + EndEdit(buffer, &edits, &view->carets, !KILL_SELECTION); Int line_offset = direction == DIR_UP ? -1 : +1; for (Int i = 0; i < saved_xy.len; i += 1) { @@ -437,7 +450,7 @@ Array Command_ReplaceEx(Allocator scratch, View *view, String16 string) { Array edits = BeginEdit(scratch, buffer, view->carets); MergeCarets(buffer, &view->carets); For(view->carets) AddEdit(&edits, it.range, string); - EndEdit(buffer, &edits, &view->carets); + EndEdit(buffer, &edits, &view->carets, KILL_SELECTION); return edits; } @@ -452,20 +465,37 @@ void Command_DuplicateLine(View *view, int direction) { Buffer *buffer = GetBuffer(view->active_buffer); BeginEdit(scratch, buffer, view->carets); - For(view->carets) it = MakeCaret(GetFront(it)); MergeCarets(buffer, &view->carets); Array edits = {scratch}; For(view->carets) { - Int line = PosToLine(buffer, it.range.min); - Range range = GetLineRange(buffer, line); + Int eof = 0; + Range range = {}; + range.max = GetFullLineEnd(buffer, it.range.max, &eof); + range.min = GetFullLineStart(buffer, it.range.min); + range.min -= Clamp(eof, (Int)0, buffer->len); String16 string = Copy(scratch, GetString(buffer, range)); - Int pos = direction == DIR_UP ? range.min : range.max; + + Int pos = direction == DIR_UP ? range.min : range.max; AddEdit(&edits, Rng(pos), string); } - EndEdit(buffer, &edits, &view->carets); + EndEdit(buffer, &edits, &view->carets, !KILL_SELECTION); - Command_Move(view, direction); + Int coef = direction == DIR_UP ? -1 : 1; + for (Int i = 0; i < edits.len; i += 1) { + Edit *edit = edits.data + i; + Caret *caret = view->carets.data + i; + + XY xymin = PosToXY(buffer, caret->range.min); + XY xymax = PosToXY(buffer, caret->range.max); + Int line_count = xymax.line - xymin.line + 1; + + xymin.line += coef * line_count; + xymax.line += coef * line_count; + + caret->range.min = XYToPos(buffer, xymin); + caret->range.max = XYToPos(buffer, xymax); + } } Array GetSelectedLinesSorted(Allocator allocator, View *view) { @@ -618,28 +648,15 @@ void SaveBuffer(View *view) { void Command_KillSelectedLines(View *view) { Scratch scratch; Buffer *buffer = GetBuffer(view->active_buffer); + SaveCaretHistoryBeforeBeginEdit(buffer, view->carets); - Array edits = BeginEdit(scratch, buffer, view->carets); - MergeCarets(buffer, &view->carets); - - Array lines = GetSelectedLinesSorted(scratch, view); - For(lines) { - - Range add_range = GetLineRange(buffer, it.min); - for (Int i = it.min + 1; i < it.max; i += 1) { - Range line_range = GetLineRange(buffer, i); - add_range.max = Max(line_range.max, add_range.max); - } - AddEdit(&edits, add_range, u""); + For (view->carets) { + Int eof = 0; + it.range.max = GetFullLineEnd(buffer, it.range.max, &eof); + it.range.min = GetFullLineStart(buffer, it.range.min); + it.range.min -= Clamp(eof, (Int)0, buffer->len); } - - For(view->carets) { - Int line = PosToLine(buffer, it.range.min); - Range range = GetLineRange(buffer, line); - it.range.min = it.range.max = range.min; - } - - EndEdit(buffer, &edits, &view->carets, KILL_SELECTION); + Command_Replace(view, u""); } void Command_Delete(View *view, int direction, bool ctrl = false) { @@ -675,7 +692,7 @@ void Command_Delete(View *view, int direction, bool ctrl = false) { MergeCarets(buffer, &view->carets); For(view->carets) AddEdit(&edits, it.range, {}); - EndEdit(buffer, &edits, &view->carets); + EndEdit(buffer, &edits, &view->carets, true); } void Command_SelectAll(View *view, String16 needle) { @@ -782,7 +799,7 @@ void Command_IdentedNewLine(View *view) { String16 string16 = ToString16(scratch, string); AddEdit(&edits, it.range, string16); } - EndEdit(buffer, &edits, &view->carets); + EndEdit(buffer, &edits, &view->carets, KILL_SELECTION); } void Command_Find(View *seek_view, String16 needle, bool forward = true) { diff --git a/src/text_editor/commands_bindings.cpp b/src/text_editor/commands_bindings.cpp index a82fb6e..076a812 100644 --- a/src/text_editor/commands_bindings.cpp +++ b/src/text_editor/commands_bindings.cpp @@ -219,9 +219,11 @@ void OnCommand(Event event) { Assert(DocumentSelected.id == -1); BSet active = GetActiveSet(); + bool mouse_in_document = CheckCollisionPointRec(mouse, active.window->document_rect); bool mouse_in_line_numbers = CheckCollisionPointRec(mouse, active.window->line_numbers_rect); if (mouse_in_document || mouse_in_line_numbers) { + CheckpointBeforeGoto(active.window); DocumentSelected = active.window->id; Int p = ScreenSpaceToBufferPos(active.window, active.view, active.buffer, mouse); @@ -511,12 +513,7 @@ void OnCommand(Event event) { } if (CtrlPress(SDLK_PERIOD)) { - String string = main.buffer->name; - String right_part = CutLastSlash(&string); - if (StartsWith(right_part, "/+")) { - CutLastSlash(&string); - } - Command_Open(string); + Command_Open(ChopLastSlash(main.buffer->name)); } if (CtrlPress(SDLK_T)) { @@ -528,19 +525,7 @@ void OnCommand(Event event) { } - if (CtrlShiftPress(SDLK_Q)) { - Scratch scratch; - Caret caret = active.view->carets[0]; - Range range = caret.range; - if (GetSize(caret.range) == 0) range = EncloseLoadWord(active.buffer, GetFront(caret)); - String16 string = NormalizePath(scratch, GetString(active.buffer, range)); - String16 right_part = CutLastSlash(&string); - if (right_part.len > 1 && right_part.data[1] == u'+') { - CutLastSlash(&string); - } - - Command_Open(string); - } else if (CtrlPress(SDLK_Q)) { + if (CtrlPress(SDLK_Q)) { Caret caret = active.view->carets[0]; Range range = caret.range; if (GetSize(caret.range) == 0) range = EncloseLoadWord(active.buffer, GetFront(caret)); diff --git a/src/text_editor/commands_clipboard.cpp b/src/text_editor/commands_clipboard.cpp index 1d16e9d..6dbd1c0 100644 --- a/src/text_editor/commands_clipboard.cpp +++ b/src/text_editor/commands_clipboard.cpp @@ -66,7 +66,7 @@ void Command_Paste(View *view) { Array edits = BeginEdit(scratch, buffer, view->carets); MergeCarets(buffer, &view->carets); For(view->carets) AddEdit(&edits, it.range, string); - EndEdit(buffer, &edits, &view->carets); + EndEdit(buffer, &edits, &view->carets, KILL_SELECTION); return; } @@ -78,5 +78,5 @@ void Command_Paste(View *view) { Caret &it = view->carets[i]; AddEdit(&edits, it.range, string); } - EndEdit(buffer, &edits, &view->carets); -} \ No newline at end of file + EndEdit(buffer, &edits, &view->carets, KILL_SELECTION); +} diff --git a/src/text_editor/lua_api.cpp b/src/text_editor/lua_api.cpp index d41a49a..2d58d36 100644 --- a/src/text_editor/lua_api.cpp +++ b/src/text_editor/lua_api.cpp @@ -51,22 +51,27 @@ View *Command_ExecHidden(String buffer_name, String cmd, String working_dir) { // ActiveWindow = set.window->id; // } -void Command_Exec(String cmd, String working_dir) { +BSet Command_BeginConsoleJump() { BSet main = GetActiveMainSet(); CheckpointBeforeGoto(main.window); + main.buffer = GetBuffer(NullBufferID); + main.view = WindowOpenBufferView(main.window, main.buffer->name); + return main; +} - Buffer *buffer = GetBuffer(NullBufferID); - View *view = WindowOpenBufferView(main.window, buffer->name); - Exec(view->id, true, cmd, working_dir); - - Int pos = XYToPos(buffer, {0, buffer->line_starts.len - 1}); - view->carets[0] = MakeCaret(pos); +void Command_EndConsoleJump(BSet main) { + Int pos = XYToPos(main.buffer, {0, main.buffer->line_starts.len - 1}); + main.view->carets[0] = MakeCaret(pos); UpdateScroll(main.window, true); - - main.window->active_view = view->id; ActiveWindow = main.window->id; } +void Command_Exec(String cmd, String working_dir) { + BSet set = Command_BeginConsoleJump(); + Exec(set.view->id, true, cmd, working_dir); + Command_EndConsoleJump(set); +} + void Command_Open(String path) { Scratch scratch; @@ -88,12 +93,21 @@ void Command_Open(String path) { String col_string = FieldString(LuaState, "col"); Int col = strtoll(col_string.data, NULL, 10); - CheckpointBeforeGoto(main.window); - View *view = WindowOpenBufferView(main.window, file_path); - Buffer *buffer = GetBuffer(view->active_buffer); - if (line != -1 && col != -1) { - Int pos = XYToPos(buffer, {col - 1, line - 1}); - view->carets[0] = MakeCaret(pos); + if (IsDir(file_path)) { + BSet set = Command_BeginConsoleJump(); + Command_Appendf(set.view, "%.*s..\n", FmtString(file_path)); + for (FileIter it = IterateFiles(scratch, file_path); IsValid(it); Advance(&it)) { + Command_Appendf(set.view, "%.*s\n", FmtString(it.absolute_path)); + } + Command_EndConsoleJump(set); + } else { + CheckpointBeforeGoto(main.window); + View *view = WindowOpenBufferView(main.window, file_path); + Buffer *buffer = GetBuffer(view->active_buffer); + if (line != -1 && col != -1) { + Int pos = XYToPos(buffer, {col - 1, line - 1}); + view->carets[0] = MakeCaret(pos); + } } UpdateScroll(main.window, true); ActiveWindow = main.window->id; diff --git a/src/text_editor/management.cpp b/src/text_editor/management.cpp index ff7dc45..b922fa2 100644 --- a/src/text_editor/management.cpp +++ b/src/text_editor/management.cpp @@ -255,6 +255,10 @@ String Command_GetDir() { } Int ConvertUTF8ToUTF16UnixLine(String string, char16_t *buffer, Int buffer_cap) { + if (string.len == 0) { + return 0; + } + Int buffer_len = 0; Assert(buffer_cap > string.len * 2); for (Int i = 0; i < string.len;) { diff --git a/src/text_editor/text_editor.h b/src/text_editor/text_editor.h index a5fa7e5..3d72d54 100644 --- a/src/text_editor/text_editor.h +++ b/src/text_editor/text_editor.h @@ -96,6 +96,9 @@ void Command_Append(View *view, String string, bool scroll_to_end_if_curs Array Command_ReplaceEx(Allocator scratch, View *view, String16 string); void Command_ReplaceWithoutMovingCarets(View *view, Range range, String16 string); +void Command_Copy(View *view); +void Command_Paste(View *view); + void ReportConsolef(const char *fmt, ...); void ReportErrorf(const char *fmt, ...); void ReportWarningf(const char *fmt, ...);