Files
text_editor/src/text_editor/text_editor.cpp
2025-12-23 08:52:42 +01:00

791 lines
25 KiB
C++

#include "basic/basic.h"
#include "basic/basic.cpp"
#include "profiler.h"
#include "SDL3/SDL.h"
#include "external/glad/glad.c"
#include "external/glad/glad.h"
#include "external/stb_truetype.h"
#include "external/stb_truetype.c"
#if OS_LINUX
#define MCO_USE_UCONTEXT
#endif
#define MINICORO_IMPL
#include "external/minicoro.h"
#include "render/generated_font.cpp"
#include "render/font.cpp"
#include "render/opengl.cpp"
#include "buffer.h"
#include "view.h"
#include "window.h"
#include "text_editor.h"
#include "globals.cpp"
#include "coroutines.cpp"
#include "buffer.cpp"
#include "view.cpp"
#include "window.cpp"
#include "window_command.cpp"
#include "window_debug.cpp"
#include "window_status.cpp"
#include "window_search.cpp"
#include "process.cpp"
#include "event.cpp"
#include "config.cpp"
#include "commands.cpp"
#include "commands_clipboard.cpp"
#include "draw.cpp"
#include "test/tests.cpp"
#if OS_WASM
EM_JS(void, JS_SetMouseCursor, (const char *cursor_str), {
document.getElementById("canvas").style.cursor = UTF8ToString(cursor_str);
})
void SetMouseCursor(SDL_SystemCursor id) {
if (id == SDL_SYSTEM_CURSOR_EW_RESIZE) {
JS_SetMouseCursor("ew-resize");
} else if (id == SDL_SYSTEM_CURSOR_NS_RESIZE) {
JS_SetMouseCursor("ns-resize");
} else if (id == SDL_SYSTEM_CURSOR_DEFAULT) {
JS_SetMouseCursor("default");
} else if (id == SDL_SYSTEM_CURSOR_TEXT) {
JS_SetMouseCursor("text");
} else {
InvalidCodepath();
}
}
#else
void SetMouseCursor(SDL_SystemCursor id) {
static SDL_Cursor *SDL_MouseCursor;
static SDL_SystemCursor last_id;
if (SDL_MouseCursor == NULL || last_id != id) {
if (SDL_MouseCursor != NULL) {
SDL_DestroyCursor(SDL_MouseCursor);
}
SDL_MouseCursor = SDL_CreateSystemCursor(id);
SDL_SetCursor(SDL_MouseCursor);
last_id = id;
}
}
#endif
void SetMouseCursor(Event event) {
Scratch scratch;
Array<Window *> order = GetWindowZOrder(scratch);
Vec2I mouse = MouseVec2I();
if (ResizerSelected.id != -1) {
Window *window = GetWindow(ResizerSelected);
if (window->layout) {
SetMouseCursor(SDL_SYSTEM_CURSOR_EW_RESIZE);
} else {
SetMouseCursor(SDL_SYSTEM_CURSOR_NS_RESIZE);
}
return;
}
if (ResizerHover.id != -1) {
Window *window = GetWindow(ResizerHover);
if (window->layout) {
SetMouseCursor(SDL_SYSTEM_CURSOR_EW_RESIZE);
} else {
SetMouseCursor(SDL_SYSTEM_CURSOR_NS_RESIZE);
}
return;
}
For(order) {
if (!it->visible) continue;
bool mouse_in_total = AreOverlapping(mouse, it->total_rect);
bool mouse_in_scrollbar = AreOverlapping(mouse, it->scrollbar_rect);
if (!IsDocumentSelectionValid() && (mouse_in_scrollbar || IsScrollbarSelectionValid())) {
SetMouseCursor(SDL_SYSTEM_CURSOR_DEFAULT);
return;
} else if (mouse_in_total || IsDocumentSelectionValid()) {
SetMouseCursor(SDL_SYSTEM_CURSOR_TEXT);
return;
}
}
SetMouseCursor(SDL_SYSTEM_CURSOR_DEFAULT);
}
void UpdateScroll(Window *window, bool update_caret_scrolling) {
ProfileFunction();
BSet set = GetBSet(window);
// Scrolling with caret
if (update_caret_scrolling) {
Caret c = set.view->carets[0];
Int front = GetFront(c);
XY xy = PosToXY(set.buffer, front);
Rect2I visible = GetVisibleCells(window);
Vec2I visible_cells = GetSize(visible);
Vec2I visible_size = visible_cells * Vec2I{window->font->char_spacing, window->font->line_spacing};
Vec2I rect_size = GetSize(window->document_rect);
if (xy.line >= visible.max.y - 2) {
Int set_view_at_line = xy.line - (visible_cells.y - 1);
Int cut_off_y = Max((Int)0, visible_size.y - rect_size.y);
set.view->scroll.y = (set_view_at_line * window->font->line_spacing) + cut_off_y;
}
if (xy.line < visible.min.y + 1) {
set.view->scroll.y = xy.line * window->font->line_spacing;
}
if (xy.col >= visible.max.x - 1) {
Int set_view_at_line = xy.col - (visible_cells.x - 1);
Int cut_off_x = Max((Int)0, visible_size.x - rect_size.x);
set.view->scroll.x = (set_view_at_line * window->font->char_spacing) + cut_off_x;
}
if (xy.col <= visible.min.x) {
set.view->scroll.x = xy.col * window->font->char_spacing;
}
}
// Clip scroll
{
Int last_line = LastLine(set.buffer);
set.view->scroll.y = Clamp(set.view->scroll.y, (Int)0, Max((Int)0, (last_line - 1) * window->font->line_spacing));
// @note:
// GetCharCountOfLongestLine is a bottleneck, there is probably an algorithm for
// calculating this value incrementally but do we even need X scrollbar or x clipping?
set.view->scroll.x = ClampBottom(set.view->scroll.x, (Int)0);
}
}
void OnCommand(Event event) {
ProfileFunction();
//
// Window cursor setting
//
Scratch scratch;
Array<Window *> order = GetWindowZOrder(scratch);
// Handle wheel scrolling
if (event.xwheel || event.ywheel) {
Vec2I mouse = MouseVec2I();
For (order) {
if (!it->visible) {
continue;
}
bool mouse_in_window = AreOverlapping(mouse, it->total_rect);
if (mouse_in_window) {
View *view = GetView(it->active_view);
view->scroll.y -= (Int)(event.ywheel * 48);
view->scroll.x += (Int)(event.xwheel * 48);
}
}
}
// Handle selected window scrollbar
// @note: the order here assumes that we won't run this code on the
// same event as the scroll was pressed
if (IsScrollbarSelectionValid() && Mouse(LEFT_UP)) {
Assert(DocumentSelected.id == -1);
ScrollbarSelected.id = -1;
} else if (IsScrollbarSelectionValid()) {
// :ScrollbarImprovement
// @todo: it generally works ok but it moves the scrollbar a bit on click
// when mouse is not even moving
Assert(DocumentSelected.id == -1);
Window *window = GetWindow(ScrollbarSelected);
View *view = GetView(window->active_view);
Vec2 mouse_vec2 = MouseVec2();
Scroller s = ComputeScrollerRect(window);
double size_y = (double)GetSize(window->scrollbar_rect).y;
double p = mouse_vec2.y - window->scrollbar_rect.min.y;
double v = p / size_y;
v = v + (window->mouse_scroller_offset);
view->scroll.y = (Int)(v * (double)s.line_count * (double)window->font->line_spacing);
}
if (DocumentSelected != ActiveWindowID) {
DocumentSelected.id = -1;
} else if (IsDocumentSelectionValid() && MouseUp()) {
Assert(ScrollbarSelected.id == -1);
DocumentSelected.id = -1;
} else if (IsDocumentSelectionValid()) {
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
// be smaller then 0 which means we cannot scroll
if (mouse.y == 0 && selected.window->document_rect.min.y == 0) {
float x, y;
SDL_GetGlobalMouseState(&x, &y);
x = roundf(DPIScale * x);
y = roundf(DPIScale * y);
if (y == 0) {
mouse.y = -10;
}
}
Int p = ScreenSpaceToBufferPos(selected.window, selected.view, selected.buffer, mouse);
Caret &caret = selected.view->carets[0];
caret = SetFrontWithAnchor(caret, DocumentAnchor, p);
}
if (ResizerSelected.id != -1 && Mouse(LEFT_UP)) {
Assert(DocumentSelected.id == -1);
Assert(ScrollbarSelected.id == -1);
ResizerSelected.id = {-1};
} else if (ResizerSelected.id != -1) {
Window *window = GetWindow(ResizerSelected);
if (window->layout) {
Vec2I mouse = MouseVec2I();
Int offx = mouse.x - window->resizer_rect.min.x;
window->weight += (double)offx / (double)WindowCalcEvenResizerValue(event.xwindow);
window->weight = Clamp(window->weight, 0.1, 100.0);
}
} else {
ResizerHover = {-1};
For(Windows) {
Vec2I mouse = MouseVec2I();
bool mouse_in_rect = AreOverlapping(mouse, it->resizer_rect);
if (mouse_in_rect) {
ResizerHover = it->id;
if (Mouse(LEFT)) {
ResizerSelected = it->id;
}
}
}
}
// Set active window on click
if (MousePress()) {
Vec2I mouse = MouseVec2I();
For(order) {
if (!it->visible) {
continue;
}
bool mouse_in_document = AreOverlapping(mouse, it->document_rect);
if (mouse_in_document) {
ActiveWindowID = it->id;
break;
}
}
}
if (Ctrl() && Shift() && Mouse(RIGHT)) {
} else if (Alt() && Ctrl() && Mouse(RIGHT)) {
} else if (Ctrl() && Mouse(RIGHT)) {
} else if (Alt() && Mouse(RIGHT)) {
} else if (Mouse(RIGHT)) {
Vec2I mouse = MouseVec2I();
BSet active = GetBSet(ActiveWindowID);
bool mouse_in_document = AreOverlapping(mouse, active.window->document_rect);
if (mouse_in_document) {
Int p = ScreenSpaceToBufferPos(active.window, active.view, active.buffer, mouse);
Int saved_front = -1;
IterRemove(active.view->carets) {
IterRemovePrepare(active.view->carets);
if (InBounds(it.range, p)) {
String16 string = GetString(active.buffer, it.range);
SaveStringInClipboard(string);
remove_item = true;
saved_front = GetFront(it);
}
}
if (active.view->carets.len == 0) Add(&active.view->carets, MakeCaret(saved_front));
if (saved_front == -1) {
Int line = PosToLine(active.buffer, p);
Range line_range = GetLineRangeWithoutNL(active.buffer, line);
String16 string = GetString(active.buffer, line_range);
SaveStringInClipboard(string);
}
}
}
if (Ctrl() && Mouse(LEFT)) {
MouseLoadWord(event);
} else if (Mouse(LEFT)) { // Uses Alt and shift
Vec2I mouse = MouseVec2I();
{
Assert(ScrollbarSelected.id == -1);
Assert(DocumentSelected.id == -1);
BSet active = GetBSet(ActiveWindowID); // using next to make sure mouse works on first click after switching the window
bool mouse_in_document = AreOverlapping(mouse, active.window->document_rect);
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);
if (!Alt() && !Shift()) active.view->carets.len = 1;
Caret &caret = active.view->carets[0];
if (Shift()) {
if (p <= caret.range.min) {
caret.range.min = p;
caret.ifront = 0;
} else if (p >= caret.range.max) {
caret.range.max = p;
caret.ifront = 1;
}
} else if (event.clicks >= 2 && InBounds({caret.range.min - 1, caret.range.max + 1}, p)) {
Range range = EncloseWord(active.buffer, p);
if (event.clicks >= 3) {
range = EncloseFullLine(active.buffer, p);
}
caret = MakeCaret(range.max, range.min);
} else {
caret = MakeCaret(p);
}
MergeCarets(active.buffer, &active.view->carets);
DocumentAnchor = caret;
}
}
// Figure out scrollbar click
// :ScrollbarImprovement
// @todo: it generally works ok but it moves the scrollbar a bit on click
// when mouse is not even moving
For(order) {
if (!it->visible) continue;
bool mouse_in_scrollbar = AreOverlapping(mouse, it->scrollbar_rect);
if (mouse_in_scrollbar) {
ScrollbarSelected = it->id;
View *view = GetView(it->active_view);
Vec2 mouse_vec2 = MouseVec2();
Scroller s = ComputeScrollerRect(it);
double size_y = (double)GetSize(it->scrollbar_rect).y;
double p = mouse_vec2.y - it->scrollbar_rect.min.y;
if (mouse_vec2.y < s.rect.min.y || mouse_vec2.y > s.rect.max.y) {
view->scroll.y = (Int)(p / size_y * (double)s.line_count * (double)it->font->line_spacing);
it->mouse_scroller_offset = -(double)GetSize(s.rect).y / 2.0 / size_y;
} else {
it->mouse_scroller_offset = (s.rect.min.y - p) / size_y;
}
break;
}
}
}
BSet main = GetBSet(LastActiveLayoutWindowID);
BSet active = GetBSet(ActiveWindowID);
For (CommandFunctions) {
if (it.trigger && MatchEvent(it.trigger, &event)) {
ProfileScopeEx(it.name);
it.function();
LastExecutedCommand = it.function;
}
}
if (event.kind == EVENT_DROP_FILE) {
WindowOpenBufferView(active.window, event.text);
}
if (event.kind == EVENT_TEXT_INPUT) {
Scratch scratch;
String16 string16 = ToString16(scratch, event.text);
Replace(active.view, string16);
}
if (event.kind == EVENT_QUIT) {
Command_Quit();
}
IF_DEBUG(AssertRanges(main.view->carets));
IF_DEBUG(AssertRanges(active.view->carets));
}
void GarbageCollect() {
if (RunGCThisFrame == false) {
return;
}
RunGCThisFrame = false;
ProfileFunction();
Allocator sys_allocator = GetSystemAllocator();
For(Buffers) {
if (it->file_mod_time) {
int64_t new_file_mod_time = GetFileModTime(it->name);
if (it->file_mod_time != new_file_mod_time) {
it->changed_on_disk = true;
if (it->dirty == false) ReopenBuffer(it);
}
}
}
IterRemove(Views) {
IterRemovePrepare(Views);
if (it->close && it->id.id == 0) {
InvalidCodepath();
}
Buffer *buffer = GetBuffer(it->active_buffer);
if (!it->close) {
if (!buffer->garbage) {
continue;
}
bool ref = ViewIsReferenced(it->id);
if (ref) {
continue;
}
}
RawAppendf(GCInfoBuffer, "View %d %S\n", (int)it->id.id, buffer->name);
remove_item = true;
Dealloc(&it->carets);
Dealloc(sys_allocator, it);
}
IterRemove(Buffers) {
IterRemovePrepare(Buffers);
if (it->close && it->id.id == 0) {
InvalidCodepath();
}
if (!it->close) {
if (!it->garbage) {
continue;
}
bool ref = BufferIsReferenced(it->id);
if (ref) {
continue;
}
}
RawAppendf(GCInfoBuffer, "Buff %d %S\n", (int)it->id.id, it->name);
remove_item = true;
DeallocBuffer(it);
}
IterRemove(Windows) {
IterRemovePrepare(Windows);
if (it->close && it->id.id == 0) {
InvalidCodepath();
}
if (it->close) {
RawAppendf(GCInfoBuffer, "Wind %d %S\n", (int)it->id.id);
Dealloc(&it->goto_history);
Dealloc(&it->goto_redo);
Dealloc(sys_allocator, it);
remove_item = true;
}
}
}
void Update(Event event) {
ProfileFunction();
LayoutWindows(event.xwindow, event.ywindow);
Scratch scratch;
Array<Window *> order = GetWindowZOrder(scratch);
For(order) {
if (!it->visible) {
continue;
}
View *view = GetView(it->active_view);
view->main_caret_on_begin_frame = view->carets[0];
view->update_scroll = true;
}
OnCommand(event);
StatusWindowUpdate();
DebugWindowUpdate();
CommandWindowUpdate();
SearchWindowUpdate();
UpdateProcesses();
CoUpdate(&event);
For (Buffers) it->begin_frame_change_id = it->change_id; // after last place we modify
{
ProfileScope(WindowEndOfFrameVisibilityAndLastActive);
For (Windows) {
if (it->sync_visibility_with_focus) {
if (it->id == ActiveWindowID) {
it->visible = true;
} else {
it->visible = false;
}
}
}
Window *window = GetWindow(ActiveWindowID);
if (ActiveWindowID.id != LastActiveLayoutWindowID.id) {
if (window->layout) {
LastActiveLayoutWindowID = ActiveWindowID;
}
}
}
For(IterateInReverse(&order)) {
if (!it->visible) continue;
View *view = GetView(it->active_view);
UpdateScroll(it, !AreEqual(view->main_caret_on_begin_frame, view->carets[0]) && view->update_scroll);
}
GarbageCollect();
}
void Windows_SetupVCVarsall(mco_coro *co) {
View *view = NULL;
{
Scratch scratch;
String working_dir = WorkDir;
String buffer_name = GetUniqueBufferName(working_dir, "vcvarsall-");
String cmd = Format(scratch, "\"%S\" && set", ConfigVCVarsall);
view = ExecHidden(buffer_name, cmd, working_dir);
}
for (;;) {
if (!ProcessIsActive(view->id)) {
break;
}
CoYield(co);
}
Scratch scratch;
Buffer *buffer = GetBuffer(view->active_buffer);
String16 string16 = GetString(buffer);
String string = ToString(scratch, string16);
Array<String> lines = Split(scratch, string, "\n");
For (lines) {
String s = Trim(it);
Array<String> values = Split(scratch, s, "=");
if (values.len == 1) continue;
Add(&ProcessEnviroment, Copy(GetSystemAllocator(), s));
}
}
void MainLoop() {
ProfileFunction();
Scratch scratch;
FrameID += 1;
Array<Event> frame_events = GetEventsForFrame(scratch);
Serializer ser = {EventBuffer};
For(frame_events) {
if (it.kind != 1) {
if (!Testing) Serialize(&ser, &it);
}
if (it.xwindow == 0 || it.ywindow == 0) {
int xwindow, ywindow;
SDL_GetWindowSizeInPixels(SDLWindow, &xwindow, &ywindow);
it.xwindow = xwindow;
it.ywindow = ywindow;
}
Update(it);
}
WaitForEvents = ConfigWaitForEvents;
if (IsDocumentSelectionValid() || IsScrollbarSelectionValid() || ActiveProcesses.len) {
WaitForEvents = false;
}
// This shouldn't matter to the state of the program, only appearance for
// the user
{
Window *window = GetActiveWind();
View *view = GetView(window->active_view);
Buffer *buffer = GetBuffer(view->active_buffer);
const char *dirty = buffer->dirty ? " !" : "";
String string = Format(scratch, "%S%s", buffer->name, dirty);
SDL_SetWindowTitle(SDLWindow, string.data);
}
Event *event = GetLast(frame_events);
SetMouseCursor(*event);
LayoutWindows(event->xwindow, event->ywindow); // This is here to render changes in title bar size without a frame of delay
BeginFrameRender(event->xwindow, event->ywindow);
Array<Window *> order = GetWindowZOrder(scratch);
For(IterateInReverse(&order)) {
if (!it->visible) continue;
DrawWindow(it, *GetLast(frame_events));
}
EndFrameRender(event->xwindow, event->ywindow, ColorBackground);
SDL_GL_SwapWindow(SDLWindow);
}
#if _WIN32
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
#else
extern char **environ;
int main(int argc, char **argv)
#endif
{
#if _WIN32
int argc = __argc;
char **argv = __argv;
AttachConsole(ATTACH_PARENT_PROCESS);
#endif
BeginProfiler();
if (1) {
RunArenaTest();
For (TestFunctions) {
it.function();
}
// ReportErrorf("Testing DONE\n");
// return 0;
}
#if !OS_WINDOWS
for (int i = 0; environ[i]; i += 1) {
Add(&ProcessEnviroment, Copy(Perm, environ[i]));
}
#endif
WorkDir = GetWorkingDir(Perm);
{
String sdl_config_path = SDL_GetPrefPath("krzosa", "text_editor");
if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '\\') {
sdl_config_path = Chop(sdl_config_path, 1); // chop '/'
}
if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '/') {
sdl_config_path = Chop(sdl_config_path, 1); // chop '/'
}
ConfigDir = NormalizePath(Perm, sdl_config_path);
SDL_free(sdl_config_path.data);
}
if (!SDL_Init(SDL_INIT_VIDEO)) {
ReportErrorf("Couldn't initialize SDL! %s", SDL_GetError());
return 1;
}
SDL_EnableScreenSaver();
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
#if OS_WASM
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#else
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
#endif
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_DisplayID primary_display_id = SDL_GetPrimaryDisplay();
const SDL_DisplayMode *display_mode = SDL_GetCurrentDisplayMode(primary_display_id);
// int w8 = (int)(display_mode->w * 0.8);
// int h8 = (int)(display_mode->h * 0.8);
int whalf = (int)(display_mode->w * 0.5) - 10;
int hhalf = (int)(display_mode->h) - 120;
int xhalf = whalf;
int yhalf = 30;
Uint32 window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY;
SDLWindow = SDL_CreateWindow("Text editor", whalf, hhalf, window_flags);
if (SDLWindow == NULL) {
ReportErrorf("Couldn't create window! %s", SDL_GetError());
return 1;
}
SDL_SetWindowPosition(SDLWindow, xhalf, yhalf);
SDL_GLContext gl_context = SDL_GL_CreateContext(SDLWindow);
SDL_GL_MakeCurrent(SDLWindow, gl_context);
SDL_ShowWindow(SDLWindow);
// Set icon
{
uint32_t data = 0xddddddff;
SDL_Surface *surface = SDL_CreateSurfaceFrom(1, 1, SDL_PIXELFORMAT_RGBA8888, &data, sizeof(uint32_t));
SDL_SetWindowIcon(SDLWindow, surface);
SDL_DestroySurface(surface);
}
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
ReportErrorf("Couldn't load OpenGL! %s", SDL_GetError());
return 1;
}
SDL_StartTextInput(SDLWindow);
SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, true);
SDL_GL_SetSwapInterval(1); // vsync
{
float scale = SDL_GetWindowDisplayScale(SDLWindow);
if (scale != 1.0f) DPIScale = scale;
}
InitBuffers();
InitRender();
ReloadFont(ConfigFont, (U32)ConfigFontSize);
InitWindows();
InitOS(ReportWarningf);
For (CommandFunctions) {
if (it.binding.len != 0) {
it.trigger = ParseKey(Perm, it.binding, it.name.data);
}
}
for (int i = 1; i < argc; i += 1) {
String it = argv[i];
if (it == "--testing") {
Testing = true;
} else {
if (!FileExists(it)) continue;
Window *window = GetWindow({0});
WindowOpenBufferView(window, it);
}
}
ReportConsolef("WorkDir = %S", WorkDir);
if (Testing) InitTests();
#if OS_WINDOWS
CoAdd(Windows_SetupVCVarsall);
#endif
#if OS_WASM
emscripten_set_main_loop(MainLoop, 0, 1);
#else
while (AppIsRunning) {
MainLoop();
}
#endif
SDL_DestroyWindow(SDLWindow);
SDL_Quit();
EndProfiler();
return 0;
}