Scrolling and word complete improvements

This commit is contained in:
Krzosa Karol
2026-01-25 12:02:06 +01:00
parent 2454370736
commit 1c25b39df0
7 changed files with 141 additions and 80 deletions

View File

@@ -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) { void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string) {
View *view = GetViewForFixingWhenBufferCommand(buffer); View *view = GetViewForBuffer(buffer);
Array<Caret> carets = Copy(GetSystemAllocator(), view->carets); Array<Caret> carets = Copy(GetSystemAllocator(), view->carets);
Scratch scratch; Scratch scratch;
@@ -133,7 +102,7 @@ void TrimWhitespace(Buffer *buffer, bool trim_lines_with_caret) {
Scratch scratch; Scratch scratch;
bool is_active_view = false; 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) { if (!is_active_view && !trim_lines_with_caret) {
trim_lines_with_caret = true; trim_lines_with_caret = true;
} }

View File

@@ -10,6 +10,13 @@ Rect2I GetVisibleCells(Window *window) {
Int _cx = size.x / window->font->char_spacing; Int _cx = size.x / window->font->char_spacing;
Int _cy = size.y / window->font->line_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 cx = _cx + 1;
Int cy = _cy + 2; Int cy = _cy + 2;
@@ -200,7 +207,7 @@ void DrawWindow(Window *window, Event &event) {
} }
// Underline word under mouse cursor // Underline word under mouse cursor
if (0) { if (1) {
Caret caret = view->carets[0]; Caret caret = view->carets[0];
Vec2I mouse = MouseVec2I(); Vec2I mouse = MouseVec2I();
bool mouse_in_document = AreOverlapping(mouse, window->document_rect); bool mouse_in_document = AreOverlapping(mouse, window->document_rect);

View File

@@ -13,6 +13,7 @@ Int ErrorCount;
#if DEBUG_BUILD #if DEBUG_BUILD
String16 InitialScratchContent = uR"==(:OpenProject String16 InitialScratchContent = uR"==(:OpenProject
C:/text_editor/src/text_editor/text_editor.cpp C:/text_editor/src/text_editor/text_editor.cpp
:Set FontSize 70
0 0
1 1
2 2

View File

@@ -23,7 +23,9 @@ void SearchWindowFindNext(bool forward = true) {
BSet set = GetBSet(SearchWindowID); BSet set = GetBSet(SearchWindowID);
String16 seek = GetString(set.buffer, GetRange(set.buffer)); String16 seek = GetString(set.buffer, GetRange(set.buffer));
Find(main.view, seek, forward); Find(main.view, seek, forward);
CenterView(PrimaryWindowID); if (!IsMainCaretOnScreen(main.window).caret_on_screen) {
CenterView(main.window->id);
}
} }
void CMD_SearchNextInSearch() { void CMD_SearchNextInSearch() {
@@ -100,7 +102,9 @@ void UpdateSearchWindow() {
main.view->carets[0] = main.window->search_bar_anchor; main.view->carets[0] = main.window->search_bar_anchor;
String16 seek = GetString(active.buffer, GetRange(active.buffer)); String16 seek = GetString(active.buffer, GetRange(active.buffer));
Find(main.view, seek, true); 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; SearchBufferChangeID = active.buffer->change_id;
} }
} }

View File

@@ -83,42 +83,53 @@ void CWSLexIdentifiers(Array<StringAndDistance> *out_idents, Buffer *buffer) {
} }
} }
void WordComplete(mco_coro *co) { #if 0
Array<BufferID> buffers = {CWS.arena}; void CWSGetNextBuffer(mco_coro *co) {
{ // Would be nice to iterate through 64 last active buffers
Add(&buffers, CWS.buffer->id); // - Then all buffers
ForItem (window, Windows) { Add(&CWS.visited_buffers, CWS.buffer->id);
if (window->visible) { mco_result res = mco_push(co, &CWS.buffer->id, sizeof(CWS.buffer->id));
View *view = GetView(window->active_view); Assert(res == MCO_SUCCESS);
Buffer *buffer = GetBuffer(view->active_buffer); mco_yield(co);
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);
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<BufferID> buffers = {CWS.arena};
Add(&buffers, CWS.buffer->id);
For (IterateInReverse(&Buffers)) {
Add(&buffers, it->id);
} }
Buffer *curr_buffer = NULL;
Int buffer_i = 0; Int buffer_i = 0;
Array<StringAndDistance> idents = {CWS.arena}; Array<StringAndDistance> idents = {CWS.arena};
Add(&idents, {CWS.prefix_string, 0}); Add(&idents, {CWS.prefix_string, 0});
for (Int i = 1;;) { for (Int i = 1;;) {
if (i >= idents.len) { if (i >= idents.len) {
while (buffer_i < buffers.len) { while (buffer_i < buffers.len) {
curr_buffer = GetBuffer(buffers[buffer_i++]); Buffer *buffer = GetBuffer(buffers[buffer_i++]);
CWSLexIdentifiers(&idents, curr_buffer); CWSLexIdentifiers(&idents, buffer);
if (i < idents.len) { if (i < idents.len) {
break; break;
} }
@@ -130,14 +141,18 @@ void WordComplete(mco_coro *co) {
StringAndDistance it = idents[i]; StringAndDistance it = idents[i];
String16 ident = Copy16(CWS.arena, it.string); String16 ident = Copy16(CWS.arena, it.string);
CWS.last_string = ident; CWS.last_string = ident;
Range range = EncloseWord(curr_buffer, CWS.original_caret_pos); Range range = EncloseWord(CWS.buffer, CWS.original_caret_pos);
View *view = GetViewForFixingWhenBufferCommand(curr_buffer); SelectRange(CWS.view, range);
SelectRange(view, range); Replace(CWS.view, ident);
Replace(view, ident);
yield:; yield:;
mco_yield(co); 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 += 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; 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 (!continue_with_previous) {
if (CWS.co) { if (CWS.co) {
mco_result res = mco_destroy(CWS.co); mco_result res = mco_destroy(CWS.co);
@@ -159,6 +174,7 @@ void WordComplete(View *view, Int pos) {
Release(&CWS.arena); Release(&CWS.arena);
MemoryZero(&CWS, sizeof(CWS)); MemoryZero(&CWS, sizeof(CWS));
// CWS.visited_buffers.allocator = CWS.arena;
CWS.buffer = buffer; CWS.buffer = buffer;
CWS.view = view; CWS.view = view;
CWS.original_caret_pos = pos; CWS.original_caret_pos = pos;

View File

@@ -41,13 +41,33 @@ View *GetView(ViewID id, View *default_view = Views[0]) {
return result; return result;
} }
API View *GetView(BufferID buffer_id, View *default_view = Views[0]) { View *GetViewForBuffer(Buffer *buffer, bool *is_active = NULL) {
For (Views) { View *view = NULL;
if (it->active_buffer == buffer_id) { if (is_active) {
return it; *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]) { 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; 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) { API View *OpenBufferView(String name) {
Buffer *buffer = BufferOpenFile(name); Buffer *buffer = BufferOpenFile(name);
View *view = CreateView(buffer->id); View *view = CreateView(buffer->id);
@@ -100,7 +129,7 @@ bool BufferIsReferenced(Buffer *buffer) {
return true; return true;
} }
if (GetView(buffer->id, NULL)) { if (FindView(buffer->id, NULL)) {
return true; return true;
} }

View File

@@ -412,13 +412,46 @@ String GetDirectory(Window *window) {
return GetDirectory(main.buffer); 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) { void MoveCursorByPageSize(Window *window, int direction, bool shift = false) {
Assert(direction == DIR_UP || direction == DIR_DOWN); Assert(direction == DIR_UP || direction == DIR_DOWN);
BSet set = GetBSet(window); BSet set = GetBSet(window);
IsOnScreenResult is_on_screen_res = IsMainCaretOnScreen(window);
Rect2I visible_cells_rect = GetVisibleCells(window); Rect2I visible_cells_rect = GetVisibleCells(window);
Int y = GetSize(visible_cells_rect).y - 2; Int y = GetSize(visible_cells_rect).y - 2;
if (direction == DIR_UP) y = -y; if (direction == DIR_UP) {
y = -y;
}
For(set.view->carets) { For(set.view->carets) {
XY xy = PosToXY(set.buffer, GetFront(it)); XY xy = PosToXY(set.buffer, GetFront(it));
@@ -430,11 +463,13 @@ void MoveCursorByPageSize(Window *window, int direction, bool shift = false) {
} }
xy.line += y; xy.line += y;
Int pos = XYToPos(set.buffer, xy); Int pos = XYToPosWithoutNL(set.buffer, xy);
if (shift) { if (shift) {
it = SetFront(it, pos); it = SetFront(it, pos);
} else { } else {
it = MakeCaret(pos); it = MakeCaret(pos);
} }
} }
SetStoredOffsetFromTop(window, is_on_screen_res);
} }