Compare commits

..

11 Commits

Author SHA1 Message Date
Krzosa Karol
20d71722ea GC temp buffers after they are 25th in history 2026-03-22 10:12:36 +01:00
Krzosa Karol
cf383c9772 Update todos 2026-03-22 10:12:18 +01:00
Krzosa Karol
82dceaca68 Missing include 2026-03-22 10:12:07 +01:00
Krzosa Karol
4f4940bcd3 Add memory usage in debug window 2026-03-22 10:11:25 +01:00
Krzosa Karol
aff3403404 Add macros 2026-03-21 17:09:40 +01:00
Krzosa Karol
b04b566681 Box mouse select 2026-03-21 16:57:19 +01:00
Krzosa Karol
aa85d9420a Fix tests 2026-03-21 16:57:07 +01:00
Krzosa Karol
4c21026842 CMD_OpenFileSystemBrowser 2026-03-21 16:56:36 +01:00
Krzosa Karol
cab882de60 Tests as part of build process and unifying with vscode keybinding scheme 2026-03-21 13:01:58 +01:00
Krzosa Karol
fadf4cd698 Move to globals SDL vars 2026-03-21 10:22:31 +01:00
Krzosa Karol
94ee03800d Basic tests and trying to fix address sanitizer errors 2026-03-21 10:10:24 +01:00
18 changed files with 293 additions and 111 deletions

View File

@@ -5,6 +5,7 @@ if [ ! -v release ]; then debug=1; fi
if [ -v debug ]; then echo "[debug build]"; fi if [ -v debug ]; then echo "[debug build]"; fi
if [ -v release ]; then echo "[release build]"; fi if [ -v release ]; then echo "[release build]"; fi
if [ -v slow ]; then echo "[slow build]"; fi if [ -v slow ]; then echo "[slow build]"; fi
if [ -v addr ]; then echo "[address sanitizer build]"; fi
mkdir -p build mkdir -p build
@@ -13,7 +14,8 @@ if [ ! -e "src/external/SDL" ]; then
git clone https://github.com/libsdl-org/SDL.git git clone https://github.com/libsdl-org/SDL.git
cd SDL cd SDL
git checkout release-3.2.30 git checkout release-3.2.30
# cmake -S . -B build_linux -DCMAKE_BUILD_TYPE=Release -DSDL_PIPEWIRE=OFF # We need older version of SDL3 because there is a bug on wayland that
# doubles click events and it's kind of unusable
cmake -S . -B build_linux -DSDL_PIPEWIRE=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr cmake -S . -B build_linux -DSDL_PIPEWIRE=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
cd build_linux cd build_linux
sudo make -j16 install sudo make -j16 install
@@ -27,8 +29,10 @@ flags="-Wall -Wextra -Werror -Wformat=2 -Wundef -Wshadow -Wno-missing-field-init
-g -fdiagnostics-absolute-paths \ -g -fdiagnostics-absolute-paths \
-nostdlib++ -fno-exceptions" -nostdlib++ -fno-exceptions"
if [ -v debug ]; then flags="$flags -fsanitize=address,undefined -fno-omit-frame-pointer -DDEBUG_BUILD=1"; fi if [ -v debug ]; then flags="$flags -fsanitize=undefined -fno-omit-frame-pointer -DDEBUG_BUILD=1"; fi
if [ -v release ]; then flags="$flags -DDEBUG_BUILD=0 -O2"; fi if [ -v release ]; then flags="$flags -DDEBUG_BUILD=0 -O2"; fi
if [ -v slow ]; then flags="$flags -DSLOW_BUILD=1"; fi if [ -v slow ]; then flags="$flags -DSLOW_BUILD=1"; fi
if [ -v addr ]; then flags="$flags -fsanitize=address"; fi
time clang -o te $flags ../src/text_editor.cpp $I -lSDL3 -lm -lbacktrace time clang -o te $flags ../src/text_editor.cpp $I -lSDL3 -lm -lbacktrace
./te :RunTests

View File

@@ -18,6 +18,8 @@ typedef void OSErrorReport(const char *, ...);
#include <poll.h> #include <poll.h>
#include <execinfo.h> #include <execinfo.h>
#include <backtrace.h> #include <backtrace.h>
#include <sys/resource.h>
API void (*Error)(const char *, ...); API void (*Error)(const char *, ...);

View File

@@ -256,4 +256,4 @@ void CMD_SelectComment() {
} }
MergeCarets(active.buffer, &active.view->carets); MergeCarets(active.buffer, &active.view->carets);
} RegisterCommand(CMD_SelectComment, "ctrl-shift-l", "Find /* and */ and select the content in between"); } RegisterCommand(CMD_SelectComment, "ctrl-semicolon", "Find /* and */ and select the content in between");

View File

@@ -80,7 +80,8 @@ void UpdateCoroutines(Event *event) {
#endif #endif
double took = GetTimeSeconds() - start; double took = GetTimeSeconds() - start;
if (took > (0.016666 / 3.0)) { bool dont_loop_on_coroutines_when_budget_is_ok_exit_immediately = Testing;
if (dont_loop_on_coroutines_when_budget_is_ok_exit_immediately || (took > (0.016666 / 3.0))) {
break; break;
} }
} }

