Compare commits

...

3 Commits

Author SHA1 Message Date
Krzosa Karol
70b752eccb Fix line number clipping, rendering issue 2026-01-30 21:44:46 +01:00
Krzosa Karol
805f5de852 Coroutine API rename 2026-01-30 21:44:28 +01:00
Krzosa Karol
c9acc31cfc PageDown, PageUp is setting cursor position to beginning of line now 2026-01-30 21:43:01 +01:00
19 changed files with 98 additions and 76 deletions

View File

@@ -20,6 +20,7 @@
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#undef Yield
API void *VReserve(size_t size) {
void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);

View File

@@ -93,6 +93,7 @@
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#undef Yield
#define BREAK() if (IsDebuggerPresent()) {__debugbreak();}
#elif OS_LINUX
#define BREAK() raise(SIGTRAP)

View File

@@ -462,6 +462,7 @@ API void CloseStdin(Process *process) {
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#undef Yield
#include <stdio.h>
#include <intrin.h>

View File

@@ -90,6 +90,14 @@ Vec2I GetMid(Rect2I r) {
return result;
}
Rect2 Round(Rect2 r) {
r.min.x = roundf(r.min.x);
r.max.x = roundf(r.max.x);
r.min.y = roundf(r.min.y);
r.max.y = roundf(r.max.y);
return r;
}
Rect2I CutLeft(Rect2I *r, Int value) {
Int minx = r->min.x;
r->min.x = Min(r->min.x + value, r->max.x);

View File

@@ -476,6 +476,7 @@ MCO_API const char *mco_result_description(mco_result res); /* Get the descripti
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#undef Yield
#endif
#ifndef MCO_NO_DEFAULT_ALLOCATOR

View File

@@ -288,7 +288,7 @@ void PushVertex2D(Allocator allocator, VertexList2D *list, Vertex2D *vertices, i
for (int i = 0; i < count; i += 1) result[i] = vertices[i];
}
void PushQuad2D(Allocator arena, VertexList2D *list, Rect2 rect, Rect2 tex, Color color, float rotation = 0.f, Vec2 rotation_point = {}) {
void PushQuad2D(Allocator arena, VertexList2D *list, Rect2 rect, Rect2 tex, Color color) {
Vertex2D *v = AllocVertex2D(arena, list, 6);
v[0] = { {rect.min.x, rect.max.y}, { tex.min.x, tex.max.y}, color };
v[1] = { {rect.max.x, rect.max.y}, { tex.max.x, tex.max.y}, color };
@@ -296,15 +296,6 @@ void PushQuad2D(Allocator arena, VertexList2D *list, Rect2 rect, Rect2 tex, Colo
v[3] = { {rect.min.x, rect.min.y}, { tex.min.x, tex.min.y}, color };
v[4] = { {rect.max.x, rect.max.y}, { tex.max.x, tex.max.y}, color };
v[5] = { {rect.max.x, rect.min.y}, { tex.max.x, tex.min.y}, color };
if (rotation != 0.f) {
float s = sinf(rotation);
float c = cosf(rotation);
for (int i = 0; i < 6; i += 1) {
v[i].pos -= rotation_point;
v[i].pos = {v[i].pos.x * c + v[i].pos.y * (-s), v[i].pos.x * s + v[i].pos.y * c};
v[i].pos += rotation_point;
}
}
}
void DrawRect(Rect2 rect, Color color) {
@@ -336,7 +327,7 @@ Vec2 DrawString(Font *font, String16 string, Vec2 pos, Color color, bool draw =
Glyph *g = GetGlyph(font, it);
Rect2 rect = Rect2FromSize(pos + g->offset, g->size);
if (draw && it != '\n' && it != ' ' && it != '\t') {
PushQuad2D(RenderArena, &Vertices, rect, g->atlas_bounding_box, color);
PushQuad2D(RenderArena, &Vertices, Round(rect), g->atlas_bounding_box, color);
}
pos.x += g->xadvance;
}

View File

@@ -21,7 +21,7 @@ void AddText(String string) {
void Wait(mco_coro *co) {
Add(&EventPlayback, {EVENT_KIND_INVALID});
for (Event *event = CoYield(co); event->kind != EVENT_KIND_INVALID; event = CoYield(co)) {
for (Event *event = Yield(co); event->kind != EVENT_KIND_INVALID; event = Yield(co)) {
}
}
@@ -153,8 +153,8 @@ void InitTests() {
WaitForEvents = false;
TestDir = Format(TestArena, "%S/test_env", GetExeDir(TestArena));
CoRemove("Test");
CoData *data = CoAdd(Test);
RemoveCoroutine("Test");
CCtx *data = AddCoroutine(Test);
data->dont_wait_until_resolved = true;
CoResume(data);
ResumeCoroutine(data);
}

View File

@@ -1,41 +1,47 @@
typedef void CoroutineProc(mco_coro *co);
CoData *CoCurr = NULL;
typedef void CoroutineFunction(mco_coro *co);
CCtx *_CoroutineContext = NULL;
void CoDestroy(CoData *n) {
CCtx *GetCoroutineContext() {
Assert(_CoroutineContext);
return _CoroutineContext;
}
void DestroyCoroutine(CCtx *n) {
mco_destroy(n->co);
Release(&n->arena);
}
void CoRemove(String name) {
void RemoveCoroutine(String name) {
IterRemove(ActiveCoroutines) {
IterRemovePrepare(ActiveCoroutines);
if (it.name == name) {
CoDestroy(&it);
DestroyCoroutine(&it);
remove_item = true;
}
}
}
#define CoAdd(x) CoAddEx(x, #x)
CoData *CoAddEx(CoroutineProc *proc, String name) {
mco_desc desc = mco_desc_init(proc, 0);
CCtx CreateCoroutine(CoroutineFunction *func, String name) {
mco_desc desc = mco_desc_init(func, 0);
mco_coro *coro = NULL;
mco_result ok = mco_create(&coro, &desc);
if (ok != MCO_SUCCESS) {
ReportWarningf("failed to create coroutine %d", ok);
return NULL;
}
assert(ok == MCO_SUCCESS);
return {coro, name};
}
Add(&ActiveCoroutines, {coro, name});
#define AddCoroutine(x) AddCoroutineEx(x, #x)
CCtx *AddCoroutineEx(CoroutineFunction *func, String name) {
CCtx coroutine = CreateCoroutine(func, name);
Add(&ActiveCoroutines, coroutine);
return GetLast(ActiveCoroutines);
}
void CoResume(CoData *dat) {
CoData *prev_curr = CoCurr;
CoCurr = dat;
void ResumeCoroutine(CCtx *dat) {
CCtx *prev_curr = _CoroutineContext;
_CoroutineContext = dat;
mco_result ok = mco_resume(dat->co);
Assert(ok == MCO_SUCCESS);
CoCurr = prev_curr;
_CoroutineContext = prev_curr;
}
void UpdateCoroutines(Event *event) {
@@ -48,17 +54,18 @@ void UpdateCoroutines(Event *event) {
mco_state status = mco_status(it.co);
if (status == MCO_DEAD) {
CoDestroy(&it);
DestroyCoroutine(&it);
remove_item = true;
} else {
mco_push(it.co, &event, sizeof(Event *));
CoCurr = &it;
_CoroutineContext = &it;
mco_result ok = mco_resume(it.co);
if (ok != MCO_SUCCESS) {
ReportWarningf("failed to resume coroutine %d", ok);
CoDestroy(&it);
DestroyCoroutine(&it);
remove_item = true;
}
_CoroutineContext = NULL;
}
}
@@ -79,7 +86,7 @@ void UpdateCoroutines(Event *event) {
}
}
Event *CoYield(mco_coro *co) {
Event *Yield(mco_coro *co) {
mco_result ok = mco_yield(co);
Assert(ok == MCO_SUCCESS);

View File

@@ -65,7 +65,7 @@ void DrawVisibleText(Window *window, Color tint) {
Rect2 rect = Rect2FromSize(p + g->offset, g->size);
if (codepoint != '\n' && codepoint != '\r' && codepoint != ' ' && codepoint != '\t') {
PushQuad2D(RenderArena, &Vertices, rect, g->atlas_bounding_box, tint);
PushQuad2D(RenderArena, &Vertices, Round(rect), g->atlas_bounding_box, tint);
}
text_offset_x += window->font->char_spacing;

View File

@@ -110,8 +110,14 @@ Array<Variable> Variables;
Array<Process> ActiveProcesses = {};
Array<String> ProcessEnviroment = {};
struct CoData { mco_coro *co; String name; BlockArena arena; bool dont_wait_until_resolved; void *user_ctx; };
Array<CoData> ActiveCoroutines;
struct CCtx {
mco_coro *co;
String name;
BlockArena arena;
bool dont_wait_until_resolved;
void *user_ctx;
};
Array<CCtx> ActiveCoroutines;
Color GruvboxDark0Hard = {0x1d, 0x20, 0x21, 0xff};
Color GruvboxDark0 = {0x28, 0x28, 0x28, 0xff};

View File

@@ -38,6 +38,7 @@ void CMD_Reopen() {
} RegisterCommand(CMD_Reopen, "", "Reads again from disk the current buffer");
void CO_Rename(mco_coro *co) {
CCtx *ctx = GetCoroutineContext();
BSet main = GetBSet(PrimaryWindowID);
Buffer *original_buffer = main.buffer;
JumpTempBuffer(&main);
@@ -60,11 +61,12 @@ void CO_Rename(mco_coro *co) {
return;
}
String16 string16 = GetString(main.buffer, {a.range.max + 1, b.range.max});
String string = ToString(CoCurr->arena, string16);
String string = ToString(ctx->arena, string16);
original_buffer->name = Intern(&GlobalInternTable, string);
} RegisterCoroutineCommand(CO_Rename, "", "Opens a UI asking for a new name of a buffer open in the last active primary window");
void CO_Close(mco_coro *co) {
CCtx *ctx = GetCoroutineContext();
BSet main = GetBSet(PrimaryWindowID);
if (main.buffer->special || main.buffer->temp) {
Close(main.view->id);
@@ -92,7 +94,7 @@ void CO_Close(mco_coro *co) {
return;
}
String question = Format(CoCurr->arena, "Do you want to save [%S] before closing?", main.buffer->name);
String question = Format(ctx->arena, "Do you want to save [%S] before closing?", main.buffer->name);
UIAction ui_action = QueryUserYesNoCancel(co, main, question);
if (ui_action == UIAction_Yes) {
SaveBuffer(main.buffer);
@@ -116,9 +118,10 @@ void CO_CloseAll(mco_coro *co) {
} RegisterCoroutineCommand(CO_CloseAll, "", "Ask user which files to save and close all open normal views and buffers");
void CO_ReplaceAll(mco_coro *co) {
CCtx *ctx = GetCoroutineContext();
BSet main = GetBSet(PrimaryWindowID);
String16 string = FetchLoadWord(main.view);
String string8 = ToString(CoCurr->arena, string);
String string8 = ToString(ctx->arena, string);
String16 needle = {};
String16 replace = {};
@@ -144,7 +147,7 @@ void CO_ReplaceAll(mco_coro *co) {
{
JumpTempBuffer(&main);
NextActiveWindowID = main.window->id;
RawAppendf(main.buffer, ":Submit (enter) :Cancel (escape)\nString to replace with::%S", ToString(CoCurr->arena, needle));
RawAppendf(main.buffer, ":Submit (enter) :Cancel (escape)\nString to replace with::%S", ToString(ctx->arena, needle));
Caret field_seek = BaseFindNext(main.buffer, u"with::", MakeCaret(0), SeekFlag_None);
main.view->carets[0] = MakeCaret(main.buffer->len, field_seek.range.max);

View File

@@ -11,7 +11,7 @@ void Windows_SetupVCVarsall(mco_coro *co) {
if (!ProcessIsActive(view->id)) {
break;
}
CoYield(co);
Yield(co);
}
Scratch scratch;
@@ -30,8 +30,8 @@ void Windows_SetupVCVarsall(mco_coro *co) {
void LoadVCVars() {
CoRemove("Windows_SetupVCVarsall");
CoData *co_data = CoAdd(Windows_SetupVCVarsall);
RemoveCoroutine("Windows_SetupVCVarsall");
CCtx *co_data = AddCoroutine(Windows_SetupVCVarsall);
co_data->dont_wait_until_resolved = true;
CoResume(co_data);
ResumeCoroutine(co_data);
}

View File

@@ -10,12 +10,13 @@ void SetProjectDirectory(String dir) {
}
void CO_OpenCode(mco_coro *co) {
Array<String> patterns = SplitWhitespace(CoCurr->arena, OpenCodePatterns);
Array<String> exclude_patterns = SplitWhitespace(CoCurr->arena, OpenCodeExcludePatterns);
Array<String> dirs = {CoCurr->arena};
Add(&dirs, Copy(CoCurr->arena, ProjectDirectory));
CCtx *ctx = GetCoroutineContext();
Array<String> patterns = SplitWhitespace(ctx->arena, OpenCodePatterns);
Array<String> exclude_patterns = SplitWhitespace(ctx->arena, OpenCodeExcludePatterns);
Array<String> dirs = {ctx->arena};
Add(&dirs, Copy(ctx->arena, ProjectDirectory));
for (int diri = 0; diri < dirs.len; diri += 1) {
for (FileIter it = IterateFiles(CoCurr->arena, dirs[diri]); IsValid(it); Advance(&it)) {
for (FileIter it = IterateFiles(ctx->arena, dirs[diri]); IsValid(it); Advance(&it)) {
bool should_open = true;
if (!it.is_directory) {
should_open = false;
@@ -45,7 +46,7 @@ void CO_OpenCode(mco_coro *co) {
BufferOpenFile(it.absolute_path);
}
CoYield(co);
Yield(co);
}
}
} RegisterCoroutineCommand(

View File

@@ -2098,6 +2098,7 @@ uint8_t reply_buf[REPLY_BUF_SIZE];
ClientContext RDBG_Ctx;
bool RDBG_InitConnection(mco_coro *co, bool create_session = true) {
CCtx *ctx = GetCoroutineContext();
ReportConsolef("Remedybg: InitConnection");
enum rdbg_CommandResult res;
if (RDBG_Ctx.command_pipe_handle != NULL) {
@@ -2112,7 +2113,7 @@ bool RDBG_InitConnection(mco_coro *co, bool create_session = true) {
if (create_session) {
if (BinaryUnderDebug.len) {
Window *window = GetWindow(PrimaryWindowID);
ResolvedOpen res = ResolveOpen(CoCurr->arena, window, BinaryUnderDebug, ResolveOpenMeta_DontError | ResolveOpenMeta_DontExec);
ResolvedOpen res = ResolveOpen(ctx->arena, window, BinaryUnderDebug, ResolveOpenMeta_DontError | ResolveOpenMeta_DontExec);
if (res.kind == OpenKind_Goto) {
file = Intern(&GlobalInternTable, res.path);
}
@@ -2133,8 +2134,8 @@ bool RDBG_InitConnection(mco_coro *co, bool create_session = true) {
}
}
String session_name = Format(CoCurr->arena, "te%llu", GetTimeNanos());
String remedy_string = Format(CoCurr->arena, "%S --servername %S", RemedyBGPath, session_name);
String session_name = Format(ctx->arena, "te%llu", GetTimeNanos());
String remedy_string = Format(ctx->arena, "%S --servername %S", RemedyBGPath, session_name);
ReportConsolef("Remedybg: %S", remedy_string);
Exec(LogView->id, true, remedy_string, GetPrimaryDirectory());
MemoryZero(&RDBG_Ctx, sizeof(RDBG_Ctx));

View File

@@ -4,9 +4,10 @@ struct SearchOpenBuffersParams {
};
void Coro_SearchOpenBuffers(mco_coro *co) {
SearchOpenBuffersParams *param = (SearchOpenBuffersParams *)CoCurr->user_ctx;
CCtx *ctx = GetCoroutineContext();
SearchOpenBuffersParams *param = (SearchOpenBuffersParams *)ctx->user_ctx;
Array<BufferID> buffers = {CoCurr->arena};
Array<BufferID> buffers = {ctx->arena};
For (Buffers) {
Add(&buffers, it->id);
}
@@ -36,7 +37,7 @@ void Coro_SearchOpenBuffers(mco_coro *co) {
Appendf(out_view, "%S ||> %S:%lld:%lld\n", line_string8, it->name, (long long)line + 1, (long long)column + 1);
}
}
CoYield(co);
Yield(co);
}
}
@@ -55,14 +56,14 @@ void UpdateSearchOpenBuffersView() {
Replace(active.view, line_string);
Append(active.view, "\n", false);
CoRemove("Coro_SearchOpenBuffers");
CoData *dat = CoAdd(Coro_SearchOpenBuffers);
RemoveCoroutine("Coro_SearchOpenBuffers");
CCtx *dat = AddCoroutine(Coro_SearchOpenBuffers);
SearchOpenBuffersParams *param = AllocType(dat->arena, SearchOpenBuffersParams);
param->needle = Copy16(dat->arena, line_string);
param->view = active.view->id;
dat->user_ctx = param;
dat->dont_wait_until_resolved = true;
CoResume(dat);
ResumeCoroutine(dat);
active.view->carets[0] = caret;
}

View File

@@ -175,9 +175,9 @@ void ShowQuitAppUI(mco_coro *co) {
}
void CMD_Quit() {
CoRemove("ShowQuitAppUI");
CoData *data = CoAdd(ShowQuitAppUI);
CoResume(data);
RemoveCoroutine("ShowQuitAppUI");
CCtx *data = AddCoroutine(ShowQuitAppUI);
ResumeCoroutine(data);
} RegisterCommand(CMD_Quit, "", "Ask user which files he would like to save and exit");
void OnCommand(Event event) {

View File

@@ -293,10 +293,10 @@ struct Register_Command {
};
#define RegisterCommand(name, binding, docs) Register_Command RC__##name(&GlobalCommands, name, #name, binding, docs)
#define RegisterCoroutineCommand(name, binding, docs, ...) void CMD_##name() {\
CoRemove(#name);\
CoData *data = CoAdd(name);\
RemoveCoroutine(#name);\
CCtx *data = AddCoroutine(name);\
__VA_ARGS__\
CoResume(data);\
ResumeCoroutine(data);\
}\
Register_Command RC__##name(&GlobalCommands, CMD_##name, #name, binding, docs)

View File

@@ -35,7 +35,7 @@ UIAction WaitForUIAction(mco_coro *co, BSet main) {
break;
}
CoYield(co);
Yield(co);
}
Close(main.buffer->id);
return result;
@@ -118,8 +118,9 @@ String16 QueryUserString(mco_coro *co, String ask) {
// 2. Are pointers and globals correct over time? Or might they get deleted etc.
// 3. Imagine a scenario where the coroutine gets deleted before completion, will the memory leak?
UIAction ShowCloseAllUI(mco_coro *co) {
CCtx *ctx = GetCoroutineContext();
BSet main = GetBSet(PrimaryWindowID);
Array<BufferID> buffers = {CoCurr->arena};
Array<BufferID> buffers = {ctx->arena};
For (Buffers) Add(&buffers, it->id);
ForItem (id, buffers) {
Buffer *it = GetBuffer(id, NULL);
@@ -130,7 +131,7 @@ UIAction ShowCloseAllUI(mco_coro *co) {
continue;
}
String question = Format(CoCurr->arena, "Do you want to save [%S] before closing?", it->name);
String question = Format(ctx->arena, "Do you want to save [%S] before closing?", it->name);
UIAction ui_action = QueryUserYesNoCancel(co, main, question);
it = GetBuffer(id, NULL);
if (it && ui_action == UIAction_Yes) {
@@ -167,7 +168,7 @@ String QueryUserFile(mco_coro *co) {
break;
}
CoYield(co);
Yield(co);
}
window->active_view = original_view;
return window->ui_query_file;

View File

@@ -453,13 +453,12 @@ void MoveCursorByPageSize(Window *window, int direction, bool shift = false) {
y = -y;
}
For(set.view->carets) {
For (set.view->carets) {
XY xy = PosToXY(set.buffer, GetFront(it));
xy.col = 0;
if (direction == DIR_DOWN && xy.line == set.buffer->line_starts.len - 1) {
Range line_range = GetLineRange(set.buffer, xy.line);
xy.col = line_range.max - line_range.min;
} else if (direction == DIR_UP && xy.line == 0) {
xy.col = 0;
}
xy.line += y;