diff options
| author | David Moc <personal@cdatgoose.org> | 2026-06-03 04:14:59 +0200 |
|---|---|---|
| committer | David Moc <personal@cdatgoose.org> | 2026-06-03 04:14:59 +0200 |
| commit | aba45a64364457f20e84de4189500f4426e11d53 (patch) | |
| tree | fe42d47f01decd53e28bb187cd3d85de78035a46 /config | |
| parent | c1ccd38b31d722c843ab311338e2b8d1905eb8f8 (diff) | |
Added ffap and fixed an eval memory leakmain
Diffstat (limited to 'config')
| -rw-r--r-- | config/c_mode_plugin.c | 170 | ||||
| -rw-r--r-- | config/ecex_api_completion_plugin.c | 30 | ||||
| -rw-r--r-- | config/ecexrc.c | 5 | ||||
| -rw-r--r-- | config/tetris.c | 9 |
4 files changed, 200 insertions, 14 deletions
diff --git a/config/c_mode_plugin.c b/config/c_mode_plugin.c index e707bca..410f029 100644 --- a/config/c_mode_plugin.c +++ b/config/c_mode_plugin.c @@ -1,10 +1,17 @@ #include "ecex.h" +#include <stdio.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" +#define C_MODE_TAB_WIDTH_DEFAULT 4 + +static int c_mode_indent_width(void) { + int width = ecex_c_mode_tab_width(); + return width > 0 ? width : C_MODE_TAB_WIDTH_DEFAULT; +} 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); @@ -108,6 +115,24 @@ static int c_mode_brace_depth_before(buffer_t *buffer, size_t limit) { return depth; } +static int c_mode_line_indent_cols(buffer_t *buffer, size_t line_start, size_t line_end) { + int cols = 0; + if (!buffer) return 0; + + for (size_t i = line_start; i < line_end; i++) { + if (buffer->data[i] == ' ') { + cols++; + } else if (buffer->data[i] == '\t') { + int width = c_mode_indent_width(); + cols += width - (cols % width); + } else { + break; + } + } + + return cols; +} + static int cmd_c_indent_line(ecex_t *ed) { if (!ed) return -1; buffer_t *buffer = ecex_current_buffer(ed); @@ -120,10 +145,11 @@ static int cmd_c_indent_line(ecex_t *ed) { if (first < line_end && buffer->data[first] == '#') return ecex_indent_line_to(buffer, 0); + int width = c_mode_indent_width(); int depth = c_mode_brace_depth_before(buffer, line_start); - int target = depth * 4; + int target = depth * width; - if (first < line_end && buffer->data[first] == '}') target -= 4; + if (first < line_end && buffer->data[first] == '}') target -= width; size_t prev = c_mode_previous_nonblank_line(buffer, line_start); if (prev != (size_t)-1) { @@ -137,7 +163,7 @@ static int cmd_c_indent_line(ecex_t *ed) { !(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; + target += width; } } @@ -145,10 +171,137 @@ static int cmd_c_indent_line(ecex_t *ed) { return ecex_indent_line_to(buffer, target); } +static int cmd_c_newline_and_indent(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; + if (buffer_insert_char(buffer, '\n') != 0) return -1; + return cmd_c_indent_line(ed); +} + +static int cmd_c_dedent_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); + int current = c_mode_line_indent_cols(buffer, line_start, line_end); + int target = current - c_mode_indent_width(); + 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_set_tab_width_command(ecex_t *ed, int width) { + int actual = ecex_c_mode_set_tab_width(width); + char message[64]; + snprintf(message, sizeof(message), "c-mode tab width: %d", actual); + ecex_message(ed, message); + return 0; +} + +static int cmd_c_tab_width_2(ecex_t *ed) { return c_mode_set_tab_width_command(ed, 2); } +static int cmd_c_tab_width_4(ecex_t *ed) { return c_mode_set_tab_width_command(ed, 4); } +static int cmd_c_tab_width_8(ecex_t *ed) { return c_mode_set_tab_width_command(ed, 8); } + +static int c_mode_path_char(char c) { + return (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '_' || c == '-' || c == '.' || c == '/' || c == '~' || c == '+'; +} + +static int c_mode_copy_range(buffer_t *buffer, size_t start, size_t end, char *out, size_t out_size) { + if (!buffer || !out || out_size == 0 || start > end || end > buffer->len) return -1; + size_t len = end - start; + if (len == 0) return -1; + if (len >= out_size) len = out_size - 1; + memcpy(out, buffer->data + start, len); + out[len] = '\0'; + return 0; +} + +static int c_mode_line_is_include(buffer_t *buffer, size_t line_start, size_t line_end) { + if (!buffer) return 0; + size_t p = line_start; + while (p < line_end && (buffer->data[p] == ' ' || buffer->data[p] == '\t')) p++; + return p + 8 <= line_end && strncmp(buffer->data + p, "#include", 8) == 0; +} + +static int c_mode_quoted_path_at_point(buffer_t *buffer, + size_t line_start, + size_t line_end, + size_t point, + char *out, + size_t out_size) { + int include_line = c_mode_line_is_include(buffer, line_start, line_end); + + for (size_t p = line_start; p < line_end; p++) { + char open = buffer->data[p]; + char close = '\0'; + if (open == '"') close = '"'; + else if (include_line && open == '<') close = '>'; + else continue; + + size_t q = p + 1; + while (q < line_end && buffer->data[q] != close) q++; + if (q >= line_end) return -1; + if (point > p && point <= q) return c_mode_copy_range(buffer, p + 1, q, out, out_size); + p = q; + } + + return -1; +} + +static int c_mode_path_at_point(buffer_t *buffer, char *out, size_t out_size) { + if (out && out_size) out[0] = '\0'; + if (!buffer || !out || out_size == 0 || !buffer->data) return -1; + + size_t point = buffer->point > buffer->len ? buffer->len : buffer->point; + size_t line_start = buffer_line_start_at(buffer, point); + size_t line_end = buffer_line_end_at(buffer, point); + + if (c_mode_quoted_path_at_point(buffer, line_start, line_end, point, out, out_size) == 0) { + return 0; + } + + size_t pos = point; + if (pos == buffer->len || (pos < buffer->len && !c_mode_path_char(buffer->data[pos]))) { + if (pos == 0 || !c_mode_path_char(buffer->data[pos - 1])) return -1; + pos--; + } + + size_t start = pos; + while (start > line_start && c_mode_path_char(buffer->data[start - 1])) start--; + + size_t end = pos; + while (end < line_end && c_mode_path_char(buffer->data[end])) end++; + + return c_mode_copy_range(buffer, start, end, out, out_size); +} + +static int cmd_c_find_file_at_point(ecex_t *ed) { + if (!ed) return -1; + buffer_t *buffer = ecex_current_buffer(ed); + if (!buffer) return -1; + + char path[1024]; + if (c_mode_path_at_point(buffer, path, sizeof(path)) != 0) { + ecex_message(ed, "No file at point"); + return 0; + } + + return ecex_find_project_file(ed, buffer->path, path); +} + +static int cmd_c_jump_to_definition(ecex_t *ed) { + return ecex_clangd_jump_to_definition(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"); @@ -158,10 +311,21 @@ 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-newline-and-indent", cmd_c_newline_and_indent); + ECEX_CONFIG_COMMAND("c-dedent-line", cmd_c_dedent_line); ECEX_CONFIG_COMMAND("c-complete", cmd_c_complete); + ECEX_CONFIG_COMMAND("c-tab-width-2", cmd_c_tab_width_2); + ECEX_CONFIG_COMMAND("c-tab-width-4", cmd_c_tab_width_4); + ECEX_CONFIG_COMMAND("c-tab-width-8", cmd_c_tab_width_8); + ECEX_CONFIG_COMMAND("c-jump-to-definition", cmd_c_jump_to_definition); + ECEX_CONFIG_COMMAND("c-find-file-at-point", cmd_c_find_file_at_point); ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, "c-mode", "TAB", "c-indent-line")); + ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, "c-mode", "S-TAB", "c-dedent-line")); + ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, "c-mode", "ENTER", "c-newline-and-indent")); 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")); + ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, "c-mode", "C-g d", "c-jump-to-definition")); + ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, "c-mode", "C-g f", "c-find-file-at-point")); if (ecex_dependency_available("clangd")) { ECEX_CONFIG_TRY(ecex_add_clangd_completion_provider(ed, diff --git a/config/ecex_api_completion_plugin.c b/config/ecex_api_completion_plugin.c index b806349..367ec09 100644 --- a/config/ecex_api_completion_plugin.c +++ b/config/ecex_api_completion_plugin.c @@ -202,6 +202,8 @@ "ecex_draw_stat_i\n" \ "ecex_draw_tetris_preview_i\n" \ "ecex_find_file\n" \ + "ecex_find_file_at\n" \ + "ecex_find_project_file\n" \ "ecex_save_current_buffer\n" \ "ecex_write_current_buffer\n" \ "ecex_compile\n" \ @@ -211,6 +213,9 @@ "ecex_next_interactive_action\n" \ "ecex_previous_interactive_action\n" \ "ecex_indent_line_to\n" \ + "ecex_clangd_jump_to_definition\n" \ + "ecex_c_mode_set_tab_width\n" \ + "ecex_c_mode_tab_width\n" \ "ecex_comment_region\n" \ "ecex_uncomment_region\n" \ "ecex_request_prompt\n" \ @@ -296,6 +301,7 @@ "ecex_path_dirname\n" \ "ecex_path_basename_dup\n" \ "ecex_path_normalize\n" \ + "ecex_project_root_for_file\n" \ "ecex_path_is_dir\n" \ "ecex_path_is_file\n" \ "ecex_path_exists\n" \ @@ -517,6 +523,8 @@ #define ECEX_API_SIGNATURE_ENTRIES_7 \ "ecex_find_file\tint ecex_find_file(ecex_t *ed, const char *path)\n" \ + "ecex_find_file_at\tint ecex_find_file_at(ecex_t *ed, const char *path, size_t line, size_t column)\n" \ + "ecex_find_project_file\tint ecex_find_project_file(ecex_t *ed, const char *from_file, const char *path)\n" \ "ecex_save_current_buffer\tint ecex_save_current_buffer(ecex_t *ed)\n" \ "ecex_write_current_buffer\tint ecex_write_current_buffer(ecex_t *ed, const char *path)\n" \ "ecex_compile\tint ecex_compile(ecex_t *ed, const char *command)\n" \ @@ -526,6 +534,9 @@ "ecex_next_interactive_action\tint ecex_next_interactive_action(ecex_t *ed)\n" \ "ecex_previous_interactive_action\tint ecex_previous_interactive_action(ecex_t *ed)\n" \ "ecex_indent_line_to\tint ecex_indent_line_to(buffer_t *buffer, int target_cols)\n" \ + "ecex_clangd_jump_to_definition\tint ecex_clangd_jump_to_definition(ecex_t *ed)\n" \ + "ecex_c_mode_set_tab_width\tint ecex_c_mode_set_tab_width(int spaces)\n" \ + "ecex_c_mode_tab_width\tint ecex_c_mode_tab_width(void)\n" \ "ecex_comment_region\tint ecex_comment_region(ecex_t *ed)\n" \ "ecex_uncomment_region\tint ecex_uncomment_region(ecex_t *ed)\n" \ "ecex_request_prompt\tvoid ecex_request_prompt(ecex_t *ed, ecex_prompt_request_t request, const char *message)\n" \ @@ -618,6 +629,7 @@ "ecex_path_dirname\tchar *ecex_path_dirname(const char *path)\n" \ "ecex_path_basename_dup\tchar *ecex_path_basename_dup(const char *path)\n" \ "ecex_path_normalize\tchar *ecex_path_normalize(const char *path)\n" \ + "ecex_project_root_for_file\tchar *ecex_project_root_for_file(const char *path)\n" \ "ecex_path_is_dir\tint ecex_path_is_dir(const char *path)\n" \ "ecex_path_is_file\tint ecex_path_is_file(const char *path)\n" \ "ecex_path_exists\tint ecex_path_exists(const char *path)\n" \ @@ -689,6 +701,9 @@ "last_eval_source\tchar *last_eval_source\n" \ "last_eval_filename\tchar *last_eval_filename\n" \ "last_eval_wrap_as_statements\tint last_eval_wrap_as_statements\n" \ + "eval_modules\tecex_eval_module_t *eval_modules\n" \ + "eval_module_cap\tsize_t eval_module_cap\n" \ + "eval_module_count\tsize_t eval_module_count\n" \ "last_compile_command\tchar *last_compile_command\n" \ "last_grep_command\tchar *last_grep_command\n" \ "prompt_request\tecex_prompt_request_t prompt_request\n" \ @@ -746,6 +761,10 @@ "plugins\n" \ "last_eval_source\n" \ "last_eval_filename\n" \ + "last_eval_wrap_as_statements\n" \ + "eval_modules\n" \ + "eval_module_cap\n" \ + "eval_module_count\n" \ "last_compile_command\n" \ "last_grep_command\n" \ "prompt_request\n" \ @@ -943,6 +962,8 @@ static const char *ecex_api_symbols[] ECEX_API_UNUSED = { "ecex_draw_stat_i", "ecex_draw_tetris_preview_i", "ecex_find_file", + "ecex_find_file_at", + "ecex_find_project_file", "ecex_save_current_buffer", "ecex_write_current_buffer", "ecex_compile", @@ -951,6 +972,10 @@ static const char *ecex_api_symbols[] ECEX_API_UNUSED = { "ecex_rerun_grep", "ecex_next_interactive_action", "ecex_previous_interactive_action", + "ecex_indent_line_to", + "ecex_clangd_jump_to_definition", + "ecex_c_mode_set_tab_width", + "ecex_c_mode_tab_width", "ecex_comment_region", "ecex_uncomment_region", "ecex_request_prompt", @@ -1036,6 +1061,7 @@ static const char *ecex_api_symbols[] ECEX_API_UNUSED = { "ecex_path_dirname", "ecex_path_basename_dup", "ecex_path_normalize", + "ecex_project_root_for_file", "ecex_path_is_dir", "ecex_path_is_file", "ecex_path_exists", @@ -1088,6 +1114,10 @@ static const char *ecex_ed_fields[] ECEX_API_UNUSED = { "plugins", "last_eval_source", "last_eval_filename", + "last_eval_wrap_as_statements", + "eval_modules", + "eval_module_cap", + "eval_module_count", "last_compile_command", "last_grep_command", "prompt_request", diff --git a/config/ecexrc.c b/config/ecexrc.c index 75f2470..dbce318 100644 --- a/config/ecexrc.c +++ b/config/ecexrc.c @@ -158,8 +158,9 @@ ECEX_CONFIG_BEGIN buffer_insert(scratch, "ecex config loaded !\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, "TAB indents; in C buffers ENTER auto-indents, S-TAB dedents, and C-TAB completes.\n"); + buffer_insert(scratch, "C-g d/f use the project root; clangd reads compile_flags.txt, C-g f also reads .ecex-project.\n"); + buffer_insert(scratch, "C completion asks clangd; c-mode highlights C syntax. Use ecex_c_mode_set_tab_width(2/4/8) in config.\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"); diff --git a/config/tetris.c b/config/tetris.c index e86ab75..852b5ee 100644 --- a/config/tetris.c +++ b/config/tetris.c @@ -131,9 +131,6 @@ static void tetris_spawn(tetris_state_t *s) { 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 a plugin slot, then consume - * it here and immediately replace it with a freshly-picked preview. */ queued = tetris_next_piece(s); if (queued < 0 || queued > 6) queued = tetris_pick(s); s->piece = queued; @@ -307,15 +304,9 @@ static int tetris_move_horizontal(tetris_state_t *s, int dx) { 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 - * otherwise very hard to distinguish from game movement. */ s->y = old_y; s->rot = old_rot; - /* Restart the gravity phase after manual lateral input. This avoids the - * visual "bop" where a key repeat lands on the same frame as gravity - * and appears to couple side movement with a vertical step. */ s->last_drop_ms = 0; ecex_log_int("tetris_move_horizontal: x=", s->x); ecex_log_int("tetris_move_horizontal: y=", s->y); |
