diff options
Diffstat (limited to 'src/ecex.c')
| -rw-r--r-- | src/ecex.c | 106 |
1 files changed, 106 insertions, 0 deletions
@@ -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); } |
