Files
el/ide/vessels/el-plugin-host/src/main.el
T
Will Anderson 3b76f0f8e0 feat: El port — vessels populated alongside Rust
Adds src/main.el + manifest.el for each vessel in this workspace,
ported from the Rust sources during the El consolidation pass on
2026-04-30. Each vessel now has both Rust (legacy) and El (target)
sources side-by-side; Rust will be removed once the El paths are
verified at runtime, vessel by vessel.

Per-vessel work was split across multiple parallel agents reading the
Rust to understand intent, then designing idiomatic El. Not 1:1
transliteration. Each ported vessel includes:
  - manifest.el per spec/language.md \u00a715.1
  - src/main.el with the vessel's public surface
  - Compile verified via dist/platform/elc + cc against the el_runtime

Known gaps surfaced during the port (held for follow-up): HMAC-SHA256
and base64 crypto, HTTP status code in handler returns, request
headers in handler signatures, subprocess primitives, streaming
responses, struct/enum types, browser/JS codegen target. Codegen bug
list of 9 items tracked separately. The El sources here are runtime-
ready under the canonical C runtime; the gaps are language/runtime
extensions still in flight.
2026-04-30 18:18:39 -05:00

252 lines
9.2 KiB
EmacsLisp

// main.el el-plugin-host
//
// Plugin lifecycle manager for el-ide. Pure-El port of the el-plugin-host
// Rust crate.
//
// Plugins are stored in process-global state via state_get / state_set.
// Each plugin lives under a key prefix `plugin/<name>/<field>`. The
// canonical list of plugin names is maintained at `plugins/index` as a
// comma-separated string (we don't have list serialisation in state yet).
//
// Public API:
// plugin_init() seed first-party plugins (idempotent)
// plugin_list_json() -> String JSON array of all plugins
// plugin_get_json(name) -> String JSON object or "null"
// plugin_install(name) -> String JSON { ok | error }
// plugin_remove(name) -> String
// plugin_enable(name) -> String
// plugin_disable(name) -> String
// State helpers (string K/V)
fn pkey(name: String, field: String) -> String {
"plugin/" + name + "/" + field
}
fn put_str(name: String, field: String, value: String) -> Void {
state_set(pkey(name, field), value)
}
fn put_bool(name: String, field: String, value: Bool) -> Void {
if value { state_set(pkey(name, field), "true") }
if !value { state_set(pkey(name, field), "false") }
}
fn get_str(name: String, field: String) -> String {
state_get(pkey(name, field))
}
fn get_bool(name: String, field: String) -> Bool {
str_eq(state_get(pkey(name, field)), "true")
}
// Plugin index: comma-separated list of names at "plugins/index".
fn index_list() -> [String] {
let raw: String = state_get("plugins/index")
if str_eq(raw, "") { return native_list_empty() }
str_split(raw, ",")
}
fn index_set(names: [String]) -> Void {
state_set("plugins/index", list_join(names, ","))
}
fn index_contains(name: String) -> Bool {
let names: [String] = index_list()
let n: Int = native_list_len(names)
let i: Int = 0
while i < n {
if str_eq(native_list_get(names, i), name) { return true }
let i = i + 1
}
false
}
fn index_add(name: String) -> Void {
if !index_contains(name) {
let names: [String] = index_list()
let names = native_list_append(names, name)
index_set(names)
}
}
// Initialisation
fn register_plugin(name: String, version: String, description: String,
installed: Bool, enabled: Bool, hooks: String,
first_party: Bool) -> Void {
put_str(name, "name", name)
put_str(name, "version", version)
put_str(name, "description", description)
put_bool(name, "installed", installed)
put_bool(name, "enabled", enabled)
put_str(name, "hooks", hooks)
put_bool(name, "first_party", first_party)
index_add(name)
}
fn plugin_init() -> Void {
if str_eq(state_get("plugins/initialised"), "true") {
return
}
register_plugin("el-theme-dark", "1.0.0", "Dark theme — the default Engram IDE theme.", true, true, "", true)
register_plugin("el-theme-light", "1.0.0", "Light theme for high-ambient-light environments.", false, false, "", true)
register_plugin("el-fmt", "0.1.0", "Code formatter — auto-formats .el files on save.", true, true, "on_save", true)
register_plugin("el-doc", "0.1.0", "Documentation generator — produces HTML docs from type definitions.", false, false, "custom_tool", true)
register_plugin("el-test", "0.1.0", "Test runner with inline results displayed in the editor gutter.", false, false, "on_build_complete,custom_panel", true)
state_set("plugins/initialised", "true")
}
// Serialisation
fn json_escape_str(s: String) -> String {
let chars: [String] = native_string_chars(s)
let n: Int = native_list_len(chars)
let out: String = ""
let i: Int = 0
while i < n {
let ch: String = native_list_get(chars, i)
if ch == "\"" { let out = out + "\\\"" }
else { if ch == "\\" { let out = out + "\\\\" }
else { if ch == "\n" { let out = out + "\\n" }
else { let out = out + ch } } }
let i = i + 1
}
out
}
fn quote_json(s: String) -> String {
"\"" + json_escape_str(s) + "\""
}
fn hooks_to_json(hooks_csv: String) -> String {
if str_eq(hooks_csv, "") { return "[]" }
let xs: [String] = str_split(hooks_csv, ",")
let n: Int = native_list_len(xs)
let out: String = "["
let i: Int = 0
while i < n {
if i > 0 { let out = out + "," }
let out = out + quote_json(native_list_get(xs, i))
let i = i + 1
}
out + "]"
}
fn plugin_to_json(name: String) -> String {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, "\"name\":" + quote_json(get_str(name, "name")))
let parts = native_list_append(parts, "\"version\":" + quote_json(get_str(name, "version")))
let parts = native_list_append(parts, "\"description\":" + quote_json(get_str(name, "description")))
let installed: String = get_str(name, "installed")
let enabled: String = get_str(name, "enabled")
let first_party: String = get_str(name, "first_party")
let parts = native_list_append(parts, "\"installed\":" + installed)
let parts = native_list_append(parts, "\"enabled\":" + enabled)
let parts = native_list_append(parts, "\"hooks\":" + hooks_to_json(get_str(name, "hooks")))
let parts = native_list_append(parts, "\"first_party\":" + first_party)
"{" + list_join(parts, ",") + "}"
}
fn plugin_list_json() -> String {
plugin_init()
let names: [String] = index_list()
let n: Int = native_list_len(names)
let out: String = "["
let i: Int = 0
while i < n {
if i > 0 { let out = out + "," }
let out = out + plugin_to_json(native_list_get(names, i))
let i = i + 1
}
out + "]"
}
fn plugin_get_json(name: String) -> String {
plugin_init()
if !index_contains(name) { return "null" }
plugin_to_json(name)
}
// Mutations
fn plugin_install(name: String) -> String {
plugin_init()
if !index_contains(name) {
return "{\"error\":\"plugin registry is not available (registry URL not configured)\"}"
}
if get_bool(name, "installed") {
return "{\"error\":\"plugin '" + json_escape_str(name) + "' is already installed\"}"
}
put_bool(name, "installed", true)
put_bool(name, "enabled", true)
plugin_to_json(name)
}
fn plugin_remove(name: String) -> String {
plugin_init()
if !index_contains(name) {
return "{\"error\":\"plugin '" + json_escape_str(name) + "' not found\"}"
}
if str_eq(name, "el-theme-dark") {
return "{\"error\":\"plugin '" + json_escape_str(name) + "' cannot be removed: it is a required built-in\"}"
}
put_bool(name, "installed", false)
put_bool(name, "enabled", false)
"{\"ok\":true}"
}
fn plugin_enable(name: String) -> String {
plugin_init()
if !index_contains(name) {
return "{\"error\":\"plugin '" + json_escape_str(name) + "' not found\"}"
}
put_bool(name, "enabled", true)
"{\"ok\":true}"
}
fn plugin_disable(name: String) -> String {
plugin_init()
if !index_contains(name) {
return "{\"error\":\"plugin '" + json_escape_str(name) + "' not found\"}"
}
put_bool(name, "enabled", false)
"{\"ok\":true}"
}
// Standalone CLI
fn host_main() -> Void {
let argv: [String] = args()
let n: Int = native_list_len(argv)
if n == 0 {
println(plugin_list_json())
return
}
let cmd: String = native_list_get(argv, 0)
if str_eq(cmd, "list") { println(plugin_list_json()) return }
if str_eq(cmd, "get") {
if n >= 2 { println(plugin_get_json(native_list_get(argv, 1))) }
return
}
if str_eq(cmd, "install") {
if n >= 2 { println(plugin_install(native_list_get(argv, 1))) }
return
}
if str_eq(cmd, "remove") {
if n >= 2 { println(plugin_remove(native_list_get(argv, 1))) }
return
}
if str_eq(cmd, "enable") {
if n >= 2 { println(plugin_enable(native_list_get(argv, 1))) }
return
}
if str_eq(cmd, "disable") {
if n >= 2 { println(plugin_disable(native_list_get(argv, 1))) }
return
}
println("usage: el-plugin-host [list|get|install|remove|enable|disable] <name>")
}
host_main()