Continuing cleanup, ui.cpp

This commit is contained in:
Krzosa Karol
2026-01-17 11:12:18 +01:00
parent e52450a3df
commit 37154a2067
5 changed files with 335 additions and 362 deletions

View File

@@ -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;
}

View File

@@ -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<Command> *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<BufferID> 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");

View File

@@ -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<Int> 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<HistoryEntry> 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<HistoryEntry> 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() {

View File

@@ -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");

291
src/text_editor/ui.cpp Normal file
View File

@@ -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<Command> *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);
}