Compare commits

..

4 Commits

Author SHA1 Message Date
Krzosa Karol
44bf7c68ff Misc 2025-12-26 15:41:29 +01:00
Krzosa Karol
4221aa8fb7 Finite size jump history, improving backtrace, redo, undo grouping, similar for jump history 2025-12-26 15:40:03 +01:00
Krzosa Karol
509db7af46 Ctrl-f fills with currently selected 2025-12-26 11:57:09 +01:00
Krzosa Karol
5c46844747 Buffer directories design change 2025-12-25 12:08:49 +01:00
13 changed files with 114 additions and 44 deletions

View File

@@ -1,8 +1,8 @@
- What precise workflow do I need for me to be viable to use this? - What precise workflow do I need for me to be viable to use this?
- From a user (novice) point of view, how does it look like? - From a user (novice) point of view, how does it look like?
- Open with seek string (open at pattern) filename:32 filename:/^Window$/
- build console window - build console window
- Use WorkDir (raname to Workspace?) to shorten file names
- Show what process/coroutines are running and allow to kill (active process buffer?) - Show what process/coroutines are running and allow to kill (active process buffer?)
- Database idea: use special buffers to store information - Database idea: use special buffers to store information
- Editing the buffer doesn't seem to be the slow part rather, accessing the data and putting it into the buffer (potentially hitting many different memory locations) I have a crazy idea to use buffers in order to store the names in a serialized format - Editing the buffer doesn't seem to be the slow part rather, accessing the data and putting it into the buffer (potentially hitting many different memory locations) I have a crazy idea to use buffers in order to store the names in a serialized format
@@ -19,10 +19,11 @@ Commands TODO:
- CONSIDER AUTOMATING: CheckpointBeforeGoto - CONSIDER AUTOMATING: CheckpointBeforeGoto
- GotoBackward how to handle that in case we want to automate and create on every move? - GotoBackward how to handle that in case we want to automate and create on every move?
- Add jump checkpoints
- Make sure the timing window works forward and backward in undo and jump
backlog backlog
ISSUE Ctrl+Alt+Down (DuplicateLine) doesn't work on ubuntu
FEATURE Search whole words, case sensitive etc. FEATURE Search whole words, case sensitive etc.
FEATURE Select all searched occurences FEATURE Select all searched occurences
FEATURE commands for scrolling: center, cursor_at_bottom_of_screen, cursor_at_top FEATURE commands for scrolling: center, cursor_at_bottom_of_screen, cursor_at_top

View File

