diff options
| author | David Moc <personal@cdatgoose.org> | 2026-06-02 13:50:21 +0200 |
|---|---|---|
| committer | David Moc <personal@cdatgoose.org> | 2026-06-02 13:50:21 +0200 |
| commit | a15cb041654ae307add0b998b526c87c3f42bf5f (patch) | |
| tree | 225bb4b70e9fa05aa5f4d2722a1a9cf5fc6fca7f /config/c_tools_plugin.c | |
| parent | 6aeaa171dc1ca43392f53cbd02097f76e1b1c5a0 (diff) | |
Add plugin hooks and mode plugins
Diffstat (limited to 'config/c_tools_plugin.c')
| -rw-r--r-- | config/c_tools_plugin.c | 210 |
1 files changed, 210 insertions, 0 deletions
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 |
