aboutsummaryrefslogtreecommitdiff
path: root/src/ecex.c
diff options
context:
space:
mode:
authorDavid Moc <personal@cdatgoose.org>2026-06-03 02:26:11 +0200
committerDavid Moc <personal@cdatgoose.org>2026-06-03 02:26:11 +0200
commitc6d44836fd8ed1442e01825cb0f9f97e7bf11515 (patch)
treed0e8cededdecafdf27476a744ac2d892d5a18620 /src/ecex.c
parentb68766967c86a6a789d65772f69f7f44939ebdf2 (diff)
Harden editor logging and packaging
Diffstat (limited to 'src/ecex.c')
-rw-r--r--src/ecex.c106
1 files changed, 106 insertions, 0 deletions
diff --git a/src/ecex.c b/src/ecex.c
index a40a792..f3b4980 100644
--- a/src/ecex.c
+++ b/src/ecex.c
@@ -33,6 +33,9 @@ extern int kill(pid_t pid, int sig);
#define ECEX_INITIAL_MODE_CAP 8
#define ECEX_INITIAL_MODE_KEYBIND_CAP 16
#define ECEX_INITIAL_HOOK_CAP 8
+#define ECEX_MESSAGES_BUFFER_NAME "*Messages*"
+#define ECEX_MESSAGES_MAX_BYTES (1024u * 1024u)
+#define ECEX_MESSAGES_TRIM_BYTES (256u * 1024u)
ecex_window_t *ecex_current_window(ecex_t *ed);
static void ecex_clear_command_hooks(ecex_t *ed);
@@ -41,6 +44,10 @@ static void ecex_clear_buffer_hooks(ecex_t *ed);
static void ecex_clear_completion_providers(ecex_t *ed);
static int ecex_complete_at_point_direction(ecex_t *ed, int direction);
static int ecex_indent_for_tab(ecex_t *ed);
+static int ecex_buffer_index_of(ecex_t *ed, buffer_t *buffer, size_t *out_index);
+static buffer_t *ecex_ensure_messages_buffer(ecex_t *ed);
+static int ecex_messages_append(ecex_t *ed, const char *prefix, const char *message);
+static void ecex_log_messages_sink(const char *line, int depth, void *userdata);
void *ecex_config_alloc(size_t size) {
@@ -769,10 +776,16 @@ static int cmd_file_browser_history_forward(ecex_t *ed) {
}
static int cmd_media_play_pause(ecex_t *ed) { return ecex_media_toggle_playback(ed); }
+static int cmd_messages(ecex_t *ed) {
+ buffer_t *buffer = ecex_ensure_messages_buffer(ed);
+ return buffer ? ecex_switch_buffer(ed, buffer->name) : ECEX_ERR;
+}
static int ecex_register_builtins(ecex_t *ed) {
ECEX_COMMAND("quit", cmd_quit);
ECEX_COMMAND("force-quit", cmd_force_quit);
+ ECEX_COMMAND("messages", cmd_messages);
+ ECEX_COMMAND("view-messages", cmd_messages);
ECEX_COMMAND("find-file", cmd_find_file);
ECEX_COMMAND("file-browser", cmd_file_browser);
ECEX_COMMAND("file-browser-here", cmd_file_browser_here);
@@ -1011,12 +1024,18 @@ ecex_t *ecex_new(void) {
}
ecex_buffer_set_major_mode_by_name(ed, scratch, "fundamental-mode");
+ if (!ecex_ensure_messages_buffer(ed)) {
+ ecex_free(ed);
+ return NULL;
+ }
+ ecex_log_set_sink(ecex_log_messages_sink, ed);
return ed;
}
void ecex_free(ecex_t *ed) {
if (!ed) return;
+ ecex_log_clear_sink(ed);
/* Buffers may hold renderer/animation callbacks and userdata destructors
* compiled by CCDJIT config modules. Run those destructors while their JIT
@@ -1109,6 +1128,91 @@ int ecex_add_buffer(ecex_t *ed, buffer_t *buffer) {
return ECEX_OK;
}
+static void ecex_messages_trim(buffer_t *buffer) {
+ if (!buffer || buffer->len <= ECEX_MESSAGES_MAX_BYTES) return;
+
+ size_t trim = buffer->len - ECEX_MESSAGES_MAX_BYTES + ECEX_MESSAGES_TRIM_BYTES;
+ if (trim > buffer->len) trim = buffer->len;
+ while (trim < buffer->len && buffer->data[trim] != '\n') trim++;
+ if (trim < buffer->len) trim++;
+
+ memmove(buffer->data, buffer->data + trim, buffer->len - trim + 1);
+ buffer->len -= trim;
+ buffer->point = buffer->len;
+ buffer->mark = 0;
+ buffer->mark_active = 0;
+ buffer->scroll_line = 0;
+ buffer->scroll_col = 0;
+}
+
+static buffer_t *ecex_ensure_messages_buffer(ecex_t *ed) {
+ if (!ed) return NULL;
+
+ buffer_t *buffer = ed->messages_buffer;
+ if (!buffer || ecex_buffer_index_of(ed, buffer, NULL) != ECEX_OK) {
+ buffer = ecex_find_buffer(ed, ECEX_MESSAGES_BUFFER_NAME);
+ }
+
+ if (!buffer) {
+ buffer = ecex_create_buffer(ed, ECEX_MESSAGES_BUFFER_NAME, NULL, 0);
+ if (!buffer) return NULL;
+ }
+
+ ed->messages_buffer = buffer;
+ buffer_set_interactive(buffer, 1);
+ if (buffer->major_mode == 0) {
+ ecex_buffer_set_major_mode_by_name(ed, buffer, "special-mode");
+ }
+ buffer->read_only = 1;
+ buffer->modified = 0;
+ return buffer;
+}
+
+static int ecex_messages_append(ecex_t *ed, const char *prefix, const char *message) {
+ if (!ed || ed->messages_append_active) return ECEX_ERR;
+
+ buffer_t *buffer = ecex_ensure_messages_buffer(ed);
+ if (!buffer) return ECEX_ERR;
+
+ int old_read_only = buffer->read_only;
+ int old_undo_disabled = buffer->undo_disabled;
+ ed->messages_append_active = 1;
+ buffer->read_only = 0;
+ buffer->undo_disabled = 1;
+
+ int result = ECEX_OK;
+ if (prefix && prefix[0]) result = buffer_append(buffer, prefix);
+ if (result == ECEX_OK) result = buffer_append(buffer, message ? message : "");
+ if (result == ECEX_OK) result = buffer_append(buffer, "\n");
+ if (result == ECEX_OK) ecex_messages_trim(buffer);
+
+ buffer->read_only = old_read_only;
+ buffer->undo_disabled = old_undo_disabled;
+ buffer->modified = 0;
+ buffer_clear_undo(buffer);
+ ed->messages_append_active = 0;
+ ecex_mark_ui_changed(ed);
+ return result;
+}
+
+static void ecex_log_messages_sink(const char *line, int depth, void *userdata) {
+ ecex_t *ed = (ecex_t *)userdata;
+ char prefix[64];
+ size_t pos = 0;
+
+ if (!ed) return;
+ int written = snprintf(prefix, sizeof(prefix), "ecex-log: ");
+ if (written < 0) return;
+ pos = (size_t)written;
+ if (pos >= sizeof(prefix)) pos = sizeof(prefix) - 1;
+ for (int i = 0; i < depth && pos + 2 < sizeof(prefix); i++) {
+ prefix[pos++] = ' ';
+ prefix[pos++] = ' ';
+ }
+ prefix[pos < sizeof(prefix) ? pos : sizeof(prefix) - 1] = '\0';
+ ecex_messages_append(ed, prefix, line);
+}
+
buffer_t *ecex_create_buffer(ecex_t *ed,
const char *name,
const char *path,
@@ -1382,6 +1486,7 @@ static int ecex_kill_buffer_impl(ecex_t *ed, const char *name, int force) {
buffer_t *victim = ed->buffers[index];
if (ed->previous_buffer == victim) ed->previous_buffer = NULL;
+ if (ed->messages_buffer == victim) ed->messages_buffer = NULL;
if (!force && victim->modified && !victim->read_only) {
fprintf(stderr,
"ecex: refusing to kill modified buffer '%s'; save it or use force-kill-buffer.\n",
@@ -2100,6 +2205,7 @@ void ecex_message(ecex_t *ed, const char *message) {
if (!ed) return;
snprintf(ed->message, sizeof(ed->message), "%s", message ? message : "");
ed->message_revision++;
+ ecex_messages_append(ed, "message: ", message ? message : "");
ecex_mark_ui_changed(ed);
}