Fix garbage collect bug, ReplaceWithoutMovingCarets takes buffer now, Reopen buffer happens automatically when not dirty

This commit is contained in:
Krzosa Karol
2025-05-08 22:42:04 +02:00
parent abb2cfb1c4
commit 46c109dce4
13 changed files with 133 additions and 90 deletions

View File

@@ -318,7 +318,10 @@ void GenerateConfig() {
void GenerateLuaApi() { void GenerateLuaApi() {
MA_Scratch scratch; MA_Scratch scratch;
S8_String file = OS_ReadFile(scratch, "../src/text_editor/lua_api.cpp"); S8_String file = OS_ReadFile(scratch, "../src/text_editor/lua_api.cpp");
S8_String file2 = OS_ReadFile(scratch, "../src/text_editor/commands.cpp");
S8_List list = S8_Split(scratch, file, "\n"); S8_List list = S8_Split(scratch, file, "\n");
S8_List list2 = S8_Split(scratch, file2, "\n");
list = S8_ConcatLists(scratch, list, list2);
S8_List funcs = {}; S8_List funcs = {};
for (S8_Node *it = list.first; it; it = it->next) { for (S8_Node *it = list.first; it; it = it->next) {

View File

@@ -210,7 +210,7 @@ Rules = {
MatchURL, MatchURL,
} }
function ApplyRules(s) function OnOpen(s)
for i = #Rules,1,-1 do for i = #Rules,1,-1 do
rule = Rules[i] rule = Rules[i]
result = rule(s) result = rule(s)
@@ -221,10 +221,6 @@ function ApplyRules(s)
return nil return nil
end end
function CFiles()
Cmd({working_dir = GetProjectDir(), kind ="a", cmd = "dir /s/b | findstr .*\\.c"})
end
Coroutines = {} Coroutines = {}
function AddCo(f) function AddCo(f)
local i = #Coroutines + 1 local i = #Coroutines + 1

View File

@@ -1070,12 +1070,12 @@ void AdjustCarets(Array<Edit> edits, Array<Caret> *carets) {
Int offset = insert_size - remove_size; Int offset = insert_size - remove_size;
for (Int i = 0; i < carets->len; i += 1) { for (Int i = 0; i < carets->len; i += 1) {
Caret &old_cursor = carets->data[i]; Caret &oldc = carets->data[i];
Caret &new_cursor = new_carets.data[i]; Caret &newc = new_carets.data[i];
if (old_cursor.range.min > edit.range.min) { if (oldc.range.min > edit.range.min) {
new_cursor.range.min += offset; newc.range.min += offset;
new_cursor.range.max += offset; newc.range.max += offset;
} }
} }
} }

View File

@@ -63,7 +63,9 @@ void AddEdit(Array<Edit> *e, Range range, String16 string);
// mouse_selection_anchor is special case for mouse handling ! // mouse_selection_anchor is special case for mouse handling !
void MergeCarets(Buffer *buffer, Array<Caret> *carets); void MergeCarets(Buffer *buffer, Array<Caret> *carets);
// was part of: Command_ReplaceWithoutMovingCarets, ReplaceTitleBarData // Adjusts caret copies after edit to make them not move after for example
// a bar modification. Sometimes works, sometimes doesn't, depends, not an all solving tool.
// For example in case of ReopenBuffer, when we select and replace entire buffer, it didn't quite work.
void AdjustCarets(Array<Edit> edits, Array<Caret> *carets); void AdjustCarets(Array<Edit> edits, Array<Caret> *carets);
void RedoEdit(Buffer *buffer, Array<Caret> *carets); void RedoEdit(Buffer *buffer, Array<Caret> *carets);

View File

@@ -16,8 +16,7 @@ void ToggleFullscreen() {
} }
void CheckpointBeforeGoto(Window *window, View *view) { void CheckpointBeforeGoto(Window *window, View *view) {
Buffer *buffer = GetBuffer(view->active_buffer); Add(&window->goto_history, {view->id, view->carets[0]});
Add(&window->goto_history, {buffer->id, view->carets[0]});
window->goto_redo.len = 0; window->goto_redo.len = 0;
} }
@@ -27,9 +26,9 @@ void CheckpointBeforeGoto(Window *window) {
GotoCrumb GetCrumb(Array<GotoCrumb> *cr) { GotoCrumb GetCrumb(Array<GotoCrumb> *cr) {
for (; cr->len;) { for (; cr->len;) {
GotoCrumb c = Pop(cr); GotoCrumb c = Pop(cr);
Buffer *buffer = GetBufferStrict(c.buffer_id); View *view = FindView(c.view_id);
if (buffer) return c; if (view) return c;
} }
return {}; return {};
} }
@@ -37,11 +36,11 @@ GotoCrumb GetCrumb(Array<GotoCrumb> *cr) {
void GotoBackward(Window *window) { void GotoBackward(Window *window) {
BSet set = GetBSet(window); BSet set = GetBSet(window);
if (window->goto_history.len <= 0) return; if (window->goto_history.len <= 0) return;
Add(&window->goto_redo, {set.buffer->id, set.view->carets[0]}); Add(&window->goto_redo, {set.view->id, set.view->carets[0]});
GotoCrumb c = GetCrumb(&window->goto_history); GotoCrumb c = GetCrumb(&window->goto_history);
Buffer *buffer = GetBuffer(c.buffer_id); window->active_view = c.view_id;
View *view = WindowOpenBufferView(window, buffer->name); View *view = GetView(c.view_id);
view->carets[0] = c.caret; view->carets[0] = c.caret;
UpdateScroll(window, true); UpdateScroll(window, true);
} }
@@ -49,11 +48,11 @@ void GotoBackward(Window *window) {
void GotoForward(Window *window) { void GotoForward(Window *window) {
BSet set = GetBSet(window); BSet set = GetBSet(window);
if (window->goto_redo.len <= 0) return; if (window->goto_redo.len <= 0) return;
Add(&window->goto_history, {set.buffer->id, set.view->carets[0]}); Add(&window->goto_history, {set.view->id, set.view->carets[0]});
GotoCrumb c = GetCrumb(&window->goto_redo); GotoCrumb c = GetCrumb(&window->goto_redo);
Buffer *buffer = GetBuffer(c.buffer_id); window->active_view = c.view_id;
View *view = WindowOpenBufferView(window, buffer->name); View *view = GetView(c.view_id);
view->carets[0] = c.caret; view->carets[0] = c.caret;
UpdateScroll(window, true); UpdateScroll(window, true);
} }
@@ -96,18 +95,28 @@ void MouseLoadWord(Event event, void (*cmd_function)(String16 string)) {
} }
} }
void Command_ReplaceWithoutMovingCarets(View *view, Range range, String16 string) { void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string) {
Buffer *buffer = GetBuffer(view->active_buffer);
Array<Caret> caret_copy = Copy(GetSystemAllocator(), view->carets);
defer {
Dealloc(&view->carets);
view->carets = caret_copy;
};
Scratch scratch; Scratch scratch;
View *view = NULL;
for (View *it = FirstView; it; it = it->next) {
if (it->active_buffer != buffer->id) {
continue;
}
view = it;
break;
}
if (!view) {
view = CreateView(buffer->id);
}
Array<Caret> carets = Copy(GetSystemAllocator(), view->carets);
Command_SelectRangeOneCursor(view, range); Command_SelectRangeOneCursor(view, range);
Array<Edit> edits = Command_ReplaceEx(scratch, view, string); Array<Edit> edits = Command_ReplaceEx(scratch, view, string);
AdjustCarets(edits, &caret_copy);
Dealloc(&view->carets);
view->carets = carets;
} }
// @todo: revamp interface since it scrolls ALL VIEWS??? or maybe not?? // @todo: revamp interface since it scrolls ALL VIEWS??? or maybe not??
@@ -589,7 +598,7 @@ void SaveBuffer(View *view) {
Scratch scratch; Scratch scratch;
String string = AllocCharString(scratch, buffer); String string = AllocCharString(scratch, buffer);
Buffer *temp_buffer = ExecAndWait(scratch, "clang-format", GetDir(buffer), string); Buffer *temp_buffer = ExecAndWait(scratch, "clang-format", GetDir(buffer), string);
Command_ReplaceWithoutMovingCarets(view, GetRange(buffer), {temp_buffer->str, temp_buffer->len}); ReplaceWithoutMovingCarets(buffer, GetRange(buffer), {temp_buffer->str, temp_buffer->len});
} }
Scratch scratch; Scratch scratch;
@@ -834,3 +843,26 @@ void Command_FuzzySort(View *view, String16 needle) {
Command_Replace(view, GetString(temp_buffer)); Command_Replace(view, GetString(temp_buffer));
Command_SelectRangeOneCursor(view, Rng(0)); Command_SelectRangeOneCursor(view, Rng(0));
} }
void ReopenBuffer(Buffer *buffer) {
Scratch scratch;
String string = ReadFile(scratch, buffer->name);
if (string.len == 0) {
return;
}
String16 string16 = ToUnixString16(scratch, string);
ReplaceWithoutMovingCarets(buffer, GetRange(buffer), string16);
buffer->file_mod_time = GetFileModTime(buffer->name);
buffer->changed_on_disk = false;
buffer->dirty = false;
}
void Command_Reopen() {
BSet set = GetActiveMainSet();
ReopenBuffer(set.buffer);
ActiveWindow = set.window->id;
}
int Lua_Reopen(lua_State *L) {
Command_Reopen();
return 0;
}

