diff options
| author | David Moc <personal@cdatgoose.org> | 2026-06-02 13:50:21 +0200 |
|---|---|---|
| committer | David Moc <personal@cdatgoose.org> | 2026-06-02 13:50:21 +0200 |
| commit | a15cb041654ae307add0b998b526c87c3f42bf5f (patch) | |
| tree | 225bb4b70e9fa05aa5f4d2722a1a9cf5fc6fca7f /include | |
| parent | 6aeaa171dc1ca43392f53cbd02097f76e1b1c5a0 (diff) | |
Add plugin hooks and mode plugins
Diffstat (limited to 'include')
| -rw-r--r-- | include/app.h | 7 | ||||
| -rw-r--r-- | include/buffers.h | 10 | ||||
| -rw-r--r-- | include/ccdjit.h | 262 | ||||
| -rw-r--r-- | include/ecex.h | 127 | ||||
| -rw-r--r-- | include/libccdjit.a | bin | 507906 -> 613206 bytes | |||
| -rwxr-xr-x | include/libccdjit.so | bin | 369144 -> 431632 bytes | |||
| -rw-r--r-- | include/plugin.h | 110 | ||||
| -rw-r--r-- | include/types.h | 138 |
8 files changed, 552 insertions, 102 deletions
diff --git a/include/app.h b/include/app.h index a420f00..9db977e 100644 --- a/include/app.h +++ b/include/app.h @@ -7,7 +7,8 @@ #include <GLFW/glfw3.h> #include <stddef.h> -#define ECEX_MINIBUFFER_SIZE 256 +#define ECEX_MINIBUFFER_SIZE 1024 +#define ECEX_MINIBUFFER_MAX_ROWS 6 #define ECEX_PREFIX_SIZE 128 typedef enum app_mode { @@ -89,11 +90,15 @@ typedef struct app { font_t font; char font_path[4096]; + unsigned long ui_revision_seen; + unsigned long font_revision_seen; + unsigned long message_revision_seen; } app_t; void app_init(app_t *app, ecex_t *ed); void app_set_window(app_t *app, GLFWwindow *window); void app_install_callbacks(app_t *app); +void app_sync_editor_ui(app_t *app); void app_message(app_t *app, const char *msg); #endif diff --git a/include/buffers.h b/include/buffers.h index 148d718..8ffc5e9 100644 --- a/include/buffers.h +++ b/include/buffers.h @@ -5,6 +5,10 @@ #include <stddef.h> +void ecex_logf(const char *fmt, ...); +void ecex_log_group_begin(const char *message); +void ecex_log_group_end(const char *message); + buffer_t *buffer_new(const char *name, const char *path, int read_only); void buffer_free(buffer_t *buffer); @@ -107,8 +111,8 @@ 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); int ecex_buffer_text_len(buffer_t *buffer); -int ecex_buffer_scroll_line(buffer_t *buffer); -int ecex_buffer_line_count_i(buffer_t *buffer); -int ecex_buffer_line_copy(buffer_t *buffer, int line, char *out, int out_cap); +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); #endif diff --git a/include/ccdjit.h b/include/ccdjit.h index f8a212b..7999125 100644 --- a/include/ccdjit.h +++ b/include/ccdjit.h @@ -33,26 +33,48 @@ * Function pointers returned by ccdjit_module_symbol() are invalid after * ccdjit_module_free(). * + * Runtime policy: + * Module calls, symbol lookup, and emission intentionally consult the + * current context options, sandbox settings, resource limits, registered + * symbols, resolver callback, and loaded libraries. Machine code that + * has already been compiled may contain resolved external addresses; use + * a new module or function-handle replacement when changing symbol + * bindings must affect already compiled call sites. + * * Threads: - * Independent contexts may compile and run from different threads. Do - * not mutate or use the same context or module concurrently from multiple - * threads unless the host provides its own synchronization. + * Public API entry points are internally serialized, and module calls, + * symbol lookup, emission, and lazy compilation take a module lock. Raw + * function pointers returned by ccdjit_module_symbol() are still host + * pointers: do not free or replace their module while those pointers may + * run, and prefer typed handle/call helpers for dynamic bindings. * * Safety: * Normal calls run in the host process. Set sandbox_execution to * CCDJIT_SANDBOX_PROCESS to run ccdjit_module_call_main() in a child - * process with optional resource, syscall, and filesystem limits. + * process with optional resource, syscall, and filesystem limits. Child + * crashes are reported through ccdjit_diagnostic. */ #include <stddef.h> +#include <stdint.h> #include <stdio.h> #ifdef __cplusplus extern "C" { #endif +#define CCDJIT_VERSION_MAJOR 1 +#define CCDJIT_VERSION_MINOR 0 +#define CCDJIT_VERSION_PATCH 0 +#define CCDJIT_VERSION_STRING "1.0.0" +#define CCDJIT_API_ABI_VERSION 1 +#define CCDJITB2_FORMAT_VERSION 2 +#define CCDJITB2_FLAG_DEBUG_INFO 1u + typedef struct ccdjit_context ccdjit_context; typedef struct ccdjit_module ccdjit_module; +typedef struct ccdjit_function_handle ccdjit_function_handle; +typedef struct ccdjit_execution_result ccdjit_execution_result; typedef struct { /* Host compiler or linker driver. NULL uses $CC, then cc. */ @@ -64,6 +86,12 @@ typedef struct { /* Print the host linker command to stderr when nonzero. */ int verbose; + + /* Wall-clock driver timeout. Zero uses the default. */ + unsigned int timeout_ms; + + /* Captured driver stdout/stderr cap. Zero uses the default. */ + size_t output_limit_bytes; } ccdjit_link_options; /* @@ -158,6 +186,9 @@ typedef struct { */ size_t sandbox_output_limit_bytes; + /* Emit and retain optional debug/source mapping metadata. */ + int debug_info; + /* * Preprocessor output byte cap. Zero uses the default currently 128 * MiB. @@ -169,6 +200,18 @@ typedef struct { /* Nested include cap. Zero uses the default. */ unsigned int preprocessor_include_depth_limit; + + /* + * Optional compiler driver used for host system include and builtin + * macro probes. NULL auto-detects from $CC, then common cc drivers. + */ + const char *preprocessor_compiler; + + /* Wall-clock timeout for one compiler probe. Zero uses the default. */ + unsigned int preprocessor_probe_timeout_ms; + + /* Captured compiler-probe output cap. Zero uses the default. */ + size_t preprocessor_probe_output_limit_bytes; } ccdjit_options; typedef struct { @@ -189,18 +232,114 @@ typedef struct { int line; int column; + /* ccdjit_diagnostic_code plus optional code-specific detail. */ + int code; + int subcode; + /* Raw child wait status or errno-style value for sandbox/API failures. */ int status; /* Signal that terminated the sandbox child, or zero. */ int signal_number; + + /* Faulting address for sandboxed crash diagnostics, when available. */ + int has_fault_address; + uintptr_t fault_address; } ccdjit_diagnostic; -/* Create a compiler context. Passing NULL uses permissive CLI-like defaults. +typedef enum { + CCDJIT_DIAG_NONE = 0, + CCDJIT_DIAG_LEXER = 1, + CCDJIT_DIAG_PARSER = 2, + CCDJIT_DIAG_PREPROCESSOR = 3, + CCDJIT_DIAG_TYPE = 4, + CCDJIT_DIAG_JIT = 5, + CCDJIT_DIAG_UNRESOLVED_SYMBOL = 6, + CCDJIT_DIAG_SANDBOX = 7, + CCDJIT_DIAG_SANDBOX_TIMEOUT = 8, + CCDJIT_DIAG_SANDBOX_FORBIDDEN_SYSCALL = 9, + CCDJIT_DIAG_SANDBOX_CRASH = 10, + CCDJIT_DIAG_INVALID_OPTIONS = 11, + CCDJIT_DIAG_INVALID_ARGUMENT = 12, + CCDJIT_DIAG_UNSUPPORTED_ABI = 13, + CCDJIT_DIAG_MALFORMED_BINARY = 14, + CCDJIT_DIAG_HOST_LINKER = 15, + CCDJIT_DIAG_OUT_OF_MEMORY = 16, +} ccdjit_diagnostic_code; + +typedef enum { + CCDJIT_SYMBOL_UNKNOWN = 0, + CCDJIT_SYMBOL_FUNCTION = 1, + CCDJIT_SYMBOL_OBJECT = 2, +} ccdjit_symbol_kind; + +typedef enum { + CCDJIT_TYPE_INT = 0, + CCDJIT_TYPE_CHAR = 1, + CCDJIT_TYPE_BOOL = 2, + CCDJIT_TYPE_VOID = 3, + CCDJIT_TYPE_FLOAT = 4, + CCDJIT_TYPE_DOUBLE = 5, + CCDJIT_TYPE_LONG = 6, + CCDJIT_TYPE_SHORT = 7, + CCDJIT_TYPE_STRUCT = 8, + CCDJIT_TYPE_ENUM = 9, + CCDJIT_TYPE_UNION = 10, + CCDJIT_TYPE_FLOAT128 = 11, +} ccdjit_type_base; + +typedef struct { + int has_type; + int base; + int pointer_level; + int array_size; + int is_unsigned; + int is_complex; + int is_atomic; +} ccdjit_type_info; + +typedef struct { + int kind; + int has_signature; + ccdjit_type_info type; + ccdjit_type_info return_type; + size_t parameter_count; + int is_variadic; + size_t size; +} ccdjit_symbol_info; + +typedef struct { + int process; + int seccomp; + int landlock; +} ccdjit_sandbox_capabilities; + +typedef struct { + const char *filename; + const char *function; + int line; + int column; + uintptr_t address; + size_t size; +} ccdjit_debug_location; + +/* + * Create a compiler context. Passing NULL uses permissive CLI-like defaults. + * Invalid options return NULL; call ccdjit_context_last_error(NULL) for the + * thread-local creation diagnostic. */ ccdjit_context *ccdjit_context_new(const ccdjit_options *options); +int ccdjit_version_major(void); +int ccdjit_version_minor(void); +int ccdjit_version_patch(void); +const char *ccdjit_version_string(void); +int ccdjit_api_abi_version(void); +int ccdjit_binary_format_version(void); +const char *ccdjit_git_commit(void); +int ccdjit_sandbox_capabilities_query(ccdjit_sandbox_capabilities *out); + /* Free the context and all context-owned diagnostics/options/lists. */ void ccdjit_context_free(ccdjit_context *ctx); @@ -239,6 +378,24 @@ int ccdjit_context_set_preprocessor_limits(ccdjit_context *ctx, size_t output_limit_bytes, unsigned int macro_depth, unsigned int include_depth); +/* Select the compiler driver used by system include and builtin macro probes. + */ +int ccdjit_context_set_preprocessor_compiler(ccdjit_context *ctx, + const char *compiler); + +/* Update compiler-probe timeout/output limits. Passing zero keeps defaults. */ +int ccdjit_context_set_preprocessor_probe_limits(ccdjit_context *ctx, + unsigned int timeout_ms, size_t output_limit_bytes); + +/* Free this thread's cached compiler probe results and system include paths. */ +void ccdjit_preprocessor_free_probe_cache(void); + +/* Enable or disable executable memory for future compile/load/call paths. */ +int ccdjit_context_set_executable_memory(ccdjit_context *ctx, int allow); + +/* Enable or disable optional debug/source mapping metadata. */ +int ccdjit_context_set_debug_info(ccdjit_context *ctx, int enable); + /* * Add a Landlock filesystem root for sandboxed execution. * @@ -249,13 +406,40 @@ int ccdjit_context_set_preprocessor_limits(ccdjit_context *ctx, int ccdjit_context_add_sandbox_filesystem_root(ccdjit_context *ctx, const char *path, int writable); -/* Return the last error for this context. The pointer is context-owned. */ +/* + * Return the last error for this context. Passing NULL returns the + * thread-local diagnostic for context creation or NULL-context API failures. + * The pointer is context-owned or thread-local. + */ const ccdjit_diagnostic *ccdjit_context_last_error(ccdjit_context *ctx); /* Return captured sandbox stdout/stderr from the last sandboxed main call. */ const char *ccdjit_context_last_stdout(ccdjit_context *ctx); const char *ccdjit_context_last_stderr(ccdjit_context *ctx); +/* + * Owned execution result snapshot for bindings and concurrent hosts. + * + * Result objects keep copies of the call status, integer result, diagnostic, + * captured stdout, and captured stderr. They are not invalidated by later + * calls on the same context. + */ +void ccdjit_execution_result_free(ccdjit_execution_result *result); +int ccdjit_execution_result_status(const ccdjit_execution_result *result); +int ccdjit_execution_result_has_value(const ccdjit_execution_result *result); +int ccdjit_execution_result_value(const ccdjit_execution_result *result); +const ccdjit_diagnostic *ccdjit_execution_result_diagnostic( + const ccdjit_execution_result *result); +const char *ccdjit_execution_result_stdout( + const ccdjit_execution_result *result); +const char *ccdjit_execution_result_stderr( + const ccdjit_execution_result *result); +int ccdjit_execution_result_signal(const ccdjit_execution_result *result); +int ccdjit_execution_result_has_fault_address( + const ccdjit_execution_result *result); +uintptr_t ccdjit_execution_result_fault_address( + const ccdjit_execution_result *result); + /* Compile a source file into a live JIT module. */ int ccdjit_compile_file(ccdjit_context *ctx, const char *path, ccdjit_module **out); @@ -291,9 +475,75 @@ int ccdjit_eval_string(ccdjit_context *ctx, const char *source, */ void *ccdjit_module_symbol(ccdjit_module *module, const char *name); +/* + * Inspect a module symbol without forcing the caller to cast blindly. + * + * JIT functions expose return type, parameter count, variadic flag, and + * per-parameter metadata through ccdjit_module_function_parameter_type(). + * Object symbols expose their object type and size. Loaded CCDJITB2 binaries + * expose function/object kind and size but not full C signatures. + */ +int ccdjit_module_symbol_info(ccdjit_module *module, const char *name, + ccdjit_symbol_info *out); + +int ccdjit_module_function_parameter_type(ccdjit_module *module, + const char *name, size_t index, ccdjit_type_info *out); + +/* + * Return a callable function pointer only when the JIT signature exactly + * matches the expected return and parameter types. + */ +void *ccdjit_module_function_symbol_checked(ccdjit_module *module, + const char *name, const ccdjit_type_info *return_type, + const ccdjit_type_info *parameter_types, size_t parameter_count, + int allow_variadic); + +/* Map a live JIT instruction address to lightweight module-owned debug info. */ +int ccdjit_module_debug_location(ccdjit_module *module, const void *address, + ccdjit_debug_location *out); + /* Call module main(argc, argv). Honors CCDJIT_SANDBOX_PROCESS. */ int ccdjit_module_call_main(ccdjit_module *module, int argc, char **argv, int *result_out); +int ccdjit_module_call_main_result(ccdjit_module *module, int argc, char **argv, + ccdjit_execution_result **out); + +/* + * Call an exported JIT function with signature int name(int). + * + * Honors CCDJIT_SANDBOX_PROCESS like ccdjit_module_call_main(). Other + * signatures and binary modules are rejected with a diagnostic. + */ +int ccdjit_module_call_i32(ccdjit_module *module, const char *symbol, int arg, + int *result_out); +int ccdjit_module_call_i32_result(ccdjit_module *module, const char *symbol, + int arg, ccdjit_execution_result **out); + +/* + * Compile and publish a named function behind a stable handle. + * + * The initial source and every replacement must define the same function + * signature. Calls through the handle do not expose raw function pointers, so + * replacements can wait for active callers and then free old generated code. + */ +int ccdjit_function_handle_new(ccdjit_context *ctx, const char *source, + const char *filename, const char *symbol, ccdjit_function_handle **out); + +/* + * Compile replacement source and atomically publish it on success. + * + * On compile failure or signature mismatch the old implementation remains + * active and the context diagnostic describes the failure. + */ +int ccdjit_function_handle_replace(ccdjit_function_handle *handle, + const char *source, const char *filename); + +/* Call a handle whose published function has signature int name(int). */ +int ccdjit_function_handle_call_i32(ccdjit_function_handle *handle, int arg, + int *result_out); + +/* Free a function handle and the currently published implementation. */ +void ccdjit_function_handle_free(ccdjit_function_handle *handle); /* Emit textual assembly-like bytes for inspection. */ int ccdjit_module_emit_assembly(ccdjit_module *module, FILE *out); diff --git a/include/ecex.h b/include/ecex.h index efcbe5a..085c3d2 100644 --- a/include/ecex.h +++ b/include/ecex.h @@ -6,6 +6,7 @@ #include "eval.h" #include "path.h" #include "media.h" +#include "plugin.h" typedef struct ecex_config_command { @@ -24,6 +25,9 @@ typedef struct ecex_config_mode_keybind { 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); @@ -35,61 +39,34 @@ 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_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); -int ecex_tetris_shape_cell(int piece, int rot, int col, int row); - -/* Host-owned variable registry for JIT plugins. Plugins can keep persistent - * scalar values, dynamic arrays, or static host buffers behind a stable owner - * pointer and a name instead of relying on fragile JIT struct/array storage. */ -void *ecex_var_get(ecex_t *ed, void *owner, const char *name); -void *ecex_var_get_or_alloc(ecex_t *ed, void *owner, const char *name, size_t count, size_t elem_size); -int ecex_var_bind_static(ecex_t *ed, void *owner, const char *name, void *data, size_t count, size_t elem_size); -int ecex_var_free(ecex_t *ed, void *owner, const char *name); -int ecex_var_free_owner(ecex_t *ed, void *owner); -int ecex_var_i32_get(ecex_t *ed, void *owner, const char *name, size_t index, int fallback); -int ecex_var_i32_set(ecex_t *ed, void *owner, const char *name, size_t index, int value); -int ecex_var_i32(ecex_t *ed, void *owner, const char *name, int fallback); -int ecex_var_i32_set_scalar(ecex_t *ed, void *owner, const char *name, int value); - -/* Host-owned object allocator for plugin state. Returned pointers are tracked - * by the editor and can be freed through the API. Prefer this for callback - * userdata over plugin/local allocations when the object has callback lifetime. - */ -void *ecex_object_alloc(ecex_t *ed, size_t size); -void *ecex_object_calloc(ecex_t *ed, size_t count, size_t size); -int ecex_object_free(ecex_t *ed, void *object); -int ecex_object_valid(ecex_t *ed, void *object); -int ecex_object_i32_get(ecex_t *ed, void *object, size_t byte_offset, int fallback); -int ecex_object_i32_set(ecex_t *ed, void *object, size_t byte_offset, int value); -void *ecex_object_ptr_get(ecex_t *ed, void *object, size_t byte_offset); -int ecex_object_ptr_set(ecex_t *ed, void *object, size_t byte_offset, void *value); - -/* Host-owned text registry for plugins that need to render arbitrary text. - * Plugins copy text into host storage, then draw by owner/id. This avoids - * passing JIT-owned literals or stack strings into the normal text renderer. */ -int ecex_text_set(ecex_t *ed, void *owner, int id, const char *text, int len); -int ecex_text_set_buffer_title(ecex_t *ed, void *owner, int id, buffer_t *buffer); -int ecex_text_free(ecex_t *ed, void *owner, int id); -int ecex_text_free_owner(ecex_t *ed, void *owner); + +/* 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(buffer_t *buffer); -int ecex_buffer_line_count_i(buffer_t *buffer); -int ecex_buffer_line_copy(buffer_t *buffer, int line, char *out, int out_cap); -int ecex_markdown_draw_line_from_buffer_i(ecex_draw_context_t *ctx, void *owner, buffer_t *buffer, int line, int y, int in_code); -int ecex_markdown_body_y_i(ecex_draw_context_t *ctx); -int ecex_draw_context_height_i(ecex_draw_context_t *ctx); -int ecex_draw_context_line_height_i(ecex_draw_context_t *ctx); +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_register_file_handler(ecex_t *ed, const char *extension, ecex_file_handler_fn fn); -int ecex_run_file_handlers(ecex_t *ed, buffer_t *buffer); +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); @@ -137,6 +114,52 @@ 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_words(ecex_t *ed, const char *name, const char *words); +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, @@ -156,6 +179,12 @@ 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); @@ -244,12 +273,13 @@ void ecex_draw_rgba(ecex_draw_context_t *ctx, /* 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(ecex_draw_context_t *ctx, int r, int g, int b, int a); +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_text_id_i(ecex_draw_context_t *ctx, void *owner, int id, int x, int y); +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); @@ -273,6 +303,7 @@ 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); @@ -320,7 +351,9 @@ void ecex_set_current_line_enabled(ecex_t *ed, int enabled); #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) int name(ecex_t *ed) { +#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 { \ diff --git a/include/libccdjit.a b/include/libccdjit.a Binary files differindex 3045696..d062f38 100644 --- a/include/libccdjit.a +++ b/include/libccdjit.a diff --git a/include/libccdjit.so b/include/libccdjit.so Binary files differindex 40456db..c3d9203 100755 --- a/include/libccdjit.so +++ b/include/libccdjit.so diff --git a/include/plugin.h b/include/plugin.h new file mode 100644 index 0000000..50aad33 --- /dev/null +++ b/include/plugin.h @@ -0,0 +1,110 @@ +#ifndef ECEX_PLUGIN_H +#define ECEX_PLUGIN_H + +#include "types.h" + +#include <stddef.h> +#include <stdint.h> + +#define ECEX_PLUGIN_API_VERSION 1 +#define ECEX_PLUGIN_EXPORT_READ 1 + +typedef struct ecex_plugin ecex_plugin_t; +typedef struct ecex_plugin_runtime ecex_plugin_runtime_t; + +typedef enum ecex_plugin_value_type { + ECEX_PLUGIN_BYTES = 0, + ECEX_PLUGIN_I32 = 1, + ECEX_PLUGIN_U32 = 2, + ECEX_PLUGIN_I64 = 3, + ECEX_PLUGIN_PTR = 4, +} ecex_plugin_value_type_t; + +ecex_plugin_runtime_t *ecex_plugin_runtime_new(void); +void ecex_plugin_runtime_free(ecex_plugin_runtime_t *runtime); + +ecex_plugin_t *ecex_plugin_register(ecex_t *ed, const char *id, int api_version); +ecex_plugin_t *ecex_plugin_require(ecex_t *ed, const char *id, int api_version); +ecex_plugin_t *ecex_plugin_find(ecex_t *ed, const char *id); +const char *ecex_plugin_id(ecex_plugin_t *plugin); + +/* Plugin slots are named, plugin-private arrays unless explicitly exported. */ +void *ecex_plugin_slot_alloc(ecex_plugin_t *plugin, + const char *name, + size_t count, + size_t elem_size); +void *ecex_plugin_slot_get(ecex_plugin_t *plugin, const char *name); +int ecex_plugin_slot_free(ecex_plugin_t *plugin, const char *name); +int ecex_plugin_slot_set_export_flags(ecex_plugin_t *plugin, + const char *name, + int type, + int flags); +int ecex_plugin_slot_read_exported(ecex_t *ed, + const char *plugin_id, + const char *name, + void *out, + size_t out_cap, + size_t *out_len); + +int ecex_plugin_slot_i32_get_scalar(ecex_plugin_t *plugin, const char *name, int fallback); +int ecex_plugin_slot_i32_get(ecex_plugin_t *plugin, + const char *name, + size_t index, + int fallback); +int ecex_plugin_slot_i32_set(ecex_plugin_t *plugin, + const char *name, + size_t index, + int value); +int ecex_plugin_slot_i32_get_2d(ecex_plugin_t *plugin, + const char *name, + size_t width, + size_t x, + size_t y, + int fallback); +int ecex_plugin_slot_i32_set_2d(ecex_plugin_t *plugin, + const char *name, + size_t width, + size_t x, + size_t y, + int value); +int ecex_plugin_slot_i32_set_scalar(ecex_plugin_t *plugin, + const char *name, + int value); + +/* Plugin objects are tracked allocations intended for callback userdata. */ +void *ecex_plugin_object_alloc(ecex_plugin_t *plugin, const char *name, size_t size); +void *ecex_plugin_object_calloc(ecex_plugin_t *plugin, + const char *name, + size_t count, + size_t size); +int ecex_plugin_object_free(ecex_plugin_t *plugin, void *object); +int ecex_plugin_object_valid(ecex_plugin_t *plugin, void *object); +int ecex_plugin_object_i32_get(ecex_plugin_t *plugin, + void *object, + size_t byte_offset, + int fallback); +int ecex_plugin_object_i32_set(ecex_plugin_t *plugin, + void *object, + size_t byte_offset, + int value); +void *ecex_plugin_object_ptr_get(ecex_plugin_t *plugin, + void *object, + size_t byte_offset); +int ecex_plugin_object_ptr_set(ecex_plugin_t *plugin, + void *object, + size_t byte_offset, + void *value); + +/* Plugin text stores host-owned strings for later draw-time lookup. */ +int ecex_plugin_text_set(ecex_plugin_t *plugin, int id, const char *text, int len); +int ecex_plugin_text_set_from_buffer_title(ecex_plugin_t *plugin, int id, buffer_t *buffer); +int ecex_plugin_text_free(ecex_plugin_t *plugin, int id); +int ecex_plugin_text_free_all(ecex_plugin_t *plugin); +const char *ecex_plugin_text_get_drawable(ecex_t *ed, void *owner, int id); + +int ecex_plugin_file_handler_register(ecex_plugin_t *plugin, + const char *extension, + ecex_file_handler_fn fn); +int ecex_plugin_file_handlers_run(ecex_t *ed, buffer_t *buffer); + +#endif diff --git a/include/types.h b/include/types.h index 800c3d6..2b63ad3 100644 --- a/include/types.h +++ b/include/types.h @@ -7,6 +7,7 @@ typedef struct ecex ecex_t; typedef struct buffer buffer_t; typedef struct ecex_window ecex_window_t; typedef struct ecex_draw_context ecex_draw_context_t; +typedef struct ecex_plugin_runtime ecex_plugin_runtime_t; typedef int (*ecex_command_fn)(ecex_t *ed); typedef int (*ecex_interactive_line_fn)(ecex_t *ed, buffer_t *buffer, size_t line, const char *payload, void *userdata); @@ -18,6 +19,37 @@ typedef int (*ecex_buffer_tick_ms_fn)(ecex_t *ed, buffer_t *buffer, int now_ms, typedef int (*ecex_buffer_mouse_fn)(ecex_t *ed, buffer_t *buffer, int event, int x, int y, int button, void *userdata); typedef int (*ecex_file_handler_fn)(ecex_t *ed, buffer_t *buffer); typedef void (*ecex_buffer_userdata_free_fn)(void *userdata); +typedef void (*ecex_hook_free_fn)(void *userdata); +typedef void (*ecex_command_hook_fn)(ecex_t *ed, const char *command, int event, int result, void *userdata); +typedef void (*ecex_prefix_hook_fn)(ecex_t *ed, const char *prefix, int event, void *userdata); +typedef void (*ecex_buffer_hook_fn)(ecex_t *ed, buffer_t *buffer, int event, void *userdata); +typedef int (*ecex_completion_provider_fn)(ecex_t *ed, + buffer_t *buffer, + const char *prefix, + char *out, + size_t out_size, + void *userdata); + +typedef enum ecex_command_hook_event { + ECEX_COMMAND_HOOK_BEFORE = 1, + ECEX_COMMAND_HOOK_AFTER = 2, +} ecex_command_hook_event_t; + +typedef enum ecex_prefix_hook_event { + ECEX_PREFIX_HOOK_BEGIN = 1, + ECEX_PREFIX_HOOK_UPDATE = 2, + ECEX_PREFIX_HOOK_CANCEL = 3, + ECEX_PREFIX_HOOK_FINISH = 4, + ECEX_PREFIX_HOOK_UNDEFINED = 5, +} ecex_prefix_hook_event_t; + +typedef enum ecex_buffer_hook_event { + ECEX_BUFFER_HOOK_CREATE = 1, + ECEX_BUFFER_HOOK_SWITCH = 2, + ECEX_BUFFER_HOOK_SAVE = 3, + ECEX_BUFFER_HOOK_KILL = 4, + ECEX_BUFFER_HOOK_MODE_CHANGE = 5, +} ecex_buffer_hook_event_t; typedef enum ecex_prompt_request { ECEX_PROMPT_NONE = 0, @@ -154,6 +186,7 @@ struct buffer { void *media_pipe; double media_last_frame_time; int media_playing; + int media_audio_pid; char media_status[256]; ecex_buffer_render_fn render_fn; @@ -198,42 +231,43 @@ typedef struct ecex_mode_keybind { char *command; } ecex_mode_keybind_t; -typedef struct ecex_major_mode { - int id; +typedef struct ecex_command_hook { char *name; -} ecex_major_mode_t; + ecex_command_hook_fn fn; + void *userdata; + ecex_hook_free_fn free_fn; +} ecex_command_hook_t; -typedef enum ecex_var_kind { - ECEX_VAR_BYTES = 0, - ECEX_VAR_I32 = 1, -} ecex_var_kind_t; +typedef struct ecex_prefix_hook { + char *name; + ecex_prefix_hook_fn fn; + void *userdata; + ecex_hook_free_fn free_fn; +} ecex_prefix_hook_t; -typedef struct ecex_var_entry { - void *owner; +typedef struct ecex_buffer_hook { char *name; - void *data; - size_t elem_size; - size_t count; - int kind; - int dynamic; -} ecex_var_entry_t; - -typedef struct ecex_text_entry { - void *owner; - int id; - char *text; - size_t len; -} ecex_text_entry_t; + ecex_buffer_hook_fn fn; + void *userdata; + ecex_hook_free_fn free_fn; +} ecex_buffer_hook_t; -typedef struct ecex_file_handler { - char *extension; - ecex_file_handler_fn fn; -} ecex_file_handler_t; +typedef struct ecex_completion_provider { + char *name; + int mode; + ecex_completion_provider_fn fn; + void *userdata; + ecex_hook_free_fn free_fn; + char *detail; + char **words; + size_t word_count; + int flags; +} ecex_completion_provider_t; -typedef struct ecex_object_entry { - void *ptr; - size_t size; -} ecex_object_entry_t; +typedef struct ecex_major_mode { + int id; + char *name; +} ecex_major_mode_t; struct ecex_window { buffer_t *buffer; @@ -273,26 +307,34 @@ struct ecex { size_t mode_keybind_cap; size_t mode_keybind_count; + ecex_command_hook_t *command_hooks; + size_t command_hook_cap; + size_t command_hook_count; + + ecex_prefix_hook_t *prefix_hooks; + size_t prefix_hook_cap; + size_t prefix_hook_count; + + ecex_buffer_hook_t *buffer_hooks; + size_t buffer_hook_cap; + size_t buffer_hook_count; + + ecex_completion_provider_t *completion_providers; + size_t completion_provider_cap; + size_t completion_provider_count; + int completion_cycle_active; + buffer_t *completion_cycle_buffer; + size_t completion_cycle_start; + size_t completion_cycle_index; + char completion_cycle_prefix[256]; + char completion_cycle_current[256]; + ecex_major_mode_t *major_modes; size_t major_mode_cap; size_t major_mode_count; int next_major_mode_id; - ecex_var_entry_t *vars; - size_t var_cap; - size_t var_count; - - ecex_text_entry_t *texts; - size_t text_cap; - size_t text_count; - - ecex_file_handler_t *file_handlers; - size_t file_handler_cap; - size_t file_handler_count; - - ecex_object_entry_t *objects; - size_t object_cap; - size_t object_count; + ecex_plugin_runtime_t *plugins; char *last_eval_source; char *last_eval_filename; @@ -305,13 +347,19 @@ struct ecex { char prompt_message[128]; char *config_path; + char message[1024]; + unsigned long message_revision; ecex_clipboard_get_fn clipboard_get; ecex_clipboard_set_fn clipboard_set; void *clipboard_userdata; + char *clipboard_text; int should_quit; + unsigned long ui_revision; + unsigned long font_revision; + ecex_theme_t theme; }; |
