diff options
Diffstat (limited to 'config/c_mode_plugin.c')
| -rw-r--r-- | config/c_mode_plugin.c | 170 |
1 files changed, 167 insertions, 3 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, |
