From 1c25b39df0999e2d7a495e403253597ad4cd6021 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Sun, 25 Jan 2026 12:02:06 +0100 Subject: [PATCH] Scrolling and word complete improvements --- src/text_editor/commands.cpp | 35 +--------- src/text_editor/draw.cpp | 9 ++- src/text_editor/globals.cpp | 1 + src/text_editor/plugin_search_window.cpp | 8 ++- src/text_editor/plugin_word_complete.cpp | 82 ++++++++++++++---------- src/text_editor/view.cpp | 43 +++++++++++-- src/text_editor/window.cpp | 43 +++++++++++-- 7 files changed, 141 insertions(+), 80 deletions(-) diff --git a/src/text_editor/commands.cpp b/src/text_editor/commands.cpp index e04db51..48f1343 100644 --- a/src/text_editor/commands.cpp +++ b/src/text_editor/commands.cpp @@ -1,36 +1,5 @@ - - -View *GetViewForFixingWhenBufferCommand(Buffer *buffer, bool *is_active = NULL) { - View *view = NULL; - if (is_active) { - *is_active = false; - } - - BSet active = GetBSet(ActiveWindowID); - if (active.buffer->id == buffer->id) { - if (is_active) { - *is_active = true; - } - return active.view; - } - - For(Views) { - if (it->active_buffer != buffer->id) { - continue; - } - view = it; - break; - } - - if (!view) { - view = CreateView(buffer->id); - } - - return view; -} - void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string) { - View *view = GetViewForFixingWhenBufferCommand(buffer); + View *view = GetViewForBuffer(buffer); Array carets = Copy(GetSystemAllocator(), view->carets); Scratch scratch; @@ -133,7 +102,7 @@ void TrimWhitespace(Buffer *buffer, bool trim_lines_with_caret) { Scratch scratch; bool is_active_view = false; - View *view = GetViewForFixingWhenBufferCommand(buffer, &is_active_view); + View *view = GetViewForBuffer(buffer, &is_active_view); if (!is_active_view && !trim_lines_with_caret) { trim_lines_with_caret = true; } diff --git a/src/text_editor/draw.cpp b/src/text_editor/draw.cpp index 238ae3c..3bb419c 100644 --- a/src/text_editor/draw.cpp +++ b/src/text_editor/draw.cpp @@ -10,6 +10,13 @@ Rect2I GetVisibleCells(Window *window) { Int _cx = size.x / window->font->char_spacing; Int _cy = size.y / window->font->line_spacing; + + // This function is mostly used for rendering, these magic numbers are here + // to make sure we are not showing unrendered part of the screen to the user. + // These were derived by testing and experience. We are only rendering the + // part that is on screen but need to account for the arbitrary pixel scrolling. + // That is a cell can be half on screen, it's not like vim where you are stuck + // to the grid. Int cx = _cx + 1; Int cy = _cy + 2; @@ -200,7 +207,7 @@ void DrawWindow(Window *window, Event &event) { } // Underline word under mouse cursor - if (0) { + if (1) { Caret caret = view->carets[0]; Vec2I mouse = MouseVec2I(); bool mouse_in_document = AreOverlapping(mouse, window->document_rect); diff --git a/src/text_editor/globals.cpp b/src/text_editor/globals.cpp index 84741f8..2a06504 100644 --- a/src/text_editor/globals.cpp +++ b/src/text_editor/globals.cpp @@ -13,6 +13,7 @@ Int ErrorCount; #if DEBUG_BUILD String16 InitialScratchContent = uR"==(:OpenProject C:/text_editor/src/text_editor/text_editor.cpp +:Set FontSize 70 0 1 2 diff --git a/src/text_editor/plugin_search_window.cpp b/src/text_editor/plugin_search_window.cpp index 718e8d8..4a09825 100644 --- a/src/text_editor/plugin_search_window.cpp +++ b/src/text_editor/plugin_search_window.cpp @@ -23,7 +23,9 @@ void SearchWindowFindNext(bool forward = true) { BSet set = GetBSet(SearchWindowID); String16 seek = GetString(set.buffer, GetRange(set.buffer)); Find(main.view, seek, forward); - CenterView(PrimaryWindowID); + if (!IsMainCaretOnScreen(main.window).caret_on_screen) { + CenterView(main.window->id); + } } void CMD_SearchNextInSearch() { @@ -100,7 +102,9 @@ void UpdateSearchWindow() { main.view->carets[0] = main.window->search_bar_anchor; String16 seek = GetString(active.buffer, GetRange(active.buffer)); Find(main.view, seek, true); - CenterView(main.window->id); + if (!IsMainCaretOnScreen(main.window).caret_on_screen) { + CenterView(main.window->id); + } SearchBufferChangeID = active.buffer->change_id; } } diff --git a/src/text_editor/plugin_word_complete.cpp b/src/text_editor/plugin_word_complete.cpp index 0e57b21..8fe13b2 100644 --- a/src/text_editor/plugin_word_complete.cpp +++ b/src/text_editor/plugin_word_complete.cpp @@ -83,42 +83,53 @@ void CWSLexIdentifiers(Array *out_idents, Buffer *buffer) { } } -void WordComplete(mco_coro *co) { - Array buffers = {CWS.arena}; - { - Add(&buffers, CWS.buffer->id); - ForItem (window, Windows) { - if (window->visible) { - View *view = GetView(window->active_view); - Buffer *buffer = GetBuffer(view->active_buffer); - if (!Contains(buffers, buffer->id)) { - Add(&buffers, buffer->id); - } - } - Int len = 0; - For (IterateInReverse(&window->goto_history)) { - View *view = GetView(it.view_id); - Buffer *buffer = GetBuffer(view->active_buffer); - if (!Contains(buffers, buffer->id)) { - Add(&buffers, buffer->id); - len += 1; - if (len > 16) continue; - } - } - } - Window *active = GetWindow(ActiveWindowID); +#if 0 +void CWSGetNextBuffer(mco_coro *co) { + // Would be nice to iterate through 64 last active buffers + // - Then all buffers + Add(&CWS.visited_buffers, CWS.buffer->id); + mco_result res = mco_push(co, &CWS.buffer->id, sizeof(CWS.buffer->id)); + Assert(res == MCO_SUCCESS); + mco_yield(co); + For (IterateInReverse(&Buffers)) { + Add(&buffers, it->id); + } + + ForItem (window, Windows) { + if (!window->visible) { + continue; + } + + View *view = GetView(window->active_view); + Buffer *buffer = GetBuffer(view->active_buffer); + if (Contains(CWS.visited_buffers, buffer->id)) { + continue; + } + + Add(&CWS.visited_buffers, buffer->id); + mco_result res = mco_push(co, &buffer->id, sizeof(buffer->id)); + Assert(res == MCO_SUCCESS); + mco_yield(co); + } +} +#endif + +void WordComplete(mco_coro *co) { + Array buffers = {CWS.arena}; + Add(&buffers, CWS.buffer->id); + For (IterateInReverse(&Buffers)) { + Add(&buffers, it->id); } - Buffer *curr_buffer = NULL; Int buffer_i = 0; Array idents = {CWS.arena}; Add(&idents, {CWS.prefix_string, 0}); for (Int i = 1;;) { if (i >= idents.len) { while (buffer_i < buffers.len) { - curr_buffer = GetBuffer(buffers[buffer_i++]); - CWSLexIdentifiers(&idents, curr_buffer); + Buffer *buffer = GetBuffer(buffers[buffer_i++]); + CWSLexIdentifiers(&idents, buffer); if (i < idents.len) { break; } @@ -130,14 +141,18 @@ void WordComplete(mco_coro *co) { StringAndDistance it = idents[i]; String16 ident = Copy16(CWS.arena, it.string); CWS.last_string = ident; - Range range = EncloseWord(curr_buffer, CWS.original_caret_pos); - View *view = GetViewForFixingWhenBufferCommand(curr_buffer); - SelectRange(view, range); - Replace(view, ident); + Range range = EncloseWord(CWS.buffer, CWS.original_caret_pos); + SelectRange(CWS.view, range); + Replace(CWS.view, ident); yield:; mco_yield(co); + if (CWS.direction == -1 && i > 0 && i == idents.len) { + // special case for when we are at the end of the list and we want to go back + // to make it sure we don't need to click twice to flip + i -= 1; + } i += CWS.direction; - i = Clamp(i, 0ll, idents.len - 1); + i = Clamp(i, 0ll, idents.len); } } @@ -149,7 +164,7 @@ void WordComplete(View *view, Int pos) { return; } - bool continue_with_previous = CWS.co && CWS.last_string == prefix && CWS.buffer == buffer; + bool continue_with_previous = CWS.co && CWS.last_string == prefix && CWS.buffer == buffer && CWS.view == view; if (!continue_with_previous) { if (CWS.co) { mco_result res = mco_destroy(CWS.co); @@ -159,6 +174,7 @@ void WordComplete(View *view, Int pos) { Release(&CWS.arena); MemoryZero(&CWS, sizeof(CWS)); + // CWS.visited_buffers.allocator = CWS.arena; CWS.buffer = buffer; CWS.view = view; CWS.original_caret_pos = pos; diff --git a/src/text_editor/view.cpp b/src/text_editor/view.cpp index d0b4b08..cca2309 100644 --- a/src/text_editor/view.cpp +++ b/src/text_editor/view.cpp @@ -41,13 +41,33 @@ View *GetView(ViewID id, View *default_view = Views[0]) { return result; } -API View *GetView(BufferID buffer_id, View *default_view = Views[0]) { - For (Views) { - if (it->active_buffer == buffer_id) { - return it; - } +View *GetViewForBuffer(Buffer *buffer, bool *is_active = NULL) { + View *view = NULL; + if (is_active) { + *is_active = false; } - return default_view; + + BSet active = GetBSet(ActiveWindowID); + if (active.buffer->id == buffer->id) { + if (is_active) { + *is_active = true; + } + return active.view; + } + + For(Views) { + if (it->active_buffer != buffer->id) { + continue; + } + view = it; + break; + } + + if (!view) { + view = CreateView(buffer->id); + } + + return view; } API View *GetView(String name, View *default_view = Views[0]) { @@ -60,6 +80,15 @@ API View *GetView(String name, View *default_view = Views[0]) { return default_view; } +API View *FindView(BufferID buffer_id, View *default_view) { + For (Views) { + if (it->active_buffer == buffer_id) { + return it; + } + } + return default_view; +} + API View *OpenBufferView(String name) { Buffer *buffer = BufferOpenFile(name); View *view = CreateView(buffer->id); @@ -100,7 +129,7 @@ bool BufferIsReferenced(Buffer *buffer) { return true; } - if (GetView(buffer->id, NULL)) { + if (FindView(buffer->id, NULL)) { return true; } diff --git a/src/text_editor/window.cpp b/src/text_editor/window.cpp index 91bbdbc..ff22fd5 100644 --- a/src/text_editor/window.cpp +++ b/src/text_editor/window.cpp @@ -412,13 +412,46 @@ String GetDirectory(Window *window) { return GetDirectory(main.buffer); } +struct IsOnScreenResult { + bool caret_on_screen; + Int offset_from_top; +}; + +IsOnScreenResult IsMainCaretOnScreen(Window *window) { + BSet set = GetBSet(window); + Int front = GetFront(set.view->carets[0]); + XY front_xy = PosToXY(set.buffer, front); + Int caret_pixel_size = front_xy.y * set.window->font->line_spacing; + Vec2I doc_pixel_size = GetSize(set.window->document_rect); + + Int ystart = set.view->scroll.y; + Int yend = ystart + doc_pixel_size.y; + bool caret_on_screen = caret_pixel_size >= ystart && caret_pixel_size < yend; + Int offset_from_top = caret_pixel_size - ystart; + return {caret_on_screen, offset_from_top}; +} + +bool SetStoredOffsetFromTop(Window *window, IsOnScreenResult res) { + if (res.caret_on_screen) { + BSet set = GetBSet(window); + Int front = GetFront(set.view->carets[0]); + XY front_xy = PosToXY(set.buffer, front); + Int caret_pixel_size = front_xy.y * set.window->font->line_spacing; + set.view->scroll.y = caret_pixel_size - res.offset_from_top; + return true; + } + return false; +} + void MoveCursorByPageSize(Window *window, int direction, bool shift = false) { Assert(direction == DIR_UP || direction == DIR_DOWN); BSet set = GetBSet(window); - + IsOnScreenResult is_on_screen_res = IsMainCaretOnScreen(window); Rect2I visible_cells_rect = GetVisibleCells(window); - Int y = GetSize(visible_cells_rect).y - 2; - if (direction == DIR_UP) y = -y; + Int y = GetSize(visible_cells_rect).y - 2; + if (direction == DIR_UP) { + y = -y; + } For(set.view->carets) { XY xy = PosToXY(set.buffer, GetFront(it)); @@ -430,11 +463,13 @@ void MoveCursorByPageSize(Window *window, int direction, bool shift = false) { } xy.line += y; - Int pos = XYToPos(set.buffer, xy); + Int pos = XYToPosWithoutNL(set.buffer, xy); if (shift) { it = SetFront(it, pos); } else { it = MakeCaret(pos); } } + + SetStoredOffsetFromTop(window, is_on_screen_res); }