Lua.OnSave, GetViewForFixingWhenBufferCommand

This commit is contained in:
Krzosa Karol
2025-05-09 20:25:05 +02:00
parent 1590f12b43
commit 8370a2ae67
9 changed files with 174 additions and 85 deletions

View File

@@ -260,8 +260,6 @@ void GenerateConfig() {
style.add({"DrawLineNumbers", "1"});
style.add({"DrawScrollbar", "1"});
style.add({"IndentSize", "4"});
style.add({"TrimWhitespaceOnSave", "1"});
style.add({"ClangFormatOnSave", "0"});
style.add({"FontSize", "15"});
style.add({"FontFilter", "0"}); // nearest = 0, linear = 1 - seems like nearest always better?
style.add({"Font", "C:/Windows/Fonts/consola.ttf"});

View File

@@ -1,3 +1,6 @@
Style.TrimWhitespaceOnSave = true
Style.ClangFormatOnSave = false
INTERNET_BROWSER = 'C:/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe'
SDLK_CTRL = 1073742048
@@ -284,4 +287,17 @@ function OnCommand(e)
end
function OnInit()
end
function OnSave(buffer_id)
local dont_trim_lines_with_caret = false
ConvertLineEndingsToLF(buffer_id, dont_trim_lines_with_caret)
if Style.TrimWhitespaceOnSave then
TrimTrailingWhitespace(buffer_id, dont_trim_lines_with_caret)
end
local name = GetBufferNameByID(buffer_id)
if Style.ClangFormatOnSave and (name:match(".c$") or name:match(".h$") or name:match(".cpp$") or name:match(".hpp$")) then
ApplyClangFormat(buffer_id)
end
end

View File

@@ -99,9 +99,21 @@ void MouseLoadWord(Event event, void (*cmd_function)(String16 string)) {
}
}
void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string) {
Scratch scratch;
View *GetViewForFixingWhenBufferCommand(Buffer *buffer, bool *is_active = NULL) {
View *view = NULL;
if (is_active) {
*is_active = false;
}
Window *active_window = GetWindow(ActiveWindow);
View *active_view = GetView(active_window->active_view);
if (active_view->active_buffer == buffer->id) {
if (is_active) {
*is_active = true;
}
return active_view;
}
for (View *it = FirstView; it; it = it->next) {
if (it->active_buffer != buffer->id) {
continue;
@@ -114,8 +126,14 @@ void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string) {
view = CreateView(buffer->id);
}
return view;
}
void ReplaceWithoutMovingCarets(Buffer *buffer, Range range, String16 string) {
View *view = GetViewForFixingWhenBufferCommand(buffer);
Array<Caret> carets = Copy(GetSystemAllocator(), view->carets);
Scratch scratch;
Command_SelectRangeOneCursor(view, range);
Array<Edit> edits = Command_ReplaceEx(scratch, view, string);
@@ -559,15 +577,22 @@ void Command_IndentSelectedLines(View *view, bool shift = false) {
view->update_scroll = false;
}
void Command_TrimTrailingWhitespace(View *view, bool dont_trim_lines_with_cursor) {
void TrimTrailingWhitespace(Buffer *buffer, bool trim_lines_with_caret = false) {
Scratch scratch;
Buffer *buffer = GetBuffer(view->active_buffer);
bool is_active_view = false;
View *view = GetViewForFixingWhenBufferCommand(buffer, &is_active_view);
if (!is_active_view && !trim_lines_with_caret) {
trim_lines_with_caret = true;
}
Array<Edit> edits = BeginEdit(scratch, buffer, view->carets);
MergeCarets(buffer, &view->carets);
Array<Range> lines_to_skip_triming = {};
if (dont_trim_lines_with_cursor) lines_to_skip_triming = GetSelectedLinesSorted(scratch, view);
if (!trim_lines_with_caret) {
lines_to_skip_triming = GetSelectedLinesSorted(scratch, view);
}
for (Int i = 0; i < buffer->line_starts.len; i += 1) {
Int range_index = FindRangeByPos(&lines_to_skip_triming, i);
@@ -588,16 +613,31 @@ void Command_TrimTrailingWhitespace(View *view, bool dont_trim_lines_with_cursor
EndEdit(buffer, &edits, &view->carets, !KILL_SELECTION);
view->update_scroll = false;
}
int Lua_TrimTrailingWhitespace(lua_State *L) {
lua_Integer buffer_id = luaL_checkinteger(L, 1);
int trim_lines_with_caret = lua_toboolean(L, 2);
lua_pop(L, 2);
Buffer *buffer = GetBuffer({buffer_id});
TrimTrailingWhitespace(buffer, trim_lines_with_caret);
return 0;
}
void Command_ConvertLineEndings(View *view, bool dont_trim_lines_with_cursor) {
void ConvertLineEndingsToLF(Buffer *buffer, bool trim_lines_with_caret = false) {
Scratch scratch;
Buffer *buffer = GetBuffer(view->active_buffer);
bool is_active_view = false;
View *view = GetViewForFixingWhenBufferCommand(buffer, &is_active_view);
if (!is_active_view && !trim_lines_with_caret) {
trim_lines_with_caret = true;
}
Array<Edit> edits = BeginEdit(scratch, buffer, view->carets);
MergeCarets(buffer, &view->carets);
Array<Range> lines_to_skip_triming = {};
if (dont_trim_lines_with_cursor) lines_to_skip_triming = GetSelectedLinesSorted(scratch, view);
if (!trim_lines_with_caret) {
lines_to_skip_triming = GetSelectedLinesSorted(scratch, view);
}
for (Int i = 0; i < buffer->line_starts.len; i += 1) {
Int range_index = FindRangeByPos(&lines_to_skip_triming, i);
@@ -613,27 +653,39 @@ void Command_ConvertLineEndings(View *view, bool dont_trim_lines_with_cursor) {
EndEdit(buffer, &edits, &view->carets, !KILL_SELECTION);
view->update_scroll = false;
}
bool IsCFile(String name) {
bool result = EndsWith(name, ".cpp") || EndsWith(name, ".c") || EndsWith(name, ".hpp") || EndsWith(name, ".h");
return result;
int Lua_ConvertLineEndingsToLF(lua_State *L) {
lua_Integer buffer_id = luaL_checkinteger(L, 1);
int trim_lines_with_caret = lua_toboolean(L, 2);
lua_pop(L, 2);
Buffer *buffer = GetBuffer({buffer_id});
ConvertLineEndingsToLF(buffer, trim_lines_with_caret);
return 0;
}
void SaveBuffer(View *view) {
Buffer *buffer = GetBuffer(view->active_buffer);
bool dont_trim_lines_with_cursor = true;
Command_ConvertLineEndings(view, dont_trim_lines_with_cursor);
if (StyleTrimWhitespaceOnSave) {
Command_TrimTrailingWhitespace(view, dont_trim_lines_with_cursor);
Assert(view->active_buffer == buffer->id);
}
void ApplyClangFormat(Buffer *buffer) {
Scratch scratch;
String string = AllocCharString(scratch, buffer);
Buffer *temp_buffer = ExecAndWait(scratch, "clang-format", GetDir(buffer), string);
ReplaceWithoutMovingCarets(buffer, GetRange(buffer), {temp_buffer->str, temp_buffer->len});
}
int Lua_ApplyClangFormat(lua_State *L) {
lua_Integer buffer_id = luaL_checkinteger(L, 1);
lua_pop(L, 1);
Buffer *buffer = GetBuffer({buffer_id});
ApplyClangFormat(buffer);
return 0;
}
if (StyleClangFormatOnSave && IsCFile(buffer->name)) {
Scratch scratch;
String string = AllocCharString(scratch, buffer);
Buffer *temp_buffer = ExecAndWait(scratch, "clang-format", GetDir(buffer), string);
ReplaceWithoutMovingCarets(buffer, GetRange(buffer), {temp_buffer->str, temp_buffer->len});
}
int Lua_GetBufferNameByID(lua_State *L) {
lua_Integer buffer_id = luaL_checkinteger(L, 1);
lua_pop(L, 1);
Buffer *buffer = GetBuffer({buffer_id});
lua_pushlstring(L, buffer->name.data, buffer->name.len);
return 1;
}
void SaveBuffer(Buffer *buffer) {
CallOnSave(buffer->id);
Scratch scratch;
String string = AllocCharString(scratch, buffer);
@@ -647,6 +699,14 @@ void SaveBuffer(View *view) {
ReportWarningf("Failed to save file with name: %.*s", FmtString(buffer->name));
}
}
void Command_Save() {
BSet set = GetActiveMainSet();
SaveBuffer(set.buffer);
}
int Lua_Save(lua_State *L) {
Command_Save();
return 0;
}
void Command_KillSelectedLines(View *view) {
Scratch scratch;
@@ -956,8 +1016,7 @@ BSet Command_Open(Window *window, String path, String meta, bool set_active = tr
Scratch scratch;
BSet set = GetBSet(window);
OnOpenResult ores = CallOnOpen(path, meta);
String kind = FieldString(LuaState, "kind");
if (kind == "text") {
if (ores.kind == "text") {
if (set_active) {
ActiveWindow = set.window->id;
}
@@ -978,16 +1037,16 @@ BSet Command_Open(Window *window, String path, String meta, bool set_active = tr
}
}
UpdateScroll(set.window, true);
} else if (kind == "exec") {
} else if (ores.kind == "exec") {
if (set_active) {
ActiveWindow = set.window->id;
}
JumpGarbageBuffer(&set);
Exec(set.view->id, true, ores.cmd, ores.working_dir);
} else if (kind == "exec_console") {
} else if (ores.kind == "exec_console") {
// this shouldn't change the focus/window/view
Exec(NullViewID, true, ores.cmd, ores.working_dir);
} else if (kind == "skip") {
} else if (ores.kind == "skip") {
return {};
} else {
ReportWarningf("Failed to match any of OnOpen results!");

View File

@@ -661,7 +661,7 @@ void OnCommand(Event event) {
if (CtrlPress(SDLK_S)) {
SaveBuffer(active.view);
SaveBuffer(active.buffer);
}
if (CtrlPress(SDLK_PERIOD)) {

View File

@@ -64,12 +64,13 @@ Style.WaitForEvents = 1
Style.DrawLineNumbers = 1
Style.DrawScrollbar = 1
Style.IndentSize = 4
Style.TrimWhitespaceOnSave = 1
Style.ClangFormatOnSave = 0
Style.FontSize = 15
Style.FontFilter = 0
Style.Font = "C:/Windows/Fonts/consola.ttf"
Style.VCVarsall = "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat"
Style.TrimWhitespaceOnSave = true
Style.ClangFormatOnSave = false
INTERNET_BROWSER = 'C:/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe'
SDLK_CTRL = 1073742048
@@ -357,6 +358,19 @@ end
function OnInit()
end
function OnSave(buffer_id)
local dont_trim_lines_with_caret = false
ConvertLineEndingsToLF(buffer_id, dont_trim_lines_with_caret)
if Style.TrimWhitespaceOnSave then
TrimTrailingWhitespace(buffer_id, dont_trim_lines_with_caret)
end
local name = GetBufferNameByID(buffer_id)
if Style.ClangFormatOnSave and (name:match(".c$") or name:match(".h$") or name:match(".cpp$") or name:match(".hpp$")) then
ApplyClangFormat(buffer_id)
end
end
)==";
void ReloadStyle() {
ColorText = GetColor("Text", ColorText);
@@ -385,8 +399,6 @@ void ReloadStyle() {
StyleDrawLineNumbers = GetStyleInt("DrawLineNumbers", StyleDrawLineNumbers);
StyleDrawScrollbar = GetStyleInt("DrawScrollbar", StyleDrawScrollbar);
StyleIndentSize = GetStyleInt("IndentSize", StyleIndentSize);
StyleTrimWhitespaceOnSave = GetStyleInt("TrimWhitespaceOnSave", StyleTrimWhitespaceOnSave);
StyleClangFormatOnSave = GetStyleInt("ClangFormatOnSave", StyleClangFormatOnSave);
StyleFontSize = GetStyleInt("FontSize", StyleFontSize);
StyleFontFilter = GetStyleInt("FontFilter", StyleFontFilter);
StyleFont = GetStyleString("Font", StyleFont);

View File

@@ -61,8 +61,6 @@ Int StyleWaitForEvents = 1;
Int StyleDrawLineNumbers = 1;
Int StyleDrawScrollbar = 1;
Int StyleIndentSize = 4;
Int StyleTrimWhitespaceOnSave = 1;
Int StyleClangFormatOnSave = 0;
Int StyleFontSize = 15;
Int StyleFontFilter = 0;
String StyleFont = "C:/Windows/Fonts/consola.ttf";

View File

@@ -1,16 +1,3 @@
String FieldString(lua_State *L, String name) {
String result = {};
if (lua_istable(L, -1)) {
lua_pushlstring(L, name.data, name.len);
lua_gettable(L, -2);
defer { lua_pop(L, 1); };
if (lua_isstring(L, -1)) {
result = lua_tostring(L, -1);
}
}
return result;
}
int Lua_Print(lua_State *L) {
Scratch scratch;
int nargs = lua_gettop(L);
@@ -197,6 +184,19 @@ Color GetColor(String name, Color default_color) {
return result;
}
String GetFieldString(lua_State *L, String name) {
String result = {};
if (lua_istable(L, -1)) {
lua_pushlstring(L, name.data, name.len);
lua_gettable(L, -2);
defer { lua_pop(L, 1); };
if (lua_isstring(L, -1)) {
result = lua_tostring(L, -1);
}
}
return result;
}
Int GetInt(lua_State *L, const char *name) {
lua_getfield(L, -1, name);
lua_Integer num = lua_tointeger(L, -1);
@@ -300,7 +300,18 @@ void ReloadLuaConfigs() {
}
}
bool CallLuaFunc(char *func_name, int arg_count, int ret_count) {
if (lua_pcall(LuaState, arg_count, ret_count, 0) != 0) {
const char *error_message = lua_tostring(LuaState, -1);
ReportWarningf("Failed to call a lua function: %s! %s", func_name, error_message);
lua_pop(LuaState, 1);
return false;
}
return true;
}
struct OnOpenResult {
String kind;
String file_path;
Int line, col;
String working_dir;
@@ -311,18 +322,16 @@ OnOpenResult CallOnOpen(String path, String meta) {
lua_getglobal(LuaState, "OnOpen");
lua_pushlstring(LuaState, path.data, path.len);
lua_pushlstring(LuaState, meta.data, meta.len);
if (lua_pcall(LuaState, 2, 1, 0) != 0) {
const char *error_message = lua_tostring(LuaState, -1);
ReportWarningf("Failed the call to OnOpen! %s", error_message);
lua_pop(LuaState, 1);
if (!CallLuaFunc("OnOpen", 2, 1)) {
return {};
}
String file_path = FieldString(LuaState, "file_path");
String line_string = FieldString(LuaState, "line");
String col_string = FieldString(LuaState, "col");
String cmd = FieldString(LuaState, "cmd");
String working_dir = FieldString(LuaState, "working_dir");
String file_path = GetFieldString(LuaState, "file_path");
String line_string = GetFieldString(LuaState, "line");
String col_string = GetFieldString(LuaState, "col");
String cmd = GetFieldString(LuaState, "cmd");
String working_dir = GetFieldString(LuaState, "working_dir");
String kind = GetFieldString(LuaState, "kind");
OnOpenResult result = {};
result.cmd = cmd;
@@ -330,38 +339,32 @@ OnOpenResult CallOnOpen(String path, String meta) {
result.file_path = file_path;
if (col_string.len) result.col = strtoll(col_string.data, NULL, 10);
if (line_string.len) result.line = strtoll(line_string.data, NULL, 10);
result.kind = kind;
return result;
}
void CallOnSave(BufferID buffer_id) {
lua_getglobal(LuaState, "OnSave");
lua_pushinteger(LuaState, buffer_id.id);
CallLuaFunc("OnSave", 1, 0);
}
void CallOnCommand(Event *event) {
lua_getglobal(LuaState, "OnCommand");
PushEvent(LuaState, event);
if (lua_pcall(LuaState, 1, 0, 0) != 0) {
const char *error_message = lua_tostring(LuaState, -1);
ReportWarningf("Failed the call to OnCommand! %s", error_message);
lua_pop(LuaState, 1);
return;
}
CallLuaFunc("OnCommand", 1, 0);
}
void CallLuaOnUpdate(Event *event) {
lua_getglobal(LuaState, "OnUpdate");
PushEvent(LuaState, event);
if (lua_pcall(LuaState, 1, 0, 0) != 0) {
const char *error_message = lua_tostring(LuaState, -1);
ReportWarningf("Failed the call to OnUpdate! %s", error_message);
lua_pop(LuaState, 1);
return;
}
CallLuaFunc("OnUpdate", 1, 0);
}
void CallLuaOnInit() {
if (luaL_dostring(LuaState, "OnInit()") != LUA_OK) {
const char *error_message = lua_tostring(LuaState, -1);
ReportErrorf("Error in OnInit! %s", error_message);
lua_pop(LuaState, 1);
}
lua_getglobal(LuaState, "OnInit");
CallLuaFunc("OnInit", 0, 0);
}
void InitLuaConfig() {

View File

@@ -14,6 +14,11 @@ luaL_Reg LuaFunctions[] = {
{"GetMainDir", Lua_GetMainDir},
{"SplitSize", Lua_SplitSize},
{"Play", Lua_Play},
{"TrimTrailingWhitespace", Lua_TrimTrailingWhitespace},
{"ConvertLineEndingsToLF", Lua_ConvertLineEndingsToLF},
{"ApplyClangFormat", Lua_ApplyClangFormat},
{"GetBufferNameByID", Lua_GetBufferNameByID},
{"Save", Lua_Save},
{"Reopen", Lua_Reopen},
{"ToggleFullscreen", Lua_ToggleFullscreen},
{"GetCFiles", Lua_GetCFiles},

View File

@@ -1,17 +1,16 @@
- Changing window properties by changing the window name?
- Scroll the console properly
- commands for scrolling: center, cursor_at_bottom_of_screen, cursor_at_top
- 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
- 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
- Convert more commands to taking buffer instead of view
- Check. Convert more commands to taking buffer instead of view
- Check. Rewrite more commands to use already implemented commands?
- Command that will select a lua (block|function|line)? and eval
- 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?
- dump text editor state to file, restore state
@@ -45,7 +44,6 @@ backlog
- text_editor --record events.txt text_editor --playback events.txt
- make the editor replayable, store events and then replay, be careful about globals
- maybe open should return multiple options if there are many more? (like in sublime if many symbols you get a window and you choose and it automatically jumps you to the symbol in the background)
- we could rewrite kill lines with simpler commands - extend selection to encompass lines->replace
- I want a way to assign flags to buffers/views/windows from user perspective so that console window concept can be created from user space
- font cache and on demand unicode loads
- color parens, braces