@@ -1,3 +1,5 @@
typedef void OSErrorReport(const char *, ...);
#if OS_POSIX #if OS_POSIX
#include <dirent.h> #include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
@@ -54,13 +56,19 @@ void RegisterCrashHandler(void) {
struct sigaction sa; struct sigaction sa;
sa.sa_sigaction = CrashHandler; sa.sa_sigaction = CrashHandler;
sigemptyset(&sa.sa_mask); sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO; sa.sa_flags = SA_SIGINFO | SA_NODEFER | SA_RESETHAND; // Important flags!
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGABRT, &sa, NULL); sigaction(SIGSEGV, &sa, NULL); // Segmentation fault
sigaction(SIGBUS, &sa, NULL); sigaction(SIGABRT, &sa, NULL); // Abort
sigaction(SIGILL, &sa, NULL); sigaction(SIGBUS, &sa, NULL); // Bus error
sigaction(SIGFPE, &sa, NULL); sigaction(SIGILL, &sa, NULL); // Illegal instruction
sigaction(SIGFPE, &sa, NULL); // Floating point exception
sigaction(SIGTRAP, &sa, NULL); // Breakpoint/trap
sigaction(SIGSYS, &sa, NULL); // Bad system call
sigaction(SIGXCPU, &sa, NULL); // CPU time limit exceeded
sigaction(SIGXFSZ, &sa, NULL); // File size limit exceeded
sigaction(SIGTERM, &sa, NULL); // Termination request (optional)
} }
API void InitOS(void (*error_proc)(const char *, ...)) { API void InitOS(void (*error_proc)(const char *, ...)) {

View File

@@ -103,6 +103,10 @@ API Int GetMin(Caret caret) {
return result; return result;
} }
API Int GetSize(Caret caret) {
return GetSize(caret.range);
}
API Caret MakeCaret(Int pos) { API Caret MakeCaret(Int pos) {
Caret result = {}; Caret result = {};
result.range.min = result.range.max = pos; result.range.min = result.range.max = pos;
@@ -874,12 +878,13 @@ API void AddEdit(Array<Edit> *e, Range range, String16 string) {
// multicursor + history // multicursor + history
/////////////////////////////// ///////////////////////////////
void SaveHistoryBeforeMergeCursor(Buffer *buffer, Array<HistoryEntry> *stack, Array<Caret> &carets) { HistoryEntry *SaveHistoryBeforeMergeCursor(Buffer *buffer, Array<HistoryEntry> *stack, Array<Caret> &carets) {
if (buffer->no_history) return; if (buffer->no_history) return NULL;
HistoryEntry entry = {}; HistoryEntry entry = {};
entry.time = GetTimeSeconds(); entry.time = GetTimeSeconds();
entry.carets = TightCopy(GetSystemAllocator(), carets); entry.carets = TightCopy(GetSystemAllocator(), carets);
Add(stack, entry); Add(stack, entry);
return GetLast(*stack);
} }
void SaveHistoryBeforeApplyEdits(Buffer *buffer, Array<HistoryEntry> *stack, Array<Edit> &edits) { void SaveHistoryBeforeApplyEdits(Buffer *buffer, Array<HistoryEntry> *stack, Array<Edit> &edits) {
@@ -924,7 +929,8 @@ API void RedoEdit(Buffer *buffer, Array<Caret> *carets) {
HistoryEntry entry = Pop(&buffer->redo_stack); HistoryEntry entry = Pop(&buffer->redo_stack);
SaveHistoryBeforeMergeCursor(buffer, &buffer->undo_stack, *carets); HistoryEntry *e = SaveHistoryBeforeMergeCursor(buffer, &buffer->undo_stack, *carets);
e->time = entry.time;
SaveHistoryBeforeApplyEdits(buffer, &buffer->undo_stack, entry.edits); SaveHistoryBeforeApplyEdits(buffer, &buffer->undo_stack, entry.edits);
ApplyEditsMultiCursor(buffer, entry.edits); ApplyEditsMultiCursor(buffer, entry.edits);
@@ -934,6 +940,13 @@ API void RedoEdit(Buffer *buffer, Array<Caret> *carets) {
Allocator sys_allocator = GetSystemAllocator(); Allocator sys_allocator = GetSystemAllocator();
For(entry.edits) Dealloc(sys_allocator, it.string.data); For(entry.edits) Dealloc(sys_allocator, it.string.data);
Dealloc(&entry.edits); Dealloc(&entry.edits);
if (buffer->redo_stack.len > 0) {
HistoryEntry *next = GetLast(buffer->redo_stack);
if (next->time - entry.time <= ConfigUndoMergeTimeWindow) {
RedoEdit(buffer, carets);
}
}
} }
API void UndoEdit(Buffer *buffer, Array<Caret> *carets) { API void UndoEdit(Buffer *buffer, Array<Caret> *carets) {
@@ -942,8 +955,8 @@ API void UndoEdit(Buffer *buffer, Array<Caret> *carets) {
if (buffer->undo_stack.len <= 0) return; if (buffer->undo_stack.len <= 0) return;
HistoryEntry entry = Pop(&buffer->undo_stack); HistoryEntry entry = Pop(&buffer->undo_stack);
HistoryEntry *e = SaveHistoryBeforeMergeCursor(buffer, &buffer->redo_stack, *carets);
SaveHistoryBeforeMergeCursor(buffer, &buffer->redo_stack, *carets); e->time = entry.time;
SaveHistoryBeforeApplyEdits(buffer, &buffer->redo_stack, entry.edits); SaveHistoryBeforeApplyEdits(buffer, &buffer->redo_stack, entry.edits);
ApplyEditsMultiCursor(buffer, entry.edits); ApplyEditsMultiCursor(buffer, entry.edits);
@@ -956,7 +969,7 @@ API void UndoEdit(Buffer *buffer, Array<Caret> *carets) {
if (buffer->undo_stack.len > 0) { if (buffer->undo_stack.len > 0) {
HistoryEntry *next = GetLast(buffer->undo_stack); HistoryEntry *next = GetLast(buffer->undo_stack);
if (entry.time - next->time <= ConfigUndoMergeTimeout) { if (entry.time - next->time <= ConfigUndoMergeTimeWindow) {
UndoEdit(buffer, carets); UndoEdit(buffer, carets);
} }
} }
@@ -1505,8 +1518,8 @@ Buffer *BufferOpenFile(String path) {
if (!FileExists(path)) { if (!FileExists(path)) {
buffer = CreateBuffer(sys_allocator, path); buffer = CreateBuffer(sys_allocator, path);
} else if (IsDir(path)) { } else if (IsDir(path)) {
ReportWarningf("failed to open, it's a directory: %S", path); buffer = CreateBuffer(sys_allocator, path);
return GetBuffer(NullBufferID); buffer->is_dir = true;
} else { } else {
String string = ReadFile(scratch, path); String string = ReadFile(scratch, path);
buffer = CreateBuffer(sys_allocator, path, string.len * 4 + 4096); buffer = CreateBuffer(sys_allocator, path, string.len * 4 + 4096);

View File

@@ -43,6 +43,7 @@ struct Buffer {
unsigned dont_try_to_save_in_bulk_ops : 1; unsigned dont_try_to_save_in_bulk_ops : 1;
unsigned close : 1; unsigned close : 1;
unsigned special : 1; unsigned special : 1;
unsigned is_dir : 1;
}; };
}; };

View File

@@ -19,27 +19,23 @@ BSet GetConsoleSet() {
return result; return result;
} }
String GetCurrentFilename() {
BSet main = GetBSet(LastActiveLayoutWindowID);
return main.buffer->name;
}
String GetDir(Buffer *buffer) { String GetDir(Buffer *buffer) {
String name = ChopLastSlash(buffer->name); if (buffer->is_dir) {
return name; return buffer->name;
} else {
return ChopLastSlash(buffer->name);
}
} }
String GetMainDir() { String GetMainDir() {
BSet main = GetBSet(LastActiveLayoutWindowID); BSet main = GetBSet(LastActiveLayoutWindowID);
String name = ChopLastSlash(main.buffer->name); return GetDir(main.buffer);
return name;
} }
void JumpGarbageBuffer(BSet *set, String buffer_name = "") { void JumpGarbageBuffer(BSet *set, String buffer_name = "") {
CheckpointBeforeGoto(set->window); CheckpointBeforeGoto(set->window);
if (buffer_name.len == 0) { if (buffer_name.len == 0) {
String current_dir = ChopLastSlash(set->buffer->name); buffer_name = GetUniqueBufferName(GetDir(set->buffer), "temp");
buffer_name = GetUniqueBufferName(current_dir, "temp");
} }
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);
@@ -618,10 +614,13 @@ BSet Open(Window *window, String path, String meta, bool set_active = true) {
ActiveWindowID = set.window->id; ActiveWindowID = set.window->id;
} }
if (IsDir(o.path)) { if (IsDir(o.path)) {
JumpGarbageBuffer(&set, GetUniqueBufferName(o.path, "temp", ".dirlisting")); CheckpointBeforeGoto(set.window);
Appendf(set.view, "..\n"); View *view = WindowOpenBufferView(set.window, o.path);
Buffer *buffer = GetBuffer(view->active_buffer);
ResetBuffer(buffer);
RawAppendf(buffer, "..\n");
for (FileIter it = IterateFiles(scratch, o.path); IsValid(it); Advance(&it)) { for (FileIter it = IterateFiles(scratch, o.path); IsValid(it); Advance(&it)) {
Appendf(set.view, "%S\n", it.filename); RawAppendf(buffer, "%S\n", it.filename);
} }
} else { } else {
CheckpointBeforeGoto(set.window); CheckpointBeforeGoto(set.window);
@@ -852,14 +851,11 @@ void Command_GotoBackward() {
void Command_GotoForward() { void Command_GotoForward() {
BSet main = GetBSet(LastActiveLayoutWindowID); BSet main = GetBSet(LastActiveLayoutWindowID);
GotoForward(main.window); GotoForward(main.window);
} RegisterCommand(Command_GotoForward, "mousex2"); } RegisterCommand(Command_GotoForward, "alt-shift-q | mousex2");
void Command_OpenUpFolder() { void Command_OpenUpFolder() {
BSet main = GetBSet(LastActiveLayoutWindowID); BSet main = GetBSet(LastActiveLayoutWindowID);
String name = ChopLastSlash(main.buffer->name); String name = ChopLastSlash(main.buffer->name);
if (EndsWith(main.buffer->name, "dirlisting")) {
name = ChopLastSlash(name);
}
Open(name); Open(name);
} RegisterCommand(Command_OpenUpFolder, "ctrl-period"); } RegisterCommand(Command_OpenUpFolder, "ctrl-period");

View File

@@ -157,5 +157,7 @@ RegisterVariable(Int, ConfigFontSize, 15);
RegisterVariable(Int, ConfigFontFilter, 0); RegisterVariable(Int, ConfigFontFilter, 0);
RegisterVariable(String, ConfigFont, "/home/krz/text_editor/package/CascadiaMono.ttf"); RegisterVariable(String, ConfigFont, "/home/krz/text_editor/package/CascadiaMono.ttf");
RegisterVariable(String, ConfigVCVarsall, "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat"); RegisterVariable(String, ConfigVCVarsall, "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat");
RegisterVariable(Float, ConfigUndoMergeTimeout, 0.3); RegisterVariable(Float, ConfigUndoMergeTimeWindow, 0.3);
RegisterVariable(Float, ConfigJumpHistoryMergeTimeWindow, 0.3);
RegisterVariable(Int, ConfigJumpHistorySize, 512);
RegisterVariable(String, ConfigInternetBrowser, "firefox"); RegisterVariable(String, ConfigInternetBrowser, "firefox");

View File

@@ -0,0 +1,34 @@
void RunRingBufferTest() {
// Scratch scratch;
// RingBuffer<int> buff = {};
// Init(scratch, &buff, 4);
// for (int i = 0; i < 4; i += 1) {
// Add(&buff, i);
// }
// for (int i = 0; i < 4; i += 1) {
// Assert(buff.data[i] == i);
// }
// Add(&buff, 4);
// Assert(buff.data[0] == 4);
// Add(&buff, 5);
// Assert(buff.data[1] == 5);
// Assert(buff.data[2] == 2);
// Assert(Pop(&buff) == 5);
// Assert(Pop(&buff) == 4);
// Assert(Pop(&buff) == 3);
// Assert(Pop(&buff) == 2);
// Assert(Pop(&buff) == 0);
// Assert(Pop(&buff) == 0);
// Assert(Pop(&buff) == 0);
// Assert(Pop(&buff) == 0);
// Assert(Pop(&buff) == 0);
// // Assert(Pop(&buff) == 2);
} RegisterFunction(&TestFunctions, RunRingBufferTest);

View File

@@ -39,10 +39,10 @@
#include "commands.cpp" #include "commands.cpp"
#include "commands_clipboard.cpp" #include "commands_clipboard.cpp"
#include "scratch.cpp"
#include "draw.cpp" #include "draw.cpp"
#include "test/tests.cpp" #include "test/tests.cpp"
#if OS_WASM #if OS_WASM
EM_JS(void, JS_SetMouseCursor, (const char *cursor_str), { EM_JS(void, JS_SetMouseCursor, (const char *cursor_str), {
document.getElementById("canvas").style.cursor = UTF8ToString(cursor_str); document.getElementById("canvas").style.cursor = UTF8ToString(cursor_str);
@@ -526,6 +526,12 @@ void Update(Event event) {
{ {
ProfileScope(WindowEndOfFrameVisibilityAndLastActive); ProfileScope(WindowEndOfFrameVisibilityAndLastActive);
For (Windows) { For (Windows) {
if (it->goto_history.len > ConfigJumpHistorySize) {
RemoveByIndex(&it->goto_history, 0);
}
if (it->goto_redo.len > ConfigJumpHistorySize) {
RemoveByIndex(&it->goto_redo, 0);
}
if (it->sync_visibility_with_focus) { if (it->sync_visibility_with_focus) {
if (it->id == ActiveWindowID) { if (it->id == ActiveWindowID) {
it->visible = true; it->visible = true;
@@ -642,6 +648,7 @@ extern char **environ;
int main(int argc, char **argv) int main(int argc, char **argv)
#endif #endif
{ {
InitOS((OSErrorReport *)printf);
#if _WIN32 #if _WIN32
int argc = __argc; int argc = __argc;
char **argv = __argv; char **argv = __argv;

View File

@@ -147,9 +147,6 @@ struct Register_Function {
struct Register_Command { Register_Command(Array<CommandData> *fucs, Function *function, String name, String binding) { if (StartsWith(name, "Command_")) name = Skip(name, 8); Add(fucs, {name, binding, function}); } }; struct Register_Command { Register_Command(Array<CommandData> *fucs, Function *function, String name, String binding) { if (StartsWith(name, "Command_")) name = Skip(name, 8); Add(fucs, {name, binding, function}); } };
#define RegisterCommand(name, binding) Register_Command RC__##name(&CommandFunctions, name, #name, binding) #define RegisterCommand(name, binding) Register_Command RC__##name(&CommandFunctions, name, #name, binding)
struct Register_Hook { Register_Hook(Array<PFunctionData> *functions, PFunction *function, String name) { if (StartsWith(name, "Hook_")) name = Skip(name, 5); Add(functions, {name, function}); } };
#define RegisterHook(functions, name) Register_Hook RC__##name(functions, (PFunction *)name, #name)
const int DIR_RIGHT = 0; const int DIR_RIGHT = 0;
const int DIR_LEFT = 1; const int DIR_LEFT = 1;
const int DIR_DOWN = 2; const int DIR_DOWN = 2;

View File

@@ -249,7 +249,7 @@ void CheckpointBeforeGoto(Window *window) {
GotoCrumb GetCrumb(Array<GotoCrumb> *cr) { GotoCrumb GetCrumb(Array<GotoCrumb> *cr) {
for (; cr->len;) { for (; cr->len;) {
GotoCrumb c = Pop(cr); GotoCrumb c = Pop(cr);
View *view = GetView(c.view_id); View *view = FindView(c.view_id, NULL);
if (view) { if (view) {
return c; return c;
} }
@@ -271,15 +271,15 @@ void GotoBackward(Window *window) {
if (window->goto_history.len) { if (window->goto_history.len) {
GotoCrumb *next = GetLast(window->goto_history); GotoCrumb *next = GetLast(window->goto_history);
if (c.time - next->time <= ConfigUndoMergeTimeout) { if (c.view_id == next->view_id && c.time - next->time <= ConfigJumpHistoryMergeTimeWindow) {
GotoBackward(window); GotoBackward(window);
} }
} }
} }
void GotoForward(Window *window) { void GotoForward(Window *window) {
if (window->goto_redo.len <= 0) return;
if (window->jump_history == false) return; if (window->jump_history == false) return;
if (window->goto_redo.len <= 0) return;
BSet set = GetBSet(window); BSet set = GetBSet(window);
Add(&window->goto_history, {set.view->id, set.view->carets[0], GetTimeSeconds()}); Add(&window->goto_history, {set.view->id, set.view->carets[0], GetTimeSeconds()});
@@ -291,7 +291,7 @@ void GotoForward(Window *window) {
if (window->goto_redo.len) { if (window->goto_redo.len) {
GotoCrumb *next = GetLast(window->goto_redo); GotoCrumb *next = GetLast(window->goto_redo);
if (c.time - next->time <= ConfigUndoMergeTimeout) { if (c.view_id == next->view_id && next->time - c.time <= ConfigJumpHistoryMergeTimeWindow) {
GotoForward(window); GotoForward(window);
} }
} }

View File

@@ -160,7 +160,7 @@ void Command_ShowBufferList() {
ActiveWindowID = command_bar.window->id; ActiveWindowID = command_bar.window->id;
ResetBuffer(command_bar.buffer); ResetBuffer(command_bar.buffer);
For (Buffers) { For (Buffers) {
if (it->special) { if (it->special || it->garbage) {
continue; continue;
} }
RawAppendf(command_bar.buffer, "\n%S", it->name); RawAppendf(command_bar.buffer, "\n%S", it->name);

View File

@@ -6,6 +6,7 @@ void DebugWindowInit() {
window->visible = false; window->visible = false;
window->z = 2; window->z = 2;
window->layout = false; window->layout = false;
window->jump_history = false;
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(WorkDir, "debug")); Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(WorkDir, "debug"));
DebugBufferID = buffer->id; DebugBufferID = buffer->id;

View File

@@ -15,6 +15,7 @@ void SearchWindowInit() {
window->layout = false; window->layout = false;
window->visible = false; window->visible = false;
window->lose_visibility_on_escape = true; window->lose_visibility_on_escape = true;
window->jump_history = false;
} }
void SearchWindowLayout(Rect2I *rect, Int wx, Int wy) { void SearchWindowLayout(Rect2I *rect, Int wx, Int wy) {
@@ -33,10 +34,19 @@ void SearchWindowLayout(Rect2I *rect, Int wx, Int wy) {
} }
void Command_Search() { void Command_Search() {
BSet main = GetBSet(ActiveWindowID);
String16 string = {};
if (main.view->carets.len == 1 && GetSize(main.view->carets[0]) > 0) {
string = GetString(main.buffer, main.view->carets[0].range);
}
BSet set = GetBSet(SearchWindowID); BSet set = GetBSet(SearchWindowID);
set.window->visible = true; set.window->visible = true;
ActiveWindowID = SearchWindowID; ActiveWindowID = SearchWindowID;
SelectEntireBuffer(set.view); SelectEntireBuffer(set.view);
if (string.len > 0) {
Replace(set.view, string);
SelectEntireBuffer(set.view);
}
} RegisterCommand(Command_Search, "ctrl-f"); } RegisterCommand(Command_Search, "ctrl-f");
void SearchWindowFindNext(bool forward = true) { void SearchWindowFindNext(bool forward = true) {