From 2b4a43ddc1480e29dfe6d88e904e7861071ee978 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Wed, 14 Aug 2024 07:35:12 +0200 Subject: [PATCH] Reopen changed files --- src/basic/filesystem.h | 15 ++++---- src/basic/win32.cpp | 15 ++++++++ src/text_editor/commands.cpp | 3 +- src/text_editor/lua_api.cpp | 6 ++++ src/text_editor/lua_api_generated.cpp | 1 + src/text_editor/management.cpp | 51 ++++++++++++++++++++++----- src/text_editor/text_editor.h | 2 ++ src/text_editor/title_bar.cpp | 3 +- src/text_editor/todo.txt | 6 ++-- 9 files changed, 80 insertions(+), 22 deletions(-) diff --git a/src/basic/filesystem.h b/src/basic/filesystem.h index 859431e..7aa0dbf 100644 --- a/src/basic/filesystem.h +++ b/src/basic/filesystem.h @@ -26,13 +26,14 @@ void Advance(FileIter *it); FileIter IterateFiles(Allocator allocator, String path); void InitOS(void (*error_proc)(const char *, ...)); -String GetExePath(Allocator allocator); -String GetExeDir(Allocator allocator); -bool FileExists(String path); -bool IsDir(String path); -bool IsFile(String path); -String GetWorkingDir(Allocator arena); -bool IsAbsolute(String path); +String GetExePath(Allocator allocator); +String GetExeDir(Allocator allocator); +bool FileExists(String path); +bool IsDir(String path); +bool IsFile(String path); +String GetWorkingDir(Allocator arena); +bool IsAbsolute(String path); +int64_t GetFileModTime(String file); struct Process { bool is_valid; diff --git a/src/basic/win32.cpp b/src/basic/win32.cpp index 74d6480..77c7bdb 100644 --- a/src/basic/win32.cpp +++ b/src/basic/win32.cpp @@ -262,6 +262,21 @@ bool DeleteFile(String path) { return result; } +int64_t GetFileModTime(String file) { + Scratch scratch; + String16 string16 = ToString16(scratch, file); + WIN32_FIND_DATAW data; + HANDLE handle = FindFirstFileW(string16.data, &data); + if (handle != INVALID_HANDLE_VALUE) { + FindClose(handle); + FILETIME time = data.ftLastWriteTime; + int64_t result = (int64_t)time.dwHighDateTime << 32 | time.dwLowDateTime; + return result; + } else { + return -1; + } +} + struct Win32Process { HANDLE handle; HANDLE child_stdout_read; diff --git a/src/text_editor/commands.cpp b/src/text_editor/commands.cpp index 01f00c9..7aaeba2 100644 --- a/src/text_editor/commands.cpp +++ b/src/text_editor/commands.cpp @@ -667,7 +667,8 @@ void SaveBuffer(View *view) { bool success = WriteFile(buffer->name, string); if (success) { - buffer->dirty = false; + buffer->file_mod_time = GetFileModTime(buffer->name); + buffer->dirty = false; } else { ReportWarningf("Failed to save file with name: %.*s", FmtString(buffer->name)); } diff --git a/src/text_editor/lua_api.cpp b/src/text_editor/lua_api.cpp index 8151d33..36cc5e8 100644 --- a/src/text_editor/lua_api.cpp +++ b/src/text_editor/lua_api.cpp @@ -116,6 +116,12 @@ int Lua_Open(lua_State *L) { return 0; } +int Lua_Reopen(lua_State *L) { + BSet main = GetActiveMainSet(); + ReopenBuffer(main.buffer); + return 0; +} + int Lua_Print(lua_State *L) { Scratch scratch; String string = lua_tostring(L, 1); diff --git a/src/text_editor/lua_api_generated.cpp b/src/text_editor/lua_api_generated.cpp index 03d2521..587c111 100644 --- a/src/text_editor/lua_api_generated.cpp +++ b/src/text_editor/lua_api_generated.cpp @@ -4,6 +4,7 @@ luaL_Reg LuaFunctions[] = { {"C", Lua_C}, {"Kill", Lua_Kill}, {"Open", Lua_Open}, + {"Reopen", Lua_Reopen}, {"Print", Lua_Print}, {"ListBuffers", Lua_ListBuffers}, {"GetBufferList", Lua_GetBufferList}, diff --git a/src/text_editor/management.cpp b/src/text_editor/management.cpp index d9d68ac..b4c4afb 100644 --- a/src/text_editor/management.cpp +++ b/src/text_editor/management.cpp @@ -309,15 +309,34 @@ Buffer *BufferOpenFile(String path) { IKnowWhatImDoing_Appendf(buffer, "%.*s\n", FmtString(it.filename)); } } else { - String string = ReadFile(scratch, path); - buffer = CreateBuffer(sys_allocator, path, string.len * 4 + 4096); - buffer->len = ConvertUTF8ToUTF16UnixLine(string, buffer->str, buffer->cap); + String string = ReadFile(scratch, path); + buffer = CreateBuffer(sys_allocator, path, string.len * 4 + 4096); + buffer->len = ConvertUTF8ToUTF16UnixLine(string, buffer->str, buffer->cap); + buffer->file_mod_time = GetFileModTime(path); UpdateLines(buffer, {}, String16{(wchar_t *)buffer->data, buffer->len}); } return buffer; } +void ResetBuffer(Buffer *buffer) { + Assert(buffer->edit_phase == 0); + buffer->line_starts.len = 1; + DeallocHistoryEntries(&buffer->redo_stack); + DeallocHistoryEntries(&buffer->undo_stack); +} + +void ReopenBuffer(Buffer *buffer) { + Scratch scratch; + String string = ReadFile(scratch, buffer->name); + if (string.len) { + ResetBuffer(buffer); + buffer->len = ConvertUTF8ToUTF16UnixLine(string, buffer->str, buffer->cap); + buffer->file_mod_time = GetFileModTime(buffer->name); + UpdateLines(buffer, {}, String16{(wchar_t *)buffer->data, buffer->len}); + } +} + View *OpenBufferView(String name) { Buffer *buffer = BufferOpenFile(name); View *view = CreateView(buffer->id); @@ -364,6 +383,15 @@ View *BufferIsReferenced(BufferID buffer_id) { return NULL; } +Window *BufferIsInWindow(BufferID buffer_id) { + For(Windows) { + Window *window = it.o; + View *view = GetView(window->active_view); + if (view->active_buffer == buffer_id) return window; + } + return NULL; +} + Window *BufferIsCrumb(BufferID buffer_id) { For(Windows) { Window *window = it.o; @@ -378,7 +406,11 @@ void GarbageCollect() { IterRemovePrepare(Views); View *view = it.o; Buffer *buffer = GetBuffer(view->active_buffer); - bool gc = buffer->gc || buffer->is_directory; + bool gc = buffer->gc; + if (buffer->is_directory) { + Window *ref = BufferIsCrumb(buffer->id); + if (!ref) gc = true; + } Window *ref = ViewIsReferenced(view->id); if (gc && !ref) { @@ -394,10 +426,6 @@ void GarbageCollect() { IterRemovePrepare(Buffers); Buffer *buffer = it.o; bool gc = buffer->gc; - if (buffer->is_directory) { - Window *ref = BufferIsCrumb(buffer->id); - if (!ref) gc = true; - } View *ref = BufferIsReferenced(buffer->id); if (gc && !ref) { @@ -409,6 +437,11 @@ void GarbageCollect() { // @todo: add buffer to free list Command_Appendf(GCInfoView, "buffer %.*s", FmtString(buffer->name)); + } else if (buffer->file_mod_time) { + int64_t new_file_mod_time = GetFileModTime(buffer->name); + if (buffer->file_mod_time != new_file_mod_time) { + buffer->changed_on_disk = true; + } } } -} \ No newline at end of file +} diff --git a/src/text_editor/text_editor.h b/src/text_editor/text_editor.h index e4f21de..4b94802 100644 --- a/src/text_editor/text_editor.h +++ b/src/text_editor/text_editor.h @@ -25,6 +25,7 @@ struct Buffer { BufferID id; String name; Int change_id; + Int file_mod_time; union { U16 *data; @@ -42,6 +43,7 @@ struct Buffer { int dirty : 1; int is_directory : 1; int gc : 1; + int changed_on_disk : 1; }; }; diff --git a/src/text_editor/title_bar.cpp b/src/text_editor/title_bar.cpp index d7ad511..28be423 100644 --- a/src/text_editor/title_bar.cpp +++ b/src/text_editor/title_bar.cpp @@ -45,7 +45,8 @@ void ReplaceTitleBarData(Window *window) { AdjustCarets(edits, &caret_copy); } - String s = Format(scratch, "%.*s:%lld:%lld", FmtString(main.buffer->name), (long long)xy.line + 1ll, (long long)xy.col + 1ll); + char *reopen = main.buffer->changed_on_disk ? " #Reopen()" : ""; + String s = Format(scratch, "%.*s:%lld:%lld%s", FmtString(main.buffer->name), (long long)xy.line + 1ll, (long long)xy.col + 1ll, reopen); String16 string = ToString16(scratch, s); String16 string_to_replace = GetString(title.buffer, replace_range); if (string_to_replace != string) { diff --git a/src/text_editor/todo.txt b/src/text_editor/todo.txt index cb98d0f..fdcef75 100644 --- a/src/text_editor/todo.txt +++ b/src/text_editor/todo.txt @@ -1,7 +1,5 @@ - garbage collect the buffers, views - store them in free list on destroy - adding items to directory should create files on save - it should ask the user (syntax: dir/ | file) -- refresh the directory - when opening up again? -- refresh the file content, periodically check file timestamp for changes and update if neccessary - ask user if he really wants to quit even though he has an unsaved buffer - popup window - add plumb rules for some web stuff - test the code editor: try writing in it, try browsing in it, create test tooling @@ -12,8 +10,8 @@ - group history entries so the you can rollback through multiple ones at once and not waste time on skipping whitespace trimming or deleting every character - Check if file exists in ApplyRules otherwise return null -- Different buffers should be GCed differently -- Special buffers that store metadata +- Add the event buffer with serialized events +- Gotos, jumping between views should preserve cursors - OnWindowCommand allow config user to overwrite the WindowCommand keybinding, introduce his own