View File

@@ -282,7 +282,7 @@ Rules = {
MatchURL, MatchURL,
} }
function ApplyRules(s) function OnOpen(s)
for i = #Rules,1,-1 do for i = #Rules,1,-1 do
rule = Rules[i] rule = Rules[i]
result = rule(s) result = rule(s)
@@ -293,10 +293,6 @@ function ApplyRules(s)
return nil return nil
end end
function CFiles()
Cmd({working_dir = GetProjectDir(), kind ="a", cmd = "dir /s/b | findstr .*\\.c"})
end
Coroutines = {} Coroutines = {}
function AddCo(f) function AddCo(f)
local i = #Coroutines + 1 local i = #Coroutines + 1

View File

@@ -72,11 +72,11 @@ BSet Command_Exec(String cmd, String working_dir) {
BSet Command_Open(String path) { BSet Command_Open(String path) {
Scratch scratch; Scratch scratch;
lua_getglobal(LuaState, "ApplyRules"); lua_getglobal(LuaState, "OnOpen");
lua_pushlstring(LuaState, path.data, path.len); lua_pushlstring(LuaState, path.data, path.len);
if (lua_pcall(LuaState, 1, 1, 0) != 0) { if (lua_pcall(LuaState, 1, 1, 0) != 0) {
const char *error_message = lua_tostring(LuaState, -1); const char *error_message = lua_tostring(LuaState, -1);
ReportWarningf("Failed the call to ApplyRules! %s", error_message); ReportWarningf("Failed the call to OnOpen! %s", error_message);
lua_pop(LuaState, 1); lua_pop(LuaState, 1);
return {}; return {};
} }
@@ -94,9 +94,9 @@ BSet Command_Open(String path) {
if (IsDir(file_path)) { if (IsDir(file_path)) {
Command_JumpNew(&main); Command_JumpNew(&main);
main.buffer->name = GetUniqueBufferName(file_path, "temp"); main.buffer->name = GetUniqueBufferName(file_path, "temp");
Command_Appendf(main.view, "%.*s/..\n", FmtString(file_path)); Command_Appendf(main.view, "..\n", FmtString(file_path));
for (FileIter it = IterateFiles(scratch, file_path); IsValid(it); Advance(&it)) { for (FileIter it = IterateFiles(scratch, file_path); IsValid(it); Advance(&it)) {
Command_Appendf(main.view, "%.*s\n", FmtString(it.absolute_path)); Command_Appendf(main.view, "%.*s\n", FmtString(it.filename));
} }
} else { } else {
CheckpointBeforeGoto(main.window); CheckpointBeforeGoto(main.window);
@@ -118,7 +118,7 @@ BSet Command_Open(String path) {
String working_dir = FieldString(LuaState, "working_dir"); String working_dir = FieldString(LuaState, "working_dir");
Exec(NullViewID, true, cmd, working_dir); Exec(NullViewID, true, cmd, working_dir);
} else { } else {
ReportWarningf("Failed to match any of ApplyRules results!"); ReportWarningf("Failed to match any of OnOpen results!");
} }
return main; return main;
@@ -287,11 +287,6 @@ int Lua_ListCommands(lua_State *L) {
return 0; return 0;
} }
int Lua_Reopen(lua_State *L) {
ReopenBuffer(GetActiveMainSet());
return 0;
}
int Lua_ToggleFullscreen(lua_State *L) { int Lua_ToggleFullscreen(lua_State *L) {
ToggleFullscreen(); ToggleFullscreen();
return 0; return 0;

View File

@@ -10,7 +10,6 @@ luaL_Reg LuaFunctions[] = {
{"Open", Lua_Open}, {"Open", Lua_Open},
{"SetProjectFile", Lua_SetProjectFile}, {"SetProjectFile", Lua_SetProjectFile},
{"ListCommands", Lua_ListCommands}, {"ListCommands", Lua_ListCommands},
{"Reopen", Lua_Reopen},
{"ToggleFullscreen", Lua_ToggleFullscreen}, {"ToggleFullscreen", Lua_ToggleFullscreen},
{"ListBuffers", Lua_ListBuffers}, {"ListBuffers", Lua_ListBuffers},
{"GetBufferList", Lua_GetBufferList}, {"GetBufferList", Lua_GetBufferList},
@@ -27,5 +26,6 @@ luaL_Reg LuaFunctions[] = {
{"GetMainDir", Lua_GetMainDir}, {"GetMainDir", Lua_GetMainDir},
{"SplitSize", Lua_SplitSize}, {"SplitSize", Lua_SplitSize},
{"Play", Lua_Play}, {"Play", Lua_Play},
{"Reopen", Lua_Reopen},
{NULL, NULL}, {NULL, NULL},
}; };

View File

@@ -179,6 +179,15 @@ View *CreateView(BufferID active_buffer) {
return view; return view;
} }
View *FindView(ViewID view_id, View *default_view = NULL) {
for (View *it = FirstView; it; it = it->next) {
if (it->id == view_id) {
return it;
}
}
return default_view;
}
View *FindView(BufferID buffer_id, View *default_view = NULL) { View *FindView(BufferID buffer_id, View *default_view = NULL) {
for (View *it = FirstView; it; it = it->next) { for (View *it = FirstView; it; it = it->next) {
if (it->active_buffer == buffer_id) { if (it->active_buffer == buffer_id) {
@@ -354,20 +363,6 @@ String16 ToUnixString16(Allocator allocator, String string_) {
return string; return string;
} }
void ReopenBuffer(BSet set) {
Scratch scratch;
String string = ReadFile(scratch, set.buffer->name);
if (string.len == 0) {
return;
}
String16 string16 = ToUnixString16(scratch, string);
Command_ReplaceWithoutMovingCarets(set.view, GetRange(set.buffer), string16);
set.buffer->file_mod_time = GetFileModTime(set.buffer->name);
set.buffer->changed_on_disk = false;
ActiveWindow = set.window->id;
}
// This function as name suggests tries to open a buffer, // This function as name suggests tries to open a buffer,
// there is no name resolution here, path should already be resolved etc. // there is no name resolution here, path should already be resolved etc.
// //
@@ -439,11 +434,23 @@ View *WindowOpenBufferView(Window *new_parent_window, String name) {
return result; return result;
} }
bool ViewIsCrumb(ViewID view_id) {
for (Window *window = FirstWindow; window; window = window->next) {
For(window->goto_history) if (it.view_id == view_id) return true;
For(window->goto_redo) if (it.view_id == view_id) return true;
}
return false;
}
bool ViewIsReferenced(ViewID view) { bool ViewIsReferenced(ViewID view) {
if (view == NullViewID) { if (view == NullViewID) {
return true; return true;
} }
if (ViewIsCrumb(view)) {
return true;
}
for (Window *it = FirstWindow; it; it = it->next) { for (Window *it = FirstWindow; it; it = it->next) {
if (it->active_view == view || it->active_goto_list == view) { if (it->active_view == view || it->active_goto_list == view) {
return true; return true;
@@ -452,25 +459,12 @@ bool ViewIsReferenced(ViewID view) {
return false; return false;
} }
bool BufferIsCrumb(BufferID buffer_id) {
for (Window *window = FirstWindow; window; window = window->next) {
For(window->goto_history) if (it.buffer_id == buffer_id) return true;
For(window->goto_redo) if (it.buffer_id == buffer_id) return true;
}
return false;
}
bool BufferIsReferenced(BufferID buffer_id) { bool BufferIsReferenced(BufferID buffer_id) {
if (buffer_id == NullBufferID) { if (buffer_id == NullBufferID) {
return true; return true;
} }
View *view = FindView(buffer_id); if (FindView(buffer_id)) {
if (view) {
return true;
}
if (BufferIsCrumb(buffer_id)) {
return true; return true;
} }
@@ -484,6 +478,7 @@ void GarbageCollect() {
int64_t new_file_mod_time = GetFileModTime(it->name); int64_t new_file_mod_time = GetFileModTime(it->name);
if (it->file_mod_time != new_file_mod_time) { if (it->file_mod_time != new_file_mod_time) {
it->changed_on_disk = true; it->changed_on_disk = true;
if (it->dirty == false) ReopenBuffer(it);
} }
} }
} }
@@ -496,6 +491,10 @@ void GarbageCollect() {
continue; continue;
} }
// if (!ViewIsReferenced(it->id)) {
// int a = 10;
// }
bool ref = ViewIsReferenced(it->id); bool ref = ViewIsReferenced(it->id);
if (ref) { if (ref) {
continue; continue;
@@ -513,6 +512,10 @@ void GarbageCollect() {
continue; continue;
} }
// if (!BufferIsReferenced(it->id)) {
// int a = 10;
// }
bool ref = BufferIsReferenced(it->id); bool ref = BufferIsReferenced(it->id);
if (ref) { if (ref) {
continue; continue;

View File

@@ -18,8 +18,13 @@ void UpdateProcesses() {
String poll = PollStdout(scratch, &it); String poll = PollStdout(scratch, &it);
View *view = GetView({it.view_id}); View *view = GetView({it.view_id});
if (poll.len) Command_Append(view, poll, it.scroll_to_end); if (poll.len) {
if (!IsValid(&it)) remove_item = true; Command_Append(view, poll, it.scroll_to_end);
}
if (!IsValid(&it)) {
Command_Append(view, Format(scratch, "process exited with code: %d\n", it.exit_code), it.scroll_to_end);
remove_item = true;
}
} }
} }

View File

@@ -16,6 +16,7 @@
#include "external/stb_truetype.c" #include "external/stb_truetype.c"
#define MINICORO_IMPL #define MINICORO_IMPL
#include "external/minicoro.h" #include "external/minicoro.h"
#include "lua.hpp"
#include "backup_font.cpp" #include "backup_font.cpp"
SDL_Window *SDLWindow; SDL_Window *SDLWindow;
@@ -42,7 +43,6 @@ int FullScreenPositionX, FullScreenPositionY;
#include "commands_bindings.cpp" #include "commands_bindings.cpp"
#include "title_bar.cpp" #include "title_bar.cpp"
#include "lua.hpp"
#include "lua_api.cpp" #include "lua_api.cpp"
#include "lua_api_generated.cpp" #include "lua_api_generated.cpp"
#include "generated.cpp" #include "generated.cpp"

View File

@@ -20,7 +20,7 @@ struct View {
}; };
struct GotoCrumb { struct GotoCrumb {
BufferID buffer_id; ViewID view_id;
Caret caret; Caret caret;
}; };
@@ -121,12 +121,12 @@ void Command_SelectRangeOneCursor(View *view, Range range);
void Command_Append(View *view, String16 string, bool scroll_to_end_if_cursor_on_last_line); void Command_Append(View *view, String16 string, bool scroll_to_end_if_cursor_on_last_line);
void Command_Append(View *view, String string, bool scroll_to_end_if_cursor_on_last_line); void Command_Append(View *view, String string, bool scroll_to_end_if_cursor_on_last_line);
Array<Edit> Command_ReplaceEx(Allocator scratch, View *view, String16 string); Array<Edit> Command_ReplaceEx(Allocator scratch, View *view, String16 string);
void Command_ReplaceWithoutMovingCarets(View *view, Range range, String16 string);
void Command_Eval(String string); void Command_Eval(String string);
void Command_Eval(String16 string); void Command_Eval(String16 string);
String Command_GetMainDir(); String Command_GetMainDir();
String Command_GetBaseDir(); String Command_GetBaseDir();
void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string);
void Command_Copy(View *view); void Command_Copy(View *view);
void Command_Paste(View *view); void Command_Paste(View *view);
@@ -137,6 +137,7 @@ void Command_Appendf(View *view, const char *fmt, ...);
Buffer *CreateBuffer(Allocator allocator, String name, Int size = 4096); Buffer *CreateBuffer(Allocator allocator, String name, Int size = 4096);
View *CreateView(BufferID active_buffer); View *CreateView(BufferID active_buffer);
void ReopenBuffer(Buffer *buffer);
inline bool operator==(WindowID a, WindowID b) { return a.id == b.id; } inline bool operator==(WindowID a, WindowID b) { return a.id == b.id; }
inline bool operator==(BufferID a, BufferID b) { return a.id == b.id; } inline bool operator==(BufferID a, BufferID b) { return a.id == b.id; }

View File

@@ -2,14 +2,18 @@
- maybe we could allow user to change window titles which would make them special in some way. A:/text_editor/+test_buffer:1:1:ADE | - maybe we could allow user to change window titles which would make them special in some way. A:/text_editor/+test_buffer:1:1:ADE |
- Scroll the console properly - Scroll the console properly
- commands for scrolling: center, cursor_at_bottom_of_screen, cursor_at_top - commands for scrolling: center, cursor_at_bottom_of_screen, cursor_at_top
- automatically generating lua bindings for simple commands - organize commands and lua bindings somehow, it's kinda confusing right now, maybe group command->luacommand, the command name implies it also doubles as lua command?
- Add metadata to Lua bindings so that we would get a better listing (function args?, what else?)
-------------- - rules, interface for opening files using the F4 (aka next path from build output)
buffer = make_buffer() - for now add ./ and .\ to valid line starters
buffer.append(list_files("src/basic")) - make search bar standout!!
activate_buffer - relative paths instead of absolute when opening directories
-------------- - change console resize behaviour, maybe on tilde, not on click, it borks, selects wrongly because of the change in size
- Kill buffer command (it should be marked for deletion and deleted at the end of frame!)
- Delete directory/file on disk command
- Create directory command (probably should enter it automatically
- save all relevant buffers and build
- shift down on last line should move the cursor to end of line!!! same for up - shift down on last line should move the cursor to end of line!!! same for up
- maybe most of the bindings should be in lua, but actual code in C - maybe most of the bindings should be in lua, but actual code in C
- LoadWord, EncloseWord configurable? - LoadWord, EncloseWord configurable?
@@ -38,7 +42,7 @@ activate_buffer
backlog backlog
- expose a coroutine based scripting enviorment where user can execute shell commands wait for them and perform actions in very linear manner - expose a coroutine based scripting enviorment where user can execute shell commands wait for them and perform actions in very linear manner
- Test stdin writing code - Test stdin writing code
- Implement shell interaction (last line should have a '$'' symbols, if you press enter it should send that line to stdin of a running shell) - Implement shell interaction (the valid cmd lines should start with '>' or '$', user can add more lines like this to expand the command size maybe?, if we have a case where we have a line with '>' but the last line doesn't have (just a space) then it should execute?)
- drop text into window - drop text into window
- I think the way sublime text and we display line highlights is confusing with multiple cursors (line highlight can be confused with selection) - I think the way sublime text and we display line highlights is confusing with multiple cursors (line highlight can be confused with selection)
- text_editor --record events.txt text_editor --playback events.txt - text_editor --record events.txt text_editor --playback events.txt
@@ -51,3 +55,9 @@ backlog
- redo tree - redo tree
- gap buffer - gap buffer
- optimize rendering - command buffer, and vertice buffer instead of vertice buffer with scissor - optimize rendering - command buffer, and vertice buffer instead of vertice buffer with scissor
--------------
buffer = make_buffer()
buffer.append(list_files("src/basic"))
activate_buffer
--------------