#ifndef ECEX_H #define ECEX_H #include "types.h" #include "buffers.h" #include "eval.h" #include "path.h" #include "media.h" #include "plugin.h" typedef struct ecex_config_command { const char *name; ecex_command_fn fn; } ecex_config_command_t; typedef struct ecex_config_keybind { const char *key; const char *command; } ecex_config_keybind_t; typedef struct ecex_config_mode_keybind { const char *mode; const char *key; const char *command; } ecex_config_mode_keybind_t; #define ECEX_COMPLETION_DEFAULT 0 #define ECEX_COMPLETION_ED_ARROW 1 ecex_t *ecex_new(void); void ecex_free(ecex_t *ed); /* Small runtime services exported to JIT configs/plugins. Prefer these over * calling libc directly from config code; the tiny JIT resolver is intentionally * conservative and host-exported wrappers are more reliable. */ void *ecex_config_alloc(size_t size); void *ecex_config_calloc(size_t count, size_t size); void ecex_config_free(void *ptr); double ecex_time_seconds(void); void ecex_log(const char *message); void ecex_logf(const char *fmt, ...); void ecex_log_group_begin(const char *message); void ecex_log_group_end(const char *message); void ecex_log_int(const char *message, int value); void ecex_log_double(const char *message, double value); void ecex_log_ptr(const char *message, const void *ptr); void ecex_log_flush(void); void ecex_log_set_sink(ecex_log_sink_fn fn, void *userdata); void ecex_log_clear_sink(void *userdata); void ecex_mem_zero(void *ptr, size_t size); int ecex_i32_get(const int *items, size_t index); void ecex_i32_set(int *items, size_t index, int value); int ecex_prng_next_bounded(unsigned int *state, int bound); int ecex_random_bounded(int bound); /* The plugin runtime owns long-lived JIT/plugin state. Plugins must register a * stable id and then allocate slots, objects, text, and file handlers through * the returned opaque handle. Slots are private unless explicitly exported, * and cross-plugin reads are copy-only. */ int ecex_buffer_text_len(buffer_t *buffer); int ecex_buffer_scroll_line_index(buffer_t *buffer); int ecex_buffer_line_count_int(buffer_t *buffer); int ecex_buffer_line_copy_text(buffer_t *buffer, int line, char *out, int out_cap); int ecex_markdown_draw_buffer_line_i(ecex_draw_context_t *ctx, void *owner, buffer_t *buffer, int line, int y, int in_code); int ecex_markdown_body_y_px(ecex_draw_context_t *ctx); int ecex_draw_context_height_px(ecex_draw_context_t *ctx); int ecex_draw_context_line_height_px(ecex_draw_context_t *ctx); int ecex_run_plugin_file_handlers(ecex_t *ed, buffer_t *buffer); int ecex_reserve_buffers(ecex_t *ed, size_t needed); int ecex_add_buffer(ecex_t *ed, buffer_t *buffer); buffer_t *ecex_create_buffer(ecex_t *ed, const char *name, const char *path, int read_only); buffer_t *ecex_find_buffer(ecex_t *ed, const char *name); int ecex_switch_buffer(ecex_t *ed, const char *name); buffer_t *ecex_current_buffer(ecex_t *ed); buffer_t *ecex_other_buffer(ecex_t *ed); ecex_window_t *ecex_current_window(ecex_t *ed); size_t ecex_window_count(ecex_t *ed); int ecex_sync_current_buffer(ecex_t *ed); int ecex_split_window_vertically(ecex_t *ed); int ecex_split_window_horizontally(ecex_t *ed); int ecex_other_window(ecex_t *ed); int ecex_previous_window(ecex_t *ed); int ecex_delete_window(ecex_t *ed); int ecex_delete_other_windows(ecex_t *ed); int ecex_balance_windows(ecex_t *ed); int ecex_next_buffer(ecex_t *ed); int ecex_previous_buffer(ecex_t *ed); int ecex_kill_buffer(ecex_t *ed, const char *name); int ecex_kill_buffer_force(ecex_t *ed, const char *name); int ecex_has_modified_buffers(ecex_t *ed); int ecex_validate_bindings(ecex_t *ed); int ecex_keep_jit_module(ecex_t *ed, void *module); int ecex_set_config_path(ecex_t *ed, const char *path); const char *ecex_config_path(ecex_t *ed); int ecex_reload_config(ecex_t *ed); int ecex_config_register_commands(ecex_t *ed, const ecex_config_command_t *commands, size_t count); int ecex_config_bind_keys(ecex_t *ed, const ecex_config_keybind_t *bindings, size_t count); int ecex_config_bind_mode_keys(ecex_t *ed, const ecex_config_mode_keybind_t *bindings, size_t count); int ecex_config_define_modes(ecex_t *ed, const char *const *modes, size_t count); int ecex_apply_theme(ecex_t *ed, const ecex_theme_t *theme); int ecex_register_command(ecex_t *ed, const char *name, ecex_command_fn fn); int ecex_execute_command(ecex_t *ed, const char *name); int ecex_add_command_hook(ecex_t *ed, const char *name, ecex_command_hook_fn fn, void *userdata, ecex_hook_free_fn free_fn); int ecex_remove_command_hook(ecex_t *ed, const char *name); int ecex_add_prefix_hook(ecex_t *ed, const char *name, ecex_prefix_hook_fn fn, void *userdata, ecex_hook_free_fn free_fn); int ecex_remove_prefix_hook(ecex_t *ed, const char *name); void ecex_notify_prefix_hooks(ecex_t *ed, const char *prefix, int event); int ecex_add_buffer_hook(ecex_t *ed, const char *name, ecex_buffer_hook_fn fn, void *userdata, ecex_hook_free_fn free_fn); int ecex_remove_buffer_hook(ecex_t *ed, const char *name); void ecex_notify_buffer_hooks(ecex_t *ed, buffer_t *buffer, int event); void ecex_message(ecex_t *ed, const char *message); int ecex_dependency_available(const char *program); int ecex_plugin_require_dependency(ecex_t *ed, const char *plugin_id, const char *program); int ecex_add_completion_provider(ecex_t *ed, const char *name, const char *mode_name, ecex_completion_provider_fn fn, void *userdata, ecex_hook_free_fn free_fn); int ecex_add_word_completion_provider(ecex_t *ed, const char *name, const char *mode_name, const char *const *words, size_t word_count, int flags); int ecex_define_word_completion_provider(ecex_t *ed, const char *name, const char *mode_name, int flags); int ecex_completion_provider_add_word(ecex_t *ed, const char *name, const char *word); int ecex_completion_provider_add_word_detail(ecex_t *ed, const char *name, const char *word, const char *detail); int ecex_completion_provider_add_words(ecex_t *ed, const char *name, const char *words); int ecex_completion_provider_add_entries(ecex_t *ed, const char *name, const char *entries); int ecex_completion_provider_set_detail(ecex_t *ed, const char *name, const char *detail); int ecex_add_clangd_completion_provider(ecex_t *ed, const char *name, const char *mode_name); int ecex_remove_completion_provider(ecex_t *ed, const char *name); int ecex_buffer_identifier_prefix(buffer_t *buffer, char *out, size_t out_size); int ecex_complete_at_point(ecex_t *ed); void ecex_set_clipboard_callbacks(ecex_t *ed, ecex_clipboard_get_fn get_fn, ecex_clipboard_set_fn set_fn, void *userdata); const char *ecex_clipboard_get(ecex_t *ed); int ecex_clipboard_set(ecex_t *ed, const char *text); int ecex_bind_key(ecex_t *ed, const char *key, const char *command); const char *ecex_lookup_key(ecex_t *ed, const char *key); int ecex_define_major_mode(ecex_t *ed, const char *name); int ecex_major_mode_by_name(ecex_t *ed, const char *name); const char *ecex_major_mode_name(ecex_t *ed, int mode); int ecex_buffer_set_major_mode(buffer_t *buffer, int mode); int ecex_buffer_set_major_mode_by_name(ecex_t *ed, buffer_t *buffer, const char *name); const char *ecex_buffer_major_mode_name(ecex_t *ed, buffer_t *buffer); int ecex_bind_mode_key(ecex_t *ed, const char *mode_name, const char *key, const char *command); const char *ecex_lookup_key_for_buffer(ecex_t *ed, buffer_t *buffer, const char *key); int ecex_key_sequence_has_prefix_for_buffer(ecex_t *ed, buffer_t *buffer, const char *prefix); int ecex_describe_key_prefix(ecex_t *ed, buffer_t *buffer, const char *prefix, char *out, size_t out_size, size_t max_items); int ecex_auto_set_major_mode(ecex_t *ed, buffer_t *buffer); int ecex_list_commands(ecex_t *ed); int ecex_list_buffers(ecex_t *ed); buffer_t *ecex_create_interactive_buffer(ecex_t *ed, const char *name); int ecex_interactive_append_line(ecex_t *ed, buffer_t *buffer, const char *text, ecex_interactive_line_fn fn, const char *payload, void *userdata); int ecex_interactive_activate_current_line(ecex_t *ed); /* * Attach an immediate-mode renderer to a buffer. The callback runs while the * buffer's window is being drawn and may call ecex_draw_* functions with * coordinates local to that window. Pass ECEX_RENDER_REPLACE_CONTENT to draw a * fully custom buffer; pass ECEX_RENDER_OVERLAY to draw on top of normal text. */ int ecex_buffer_set_renderer(buffer_t *buffer, ecex_buffer_render_fn fn, void *userdata, ecex_buffer_userdata_free_fn free_fn, int flags); int ecex_buffer_clear_renderer(buffer_t *buffer); int ecex_buffer_has_renderer(buffer_t *buffer); void *ecex_buffer_renderer_userdata(buffer_t *buffer); /* * Optional mouse/pointer input for rendered buffers. Coordinates are integer * pixels local to the buffer window. Return nonzero from the callback when the * event changes state and should trigger a redraw. Prefer this host-dispatched * path over reading windowing-system globals from a plugin. */ int ecex_buffer_set_mouse_handler(buffer_t *buffer, ecex_buffer_mouse_fn fn, void *userdata, ecex_buffer_userdata_free_fn free_fn); int ecex_buffer_clear_mouse_handler(buffer_t *buffer); int ecex_buffer_has_mouse_handler(buffer_t *buffer); void *ecex_buffer_mouse_userdata(buffer_t *buffer); /* * Host-driven animation for rendered buffers. The editor calls tick_fn at up to * fps while the main loop is alive; returning nonzero marks the frame dirty and * triggers a redraw. Use this instead of timers, sleep, clock calls, or threads * in CCDJIT plugins. fps <= 0 defaults to 60; values above 240 are clamped. */ int ecex_buffer_set_animation(buffer_t *buffer, ecex_buffer_tick_fn fn, void *userdata, ecex_buffer_userdata_free_fn free_fn, double fps); int ecex_buffer_set_animation_ms(buffer_t *buffer, ecex_buffer_tick_ms_fn fn, void *userdata, ecex_buffer_userdata_free_fn free_fn, int fps); int ecex_buffer_clear_animation(buffer_t *buffer); int ecex_buffer_is_animating(buffer_t *buffer); void *ecex_buffer_animation_userdata(buffer_t *buffer); int ecex_tick_animations(ecex_t *ed, double now_seconds); int ecex_buffer_replace_text(buffer_t *buffer, const char *text); void ecex_buffer_set_modified(buffer_t *buffer, int modified); /* Drawing helpers. x/y are local to the buffer window; ecex_draw_text uses * top-left coordinates. ecex_draw_rgba expects tightly packed RGBA8 pixels. */ void ecex_draw_set_color(ecex_draw_context_t *ctx, float r, float g, float b, float a); void ecex_draw_rect(ecex_draw_context_t *ctx, float x, float y, float w, float h); void ecex_draw_rect_outline(ecex_draw_context_t *ctx, float x, float y, float w, float h, float thickness); void ecex_draw_line(ecex_draw_context_t *ctx, float x1, float y1, float x2, float y2, float thickness); void ecex_draw_text(ecex_draw_context_t *ctx, float x, float y, const char *text); void ecex_draw_text_aligned(ecex_draw_context_t *ctx, float x, float y, float w, const char *text, int align); float ecex_draw_text_width(ecex_draw_context_t *ctx, const char *text); void ecex_draw_rgba(ecex_draw_context_t *ctx, float x, float y, float w, float h, const unsigned char *rgba, int image_w, int image_h); /* CCDJIT-safe integer drawing wrappers. Prefer these from plugins when the * JIT ABI is known to be fragile with float/double arguments. Colors are * 0..255 RGBA and coordinates are integer pixels local to the buffer window. */ void ecex_draw_color_rgba8_i(ecex_draw_context_t *ctx, int r, int g, int b, int a); void ecex_draw_rect_i(ecex_draw_context_t *ctx, int x, int y, int w, int h); void ecex_draw_rect_outline_i(ecex_draw_context_t *ctx, int x, int y, int w, int h, int thickness); void ecex_draw_line_i(ecex_draw_context_t *ctx, int x1, int y1, int x2, int y2, int thickness); void ecex_draw_text_i(ecex_draw_context_t *ctx, int x, int y, const char *text); void ecex_draw_plugin_text_i(ecex_draw_context_t *ctx, void *owner, int id, int x, int y); void ecex_draw_plugin_text_rect_i(ecex_draw_context_t *ctx, void *owner, int id, int x, int y, int w, int h, int padding, unsigned int bg_rgba, unsigned int fg_rgba); void ecex_draw_markdown_canvas_i(ecex_draw_context_t *ctx, void *owner, int title_id, int x, int y, int w, int line_h); void ecex_draw_markdown_text_i(ecex_draw_context_t *ctx, void *owner, int text_id, int x, int y, int w, int line_h, int style); /* Lower-arity Markdown helpers preferred by CCDJIT plugins. Some JIT call * paths are fragile with 7+ arguments, so these derive layout from ctx. */ void ecex_draw_markdown_canvas_auto_i(ecex_draw_context_t *ctx, void *owner, int title_id); void ecex_draw_markdown_line_auto_i(ecex_draw_context_t *ctx, void *owner, int text_id, int y, int style); /* CCDJIT-safe host-owned text helpers. Plugins pass integer ids instead of * pointers to JIT-owned string literals/stack buffers. */ void ecex_draw_label_i(ecex_draw_context_t *ctx, int x, int y, int label_id); void ecex_draw_stat_i(ecex_draw_context_t *ctx, int x, int y, int label_id, int value); void ecex_draw_tetris_preview_i(ecex_draw_context_t *ctx, int piece, int x, int y, int cell, int alpha); int ecex_find_file(ecex_t *ed, const char *path); int ecex_save_current_buffer(ecex_t *ed); int ecex_write_current_buffer(ecex_t *ed, const char *path); int ecex_compile(ecex_t *ed, const char *command); int ecex_grep(ecex_t *ed, const char *command); int ecex_rerun_compile(ecex_t *ed); int ecex_rerun_grep(ecex_t *ed); int ecex_next_interactive_action(ecex_t *ed); int ecex_previous_interactive_action(ecex_t *ed); int ecex_indent_line_to(buffer_t *buffer, int target_cols); int ecex_comment_region(ecex_t *ed); int ecex_uncomment_region(ecex_t *ed); void ecex_request_prompt(ecex_t *ed, ecex_prompt_request_t request, const char *message); void ecex_clear_prompt_request(ecex_t *ed); const char *ecex_complete_command(ecex_t *ed, const char *query); int ecex_set_font(ecex_t *ed, const char *path); float ecex_get_font_size(ecex_t *ed); int ecex_set_font_size(ecex_t *ed, float size); int ecex_adjust_font_size(ecex_t *ed, float delta); void ecex_set_bg_color(ecex_t *ed, float r, float g, float b); void ecex_set_fg_color(ecex_t *ed, float r, float g, float b); void ecex_set_status_bg_color(ecex_t *ed, float r, float g, float b); void ecex_set_status_fg_color(ecex_t *ed, float r, float g, float b); void ecex_set_status_border_color(ecex_t *ed, float r, float g, float b); void ecex_set_cursor_color(ecex_t *ed, float r, float g, float b); void ecex_set_region_bg_color(ecex_t *ed, float r, float g, float b); void ecex_set_minibuffer_bg_color(ecex_t *ed, float r, float g, float b); void ecex_set_minibuffer_fg_color(ecex_t *ed, float r, float g, float b); void ecex_set_completion_fg_color(ecex_t *ed, float r, float g, float b); void ecex_set_completion_enabled(ecex_t *ed, int enabled); void ecex_set_interactive_highlight_bg_color(ecex_t *ed, float r, float g, float b); void ecex_set_interactive_highlight_fg_color(ecex_t *ed, float r, float g, float b); void ecex_set_current_line_bg_color(ecex_t *ed, float r, float g, float b); void ecex_set_search_bg_color(ecex_t *ed, float r, float g, float b); void ecex_set_line_numbers_enabled(ecex_t *ed, int enabled); void ecex_set_current_line_enabled(ecex_t *ed, int enabled); /* * Small config/plugin convenience layer. * * Config files are still plain C and must export ecex_config_init(ecex_t *ed), * but these macros make them safer to write: every registration/bind operation * is checked and failures abort config loading instead of being ignored. */ #define ECEX_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0])) #define ECEX_RGB8(r, g, b) ((float)(r) / 255.0f), ((float)(g) / 255.0f), ((float)(b) / 255.0f) #define ECEX_CONFIG_BEGIN int ecex_config_init(ecex_t *ed) { #define ECEX_CONFIG_END return 0; } #define ECEX_PLUGIN_BEGIN(name, id) int name(ecex_t *ed) { \ ecex_plugin_t *plugin = ecex_plugin_require(ed, (id), ECEX_PLUGIN_API_VERSION); \ if (!plugin) return -1; #define ECEX_PLUGIN_END return 0; } #define ECEX_CONFIG_TRY(expr) \ do { \ if ((expr) != 0) return -1; \ } while (0) #define ECEX_CONFIG_COMMAND(name, fn) ECEX_CONFIG_TRY(ecex_register_command(ed, (name), (fn))) #define ECEX_CONFIG_BIND(key, command) ECEX_CONFIG_TRY(ecex_bind_key(ed, (key), (command))) #define ECEX_CONFIG_MODE(name) \ do { \ if (ecex_define_major_mode(ed, (name)) == 0) return -1; \ } while (0) #define ECEX_CONFIG_MODE_BIND(mode, key, command) ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, (mode), (key), (command))) #define ECEX_CONFIG_COMMANDS(commands) ECEX_CONFIG_TRY(ecex_config_register_commands(ed, (commands), ECEX_ARRAY_COUNT(commands))) #define ECEX_CONFIG_BINDS(bindings) ECEX_CONFIG_TRY(ecex_config_bind_keys(ed, (bindings), ECEX_ARRAY_COUNT(bindings))) #define ECEX_CONFIG_MODE_BINDS(bindings) ECEX_CONFIG_TRY(ecex_config_bind_mode_keys(ed, (bindings), ECEX_ARRAY_COUNT(bindings))) #define ECEX_CONFIG_MODES(modes) ECEX_CONFIG_TRY(ecex_config_define_modes(ed, (modes), ECEX_ARRAY_COUNT(modes))) #define ECEX_CONFIG_THEME(theme_ptr) ECEX_CONFIG_TRY(ecex_apply_theme(ed, (theme_ptr))) #define ECEX_CONFIG_INCLUDE(plugin_init_fn) ECEX_CONFIG_TRY((plugin_init_fn)(ed)) #endif