aboutsummaryrefslogtreecommitdiff
path: root/config
diff options
context:
space:
mode:
Diffstat (limited to 'config')
-rw-r--r--config/c_mode_plugin.c170
-rw-r--r--config/ecex_api_completion_plugin.c30
-rw-r--r--config/ecexrc.c5
-rw-r--r--config/tetris.c9
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);