diff --git a/src/text_editor/buffer.cpp b/src/text_editor/buffer.cpp index 7cd5155..c7b9345 100644 --- a/src/text_editor/buffer.cpp +++ b/src/text_editor/buffer.cpp @@ -996,7 +996,7 @@ API void UndoEdit(Buffer *buffer, Array *carets) { if (buffer->undo_stack.len > 0) { HistoryEntry *next = GetLast(buffer->undo_stack); - if (entry.time - next->time <= 0.3) { + if (entry.time - next->time <= StyleUndoMergeTimeout) { UndoEdit(buffer, carets); } } @@ -1017,6 +1017,16 @@ API void ResetHistory(Buffer *buffer) { DeallocHistoryEntries(&buffer->undo_stack); } +API void ResetBuffer(Buffer *buffer) { + ResetHistory(buffer); + buffer->change_id += 1; + buffer->line_starts.len = 0; + buffer->len = 0; + if (!buffer->no_line_starts) { + Add(&buffer->line_starts, (Int)0); + } +} + void ClearRedoStack(Buffer *buffer) { DeallocHistoryEntries(&buffer->redo_stack); } diff --git a/src/text_editor/commands.cpp b/src/text_editor/commands.cpp index 3d053ed..6466868 100644 --- a/src/text_editor/commands.cpp +++ b/src/text_editor/commands.cpp @@ -1,4 +1,64 @@ +void CheckpointBeforeGoto(Window *window, View *view) { + if (window->jump_history == false) return; + Add(&window->goto_history, {view->id, view->carets[0], GetTimeSeconds()}); + window->goto_redo.len = 0; +} + +void CheckpointBeforeGoto(Window *window) { + CheckpointBeforeGoto(window, GetView(window->active_view)); +} + +GotoCrumb GetCrumb(Array *cr) { + for (; cr->len;) { + GotoCrumb c = Pop(cr); + View *view = FindView(c.view_id); + if (view) return c; + } + return {}; +} + +void GotoBackward(Window *window) { + if (window->jump_history == false) return; + if (window->goto_history.len <= 0) return; + BSet set = GetBSet(window); + Add(&window->goto_redo, {set.view->id, set.view->carets[0], GetTimeSeconds()}); + + GotoCrumb c = GetCrumb(&window->goto_history); + window->active_view = c.view_id; + View *view = GetView(c.view_id); + view->carets[0] = c.caret; + UpdateScroll(window, true); + + if (window->goto_history.len) { + GotoCrumb *next = GetLast(window->goto_history); + if (c.time - next->time <= StyleUndoMergeTimeout) { + GotoBackward(window); + } + } +} + +void GotoForward(Window *window) { + if (window->goto_redo.len <= 0) return; + if (window->jump_history == false) return; + BSet set = GetBSet(window); + Add(&window->goto_history, {set.view->id, set.view->carets[0], GetTimeSeconds()}); + + GotoCrumb c = GetCrumb(&window->goto_redo); + window->active_view = c.view_id; + View *view = GetView(c.view_id); + view->carets[0] = c.caret; + UpdateScroll(window, true); + + if (window->goto_redo.len) { + GotoCrumb *next = GetLast(window->goto_redo); + if (c.time - next->time <= StyleUndoMergeTimeout) { + GotoForward(window); + } + } +} + void JumpGarbageBuffer(BSet *set, String buffer_name = "") { + CheckpointBeforeGoto(set->window); if (buffer_name.len == 0) { String current_dir = ChopLastSlash(set->buffer->name); buffer_name = GetUniqueBufferName(current_dir, "temp"); @@ -9,6 +69,7 @@ void JumpGarbageBuffer(BSet *set, String buffer_name = "") { } void Command_BeginJump(BSet *set, BufferID buffer_id = NullBufferID) { + CheckpointBeforeGoto(set->window); set->buffer = GetBuffer(buffer_id); set->view = WindowOpenBufferView(set->window, set->buffer->name); } @@ -855,6 +916,43 @@ void Command_Find(View *seek_view, String16 needle, bool forward = true) { IF_DEBUG(AssertRanges(seek_view->carets)); } +void Command_GotoNextInList(Window *window, Int line_offset = 1) { + Assert(line_offset == 1 || line_offset == -1); + View *active_view = GetView(window->active_view); + + View *view_goto = GetView(window->active_goto_list); + window->active_view = view_goto->id; + + Buffer *buffer_goto = GetBuffer(view_goto->active_buffer); + int64_t pos = window->goto_list_pos; + Int line = PosToLine(buffer_goto, pos); + + bool opened = false; + for (Int i = line + line_offset; i >= 0 && i < buffer_goto->line_starts.len; i += line_offset) { + Range line_range = GetLineRangeWithoutNL(buffer_goto, i); + String16 line = GetString(buffer_goto, line_range); + view_goto->carets[0] = MakeCaret(line_range.min); + window->goto_list_pos = line_range.min; + line = Trim(line); + + MergeCarets(buffer_goto, &view_goto->carets); + IF_DEBUG(AssertRanges(view_goto->carets)); + if (line.len == 0) { + continue; + } + + BSet set = Command_Open(line, "goto_build"); + if (set.window == NULL) { + continue; + } + + opened = true; + break; + } + + if (!opened) window->active_view = active_view->id; +} + void Command_FuzzySort(View *view, String16 needle) { Buffer *buffer = GetBuffer(view->active_buffer); @@ -1051,7 +1149,8 @@ BSet Command_Open(Window *window, String path, String meta, bool set_active = tr Command_Appendf(set.view, "%S\n", it.filename); } } else { - View *view = WindowOpenBufferView(set.window, ores.file_path); + CheckpointBeforeGoto(set.window); + View *view = WindowOpenBufferView(set.window, ores.file_path); Buffer *buffer = GetBuffer(view->active_buffer); if (ores.line != -1) { if (ores.col == -1) ores.col = 1; @@ -1122,6 +1221,8 @@ int Lua_Cmd(lua_State *L) { BSet main = GetLastActiveLayoutSet(); if (kind == "console") { BSet set = GetConsoleSet(); + main.window->active_goto_list = set.view->id; + main.window->goto_list_pos = set.buffer->len; Command_SelectRangeOneCursor(set.view, MakeRange(set.buffer->len)); Command_BeginJump(&set); Exec(set.view->id, true, cmd, working_dir); @@ -1133,6 +1234,8 @@ int Lua_Cmd(lua_State *L) { ActiveWindow = main.window->id; } else { JumpGarbageBuffer(&main); + main.window->active_goto_list = main.view->id; + main.window->goto_list_pos = 0; Exec(main.view->id, true, cmd, working_dir); ActiveWindow = main.window->id; } @@ -1140,10 +1243,11 @@ int Lua_Cmd(lua_State *L) { return 0; } -void Command_ListBuffers() { - BSet main = GetActiveSet(); +void Command_ShowBufferList() { + BSet main = GetBSet(CommandBarWindowID); + main.window->visible = true; ActiveWindow = main.window->id; - JumpGarbageBuffer(&main); + ResetBuffer(main.buffer); For(Buffers) { RawAppendf(main.buffer, "%-80S || %S\n", SkipToLastSlash(it->name), it->name); } @@ -1152,11 +1256,6 @@ void Command_ListBuffers() { Command_SelectRangeOneCursor(main.view, GetBufferEndAsRange(main.buffer)); } -int Lua_ListBuffers(lua_State *L) { - Command_ListBuffers(); - return 0; -} - void Command_ListViews() { BSet main = GetLastActiveLayoutSet(); ActiveWindow = main.window->id; diff --git a/src/text_editor/commands_bindings.cpp b/src/text_editor/commands_bindings.cpp index 348716a..f6360df 100644 --- a/src/text_editor/commands_bindings.cpp +++ b/src/text_editor/commands_bindings.cpp @@ -126,6 +126,7 @@ void OnCommand(Event event) { Assert(ScrollbarSelected.id == -1); BSet selected = GetBSet(DocumentSelected); + Vec2I mouse = MouseVec2I(); // Special case for full-screen where we can have document // aligned with monitor screen in which case mouse cursor cannot @@ -184,6 +185,13 @@ void OnCommand(Event event) { } } + if (Mouse(X2)) { + GotoForward(GetLastActiveLayoutSet().window); + } + if (Mouse(X1)) { + GotoBackward(GetLastActiveLayoutSet().window); + } + if (Ctrl() && Shift() && Mouse(RIGHT)) { } else if (Alt() && Ctrl() && Mouse(RIGHT)) { @@ -232,6 +240,8 @@ void OnCommand(Event event) { bool mouse_in_line_numbers = AreOverlapping(mouse, active.window->line_numbers_rect); if (mouse_in_document || mouse_in_line_numbers) { DocumentSelected = active.window->id; + CheckpointBeforeGoto(active.window); + Int p = ScreenSpaceToBufferPos(active.window, active.view, active.buffer, mouse); if (Alt()) Insert(&active.view->carets, MakeCaret(p, p), 0); @@ -293,12 +303,7 @@ void OnCommand(Event event) { if (CtrlAltPress(SDLK_P)) { } else if (CtrlPress(SDLK_P)) { - // Command_ListCode(); - Window *window = GetWindow(CommandBarWindowID); - window->visible = !window->visible; - ActiveWindow = window->id; - BSet set = GetBSet(window); - Command_ListBuffers(); + Command_ShowBufferList(); } if (CtrlPress(SDLK_0)) { @@ -350,6 +355,10 @@ void OnCommand(Event event) { WindowOpenBufferView(active.window, event.text); } + if (Press(SDLK_DOWN) || Press(SDLK_RIGHT) || Press(SDLK_LEFT) || Press(SDLK_UP)) { + CheckpointBeforeGoto(active.window); + } + if (CtrlAltPress(SDLK_DOWN)) { Command_DuplicateLine(active.view, DIR_DOWN); } else if (AltShiftPress(SDLK_DOWN)) { @@ -428,30 +437,40 @@ void OnCommand(Event event) { } if (ShiftPress(SDLK_PAGEUP)) { + CheckpointBeforeGoto(active.window); Command_MoveCursorsByPageSize(active.window, DIR_UP, SHIFT_PRESS); } else if (CtrlPress(SDLK_PAGEUP)) { + CheckpointBeforeGoto(active.window); Command_SelectRangeOneCursor(active.view, MakeRange(0)); } else if (Press(SDLK_PAGEUP)) { + CheckpointBeforeGoto(active.window); Command_MoveCursorsByPageSize(active.window, DIR_UP); } if (ShiftPress(SDLK_PAGEDOWN)) { + CheckpointBeforeGoto(active.window); Command_MoveCursorsByPageSize(active.window, DIR_DOWN, SHIFT_PRESS); } else if (CtrlPress(SDLK_PAGEDOWN)) { + CheckpointBeforeGoto(active.window); Command_SelectRangeOneCursor(active.view, MakeRange(active.buffer->len)); } else if (Press(SDLK_PAGEDOWN)) { + CheckpointBeforeGoto(active.window); Command_MoveCursorsByPageSize(active.window, DIR_DOWN); } if (ShiftPress(SDLK_HOME)) { + CheckpointBeforeGoto(active.window); Command_MoveCursorsToSide(active.view, DIR_LEFT, SHIFT_PRESS); } else if (Press(SDLK_HOME)) { + CheckpointBeforeGoto(active.window); Command_MoveCursorsToSide(active.view, DIR_LEFT); } if (ShiftPress(SDLK_END)) { + CheckpointBeforeGoto(active.window); Command_MoveCursorsToSide(active.view, DIR_RIGHT, SHIFT_PRESS); } else if (Press(SDLK_END)) { + CheckpointBeforeGoto(active.window); Command_MoveCursorsToSide(active.view, DIR_RIGHT); } @@ -494,6 +513,7 @@ void OnCommand(Event event) { } if (CtrlPress(SDLK_D)) { + CheckpointBeforeGoto(active.window); String16 string = GetString(active.buffer, active.view->carets[0].range); Caret caret = FindNext(active.buffer, string, active.view->carets[0]); Insert(&active.view->carets, caret, 0); @@ -558,10 +578,6 @@ void OnCommand(Event event) { window->visible = !window->visible; } - // if (CtrlPress(SDLK_N)) { - // Command_New(); - // } - if (CtrlPress(SDLK_S)) { SaveBuffer(active.buffer); } @@ -585,6 +601,10 @@ void OnCommand(Event event) { } + if (AltPress(SDLK_Q)) { + GotoBackward(main.window); + } + if (CtrlPress(SDLK_Q)) { if (active.view->fuzzy_search) { @@ -596,7 +616,15 @@ void OnCommand(Event event) { if (Press(SDLK_ESCAPE)) { active.view->carets.len = 1; - active.view->carets[0] = MakeCaret(GetFront(active.view->carets[0])); + active.view->carets[0] = MakeCaret(GetFront(active.view->carets[0])); + + if (active.window->lose_focus_on_escape && active.window->id == ActiveWindow) { + if (active.window->layout) { + // + } else { + ActiveWindow = LastActiveLayoutWindowID; + } + } } // :OnCommandEnding diff --git a/src/text_editor/lua_api.cpp b/src/text_editor/lua_api.cpp index faca795..e905f10 100644 --- a/src/text_editor/lua_api.cpp +++ b/src/text_editor/lua_api.cpp @@ -210,21 +210,21 @@ String GetFieldString(lua_State *L, String name) { return result; } -Int GetFieldInt(lua_State *L, const char *name) { +Int GetFieldAInt(lua_State *L, const char *name) { lua_getfield(L, -1, name); lua_Integer num = lua_tointeger(L, -1); lua_pop(L, 1); return (Int)num; } -double GetFieldFloat(lua_State *L, const char *name) { +double GetFieldAFloat(lua_State *L, const char *name) { lua_getfield(L, -1, name); double num = lua_tonumber(L, -1); lua_pop(L, 1); return num; } -const char *GetFieldString(lua_State *L, const char *name) { +const char *GetFieldAString(lua_State *L, const char *name) { lua_getfield(L, -1, name); const char *result = lua_tostring(L, -1); @@ -256,7 +256,7 @@ int Lua_Play(lua_State *L) { defer { lua_pop(L, 1); }; Event event = {}; -#define X(TYPE, KIND, NAME) event.NAME = (TYPE)GetField##KIND(L, #NAME); +#define X(TYPE, KIND, NAME) event.NAME = (TYPE)GetFieldA##KIND(L, #NAME); EVENT_FIELDS #undef X Add(&EventPlayback, event); diff --git a/src/text_editor/lua_api_generated.cpp b/src/text_editor/lua_api_generated.cpp index 61f00fb..e70b169 100644 --- a/src/text_editor/lua_api_generated.cpp +++ b/src/text_editor/lua_api_generated.cpp @@ -29,7 +29,6 @@ luaL_Reg LuaFunctions[] = { {"C", Lua_C}, {"Open", Lua_Open}, {"Cmd", Lua_Cmd}, - {"ListBuffers", Lua_ListBuffers}, {"ListViews", Lua_ListViews}, {"Eval", Lua_Eval}, {"SetProjectFile", Lua_SetProjectFile}, diff --git a/src/text_editor/management.cpp b/src/text_editor/management.cpp index 39be11d..0e94737 100644 --- a/src/text_editor/management.cpp +++ b/src/text_editor/management.cpp @@ -149,16 +149,17 @@ Buffer *CreateBuffer(Allocator allocator, String name, Int size) { } Window *CreateWind() { - Allocator allocator = GetSystemAllocator(); - Window *w = AllocType(allocator, Window); - w->font = &PrimaryFont; - w->visible = true; - w->layout = true; - w->draw_scrollbar = StyleDrawScrollbar; + Allocator allocator = GetSystemAllocator(); + Window *w = AllocType(allocator, Window); + w->font = &PrimaryFont; + w->visible = true; + w->layout = true; + w->draw_scrollbar = StyleDrawScrollbar; w->draw_line_numbers = StyleDrawLineNumbers; w->draw_line_highlight = true; - w->id = AllocWindowID(w); - w->weight = 1.0; + w->jump_history = true; + w->id = AllocWindowID(w); + w->weight = 1.0; Add(&Windows, w); return w; } @@ -380,11 +381,23 @@ View *WindowOpenBufferView(Window *new_parent_window, String name) { return result; } +bool ViewIsCrumb(ViewID view_id) { + ForItem(window, Windows) { + For(window->goto_history) if (it.view_id == view_id) return true; + For(window->goto_redo) if (it.view_id == view_id) return true; + } + return false; +} + bool ViewIsReferenced(ViewID view) { if (view == NullViewID) { return true; } + if (ViewIsCrumb(view)) { + return true; + } + For(Windows) { if (it->active_view == view) { return true; @@ -455,6 +468,8 @@ void GarbageCollect() { IterRemove(Windows) { IterRemovePrepare(Windows); if (it->kill) { + Dealloc(&it->goto_history); + Dealloc(&it->goto_redo); Dealloc(sys_allocator, it); remove_item = true; } diff --git a/src/text_editor/text_editor.h b/src/text_editor/text_editor.h index 5e9aa8c..4ea03b1 100644 --- a/src/text_editor/text_editor.h +++ b/src/text_editor/text_editor.h @@ -16,6 +16,12 @@ struct View { String16 prev_search_line; }; +struct GotoCrumb { + ViewID view_id; + Caret caret; + double time; +}; + struct Window { WindowID id; ViewID active_view; @@ -32,6 +38,12 @@ struct Window { double weight; Int status_bar_last_buffer_change_id; + Array goto_history; + Array goto_redo; + + ViewID active_goto_list; + Int goto_list_pos; + struct { bool draw_scrollbar : 1; bool draw_line_numbers : 1; @@ -41,6 +53,8 @@ struct Window { bool layout : 1; bool kill : 1; bool sync_visibility_with_focus : 1; + bool lose_focus_on_escape : 1; + bool jump_history : 1; }; }; diff --git a/src/text_editor/window.cpp b/src/text_editor/window.cpp index dc26a97..dd7a92e 100644 --- a/src/text_editor/window.cpp +++ b/src/text_editor/window.cpp @@ -16,17 +16,7 @@ Int GetExpandingBarSize(Window *window) { void InitWindows() { Scratch scratch; - { - Window *window = CreateWind(); - window->active_view = CreateView(ScratchBuffer->id)->id; - window->weight = 1.0; - { - Window *window = CreateWind(); - window->active_view = TraceView->id; - CreateWind(); - CreateWind(); - } - } + CreateWind(); // COMMAND BAR { @@ -42,7 +32,8 @@ void InitWindows() { window->layout = false; window->visible = false; window->sync_visibility_with_focus = true; - buffer->no_history = true; + window->lose_focus_on_escape = true; + window->jump_history = false; } // SEARCH BAR @@ -60,6 +51,7 @@ void InitWindows() { window->draw_line_highlight = false; window->layout = false; window->visible = false; + window->lose_focus_on_escape = true; } // STATUS BAR at the bottom