diff --git a/build_file.cpp b/build_file.cpp index f4c0d1b..792a8d2 100644 --- a/build_file.cpp +++ b/build_file.cpp @@ -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"}); diff --git a/data/init.lua b/data/init.lua index 7ba3dd8..b6eedbe 100644 --- a/data/init.lua +++ b/data/init.lua @@ -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 \ No newline at end of file diff --git a/src/text_editor/commands.cpp b/src/text_editor/commands.cpp index 8528645..5797924 100644 --- a/src/text_editor/commands.cpp +++ b/src/text_editor/commands.cpp @@ -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 carets = Copy(GetSystemAllocator(), view->carets); + Scratch scratch; Command_SelectRangeOneCursor(view, range); Array 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 edits = BeginEdit(scratch, buffer, view->carets); MergeCarets(buffer, &view->carets); Array 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 edits = BeginEdit(scratch, buffer, view->carets); MergeCarets(buffer, &view->carets); Array 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!"); diff --git a/src/text_editor/commands_bindings.cpp b/src/text_editor/commands_bindings.cpp index b82ef87..86f20f9 100644 --- a/src/text_editor/commands_bindings.cpp +++ b/src/text_editor/commands_bindings.cpp @@ -661,7 +661,7 @@ void OnCommand(Event event) { if (CtrlPress(SDLK_S)) { - SaveBuffer(active.view); + SaveBuffer(active.buffer); } if (CtrlPress(SDLK_PERIOD)) { diff --git a/src/text_editor/generated.cpp b/src/text_editor/generated.cpp index b23bb1d..f6a2d52 100644 --- a/src/text_editor/generated.cpp +++ b/src/text_editor/generated.cpp @@ -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); diff --git a/src/text_editor/generated_variables.cpp b/src/text_editor/generated_variables.cpp index 474338f..db338c3 100644 --- a/src/text_editor/generated_variables.cpp +++ b/src/text_editor/generated_variables.cpp @@ -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"; diff --git a/src/text_editor/lua_api.cpp b/src/text_editor/lua_api.cpp index 46a3e49..9bef680 100644 --- a/src/text_editor/lua_api.cpp +++ b/src/text_editor/lua_api.cpp @@ -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() { diff --git a/src/text_editor/lua_api_generated.cpp b/src/text_editor/lua_api_generated.cpp index 5ebb08d..e5c5d8e 100644 --- a/src/text_editor/lua_api_generated.cpp +++ b/src/text_editor/lua_api_generated.cpp @@ -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}, diff --git a/src/text_editor/todo.txt b/src/text_editor/todo.txt index a4f0e3a..f1b23ed 100644 --- a/src/text_editor/todo.txt +++ b/src/text_editor/todo.txt @@ -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