View File

@@ -1,4 +1,7 @@
SDL_Window *SDLWindow; SDL_Window *SDLWindow;
SDL_GLContext SDL_WindowGLContext;
SDL_Cursor *SDL_MouseCursor;
SDL_SystemCursor SDL_MouseCursorLastID;
bool IsInFullscreen; bool IsInFullscreen;
int FullScreenSizeX, FullScreenSizeY; int FullScreenSizeX, FullScreenSizeY;
int FullScreenPositionX, FullScreenPositionY; int FullScreenPositionX, FullScreenPositionY;
@@ -10,6 +13,7 @@ bool SearchWordBoundary = false;
bool BreakOnError = false; bool BreakOnError = false;
Int ErrorCount; Int ErrorCount;
bool DebugTraceBufferInits = false; bool DebugTraceBufferInits = false;
bool Testing = false;
Allocator SysAllocator = {SystemAllocatorProc}; Allocator SysAllocator = {SystemAllocatorProc};
float DPIScale = 1.0f; float DPIScale = 1.0f;
@@ -61,6 +65,8 @@ Vec2I MouseMiddleAnchor;
RandomSeed UniqueBufferNameSeed = {}; RandomSeed UniqueBufferNameSeed = {};
Array<Event> EventPlayback; Array<Event> EventPlayback;
Array<Event> MacroPlayback;
bool RecordingMacro = false;
BlockArena Perm; BlockArena Perm;
// clipboard // clipboard

View File

