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() {
MA_Scratch scratch;
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 list2 = S8_Split(scratch, file2, "\n");
list = S8_ConcatLists(scratch, list, list2);
S8_List funcs = {};
for (S8_Node *it = list.first; it; it = it->next) {

View File

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

View File

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

View File

@@ -16,8 +16,7 @@ void ToggleFullscreen() {
}
void CheckpointBeforeGoto(Window *window, View *view) {
Buffer *buffer = GetBuffer(view->active_buffer);
Add(&window->goto_history, {buffer->id, view->carets[0]});
Add(&window->goto_history, {view->id, view->carets[0]});
window->goto_redo.len = 0;
}
@@ -27,9 +26,9 @@ void CheckpointBeforeGoto(Window *window) {
GotoCrumb GetCrumb(Array<GotoCrumb> *cr) {
for (; cr->len;) {
GotoCrumb c = Pop(cr);
Buffer *buffer = GetBufferStrict(c.buffer_id);
if (buffer) return c;
GotoCrumb c = Pop(cr);
View *view = FindView(c.view_id);
if (view) return c;
}
return {};
}
@@ -37,11 +36,11 @@ GotoCrumb GetCrumb(Array<GotoCrumb> *cr) {
void GotoBackward(Window *window) {
BSet set = GetBSet(window);
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);
Buffer *buffer = GetBuffer(c.buffer_id);
View *view = WindowOpenBufferView(window, buffer->name);
GotoCrumb c = GetCrumb(&window->goto_history);
window->active_view = c.view_id;
View *view = GetView(c.view_id);
view->carets[0] = c.caret;
UpdateScroll(window, true);
}
@@ -49,11 +48,11 @@ void GotoBackward(Window *window) {
void GotoForward(Window *window) {
BSet set = GetBSet(window);
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);
Buffer *buffer = GetBuffer(c.buffer_id);
View *view = WindowOpenBufferView(window, buffer->name);
window->active_view = c.view_id;
View *view = GetView(c.view_id);
view->carets[0] = c.caret;
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) {
Buffer *buffer = GetBuffer(view->active_buffer);
Array<Caret> caret_copy = Copy(GetSystemAllocator(), view->carets);
defer {
Dealloc(&view->carets);
view->carets = caret_copy;
};
void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string) {
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);
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??
@@ -589,7 +598,7 @@ void SaveBuffer(View *view) {
Scratch scratch;
String string = AllocCharString(scratch, buffer);
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;
@@ -834,3 +843,26 @@ void Command_FuzzySort(View *view, String16 needle) {
Command_Replace(view, GetString(temp_buffer));
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,
}
function ApplyRules(s)
function OnOpen(s)
for i = #Rules,1,-1 do
rule = Rules[i]
result = rule(s)
@@ -293,10 +293,6 @@ function ApplyRules(s)
return nil
end
function CFiles()
Cmd({working_dir = GetProjectDir(), kind ="a", cmd = "dir /s/b | findstr .*\\.c"})
end
Coroutines = {}
function AddCo(f)
local i = #Coroutines + 1

View File

@@ -72,11 +72,11 @@ BSet Command_Exec(String cmd, String working_dir) {
BSet Command_Open(String path) {
Scratch scratch;
lua_getglobal(LuaState, "ApplyRules");
lua_getglobal(LuaState, "OnOpen");
lua_pushlstring(LuaState, path.data, path.len);
if (lua_pcall(LuaState, 1, 1, 0) != 0) {
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);
return {};
}
@@ -94,9 +94,9 @@ BSet Command_Open(String path) {
if (IsDir(file_path)) {
Command_JumpNew(&main);
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)) {
Command_Appendf(main.view, "%.*s\n", FmtString(it.absolute_path));
Command_Appendf(main.view, "%.*s\n", FmtString(it.filename));
}
} else {
CheckpointBeforeGoto(main.window);
@@ -118,7 +118,7 @@ BSet Command_Open(String path) {
String working_dir = FieldString(LuaState, "working_dir");
Exec(NullViewID, true, cmd, working_dir);
} else {
ReportWarningf("Failed to match any of ApplyRules results!");
ReportWarningf("Failed to match any of OnOpen results!");
}
return main;
@@ -287,11 +287,6 @@ int Lua_ListCommands(lua_State *L) {
return 0;
}
int Lua_Reopen(lua_State *L) {
ReopenBuffer(GetActiveMainSet());
return 0;
}
int Lua_ToggleFullscreen(lua_State *L) {
ToggleFullscreen();
return 0;

View File

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

View File

@@ -179,6 +179,15 @@ View *CreateView(BufferID active_buffer) {
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) {
for (View *it = FirstView; it; it = it->next) {
if (it->active_buffer == buffer_id) {
@@ -354,20 +363,6 @@ String16 ToUnixString16(Allocator allocator, String 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,
// 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;
}
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) {
if (view == NullViewID) {
return true;
}
if (ViewIsCrumb(view)) {
return true;
}
for (Window *it = FirstWindow; it; it = it->next) {
if (it->active_view == view || it->active_goto_list == view) {
return true;
@@ -452,25 +459,12 @@ bool ViewIsReferenced(ViewID view) {
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) {
if (buffer_id == NullBufferID) {
return true;
}
View *view = FindView(buffer_id);
if (view) {
return true;
}
if (BufferIsCrumb(buffer_id)) {
if (FindView(buffer_id)) {
return true;
}
@@ -484,6 +478,7 @@ void GarbageCollect() {
int64_t new_file_mod_time = GetFileModTime(it->name);
if (it->file_mod_time != new_file_mod_time) {
it->changed_on_disk = true;
if (it->dirty == false) ReopenBuffer(it);
}
}
}
@@ -496,6 +491,10 @@ void GarbageCollect() {
continue;
}
// if (!ViewIsReferenced(it->id)) {
// int a = 10;
// }
bool ref = ViewIsReferenced(it->id);
if (ref) {
continue;
@@ -513,6 +512,10 @@ void GarbageCollect() {
continue;
}
// if (!BufferIsReferenced(it->id)) {
// int a = 10;
// }
bool ref = BufferIsReferenced(it->id);
if (ref) {
continue;

View File

@@ -18,8 +18,13 @@ void UpdateProcesses() {
String poll = PollStdout(scratch, &it);
View *view = GetView({it.view_id});
if (poll.len) Command_Append(view, poll, it.scroll_to_end);
if (!IsValid(&it)) remove_item = true;
if (poll.len) {
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"
#define MINICORO_IMPL
#include "external/minicoro.h"
#include "lua.hpp"
#include "backup_font.cpp"
SDL_Window *SDLWindow;
@@ -42,7 +43,6 @@ int FullScreenPositionX, FullScreenPositionY;
#include "commands_bindings.cpp"
#include "title_bar.cpp"
#include "lua.hpp"
#include "lua_api.cpp"
#include "lua_api_generated.cpp"
#include "generated.cpp"

View File

@@ -20,7 +20,7 @@ struct View {
};
struct GotoCrumb {
BufferID buffer_id;
ViewID view_id;
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, String string, bool scroll_to_end_if_cursor_on_last_line);
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(String16 string);
String Command_GetMainDir();
String Command_GetBaseDir();
void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string);
void Command_Copy(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);
View *CreateView(BufferID active_buffer);
void ReopenBuffer(Buffer *buffer);
inline bool operator==(WindowID a, WindowID 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 |
- Scroll the console properly
- commands for scrolling: center, cursor_at_bottom_of_screen, cursor_at_top
- automatically generating lua bindings for simple commands
--------------
buffer = make_buffer()
buffer.append(list_files("src/basic"))
activate_buffer
--------------
- 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)
- for now add ./ and .\ to valid line starters
- make search bar standout!!
- 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
- maybe most of the bindings should be in lua, but actual code in C
- LoadWord, EncloseWord configurable?
@@ -38,7 +42,7 @@ activate_buffer
backlog
- 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
- 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
- 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
@@ -51,3 +55,9 @@ backlog
- redo tree
- gap buffer
- optimize rendering - command buffer, and vertice buffer instead of vertice buffer with scissor
--------------
buffer = make_buffer()
buffer.append(list_files("src/basic"))
activate_buffer
--------------