#include #include #include "el_runtime.h" el_val_t pkey(el_val_t name, el_val_t field); el_val_t put_str(el_val_t name, el_val_t field, el_val_t value); el_val_t put_bool(el_val_t name, el_val_t field, el_val_t value); el_val_t get_str(el_val_t name, el_val_t field); el_val_t get_bool(el_val_t name, el_val_t field); el_val_t index_list(void); el_val_t index_set(el_val_t names); el_val_t index_contains(el_val_t name); el_val_t index_add(el_val_t name); el_val_t register_plugin(el_val_t name, el_val_t version, el_val_t description, el_val_t installed, el_val_t enabled, el_val_t hooks, el_val_t first_party); el_val_t plugin_init(void); el_val_t json_escape_str(el_val_t s); el_val_t quote_json(el_val_t s); el_val_t hooks_to_json(el_val_t hooks_csv); el_val_t plugin_to_json(el_val_t name); el_val_t plugin_list_json(void); el_val_t plugin_get_json(el_val_t name); el_val_t plugin_install(el_val_t name); el_val_t plugin_remove(el_val_t name); el_val_t plugin_enable(el_val_t name); el_val_t plugin_disable(el_val_t name); el_val_t host_main(void); el_val_t pkey(el_val_t name, el_val_t field) { return el_str_concat(el_str_concat(el_str_concat(EL_STR("plugin/"), name), EL_STR("/")), field); return 0; } el_val_t put_str(el_val_t name, el_val_t field, el_val_t value) { state_set(pkey(name, field), value); return 0; } el_val_t put_bool(el_val_t name, el_val_t field, el_val_t value) { if (value) { state_set(pkey(name, field), EL_STR("true")); } if (!value) { state_set(pkey(name, field), EL_STR("false")); } return 0; } el_val_t get_str(el_val_t name, el_val_t field) { return state_get(pkey(name, field)); return 0; } el_val_t get_bool(el_val_t name, el_val_t field) { return str_eq(state_get(pkey(name, field)), EL_STR("true")); return 0; } el_val_t index_list(void) { el_val_t raw = state_get(EL_STR("plugins/index")); if (str_eq(raw, EL_STR(""))) { return native_list_empty(); } return str_split(raw, EL_STR(",")); return 0; } el_val_t index_set(el_val_t names) { state_set(EL_STR("plugins/index"), list_join(names, EL_STR(","))); return 0; } el_val_t index_contains(el_val_t name) { el_val_t names = index_list(); el_val_t n = native_list_len(names); el_val_t i = 0; while (i < n) { if (str_eq(native_list_get(names, i), name)) { return 1; } i = (i + 1); } return 0; return 0; } el_val_t index_add(el_val_t name) { if (!index_contains(name)) { el_val_t names = index_list(); names = native_list_append(names, name); index_set(names); } return 0; } el_val_t register_plugin(el_val_t name, el_val_t version, el_val_t description, el_val_t installed, el_val_t enabled, el_val_t hooks, el_val_t first_party) { put_str(name, EL_STR("name"), name); put_str(name, EL_STR("version"), version); put_str(name, EL_STR("description"), description); put_bool(name, EL_STR("installed"), installed); put_bool(name, EL_STR("enabled"), enabled); put_str(name, EL_STR("hooks"), hooks); put_bool(name, EL_STR("first_party"), first_party); index_add(name); return 0; } el_val_t plugin_init(void) { if (str_eq(state_get(EL_STR("plugins/initialised")), EL_STR("true"))) { return 0; } register_plugin(EL_STR("el-theme-dark"), EL_STR("1.0.0"), EL_STR("Dark theme — the default Engram IDE theme."), 1, 1, EL_STR(""), 1); register_plugin(EL_STR("el-theme-light"), EL_STR("1.0.0"), EL_STR("Light theme for high-ambient-light environments."), 0, 0, EL_STR(""), 1); register_plugin(EL_STR("el-fmt"), EL_STR("0.1.0"), EL_STR("Code formatter — auto-formats .el files on save."), 1, 1, EL_STR("on_save"), 1); register_plugin(EL_STR("el-doc"), EL_STR("0.1.0"), EL_STR("Documentation generator — produces HTML docs from type definitions."), 0, 0, EL_STR("custom_tool"), 1); register_plugin(EL_STR("el-test"), EL_STR("0.1.0"), EL_STR("Test runner with inline results displayed in the editor gutter."), 0, 0, EL_STR("on_build_complete,custom_panel"), 1); state_set(EL_STR("plugins/initialised"), EL_STR("true")); return 0; } el_val_t json_escape_str(el_val_t s) { el_val_t chars = native_string_chars(s); el_val_t n = native_list_len(chars); el_val_t out = EL_STR(""); el_val_t i = 0; while (i < n) { el_val_t ch = native_list_get(chars, i); if (str_eq(ch, EL_STR("\""))) { out = el_str_concat(out, EL_STR("\\\"")); } else { if (str_eq(ch, EL_STR("\\"))) { out = el_str_concat(out, EL_STR("\\\\")); } else { if (str_eq(ch, EL_STR("\n"))) { out = el_str_concat(out, EL_STR("\\n")); } else { out = el_str_concat(out, ch); } } } i = (i + 1); } return out; return 0; } el_val_t quote_json(el_val_t s) { return el_str_concat(el_str_concat(EL_STR("\""), json_escape_str(s)), EL_STR("\"")); return 0; } el_val_t hooks_to_json(el_val_t hooks_csv) { if (str_eq(hooks_csv, EL_STR(""))) { return EL_STR("[]"); } el_val_t xs = str_split(hooks_csv, EL_STR(",")); el_val_t n = native_list_len(xs); el_val_t out = EL_STR("["); el_val_t i = 0; while (i < n) { if (i > 0) { out = el_str_concat(out, EL_STR(",")); } out = el_str_concat(out, quote_json(native_list_get(xs, i))); i = (i + 1); } return el_str_concat(out, EL_STR("]")); return 0; } el_val_t plugin_to_json(el_val_t name) { el_val_t parts = native_list_empty(); parts = native_list_append(parts, el_str_concat(EL_STR("\"name\":"), quote_json(get_str(name, EL_STR("name"))))); parts = native_list_append(parts, el_str_concat(EL_STR("\"version\":"), quote_json(get_str(name, EL_STR("version"))))); parts = native_list_append(parts, el_str_concat(EL_STR("\"description\":"), quote_json(get_str(name, EL_STR("description"))))); el_val_t installed = get_str(name, EL_STR("installed")); el_val_t enabled = get_str(name, EL_STR("enabled")); el_val_t first_party = get_str(name, EL_STR("first_party")); parts = native_list_append(parts, el_str_concat(EL_STR("\"installed\":"), installed)); parts = native_list_append(parts, el_str_concat(EL_STR("\"enabled\":"), enabled)); parts = native_list_append(parts, el_str_concat(EL_STR("\"hooks\":"), hooks_to_json(get_str(name, EL_STR("hooks"))))); parts = native_list_append(parts, el_str_concat(EL_STR("\"first_party\":"), first_party)); return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}")); return 0; } el_val_t plugin_list_json(void) { plugin_init(); el_val_t names = index_list(); el_val_t n = native_list_len(names); el_val_t out = EL_STR("["); el_val_t i = 0; while (i < n) { if (i > 0) { out = el_str_concat(out, EL_STR(",")); } out = el_str_concat(out, plugin_to_json(native_list_get(names, i))); i = (i + 1); } return el_str_concat(out, EL_STR("]")); return 0; } el_val_t plugin_get_json(el_val_t name) { plugin_init(); if (!index_contains(name)) { return EL_STR("null"); } return plugin_to_json(name); return 0; } el_val_t plugin_install(el_val_t name) { plugin_init(); if (!index_contains(name)) { return EL_STR("{\"error\":\"plugin registry is not available (registry URL not configured)\"}"); } if (get_bool(name, EL_STR("installed"))) { return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' is already installed\"}")); } put_bool(name, EL_STR("installed"), 1); put_bool(name, EL_STR("enabled"), 1); return plugin_to_json(name); return 0; } el_val_t plugin_remove(el_val_t name) { plugin_init(); if (!index_contains(name)) { return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' not found\"}")); } if (str_eq(name, EL_STR("el-theme-dark"))) { return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' cannot be removed: it is a required built-in\"}")); } put_bool(name, EL_STR("installed"), 0); put_bool(name, EL_STR("enabled"), 0); return EL_STR("{\"ok\":true}"); return 0; } el_val_t plugin_enable(el_val_t name) { plugin_init(); if (!index_contains(name)) { return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' not found\"}")); } put_bool(name, EL_STR("enabled"), 1); return EL_STR("{\"ok\":true}"); return 0; } el_val_t plugin_disable(el_val_t name) { plugin_init(); if (!index_contains(name)) { return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' not found\"}")); } put_bool(name, EL_STR("enabled"), 0); return EL_STR("{\"ok\":true}"); return 0; } el_val_t host_main(void) { el_val_t argv = args(); el_val_t n = native_list_len(argv); if (n == 0) { println(plugin_list_json()); return 0; } el_val_t cmd = native_list_get(argv, 0); if (str_eq(cmd, EL_STR("list"))) { println(plugin_list_json()); return 0; } if (str_eq(cmd, EL_STR("get"))) { if (n >= 2) { println(plugin_get_json(native_list_get(argv, 1))); } return 0; } if (str_eq(cmd, EL_STR("install"))) { if (n >= 2) { println(plugin_install(native_list_get(argv, 1))); } return 0; } if (str_eq(cmd, EL_STR("remove"))) { if (n >= 2) { println(plugin_remove(native_list_get(argv, 1))); } return 0; } if (str_eq(cmd, EL_STR("enable"))) { if (n >= 2) { println(plugin_enable(native_list_get(argv, 1))); } return 0; } if (str_eq(cmd, EL_STR("disable"))) { if (n >= 2) { println(plugin_disable(native_list_get(argv, 1))); } return 0; } println(EL_STR("usage: el-plugin-host [list|get|install|remove|enable|disable] ")); return 0; } int main(int argc, char** argv) { el_runtime_init_args(argc, argv); host_main(); return 0; }