From a15cb041654ae307add0b998b526c87c3f42bf5f Mon Sep 17 00:00:00 2001 From: David Moc Date: Tue, 2 Jun 2026 13:50:21 +0200 Subject: Add plugin hooks and mode plugins --- src/config.c | 195 +++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 123 insertions(+), 72 deletions(-) (limited to 'src/config.c') diff --git a/src/config.c b/src/config.c index 1b0b5ed..e9bba5d 100644 --- a/src/config.c +++ b/src/config.c @@ -5,7 +5,6 @@ #include "common.h" #include "ecex.h" #include "eval.h" -#include "util.h" #include "path.h" #include @@ -65,46 +64,56 @@ static const host_symbol_t host_symbols[] = { HOST_SYMBOL(ecex_config_free), HOST_SYMBOL(ecex_time_seconds), HOST_SYMBOL(ecex_log), + HOST_SYMBOL(ecex_logf), + HOST_SYMBOL(ecex_log_group_begin), + HOST_SYMBOL(ecex_log_group_end), HOST_SYMBOL(ecex_log_int), HOST_SYMBOL(ecex_log_double), HOST_SYMBOL(ecex_log_ptr), + HOST_SYMBOL(ecex_log_flush), HOST_SYMBOL(ecex_mem_zero), HOST_SYMBOL(ecex_i32_get), HOST_SYMBOL(ecex_i32_set), HOST_SYMBOL(ecex_prng_next_bounded), HOST_SYMBOL(ecex_random_bounded), - HOST_SYMBOL(ecex_tetris_shape_cell), - HOST_SYMBOL(ecex_var_get), - HOST_SYMBOL(ecex_var_get_or_alloc), - HOST_SYMBOL(ecex_var_bind_static), - HOST_SYMBOL(ecex_var_free), - HOST_SYMBOL(ecex_var_free_owner), - HOST_SYMBOL(ecex_var_i32_get), - HOST_SYMBOL(ecex_var_i32_set), - HOST_SYMBOL(ecex_var_i32), - HOST_SYMBOL(ecex_var_i32_set_scalar), - HOST_SYMBOL(ecex_object_alloc), - HOST_SYMBOL(ecex_object_calloc), - HOST_SYMBOL(ecex_object_free), - HOST_SYMBOL(ecex_object_valid), - HOST_SYMBOL(ecex_object_i32_get), - HOST_SYMBOL(ecex_object_i32_set), - HOST_SYMBOL(ecex_object_ptr_get), - HOST_SYMBOL(ecex_object_ptr_set), - HOST_SYMBOL(ecex_text_set), - HOST_SYMBOL(ecex_text_set_buffer_title), - HOST_SYMBOL(ecex_text_free), - HOST_SYMBOL(ecex_text_free_owner), + HOST_SYMBOL(ecex_plugin_register), + HOST_SYMBOL(ecex_plugin_require), + HOST_SYMBOL(ecex_plugin_find), + HOST_SYMBOL(ecex_plugin_id), + HOST_SYMBOL(ecex_plugin_slot_alloc), + HOST_SYMBOL(ecex_plugin_slot_get), + HOST_SYMBOL(ecex_plugin_slot_free), + HOST_SYMBOL(ecex_plugin_slot_set_export_flags), + HOST_SYMBOL(ecex_plugin_slot_read_exported), + HOST_SYMBOL(ecex_plugin_slot_i32_get), + HOST_SYMBOL(ecex_plugin_slot_i32_set), + HOST_SYMBOL(ecex_plugin_slot_i32_get_2d), + HOST_SYMBOL(ecex_plugin_slot_i32_set_2d), + HOST_SYMBOL(ecex_plugin_slot_i32_get_scalar), + HOST_SYMBOL(ecex_plugin_slot_i32_set_scalar), + HOST_SYMBOL(ecex_plugin_object_alloc), + HOST_SYMBOL(ecex_plugin_object_calloc), + HOST_SYMBOL(ecex_plugin_object_free), + HOST_SYMBOL(ecex_plugin_object_valid), + HOST_SYMBOL(ecex_plugin_object_i32_get), + HOST_SYMBOL(ecex_plugin_object_i32_set), + HOST_SYMBOL(ecex_plugin_object_ptr_get), + HOST_SYMBOL(ecex_plugin_object_ptr_set), + HOST_SYMBOL(ecex_plugin_text_set), + HOST_SYMBOL(ecex_plugin_text_set_from_buffer_title), + HOST_SYMBOL(ecex_plugin_text_free), + HOST_SYMBOL(ecex_plugin_text_free_all), + HOST_SYMBOL(ecex_plugin_text_get_drawable), HOST_SYMBOL(ecex_buffer_text_len), - HOST_SYMBOL(ecex_buffer_scroll_line), - HOST_SYMBOL(ecex_buffer_line_count_i), - HOST_SYMBOL(ecex_buffer_line_copy), - HOST_SYMBOL(ecex_markdown_draw_line_from_buffer_i), - HOST_SYMBOL(ecex_markdown_body_y_i), - HOST_SYMBOL(ecex_draw_context_height_i), - HOST_SYMBOL(ecex_draw_context_line_height_i), - HOST_SYMBOL(ecex_register_file_handler), - HOST_SYMBOL(ecex_run_file_handlers), + HOST_SYMBOL(ecex_buffer_scroll_line_index), + HOST_SYMBOL(ecex_buffer_line_count_int), + HOST_SYMBOL(ecex_buffer_line_copy_text), + HOST_SYMBOL(ecex_markdown_draw_buffer_line_i), + HOST_SYMBOL(ecex_markdown_body_y_px), + HOST_SYMBOL(ecex_draw_context_height_px), + HOST_SYMBOL(ecex_draw_context_line_height_px), + HOST_SYMBOL(ecex_plugin_file_handler_register), + HOST_SYMBOL(ecex_plugin_file_handlers_run), HOST_SYMBOL(ecex_config_register_commands), HOST_SYMBOL(ecex_config_bind_keys), @@ -113,6 +122,27 @@ static const host_symbol_t host_symbols[] = { HOST_SYMBOL(ecex_apply_theme), HOST_SYMBOL(ecex_register_command), HOST_SYMBOL(ecex_execute_command), + HOST_SYMBOL(ecex_add_command_hook), + HOST_SYMBOL(ecex_remove_command_hook), + HOST_SYMBOL(ecex_add_prefix_hook), + HOST_SYMBOL(ecex_remove_prefix_hook), + HOST_SYMBOL(ecex_notify_prefix_hooks), + HOST_SYMBOL(ecex_add_buffer_hook), + HOST_SYMBOL(ecex_remove_buffer_hook), + HOST_SYMBOL(ecex_notify_buffer_hooks), + HOST_SYMBOL(ecex_message), + HOST_SYMBOL(ecex_dependency_available), + HOST_SYMBOL(ecex_plugin_require_dependency), + HOST_SYMBOL(ecex_add_completion_provider), + HOST_SYMBOL(ecex_add_word_completion_provider), + HOST_SYMBOL(ecex_define_word_completion_provider), + HOST_SYMBOL(ecex_completion_provider_add_word), + HOST_SYMBOL(ecex_completion_provider_add_words), + HOST_SYMBOL(ecex_completion_provider_set_detail), + HOST_SYMBOL(ecex_add_clangd_completion_provider), + HOST_SYMBOL(ecex_remove_completion_provider), + HOST_SYMBOL(ecex_buffer_identifier_prefix), + HOST_SYMBOL(ecex_complete_at_point), HOST_SYMBOL(ecex_bind_key), HOST_SYMBOL(ecex_lookup_key), HOST_SYMBOL(ecex_define_major_mode), @@ -124,6 +154,7 @@ static const host_symbol_t host_symbols[] = { HOST_SYMBOL(ecex_bind_mode_key), HOST_SYMBOL(ecex_lookup_key_for_buffer), HOST_SYMBOL(ecex_key_sequence_has_prefix_for_buffer), + HOST_SYMBOL(ecex_describe_key_prefix), HOST_SYMBOL(ecex_auto_set_major_mode), HOST_SYMBOL(ecex_list_commands), HOST_SYMBOL(ecex_list_buffers), @@ -153,12 +184,13 @@ static const host_symbol_t host_symbols[] = { HOST_SYMBOL(ecex_draw_text), HOST_SYMBOL(ecex_draw_text_aligned), HOST_SYMBOL(ecex_draw_text_width), - HOST_SYMBOL(ecex_draw_color_rgba8), + HOST_SYMBOL(ecex_draw_color_rgba8_i), HOST_SYMBOL(ecex_draw_rect_i), HOST_SYMBOL(ecex_draw_rect_outline_i), HOST_SYMBOL(ecex_draw_line_i), HOST_SYMBOL(ecex_draw_text_i), - HOST_SYMBOL(ecex_draw_text_id_i), + HOST_SYMBOL(ecex_draw_plugin_text_i), + HOST_SYMBOL(ecex_draw_plugin_text_rect_i), HOST_SYMBOL(ecex_draw_markdown_canvas_i), HOST_SYMBOL(ecex_draw_markdown_text_i), HOST_SYMBOL(ecex_draw_markdown_canvas_auto_i), @@ -170,6 +202,15 @@ static const host_symbol_t host_symbols[] = { HOST_SYMBOL(ecex_find_file), HOST_SYMBOL(ecex_save_current_buffer), HOST_SYMBOL(ecex_write_current_buffer), + HOST_SYMBOL(ecex_compile), + HOST_SYMBOL(ecex_grep), + HOST_SYMBOL(ecex_rerun_compile), + HOST_SYMBOL(ecex_rerun_grep), + HOST_SYMBOL(ecex_next_interactive_action), + HOST_SYMBOL(ecex_previous_interactive_action), + HOST_SYMBOL(ecex_indent_line_to), + HOST_SYMBOL(ecex_comment_region), + HOST_SYMBOL(ecex_uncomment_region), HOST_SYMBOL(ecex_request_prompt), HOST_SYMBOL(ecex_clear_prompt_request), HOST_SYMBOL(ecex_complete_command), @@ -308,32 +349,48 @@ int ecex_add_ccdjit_include_paths(ccdjit_context *ctx) { return ECEX_OK; } -static char *make_config_source_with_forced_main(const char *path) { - size_t source_size = 0; - char *source = ecex_read_entire_file(path, &source_size); - if (!source) return NULL; - - const char *forced_main = - "\n" - "\n" - "int main(int argc, char **argv) {\n" - " (void)argc;\n" - " (void)argv;\n" - " return ecex_config_init((ecex_t *)0);\n" - "}\n"; - - size_t forced_main_len = strlen(forced_main); - char *combined = malloc(source_size + forced_main_len + 1); - if (!combined) { - free(source); - return NULL; - } +static int ccdjit_type_is_int(ccdjit_type_info type) { + return type.has_type && + type.base == CCDJIT_TYPE_INT && + type.pointer_level == 0; +} + +static int ccdjit_type_is_pointer(ccdjit_type_info type) { + return type.has_type && type.pointer_level == 1; +} - memcpy(combined, source, source_size); - memcpy(combined + source_size, forced_main, forced_main_len + 1); +static ecex_config_init_fn ecex_config_init_symbol(ccdjit_context *ctx, + ccdjit_module *module, + const char *path) { + void *symbol = ccdjit_module_symbol(module, "ecex_config_init"); + if (!symbol) return NULL; + + ccdjit_symbol_info info; + memset(&info, 0, sizeof(info)); + if (ccdjit_module_symbol_info(module, "ecex_config_init", &info) == 0 && + info.has_signature) { + ccdjit_type_info param; + memset(¶m, 0, sizeof(param)); + + if (info.kind != CCDJIT_SYMBOL_FUNCTION || + !ccdjit_type_is_int(info.return_type) || + info.parameter_count != 1 || + info.is_variadic || + ccdjit_module_function_parameter_type(module, "ecex_config_init", 0, ¶m) != 0 || + !ccdjit_type_is_pointer(param)) { + fprintf(stderr, + "ecex: invalid config signature in %s; expected int ecex_config_init(ecex_t *ed)\n", + path ? path : ""); + return NULL; + } + } else if (ctx) { + const ccdjit_diagnostic *diag = ccdjit_context_last_error(ctx); + if (diag && diag->code != CCDJIT_DIAG_NONE) { + ecex_print_ccdjit_error(ctx); + } + } - free(source); - return combined; + return (ecex_config_init_fn)symbol; } int ecex_load_c_config(ecex_t *ed, const char *path) { @@ -350,26 +407,15 @@ int ecex_load_c_config(ecex_t *ed, const char *path) { return ECEX_ERR; } - char *source = make_config_source_with_forced_main(path); - if (!source) { - fprintf(stderr, "ecex: failed to read or prepare config: %s\n", path); - ccdjit_context_free(ctx); - return ECEX_ERR; - } - ccdjit_module *module = NULL; - if (ccdjit_compile_string(ctx, source, path, &module) != 0) { + if (ccdjit_compile_file_module(ctx, path, &module) != 0) { fprintf(stderr, "ecex: failed to compile config: %s\n", path); ecex_print_ccdjit_error(ctx); - free(source); ccdjit_context_free(ctx); return ECEX_ERR; } - free(source); - - ecex_config_init_fn init = - (ecex_config_init_fn)ccdjit_module_symbol(module, "ecex_config_init"); + ecex_config_init_fn init = ecex_config_init_symbol(ctx, module, path); if (!init) { fprintf(stderr, "ecex: config missing function: ecex_config_init\n"); @@ -382,7 +428,12 @@ int ecex_load_c_config(ecex_t *ed, const char *path) { int result = init(ed); if (result != 0) { fprintf(stderr, "ecex: config init failed with code: %d\n", result); - ccdjit_module_free(module); + /* Config init may have registered hooks/renderers before returning an + * error. Keep the module alive so rollback can call any JIT-owned + * destructors safely. */ + if (ecex_keep_jit_module(ed, module) != ECEX_OK) { + fprintf(stderr, "ecex: failed to retain failed config module; leaving it mapped for cleanup safety\n"); + } ccdjit_context_free(ctx); return ECEX_ERR; } -- cgit v1.2.3