@@ -37,7 +37,7 @@ void CMD_DedentSelectedLines() {
void CMD_DuplicateLineDown() { void CMD_DuplicateLineDown() {
BSet active = GetBSet(ActiveWindowID); BSet active = GetBSet(ActiveWindowID);
DuplicateLine(active.view, DIR_DOWN); DuplicateLine(active.view, DIR_DOWN);
} RegisterCommand(CMD_DuplicateLineDown, "ctrl-alt-down", ""); } RegisterCommand(CMD_DuplicateLineDown, "ctrl-shift-alt-down", "");
void CMD_CreateCursorDown() { void CMD_CreateCursorDown() {
BSet active = GetBSet(ActiveWindowID); BSet active = GetBSet(ActiveWindowID);
@@ -72,7 +72,7 @@ void CMD_MoveDown() {
void CMD_DuplicateLineUp() { void CMD_DuplicateLineUp() {
BSet active = GetBSet(ActiveWindowID); BSet active = GetBSet(ActiveWindowID);
DuplicateLine(active.view, DIR_UP); DuplicateLine(active.view, DIR_UP);
} RegisterCommand(CMD_DuplicateLineUp, "ctrl-alt-up", ""); } RegisterCommand(CMD_DuplicateLineUp, "ctrl-shift-alt-up", "");
void CMD_CreateCursorUp() { void CMD_CreateCursorUp() {
BSet active = GetBSet(ActiveWindowID); BSet active = GetBSet(ActiveWindowID);
@@ -234,6 +234,12 @@ void CMD_NewLine() {
IndentedNewLine(active.view); IndentedNewLine(active.view);
} RegisterCommand(CMD_NewLine, "enter | shift-enter", ""); } RegisterCommand(CMD_NewLine, "enter | shift-enter", "");
void CMD_SearchAllOccurences() {
BSet active = GetBSet(ActiveWindowID);
String16 needle = GetString(active.buffer, active.view->carets[0].range);
SelectAllOccurences(active.view, needle);
} RegisterCommand(CMD_SearchAllOccurences, "ctrl-shift-l", "Use the selected word as needle and selects all the possible occurences in current buffer");
void CMD_CreateCaretOnNextFind() { void CMD_CreateCaretOnNextFind() {
BSet active = GetBSet(ActiveWindowID); BSet active = GetBSet(ActiveWindowID);
String16 string = GetString(active.buffer, active.view->carets[0].range); String16 string = GetString(active.buffer, active.view->carets[0].range);
@@ -247,3 +253,21 @@ void CMD_ClearCarets() {
active.view->carets.len = 1; active.view->carets.len = 1;
active.view->carets[0] = MakeCaret(GetFront(active.view->carets[0])); active.view->carets[0] = MakeCaret(GetFront(active.view->carets[0]));
} RegisterCommand(CMD_ClearCarets, "escape", "Clear all carets and reset to 1 caret, also do some windowing stuff that closes things on escape"); } RegisterCommand(CMD_ClearCarets, "escape", "Clear all carets and reset to 1 caret, also do some windowing stuff that closes things on escape");
void CMD_ToggleMacroRecording() {
if (RecordingMacro) {
RecordingMacro = false;
Pop(&MacroPlayback); // Remove the ctrl-m from macro playback thing
} else {
RecordingMacro = true;
MacroPlayback.len = 0;
}
} RegisterCommand(CMD_ToggleMacroRecording, "ctrl-m", "Start recording a macro");
void CMD_PlayMacro() {
if (RecordingMacro) {
return;
}
For (MacroPlayback) Add(&EventPlayback, it);
} RegisterCommand(CMD_PlayMacro, "alt-m", "Start playing back a macro recording");

View File

@@ -88,17 +88,17 @@ void Set(String string) {
string = Skip(string, 1); string = Skip(string, 1);
String quote = SkipUntil(&string, {&c, 1}); String quote = SkipUntil(&string, {&c, 1});
ExpectP(At(string, 0) == c, ":Set %S <error here>, unclosed quote", name); ExpectP(At(string, 0) == c, ":Set %S <error here>, unclosed quote", name);
ReportConsolef(":Set %S %c%S%c", name, c, quote, c); // ReportConsolef(":Set %S %c%S%c", name, c, quote, c);
*var->string = Intern(&GlobalInternTable, quote); *var->string = Intern(&GlobalInternTable, quote);
} else if (var->type == VariableType_Int) { } else if (var->type == VariableType_Int) {
ExpectP(IsDigit(At(string, 0)), "Expected an integer to follow the command name, instead got: %S", string); ExpectP(IsDigit(At(string, 0)), "Expected an integer to follow the command name, instead got: %S", string);
Int number = SkipInt(&string); Int number = SkipInt(&string);
ReportConsolef(":Set %S %lld", name, number); // ReportConsolef(":Set %S %lld", name, number);
*var->i = number; *var->i = number;
} else if (var->type == VariableType_Float) { } else if (var->type == VariableType_Float) {
ExpectP(IsDigit(At(string, 0)), "Expected float to follow the command name, instead got: %S", string); ExpectP(IsDigit(At(string, 0)), "Expected float to follow the command name, instead got: %S", string);
Float number = SkipFloat(&string); Float number = SkipFloat(&string);
ReportConsolef(":Set %S %f", name, number); // ReportConsolef(":Set %S %f", name, number);
*var->f = number; *var->f = number;
} else if (var->type == VariableType_Color) { } else if (var->type == VariableType_Color) {
ExpectP(IsHexDigit(At(string, 0)), "Expected hex integer to follow the command name, instead got: %S", string); ExpectP(IsHexDigit(At(string, 0)), "Expected hex integer to follow the command name, instead got: %S", string);
@@ -107,7 +107,7 @@ void Set(String string) {
string = Skip(string, 1); string = Skip(string, 1);
begin.len += 1; begin.len += 1;
} }
ReportConsolef(":Set %S %S", name, begin); // ReportConsolef(":Set %S %S", name, begin);
var->color->value = (uint32_t)strtoll(begin.data, NULL, 16); var->color->value = (uint32_t)strtoll(begin.data, NULL, 16);
} ElseInvalidCodepath(); } ElseInvalidCodepath();

View File

@@ -48,7 +48,13 @@ void UpdateDebugWindow() {
return; return;
} }
RawReplaceText(set.buffer, GetRange(set.buffer), u"Active buffers and views:\n"); RawReplaceText(set.buffer, GetRange(set.buffer), u"");
#if OS_POSIX
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);
RawAppendf(set.buffer, "MemoryUsage: %lld\n", usage.ru_maxrss);
#endif
For (Views) { For (Views) {
Buffer *buffer = GetBuffer(it->active_buffer); Buffer *buffer = GetBuffer(it->active_buffer);
RawAppendf(set.buffer, "view->id:%lld, buffer->id:%lld, buffer->name:%S\n", (long long)it->id.id, (long long)buffer->id.id, buffer->name); RawAppendf(set.buffer, "view->id:%lld, buffer->id:%lld, buffer->name:%S\n", (long long)it->id.id, (long long)buffer->id.id, buffer->name);

View File

@@ -8,6 +8,16 @@ void CMD_OpenUpFolder() {
Open(name); Open(name);
} RegisterCommand(CMD_OpenUpFolder, "ctrl-o", "Open current's file directory or up directory in other cases"); } RegisterCommand(CMD_OpenUpFolder, "ctrl-o", "Open current's file directory or up directory in other cases");
void CMD_OpenSystemFileBrowser() {
Scratch scratch;
String string = GetPrimaryDirectory();
#if OS_WINDOWS
Open(Format(scratch, "!!explorer %S", string));
#else
Open(Format(scratch, "!!xdg-open %S & disown", string));
#endif
} RegisterCommand(CMD_OpenSystemFileBrowser, "ctrl-alt-r", "Opens the directory of current file in the systems file browser");
void InsertDirectoryNavigation(Buffer *buffer) { void InsertDirectoryNavigation(Buffer *buffer) {
Assert(buffer->is_dir); Assert(buffer->is_dir);
Scratch scratch; Scratch scratch;

View File

@@ -15,6 +15,7 @@ void New(Window *window, String name = "") {
name = GetUniqueBufferName(dir, "new"); name = GetUniqueBufferName(dir, "new");
} }
WindowOpenBufferView(window, name); WindowOpenBufferView(window, name);
RunGCThisFrame = true;
} }
void CMD_New() { void CMD_New() {

View File

@@ -1,14 +1,33 @@
bool Testing = true; #if PLUGIN_TESTS
void Wait(mco_coro *co) { void Wait(mco_coro *co) {
Add(&EventPlayback, {EVENT_KIND_INVALID}); {Event ev = {};ev.kind = EVENT_KIND_INVALID; Add(&EventPlayback, ev);}
for (Event *event = Yield(co); event->kind != EVENT_KIND_INVALID; event = Yield(co)) { Event *event = NULL;
for (event = Yield(co); event->kind != EVENT_KIND_INVALID; event = Yield(co)) {
} }
} }
void CO_FirstTest(mco_coro *co) { void Wait(mco_coro *co, int updates) {
for (int i = 0; i < updates; i += 1) {
{Event ev = {};ev.kind = EVENT_UPDATE; Add(&EventPlayback, ev);}
}
Wait(co);
}
void OpenCloseCodeTest(mco_coro *co) {
Int initial_buffers_count = Buffers.len;
CO_OpenCode(co);
Assert(Buffers.len > initial_buffers_count);
CO_CloseAll(co);
Wait(co);
Assert(initial_buffers_count - 1 == Buffers.len);
}
void CO_RunTests(mco_coro *co) {
Testing = true; Testing = true;
WaitForEvents = false; WaitForEvents = false;
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);} {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);} {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);} {Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
@@ -37,4 +56,68 @@ void CO_FirstTest(mco_coro *co) {
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "f"; Add(&EventPlayback, ev);} {Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "f"; Add(&EventPlayback, ev);}
Wait(co); Wait(co);
} RegisterCoroutineCommand(CO_FirstTest, "", "Basic tests"); String16 result = uR"FOO(
Memes and stuff)FOO";
BSet set = GetBSet(PrimaryWindowID);
Assert(AreEqual(result, GetString(set.buffer)));
// Test the box selection
CMD_New();
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_RETURN; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_LSHIFT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.shift = 1; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.shift = 1; ev.text = "M"; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "e"; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "m"; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "e"; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "s"; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_SPACE; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = " "; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_LSHIFT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.shift = 1; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "a"; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "n"; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "d"; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_KEY_PRESS; ev.key = SDLK_SPACE; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = " "; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "s"; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "t"; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "u"; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "f"; Add(&EventPlayback, ev);}
{Event ev = {};ev.kind = EVENT_TEXT_INPUT; ev.xwindow = 1412; ev.ywindow = 1032; ev.xmouse = 1234; ev.ymouse = 594; ev.text = "f"; Add(&EventPlayback, ev);}
Wait(co);
result = uR"FOO(
Memes and stuff)FOO";
set = GetBSet(PrimaryWindowID);
Assert(AreEqual(result, GetString(set.buffer)));
if (ErrorCount != 0) {
Scratch scratch;
String string = AllocCharString(scratch, LogBuffer);
printf("TEXT EDITOR ERRORS!\n==========================\n%.*s", (int)string.len, string.data);
}
printf("RunTests OK\n");
fflush(stdout);
void CMD_QuitWithoutSaving();
CMD_QuitWithoutSaving();
} RegisterCoroutineCommand(CO_RunTests, "", "Basic tests");
#endif

