plugin_directory_navigation

This commit is contained in:
Krzosa Karol
2026-01-16 08:58:13 +01:00
parent 6c200f7764
commit d2be40a282
9 changed files with 294 additions and 126 deletions

View File

@@ -306,6 +306,11 @@ API Int GetWordEnd(Buffer *buffer, Int pos) {
return pos;
}
bool IsOpenBoundary(char c) {
bool result = c == 0 || IsParen(c) || IsBrace(c) || c == ':' || c == '\t' || c == '\n' || c == '"' || c == '\'';
return result;
}
API bool IsLoadWord(char16_t w) {
bool result = w == u'-' || w == u'/' || w == u'\\' || w == u':' || w == u'$' || w == u'_' || w == u'.' || w == u'!' || w == u'@';
if (!result) {
@@ -1503,8 +1508,10 @@ Buffer *BufferOpenFile(String path) {
buffer = CreateBuffer(sys_allocator, path);
} else if (IsDir(path)) {
buffer = CreateBuffer(sys_allocator, path);
#ifdef PLUGIN_DIRECTORY_NAVIGATION
buffer->is_dir = true;
buffer->temp = true;
#endif
} else {
String string = ReadFile(scratch, path);
buffer = CreateBuffer(sys_allocator, path, string.len * 4 + 4096);
@@ -1530,13 +1537,10 @@ bool BufferIsReferenced(BufferID buffer_id) {
void ReopenBuffer(Buffer *buffer) {
Scratch scratch;
if (buffer->is_dir) {
ResetBuffer(buffer);
for (FileIter it = IterateFiles(scratch, buffer->name); IsValid(it); Advance(&it)) {
RawAppendf(buffer, "%S\n", it.filename);
}
return;
}
#ifdef PLUGIN_DIRECTORY_NAVIGATION
if (buffer->is_dir) { InsertDirectoryNavigation(buffer); return; }
#endif
String string = ReadFile(scratch, buffer->name);
if (string.len == 0) {

View File

@@ -20,12 +20,13 @@ BSet GetConsoleSet() {
}
String GetDir(Buffer *buffer) {
#ifdef PLUGIN_DIRECTORY_NAVIGATION
if (buffer->is_dir) {
return buffer->name;
} else {
String result = ChopLastSlash(buffer->name);
return result;
}
#endif
String result = ChopLastSlash(buffer->name);
return result;
}
String GetMainDir() {
@@ -445,101 +446,6 @@ void CMD_GotoPrevInList() {
GotoNextInList(main.window, -1);
} RegisterCommand(CMD_GotoPrevInList, "alt-e", "For example: when jumping from build panel to build error, a jump point is setup, user can click this button to go over to the previous compiler error");
bool IsOpenBoundary(char c) {
bool result = c == 0 || IsParen(c) || IsBrace(c) || c == ':' || c == '\t' || c == '\n' || c == '"' || c == '\'';
return result;
}
#define ExpectP(x, ...) \
if (!(x)) { \
ReportErrorf("Failed to parse '" __FUNCTION__ "' command, " __VA_ARGS__); \
return; \
}
void Set(String string) {
String name = SkipIdent(&string);
ExpectP(name.len != 0, "expected a variable name, instead got '%S'", string);
Variable *var = NULL;
For (Variables) {
if (name == it.name) {
var = ⁢
break;
}
}
if (var) {
SkipWhitespace(&string);
if (var->type == VariableType_String) {
char c = At(string, 0);
ExpectP(c == u'"' || c == u'\'', "Expected string to follow the command name, instead got %S", string);
string = Skip(string, 1);
String quote = SkipUntil(&string, {&c, 1});
ExpectP(At(string, 0) == c, ":Set %S <error here>, unclosed quote", name);
ReportConsolef(":Set %S %c%S%c", name, c, quote, c);
*var->string = Intern(&GlobalInternTable, quote);
} else if (var->type == VariableType_Int) {
ExpectP(IsDigit(At(string, 0)), "Expected an integer to follow the command name, instead got: %S", string);
Int number = SkipInt(&string);
ReportConsolef(":Set %S %lld", name, number);
*var->i = number;
} else if (var->type == VariableType_Float) {
ExpectP(IsDigit(At(string, 0)), "Expected float to follow the command name, instead got: %S", string);
Float number = SkipFloat(&string);
ReportConsolef(":Set %S %f", name, number);
*var->f = number;
} else if (var->type == VariableType_Color) {
ExpectP(IsHexDigit(At(string, 0)), "Expected hex integer to follow the command name, instead got: %S", string);
String begin = {string.data, 0};
while (IsHexDigit(At(string, 0))) {
string = Skip(string, 1);
begin.len += 1;
}
ReportConsolef(":Set %S %S", name, begin);
var->color->value = (uint32_t)strtoll(begin.data, NULL, 16);
} ElseInvalidCodepath();
if (name == "FontSize" || name == "PathToFont") {
ReloadFont(PathToFont, (U32)FontSize);
}
#ifdef PLUGIN_PROJECT_MANAGEMENT
if (name == "ProjectDirectory") {
SetProjectDirectory(*var->string);
}
#endif
return;
}
Command *cmd = NULL;
For (GlobalCommands) {
if (it.name == name) {
cmd = &it;
break;
}
}
if (cmd) {
SkipWhitespace(&string);
char c = At(string, 0);
ExpectP(c == u'"' || c == u'\'', "Expected string to follow the command name, instead got %S", string);
string = Skip(string, 1);
String quote = SkipUntil(&string, {&c, 1});
ExpectP(At(string, 0) == c, "Failed to parse command, unclose quote");
quote = Intern(&GlobalInternTable, quote);
ReportConsolef(":Set %S %c%S%c", name, c, quote, c);
Trigger *trigger = ParseKeyCached(quote);
if (trigger) {
cmd->binding = quote;
cmd->trigger = trigger;
}
return;
}
ReportErrorf("Failed to :Set, no such variable found: %S", name);
}
ResolvedOpen ResolveOpen(Allocator alo, String path, ResolveOpenMeta meta) {
ResolvedOpen result = {};
path = Trim(path);
@@ -692,18 +598,12 @@ BSet Open(Window *window, String path, ResolveOpenMeta meta, bool set_active = t
if (set_active) {
NextActiveWindowID = set.window->id;
}
View *view = WindowOpenBufferView(set.window, o.path);
if (IsDir(o.path)) {
View *view = WindowOpenBufferView(set.window, o.path);
SetFuzzy(view);
Buffer *buffer = GetBuffer(view->active_buffer);
ResetBuffer(buffer);
RawAppendf(buffer, "\n");
for (FileIter it = IterateFiles(scratch, o.path); IsValid(it); Advance(&it)) {
RawAppendf(buffer, "%S\n", it.filename);
}
SelectRange(view, GetBufferBeginAsRange(buffer));
#ifdef PLUGIN_DIRECTORY_NAVIGATION
OpenDirectoryNavigation(view);
#endif
} else {
View *view = WindowOpenBufferView(set.window, o.path);
Buffer *buffer = GetBuffer(view->active_buffer);
if (o.line != -1) {
if (o.col == -1) o.col = 1;
@@ -1010,12 +910,6 @@ void CMD_Next() {
NextActiveWindowID = main.window->id;
} RegisterCommand(CMD_Next, "alt-shift-q | mousex2", "Go to next position, after backtracking, in the primary window");
void CMD_OpenUpFolder() {
BSet main = GetBSet(PrimaryWindowID);
String name = ChopLastSlash(main.buffer->name);
Open(name);
} RegisterCommand(CMD_OpenUpFolder, "ctrl-o", "Open current's file directory or up directory in other cases");
void CMD_MakeFontLarger() {
FontSize += 1;
ReloadFont(PathToFont, (U32)FontSize);
@@ -1144,9 +1038,14 @@ void Coro_ReplaceAll(mco_coro *co) {
}
ForItem (buffer, Buffers) {
if (buffer->special || buffer->is_dir || buffer->temp || buffer->dont_try_to_save_in_bulk_ops) {
if (buffer->special || buffer->temp || buffer->dont_try_to_save_in_bulk_ops) {
continue;
}
#ifdef PLUGIN_DIRECTORY_NAVIGATION
if (buffer->is_dir) {
continue;
}
#endif
View *view = OpenBufferView(buffer->name);
if (SelectAllOccurences(view, needle)) {

View File

@@ -351,3 +351,94 @@ BufferID LoadConfig(String config_path) {
}
return buffer->id;
}
#define ExpectP(x, ...) \
if (!(x)) { \
ReportErrorf("Failed to parse '" __FUNCTION__ "' command, " __VA_ARGS__); \
return; \
}
void Set(String string) {
String name = SkipIdent(&string);
ExpectP(name.len != 0, "expected a variable name, instead got '%S'", string);
Variable *var = NULL;
For (Variables) {
if (name == it.name) {
var = &it;
break;
}
}
if (var) {
SkipWhitespace(&string);
if (var->type == VariableType_String) {
char c = At(string, 0);
ExpectP(c == u'"' || c == u'\'', "Expected string to follow the command name, instead got %S", string);
string = Skip(string, 1);
String quote = SkipUntil(&string, {&c, 1});
ExpectP(At(string, 0) == c, ":Set %S <error here>, unclosed quote", name);
ReportConsolef(":Set %S %c%S%c", name, c, quote, c);
*var->string = Intern(&GlobalInternTable, quote);
} else if (var->type == VariableType_Int) {
ExpectP(IsDigit(At(string, 0)), "Expected an integer to follow the command name, instead got: %S", string);
Int number = SkipInt(&string);
ReportConsolef(":Set %S %lld", name, number);
*var->i = number;
} else if (var->type == VariableType_Float) {
ExpectP(IsDigit(At(string, 0)), "Expected float to follow the command name, instead got: %S", string);
Float number = SkipFloat(&string);
ReportConsolef(":Set %S %f", name, number);
*var->f = number;
} else if (var->type == VariableType_Color) {
ExpectP(IsHexDigit(At(string, 0)), "Expected hex integer to follow the command name, instead got: %S", string);
String begin = {string.data, 0};
while (IsHexDigit(At(string, 0))) {
string = Skip(string, 1);
begin.len += 1;
}
ReportConsolef(":Set %S %S", name, begin);
var->color->value = (uint32_t)strtoll(begin.data, NULL, 16);
} ElseInvalidCodepath();
if (name == "FontSize" || name == "PathToFont") {
ReloadFont(PathToFont, (U32)FontSize);
}
#ifdef PLUGIN_PROJECT_MANAGEMENT
if (name == "ProjectDirectory") {
SetProjectDirectory(*var->string);
}
#endif
return;
}
Command *cmd = NULL;
For (GlobalCommands) {
if (it.name == name) {
cmd = &it;
break;
}
}
if (cmd) {
SkipWhitespace(&string);
char c = At(string, 0);
ExpectP(c == u'"' || c == u'\'', "Expected string to follow the command name, instead got %S", string);
string = Skip(string, 1);
String quote = SkipUntil(&string, {&c, 1});
ExpectP(At(string, 0) == c, "Failed to parse command, unclose quote");
quote = Intern(&GlobalInternTable, quote);
ReportConsolef(":Set %S %c%S%c", name, c, quote, c);
Trigger *trigger = ParseKeyCached(quote);
if (trigger) {
cmd->binding = quote;
cmd->trigger = trigger;
}
return;
}
ReportErrorf("Failed to :Set, no such variable found: %S", name);
}

View File

@@ -35,7 +35,10 @@ void CMD_ShowDebugBufferList() {
NextActiveWindowID = command_bar.window->id;
ResetBuffer(command_bar.buffer);
For (Buffers) {
bool is_special = it->special || it->temp || it->is_dir || it->dont_try_to_save_in_bulk_ops;
bool is_special = it->special || it->temp || it->dont_try_to_save_in_bulk_ops;
#ifdef PLUGIN_DIRECTORY_NAVIGATION
is_special |= it->is_dir;
#endif
if (!is_special) {
continue;
}
@@ -56,7 +59,10 @@ void CMD_ShowBufferList() {
NextActiveWindowID = command_bar.window->id;
ResetBuffer(command_bar.buffer);
For (Buffers) {
bool is_special = it->special || it->temp || it->is_dir || it->dont_try_to_save_in_bulk_ops;
bool is_special = it->special || it->temp || it->dont_try_to_save_in_bulk_ops;
#ifdef PLUGIN_DIRECTORY_NAVIGATION
is_special |= it->is_dir;
#endif
if (is_special) {
continue;
}

View File

@@ -0,0 +1,156 @@
// @todo: On save rename files, delete files etc ...
// Instead of toying with Reopen maybe it should actually detect changes in directory etc. on update
void CMD_OpenUpFolder() {
BSet main = GetBSet(PrimaryWindowID);
String name = ChopLastSlash(main.buffer->name);
Open(name);
} RegisterCommand(CMD_OpenUpFolder, "ctrl-o", "Open current's file directory or up directory in other cases");
void InsertDirectoryNavigation(Buffer *buffer) {
Assert(buffer->is_dir);
Scratch scratch;
ResetBuffer(buffer);
RawAppendf(buffer, "\n");
for (FileIter it = IterateFiles(scratch, buffer->name); IsValid(it); Advance(&it)) {
RawAppendf(buffer, "%S\n", it.filename);
}
}
void OpenDirectoryNavigation(View *view) {
SetFuzzy(view);
Buffer *buffer = GetBuffer(view->active_buffer);
InsertDirectoryNavigation(buffer);
SelectRange(view, GetBufferBeginAsRange(buffer));
}
#if 0
typedef struct {
HANDLE dir;
OVERLAPPED ov;
HANDLE event;
BYTE buffer[4096];
BOOL pending;
} DirectoryWatcher;
BOOL InitDirectoryWatcher(DirectoryWatcher *w, const wchar_t *path)
{
ZeroMemory(w, sizeof(*w));
w->dir = CreateFileW(
path,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL
);
if (w->dir == INVALID_HANDLE_VALUE)
return FALSE;
w->event = CreateEventW(NULL, TRUE, FALSE, NULL);
if (!w->event)
return FALSE;
w->ov.hEvent = w->event;
w->pending = FALSE;
return TRUE;
}
BOOL WasDirectoryModified(DirectoryWatcher *w)
{
DWORD bytes;
if (!w->pending) {
ResetEvent(w->event);
w->pending = ReadDirectoryChangesW(
w->dir,
w->buffer,
sizeof(w->buffer),
TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL,
&w->ov,
NULL
);
if (!w->pending)
return FALSE;
}
if (WaitForSingleObject(w->event, 0) == WAIT_OBJECT_0) {
GetOverlappedResult(w->dir, &w->ov, &bytes, FALSE);
w->pending = FALSE;
return TRUE;
}
return FALSE;
}
void DestroyDirectoryWatcher(DirectoryWatcher *w)
{
if (w->dir != INVALID_HANDLE_VALUE)
CloseHandle(w->dir);
if (w->event)
CloseHandle(w->event);
}
//
DirectoryWatcher w;
InitDirectoryWatcher(&w, L"C:\\mydir");
if (WasDirectoryModified(&w)) {
// something changed
}
#if OS_LINUX
#include <sys/inotify.h>
#include <unistd.h>
#include <fcntl.h>
typedef struct {
int fd;
int wd;
} DirectoryWatcher;
int InitDirectoryWatcher(DirectoryWatcher *w, const char *path)
{
w->fd = inotify_init1(IN_NONBLOCK);
if (w->fd < 0)
return 0;
w->wd = inotify_add_watch(
w->fd,
path,
IN_CREATE | IN_DELETE | IN_MOVE | IN_MODIFY
);
return w->wd >= 0;
}
int WasDirectoryModified(DirectoryWatcher *w)
{
char buffer[4096];
ssize_t len = read(w->fd, buffer, sizeof(buffer));
if (len > 0)
return 1;
return 0; // no data = no changes
}
void DestroyDirectoryWatcher(DirectoryWatcher *w)
{
if (w->wd >= 0)
inotify_rm_watch(w->fd, w->wd);
if (w->fd >= 0)
close(w->fd);
}
#endif
#endif

View File

@@ -0,0 +1,3 @@
#define PLUGIN_DIRECTORY_NAVIGATION
void InsertDirectoryNavigation(struct Buffer *buffer);
void OpenDirectoryNavigation(struct View *view);

View File

@@ -13,9 +13,14 @@ void Coro_SearchOpenBuffers(mco_coro *co) {
ForItem (id, buffers) {
Buffer *it = GetBuffer(id, NULL);
if (it == NULL || it->special || it->is_dir || it->temp || it->dont_try_to_save_in_bulk_ops) {
if (it == NULL || it->special || it->temp || it->dont_try_to_save_in_bulk_ops) {
continue;
}
#ifdef PLUGIN_DIRECTORY_NAVIGATION
if (it->is_dir) {
continue;
}
#endif
{
Scratch scratch;

View File

@@ -14,6 +14,7 @@
#include "render/generated_font.cpp"
#include "render/font.cpp"
#include "render/opengl.cpp"
#include "plugin_directory_navigation.h"
#include "text_editor.h"
#include "plugin_command_window.h"
#include "plugin_search_window.h"
@@ -40,6 +41,7 @@
#include "test/tests.cpp"
#include "commands_clipboard.cpp"
#include "plugin_directory_navigation.cpp"
#include "plugin_search_open_buffers.cpp"
#include "plugin_project_management.cpp"
#include "plugin_basic_commands.cpp"

View File

@@ -63,7 +63,9 @@ struct Buffer {
uint32_t dont_try_to_save_in_bulk_ops : 1;
uint32_t close : 1;
uint32_t special : 1;
#ifdef PLUGIN_DIRECTORY_NAVIGATION
uint32_t is_dir : 1;
#endif
};
};