From 37154a2067d406e27672ac3902969c0f642cd261 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Sat, 17 Jan 2026 11:12:18 +0100 Subject: [PATCH] Continuing cleanup, ui.cpp --- src/text_editor/buffer.cpp | 10 + src/text_editor/commands.cpp | 348 ++---------------------- src/text_editor/plugin_debug_window.cpp | 37 +-- src/text_editor/text_editor.cpp | 11 +- src/text_editor/ui.cpp | 291 ++++++++++++++++++++ 5 files changed, 335 insertions(+), 362 deletions(-) create mode 100644 src/text_editor/ui.cpp diff --git a/src/text_editor/buffer.cpp b/src/text_editor/buffer.cpp index 15cb8fa..12cdf3a 100644 --- a/src/text_editor/buffer.cpp +++ b/src/text_editor/buffer.cpp @@ -1556,3 +1556,13 @@ void SaveBuffer(Buffer *buffer) { ReportWarningf("Failed to save file with name: %S", buffer->name); } } + +String GetBufferDirectory(Buffer *buffer) { +#if PLUGIN_DIRECTORY_NAVIGATION + if (buffer->is_dir) { + return buffer->name; + } +#endif + String result = ChopLastSlash(buffer->name); + return result; +} diff --git a/src/text_editor/commands.cpp b/src/text_editor/commands.cpp index 88322b5..ad45f69 100644 --- a/src/text_editor/commands.cpp +++ b/src/text_editor/commands.cpp @@ -1,27 +1,3 @@ -String GetBufferDirectory(Buffer *buffer) { -#if PLUGIN_DIRECTORY_NAVIGATION - if (buffer->is_dir) { - return buffer->name; - } -#endif - String result = ChopLastSlash(buffer->name); - return result; -} - -String GetPrimaryDirectory() { - BSet main = GetBSet(PrimaryWindowID); - return GetBufferDirectory(main.buffer); -} - -void JumpTempBuffer(BSet *set, String buffer_name) { - if (buffer_name.len == 0) { - buffer_name = GetUniqueBufferName(GetBufferDirectory(set->buffer), "temp"); - } - set->view = WindowOpenBufferView(set->window, buffer_name); - set->buffer = GetBuffer(set->view->active_buffer); - set->buffer->temp = true; -} - View *GetViewForFixingWhenBufferCommand(Buffer *buffer, bool *is_active = NULL) { View *view = NULL; if (is_active) { @@ -120,20 +96,6 @@ void Appendf(View *view, const char *fmt, ...) { Append(view, string, true); } -void UIMessagef(const char *fmt, ...) { - Scratch scratch; - STRING_FORMAT(scratch, fmt, string); - BSet main = GetBSet(PrimaryWindowID); - JumpTempBuffer(&main); - NextActiveWindowID = main.window->id; - RawAppendf(main.buffer, "\n %S\n :Close\n", string); - main.view->carets[0] = FindNext(main.buffer, u":Close", MakeCaret(0)); - AddCommand(&main.view->commands, "Close", EnterOrEscapeKeySet, [](){ - BSet active = GetBSet(ActiveWindowID); - Close(active.buffer->id); - }); -} - void ReportErrorf(const char *fmt, ...) { Scratch scratch; STRING_FORMAT(scratch, fmt, string); @@ -145,7 +107,7 @@ void ReportErrorf(const char *fmt, ...) { if (view) { Appendf(view, "%S\n", string); } - UIMessagef("%S", string); + ShowUIMessagef("%S", string); } void ReportConsolef(const char *fmt, ...) { @@ -165,6 +127,13 @@ void ReportWarningf(const char *fmt, ...) { Appendf(null_view, "%S\n", string); } +void CMD_OpenLoadWord() { + Scratch scratch; + BSet active = GetBSet(ActiveWindowID); + String16 load_word = FetchLoadWord(active.view); + Open(load_word); +} RegisterCommand(CMD_OpenLoadWord, "ctrl-q | f12", "Open a link under the caret (file link, url, command) or open the selection"); + void CMD_CenterView() { CenterView(PrimaryWindowID); } RegisterCommand(CMD_CenterView, ""); @@ -269,226 +238,6 @@ void CMD_SaveAll() { } } RegisterCommand(CMD_SaveAll, "ctrl-shift-s"); -void MouseLoadWord(Event event, ResolveOpenMeta meta = ResolveOpenMeta_Normal) { - Vec2I mouse = MouseVec2I(); - BSet active = GetBSet(ActiveWindowID); - - bool mouse_in_document = AreOverlapping(mouse, active.window->document_rect); - if (mouse_in_document) { - Int p = ScreenSpaceToBufferPosErrorOutOfBounds(active.window, active.view, active.buffer, mouse); - if (p != -1) { - Range enclose = EncloseLoadWord(active.buffer, p); - if (InBounds(active.view->carets[0].range, p)) enclose = active.view->carets[0].range; - String16 string = GetString(active.buffer, enclose); - - active.view->carets.len = 1; - active.view->carets[0] = MakeCaret(p); - Open(string, meta); - } - } -} - -ResolvedOpen ResolveOpen(Allocator alo, String path, ResolveOpenMeta meta) { - ResolvedOpen result = {}; - path = Trim(path); - - // Editor command - if(!(ResolveOpenMeta_DontExec & meta)) { - if (StartsWith(path, ":")) { -#if PLUGIN_CONFIG - if (StartsWith(path, ":Set ")) { - result.kind = OpenKind_Set; - result.path = Skip(path, 5); - return result; - } -#endif - - result.kind = OpenKind_Command; - path = Skip(path, 1); - result.path.data = path.data; - for (Int i = 0; i < path.len; i += 1) { - if (IsNonWord(path.data[i])) { - break; - } - result.path.len += 1; - } - return result; - } - } - - // Shell - if (!(ResolveOpenMeta_DontExec & meta)) { - if (StartsWith(path, "!!")) { - result.kind = OpenKind_BackgroundExec; - result.path = Skip(path, 2); - return result; - } - if (StartsWith(path, "!")) { - result.kind = OpenKind_Exec; - result.path = Skip(path, 1); - return result; - } - } - - // Web - if (!(ResolveOpenMeta_DontExec & meta)) { - if (StartsWith(path, "https://") || StartsWith(path, "http://")) { - result.path = Format(alo, "%S %S", InternetBrowser, path); - result.kind = OpenKind_BackgroundExec; - return result; - } - } - - // Commit - if (!(ResolveOpenMeta_DontExec & meta)) { - if (StartsWith(path, "commit ")) { - path = Skip(path, 7); - result.path = Format(alo, "git --no-pager show %S", path); - result.kind = OpenKind_Exec; - return result; - } - } - - { - String p = NormalizePath(alo, path); - if (p.len == 2 && IsAlphabetic(ToLowerCase(At(p, 0))) && At(p, 1) == ':') { - p = Format(alo, "%S/", p); - } - String pstart = p; - - bool is_absolute = false; - if (IsAlphabetic(ToLowerCase(At(p, 0))) && At(p, 1) == ':' && At(p, 2) == '/') { - is_absolute = true; - p = Skip(p, 3); - } else if (At(p, 0) == '/') { - is_absolute = true; - p = Skip(p, 1); - } - - while (!IsOpenBoundary(At(p, 0))) { - p = Skip(p, 1); - } - path = {pstart.data, (Int)(p.data - pstart.data)}; - - if (At(p, 0) == ':') { - p = Skip(p, 1); - result.line = SkipInt(&p); - if (At(p, 0) == ':') { - p = Skip(p, 1); - Int b = SkipInt(&p); - result.col = b; - } - } else if (At(p, 0) == '(') { - p = Skip(p, 1); - result.line = SkipInt(&p); - if (At(p, 0) == ',') { - p = Skip(p, 1); - Int b = SkipInt(&p); - result.col = b; - } - } - - Buffer *existing_buffer = GetBuffer(path, NULL); - if (existing_buffer != NULL) { - result.path = path; - result.kind = OpenKind_Goto; - result.existing_buffer = true; - return result; - } - - if (is_absolute && FileExists(path)) { - result.path = path; - result.kind = OpenKind_Goto; - return result; - } else { - String rel_path = Format(alo, "%S/%S", GetPrimaryDirectory(), path); - existing_buffer = GetBuffer(rel_path, NULL); - if (existing_buffer || FileExists(rel_path)) { - result.existing_buffer = existing_buffer; - result.path = rel_path; - result.kind = OpenKind_Goto; - return result; - } - } - - } - - if (meta & ResolveOpenMeta_DontError) { - result.path = path; - result.kind = OpenKind_Skip; - return result; - } - - return result; -} - -BSet Open(Window *window, String path, ResolveOpenMeta meta, bool set_active = true) { - Scratch scratch; - BSet set = GetBSet(window); - ResolvedOpen o = ResolveOpen(scratch, path, meta); - if (o.kind == OpenKind_Goto) { - if (set_active) { - NextActiveWindowID = set.window->id; - } - View *view = WindowOpenBufferView(set.window, o.path); - if (IsDir(o.path)) { -#if PLUGIN_DIRECTORY_NAVIGATION - OpenDirectoryNavigation(view); -#endif - } - - Buffer *buffer = GetBuffer(view->active_buffer); - if (o.line != -1) { - if (o.col == -1) o.col = 1; - Int pos = XYToPos(buffer, {o.col - 1, o.line - 1}); - SelectRange(view, MakeCaret(pos)); - } - CenterView(window->id); - } else if (o.kind == OpenKind_Exec) { - if (set_active) { - NextActiveWindowID = set.window->id; - } - JumpTempBuffer(&set); - Exec(set.view->id, false, o.path, GetPrimaryDirectory()); - } else if (o.kind == OpenKind_BackgroundExec) { - // this shouldn't change the focus/window/view - Exec(NullViewID, false, o.path, GetPrimaryDirectory()); - } else if (o.kind == OpenKind_Command) { - EvalCommand(o.path); - } -#if PLUGIN_CONFIG - else if (o.kind == OpenKind_Set) { - Set(o.path); - } -#endif - else if (o.kind == OpenKind_Skip) { - return {}; - } else { - ReportErrorf("Failed to open: %S", path); - } - - return GetBSet(window); -} - -BSet Open(String path, ResolveOpenMeta meta) { - BSet main = GetBSet(PrimaryWindowID); - main = Open(main.window, path, meta); - return main; -} - -BSet Open(String16 path, ResolveOpenMeta meta) { - Scratch scratch; - String string = ToString(scratch, path); - return Open(string, meta); -} - -void CMD_OpenLoadWord() { - Scratch scratch; - BSet active = GetBSet(ActiveWindowID); - String16 load_word = FetchLoadWord(active.view); - Open(load_word); -} RegisterCommand(CMD_OpenLoadWord, "ctrl-q | f12", "Open a link under the caret (file link, url, command) or open the selection"); - void CMD_Save() { BSet active = GetBSet(PrimaryWindowID); SaveBuffer(active.buffer); @@ -527,41 +276,7 @@ void CMD_KillProcess() { KillProcess(main.view); } RegisterCommand(CMD_KillProcess, "", "Kill process in the last active primary window"); -void AddCommand(Array *arr, String name, Trigger *trigger, CMDFunction *function) { - IterRemove(*arr) { - IterRemovePrepare(*arr); - if (it.name == name) { - remove_item = true; - } - } - Command cmd = {}; - cmd.name = name; - cmd.function = function; - cmd.trigger = trigger; - cmd.docs = "Not listing commands attached to things anywhere currently, maybe should change?"; - Add(arr, cmd); -} - -UIAction WaitForUIAction(mco_coro *co, BSet main) { - UIAction result = UIAction_Cancel; - for (;;) { - if (main.window->active_view != main.view->id || (main.window->id != PrimaryWindowID && main.window->id != ActiveWindowID) || main.window->close) { - break; - } - - if (main.view->ui_action != UIAction_Null) { - result = main.view->ui_action; - break; - } - - CoYield(co); - } - Close(main.buffer->id); - return result; -} -#define AddUIAction(VIEW,NAME,TRIGGER,ACTION) AddCommand(&((VIEW)->commands),(NAME),(TRIGGER),[](){BSet active = GetBSet(ActiveWindowID); active.view->ui_action = (ACTION);}) - -void Coro_Rename(mco_coro *co) { +void ShowRenameUI(mco_coro *co) { BSet main = GetBSet(PrimaryWindowID); Buffer *original_buffer = main.buffer; JumpTempBuffer(&main); @@ -589,29 +304,12 @@ void Coro_Rename(mco_coro *co) { } void CMD_Rename() { - CoRemove("Coro_Rename"); - CoData *data = CoAdd(Coro_Rename); + CoRemove("ShowRenameUI"); + CoData *data = CoAdd(ShowRenameUI); CoResume(data); } RegisterCommand(CMD_Rename, "", "Opens a UI asking for a new name of a buffer open in the last active primary window"); -UIAction Coro_YesNoCancel(mco_coro *co, BSet main, String question) { - JumpTempBuffer(&main); - NextActiveWindowID = main.window->id; - RawAppendf(main.buffer, R"==( -%S - - :Yes :No :Cancel -)==", question); - main.view->carets[0] = FindNext(main.buffer, u":Yes", MakeCaret(0)); - main.view->carets[0].range.min = main.view->carets[0].range.max; - AddUIAction(main.view, "Yes", EnterKey, UIAction_Yes); - AddUIAction(main.view, "No", NULL, UIAction_No); - AddUIAction(main.view, "Cancel", EscapeKey, UIAction_Cancel); - UIAction ui_action = WaitForUIAction(co, main); - return ui_action; -} - -void Coro_Close(mco_coro *co) { +void ShowCloseViewUI(mco_coro *co) { BSet main = GetBSet(PrimaryWindowID); if (main.buffer->special || main.buffer->temp) { Close(main.view->id); @@ -640,7 +338,7 @@ void Coro_Close(mco_coro *co) { } String question = Format(CoCurr->arena, "Do you want to save [%S] before closing?", main.buffer->name); - UIAction ui_action = Coro_YesNoCancel(co, main, question); + UIAction ui_action = ShowYesNoCancelUI(co, main, question); if (ui_action == UIAction_Yes) { SaveBuffer(main.buffer); Close(main.buffer->id); @@ -652,8 +350,8 @@ void Coro_Close(mco_coro *co) { } void CMD_Close() { - CoRemove("Coro_Close"); - CoData *data = CoAdd(Coro_Close); + CoRemove("ShowCloseViewUI"); + CoData *data = CoAdd(ShowCloseViewUI); CoResume(data); } RegisterCommand(CMD_Close, "ctrl-w", "Close open view in the last active primary window"); @@ -668,7 +366,7 @@ void CMD_DeleteFile() { // 1. Does scratch memory leak across Yield boundary? Or interacts badly with Yield stuff in any way? // 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? -String Coro_CloseAllEx(mco_coro *co) { +UIAction ShowCloseAllUI(mco_coro *co) { BSet main = GetBSet(PrimaryWindowID); Array buffers = {CoCurr->arena}; For (Buffers) Add(&buffers, it->id); @@ -682,24 +380,24 @@ String Coro_CloseAllEx(mco_coro *co) { } String question = Format(CoCurr->arena, "Do you want to save [%S] before closing?", it->name); - UIAction ui_action = Coro_YesNoCancel(co, main, question); + UIAction ui_action = ShowYesNoCancelUI(co, main, question); it = GetBuffer(id, NULL); if (it && ui_action == UIAction_Yes) { SaveBuffer(it); } else if (ui_action == UIAction_No) { } else if (ui_action == UIAction_Cancel) { - return "Cancel"; + return UIAction_Cancel; } ElseInvalidCodepath(); } For(Buffers) { Close(it->id); } - return "Yes"; + return UIAction_Yes; } void Coro_CloseAll(mco_coro *co) { - Coro_CloseAllEx(co); + ShowCloseAllUI(co); } void CMD_CloseAll() { @@ -720,7 +418,7 @@ void CMD_MakeFontSmaller() { } } RegisterCommand(CMD_MakeFontSmaller, "ctrl-minus", "Decrease the font size"); -void Coro_ReplaceAll(mco_coro *co) { +void ShowReplaceAllUI(mco_coro *co) { BSet main = GetBSet(PrimaryWindowID); String16 string = FetchLoadWord(main.view); String string8 = ToString(CoCurr->arena, string); @@ -783,7 +481,7 @@ void Coro_ReplaceAll(mco_coro *co) { } void CMD_ReplaceAll() { - CoRemove("Coro_ReplaceAll"); - CoData *data = CoAdd(Coro_ReplaceAll); + CoRemove("ShowReplaceAllUI"); + CoData *data = CoAdd(ShowReplaceAllUI); CoResume(data); } RegisterCommand(CMD_ReplaceAll, "ctrl-shift-r", "Search and replace over the entire project, you need to select a text with format like this 'FindAnd@>ReplaceWith' and executing the command will change all occurences of FindAnd to ReplaceWith"); diff --git a/src/text_editor/plugin_debug_window.cpp b/src/text_editor/plugin_debug_window.cpp index 263e47d..5ad0490 100644 --- a/src/text_editor/plugin_debug_window.cpp +++ b/src/text_editor/plugin_debug_window.cpp @@ -48,39 +48,12 @@ void UpdateDebugWindow() { } BSet main = GetBSet(PrimaryWindowID); + RawReplaceText(set.buffer, GetRange(set.buffer), u"Active buffers and views:\n"); - Scratch scratch; - String s = Format(scratch, "wid: %d\nvid: %d\nbid: %d\nframe: %lld\n", (int)main.window->id.id, (int)main.view->id.id, (int)main.buffer->id.id, (long long)FrameID); - String16 string = ToString16(scratch, s); - RawReplaceText(set.buffer, GetRange(set.buffer), string); - - float xmouse, ymouse; - SDL_GetMouseState(&xmouse, &ymouse); - RawAppendf(set.buffer, "mouse: [%f, %f]\n", roundf(DPIScale * xmouse), roundf(DPIScale * ymouse)); - - RawAppendf(set.buffer, "BufferID id = %d\n", main.buffer->id.id); - RawAppendf(set.buffer, "String name = %S\n", main.buffer->name); - RawAppendf(set.buffer, "Int change_id = %lld\n", (long long)main.buffer->change_id); - RawAppendf(set.buffer, "Int user_change_id = %lld\n", (long long)main.buffer->user_change_id); - RawAppendf(set.buffer, "Int file_mod_time = %lld\n", (long long)main.buffer->file_mod_time); - RawAppendf(set.buffer, "\n"); - - RawAppendf(set.buffer, "U16 *data = %zu\n", main.buffer->data); - RawAppendf(set.buffer, "Int len = %lld\n", (long long)main.buffer->len); - RawAppendf(set.buffer, "Int cap = %lld\n", (long long)main.buffer->cap); - RawAppendf(set.buffer, "Array line_starts = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->line_starts.len, (long long)main.buffer->line_starts.cap, main.buffer->line_starts.data); - RawAppendf(set.buffer, "\n"); - - RawAppendf(set.buffer, "Array undo_stack = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->undo_stack.len, (long long)main.buffer->undo_stack.cap, main.buffer->undo_stack.data); - RawAppendf(set.buffer, "Array redo_stack = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->redo_stack.len, (long long)main.buffer->redo_stack.cap, main.buffer->redo_stack.data); - RawAppendf(set.buffer, "int edit_phase = %d", main.buffer->edit_phase); - RawAppendf(set.buffer, "\n"); - - RawAppendf(set.buffer, "int no_history = %d\n", main.buffer->no_history); - RawAppendf(set.buffer, "int no_line_starts = %d\n", main.buffer->no_line_starts); - RawAppendf(set.buffer, "int dirty = %d\n", main.buffer->dirty); - RawAppendf(set.buffer, "int changed_on_disk = %d\n", main.buffer->changed_on_disk); - RawAppendf(set.buffer, "int temp = %d\n", main.buffer->temp); + For (Views) { + Buffer *buffer = GetBuffer(it->active_buffer); + RawAppendf(set.buffer, "view->id:%lld, buffer->id:%lld, buffer->name:%S\n", (long long)it->id.id, (long long)buffer->id.id, buffer->name); + } } void CMD_ToggleDebug() { diff --git a/src/text_editor/text_editor.cpp b/src/text_editor/text_editor.cpp index 6d759d2..7d1f13b 100644 --- a/src/text_editor/text_editor.cpp +++ b/src/text_editor/text_editor.cpp @@ -48,6 +48,7 @@ #include "process.cpp" #include "event.cpp" #include "config.cpp" +#include "ui.cpp" #include "commands.cpp" #include "commands_clipboard.cpp" #include "scratch.cpp" @@ -159,16 +160,16 @@ void CMD_QuitWithoutSaving() { AppIsRunning = false; } RegisterCommand(CMD_QuitWithoutSaving, "", "Self explanatory"); -void Coro_Quit(mco_coro *co) { - String res = Coro_CloseAllEx(co); - if (res != "Cancel") { +void ShowQuitAppUI(mco_coro *co) { + UIAction res = ShowCloseAllUI(co); + if (res != UIAction_Cancel) { CMD_QuitWithoutSaving(); } } void CMD_Quit() { - CoRemove("Coro_Quit"); - CoData *data = CoAdd(Coro_Quit); + CoRemove("ShowQuitAppUI"); + CoData *data = CoAdd(ShowQuitAppUI); CoResume(data); } RegisterCommand(CMD_Quit, "", "Ask user which files he would like to save and exit"); diff --git a/src/text_editor/ui.cpp b/src/text_editor/ui.cpp new file mode 100644 index 0000000..43730df --- /dev/null +++ b/src/text_editor/ui.cpp @@ -0,0 +1,291 @@ +void JumpTempBuffer(BSet *set, String buffer_name) { + if (buffer_name.len == 0) { + buffer_name = GetUniqueBufferName(GetBufferDirectory(set->buffer), "temp"); + } + set->view = WindowOpenBufferView(set->window, buffer_name); + set->buffer = GetBuffer(set->view->active_buffer); + set->buffer->temp = true; +} + +void AddCommand(Array *arr, String name, Trigger *trigger, CMDFunction *function) { + IterRemove(*arr) { + IterRemovePrepare(*arr); + if (it.name == name) { + remove_item = true; + } + } + Command cmd = {}; + cmd.name = name; + cmd.function = function; + cmd.trigger = trigger; + cmd.docs = "Not listing commands attached to things anywhere currently, maybe should change?"; + Add(arr, cmd); +} + +#define AddUIAction(VIEW,NAME,TRIGGER,ACTION) AddCommand(&((VIEW)->commands),(NAME),(TRIGGER),[](){BSet active = GetBSet(ActiveWindowID); active.view->ui_action = (ACTION);}) +UIAction WaitForUIAction(mco_coro *co, BSet main) { + UIAction result = UIAction_Cancel; + for (;;) { + if (main.window->active_view != main.view->id || (main.window->id != PrimaryWindowID && main.window->id != ActiveWindowID) || main.window->close) { + break; + } + + if (main.view->ui_action != UIAction_Null) { + result = main.view->ui_action; + break; + } + + CoYield(co); + } + Close(main.buffer->id); + return result; +} + +UIAction ShowYesNoCancelUI(mco_coro *co, BSet main, String question) { + JumpTempBuffer(&main); + NextActiveWindowID = main.window->id; + RawAppendf(main.buffer, R"==( +%S + + :Yes :No :Cancel +)==", question); + main.view->carets[0] = FindNext(main.buffer, u":Yes", MakeCaret(0)); + main.view->carets[0].range.min = main.view->carets[0].range.max; + AddUIAction(main.view, "Yes", EnterKey, UIAction_Yes); + AddUIAction(main.view, "No", NULL, UIAction_No); + AddUIAction(main.view, "Cancel", EscapeKey, UIAction_Cancel); + UIAction ui_action = WaitForUIAction(co, main); + return ui_action; +} + +void ShowUIMessagef(const char *fmt, ...) { + Scratch scratch; + STRING_FORMAT(scratch, fmt, string); + BSet main = GetBSet(PrimaryWindowID); + JumpTempBuffer(&main); + NextActiveWindowID = main.window->id; + RawAppendf(main.buffer, "\n %S\n :Close\n", string); + main.view->carets[0] = FindNext(main.buffer, u":Close", MakeCaret(0)); + AddCommand(&main.view->commands, "Close", EnterOrEscapeKeySet, [](){ + BSet active = GetBSet(ActiveWindowID); + Close(active.buffer->id); + }); +} + +String GetPrimaryDirectory() { + BSet main = GetBSet(PrimaryWindowID); + return GetBufferDirectory(main.buffer); +} + +void MouseLoadWord(Event event, ResolveOpenMeta meta = ResolveOpenMeta_Normal) { + Vec2I mouse = MouseVec2I(); + BSet active = GetBSet(ActiveWindowID); + + bool mouse_in_document = AreOverlapping(mouse, active.window->document_rect); + if (mouse_in_document) { + Int p = ScreenSpaceToBufferPosErrorOutOfBounds(active.window, active.view, active.buffer, mouse); + if (p != -1) { + Range enclose = EncloseLoadWord(active.buffer, p); + if (InBounds(active.view->carets[0].range, p)) enclose = active.view->carets[0].range; + String16 string = GetString(active.buffer, enclose); + + active.view->carets.len = 1; + active.view->carets[0] = MakeCaret(p); + Open(string, meta); + } + } +} + +ResolvedOpen ResolveOpen(Allocator alo, String path, ResolveOpenMeta meta) { + ResolvedOpen result = {}; + path = Trim(path); + + // Editor command + if(!(ResolveOpenMeta_DontExec & meta)) { + if (StartsWith(path, ":")) { +#if PLUGIN_CONFIG + if (StartsWith(path, ":Set ")) { + result.kind = OpenKind_Set; + result.path = Skip(path, 5); + return result; + } +#endif + + result.kind = OpenKind_Command; + path = Skip(path, 1); + result.path.data = path.data; + for (Int i = 0; i < path.len; i += 1) { + if (IsNonWord(path.data[i])) { + break; + } + result.path.len += 1; + } + return result; + } + } + + // Shell + if (!(ResolveOpenMeta_DontExec & meta)) { + if (StartsWith(path, "!!")) { + result.kind = OpenKind_BackgroundExec; + result.path = Skip(path, 2); + return result; + } + if (StartsWith(path, "!")) { + result.kind = OpenKind_Exec; + result.path = Skip(path, 1); + return result; + } + } + + // Web + if (!(ResolveOpenMeta_DontExec & meta)) { + if (StartsWith(path, "https://") || StartsWith(path, "http://")) { + result.path = Format(alo, "%S %S", InternetBrowser, path); + result.kind = OpenKind_BackgroundExec; + return result; + } + } + + // Commit + if (!(ResolveOpenMeta_DontExec & meta)) { + if (StartsWith(path, "commit ")) { + path = Skip(path, 7); + result.path = Format(alo, "git --no-pager show %S", path); + result.kind = OpenKind_Exec; + return result; + } + } + + { + String p = NormalizePath(alo, path); + if (p.len == 2 && IsAlphabetic(ToLowerCase(At(p, 0))) && At(p, 1) == ':') { + p = Format(alo, "%S/", p); + } + String pstart = p; + + bool is_absolute = false; + if (IsAlphabetic(ToLowerCase(At(p, 0))) && At(p, 1) == ':' && At(p, 2) == '/') { + is_absolute = true; + p = Skip(p, 3); + } else if (At(p, 0) == '/') { + is_absolute = true; + p = Skip(p, 1); + } + + while (!IsOpenBoundary(At(p, 0))) { + p = Skip(p, 1); + } + path = {pstart.data, (Int)(p.data - pstart.data)}; + + if (At(p, 0) == ':') { + p = Skip(p, 1); + result.line = SkipInt(&p); + if (At(p, 0) == ':') { + p = Skip(p, 1); + Int b = SkipInt(&p); + result.col = b; + } + } else if (At(p, 0) == '(') { + p = Skip(p, 1); + result.line = SkipInt(&p); + if (At(p, 0) == ',') { + p = Skip(p, 1); + Int b = SkipInt(&p); + result.col = b; + } + } + + Buffer *existing_buffer = GetBuffer(path, NULL); + if (existing_buffer != NULL) { + result.path = path; + result.kind = OpenKind_Goto; + result.existing_buffer = true; + return result; + } + + if (is_absolute && FileExists(path)) { + result.path = path; + result.kind = OpenKind_Goto; + return result; + } else { + String rel_path = Format(alo, "%S/%S", GetPrimaryDirectory(), path); + existing_buffer = GetBuffer(rel_path, NULL); + if (existing_buffer || FileExists(rel_path)) { + result.existing_buffer = existing_buffer; + result.path = rel_path; + result.kind = OpenKind_Goto; + return result; + } + } + + } + + if (meta & ResolveOpenMeta_DontError) { + result.path = path; + result.kind = OpenKind_Skip; + return result; + } + + return result; +} + +BSet Open(Window *window, String path, ResolveOpenMeta meta, bool set_active = true) { + Scratch scratch; + BSet set = GetBSet(window); + ResolvedOpen o = ResolveOpen(scratch, path, meta); + if (o.kind == OpenKind_Goto) { + if (set_active) { + NextActiveWindowID = set.window->id; + } + View *view = WindowOpenBufferView(set.window, o.path); + if (IsDir(o.path)) { +#if PLUGIN_DIRECTORY_NAVIGATION + OpenDirectoryNavigation(view); +#endif + } + + Buffer *buffer = GetBuffer(view->active_buffer); + if (o.line != -1) { + if (o.col == -1) o.col = 1; + Int pos = XYToPos(buffer, {o.col - 1, o.line - 1}); + SelectRange(view, MakeCaret(pos)); + } + CenterView(window->id); + } else if (o.kind == OpenKind_Exec) { + if (set_active) { + NextActiveWindowID = set.window->id; + } + JumpTempBuffer(&set); + Exec(set.view->id, false, o.path, GetPrimaryDirectory()); + } else if (o.kind == OpenKind_BackgroundExec) { + // this shouldn't change the focus/window/view + Exec(NullViewID, false, o.path, GetPrimaryDirectory()); + } else if (o.kind == OpenKind_Command) { + EvalCommand(o.path); + } +#if PLUGIN_CONFIG + else if (o.kind == OpenKind_Set) { + Set(o.path); + } +#endif + else if (o.kind == OpenKind_Skip) { + return {}; + } else { + ReportErrorf("Failed to open: %S", path); + } + + return GetBSet(window); +} + +BSet Open(String path, ResolveOpenMeta meta) { + BSet main = GetBSet(PrimaryWindowID); + main = Open(main.window, path, meta); + return main; +} + +BSet Open(String16 path, ResolveOpenMeta meta) { + Scratch scratch; + String string = ToString(scratch, path); + return Open(string, meta); +}