View File

@@ -9,14 +9,14 @@ void CMD_Prev() {
main.window->skip_checkpoint = true; main.window->skip_checkpoint = true;
JumpBack(main.window); JumpBack(main.window);
NextActiveWindowID = main.window->id; NextActiveWindowID = main.window->id;
} RegisterCommand(CMD_Prev, "alt-q | mousex1", "Go to previous position (either previous view that was open or caret position) in the primary window"); } RegisterCommand(CMD_Prev, "alt-q | mousex1 | ctrl-alt-minus", "Go to previous position (either previous view that was open or caret position) in the primary window");
void CMD_Next() { void CMD_Next() {
BSet main = GetBSet(PrimaryWindowID); BSet main = GetBSet(PrimaryWindowID);
main.window->skip_checkpoint = true; main.window->skip_checkpoint = true;
JumpForward(main.window); JumpForward(main.window);
NextActiveWindowID = main.window->id; NextActiveWindowID = main.window->id;
} RegisterCommand(CMD_Next, "alt-shift-q | mousex2", "Go to next position, after backtracking, in the primary window"); } RegisterCommand(CMD_Next, "alt-shift-q | mousex2 | ctrl-shift-minus", "Go to next position, after backtracking, in the primary window");
void CMD_FocusLeftWindow() { void CMD_FocusLeftWindow() {
NextActiveWindowID = SwitchWindow(DIR_LEFT)->id; NextActiveWindowID = SwitchWindow(DIR_LEFT)->id;
@@ -74,9 +74,9 @@ void CMD_CloseWindow() {
void CMD_GotoNextInList() { void CMD_GotoNextInList() {
BSet main = GetBSet(PrimaryWindowID); BSet main = GetBSet(PrimaryWindowID);
GotoNextInList(main.window, 1); GotoNextInList(main.window, 1);
} RegisterCommand(CMD_GotoNextInList, "ctrl-e", "For example: when jumping from build panel to build error, a jump point is setup, user can click this button to go over to the next compiler error"); } RegisterCommand(CMD_GotoNextInList, "ctrl-e | f8", "For example: when jumping from build panel to build error, a jump point is setup, user can click this button to go over to the next compiler error");
void CMD_GotoPrevInList() { void CMD_GotoPrevInList() {
BSet main = GetBSet(PrimaryWindowID); BSet main = GetBSet(PrimaryWindowID);
GotoNextInList(main.window, -1); GotoNextInList(main.window, -1);
} RegisterCommand(CMD_GotoPrevInList, "alt-e", "For example: when jumping from build panel to build error, a jump point is setup, user can click this button to go over to the previous compiler error"); } RegisterCommand(CMD_GotoPrevInList, "alt-e | shift-f8", "For example: when jumping from build panel to build error, a jump point is setup, user can click this button to go over to the previous compiler error");

View File

@@ -31,9 +31,7 @@ BlockArena RenderArena;
Rect2 CurrentScissor; Rect2 CurrentScissor;
Font PrimaryFont; Font PrimaryFont;
Font SecondaryFont;
// ---------- shaders (ES3 / WebGL2) ----------
static const char *glsl_vshader_es3 = R"==(#version 300 es static const char *glsl_vshader_es3 = R"==(#version 300 es
precision highp float; precision highp float;
@@ -82,7 +80,6 @@ void GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLs
} }
} }
// ---------- helper: compile/link ----------
static GLuint CompileShaderSrc(GLenum kind, const char *src) { static GLuint CompileShaderSrc(GLenum kind, const char *src) {
GLuint s = glCreateShader(kind); GLuint s = glCreateShader(kind);
glShaderSource(s, 1, &src, NULL); glShaderSource(s, 1, &src, NULL);
@@ -137,7 +134,6 @@ Shader CreateShaderES3(const char *vsrc, const char *fsrc) {
return out; return out;
} }
// ---------- InitRender for ES3 ----------
void InitRender() { void InitRender() {
#if !OS_WASM #if !OS_WASM
glDebugMessageCallback(&GLDebugCallback, NULL); glDebugMessageCallback(&GLDebugCallback, NULL);
@@ -185,7 +181,6 @@ void BeginFrameRender(float wx, float wy) {
CurrentScissor = Rect0Size(wx, wy); CurrentScissor = Rect0Size(wx, wy);
} }
// ---------- EndFrameRender for ES3 ----------
void EndFrameRender(float wx, float wy, Color color) { void EndFrameRender(float wx, float wy, Color color) {
ProfileFunction(); ProfileFunction();
glEnable(GL_BLEND); glEnable(GL_BLEND);
@@ -391,6 +386,9 @@ void ReloadFont(String path, U32 size) {
Scratch scratch; Scratch scratch;
Atlas atlas = CreateAtlas(scratch, {2048, 2048}); Atlas atlas = CreateAtlas(scratch, {2048, 2048});
PrimaryFont = CreateFont(&atlas, (uint32_t)ClampBottom(2u, (U32)size), path); PrimaryFont = CreateFont(&atlas, (uint32_t)ClampBottom(2u, (U32)size), path);
SecondaryFont = CreateFont(&atlas, 12, path); PrimaryFont.texture_id = UploadAtlas(&atlas);
SecondaryFont.texture_id = PrimaryFont.texture_id = UploadAtlas(&atlas); }
void CleanupRender() {
Dealloc(&PrimaryFont.glyphs);
} }

View File

@@ -1,56 +1,57 @@
/* /*
- [x] list_functions.sh and rg in general in the color mode is prinitng differently from terminal as well as output is truncated!
- [ ] Syntax for executing commands from root of project ## Basics
- [ ] Fuzzy search over executed command ouput - [ ] Ctrl+Shift+ArrowDown at the end of buffer, doesn't capture characters on last line without new line, same at the top
- [ ] When inserting parenthesis and selection is there, put the parens on both sides? - [ ] When inserting parenthesis and selection is there, put the parens on both sides?
- [ ] KillProcess in console !!! - should also kill all the children ........... - [ ] I noticed WordComplete getting busted after using editor for a while and returning cutdown words but not sure how to repro ...
- [x] ctrl-e with these short main.c:290: breaks a little, need to first click ctrl-e and then alt-e to jump - [ ] Rewrite WordComplete to use CoroutineCreate, maybe try reducing globals to just the coroutine itself
- [ ] Use command window without special fuzzy search features to type commands and stuff for executing shell etc.. - [ ] WorkComplete, Bounded search, after a while it becomes problematic with many buffers
## Monaco like design for familiarity
- [ ] ctrl-tab - switch file lister with instant hold release semantics?
- [ ] SearchAndReplace how to do better?
- [ ] ctrl-t find workspace symbols? how can we do it?
- [ ] Snippet design?
## Refactor
- [ ] Make a platform layer and separate SDL stuff out - [ ] Make a platform layer and separate SDL stuff out
- [x] ReplaceAll - heap-use-after-free address, how to debug? I think would be nice to iterate all buffer ids and their addresses along with the state - [ ] Report a regression in SDL in newer versions that make it so that events are doubled when unpressing the held button
- [ ] Remove -lbacktrace and add my backtrace library thing
- [ ] Refactor build.sh to accept commands and remove build_web.sh
- [ ] GetWindowZOrder to IterateWindowsInZOrder
- [ ] Investigate reworking history API, tagging modification blocks with carets?
- [ ] How to enable framerate to be unlimited and not break scrolling?
- [ ] When 2 views of same buffer are open, the view with caret below the caret which modifies the view - starts moving and getting messed up
- [ ] The lexing / parsing code for config / bindings appears sloppy would be nice to clean it up but I don't have any ideas
- [ ] Redesign `:QueryFile`
- [x] BRO, the caret teleports on linux when I press the arrow for too long ## Features
- [ ] Report SDL newest vs SDL previous version on wayland - [ ] KillProcess in console !!! - should also kill all the children ...........
- [ ] BeginLog and EndLog, show all logs in the UI thing at the end
- [ ] Fuzzy search over executed command ouput
- [ ] Command window but for executing commands without special fuzzy stuff?
- [ ] Implement Regex and jumps using regex so as to allow for using ctags
- [ ] Add UndoKinds (SnapshotUndo, DiffUndo), I want to enable history in fuzzy search buffers without a huge memory cost
- [ ] General parser / data description thing, rewrite other parsers using this, rewrite env handling
- [ ] DirNav, update lister when files change on disk
- [ ] Investigate ways to bind "open" commands to keys from config
- [ ] Ability to access and set clipboard as well as affect selection in the open scripts
- [ ] Syntax for executing commands from root of project, or maybe commands should be executed from root of the CurrentDirectory
- [ ] Cleanups ## Test
- [ ] How to enable framerate to be unlimited and not break scrolling?
- [x] When dragging a file into the editor, would be nice if the file opened in the window user dropped the file into. Not the active window.
- [ ] When 2 views of same buffer are open, the view with caret below the caret which modifies the view - starts moving and getting messed up
- [ ] Reduce number of created buffers to one per window, should be enough
- [ ] GetWindowZOrder to IterateWindowsInZOrder
- [ ] Rework history API, tagging modification blocks with carets?
- [ ] The lexing / parsing code for config / bindings appears sloppy would be nice to clean it up but I don't have any ideas
- [ ] Directory tree doesn't make much sense! Maybe just consolidate into one folder? create nice names - the raddbg idea didn't pan out well here
- [ ] Test BlockArena correctnsess - random allocations, writes and undos, try to crash - [ ] Test BlockArena correctnsess - random allocations, writes and undos, try to crash
- [ ] General parser / data description thing ## Low
- [ ] Rewrite other parsers using this
- [ ] Rewrite Env handling (to not be OS specific)
- New error mechanism - we were losing errors when ReportError was called multiple times, I still want that but I don't want to lose errors, so turn it into a summary list of errors
- [ ] BeginLog EndLog, and then show all logs as a list in the UI thing
- [ ] Undo kinds (to enable history in fuzzy buffers)
- [ ] Add undo kind. Snapshot kind, so that history is possible in weird buffers without paying a huge memory cost. The idea is that we would store the exact buffer state to replace with, editor would just save history of first line etc.
- [ ] Macros
- [ ] Regex
- [ ] ctags based indexing
- [ ] WordComplete
- [ ] Rewrite WordComplete to use CoroutineCreate, maybe try reducing globals to just the coroutine itself
- [ ] More bounded? seems like it might be problematic on a bigger project but so far it isn't (even for SDL or raddbg)
- [ ] Shell / terminal buffer plugin (keep the shell alive and talk with it) - [ ] Shell / terminal buffer plugin (keep the shell alive and talk with it)
- [ ] Directory Navigation - [ ] Fix somehow OpenCode hanging the editor on big files
- [ ] Remake lister when files change on disk
- [ ] When saving apply all the modifications instead (like deleting files, renaming etc.) or maybe that's not even needed considering we are integrating shell commands -----------------------------------------------------------------------
- [ ] OpenCode
- [ ] Hangs the editor on big files ## :QueryFile problems
- [ ] Open - User doesn't see that he in a special mode
- [ ] Way to bind "open" commands to keys from config - Coroutine is boundless here and the boundries of the mode are too lossely defined, it makes it strange when you learn that you are still in this mode
- [ ] Ability to access and set clipboard as well as affect selection in the open scripts - How do we kill all the views/buffers we entered? Do we care?
- [ ] QueryFile
- [ ] Indicate to user that he is choosing a file
- [ ] Define clear rules for opt out (like switching to different window) and kill or views that were for choosing?
*/ */
#define PLUGIN_PROFILER 1 #define PLUGIN_PROFILER 1
@@ -91,6 +92,7 @@
#define PLUGIN_REMEDYBG OS_WINDOWS #define PLUGIN_REMEDYBG OS_WINDOWS
#define PLUGIN_FILE_COMMANDS 1 #define PLUGIN_FILE_COMMANDS 1
#define PLUGIN_WORD_COMPLETE 1 #define PLUGIN_WORD_COMPLETE 1
#define PLUGIN_TESTS 1
#include "plugin_directory_navigation.h" #include "plugin_directory_navigation.h"
#include "plugin_search_window.h" #include "plugin_search_window.h"
@@ -154,16 +156,13 @@ void SetMouseCursor(SDL_SystemCursor id) {
} }
#else #else
void SetMouseCursor(SDL_SystemCursor id) { void SetMouseCursor(SDL_SystemCursor id) {
static SDL_Cursor *SDL_MouseCursor; if (SDL_MouseCursor == NULL || SDL_MouseCursorLastID != id) {
static SDL_SystemCursor last_id;
if (SDL_MouseCursor == NULL || last_id != id) {
if (SDL_MouseCursor != NULL) { if (SDL_MouseCursor != NULL) {
SDL_DestroyCursor(SDL_MouseCursor); SDL_DestroyCursor(SDL_MouseCursor);
} }
SDL_MouseCursor = SDL_CreateSystemCursor(id); SDL_MouseCursor = SDL_CreateSystemCursor(id);
SDL_SetCursor(SDL_MouseCursor); SDL_SetCursor(SDL_MouseCursor);
last_id = id; SDL_MouseCursorLastID = id;
} }
} }
#endif #endif
@@ -216,9 +215,6 @@ void SetMouseCursor(Event event) {
} }
void CMD_QuitWithoutSaving() { void CMD_QuitWithoutSaving() {
#if PLUGIN_REMEDYBG
QuitDebugger();
#endif
AppIsRunning = false; AppIsRunning = false;
} RegisterCommand(CMD_QuitWithoutSaving, "", "Self explanatory"); } RegisterCommand(CMD_QuitWithoutSaving, "", "Self explanatory");
@@ -308,6 +304,27 @@ void OnCommand(Event event) {
Int p = ScreenSpaceToBufferPos(selected.window, selected.view, selected.buffer, mouse); Int p = ScreenSpaceToBufferPos(selected.window, selected.view, selected.buffer, mouse);
Caret &caret = selected.view->carets[0]; Caret &caret = selected.view->carets[0];
caret = SetFrontWithAnchor(caret, DocumentAnchor, p); caret = SetFrontWithAnchor(caret, DocumentAnchor, p);
if (event.alt && event.shift) {
Int front = GetFront(DocumentAnchor);
XY from = PosToXY(selected.buffer, front);
XY to = ScreenSpaceToXY(selected.window, selected.view, mouse);
Int min_line = Min(from.y, to.y);
Int max_line = Max(from.y, to.y);
Int min_col = Min(from.x, to.x);
Int max_col = Max(from.x, to.x);
selected.view->carets.len = 0;
for (Int line = min_line; line <= max_line; line += 1) {
XY left_xy = {min_col, line};
XY right_xy = {max_col, line};
Int left = XYToPosWithoutNL(selected.buffer, left_xy);
Int right = XYToPosWithoutNL(selected.buffer, right_xy);
Caret new_selection = MakeCaret(left, right);
Add(&selected.view->carets, new_selection);
}
MergeCarets(selected.buffer, &selected.view->carets);
}
} }
if (ResizerSelected.id != -1 && Mouse(LEFT_UP)) { if (ResizerSelected.id != -1 && Mouse(LEFT_UP)) {
@@ -414,8 +431,11 @@ void OnCommand(Event event) {
DocumentSelected = active.window->id; DocumentSelected = active.window->id;
Int p = ScreenSpaceToBufferPos(active.window, active.view, active.buffer, mouse); Int p = ScreenSpaceToBufferPos(active.window, active.view, active.buffer, mouse);
if (event.alt) Insert(&active.view->carets, MakeCaret(p, p), 0); if (event.alt) {
if (!event.alt && !event.shift) active.view->carets.len = 1; Insert(&active.view->carets, MakeCaret(p, p), 0);
} else if (!event.alt && !event.shift) {
active.view->carets.len = 1;
}
Caret &caret = active.view->carets[0]; Caret &caret = active.view->carets[0];
if (event.shift) { if (event.shift) {
@@ -547,6 +567,7 @@ void GarbageCollect() {
return; return;
} }
RunGCThisFrame = false; RunGCThisFrame = false;
ReportConsolef("GarbageCollect");
ProfileFunction(); ProfileFunction();
Allocator sys_allocator = GetSystemAllocator(); Allocator sys_allocator = GetSystemAllocator();
@@ -568,8 +589,8 @@ void GarbageCollect() {
continue; continue;
} }
bool ref = ViewIsReferenced(it); Int ref = ViewIsReferenced(it);
if (ref) { if (ref < 25) {
continue; continue;
} }
@@ -840,6 +861,9 @@ void MainLoop() {
FrameEvents.len = 0; FrameEvents.len = 0;
GetEventsForFrame(&FrameEvents); GetEventsForFrame(&FrameEvents);
For (FrameEvents) { For (FrameEvents) {
if (RecordingMacro) {
Add(&MacroPlayback, it);
}
#if PLUGIN_RECORD_EVENTS #if PLUGIN_RECORD_EVENTS
if (it.kind != EVENT_UPDATE && !Testing) { if (it.kind != EVENT_UPDATE && !Testing) {
Serialize(EventBuffer, &it); Serialize(EventBuffer, &it);
@@ -1012,8 +1036,8 @@ int main(int argc, char **argv, char **envp)
} }
SDL_SetWindowPosition(SDLWindow, xhalf, yhalf); SDL_SetWindowPosition(SDLWindow, xhalf, yhalf);
SDL_GLContext gl_context = SDL_GL_CreateContext(SDLWindow); SDL_WindowGLContext = SDL_GL_CreateContext(SDLWindow);
SDL_GL_MakeCurrent(SDLWindow, gl_context); SDL_GL_MakeCurrent(SDLWindow, SDL_WindowGLContext);
SDL_ShowWindow(SDLWindow); SDL_ShowWindow(SDLWindow);
// Set icon // Set icon
@@ -1136,6 +1160,10 @@ int main(int argc, char **argv, char **envp)
} }
#endif #endif
#if PLUGIN_REMEDYBG
QuitDebugger();
#endif
CleanupRender();
SDL_DestroyWindow(SDLWindow); SDL_DestroyWindow(SDLWindow);
SDL_Quit(); SDL_Quit();

View File

@@ -5,6 +5,7 @@ void JumpTempBuffer(BSet *set, String buffer_name) {
set->view = WindowOpenBufferView(set->window, buffer_name); set->view = WindowOpenBufferView(set->window, buffer_name);
set->buffer = GetBuffer(set->view->active_buffer); set->buffer = GetBuffer(set->view->active_buffer);
set->buffer->temp = true; set->buffer->temp = true;
RunGCThisFrame = true;
} }
void AddCommand(Array<Command> *arr, String name, Trigger *trigger, CMDFunction *function) { void AddCommand(Array<Command> *arr, String name, Trigger *trigger, CMDFunction *function) {

View File

@@ -103,6 +103,22 @@ API bool ViewIsCrumb(ViewID view_id) {
return false; return false;
} }
API Int GetViewReferencedInHistoryRating(ViewID view_id) {
ForItem (window, Windows) {
For (window->goto_redo) if (it.view_id == view_id) return 1;
Int i = 0;
For (window->goto_history) {
i += 1;
if (it.view_id == view_id) {
return i;
}
}
}
return 0;
}
bool ViewIsActive(ViewID id) { bool ViewIsActive(ViewID id) {
For (Windows) { For (Windows) {
if (it->active_view == id) { if (it->active_view == id) {
@@ -112,16 +128,16 @@ bool ViewIsActive(ViewID id) {
return false; return false;
} }
API bool ViewIsReferenced(View *view) { API Int ViewIsReferenced(View *view) {
if (view->special) { if (view->special) {
return true; return 1;
} }
if (ViewIsCrumb(view->id)) { if (ViewIsActive(view->id)) {
return true; return 1;
} }
return ViewIsActive(view->id); return GetViewReferencedInHistoryRating(view->id);
} }
bool BufferIsReferenced(Buffer *buffer) { bool BufferIsReferenced(Buffer *buffer) {

View File

@@ -176,20 +176,22 @@ Window *SwitchWindow(int direction) {
return result; return result;
} }
Int ScreenSpaceToBufferPos(Window *window, View *view, Buffer *buffer, Vec2I mouse) { XY ScreenSpaceToXY(Window *window, View *view, Vec2I mouse) {
Vec2I mworld = mouse - window->document_rect.min + view->scroll; Vec2I mworld = mouse - window->document_rect.min + view->scroll;
double px = (double)mworld.x / (double)window->font->char_spacing; double px = (double)mworld.x / (double)window->font->char_spacing;
double py = (double)mworld.y / (double)window->font->line_spacing; double py = (double)mworld.y / (double)window->font->line_spacing;
XY xy = {(Int)round(px), (Int)floor(py)}; XY xy = {(Int)round(px), (Int)floor(py)};
return xy;
}
Int ScreenSpaceToBufferPos(Window *window, View *view, Buffer *buffer, Vec2I mouse) {
XY xy = ScreenSpaceToXY(window, view, mouse);
Int result = XYToPosWithoutNL(buffer, xy); Int result = XYToPosWithoutNL(buffer, xy);
return result; return result;
} }
Int ScreenSpaceToBufferPosErrorOutOfBounds(Window *window, View *view, Buffer *buffer, Vec2I mouse) { Int ScreenSpaceToBufferPosErrorOutOfBounds(Window *window, View *view, Buffer *buffer, Vec2I mouse) {
Vec2I mworld = mouse - window->document_rect.min + view->scroll; XY xy = ScreenSpaceToXY(window, view, mouse);
double px = (double)mworld.x / (double)window->font->char_spacing;
double py = (double)mworld.y / (double)window->font->line_spacing;
XY xy = {(Int)round(px), (Int)floor(py)};
Int result = XYToPosErrorOutOfBounds(buffer, xy); Int result = XYToPosErrorOutOfBounds(buffer, xy);
return result; return result;
} }