aboutsummaryrefslogtreecommitdiff
path: root/config
diff options
context:
space:
mode:
Diffstat (limited to 'config')
-rw-r--r--config/c_mode_plugin.c192
-rw-r--r--config/c_tools_plugin.c210
-rw-r--r--config/ecex_api_completion_plugin.c806
-rw-r--r--config/ecexrc.c17
-rw-r--r--config/markdown_plugin.c68
-rw-r--r--config/render_demo.c118
-rw-r--r--config/tetris.c188
-rw-r--r--config/which_key_plugin.c107
8 files changed, 1543 insertions, 163 deletions
diff --git a/config/c_mode_plugin.c b/config/c_mode_plugin.c
new file mode 100644
index 0000000..e707bca
--- /dev/null
+++ b/config/c_mode_plugin.c
@@ -0,0 +1,192 @@
+#include "ecex.h"
+
+#include <string.h>
+
+#define C_MODE_PLUGIN_ID "c-mode"
+#define C_MODE_LSP_PROVIDER_NAME "c-mode-clangd"
+#define C_MODE_SLOT_CLANGD "clangd"
+
+static int c_mode_line_starts_with(buffer_t *buffer, size_t first, size_t line_end, const char *word) {
+ size_t len = strlen(word);
+ if (!buffer || !word || first + len > line_end) return 0;
+ if (strncmp(buffer->data + first, word, len) != 0) return 0;
+ if (first + len == line_end) return 1;
+ char c = buffer->data[first + len];
+ return !(c == '_' ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9'));
+}
+
+static int c_mode_line_ends_with_colon(buffer_t *buffer, size_t first, size_t line_end) {
+ if (!buffer || first >= line_end) return 0;
+ size_t p = line_end;
+ while (p > first && (buffer->data[p - 1] == ' ' || buffer->data[p - 1] == '\t')) p--;
+ return p > first && buffer->data[p - 1] == ':';
+}
+
+static size_t c_mode_previous_nonblank_line(buffer_t *buffer, size_t line_start) {
+ if (!buffer || line_start == 0) return (size_t)-1;
+
+ size_t end = line_start - 1;
+ while (end > 0 && buffer->data[end - 1] == '\n') end--;
+
+ for (;;) {
+ size_t start = buffer_line_start_at(buffer, end);
+ size_t line_end = buffer_line_end_at(buffer, start);
+ size_t p = start;
+ while (p < line_end && (buffer->data[p] == ' ' || buffer->data[p] == '\t')) p++;
+ if (p < line_end) return start;
+ if (start == 0) break;
+ end = start - 1;
+ }
+
+ return (size_t)-1;
+}
+
+static int c_mode_brace_depth_before(buffer_t *buffer, size_t limit) {
+ int depth = 0;
+ int in_block = 0;
+ int in_line = 0;
+ int in_string = 0;
+ int in_char = 0;
+ int escaped = 0;
+
+ if (!buffer || !buffer->data) return 0;
+ if (limit > buffer->len) limit = buffer->len;
+
+ for (size_t i = 0; i < limit; i++) {
+ char c = buffer->data[i];
+ char next = i + 1 < limit ? buffer->data[i + 1] : '\0';
+
+ if (in_line) {
+ if (c == '\n') in_line = 0;
+ continue;
+ }
+
+ if (in_block) {
+ if (c == '*' && next == '/') {
+ in_block = 0;
+ i++;
+ }
+ continue;
+ }
+
+ if (in_string) {
+ if (escaped) escaped = 0;
+ else if (c == '\\') escaped = 1;
+ else if (c == '"') in_string = 0;
+ else if (c == '\n') in_string = 0;
+ continue;
+ }
+
+ if (in_char) {
+ if (escaped) escaped = 0;
+ else if (c == '\\') escaped = 1;
+ else if (c == '\'') in_char = 0;
+ else if (c == '\n') in_char = 0;
+ continue;
+ }
+
+ if (c == '/' && next == '/') {
+ in_line = 1;
+ i++;
+ } else if (c == '/' && next == '*') {
+ in_block = 1;
+ i++;
+ } else if (c == '"') {
+ in_string = 1;
+ } else if (c == '\'') {
+ in_char = 1;
+ } else if (c == '{') {
+ depth++;
+ } else if (c == '}') {
+ if (depth > 0) depth--;
+ }
+ }
+
+ return depth;
+}
+
+static int cmd_c_indent_line(ecex_t *ed) {
+ if (!ed) return -1;
+ buffer_t *buffer = ecex_current_buffer(ed);
+ if (!buffer || buffer->read_only || buffer_is_interactive(buffer)) return -1;
+
+ size_t line_start = buffer_current_line_start(buffer);
+ size_t line_end = buffer_current_line_end(buffer);
+ size_t first = line_start;
+ while (first < line_end && (buffer->data[first] == ' ' || buffer->data[first] == '\t')) first++;
+
+ if (first < line_end && buffer->data[first] == '#') return ecex_indent_line_to(buffer, 0);
+
+ int depth = c_mode_brace_depth_before(buffer, line_start);
+ int target = depth * 4;
+
+ if (first < line_end && buffer->data[first] == '}') target -= 4;
+
+ size_t prev = c_mode_previous_nonblank_line(buffer, line_start);
+ if (prev != (size_t)-1) {
+ size_t prev_end = buffer_line_end_at(buffer, prev);
+ size_t prev_first = prev;
+ while (prev_first < prev_end && (buffer->data[prev_first] == ' ' || buffer->data[prev_first] == '\t')) prev_first++;
+
+ if ((c_mode_line_starts_with(buffer, prev_first, prev_end, "case") ||
+ c_mode_line_starts_with(buffer, prev_first, prev_end, "default")) &&
+ c_mode_line_ends_with_colon(buffer, prev_first, prev_end) &&
+ !(c_mode_line_starts_with(buffer, first, line_end, "case") ||
+ c_mode_line_starts_with(buffer, first, line_end, "default") ||
+ (first < line_end && buffer->data[first] == '}'))) {
+ target += 4;
+ }
+ }
+
+ if (target < 0) target = 0;
+ return ecex_indent_line_to(buffer, target);
+}
+
+static int cmd_c_complete(ecex_t *ed) {
+ return ecex_complete_at_point(ed);
+}
+
+static int c_mode_file_handler(ecex_t *ed, buffer_t *buffer) {
+ if (!ed || !buffer) return -1;
+ return ecex_buffer_set_major_mode_by_name(ed, buffer, "c-mode");
+}
+
+ECEX_PLUGIN_BEGIN(ecex_c_mode_plugin, C_MODE_PLUGIN_ID)
+ if (!ecex_define_major_mode(ed, "c-mode")) return -1;
+
+ ECEX_CONFIG_COMMAND("c-indent-line", cmd_c_indent_line);
+ ECEX_CONFIG_COMMAND("c-complete", cmd_c_complete);
+ ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, "c-mode", "TAB", "c-indent-line"));
+ ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, "c-mode", "C-TAB", "c-complete"));
+ ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, "c-mode", "C-S-TAB", "complete-at-point-previous"));
+
+ if (ecex_dependency_available("clangd")) {
+ ECEX_CONFIG_TRY(ecex_add_clangd_completion_provider(ed,
+ C_MODE_LSP_PROVIDER_NAME,
+ "c-mode"));
+ ecex_plugin_slot_i32_set_scalar(plugin, C_MODE_SLOT_CLANGD, 1);
+ } else {
+ ecex_plugin_slot_i32_set_scalar(plugin, C_MODE_SLOT_CLANGD, 0);
+ ecex_message(ed, "c-mode: clangd not found; C completion disabled");
+ }
+
+ ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".c", c_mode_file_handler));
+ ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".h", c_mode_file_handler));
+ ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".cc", c_mode_file_handler));
+ ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".cpp", c_mode_file_handler));
+ ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".cxx", c_mode_file_handler));
+ ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".hh", c_mode_file_handler));
+ ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".hpp", c_mode_file_handler));
+ ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".hxx", c_mode_file_handler));
+
+ return 0;
+ECEX_PLUGIN_END
+
+#ifndef ECEX_NO_STANDALONE_CONFIG
+ECEX_CONFIG_BEGIN
+ ECEX_CONFIG_INCLUDE(ecex_c_mode_plugin);
+ECEX_CONFIG_END
+#endif
diff --git a/config/c_tools_plugin.c b/config/c_tools_plugin.c
new file mode 100644
index 0000000..c3c075f
--- /dev/null
+++ b/config/c_tools_plugin.c
@@ -0,0 +1,210 @@
+#include "ecex.h"
+
+#include <string.h>
+
+#define C_TOOLS_PLUGIN_ID "c-tools"
+
+static int c_tools_append_char(char *out, int out_cap, int *used, char ch) {
+ if (!out || !used || out_cap <= 0 || *used < 0 || *used + 1 >= out_cap) return -1;
+ out[*used] = ch;
+ *used = *used + 1;
+ out[*used] = '\0';
+ return 0;
+}
+
+static int c_tools_append_text(char *out, int out_cap, int *used, const char *text) {
+ if (!text) text = "";
+ for (int i = 0; text[i]; i++) {
+ if (c_tools_append_char(out, out_cap, used, text[i]) != 0) return -1;
+ }
+ return 0;
+}
+
+static int c_tools_copy_text(char *out, int out_cap, const char *text) {
+ int used = 0;
+ if (!out || out_cap <= 0) return -1;
+ out[0] = '\0';
+ return c_tools_append_text(out, out_cap, &used, text);
+}
+
+static int c_tools_shell_quote(const char *input, char *out, int out_cap) {
+ int used = 0;
+ if (!input || !out || out_cap < 3) return -1;
+ out[0] = '\0';
+
+ if (c_tools_append_char(out, out_cap, &used, '\'') != 0) return -1;
+ for (int i = 0; input[i]; i++) {
+ if (input[i] == '\'') {
+ if (c_tools_append_text(out, out_cap, &used, "'\\''") != 0) return -1;
+ } else {
+ if (c_tools_append_char(out, out_cap, &used, input[i]) != 0) return -1;
+ }
+ }
+ if (c_tools_append_char(out, out_cap, &used, '\'') != 0) return -1;
+ return 0;
+}
+
+static buffer_t *c_tools_current_path_buffer(ecex_t *ed) {
+ buffer_t *buffer = ecex_current_buffer(ed);
+ if (!buffer || !buffer->path || !buffer->path[0]) {
+ ecex_message(ed, "C tooling needs a file-backed buffer");
+ return 0;
+ }
+ return buffer;
+}
+
+static int c_tools_ecex_include_flags(char *out, int out_cap, int force) {
+ if (!out || out_cap <= 0) return -1;
+ out[0] = '\0';
+
+ char cwd[4096];
+ if (ecex_path_cwd(cwd, (size_t)sizeof(cwd)) != 0 || !cwd[0]) {
+ if (c_tools_copy_text(cwd, (int)sizeof(cwd), ".") != 0) return -1;
+ }
+
+ char include_dir[8192];
+ int used = 0;
+ include_dir[0] = '\0';
+ if (c_tools_append_text(include_dir, (int)sizeof(include_dir), &used, cwd) != 0) return -1;
+ if (c_tools_append_text(include_dir, (int)sizeof(include_dir), &used, "/include") != 0) return -1;
+
+ char header[8192];
+ used = 0;
+ header[0] = '\0';
+ if (c_tools_append_text(header, (int)sizeof(header), &used, include_dir) != 0) return -1;
+ if (c_tools_append_text(header, (int)sizeof(header), &used, "/ecex.h") != 0) return -1;
+
+ if (!force && !ecex_path_exists(header)) return 0;
+
+ char quoted[8192];
+ if (c_tools_shell_quote(include_dir, quoted, (int)sizeof(quoted)) != 0) return -1;
+
+ used = 0;
+ if (c_tools_append_text(out, out_cap, &used, " -I") != 0) return -1;
+ if (c_tools_append_text(out, out_cap, &used, quoted) != 0) return -1;
+ return 0;
+}
+
+static int c_tools_command_lsp_check(const char *quoted, char *out, int out_cap) {
+ int used = 0;
+ if (!quoted || !out || out_cap <= 0) return -1;
+ out[0] = '\0';
+ if (c_tools_append_text(out, out_cap, &used, "clangd --check=") != 0) return -1;
+ if (c_tools_append_text(out, out_cap, &used, quoted) != 0) return -1;
+ return 0;
+}
+
+static int c_tools_command_lint(const char *quoted,
+ const char *include_flags,
+ char *out,
+ int out_cap) {
+ int used = 0;
+ if (!quoted || !include_flags || !out || out_cap <= 0) return -1;
+ out[0] = '\0';
+ if (c_tools_append_text(out, out_cap, &used, "${CC:-clang} -fsyntax-only -Wall -Wextra") != 0) return -1;
+ if (c_tools_append_text(out, out_cap, &used, include_flags) != 0) return -1;
+ if (c_tools_append_char(out, out_cap, &used, ' ') != 0) return -1;
+ if (c_tools_append_text(out, out_cap, &used, quoted) != 0) return -1;
+ return 0;
+}
+
+static int cmd_c_lsp_check(ecex_t *ed) {
+ buffer_t *buffer = c_tools_current_path_buffer(ed);
+ if (!buffer) return -1;
+
+ char quoted[4096];
+ char command[8192];
+ if (c_tools_shell_quote(buffer->path, quoted, (int)sizeof(quoted)) != 0) {
+ ecex_message(ed, "Could not quote C buffer path");
+ return -1;
+ }
+ if (c_tools_command_lsp_check(quoted, command, (int)sizeof(command)) != 0) {
+ ecex_message(ed, "Could not build clangd command");
+ return -1;
+ }
+
+ return ecex_compile(ed, command);
+}
+
+static int cmd_c_lint_buffer(ecex_t *ed) {
+ buffer_t *buffer = c_tools_current_path_buffer(ed);
+ if (!buffer) return -1;
+
+ char quoted[4096];
+ char include_flags[8192];
+ char command[12288];
+ if (c_tools_shell_quote(buffer->path, quoted, (int)sizeof(quoted)) != 0) {
+ ecex_message(ed, "Could not quote C buffer path");
+ return -1;
+ }
+ if (c_tools_ecex_include_flags(include_flags, (int)sizeof(include_flags), 0) != 0) {
+ ecex_message(ed, "Could not build ecex include flags");
+ return -1;
+ }
+ if (c_tools_command_lint(quoted, include_flags, command, (int)sizeof(command)) != 0) {
+ ecex_message(ed, "Could not build C lint command");
+ return -1;
+ }
+
+ return ecex_compile(ed, command);
+}
+
+static int cmd_c_ecex_lint_buffer(ecex_t *ed) {
+ buffer_t *buffer = c_tools_current_path_buffer(ed);
+ if (!buffer) return -1;
+
+ char quoted[4096];
+ char include_flags[8192];
+ char command[12288];
+ if (c_tools_shell_quote(buffer->path, quoted, (int)sizeof(quoted)) != 0) {
+ ecex_message(ed, "Could not quote C buffer path");
+ return -1;
+ }
+ if (c_tools_ecex_include_flags(include_flags, (int)sizeof(include_flags), 1) != 0) {
+ ecex_message(ed, "Could not build ecex include flags");
+ return -1;
+ }
+ if (c_tools_command_lint(quoted, include_flags, command, (int)sizeof(command)) != 0) {
+ ecex_message(ed, "Could not build C lint command");
+ return -1;
+ }
+
+ return ecex_compile(ed, command);
+}
+
+static int cmd_c_tools_status(ecex_t *ed) {
+ buffer_t *buffer = ecex_create_interactive_buffer(ed, "*c-tools*");
+ if (!buffer) return -1;
+
+ buffer_clear(buffer);
+ buffer_append(buffer, "C tools\n\n");
+ buffer_append(buffer, "C-x c l runs clangd --check for the current file.\n");
+ buffer_append(buffer, "C-x c k runs ${CC:-clang} -fsyntax-only -Wall -Wextra, adding ./include when ecex.h exists.\n");
+ buffer_append(buffer, "C-x c e runs the same lint with ./include forced for ecex development.\n");
+ buffer_append(buffer, "Both commands use the file on disk; save first for exact diagnostics.\n");
+ buffer->modified = 0;
+ return ecex_switch_buffer(ed, "*c-tools*");
+}
+
+ECEX_PLUGIN_BEGIN(ecex_c_tools_plugin, C_TOOLS_PLUGIN_ID)
+ if (ecex_plugin_require_dependency(ed, C_TOOLS_PLUGIN_ID, "clang") != 0) return 0;
+ if (ecex_plugin_require_dependency(ed, C_TOOLS_PLUGIN_ID, "clangd") != 0) return 0;
+
+ ECEX_CONFIG_COMMAND("c-lsp-check", cmd_c_lsp_check);
+ ECEX_CONFIG_COMMAND("c-lint-buffer", cmd_c_lint_buffer);
+ ECEX_CONFIG_COMMAND("c-ecex-lint-buffer", cmd_c_ecex_lint_buffer);
+ ECEX_CONFIG_COMMAND("c-tools-status", cmd_c_tools_status);
+
+ ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, "c-mode", "C-x c l", "c-lsp-check"));
+ ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, "c-mode", "C-x c k", "c-lint-buffer"));
+ ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, "c-mode", "C-x c e", "c-ecex-lint-buffer"));
+ ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, "c-mode", "C-x c s", "c-tools-status"));
+
+ return 0;
+ECEX_PLUGIN_END
+
+#ifndef ECEX_NO_STANDALONE_CONFIG
+ECEX_CONFIG_BEGIN
+ ECEX_CONFIG_INCLUDE(ecex_c_tools_plugin);
+ECEX_CONFIG_END
+#endif
diff --git a/config/ecex_api_completion_plugin.c b/config/ecex_api_completion_plugin.c
new file mode 100644
index 0000000..dae5745
--- /dev/null
+++ b/config/ecex_api_completion_plugin.c
@@ -0,0 +1,806 @@
+#include "ecex.h"
+
+#include <string.h>
+
+#define ECEX_API_PLUGIN_ID "ecex-mode"
+#define ECEX_API_SYMBOLS_PROVIDER "ecex-mode-symbols"
+#define ECEX_API_ED_FIELDS_PROVIDER "ecex-mode-ed-fields"
+#define ECEX_API_SLOT_ENABLED "enabled"
+#if defined(__GNUC__) || defined(__clang__)
+#define ECEX_API_UNUSED __attribute__((unused))
+#else
+#define ECEX_API_UNUSED
+#endif
+#define ECEX_API_SYMBOL_WORDS \
+ "ecex_t\n" \
+ "buffer_t\n" \
+ "ecex_window_t\n" \
+ "ecex_draw_context_t\n" \
+ "ecex_plugin_t\n" \
+ "ecex_plugin_runtime_t\n" \
+ "ecex_command_fn\n" \
+ "ecex_completion_provider_fn\n" \
+ "ecex_file_handler_fn\n" \
+ "ECEX_CONFIG_BEGIN\n" \
+ "ECEX_CONFIG_END\n" \
+ "ECEX_PLUGIN_BEGIN\n" \
+ "ECEX_PLUGIN_END\n" \
+ "ECEX_CONFIG_TRY\n" \
+ "ECEX_CONFIG_COMMAND\n" \
+ "ECEX_CONFIG_BIND\n" \
+ "ECEX_CONFIG_MODE\n" \
+ "ECEX_CONFIG_MODE_BIND\n" \
+ "ECEX_CONFIG_INCLUDE\n" \
+ "ECEX_RGB8\n" \
+ "ECEX_ARRAY_COUNT\n" \
+ "ecex_new\n" \
+ "ecex_free\n" \
+ "ecex_config_alloc\n" \
+ "ecex_config_calloc\n" \
+ "ecex_config_free\n" \
+ "ecex_time_seconds\n" \
+ "ecex_log\n" \
+ "ecex_logf\n" \
+ "ecex_log_group_begin\n" \
+ "ecex_log_group_end\n" \
+ "ecex_log_int\n" \
+ "ecex_log_double\n" \
+ "ecex_log_ptr\n" \
+ "ecex_log_flush\n" \
+ "ecex_mem_zero\n" \
+ "ecex_i32_get\n" \
+ "ecex_i32_set\n" \
+ "ecex_prng_next_bounded\n" \
+ "ecex_random_bounded\n" \
+ "ecex_plugin_register\n" \
+ "ecex_plugin_require\n" \
+ "ecex_plugin_find\n" \
+ "ecex_plugin_id\n" \
+ "ecex_plugin_slot_alloc\n" \
+ "ecex_plugin_slot_get\n" \
+ "ecex_plugin_slot_free\n" \
+ "ecex_plugin_slot_set_export_flags\n" \
+ "ecex_plugin_slot_read_exported\n" \
+ "ecex_plugin_slot_i32_get_scalar\n" \
+ "ecex_plugin_slot_i32_get\n" \
+ "ecex_plugin_slot_i32_set\n" \
+ "ecex_plugin_slot_i32_get_2d\n" \
+ "ecex_plugin_slot_i32_set_2d\n" \
+ "ecex_plugin_slot_i32_set_scalar\n" \
+ "ecex_plugin_object_alloc\n" \
+ "ecex_plugin_object_calloc\n" \
+ "ecex_plugin_object_free\n" \
+ "ecex_plugin_object_valid\n" \
+ "ecex_plugin_object_i32_get\n" \
+ "ecex_plugin_object_i32_set\n" \
+ "ecex_plugin_object_ptr_get\n" \
+ "ecex_plugin_object_ptr_set\n" \
+ "ecex_plugin_text_set\n" \
+ "ecex_plugin_text_set_from_buffer_title\n" \
+ "ecex_plugin_text_free\n" \
+ "ecex_plugin_text_free_all\n" \
+ "ecex_plugin_text_get_drawable\n" \
+ "ecex_plugin_file_handler_register\n" \
+ "ecex_plugin_file_handlers_run\n" \
+ "ecex_buffer_text_len\n" \
+ "ecex_buffer_scroll_line_index\n" \
+ "ecex_buffer_line_count_int\n" \
+ "ecex_buffer_line_copy_text\n" \
+ "ecex_run_plugin_file_handlers\n" \
+ "ecex_reserve_buffers\n" \
+ "ecex_add_buffer\n" \
+ "ecex_create_buffer\n" \
+ "ecex_find_buffer\n" \
+ "ecex_switch_buffer\n" \
+ "ecex_current_buffer\n" \
+ "ecex_other_buffer\n" \
+ "ecex_current_window\n" \
+ "ecex_window_count\n" \
+ "ecex_sync_current_buffer\n" \
+ "ecex_split_window_vertically\n" \
+ "ecex_split_window_horizontally\n" \
+ "ecex_other_window\n" \
+ "ecex_previous_window\n" \
+ "ecex_delete_window\n" \
+ "ecex_delete_other_windows\n" \
+ "ecex_balance_windows\n" \
+ "ecex_next_buffer\n" \
+ "ecex_previous_buffer\n" \
+ "ecex_kill_buffer\n" \
+ "ecex_kill_buffer_force\n" \
+ "ecex_has_modified_buffers\n" \
+ "ecex_validate_bindings\n" \
+ "ecex_keep_jit_module\n" \
+ "ecex_set_config_path\n" \
+ "ecex_config_path\n" \
+ "ecex_reload_config\n" \
+ "ecex_config_register_commands\n" \
+ "ecex_config_bind_keys\n" \
+ "ecex_config_bind_mode_keys\n" \
+ "ecex_config_define_modes\n" \
+ "ecex_apply_theme\n" \
+ "ecex_register_command\n" \
+ "ecex_execute_command\n" \
+ "ecex_add_command_hook\n" \
+ "ecex_remove_command_hook\n" \
+ "ecex_add_prefix_hook\n" \
+ "ecex_remove_prefix_hook\n" \
+ "ecex_notify_prefix_hooks\n" \
+ "ecex_add_buffer_hook\n" \
+ "ecex_remove_buffer_hook\n" \
+ "ecex_notify_buffer_hooks\n" \
+ "ecex_message\n" \
+ "ecex_dependency_available\n" \
+ "ecex_plugin_require_dependency\n" \
+ "ecex_add_completion_provider\n" \
+ "ecex_add_word_completion_provider\n" \
+ "ecex_define_word_completion_provider\n" \
+ "ecex_completion_provider_add_word\n" \
+ "ecex_completion_provider_add_words\n" \
+ "ecex_completion_provider_set_detail\n" \
+ "ecex_add_clangd_completion_provider\n" \
+ "ecex_remove_completion_provider\n" \
+ "ecex_buffer_identifier_prefix\n" \
+ "ecex_complete_at_point\n" \
+ "ecex_set_clipboard_callbacks\n" \
+ "ecex_clipboard_get\n" \
+ "ecex_clipboard_set\n" \
+ "ecex_bind_key\n" \
+ "ecex_lookup_key\n" \
+ "ecex_define_major_mode\n" \
+ "ecex_major_mode_by_name\n" \
+ "ecex_major_mode_name\n" \
+ "ecex_buffer_set_major_mode\n" \
+ "ecex_buffer_set_major_mode_by_name\n" \
+ "ecex_buffer_major_mode_name\n" \
+ "ecex_bind_mode_key\n" \
+ "ecex_lookup_key_for_buffer\n" \
+ "ecex_key_sequence_has_prefix_for_buffer\n" \
+ "ecex_describe_key_prefix\n" \
+ "ecex_auto_set_major_mode\n" \
+ "ecex_list_commands\n" \
+ "ecex_list_buffers\n" \
+ "ecex_create_interactive_buffer\n" \
+ "ecex_interactive_append_line\n" \
+ "ecex_interactive_activate_current_line\n" \
+ "ecex_buffer_set_renderer\n" \
+ "ecex_buffer_clear_renderer\n" \
+ "ecex_buffer_has_renderer\n" \
+ "ecex_buffer_renderer_userdata\n" \
+ "ecex_buffer_set_mouse_handler\n" \
+ "ecex_buffer_clear_mouse_handler\n" \
+ "ecex_buffer_has_mouse_handler\n" \
+ "ecex_buffer_mouse_userdata\n" \
+ "ecex_buffer_set_animation\n" \
+ "ecex_buffer_set_animation_ms\n" \
+ "ecex_buffer_clear_animation\n" \
+ "ecex_buffer_is_animating\n" \
+ "ecex_buffer_animation_userdata\n" \
+ "ecex_tick_animations\n" \
+ "ecex_buffer_replace_text\n" \
+ "ecex_buffer_set_modified\n" \
+ "ecex_draw_set_color\n" \
+ "ecex_draw_rect\n" \
+ "ecex_draw_rect_outline\n" \
+ "ecex_draw_line\n" \
+ "ecex_draw_text\n" \
+ "ecex_draw_text_aligned\n" \
+ "ecex_draw_text_width\n" \
+ "ecex_draw_rgba\n" \
+ "ecex_draw_color_rgba8_i\n" \
+ "ecex_draw_rect_i\n" \
+ "ecex_draw_rect_outline_i\n" \
+ "ecex_draw_line_i\n" \
+ "ecex_draw_text_i\n" \
+ "ecex_draw_plugin_text_i\n" \
+ "ecex_draw_plugin_text_rect_i\n" \
+ "ecex_draw_markdown_canvas_i\n" \
+ "ecex_draw_markdown_text_i\n" \
+ "ecex_draw_markdown_canvas_auto_i\n" \
+ "ecex_draw_markdown_line_auto_i\n" \
+ "ecex_draw_label_i\n" \
+ "ecex_draw_stat_i\n" \
+ "ecex_draw_tetris_preview_i\n" \
+ "ecex_find_file\n" \
+ "ecex_save_current_buffer\n" \
+ "ecex_write_current_buffer\n" \
+ "ecex_compile\n" \
+ "ecex_grep\n" \
+ "ecex_rerun_compile\n" \
+ "ecex_rerun_grep\n" \
+ "ecex_next_interactive_action\n" \
+ "ecex_previous_interactive_action\n" \
+ "ecex_indent_line_to\n" \
+ "ecex_comment_region\n" \
+ "ecex_uncomment_region\n" \
+ "ecex_request_prompt\n" \
+ "ecex_clear_prompt_request\n" \
+ "ecex_complete_command\n" \
+ "ecex_set_font\n" \
+ "ecex_get_font_size\n" \
+ "ecex_set_font_size\n" \
+ "ecex_adjust_font_size\n" \
+ "ecex_set_bg_color\n" \
+ "ecex_set_fg_color\n" \
+ "ecex_set_status_bg_color\n" \
+ "ecex_set_status_fg_color\n" \
+ "ecex_set_status_border_color\n" \
+ "ecex_set_cursor_color\n" \
+ "ecex_set_region_bg_color\n" \
+ "ecex_set_minibuffer_bg_color\n" \
+ "ecex_set_minibuffer_fg_color\n" \
+ "ecex_set_completion_fg_color\n" \
+ "ecex_set_completion_enabled\n" \
+ "ecex_set_interactive_highlight_bg_color\n" \
+ "ecex_set_interactive_highlight_fg_color\n" \
+ "ecex_set_current_line_bg_color\n" \
+ "ecex_set_search_bg_color\n" \
+ "ecex_set_line_numbers_enabled\n" \
+ "ecex_set_current_line_enabled\n" \
+ "buffer_new\n" \
+ "buffer_free\n" \
+ "buffer_reserve\n" \
+ "buffer_clear\n" \
+ "buffer_set_text\n" \
+ "buffer_insert\n" \
+ "buffer_append\n" \
+ "buffer_prepend\n" \
+ "buffer_insert_at\n" \
+ "buffer_insert_char\n" \
+ "buffer_delete_range\n" \
+ "buffer_delete_selection\n" \
+ "buffer_replace_selection\n" \
+ "buffer_backspace\n" \
+ "buffer_delete_forward\n" \
+ "buffer_kill_line\n" \
+ "buffer_undo\n" \
+ "buffer_redo\n" \
+ "buffer_clear_undo\n" \
+ "buffer_set_point\n" \
+ "buffer_move_left\n" \
+ "buffer_move_right\n" \
+ "buffer_move_up\n" \
+ "buffer_move_down\n" \
+ "buffer_move_word_left\n" \
+ "buffer_move_word_right\n" \
+ "buffer_move_beginning_of_line\n" \
+ "buffer_move_end_of_line\n" \
+ "buffer_move_beginning_of_buffer\n" \
+ "buffer_move_end_of_buffer\n" \
+ "buffer_search_forward\n" \
+ "buffer_search_backward\n" \
+ "buffer_set_mark\n" \
+ "buffer_clear_mark\n" \
+ "buffer_has_selection\n" \
+ "buffer_selection_range\n" \
+ "buffer_line_start_at\n" \
+ "buffer_line_end_at\n" \
+ "buffer_current_line_start\n" \
+ "buffer_current_line_end\n" \
+ "buffer_current_column\n" \
+ "buffer_current_line_number\n" \
+ "buffer_line_count\n" \
+ "buffer_substring\n" \
+ "buffer_current_line_copy\n" \
+ "buffer_load_file\n" \
+ "buffer_save\n" \
+ "buffer_save_as\n" \
+ "buffer_set_interactive\n" \
+ "buffer_is_interactive\n" \
+ "buffer_clear_interactive_actions\n" \
+ "buffer_add_interactive_action\n" \
+ "buffer_interactive_action_at_line\n" \
+ "ecex_path_copy\n" \
+ "ecex_path_expand_user\n" \
+ "ecex_path_join\n" \
+ "ecex_path_dirname\n" \
+ "ecex_path_basename_dup\n" \
+ "ecex_path_normalize\n" \
+ "ecex_path_is_dir\n" \
+ "ecex_path_is_file\n" \
+ "ecex_path_exists\n" \
+ "ecex_path_file_size\n" \
+ "ecex_path_is_image\n" \
+ "ecex_path_is_previewable_image\n" \
+ "ecex_path_is_video\n" \
+ "ecex_path_is_media\n" \
+ "ecex_path_cwd\n" \
+ "ecex_media_open\n" \
+ "ecex_media_toggle_playback\n"
+#define ECEX_API_ED_FIELD_WORDS \
+ "buffers\n" \
+ "buffer_cap\n" \
+ "buffer_count\n" \
+ "current_buffer_index\n" \
+ "current_buffer\n" \
+ "previous_buffer\n" \
+ "windows\n" \
+ "window_cap\n" \
+ "window_count\n" \
+ "current_window_index\n" \
+ "jit_modules\n" \
+ "jit_module_cap\n" \
+ "jit_module_count\n" \
+ "commands\n" \
+ "command_cap\n" \
+ "command_count\n" \
+ "keybinds\n" \
+ "keybind_cap\n" \
+ "keybind_count\n" \
+ "mode_keybinds\n" \
+ "mode_keybind_cap\n" \
+ "mode_keybind_count\n" \
+ "command_hooks\n" \
+ "command_hook_cap\n" \
+ "command_hook_count\n" \
+ "prefix_hooks\n" \
+ "prefix_hook_cap\n" \
+ "prefix_hook_count\n" \
+ "buffer_hooks\n" \
+ "buffer_hook_cap\n" \
+ "buffer_hook_count\n" \
+ "completion_providers\n" \
+ "completion_provider_cap\n" \
+ "completion_provider_count\n" \
+ "major_modes\n" \
+ "major_mode_cap\n" \
+ "major_mode_count\n" \
+ "next_major_mode_id\n" \
+ "plugins\n" \
+ "last_eval_source\n" \
+ "last_eval_filename\n" \
+ "last_compile_command\n" \
+ "last_grep_command\n" \
+ "prompt_request\n" \
+ "prompt_message\n" \
+ "config_path\n" \
+ "message\n" \
+ "message_revision\n" \
+ "clipboard_get\n" \
+ "clipboard_set\n" \
+ "clipboard_userdata\n" \
+ "clipboard_text\n" \
+ "should_quit\n" \
+ "ui_revision\n" \
+ "font_revision\n" \
+ "theme\n"
+
+static const char *ecex_api_symbols[] ECEX_API_UNUSED = {
+ "ecex_t",
+ "buffer_t",
+ "ecex_window_t",
+ "ecex_draw_context_t",
+ "ecex_plugin_t",
+ "ecex_plugin_runtime_t",
+ "ecex_command_fn",
+ "ecex_completion_provider_fn",
+ "ecex_file_handler_fn",
+ "ECEX_CONFIG_BEGIN",
+ "ECEX_CONFIG_END",
+ "ECEX_PLUGIN_BEGIN",
+ "ECEX_PLUGIN_END",
+ "ECEX_CONFIG_TRY",
+ "ECEX_CONFIG_COMMAND",
+ "ECEX_CONFIG_BIND",
+ "ECEX_CONFIG_MODE",
+ "ECEX_CONFIG_MODE_BIND",
+ "ECEX_CONFIG_INCLUDE",
+ "ECEX_RGB8",
+ "ECEX_ARRAY_COUNT",
+ "ecex_new",
+ "ecex_free",
+ "ecex_config_alloc",
+ "ecex_config_calloc",
+ "ecex_config_free",
+ "ecex_time_seconds",
+ "ecex_log",
+ "ecex_logf",
+ "ecex_log_group_begin",
+ "ecex_log_group_end",
+ "ecex_log_int",
+ "ecex_log_double",
+ "ecex_log_ptr",
+ "ecex_log_flush",
+ "ecex_mem_zero",
+ "ecex_i32_get",
+ "ecex_i32_set",
+ "ecex_prng_next_bounded",
+ "ecex_random_bounded",
+ "ecex_plugin_register",
+ "ecex_plugin_require",
+ "ecex_plugin_find",
+ "ecex_plugin_id",
+ "ecex_plugin_slot_alloc",
+ "ecex_plugin_slot_get",
+ "ecex_plugin_slot_free",
+ "ecex_plugin_slot_set_export_flags",
+ "ecex_plugin_slot_read_exported",
+ "ecex_plugin_slot_i32_get_scalar",
+ "ecex_plugin_slot_i32_get",
+ "ecex_plugin_slot_i32_set",
+ "ecex_plugin_slot_i32_get_2d",
+ "ecex_plugin_slot_i32_set_2d",
+ "ecex_plugin_slot_i32_set_scalar",
+ "ecex_plugin_object_alloc",
+ "ecex_plugin_object_calloc",
+ "ecex_plugin_object_free",
+ "ecex_plugin_object_valid",
+ "ecex_plugin_object_i32_get",
+ "ecex_plugin_object_i32_set",
+ "ecex_plugin_object_ptr_get",
+ "ecex_plugin_object_ptr_set",
+ "ecex_plugin_text_set",
+ "ecex_plugin_text_set_from_buffer_title",
+ "ecex_plugin_text_free",
+ "ecex_plugin_text_free_all",
+ "ecex_plugin_text_get_drawable",
+ "ecex_plugin_file_handler_register",
+ "ecex_plugin_file_handlers_run",
+ "ecex_buffer_text_len",
+ "ecex_buffer_scroll_line_index",
+ "ecex_buffer_line_count_int",
+ "ecex_buffer_line_copy_text",
+ "ecex_run_plugin_file_handlers",
+ "ecex_reserve_buffers",
+ "ecex_add_buffer",
+ "ecex_create_buffer",
+ "ecex_find_buffer",
+ "ecex_switch_buffer",
+ "ecex_current_buffer",
+ "ecex_other_buffer",
+ "ecex_current_window",
+ "ecex_window_count",
+ "ecex_sync_current_buffer",
+ "ecex_split_window_vertically",
+ "ecex_split_window_horizontally",
+ "ecex_other_window",
+ "ecex_previous_window",
+ "ecex_delete_window",
+ "ecex_delete_other_windows",
+ "ecex_balance_windows",
+ "ecex_next_buffer",
+ "ecex_previous_buffer",
+ "ecex_kill_buffer",
+ "ecex_kill_buffer_force",
+ "ecex_has_modified_buffers",
+ "ecex_validate_bindings",
+ "ecex_keep_jit_module",
+ "ecex_set_config_path",
+ "ecex_config_path",
+ "ecex_reload_config",
+ "ecex_config_register_commands",
+ "ecex_config_bind_keys",
+ "ecex_config_bind_mode_keys",
+ "ecex_config_define_modes",
+ "ecex_apply_theme",
+ "ecex_register_command",
+ "ecex_execute_command",
+ "ecex_add_command_hook",
+ "ecex_remove_command_hook",
+ "ecex_add_prefix_hook",
+ "ecex_remove_prefix_hook",
+ "ecex_notify_prefix_hooks",
+ "ecex_message",
+ "ecex_add_completion_provider",
+ "ecex_remove_completion_provider",
+ "ecex_add_clangd_completion_provider",
+ "ecex_buffer_identifier_prefix",
+ "ecex_complete_at_point",
+ "ecex_set_clipboard_callbacks",
+ "ecex_clipboard_get",
+ "ecex_clipboard_set",
+ "ecex_bind_key",
+ "ecex_lookup_key",
+ "ecex_define_major_mode",
+ "ecex_major_mode_by_name",
+ "ecex_major_mode_name",
+ "ecex_buffer_set_major_mode",
+ "ecex_buffer_set_major_mode_by_name",
+ "ecex_buffer_major_mode_name",
+ "ecex_bind_mode_key",
+ "ecex_lookup_key_for_buffer",
+ "ecex_key_sequence_has_prefix_for_buffer",
+ "ecex_describe_key_prefix",
+ "ecex_auto_set_major_mode",
+ "ecex_list_commands",
+ "ecex_list_buffers",
+ "ecex_create_interactive_buffer",
+ "ecex_interactive_append_line",
+ "ecex_interactive_activate_current_line",
+ "ecex_buffer_set_renderer",
+ "ecex_buffer_clear_renderer",
+ "ecex_buffer_has_renderer",
+ "ecex_buffer_renderer_userdata",
+ "ecex_buffer_set_mouse_handler",
+ "ecex_buffer_clear_mouse_handler",
+ "ecex_buffer_has_mouse_handler",
+ "ecex_buffer_mouse_userdata",
+ "ecex_buffer_set_animation",
+ "ecex_buffer_set_animation_ms",
+ "ecex_buffer_clear_animation",
+ "ecex_buffer_is_animating",
+ "ecex_buffer_animation_userdata",
+ "ecex_tick_animations",
+ "ecex_buffer_replace_text",
+ "ecex_buffer_set_modified",
+ "ecex_draw_set_color",
+ "ecex_draw_rect",
+ "ecex_draw_rect_outline",
+ "ecex_draw_line",
+ "ecex_draw_text",
+ "ecex_draw_text_aligned",
+ "ecex_draw_text_width",
+ "ecex_draw_rgba",
+ "ecex_draw_color_rgba8_i",
+ "ecex_draw_rect_i",
+ "ecex_draw_rect_outline_i",
+ "ecex_draw_line_i",
+ "ecex_draw_text_i",
+ "ecex_draw_plugin_text_i",
+ "ecex_draw_plugin_text_rect_i",
+ "ecex_draw_markdown_canvas_i",
+ "ecex_draw_markdown_text_i",
+ "ecex_draw_markdown_canvas_auto_i",
+ "ecex_draw_markdown_line_auto_i",
+ "ecex_draw_label_i",
+ "ecex_draw_stat_i",
+ "ecex_draw_tetris_preview_i",
+ "ecex_find_file",
+ "ecex_save_current_buffer",
+ "ecex_write_current_buffer",
+ "ecex_compile",
+ "ecex_grep",
+ "ecex_rerun_compile",
+ "ecex_rerun_grep",
+ "ecex_next_interactive_action",
+ "ecex_previous_interactive_action",
+ "ecex_comment_region",
+ "ecex_uncomment_region",
+ "ecex_request_prompt",
+ "ecex_clear_prompt_request",
+ "ecex_complete_command",
+ "ecex_set_font",
+ "ecex_get_font_size",
+ "ecex_set_font_size",
+ "ecex_adjust_font_size",
+ "ecex_set_bg_color",
+ "ecex_set_fg_color",
+ "ecex_set_status_bg_color",
+ "ecex_set_status_fg_color",
+ "ecex_set_status_border_color",
+ "ecex_set_cursor_color",
+ "ecex_set_region_bg_color",
+ "ecex_set_minibuffer_bg_color",
+ "ecex_set_minibuffer_fg_color",
+ "ecex_set_completion_fg_color",
+ "ecex_set_completion_enabled",
+ "ecex_set_interactive_highlight_bg_color",
+ "ecex_set_interactive_highlight_fg_color",
+ "ecex_set_current_line_bg_color",
+ "ecex_set_search_bg_color",
+ "ecex_set_line_numbers_enabled",
+ "ecex_set_current_line_enabled",
+ "buffer_new",
+ "buffer_free",
+ "buffer_reserve",
+ "buffer_clear",
+ "buffer_set_text",
+ "buffer_insert",
+ "buffer_append",
+ "buffer_prepend",
+ "buffer_insert_at",
+ "buffer_insert_char",
+ "buffer_delete_range",
+ "buffer_delete_selection",
+ "buffer_replace_selection",
+ "buffer_backspace",
+ "buffer_delete_forward",
+ "buffer_kill_line",
+ "buffer_undo",
+ "buffer_redo",
+ "buffer_clear_undo",
+ "buffer_set_point",
+ "buffer_move_left",
+ "buffer_move_right",
+ "buffer_move_up",
+ "buffer_move_down",
+ "buffer_move_word_left",
+ "buffer_move_word_right",
+ "buffer_move_beginning_of_line",
+ "buffer_move_end_of_line",
+ "buffer_move_beginning_of_buffer",
+ "buffer_move_end_of_buffer",
+ "buffer_search_forward",
+ "buffer_search_backward",
+ "buffer_set_mark",
+ "buffer_clear_mark",
+ "buffer_has_selection",
+ "buffer_selection_range",
+ "buffer_line_start_at",
+ "buffer_line_end_at",
+ "buffer_current_line_start",
+ "buffer_current_line_end",
+ "buffer_current_column",
+ "buffer_current_line_number",
+ "buffer_line_count",
+ "buffer_substring",
+ "buffer_current_line_copy",
+ "buffer_load_file",
+ "buffer_save",
+ "buffer_save_as",
+ "buffer_set_interactive",
+ "buffer_is_interactive",
+ "buffer_clear_interactive_actions",
+ "buffer_add_interactive_action",
+ "buffer_interactive_action_at_line",
+ "ecex_path_copy",
+ "ecex_path_expand_user",
+ "ecex_path_join",
+ "ecex_path_dirname",
+ "ecex_path_basename_dup",
+ "ecex_path_normalize",
+ "ecex_path_is_dir",
+ "ecex_path_is_file",
+ "ecex_path_exists",
+ "ecex_path_file_size",
+ "ecex_path_is_image",
+ "ecex_path_is_previewable_image",
+ "ecex_path_is_video",
+ "ecex_path_is_media",
+ "ecex_path_cwd",
+ "ecex_media_open",
+ "ecex_media_toggle_playback"
+};
+
+static const char *ecex_ed_fields[] ECEX_API_UNUSED = {
+ "buffers",
+ "buffer_cap",
+ "buffer_count",
+ "current_buffer_index",
+ "current_buffer",
+ "previous_buffer",
+ "windows",
+ "window_cap",
+ "window_count",
+ "current_window_index",
+ "jit_modules",
+ "jit_module_cap",
+ "jit_module_count",
+ "commands",
+ "command_cap",
+ "command_count",
+ "keybinds",
+ "keybind_cap",
+ "keybind_count",
+ "mode_keybinds",
+ "mode_keybind_cap",
+ "mode_keybind_count",
+ "command_hooks",
+ "command_hook_cap",
+ "command_hook_count",
+ "prefix_hooks",
+ "prefix_hook_cap",
+ "prefix_hook_count",
+ "completion_providers",
+ "completion_provider_cap",
+ "completion_provider_count",
+ "major_modes",
+ "major_mode_cap",
+ "major_mode_count",
+ "next_major_mode_id",
+ "plugins",
+ "last_eval_source",
+ "last_eval_filename",
+ "last_compile_command",
+ "last_grep_command",
+ "prompt_request",
+ "prompt_message",
+ "config_path",
+ "message",
+ "message_revision",
+ "clipboard_get",
+ "clipboard_set",
+ "clipboard_userdata",
+ "clipboard_text",
+ "should_quit",
+ "ui_revision",
+ "font_revision",
+ "theme"
+};
+
+static int ecex_api_enabled(ecex_plugin_t *plugin) {
+ return ecex_plugin_slot_i32_get_scalar(plugin, ECEX_API_SLOT_ENABLED, 1) != 0;
+}
+
+static int ecex_api_register_providers(ecex_t *ed) {
+ if (ecex_define_word_completion_provider(ed,
+ ECEX_API_SYMBOLS_PROVIDER,
+ 0,
+ ECEX_COMPLETION_DEFAULT) != 0) {
+ return -1;
+ }
+
+ if (ecex_completion_provider_add_words(ed, ECEX_API_SYMBOLS_PROVIDER, ECEX_API_SYMBOL_WORDS) != 0) {
+ ecex_remove_completion_provider(ed, ECEX_API_SYMBOLS_PROVIDER);
+ return -1;
+ }
+ if (ecex_completion_provider_set_detail(ed, ECEX_API_SYMBOLS_PROVIDER, "ecex API symbol") != 0) {
+ ecex_remove_completion_provider(ed, ECEX_API_SYMBOLS_PROVIDER);
+ return -1;
+ }
+
+ if (ecex_define_word_completion_provider(ed,
+ ECEX_API_ED_FIELDS_PROVIDER,
+ 0,
+ ECEX_COMPLETION_ED_ARROW) != 0) {
+ ecex_remove_completion_provider(ed, ECEX_API_SYMBOLS_PROVIDER);
+ return -1;
+ }
+
+ if (ecex_completion_provider_add_words(ed, ECEX_API_ED_FIELDS_PROVIDER, ECEX_API_ED_FIELD_WORDS) != 0) {
+ ecex_remove_completion_provider(ed, ECEX_API_SYMBOLS_PROVIDER);
+ ecex_remove_completion_provider(ed, ECEX_API_ED_FIELDS_PROVIDER);
+ return -1;
+ }
+ if (ecex_completion_provider_set_detail(ed, ECEX_API_ED_FIELDS_PROVIDER, "ecex_t field") != 0) {
+ ecex_remove_completion_provider(ed, ECEX_API_SYMBOLS_PROVIDER);
+ ecex_remove_completion_provider(ed, ECEX_API_ED_FIELDS_PROVIDER);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void ecex_api_remove_providers(ecex_t *ed) {
+ ecex_remove_completion_provider(ed, ECEX_API_SYMBOLS_PROVIDER);
+ ecex_remove_completion_provider(ed, ECEX_API_ED_FIELDS_PROVIDER);
+}
+
+static int cmd_ecex_api_complete(ecex_t *ed) {
+ return ecex_complete_at_point(ed);
+}
+
+static int cmd_ecex_mode(ecex_t *ed) {
+ buffer_t *buffer = ecex_current_buffer(ed);
+ if (!buffer) return -1;
+ return ecex_buffer_set_major_mode_by_name(ed, buffer, "ecex-mode");
+}
+
+static int cmd_ecex_api_completion_mode(ecex_t *ed) {
+ ecex_plugin_t *plugin = ecex_plugin_find(ed, ECEX_API_PLUGIN_ID);
+ if (!plugin) return -1;
+
+ int enabled = !ecex_api_enabled(plugin);
+ ecex_plugin_slot_i32_set_scalar(plugin, ECEX_API_SLOT_ENABLED, enabled);
+ if (enabled) {
+ if (ecex_api_register_providers(ed) != 0) return -1;
+ } else {
+ ecex_api_remove_providers(ed);
+ }
+ ecex_message(ed, enabled ? "ecex API completion enabled" : "ecex API completion disabled");
+ return 0;
+}
+
+ECEX_PLUGIN_BEGIN(ecex_api_completion_plugin, ECEX_API_PLUGIN_ID)
+ if (!ecex_define_major_mode(ed, "ecex-mode")) return -1;
+
+ if (ecex_plugin_slot_i32_get_scalar(plugin, ECEX_API_SLOT_ENABLED, -1) < 0) {
+ ecex_plugin_slot_i32_set_scalar(plugin, ECEX_API_SLOT_ENABLED, 1);
+ }
+
+ ECEX_CONFIG_COMMAND("ecex-mode", cmd_ecex_mode);
+ ECEX_CONFIG_COMMAND("ecex-mode-complete", cmd_ecex_api_complete);
+ ECEX_CONFIG_COMMAND("ecex-mode-completion-mode", cmd_ecex_api_completion_mode);
+ ECEX_CONFIG_COMMAND("ecex-api-complete", cmd_ecex_api_complete);
+ ECEX_CONFIG_COMMAND("ecex-api-completion-mode", cmd_ecex_api_completion_mode);
+ if (ecex_api_enabled(plugin)) {
+ ECEX_CONFIG_TRY(ecex_api_register_providers(ed));
+ }
+ return 0;
+ECEX_PLUGIN_END
+
+#ifndef ECEX_NO_STANDALONE_CONFIG
+ECEX_CONFIG_BEGIN
+ ECEX_CONFIG_INCLUDE(ecex_api_completion_plugin);
+ECEX_CONFIG_END
+#endif
diff --git a/config/ecexrc.c b/config/ecexrc.c
index 1d13f18..75f2470 100644
--- a/config/ecexrc.c
+++ b/config/ecexrc.c
@@ -5,6 +5,10 @@
#include "render_demo.c"
#include "tetris.c"
#include "markdown_plugin.c"
+#include "which_key_plugin.c"
+#include "c_mode_plugin.c"
+#include "ecex_api_completion_plugin.c"
+#include "c_tools_plugin.c"
#undef ECEX_NO_STANDALONE_CONFIG
#define RGB(r, g, b) ECEX_RGB8(r, g, b)
@@ -141,11 +145,22 @@ ECEX_CONFIG_BEGIN
ECEX_CONFIG_INCLUDE(ecex_render_demo_plugin);
ECEX_CONFIG_INCLUDE(ecex_tetris_plugin);
ECEX_CONFIG_INCLUDE(ecex_markdown_plugin);
+ ECEX_CONFIG_INCLUDE(ecex_which_key_plugin);
+ ECEX_CONFIG_INCLUDE(ecex_c_mode_plugin);
+ ECEX_CONFIG_INCLUDE(ecex_api_completion_plugin);
+ if (ecex_plugin_require_dependency(ed, "c-tools", "clang") == 0 &&
+ ecex_plugin_require_dependency(ed, "c-tools", "clangd") == 0) {
+ ECEX_CONFIG_INCLUDE(ecex_c_tools_plugin);
+ }
buffer_t *scratch = ecex_current_buffer(ed);
buffer_insert(scratch, "ecex config loaded !\n");
- buffer_insert(scratch, "Try F1, C-x C-f, C-x C-s, C-x d file browser, F2 commands, F3 buffers, F4 demo menu, C-x b.\n");
+ buffer_insert(scratch, "Try M-x, C-x C-f, C-x C-s, C-x d file browser, F2 commands, F3 buffers, F4 demo menu, C-x b.\n");
+ buffer_insert(scratch, "which-key is enabled: press C-x or M-g and the minibuffer will show continuations.\n");
+ buffer_insert(scratch, "TAB indents; in C buffers it smart-indents. C-TAB completes and cycles candidates.\n");
+ buffer_insert(scratch, "C completion asks clangd; c-mode highlights C syntax.\n");
+ buffer_insert(scratch, "When c-tools is loaded, C-x c l checks, C-x c k lints, C-x c e lints with ./include.\n");
buffer_insert(scratch, "Run M-x render-demo for a custom renderer demo, or M-x tetris for Tetris.\n");
buffer_insert(scratch, "Example line to edit then C-x C-e: ecex_set_font_size(ed, 28.0f);\n");
buffer_insert(scratch, "Example relative change: ecex_adjust_font_size(ed, 2.0f);\n\n");
diff --git a/config/markdown_plugin.c b/config/markdown_plugin.c
index ded0e1c..1026d31 100644
--- a/config/markdown_plugin.c
+++ b/config/markdown_plugin.c
@@ -3,9 +3,28 @@
#define MD_TEXT_TITLE 1
typedef struct md_state {
- ecex_t *ed;
+ ecex_plugin_t *plugin;
} md_state_t;
+static int md_line_is_fence(const char *line) {
+ const char *p = line;
+ if (!p) return 0;
+ while (*p == ' ' || *p == '\t') ++p;
+ return p[0] == '`' && p[1] == '`' && p[2] == '`';
+}
+
+static int md_in_code_at_line(buffer_t *buffer, int line) {
+ char scratch[256];
+ int in_code = 0;
+ int i;
+ if (!buffer || line <= 0) return 0;
+ for (i = 0; i < line; ++i) {
+ if (ecex_buffer_line_copy_text(buffer, i, scratch, (int)sizeof(scratch)) < 0) continue;
+ if (md_line_is_fence(scratch)) in_code = !in_code;
+ }
+ return in_code;
+}
+
static int md_render(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, void *userdata) {
md_state_t *state = (md_state_t *)userdata;
int draw_count;
@@ -19,31 +38,32 @@ static int md_render(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, voi
if (!ed || !buffer || !ctx || !state) return 0;
- draw_count = ecex_var_i32(ed, state, "draw_count", 0) + 1;
- ecex_var_i32_set_scalar(ed, state, "draw_count", draw_count);
+ draw_count = ecex_plugin_slot_i32_get_scalar(state->plugin, "draw_count", 0) + 1;
+ ecex_plugin_slot_i32_set_scalar(state->plugin, "draw_count", draw_count);
if (draw_count <= 2 || (draw_count % 120) == 0) {
ecex_log_int("markdown_plugin_draw: count=", draw_count);
}
- line_count = ecex_buffer_line_count_i(buffer);
+ line_count = ecex_buffer_line_count_int(buffer);
ecex_log_int("markdown_plugin_draw: line_count=", line_count);
if (line_count < 0) line_count = 0;
- ecex_text_set_buffer_title(ed, state, MD_TEXT_TITLE, buffer);
- ecex_draw_markdown_canvas_auto_i(ctx, state, MD_TEXT_TITLE);
+ ecex_plugin_text_set_from_buffer_title(state->plugin, MD_TEXT_TITLE, buffer);
+ ecex_draw_markdown_canvas_auto_i(ctx, state->plugin, MD_TEXT_TITLE);
- y = ecex_markdown_body_y_i(ctx);
- h = ecex_draw_context_height_i(ctx);
- line_h = ecex_draw_context_line_height_i(ctx);
- scroll = ecex_buffer_scroll_line(buffer);
+ y = ecex_markdown_body_y_px(ctx);
+ h = ecex_draw_context_height_px(ctx);
+ line_h = ecex_draw_context_line_height_px(ctx);
+ scroll = ecex_buffer_scroll_line_index(buffer);
if (scroll < 0) scroll = 0;
if (scroll > line_count) scroll = line_count;
+ in_code = md_in_code_at_line(buffer, scroll);
for (i = scroll; i < line_count && y < h - line_h; ++i) {
int packed;
int advance;
if (i < scroll + 4) ecex_log_int("markdown_plugin_draw: host line=", i);
- packed = ecex_markdown_draw_line_from_buffer_i(ctx, state, buffer, i, y, in_code);
+ packed = ecex_markdown_draw_buffer_line_i(ctx, state->plugin, buffer, i, y, in_code);
in_code = (packed & 0x10000) ? 1 : 0;
advance = packed & 0xffff;
if (advance <= 0) advance = line_h;
@@ -55,12 +75,9 @@ static int md_render(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, voi
static void md_free_state(void *userdata) {
md_state_t *state = (md_state_t *)userdata;
- ecex_t *ed;
if (!state) return;
- ed = state->ed;
- ecex_text_free_owner(ed, state);
- ecex_var_free_owner(ed, state);
- ecex_object_free(ed, state);
+ ecex_plugin_text_free_all(state->plugin);
+ ecex_plugin_object_free(state->plugin, state);
}
static int md_view_buffer(ecex_t *ed, buffer_t *buffer) {
@@ -68,12 +85,13 @@ static int md_view_buffer(ecex_t *ed, buffer_t *buffer) {
if (!ed || !buffer) return -1;
if (ecex_buffer_has_renderer(buffer)) ecex_buffer_clear_renderer(buffer);
- state = (md_state_t *)ecex_object_calloc(ed, 1, sizeof(*state));
+ ecex_plugin_t *plugin = ecex_plugin_find(ed, "markdown");
+ state = (md_state_t *)ecex_plugin_object_calloc(plugin, "state", 1, sizeof(*state));
if (!state) return -1;
- state->ed = ed;
+ state->plugin = plugin;
if (ecex_buffer_set_renderer(buffer, md_render, state, md_free_state, ECEX_RENDER_REPLACE_CONTENT) != 0) {
- ecex_object_free(ed, state);
+ ecex_plugin_object_free(plugin, state);
return -1;
}
ecex_buffer_set_major_mode_by_name(ed, buffer, "markdown-mode");
@@ -105,19 +123,19 @@ static int cmd_markdown_toggle(ecex_t *ed) {
return md_view_buffer(ed, buffer);
}
-int ecex_markdown_plugin(ecex_t *ed) {
+ECEX_PLUGIN_BEGIN(ecex_markdown_plugin, "markdown")
ECEX_CONFIG_MODE("markdown-mode");
ECEX_CONFIG_COMMAND("markdown-view", cmd_markdown_view);
ECEX_CONFIG_COMMAND("markdown-source", cmd_markdown_source);
ECEX_CONFIG_COMMAND("markdown-toggle", cmd_markdown_toggle);
ECEX_CONFIG_MODE_BIND("markdown-mode", "C-c C-c", "markdown-toggle");
ECEX_CONFIG_MODE_BIND("markdown-mode", "C-c C-s", "markdown-source");
- ecex_register_file_handler(ed, ".md", md_file_handler);
- ecex_register_file_handler(ed, ".markdown", md_file_handler);
- ecex_register_file_handler(ed, ".mdown", md_file_handler);
- ecex_register_file_handler(ed, ".mkd", md_file_handler);
+ ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".md", md_file_handler));
+ ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".markdown", md_file_handler));
+ ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".mdown", md_file_handler));
+ ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".mkd", md_file_handler));
return 0;
-}
+ECEX_PLUGIN_END
#ifndef ECEX_NO_STANDALONE_CONFIG
ECEX_CONFIG_BEGIN
diff --git a/config/render_demo.c b/config/render_demo.c
index 761b552..a0fb124 100644
--- a/config/render_demo.c
+++ b/config/render_demo.c
@@ -22,34 +22,33 @@
#define RENDER_DEMO_LABEL_CLICK 25
typedef struct render_demo_state {
- ecex_t *ed;
+ ecex_plugin_t *plugin;
int log_draw_count;
} render_demo_state_t;
-static int render_demo_get(ecex_t *ed, render_demo_state_t *s, const char *name, int fallback) {
- if (!ed || !s || !name) return fallback;
- return ecex_var_i32(ed, s, name, fallback);
+static int render_demo_get(render_demo_state_t *s, const char *name, int fallback) {
+ if (!s || !name) return fallback;
+ return ecex_plugin_slot_i32_get_scalar(s->plugin, name, fallback);
}
-static void render_demo_set(ecex_t *ed, render_demo_state_t *s, const char *name, int value) {
- if (!ed || !s || !name) return;
- ecex_var_i32_set_scalar(ed, s, name, value);
+static void render_demo_set(render_demo_state_t *s, const char *name, int value) {
+ if (!s || !name) return;
+ ecex_plugin_slot_i32_set_scalar(s->plugin, name, value);
}
-static void render_demo_reset(ecex_t *ed, render_demo_state_t *s) {
+static void render_demo_reset(render_demo_state_t *s) {
ecex_log_ptr("render_demo_reset: state=", s);
- if (!ed || !s) return;
+ if (!s) return;
- /* Store mutable demo state in the host variable registry. This mirrors the
- * Tetris plugin flow and avoids relying on CCDJIT struct-field writes for
- * values that must survive renderer, animation, and mouse callbacks.
+ /* Store mutable demo state in plugin slots. This avoids relying on CCDJIT
+ * struct-field writes for values shared by renderer, animation, and mouse callbacks.
* Values are fixed-point integers: 0..1000 represents the available travel
* range inside the demo frame. */
- render_demo_set(ed, s, RENDER_DEMO_VAR_X, 0);
- render_demo_set(ed, s, RENDER_DEMO_VAR_Y, 500);
- render_demo_set(ed, s, RENDER_DEMO_VAR_TARGET_X, 0);
- render_demo_set(ed, s, RENDER_DEMO_VAR_TARGET_Y, 500);
- render_demo_set(ed, s, RENDER_DEMO_VAR_MOVING, 0);
+ render_demo_set(s, RENDER_DEMO_VAR_X, 0);
+ render_demo_set(s, RENDER_DEMO_VAR_Y, 500);
+ render_demo_set(s, RENDER_DEMO_VAR_TARGET_X, 0);
+ render_demo_set(s, RENDER_DEMO_VAR_TARGET_Y, 500);
+ render_demo_set(s, RENDER_DEMO_VAR_MOVING, 0);
s->log_draw_count = 0;
}
@@ -93,27 +92,27 @@ static int render_demo_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *c
if (cw < 1) cw = 1;
if (ch < 1) ch = 1;
- ecex_draw_color_rgba8(ctx, 26, 28, 36, 255);
+ ecex_draw_color_rgba8_i(ctx, 26, 28, 36, 255);
ecex_draw_rect_i(ctx, 0, 0, w, h);
- ecex_draw_color_rgba8(ctx, 48, 96, 180, 255);
+ ecex_draw_color_rgba8_i(ctx, 48, 96, 180, 255);
ecex_draw_rect_i(ctx, cx, cy, (cw * 55) / 100, line_h * 2);
- ecex_draw_color_rgba8(ctx, 242, 230, 191, 255);
+ ecex_draw_color_rgba8_i(ctx, 242, 230, 191, 255);
ecex_draw_rect_outline_i(ctx, cx, cy, cw, ch, 2);
ecex_draw_label_i(ctx, cx + 12, cy + 10, RENDER_DEMO_LABEL_TITLE);
- ecex_draw_color_rgba8(ctx, 229, 89, 64, 255);
+ ecex_draw_color_rgba8_i(ctx, 229, 89, 64, 255);
ecex_draw_line_i(ctx, cx, cy + ch, cx + cw, cy, 3);
- ecex_draw_color_rgba8(ctx, 160, 230, 180, 255);
+ ecex_draw_color_rgba8_i(ctx, 160, 230, 180, 255);
ecex_draw_label_i(ctx, cx + 12, cy + line_h * 3, RENDER_DEMO_LABEL_SUBTITLE);
ecex_draw_label_i(ctx, cx + 12, cy + line_h * 4, RENDER_DEMO_LABEL_SAFE_DRAW);
ecex_draw_label_i(ctx, cx + 12, cy + line_h * 5, RENDER_DEMO_LABEL_ANIM);
ecex_draw_label_i(ctx, cx + 12, cy + line_h * 6, RENDER_DEMO_LABEL_CLICK);
- x_milli = render_demo_get(ed, s, RENDER_DEMO_VAR_X, 0);
- y_milli = render_demo_get(ed, s, RENDER_DEMO_VAR_Y, 500);
+ x_milli = render_demo_get(s, RENDER_DEMO_VAR_X, 0);
+ y_milli = render_demo_get(s, RENDER_DEMO_VAR_Y, 500);
ecex_draw_stat_i(ctx, cx + 12, cy + line_h * 8, RENDER_DEMO_LABEL_POSITION, x_milli);
box = line_h * 2;
@@ -132,20 +131,20 @@ static int render_demo_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *c
box_x = area_x + (travel * x_milli) / 1000;
box_y = area_y + (travel_y * y_milli) / 1000;
- render_demo_set(ed, s, RENDER_DEMO_VAR_BOX_X, box_x);
- render_demo_set(ed, s, RENDER_DEMO_VAR_BOX_Y, box_y);
- render_demo_set(ed, s, RENDER_DEMO_VAR_BOX_SIZE, box);
- render_demo_set(ed, s, RENDER_DEMO_VAR_TRAVEL_X, travel);
- render_demo_set(ed, s, RENDER_DEMO_VAR_TRAVEL_Y, travel_y);
- render_demo_set(ed, s, RENDER_DEMO_VAR_AREA_X, area_x);
- render_demo_set(ed, s, RENDER_DEMO_VAR_AREA_Y, area_y);
+ render_demo_set(s, RENDER_DEMO_VAR_BOX_X, box_x);
+ render_demo_set(s, RENDER_DEMO_VAR_BOX_Y, box_y);
+ render_demo_set(s, RENDER_DEMO_VAR_BOX_SIZE, box);
+ render_demo_set(s, RENDER_DEMO_VAR_TRAVEL_X, travel);
+ render_demo_set(s, RENDER_DEMO_VAR_TRAVEL_Y, travel_y);
+ render_demo_set(s, RENDER_DEMO_VAR_AREA_X, area_x);
+ render_demo_set(s, RENDER_DEMO_VAR_AREA_Y, area_y);
- ecex_draw_color_rgba8(ctx, 70, 76, 90, 255);
+ ecex_draw_color_rgba8_i(ctx, 70, 76, 90, 255);
ecex_draw_rect_outline_i(ctx, area_x, area_y, travel + box, travel_y + box, 1);
- ecex_draw_color_rgba8(ctx, 89, 242, 140, 255);
+ ecex_draw_color_rgba8_i(ctx, 89, 242, 140, 255);
ecex_draw_rect_i(ctx, box_x, box_y, box, box);
- ecex_draw_color_rgba8(ctx, 20, 24, 30, 255);
+ ecex_draw_color_rgba8_i(ctx, 20, 24, 30, 255);
ecex_draw_rect_outline_i(ctx, box_x, box_y, box, box, 2);
return 0;
@@ -185,20 +184,20 @@ static int render_demo_tick(ecex_t *ed, buffer_t *buffer, int now_ms, void *user
(void)buffer;
(void)now_ms;
if (!ed || !s) return 0;
- if (!render_demo_get(ed, s, RENDER_DEMO_VAR_MOVING, 0)) return 0;
+ if (!render_demo_get(s, RENDER_DEMO_VAR_MOVING, 0)) return 0;
- x = render_demo_get(ed, s, RENDER_DEMO_VAR_X, 0);
- y = render_demo_get(ed, s, RENDER_DEMO_VAR_Y, 500);
- target_x = render_demo_get(ed, s, RENDER_DEMO_VAR_TARGET_X, x);
- target_y = render_demo_get(ed, s, RENDER_DEMO_VAR_TARGET_Y, y);
+ x = render_demo_get(s, RENDER_DEMO_VAR_X, 0);
+ y = render_demo_get(s, RENDER_DEMO_VAR_Y, 500);
+ target_x = render_demo_get(s, RENDER_DEMO_VAR_TARGET_X, x);
+ target_y = render_demo_get(s, RENDER_DEMO_VAR_TARGET_Y, y);
next_x = render_demo_delta_step(x, target_x);
next_y = render_demo_delta_step(y, target_y);
- render_demo_set(ed, s, RENDER_DEMO_VAR_X, render_demo_clamp_milli(next_x));
- render_demo_set(ed, s, RENDER_DEMO_VAR_Y, render_demo_clamp_milli(next_y));
+ render_demo_set(s, RENDER_DEMO_VAR_X, render_demo_clamp_milli(next_x));
+ render_demo_set(s, RENDER_DEMO_VAR_Y, render_demo_clamp_milli(next_y));
if (next_x == target_x && next_y == target_y) {
- render_demo_set(ed, s, RENDER_DEMO_VAR_MOVING, 0);
+ render_demo_set(s, RENDER_DEMO_VAR_MOVING, 0);
ecex_log("render_demo_tick: target reached");
}
return 1;
@@ -218,11 +217,11 @@ static int render_demo_mouse(ecex_t *ed, buffer_t *buffer, int event, int x, int
if (!ed || !s || button != ECEX_MOUSE_BUTTON_LEFT) return 0;
if (event != ECEX_MOUSE_PRESS) return 0;
- box = render_demo_get(ed, s, RENDER_DEMO_VAR_BOX_SIZE, 0);
- area_x = render_demo_get(ed, s, RENDER_DEMO_VAR_AREA_X, 0);
- area_y = render_demo_get(ed, s, RENDER_DEMO_VAR_AREA_Y, 0);
- travel_x = render_demo_get(ed, s, RENDER_DEMO_VAR_TRAVEL_X, 0);
- travel_y = render_demo_get(ed, s, RENDER_DEMO_VAR_TRAVEL_Y, 0);
+ box = render_demo_get(s, RENDER_DEMO_VAR_BOX_SIZE, 0);
+ area_x = render_demo_get(s, RENDER_DEMO_VAR_AREA_X, 0);
+ area_y = render_demo_get(s, RENDER_DEMO_VAR_AREA_Y, 0);
+ travel_x = render_demo_get(s, RENDER_DEMO_VAR_TRAVEL_X, 0);
+ travel_y = render_demo_get(s, RENDER_DEMO_VAR_TRAVEL_Y, 0);
if (box <= 0) return 0;
if (x < area_x || x >= area_x + travel_x + box ||
@@ -232,12 +231,12 @@ static int render_demo_mouse(ecex_t *ed, buffer_t *buffer, int event, int x, int
nx = x - area_x - box / 2;
ny = y - area_y - box / 2;
- if (travel_x <= 0) render_demo_set(ed, s, RENDER_DEMO_VAR_TARGET_X, 0);
- else render_demo_set(ed, s, RENDER_DEMO_VAR_TARGET_X, render_demo_clamp_milli((nx * 1000) / travel_x));
- if (travel_y <= 0) render_demo_set(ed, s, RENDER_DEMO_VAR_TARGET_Y, 0);
- else render_demo_set(ed, s, RENDER_DEMO_VAR_TARGET_Y, render_demo_clamp_milli((ny * 1000) / travel_y));
+ if (travel_x <= 0) render_demo_set(s, RENDER_DEMO_VAR_TARGET_X, 0);
+ else render_demo_set(s, RENDER_DEMO_VAR_TARGET_X, render_demo_clamp_milli((nx * 1000) / travel_x));
+ if (travel_y <= 0) render_demo_set(s, RENDER_DEMO_VAR_TARGET_Y, 0);
+ else render_demo_set(s, RENDER_DEMO_VAR_TARGET_Y, render_demo_clamp_milli((ny * 1000) / travel_y));
- render_demo_set(ed, s, RENDER_DEMO_VAR_MOVING, 1);
+ render_demo_set(s, RENDER_DEMO_VAR_MOVING, 1);
ecex_log("render_demo_mouse: target set");
return 1;
}
@@ -245,8 +244,7 @@ static int render_demo_mouse(ecex_t *ed, buffer_t *buffer, int event, int x, int
static void render_demo_free_state(void *userdata) {
render_demo_state_t *s = (render_demo_state_t *)userdata;
if (!s) return;
- ecex_var_free_owner(s->ed, s);
- ecex_config_free(s);
+ ecex_plugin_object_free(s->plugin, s);
}
static int cmd_render_demo(ecex_t *ed) {
@@ -262,10 +260,11 @@ static int cmd_render_demo(ecex_t *ed) {
s = (render_demo_state_t *)ecex_buffer_renderer_userdata(buffer);
if (!s) {
- s = (render_demo_state_t *)ecex_config_calloc(1, sizeof(*s));
+ ecex_plugin_t *plugin = ecex_plugin_find(ed, "render-demo");
+ s = (render_demo_state_t *)ecex_plugin_object_calloc(plugin, "state", 1, sizeof(*s));
if (!s) return -1;
- s->ed = ed;
- render_demo_reset(ed, s);
+ s->plugin = plugin;
+ render_demo_reset(s);
if (ecex_buffer_set_renderer(buffer, render_demo_draw, s, render_demo_free_state, ECEX_RENDER_REPLACE_CONTENT) != 0) {
render_demo_free_state(s);
@@ -290,10 +289,11 @@ static int cmd_render_demo(ecex_t *ed) {
return ecex_switch_buffer(ed, RENDER_DEMO_BUF);
}
-int ecex_render_demo_plugin(ecex_t *ed) {
+ECEX_PLUGIN_BEGIN(ecex_render_demo_plugin, "render-demo")
+ (void)plugin;
ECEX_CONFIG_COMMAND("render-demo", cmd_render_demo);
return 0;
-}
+ECEX_PLUGIN_END
#ifndef ECEX_NO_STANDALONE_CONFIG
ECEX_CONFIG_BEGIN
diff --git a/config/tetris.c b/config/tetris.c
index 542963a..e86ab75 100644
--- a/config/tetris.c
+++ b/config/tetris.c
@@ -9,8 +9,7 @@
#define TETRIS_VAR_NEXT "next_piece"
typedef struct tetris_state {
- ecex_t *ed;
- int *board;
+ ecex_plugin_t *plugin;
int piece;
int rot;
int x;
@@ -34,28 +33,61 @@ static int tetris_idx(int x, int y) {
}
static int tetris_shape_cell(int piece, int rot, int col, int row) {
- return ecex_tetris_shape_cell(piece, rot, col, row);
+ int p = piece % 7;
+ int r = rot & 3;
+ if (p < 0) p += 7;
+ if (col < 0 || col >= 4 || row < 0 || row >= 4) return 0;
+
+ if (p == 0) {
+ if ((r & 1) == 0) return row == 1;
+ return col == 1;
+ }
+ if (p == 1) return (row == 1 || row == 2) && (col == 1 || col == 2);
+ if (p == 2) {
+ if (r == 0) return (row == 1 && col >= 0 && col <= 2) || (row == 2 && col == 1);
+ if (r == 1) return (col == 1 && row >= 0 && row <= 2) || (row == 1 && col == 2);
+ if (r == 2) return (row == 1 && col >= 0 && col <= 2) || (row == 0 && col == 1);
+ return (col == 1 && row >= 0 && row <= 2) || (row == 1 && col == 0);
+ }
+ if (p == 3) {
+ if ((r & 1) == 0) return (row == 1 && (col == 1 || col == 2)) || (row == 2 && (col == 0 || col == 1));
+ return (col == 1 && (row == 0 || row == 1)) || (col == 2 && (row == 1 || row == 2));
+ }
+ if (p == 4) {
+ if ((r & 1) == 0) return (row == 1 && (col == 0 || col == 1)) || (row == 2 && (col == 1 || col == 2));
+ return (col == 2 && (row == 0 || row == 1)) || (col == 1 && (row == 1 || row == 2));
+ }
+ if (p == 5) {
+ if (r == 0) return (row == 1 && col >= 0 && col <= 2) || (row == 0 && col == 0);
+ if (r == 1) return (col == 1 && row >= 0 && row <= 2) || (row == 0 && col == 2);
+ if (r == 2) return (row == 1 && col >= 0 && col <= 2) || (row == 2 && col == 2);
+ return (col == 1 && row >= 0 && row <= 2) || (row == 2 && col == 0);
+ }
+ if (r == 0) return (row == 1 && col >= 0 && col <= 2) || (row == 0 && col == 2);
+ if (r == 1) return (col == 1 && row >= 0 && row <= 2) || (row == 2 && col == 2);
+ if (r == 2) return (row == 1 && col >= 0 && col <= 2) || (row == 2 && col == 0);
+ return (col == 1 && row >= 0 && row <= 2) || (row == 0 && col == 0);
}
-static int *tetris_board(ecex_t *ed, tetris_state_t *s) {
- if (!ed || !s) return 0;
- return (int *)ecex_var_get_or_alloc(ed, s, TETRIS_VAR_BOARD, (size_t)TETRIS_CELLS, sizeof(int));
+static int *tetris_board(tetris_state_t *s) {
+ if (!s) return 0;
+ return (int *)ecex_plugin_slot_alloc(s->plugin, TETRIS_VAR_BOARD, (size_t)TETRIS_CELLS, sizeof(int));
}
-static int tetris_next_piece(ecex_t *ed, tetris_state_t *s) {
- return ecex_var_i32(ed, s, TETRIS_VAR_NEXT, 0);
+static int tetris_next_piece(tetris_state_t *s) {
+ return ecex_plugin_slot_i32_get_scalar(s ? s->plugin : 0, TETRIS_VAR_NEXT, 0);
}
-static void tetris_set_next_piece(ecex_t *ed, tetris_state_t *s, int piece) {
+static void tetris_set_next_piece(tetris_state_t *s, int piece) {
if (piece < 0) piece = 0;
if (piece > 6) piece = piece % 7;
- ecex_var_i32_set_scalar(ed, s, TETRIS_VAR_NEXT, piece);
+ ecex_plugin_slot_i32_set_scalar(s ? s->plugin : 0, TETRIS_VAR_NEXT, piece);
}
-static void tetris_clear_board(ecex_t *ed, tetris_state_t *s) {
+static void tetris_clear_board(tetris_state_t *s) {
int *board;
if (!s) { ecex_log("tetris_clear_board: null state"); return; }
- board = tetris_board(ed, s);
+ board = tetris_board(s);
ecex_log_ptr("tetris_clear_board: board=", board);
ecex_mem_zero(board, (size_t)TETRIS_CELLS * sizeof(int));
ecex_log("tetris_clear_board: done");
@@ -71,11 +103,11 @@ static int tetris_pick(tetris_state_t *s) {
return piece;
}
-static int tetris_collides(ecex_t *ed, tetris_state_t *s, int piece, int rot, int px, int py) {
+static int tetris_collides(tetris_state_t *s, int piece, int rot, int px, int py) {
int r;
int c;
- int *board = tetris_board(ed, s);
+ int *board = tetris_board(s);
if (!s || !board) return 1;
for (r = 0; r < 4; ++r) {
for (c = 0; c < 4; ++c) {
@@ -93,44 +125,44 @@ static int tetris_collides(ecex_t *ed, tetris_state_t *s, int piece, int rot, in
return 0;
}
-static void tetris_spawn(ecex_t *ed, tetris_state_t *s) {
+static void tetris_spawn(tetris_state_t *s) {
int queued;
ecex_log_ptr("tetris_spawn: state=", s);
if (!s) return;
/* The sidebar must show the piece that will become active on the next
- * spawn. Keep that queued value in the host variable registry, then consume
+ * spawn. Keep that queued value in a plugin slot, then consume
* it here and immediately replace it with a freshly-picked preview. */
- queued = tetris_next_piece(ed, s);
+ queued = tetris_next_piece(s);
if (queued < 0 || queued > 6) queued = tetris_pick(s);
s->piece = queued;
- tetris_set_next_piece(ed, s, tetris_pick(s));
+ tetris_set_next_piece(s, tetris_pick(s));
ecex_log_int("tetris_spawn: current_piece=", s->piece);
- ecex_log_int("tetris_spawn: preview_piece=", tetris_next_piece(ed, s));
+ ecex_log_int("tetris_spawn: preview_piece=", tetris_next_piece(s));
s->rot = 0;
s->x = 3;
s->y = -1;
s->last_drop_ms = 0;
- if (tetris_collides(ed, s, s->piece, s->rot, s->x, s->y)) {
+ if (tetris_collides(s, s->piece, s->rot, s->x, s->y)) {
ecex_log("tetris_spawn: immediate collision -> game over");
s->game_over = 1;
}
}
-static void tetris_reset(ecex_t *ed, tetris_state_t *s) {
+static void tetris_reset(tetris_state_t *s) {
ecex_log_ptr("tetris_reset: state=", s);
if (!s) return;
- tetris_clear_board(ed, s);
+ tetris_clear_board(s);
ecex_log("tetris_reset: after clear");
/* Avoid calling libc/time-returning host functions from CCDJIT plugin code.
* Seed from stable host-owned addresses instead; good enough for a toy game
* and much safer for the tiny JIT ABI. */
- s->rng = 0x9e3779b9u ^ (unsigned int)(size_t)s ^ (unsigned int)(size_t)tetris_board(ed, s);
+ s->rng = 0x9e3779b9u ^ (unsigned int)(size_t)s ^ (unsigned int)(size_t)tetris_board(s);
ecex_log_int("tetris_reset: rng=", (int)s->rng);
s->score = 0;
s->lines = 0;
@@ -139,15 +171,15 @@ static void tetris_reset(ecex_t *ed, tetris_state_t *s) {
s->paused = 0;
s->last_drop_ms = 0;
s->drop_interval_ms = 650;
- tetris_set_next_piece(ed, s, tetris_pick(s));
+ tetris_set_next_piece(s, tetris_pick(s));
s->log_tick_count = 0;
s->log_draw_count = 0;
ecex_log("tetris_reset: spawning first piece");
- tetris_spawn(ed, s);
+ tetris_spawn(s);
ecex_log("tetris_reset: done");
}
-static void tetris_lock(ecex_t *ed, tetris_state_t *s) {
+static void tetris_lock(tetris_state_t *s) {
ecex_log_ptr("tetris_lock: state=", s);
int r;
int c;
@@ -156,7 +188,7 @@ static void tetris_lock(ecex_t *ed, tetris_state_t *s) {
int *board;
if (!s) return;
- board = tetris_board(ed, s);
+ board = tetris_board(s);
if (!board) return;
for (r = 0; r < 4; ++r) {
@@ -214,7 +246,7 @@ static void tetris_lock(ecex_t *ed, tetris_state_t *s) {
}
ecex_log_int("tetris_lock: cleared=", cleared);
- tetris_spawn(ed, s);
+ tetris_spawn(s);
}
@@ -244,11 +276,11 @@ static int tetris_top_out_if_hidden(tetris_state_t *s, const char *where) {
return 1;
}
-static int tetris_soft_drop(ecex_t *ed, tetris_state_t *s) {
+static int tetris_soft_drop(tetris_state_t *s) {
if (!s) { ecex_log("tetris_soft_drop: null state"); return 0; }
if (s->game_over || s->paused) return 0;
- if (!tetris_collides(ed, s, s->piece, s->rot, s->x, s->y + 1)) {
+ if (!tetris_collides(s, s->piece, s->rot, s->x, s->y + 1)) {
++s->y;
return 1;
}
@@ -256,12 +288,12 @@ static int tetris_soft_drop(ecex_t *ed, tetris_state_t *s) {
ecex_log_int("tetris_soft_drop: locking piece=", s->piece);
ecex_log_int("tetris_soft_drop: y=", s->y);
if (tetris_top_out_if_hidden(s, "tetris_soft_drop: hidden lock -> game over")) return 1;
- tetris_lock(ed, s);
+ tetris_lock(s);
return 0;
}
-static int tetris_move_horizontal(ecex_t *ed, tetris_state_t *s, int dx) {
+static int tetris_move_horizontal(tetris_state_t *s, int dx) {
int old_x;
int old_y;
int old_rot;
@@ -273,7 +305,7 @@ static int tetris_move_horizontal(ecex_t *ed, tetris_state_t *s, int dx) {
old_y = s->y;
old_rot = s->rot;
- if (!tetris_collides(ed, s, s->piece, s->rot, old_x + dx, old_y)) {
+ if (!tetris_collides(s, s->piece, s->rot, old_x + dx, old_y)) {
s->x = old_x + dx;
/* Horizontal movement must never vertical-kick the active piece.
* Keep these assignments explicit because plugin/JIT callback bugs are
@@ -296,18 +328,18 @@ static int tetris_move_horizontal(ecex_t *ed, tetris_state_t *s, int dx) {
return 0;
}
-static void tetris_hard_drop(ecex_t *ed, tetris_state_t *s) {
+static void tetris_hard_drop(tetris_state_t *s) {
int moved = 0;
if (!s || s->game_over || s->paused) return;
- while (!tetris_collides(ed, s, s->piece, s->rot, s->x, s->y + 1)) {
+ while (!tetris_collides(s, s->piece, s->rot, s->x, s->y + 1)) {
++s->y;
++moved;
}
s->score += moved * 2;
if (tetris_top_out_if_hidden(s, "tetris_hard_drop: hidden lock -> game over")) return;
- tetris_lock(ed, s);
+ tetris_lock(s);
}
static tetris_state_t *tetris_state_for_ed(ecex_t *ed) {
@@ -330,14 +362,14 @@ static int tetris_alpha8(int alpha) {
static void tetris_color(ecex_draw_context_t *ctx, int cell, int alpha) {
alpha = tetris_alpha8(alpha);
- if (cell == 1) ecex_draw_color_rgba8(ctx, 51, 191, 242, alpha);
- else if (cell == 2) ecex_draw_color_rgba8(ctx, 242, 217, 51, alpha);
- else if (cell == 3) ecex_draw_color_rgba8(ctx, 179, 89, 242, alpha);
- else if (cell == 4) ecex_draw_color_rgba8(ctx, 77, 217, 89, alpha);
- else if (cell == 5) ecex_draw_color_rgba8(ctx, 242, 64, 64, alpha);
- else if (cell == 6) ecex_draw_color_rgba8(ctx, 64, 102, 242, alpha);
- else if (cell == 7) ecex_draw_color_rgba8(ctx, 242, 140, 51, alpha);
- else ecex_draw_color_rgba8(ctx, 41, 41, 46, alpha);
+ if (cell == 1) ecex_draw_color_rgba8_i(ctx, 51, 191, 242, alpha);
+ else if (cell == 2) ecex_draw_color_rgba8_i(ctx, 242, 217, 51, alpha);
+ else if (cell == 3) ecex_draw_color_rgba8_i(ctx, 179, 89, 242, alpha);
+ else if (cell == 4) ecex_draw_color_rgba8_i(ctx, 77, 217, 89, alpha);
+ else if (cell == 5) ecex_draw_color_rgba8_i(ctx, 242, 64, 64, alpha);
+ else if (cell == 6) ecex_draw_color_rgba8_i(ctx, 64, 102, 242, alpha);
+ else if (cell == 7) ecex_draw_color_rgba8_i(ctx, 242, 140, 51, alpha);
+ else ecex_draw_color_rgba8_i(ctx, 41, 41, 46, alpha);
}
static void tetris_draw_piece(ecex_draw_context_t *ctx,
@@ -386,7 +418,7 @@ static int tetris_tick(ecex_t *ed, buffer_t *buffer, int now_ms, void *userdata)
if (!s->game_over && !s->paused && now_ms - s->last_drop_ms >= s->drop_interval_ms) {
ecex_log("tetris_tick: dropping piece");
- tetris_soft_drop(ed, s);
+ tetris_soft_drop(s);
s->last_drop_ms = now_ms;
return 1;
}
@@ -413,8 +445,8 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v
if (!s) { ecex_log("tetris_draw: null state"); return 0; }
if (!ctx) { ecex_log("tetris_draw: null ctx"); return 0; }
- board = tetris_board(ed, s);
- if (!board) { ecex_log("tetris_draw: no registry board"); return 0; }
+ board = tetris_board(s);
+ if (!board) { ecex_log("tetris_draw: no plugin board slot"); return 0; }
s->log_draw_count += 1;
if (s->log_draw_count <= 8 || (s->log_draw_count % 60) == 0) {
ecex_log_int("tetris_draw: count=", s->log_draw_count);
@@ -423,7 +455,7 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v
}
if (s->log_draw_count <= 3) ecex_log("tetris_draw: background");
- ecex_draw_color_rgba8(ctx, 20, 23, 28, 255);
+ ecex_draw_color_rgba8_i(ctx, 20, 23, 28, 255);
ecex_draw_rect_i(ctx, 0, 0, (int)ctx->w, (int)ctx->h);
max_board_w = ((int)ctx->content_w * 62) / 100;
@@ -438,9 +470,9 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v
oy = (int)ctx->content_y + 12;
if (s->log_draw_count <= 3) ecex_log("tetris_draw: board frame");
- ecex_draw_color_rgba8(ctx, 8, 9, 12, 255);
+ ecex_draw_color_rgba8_i(ctx, 8, 9, 12, 255);
ecex_draw_rect_i(ctx, ox - 6, oy - 6, board_w + 12, board_h + 12);
- ecex_draw_color_rgba8(ctx, 97, 102, 115, 255);
+ ecex_draw_color_rgba8_i(ctx, 97, 102, 115, 255);
ecex_draw_rect_outline_i(ctx, ox - 6, oy - 6, board_w + 12, board_h + 12, 2);
if (s->log_draw_count <= 3) ecex_log("tetris_draw: cells");
@@ -459,7 +491,7 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v
sy = oy;
if (s->log_draw_count <= 3) ecex_log("tetris_draw: sidebar");
- ecex_draw_color_rgba8(ctx, 235, 235, 214, 255);
+ ecex_draw_color_rgba8_i(ctx, 235, 235, 214, 255);
ecex_log("tetris_draw: sidebar title");
ecex_draw_label_i(ctx, sx, sy, 1);
sy += ((int)ctx->line_height * 16) / 10;
@@ -475,11 +507,11 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v
ecex_draw_label_i(ctx, sx, sy, 5);
sy += ((int)ctx->line_height * 8) / 10;
- ecex_log_int("tetris_draw: preview_piece=", tetris_next_piece(ed, s));
- ecex_draw_tetris_preview_i(ctx, tetris_next_piece(ed, s), sx, sy, (cell * 72) / 100, 255);
+ ecex_log_int("tetris_draw: preview_piece=", tetris_next_piece(s));
+ ecex_draw_tetris_preview_i(ctx, tetris_next_piece(s), sx, sy, (cell * 72) / 100, 255);
sy += cell * 4;
- ecex_draw_color_rgba8(ctx, 184, 189, 199, 255);
+ ecex_draw_color_rgba8_i(ctx, 184, 189, 199, 255);
ecex_log("tetris_draw: sidebar help");
ecex_draw_label_i(ctx, sx, sy, 6); sy += (int)ctx->line_height;
ecex_draw_label_i(ctx, sx, sy, 7); sy += (int)ctx->line_height;
@@ -489,9 +521,9 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v
ecex_draw_label_i(ctx, sx, sy, 11);
if (s->paused || s->game_over) {
- ecex_draw_color_rgba8(ctx, 0, 0, 0, 174);
+ ecex_draw_color_rgba8_i(ctx, 0, 0, 0, 174);
ecex_draw_rect_i(ctx, ox, oy + (board_h * 43) / 100, board_w, ((int)ctx->line_height * 22) / 10);
- ecex_draw_color_rgba8(ctx, 255, 235, 89, 255);
+ ecex_draw_color_rgba8_i(ctx, 255, 235, 89, 255);
ecex_draw_label_i(ctx, ox + cell, oy + (board_h * 43) / 100 + ((int)ctx->line_height * 55) / 100, s->game_over ? 12 : 13);
}
@@ -502,8 +534,7 @@ static int tetris_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, v
static void tetris_free_state(void *userdata) {
tetris_state_t *s = (tetris_state_t *)userdata;
if (!s) return;
- ecex_var_free_owner(s->ed, s);
- ecex_config_free(s);
+ ecex_plugin_object_free(s->plugin, s);
}
static int cmd_tetris(ecex_t *ed) {
@@ -527,25 +558,25 @@ static int cmd_tetris(ecex_t *ed) {
ecex_log_ptr("cmd_tetris: existing state=", s);
if (!s) {
ecex_log_int("cmd_tetris: allocating state bytes=", (int)sizeof(*s));
- s = (tetris_state_t *)ecex_config_calloc(1, sizeof(*s));
+ ecex_plugin_t *plugin = ecex_plugin_find(ed, "tetris");
+ s = (tetris_state_t *)ecex_plugin_object_calloc(plugin, "state", 1, sizeof(*s));
ecex_log_ptr("cmd_tetris: allocated state=", s);
if (!s) { ecex_log("cmd_tetris: allocation failed"); return -1; }
- s->ed = ed;
- ecex_log_int("cmd_tetris: registry board bytes=", (int)((size_t)TETRIS_CELLS * sizeof(int)));
- ecex_log_ptr("cmd_tetris: registry board=", tetris_board(ed, s));
- if (!tetris_board(ed, s)) {
- ecex_log("cmd_tetris: registry board allocation failed");
- ecex_var_free_owner(ed, s);
- ecex_config_free(s);
+ s->plugin = plugin;
+ ecex_log_int("cmd_tetris: board slot bytes=", (int)((size_t)TETRIS_CELLS * sizeof(int)));
+ ecex_log_ptr("cmd_tetris: board slot=", tetris_board(s));
+ if (!tetris_board(s)) {
+ ecex_log("cmd_tetris: board slot allocation failed");
+ ecex_plugin_object_free(plugin, s);
return -1;
}
- tetris_reset(ed, s);
+ tetris_reset(s);
ecex_log("cmd_tetris: setting renderer");
if (ecex_buffer_set_renderer(buffer, tetris_draw, s, tetris_free_state, ECEX_RENDER_REPLACE_CONTENT) != 0) {
ecex_log("cmd_tetris: set_renderer failed");
- ecex_config_free(s);
+ ecex_plugin_object_free(plugin, s);
return -1;
}
@@ -577,19 +608,19 @@ static int cmd_tetris_new(ecex_t *ed) {
ecex_log("cmd_tetris_new: enter");
tetris_state_t *s = tetris_state_for_ed(ed);
if (!s) return cmd_tetris(ed);
- tetris_reset(ed, s);
+ tetris_reset(s);
return 0;
}
static int cmd_tetris_left(ecex_t *ed) {
ecex_log("cmd_tetris_left: enter");
- tetris_move_horizontal(ed, tetris_state_for_ed(ed), -1);
+ tetris_move_horizontal(tetris_state_for_ed(ed), -1);
return 0;
}
static int cmd_tetris_right(ecex_t *ed) {
ecex_log("cmd_tetris_right: enter");
- tetris_move_horizontal(ed, tetris_state_for_ed(ed), 1);
+ tetris_move_horizontal(tetris_state_for_ed(ed), 1);
return 0;
}
@@ -597,7 +628,7 @@ static int cmd_tetris_down(ecex_t *ed) {
ecex_log("cmd_tetris_down: enter");
tetris_state_t *s = tetris_state_for_ed(ed);
if (s) {
- if (tetris_soft_drop(ed, s)) s->score += 1;
+ if (tetris_soft_drop(s)) s->score += 1;
s->last_drop_ms = 0;
}
return 0;
@@ -611,12 +642,12 @@ static int cmd_tetris_rotate(ecex_t *ed) {
if (!s || s->game_over || s->paused) return 0;
nr = (s->rot + 1) & 3;
- if (!tetris_collides(ed, s, s->piece, nr, s->x, s->y)) {
+ if (!tetris_collides(s, s->piece, nr, s->x, s->y)) {
s->rot = nr;
- } else if (!tetris_collides(ed, s, s->piece, nr, s->x - 1, s->y)) {
+ } else if (!tetris_collides(s, s->piece, nr, s->x - 1, s->y)) {
--s->x;
s->rot = nr;
- } else if (!tetris_collides(ed, s, s->piece, nr, s->x + 1, s->y)) {
+ } else if (!tetris_collides(s, s->piece, nr, s->x + 1, s->y)) {
++s->x;
s->rot = nr;
}
@@ -626,7 +657,7 @@ static int cmd_tetris_rotate(ecex_t *ed) {
static int cmd_tetris_drop(ecex_t *ed) {
ecex_log("cmd_tetris_drop: enter");
- tetris_hard_drop(ed, tetris_state_for_ed(ed));
+ tetris_hard_drop(tetris_state_for_ed(ed));
return 0;
}
@@ -645,8 +676,9 @@ static int cmd_tetris_quit(ecex_t *ed) {
return ecex_execute_command(ed, "quit-window");
}
-int ecex_tetris_plugin(ecex_t *ed) {
+ECEX_PLUGIN_BEGIN(ecex_tetris_plugin, "tetris")
ecex_log("ecex_tetris_plugin: enter");
+ (void)plugin;
ECEX_CONFIG_MODE(TETRIS_MODE);
ECEX_CONFIG_COMMAND("tetris", cmd_tetris);
ECEX_CONFIG_COMMAND("tetris-new", cmd_tetris_new);
@@ -673,7 +705,7 @@ int ecex_tetris_plugin(ecex_t *ed) {
ecex_log("ecex_tetris_plugin: registered all commands and keybinds");
return 0;
-}
+ECEX_PLUGIN_END
#ifndef ECEX_NO_STANDALONE_CONFIG
ECEX_CONFIG_BEGIN
diff --git a/config/which_key_plugin.c b/config/which_key_plugin.c
new file mode 100644
index 0000000..8d4fb50
--- /dev/null
+++ b/config/which_key_plugin.c
@@ -0,0 +1,107 @@
+#include "ecex.h"
+
+#define WK_HOOK_NAME "which-key"
+#define WK_SLOT_ENABLED "enabled"
+
+typedef struct which_key_state {
+ ecex_plugin_t *plugin;
+ int showing;
+} which_key_state_t;
+
+static int which_key_enabled(ecex_plugin_t *plugin) {
+ return ecex_plugin_slot_i32_get_scalar(plugin, WK_SLOT_ENABLED, 1) != 0;
+}
+
+static int cmd_which_key_toggle(ecex_t *ed) {
+ ecex_plugin_t *plugin = ecex_plugin_find(ed, "which-key");
+ if (!plugin) return -1;
+
+ int enabled = !which_key_enabled(plugin);
+ ecex_plugin_slot_i32_set_scalar(plugin, WK_SLOT_ENABLED, enabled);
+ ecex_message(ed, enabled ? "which-key enabled" : "which-key disabled");
+ return 0;
+}
+
+static void which_key_prefix_hook(ecex_t *ed, const char *prefix, int event, void *userdata) {
+ which_key_state_t *state = (which_key_state_t *)userdata;
+ char line[1024];
+
+ if (!ed || !state || !state->plugin) return;
+ if (!which_key_enabled(state->plugin)) {
+ if (state->showing) ecex_message(ed, "");
+ state->showing = 0;
+ return;
+ }
+
+ if (event == ECEX_PREFIX_HOOK_CANCEL ||
+ event == ECEX_PREFIX_HOOK_FINISH ||
+ event == ECEX_PREFIX_HOOK_UNDEFINED) {
+ if (state->showing) ecex_message(ed, "");
+ state->showing = 0;
+ return;
+ }
+
+ if (event != ECEX_PREFIX_HOOK_BEGIN && event != ECEX_PREFIX_HOOK_UPDATE) {
+ return;
+ }
+
+ if (ecex_describe_key_prefix(ed, ecex_current_buffer(ed), prefix, line, sizeof(line), 24) >= 0) {
+ ecex_message(ed, line);
+ state->showing = 1;
+ }
+}
+
+static void which_key_command_hook(ecex_t *ed,
+ const char *command,
+ int event,
+ int result,
+ void *userdata) {
+ (void)command;
+ (void)result;
+
+ which_key_state_t *state = (which_key_state_t *)userdata;
+ if (event == ECEX_COMMAND_HOOK_AFTER && state && state->showing) {
+ ecex_plugin_t *plugin = state->plugin ? state->plugin : ecex_plugin_find(ed, "which-key");
+ if (plugin) {
+ ecex_message(ed, "");
+ state->showing = 0;
+ }
+ }
+}
+
+static void which_key_free(void *userdata) {
+ which_key_state_t *state = (which_key_state_t *)userdata;
+ if (!state) return;
+ ecex_plugin_object_free(state->plugin, state);
+}
+
+ECEX_PLUGIN_BEGIN(ecex_which_key_plugin, "which-key")
+ if (ecex_plugin_slot_i32_get_scalar(plugin, WK_SLOT_ENABLED, -1) < 0) {
+ ecex_plugin_slot_i32_set_scalar(plugin, WK_SLOT_ENABLED, 1);
+ }
+
+ ECEX_CONFIG_COMMAND("which-key-toggle", cmd_which_key_toggle);
+
+ which_key_state_t *state = ecex_plugin_object_calloc(plugin, "state", 1, sizeof(*state));
+ if (!state) return -1;
+ state->plugin = plugin;
+
+ if (ecex_add_command_hook(ed, WK_HOOK_NAME, which_key_command_hook, state, 0) != 0) {
+ ecex_plugin_object_free(plugin, state);
+ return -1;
+ }
+
+ if (ecex_add_prefix_hook(ed, WK_HOOK_NAME, which_key_prefix_hook, state, which_key_free) != 0) {
+ ecex_remove_command_hook(ed, WK_HOOK_NAME);
+ ecex_plugin_object_free(plugin, state);
+ return -1;
+ }
+
+ return 0;
+ECEX_PLUGIN_END
+
+#ifndef ECEX_NO_STANDALONE_CONFIG
+ECEX_CONFIG_BEGIN
+ ECEX_CONFIG_INCLUDE(ecex_which_key_plugin);
+ECEX_CONFIG_END
+#endif