aboutsummaryrefslogtreecommitdiff
path: root/config/c_tools_plugin.c
diff options
context:
space:
mode:
Diffstat (limited to 'config/c_tools_plugin.c')
-rw-r--r--config/c_tools_plugin.c210
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