diff --git a/src/test/basic_env/basic_env.cpp b/src/test/basic_env/basic_env.cpp new file mode 100644 index 0000000..ee518a7 --- /dev/null +++ b/src/test/basic_env/basic_env.cpp @@ -0,0 +1,367 @@ +#include "src/build_tool/library.cpp" + +enum { + PROFILE_DEBUG, + PROFILE_RELEASE, +}; +int Profile = PROFILE_DEBUG; +S8_String Compiler = "cl.exe"; + +void AddCommonFlags(Array *cmd) { + if (Compiler == "cl.exe") { + cmd->add("/MP"); + } + cmd->add("/Zi /FC /nologo"); + cmd->add("/WX /W3 /wd4200 /diagnostics:column"); + if (Compiler == "clang-cl.exe") { + cmd->add("-fdiagnostics-absolute-paths"); + cmd->add("-Wno-missing-braces"); + cmd->add("-Wno-writable-strings"); + cmd->add("-Wno-unused-function"); + } + cmd->add("/Oi"); + cmd->add("-D_CRT_SECURE_NO_WARNINGS"); + if (Profile == PROFILE_DEBUG) { + cmd->add("-DDEBUG_BUILD=1"); + cmd->add("-DRELEASE_BUILD=0"); + // cmd->add("-fsanitize=address"); + // cmd->add("-DUSE_ADDRESS_SANITIZER"); + // cmd->add("/MDd"); + } else { + cmd->add("-DDEBUG_BUILD=0"); + cmd->add("-DRELEASE_BUILD=1"); + cmd->add("/O2 /MT /DNDEBUG /GL"); + } +} + +struct Library { + Array sources; + Array objects; + Array include_paths; + Array defines; + Array link; +}; + +Library PrepareSDL() { + Library l = {}; + l.include_paths.add("../src/external/SDL/include"); + l.objects.add("../src/external/SDL/VisualC/static_x64/Release/SDL3.lib"); + l.link.add("/SUBSYSTEM:WINDOWS"); + l.link.add("opengl32.lib"); + l.link.add("imm32.lib"); + l.link.add("gdi32.lib"); + l.link.add("winmm.lib"); + l.link.add("shell32.lib"); + l.link.add("user32.lib"); + l.link.add("advapi32.lib"); + l.link.add("Setupapi.lib"); + l.link.add("ole32.lib"); + l.link.add("oleaut32.lib"); + l.link.add("Version.lib"); + return l; +} + +Library PrepareSDLDynamic() { + Library l = {}; + l.include_paths.add("../src/external/SDL/include"); + l.objects.add("../src/external/SDL/VisualC/x64/Release/SDL3.lib"); + l.link.add("/SUBSYSTEM:WINDOWS"); + OS_CopyFile("../src/external/SDL/VisualC/x64/Release/SDL3.dll", "./SDL3.dll", false); + return l; +} + +Library PrepareRaylib() { + Library l = {}; + l.include_paths.add("../src/external/raylib/include"); + if (0) { + l.objects.add("../src/external/raylib/lib/raylib.lib"); + } else { + l.objects.add("../src/external/raylib/lib/raylibdll.lib"); + OS_CopyFile("../src/external/raylib/lib/raylib.dll", "./raylib.dll", false); + } + return l; +} + +Library PrepareLua() { + Library l = {}; + l.include_paths.add("../src/external/lua/src"); + + MA_Scratch scratch; + for (OS_FileIter it = OS_IterateFiles(scratch, "../src/external/lua/src"); OS_IsValid(it); OS_Advance(&it)) { + if (it.filename == "luac.c") continue; + if (it.filename == "lua.c") continue; + if (S8_EndsWith(it.filename, ".c", true)) { + l.sources.add(it.absolute_path); + S8_String file = S8_ChopLastPeriod(it.filename); + l.objects.add(Fmt("%.*s.obj", S8_Expand(file))); + } + } + + if (!OS_FileExists(l.objects[0])) { + Array cmd = {}; + cmd.add(Compiler); + cmd.add("-c"); + AddCommonFlags(&cmd); + For(l.include_paths) cmd.add(S8_Format(Perm, "-I %.*s ", S8_Expand(it))); + cmd += l.sources; + Run(cmd); + } + return l; +} + +Library PrepareGlad() { + Library l = {}; + l.sources.add("../src/external/glad/glad.c"); + l.include_paths.add("../src/external/glad/"); + l.objects.add("glad.obj"); + if (!OS_FileExists(l.objects[0])) { + Array cmd = {}; + cmd.add(Compiler); + cmd.add("-c"); + AddCommonFlags(&cmd); + For(l.include_paths) cmd.add(S8_Format(Perm, "-I %.*s ", S8_Expand(it))); + cmd += l.sources; + Run(cmd); + } + return l; +} + +int CompileTextEditor() { + int result = 0; + + Array libs = {}; + // if (Profile == PROFILE_DEBUG) libs.add(PrepareSDLDynamic()); + // else + libs.add(PrepareSDL()); + libs.add(PrepareLua()); + libs.add(PrepareGlad()); + + Array cmd = {}; + cmd.add(Compiler); + cmd.add("-Fe:te.exe"); + cmd.add("-Fd:te.pdb"); + cmd.add("-I ../src"); + AddCommonFlags(&cmd); + For2(lib, libs) For(lib.defines) cmd.add(it); + + cmd.add("../src/text_editor/text_editor.cpp"); + cmd.add("../src/basic/win32.cpp"); + + For2(lib, libs) For(lib.include_paths) cmd.add(Fmt("-I %.*s", S8_Expand(it))); + + cmd.add("/link"); + cmd.add("/incremental:no"); + // cmd.add("/SUBSYSTEM:WINDOWS"); + // cmd.add("opengl32.lib gdi32.lib winmm.lib shell32.lib user32.lib msvcrt.lib /NODEFAULTLIB:LIBCMT"); + For2(lib, libs) For(lib.link) cmd.add(it); + For(libs) For2(o, it.objects) cmd.add(o); + cmd.add("icon.res"); + + OS_DeleteFile("te.pdb"); + // For(cmd) IO_Printf("%.*s\n", S8_Expand(it)); + if (!OS_FileExists("icon.res")) { + OS_CopyFile("../data/icon.ico", "icon.ico", true); + OS_CopyFile("../data/icon.rc", "icon.rc", true); + Run("rc icon.rc"); + } + result += Run(cmd); + if (result == 0) { + OS_MakeDir("../package"); + OS_CopyFile("../build/te.exe", "../package/te.exe", true); + OS_CopyFile("../build/te.pdb", "../package/te.pdb", true); + } + + return result; +} + +char *C(const char *value) { + if (value[0] == '0' && value[1] == 'x') { + S8_String f = Fmt("{0x%.*s, 0x%.*s, 0x%.*s, 0x%.*s}", 2, value + 2, 2, value + 4, 2, value + 6, 2, value + 8); + return f.str; + } else { + return (char *)value; + } +} + +void GenerateConfig() { + struct Var { + const char *name; + const char *value; + }; + + Array gruvbox = {}; + gruvbox.add({"GruvboxDark0Hard", "0x1d2021ff"}); + gruvbox.add({"GruvboxDark0", "0x282828ff"}); + gruvbox.add({"GruvboxDark0Soft", "0x32302fff"}); + gruvbox.add({"GruvboxDark1", "0x3c3836ff"}); + gruvbox.add({"GruvboxDark2", "0x504945ff"}); + gruvbox.add({"GruvboxDark3", "0x665c54ff"}); + gruvbox.add({"GruvboxDark4", "0x7c6f64ff"}); + gruvbox.add({"GruvboxGray245", "0x928374ff"}); + gruvbox.add({"GruvboxGray244", "0x928374ff"}); + gruvbox.add({"GruvboxLight0Hard", "0xf9f5d7ff"}); + gruvbox.add({"GruvboxLight0", "0xfbf1c7ff"}); + gruvbox.add({"GruvboxLight0Soft", "0xf2e5bcff"}); + gruvbox.add({"GruvboxLight1", "0xebdbb2ff"}); + gruvbox.add({"GruvboxLight2", "0xd5c4a1ff"}); + gruvbox.add({"GruvboxLight3", "0xbdae93ff"}); + gruvbox.add({"GruvboxLight4", "0xa89984ff"}); + gruvbox.add({"GruvboxBrightRed", "0xfb4934ff"}); + gruvbox.add({"GruvboxBrightGreen", "0xb8bb26ff"}); + gruvbox.add({"GruvboxBrightYellow", "0xfabd2fff"}); + gruvbox.add({"GruvboxBrightBlue", "0x83a598ff"}); + gruvbox.add({"GruvboxBrightPurple", "0xd3869bff"}); + gruvbox.add({"GruvboxBrightAqua", "0x8ec07cff"}); + gruvbox.add({"GruvboxBrightOrange", "0xfe8019ff"}); + gruvbox.add({"GruvboxNeutralRed", "0xcc241dff"}); + gruvbox.add({"GruvboxNeutralGreen", "0x98971aff"}); + gruvbox.add({"GruvboxNeutralYellow", "0xd79921ff"}); + gruvbox.add({"GruvboxNeutralBlue", "0x458588ff"}); + gruvbox.add({"GruvboxNeutralPurple", "0xb16286ff"}); + gruvbox.add({"GruvboxNeutralAqua", "0x689d6aff"}); + gruvbox.add({"GruvboxNeutralOrange", "0xd65d0eff"}); + gruvbox.add({"GruvboxFadedRed", "0x9d0006ff"}); + gruvbox.add({"GruvboxFadedGreen", "0x79740eff"}); + gruvbox.add({"GruvboxFadedYellow", "0xb57614ff"}); + gruvbox.add({"GruvboxFadedBlue", "0x076678ff"}); + gruvbox.add({"GruvboxFadedPurple", "0x8f3f71ff"}); + gruvbox.add({"GruvboxFadedAqua", "0x427b58ff"}); + gruvbox.add({"GruvboxFadedOrange", "0xaf3a03ff"}); + + Array colors = {}; + colors.add({"Text", "GruvboxDark0Hard"}); + colors.add({"LoadTextHighlight", "0x0000000F"}); + colors.add({"Background", "GruvboxLight0Hard"}); + colors.add({"InactiveWindow", "0x0000000F"}); + colors.add({"TextLineNumbers", "GruvboxDark4"}); + colors.add({"LineHighlight", "GruvboxLight0Soft"}); + colors.add({"MainCaret", "GruvboxDark0Hard"}); + colors.add({"SubCaret", "GruvboxGray245"}); + colors.add({"Selection", "GruvboxLight1"}); + colors.add({"WhitespaceDuringSelection", "GruvboxLight4"}); + colors.add({"MouseUnderline", "GruvboxDark0Hard"}); + colors.add({"CaretUnderline", "GruvboxGray245"}); + + colors.add({"FuzzySearchLineHighlight", "GruvboxDark0"}); + + colors.add({"ScrollbarBackground", "GruvboxLight2"}); + colors.add({"ScrollbarScroller", "GruvboxLight1"}); + colors.add({"ScrollbarScrollerSelected", "GruvboxLight0Hard"}); + + colors.add({"TitleBarText", "GruvboxDark2"}); + colors.add({"TitleBarBackground", "GruvboxLight1"}); + colors.add({"TitleBarActiveBackground", "0xfefefefe"}); + colors.add({"TitleBarSelection", "GruvboxLight3"}); + + colors.add({"ResizerBackground", "GruvboxLight0Hard"}); + colors.add({"ResizerOutline", "GruvboxLight3"}); + + Array style = {}; + style.add({"WaitForEvents", "1"}); + style.add({"DrawLineNumbers", "1"}); + style.add({"DrawScrollbar", "1"}); + style.add({"IndentSize", "4"}); + 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"}); + style.add({"VCVarsall", "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat"}); + + { + MA_Scratch scratch; + Array sb = {MA_GetAllocator(scratch)}; + { + For(gruvbox) sb.add(Fmt("Color %s = %s;", it.name, C(it.value))); + For(colors) sb.add(Fmt("Color Color%s = %s;", it.name, C(it.value))); + For(style) { + if (CHAR_IsDigit(it.value[0])) { + sb.add(Fmt("Int Style%s = %s;", it.name, it.value)); + } else { + sb.add(Fmt("String Style%s = \"%s\";", it.name, it.value)); + } + } + } + S8_String string = Merge(scratch, sb, "\n"); + OS_WriteFile("../src/text_editor/generated_variables.cpp", string); + sb = {}; + + sb.add("String BaseLuaConfig = R\"==("); + { + For(gruvbox) sb.add(Fmt("local %s = %s", it.name, it.value)); + + sb.add("Color = {}"); + For(colors) sb.add(Fmt("Color.%s = %s", it.name, it.value)); + + sb.add("Style = {}"); + For(style) { + if (CHAR_IsDigit(it.value[0])) sb.add(Fmt("Style.%s = %s", it.name, it.value)); + else sb.add(Fmt("Style.%s = \"%s\"", it.name, it.value)); + } + + S8_String init_file = OS_ReadFile(scratch, "../data/init.lua"); + sb.add(init_file); + } + sb.add(")==\";"); + + sb.add("void ReloadStyle() {"); + { + For(colors) sb.add(Fmt(" Color%s = GetColor(\"%s\", Color%s);", it.name, it.name, it.name)); + For(style) { + if (CHAR_IsDigit(it.value[0])) sb.add(Fmt(" Style%s = GetStyleInt(\"%s\", Style%s);", it.name, it.name, it.name)); + else sb.add(Fmt(" Style%s = GetStyleString(\"%s\", Style%s);", it.name, it.name, it.name)); + } + } + sb.add("}"); + + string = Merge(scratch, sb, "\n"); + OS_WriteFile("../src/text_editor/generated.cpp", string); + } +} + +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) { + S8_String s = S8_Trim(it->string); + if (S8_StartsWith(s, "int Lua_")) { + s = S8_Skip(s, 4); + S8_Seek(s, "(", 0, &s.len); + S8_Add(scratch, &funcs, s); + } + } + + S8_List sb = {}; + S8_AddF(scratch, &sb, "luaL_Reg LuaFunctions[] = {\n"); + for (S8_Node *it = funcs.first; it; it = it->next) { + S8_String lua_name = S8_Skip(it->string, 4); + S8_AddF(scratch, &sb, " {\"%.*s\", %.*s},\n", S8_Expand(lua_name), S8_Expand(it->string)); + } + S8_AddF(scratch, &sb, " {NULL, NULL},\n"); + S8_AddF(scratch, &sb, "};\n"); + + S8_String output = S8_Merge(scratch, sb); + OS_WriteFile("../src/text_editor/lua_api_generated.cpp", output); +} + +int main() { + MA_InitScratch(); + SRC_InitCache(Perm, "te.cache"); + + GenerateLuaApi(); + GenerateConfig(); + int result = CompileTextEditor(); + // int result = CompileNewPlatform(); + + if (result != 0) { + OS_DeleteFile("te.cache"); + return result; + } + + SRC_SaveCache(); + return 0; +} \ No newline at end of file diff --git a/src/test/tests.cpp b/src/test/tests.cpp new file mode 100644 index 0000000..3f33932 --- /dev/null +++ b/src/test/tests.cpp @@ -0,0 +1,118 @@ +String TestDir; + +void AddCtrlPress(SDL_Keycode key) { + Event event = {}; + event.key = key; + event.ctrl = 1; + event.xwindow = 1280; + event.ywindow = 720; + Add(&EventPlayback, event); +} + +void AddText(String string) { + Event event = {}; + event.kind = EVENT_TEXT_INPUT; + event.xwindow = 1280; + event.ywindow = 720; + event.text = Copy(Perm, string).data; + Add(&EventPlayback, event); +} + +void Wait(mco_coro *co) { + Add(&EventPlayback, {111}); + for (Event *event = Yield(co); event->kind != 111; event = Yield(co)) { + } +} + +void PlayTestOpen(mco_coro *co) { + // Open file, move a little, then open again and verify the caret didn't move + String basic_env_cpp = Format(Perm, "%.*s/basic_env/basic_env.cpp", FmtString(TestDir)); + + AddCtrlPress(SDLK_P); + Add(&EventPlayback, {EVENT_KEY_PRESS, SDLK_UP, 1280, 720}); + AddCtrlPress(SDLK_Q); + Wait(co); + + { + BSet main = GetActiveMainSet(); + Assert(main.buffer->name == basic_env_cpp); + Assert(main.view->carets[0].range.min == 0); + Assert(main.view->carets[0].range.max == 0); + Int line = PosToLine(main.buffer, main.view->carets[0].range.min); + Assert(line == 0); + } + + AddCtrlPress(SDLK_DOWN); + AddCtrlPress(SDLK_DOWN); + AddCtrlPress(SDLK_DOWN); + Wait(co); + + Range range = {}; + { + BSet main = GetActiveMainSet(); + Assert(main.view->carets[0].range.min > 0); + Assert(main.view->carets[0].range.max > 0); + range = main.view->carets[0].range; + } + + AddCtrlPress(SDLK_P); + Add(&EventPlayback, {EVENT_KEY_PRESS, SDLK_UP, 1280, 720}); + AddCtrlPress(SDLK_Q); + Wait(co); + + Int buffer_len = 0; + { + BSet main = GetActiveMainSet(); + Assert(main.buffer->name == basic_env_cpp); + Assert(main.view->carets[0].range.min == range.min); + Assert(main.view->carets[0].range.max == range.max); + buffer_len = main.buffer->len; + } + + AddText(Format(Perm, "%.*s:20", FmtString(basic_env_cpp))); + AddCtrlPress(SDLK_Q); + Wait(co); + + { + BSet main = GetActiveMainSet(); + Int pos = main.view->carets[0].range.min; + Int line = PosToLine(main.buffer, pos); + Assert(line == 19); + Assert(main.buffer->name == basic_env_cpp); + Assert(main.buffer->len != buffer_len); + Assert(main.buffer->dirty); + } + + AddCtrlPress(SDLK_Z); + AddCtrlPress(SDLK_PAGEUP); + Wait(co); + + { + BSet main = GetActiveMainSet(); + Assert(main.buffer->len == buffer_len); + Assert(main.view->carets[0].range.min == 0); + Assert(main.view->carets[0].range.max == 0); + } + ReportConsolef(__FUNCTION__ " DONE"); + +} + +void Test(mco_coro *co) { + WorkDir = Format(Perm, "%.*s/basic_env", FmtString(TestDir)); + PlayTestOpen(co); + + + +// Add(&EventPlayback, {EVENT_QUIT}); +} + +void InitTests() { + StyleWaitForEvents = false; + { + String file = __FILE__; + file = NormalizePath(Perm, file); + file = ChopLastSlash(file); + TestDir = file; + } + AddCo(Test); +} diff --git a/src/text_editor/prototype.cpp b/src/text_editor/prototype.cpp deleted file mode 100644 index 09fa163..0000000 --- a/src/text_editor/prototype.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// struct String16Builder { -// Arena *arena; -// size_t align; -// size_t start_len; -// }; - -// String16Builder BeginString16(Arena *arena) { -// String16Builder result = {arena, arena->align, arena->len}; -// arena->align = 0; -// return result; -// } - -// char16_t *Add(String16Builder *sb, String16 string) { -// char16_t *r = AllocArray(*sb->arena, char16_t, string.len); -// MemoryCopy(r, string.data, string.len * sizeof(char16_t)); -// return r; -// } - -// String16 EndString16(String16Builder *sb) { -// sb->arena->align = sb->align; -// String16 result = {(char16_t *)(sb->arena->data + sb->start_len), (Int)((sb->arena->len - sb->start_len) / sizeof(char16_t))}; -// return result; -// } - -// String16Builder &operator<<(String16Builder &sb, String16 string) { -// Add(&sb, string); -// return sb; -// } - -// String16Builder &operator<<(String16Builder &sb, Int num) { -// Scratch scratch(sb.arena); -// String16 ss = Format16(scratch, u"%lld", (long long)num); -// Add(&sb, ss); -// return sb; -// } - -// String16Builder &operator<<(String16Builder &sb, int num) { -// return sb << (Int)num; -// } - -// String16Builder &operator<<(String16Builder &sb, double num) { -// Scratch scratch(sb.arena); -// String16 ss = Format16(scratch, u"%f", num); -// Add(&sb, ss); -// return sb; -// } - -// void Prototype() { -// Scratch scratch; -// String16Builder sb = BeginString16(scratch); -// sb << 32 << 32.0 << u"\n"; -// String16 result = EndString16(&sb); - -// int a = 10; -// } \ No newline at end of file diff --git a/src/text_editor/test.cpp b/src/text_editor/test.cpp deleted file mode 100644 index ddc811e..0000000 --- a/src/text_editor/test.cpp +++ /dev/null @@ -1,19 +0,0 @@ -void Test(mco_coro *co) { - Add(&EventPlayback, {EVENT_KEY_PRESS, SDLK_DOWN, 1280, 720}); - Add(&EventPlayback, {EVENT_KEY_PRESS, SDLK_DOWN, 1280, 720}); - Add(&EventPlayback, {EVENT_KEY_PRESS, SDLK_DOWN, 1280, 720}); - Add(&EventPlayback, {EVENT_KEY_PRESS, SDLK_DOWN, 1280, 720}); - Add(&EventPlayback, {EVENT_KEY_PRESS, SDLK_DOWN, 1280, 720}); - Add(&EventPlayback, {EVENT_KEY_PRESS, SDLK_DOWN, 1280, 720}); - Add(&EventPlayback, {EVENT_KEY_PRESS, SDLK_DOWN, 1280, 720}); - Add(&EventPlayback, {EVENT_KEY_PRESS, SDLK_Q, 1280, 720}); - GetLast(EventPlayback)->ctrl = 1; - Add(&EventPlayback, {111}); - - for (Event *event = Yield(co); event->kind != 111; event = Yield(co)) { - } - - BSet main = GetActiveMainSet(); - Assert(main.buffer->name == "C:/Work/text_editor/src/text_editor/buffer_history.cpp"); - Add(&EventPlayback, {EVENT_QUIT}); -} \ No newline at end of file diff --git a/src/text_editor/text_editor.cpp b/src/text_editor/text_editor.cpp index 6a32c3f..5469f7e 100644 --- a/src/text_editor/text_editor.cpp +++ b/src/text_editor/text_editor.cpp @@ -1,6 +1,5 @@ #include - #define BASIC_IMPL #include "basic/basic.h" #include "basic/filesystem.h" @@ -50,10 +49,8 @@ int FullScreenPositionX, FullScreenPositionY; #include "generated.cpp" #include "window_draw.cpp" - #include "coroutines.cpp" -#include "test.cpp" -#include "prototype.cpp" +#include "test/tests.cpp" void FillEventWithBasicData(Event *event) { SDL_Keymod mod = SDL_GetModState(); @@ -323,7 +320,6 @@ int main(int argc, char **argv) BeginProfiler(); InitScratch(); InitArena(&Perm); - // Prototype(); WorkDir = GetWorkingDir(Perm); { @@ -420,10 +416,7 @@ int main(int argc, char **argv) } InitLuaConfig(); - - // :Tests - // StyleWaitForEvents = false; - // AddCo(Test); + InitTests(); #if _WIN32 AddCo(Windows_SetupVCVarsall); #endif diff --git a/src/text_editor/text_editor.h b/src/text_editor/text_editor.h index 22ca174..724b0aa 100644 --- a/src/text_editor/text_editor.h +++ b/src/text_editor/text_editor.h @@ -2,7 +2,6 @@ struct Window; struct View; struct WindowSplit; struct ViewID { Int id; View *o; }; struct WindowID { Int id; Window *o; }; - struct View { ViewID id; BufferID active_buffer; diff --git a/src/text_editor/todo.txt b/src/text_editor/todo.txt index a7e6d67..07b9a40 100644 --- a/src/text_editor/todo.txt +++ b/src/text_editor/todo.txt @@ -9,10 +9,6 @@ - Lua namespaces for different kinds of commands (by ids, using main_set, using active_set)? - Some decl/function indexing in fuzzy format -- testing - - maybe setup folders, a folder with project, without project, stuff like that - - text_editor --record events.txt text_editor --playback events.txt - - Fix jump scroll, the query ends up the last line on screen, kind of wacky - LoadWord, EncloseWord configurable?