#include <stdint.h>
#include <stdlib.h>
#include "el_runtime.h"

el_val_t is_digit_ch(el_val_t ch);
el_val_t is_alpha_ch(el_val_t ch);
el_val_t is_ident_ch(el_val_t ch);
el_val_t extract_prefix(el_val_t source, el_val_t pos);
el_val_t extract_token(el_val_t source, el_val_t pos);
el_val_t pos_to_line_col(el_val_t source, el_val_t pos);
el_val_t json_escape(el_val_t s);
el_val_t json_str(el_val_t s);
el_val_t json_kv_str(el_val_t k, el_val_t v);
el_val_t json_kv_int(el_val_t k, el_val_t v);
el_val_t json_kv_float(el_val_t k, el_val_t v);
el_val_t json_kv_raw(el_val_t k, el_val_t raw_v);
el_val_t json_array_of(el_val_t items);
el_val_t keywords_list(void);
el_val_t builtin_type_names(void);
el_val_t builtin_type_doc(el_val_t name);
el_val_t builtin_fns(void);
el_val_t keyword_doc(el_val_t kw);
el_val_t first_ident_in(el_val_t rest);
el_val_t scan_decls(el_val_t source);
el_val_t list_contains_str(el_val_t xs, el_val_t needle);
el_val_t prefix_score(el_val_t label, el_val_t prefix);
el_val_t completion_json(el_val_t label, el_val_t kind, el_val_t detail, el_val_t doc, el_val_t score);
el_val_t lsp_complete(el_val_t source, el_val_t pos);
el_val_t lsp_hover(el_val_t source, el_val_t pos);
el_val_t diag_json(el_val_t message, el_val_t severity, el_val_t line, el_val_t col);
el_val_t lsp_diagnostics(el_val_t source);
el_val_t outline_item_json(el_val_t kind, el_val_t name, el_val_t line);
el_val_t lsp_outline(el_val_t source);
el_val_t format_strip_trailing(el_val_t line);
el_val_t lsp_format(el_val_t source);
el_val_t lsp_type_graph(el_val_t source);
el_val_t collect_struct_fields(el_val_t source, el_val_t type_name);
el_val_t referenced_type(el_val_t field_str, el_val_t known_types);
el_val_t lsp_definition(el_val_t source, el_val_t pos);
el_val_t lsp_references(el_val_t source, el_val_t pos);
el_val_t lsp_jsonrpc(el_val_t method, el_val_t params_json);
el_val_t read_headers(void);
el_val_t lsp_stdio_loop(void);
el_val_t lsp_main(void);
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 jstr(el_val_t s);
el_val_t jerr(el_val_t msg);
el_val_t ok_obj(void);
el_val_t url_decode(el_val_t s);
el_val_t hex_digit(el_val_t ch);
el_val_t hex_to_int(el_val_t hex);
el_val_t char_from_code(el_val_t code);
el_val_t split_path_query(el_val_t path);
el_val_t query_get(el_val_t query, el_val_t key);
el_val_t cfg_port(void);
el_val_t cfg_project_path(void);
el_val_t cfg_engram_url(void);
el_val_t cfg_el_binary(void);
el_val_t safe_join(el_val_t rel);
el_val_t route_status(void);
el_val_t route_config(void);
el_val_t route_files_list(el_val_t query);
el_val_t route_file_read(el_val_t query);
el_val_t route_file_write(el_val_t body);
el_val_t route_lsp_complete(el_val_t query);
el_val_t route_lsp_hover(el_val_t query);
el_val_t route_lsp_errors(el_val_t query);
el_val_t route_lsp_jsonrpc(el_val_t body);
el_val_t route_outline(el_val_t query);
el_val_t route_definition(el_val_t query);
el_val_t route_references(el_val_t query);
el_val_t route_format(el_val_t body);
el_val_t route_type_graph(el_val_t query);
el_val_t active_theme(void);
el_val_t route_themes_list(void);
el_val_t route_themes_set(el_val_t body);
el_val_t route_plugins_list(void);
el_val_t route_plugins_install(el_val_t body);
el_val_t route_plugins_remove(el_val_t body);
el_val_t route_plugins_enable(el_val_t body);
el_val_t route_plugins_disable(el_val_t body);
el_val_t route_snippets(void);
el_val_t settings_path(void);
el_val_t default_settings_json(void);
el_val_t route_settings_get(void);
el_val_t route_settings_save(el_val_t body);
el_val_t route_settings_reset(void);
el_val_t route_build_or_run(el_val_t action, el_val_t body);
el_val_t route_build(el_val_t body);
el_val_t route_run(el_val_t body);
el_val_t route_reason(el_val_t body);
el_val_t route_activate_preview(el_val_t query);
el_val_t route_index(void);
el_val_t route_health(void);
el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body);
el_val_t server_main(void);

el_val_t is_digit_ch(el_val_t ch) {
  if (str_eq(ch, EL_STR("0"))) {
    return 1;
  }
  if (str_eq(ch, EL_STR("1"))) {
    return 1;
  }
  if (str_eq(ch, EL_STR("2"))) {
    return 1;
  }
  if (str_eq(ch, EL_STR("3"))) {
    return 1;
  }
  if (str_eq(ch, EL_STR("4"))) {
    return 1;
  }
  if (str_eq(ch, EL_STR("5"))) {
    return 1;
  }
  if (str_eq(ch, EL_STR("6"))) {
    return 1;
  }
  if (str_eq(ch, EL_STR("7"))) {
    return 1;
  }
  if (str_eq(ch, EL_STR("8"))) {
    return 1;
  }
  if (str_eq(ch, EL_STR("9"))) {
    return 1;
  }
  return 0;
  return 0;
}

el_val_t is_alpha_ch(el_val_t ch) {
  el_val_t lower = str_to_lower(ch);
  if (str_eq(ch, EL_STR(""))) {
    return 0;
  }
  if (!str_eq(lower, str_to_upper(lower))) {
    return 1;
  }
  if (str_contains(EL_STR("abcdefghijklmnopqrstuvwxyz"), ch)) {
    return 1;
  }
  if (str_contains(EL_STR("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), ch)) {
    return 1;
  }
  return 0;
  return 0;
}

el_val_t is_ident_ch(el_val_t ch) {
  if (is_digit_ch(ch)) {
    return 1;
  }
  if (is_alpha_ch(ch)) {
    return 1;
  }
  if (str_eq(ch, EL_STR("_"))) {
    return 1;
  }
  return 0;
  return 0;
}

el_val_t extract_prefix(el_val_t source, el_val_t pos) {
  el_val_t n = str_len(source);
  el_val_t end = pos;
  if (end > n) {
    end = n;
  }
  el_val_t start = end;
  el_val_t chars = native_string_chars(source);
  el_val_t total = native_list_len(chars);
  el_val_t scanning = 1;
  while (scanning) {
    if (start <= 0) {
      scanning = 0;
    } else {
      el_val_t prev = native_list_get(chars, (start - 1));
      if (is_ident_ch(prev)) {
        start = (start - 1);
      } else {
        scanning = 0;
      }
    }
  }
  return str_slice(source, start, end);
  return 0;
}

el_val_t extract_token(el_val_t source, el_val_t pos) {
  el_val_t n = str_len(source);
  if (pos > n) {
    return EL_STR("");
  }
  el_val_t chars = native_string_chars(source);
  el_val_t total = native_list_len(chars);
  el_val_t start = pos;
  el_val_t go_left = 1;
  while (go_left) {
    if (start <= 0) {
      go_left = 0;
    } else {
      el_val_t prev = native_list_get(chars, (start - 1));
      if (is_ident_ch(prev)) {
        start = (start - 1);
      } else {
        go_left = 0;
      }
    }
  }
  el_val_t end = pos;
  el_val_t go_right = 1;
  while (go_right) {
    if (end >= total) {
      go_right = 0;
    } else {
      el_val_t cur = native_list_get(chars, end);
      if (is_ident_ch(cur)) {
        end = (end + 1);
      } else {
        go_right = 0;
      }
    }
  }
  return str_slice(source, start, end);
  return 0;
}

el_val_t pos_to_line_col(el_val_t source, el_val_t pos) {
  el_val_t n = str_len(source);
  el_val_t cap = pos;
  if (cap > n) {
    cap = n;
  }
  el_val_t chars = native_string_chars(source);
  el_val_t i = 0;
  el_val_t line = 1;
  el_val_t col = 1;
  while (i < cap) {
    el_val_t ch = native_list_get(chars, i);
    if (str_eq(ch, EL_STR("\n"))) {
      line = (line + 1);
      col = 1;
    } else {
      col = (col + 1);
    }
    i = (i + 1);
  }
  return el_map_new(2, "line", line, "col", col);
  return 0;
}

el_val_t json_escape(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 {
          if (str_eq(ch, EL_STR("\r"))) {
            out = el_str_concat(out, EL_STR("\\r"));
          } else {
            if (str_eq(ch, EL_STR("\t"))) {
              out = el_str_concat(out, EL_STR("\\t"));
            } else {
              out = el_str_concat(out, ch);
            }
          }
        }
      }
    }
    i = (i + 1);
  }
  return out;
  return 0;
}

el_val_t json_str(el_val_t s) {
  return el_str_concat(el_str_concat(EL_STR("\""), json_escape(s)), EL_STR("\""));
  return 0;
}

el_val_t json_kv_str(el_val_t k, el_val_t v) {
  return el_str_concat(el_str_concat(json_str(k), EL_STR(":")), json_str(v));
  return 0;
}

el_val_t json_kv_int(el_val_t k, el_val_t v) {
  return el_str_concat(el_str_concat(json_str(k), EL_STR(":")), int_to_str(v));
  return 0;
}

el_val_t json_kv_float(el_val_t k, el_val_t v) {
  return el_str_concat(el_str_concat(json_str(k), EL_STR(":")), float_to_str(v));
  return 0;
}

el_val_t json_kv_raw(el_val_t k, el_val_t raw_v) {
  return el_str_concat(el_str_concat(json_str(k), EL_STR(":")), raw_v);
  return 0;
}

el_val_t json_array_of(el_val_t items) {
  el_val_t n = native_list_len(items);
  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, native_list_get(items, i));
    i = (i + 1);
  }
  return el_str_concat(out, EL_STR("]"));
  return 0;
}

el_val_t keywords_list(void) {
  el_val_t ks = native_list_empty();
  ks = native_list_append(ks, EL_STR("let"));
  ks = native_list_append(ks, EL_STR("fn"));
  ks = native_list_append(ks, EL_STR("type"));
  ks = native_list_append(ks, EL_STR("enum"));
  ks = native_list_append(ks, EL_STR("match"));
  ks = native_list_append(ks, EL_STR("return"));
  ks = native_list_append(ks, EL_STR("activate"));
  ks = native_list_append(ks, EL_STR("where"));
  ks = native_list_append(ks, EL_STR("sealed"));
  ks = native_list_append(ks, EL_STR("if"));
  ks = native_list_append(ks, EL_STR("else"));
  ks = native_list_append(ks, EL_STR("for"));
  ks = native_list_append(ks, EL_STR("in"));
  ks = native_list_append(ks, EL_STR("while"));
  ks = native_list_append(ks, EL_STR("true"));
  ks = native_list_append(ks, EL_STR("false"));
  ks = native_list_append(ks, EL_STR("test"));
  ks = native_list_append(ks, EL_STR("seed"));
  ks = native_list_append(ks, EL_STR("assert"));
  ks = native_list_append(ks, EL_STR("target"));
  ks = native_list_append(ks, EL_STR("protocol"));
  ks = native_list_append(ks, EL_STR("impl"));
  ks = native_list_append(ks, EL_STR("import"));
  ks = native_list_append(ks, EL_STR("from"));
  ks = native_list_append(ks, EL_STR("as"));
  ks = native_list_append(ks, EL_STR("with"));
  ks = native_list_append(ks, EL_STR("retry"));
  ks = native_list_append(ks, EL_STR("times"));
  ks = native_list_append(ks, EL_STR("fallback"));
  ks = native_list_append(ks, EL_STR("reason"));
  ks = native_list_append(ks, EL_STR("parallel"));
  ks = native_list_append(ks, EL_STR("trace"));
  ks = native_list_append(ks, EL_STR("requires"));
  ks = native_list_append(ks, EL_STR("deploy"));
  ks = native_list_append(ks, EL_STR("to"));
  ks = native_list_append(ks, EL_STR("via"));
  ks = native_list_append(ks, EL_STR("vessel"));
  ks = native_list_append(ks, EL_STR("cgi"));
  return ks;
  return 0;
}

el_val_t builtin_type_names(void) {
  el_val_t ts = native_list_empty();
  ts = native_list_append(ts, EL_STR("Int"));
  ts = native_list_append(ts, EL_STR("Float"));
  ts = native_list_append(ts, EL_STR("String"));
  ts = native_list_append(ts, EL_STR("Bool"));
  ts = native_list_append(ts, EL_STR("Uuid"));
  ts = native_list_append(ts, EL_STR("Void"));
  ts = native_list_append(ts, EL_STR("Any"));
  return ts;
  return 0;
}

el_val_t builtin_type_doc(el_val_t name) {
  if (str_eq(name, EL_STR("Int"))) {
    return EL_STR("64-bit signed integer");
  }
  if (str_eq(name, EL_STR("Float"))) {
    return EL_STR("64-bit IEEE 754 double");
  }
  if (str_eq(name, EL_STR("String"))) {
    return EL_STR("UTF-8 string");
  }
  if (str_eq(name, EL_STR("Bool"))) {
    return EL_STR("Boolean value");
  }
  if (str_eq(name, EL_STR("Uuid"))) {
    return EL_STR("RFC 4122 UUID");
  }
  if (str_eq(name, EL_STR("Void"))) {
    return EL_STR("Unit type — no value");
  }
  if (str_eq(name, EL_STR("Any"))) {
    return EL_STR("Dynamically-typed value");
  }
  return EL_STR("");
  return 0;
}

el_val_t builtin_fns(void) {
  el_val_t xs = native_list_empty();
  xs = native_list_append(xs, EL_STR("println"));
  xs = native_list_append(xs, EL_STR("fn(value: String) -> Void"));
  xs = native_list_append(xs, EL_STR("print"));
  xs = native_list_append(xs, EL_STR("fn(value: String) -> Void"));
  xs = native_list_append(xs, EL_STR("readline"));
  xs = native_list_append(xs, EL_STR("fn() -> String"));
  xs = native_list_append(xs, EL_STR("args"));
  xs = native_list_append(xs, EL_STR("fn() -> [String]"));
  xs = native_list_append(xs, EL_STR("int_to_str"));
  xs = native_list_append(xs, EL_STR("fn(n: Int) -> String"));
  xs = native_list_append(xs, EL_STR("str_to_int"));
  xs = native_list_append(xs, EL_STR("fn(s: String) -> Int"));
  xs = native_list_append(xs, EL_STR("str_len"));
  xs = native_list_append(xs, EL_STR("fn(s: String) -> Int"));
  xs = native_list_append(xs, EL_STR("str_slice"));
  xs = native_list_append(xs, EL_STR("fn(s: String, start: Int, end: Int) -> String"));
  xs = native_list_append(xs, EL_STR("str_concat"));
  xs = native_list_append(xs, EL_STR("fn(a: String, b: String) -> String"));
  xs = native_list_append(xs, EL_STR("str_contains"));
  xs = native_list_append(xs, EL_STR("fn(s: String, sub: String) -> Bool"));
  xs = native_list_append(xs, EL_STR("str_starts_with"));
  xs = native_list_append(xs, EL_STR("fn(s: String, p: String) -> Bool"));
  xs = native_list_append(xs, EL_STR("str_ends_with"));
  xs = native_list_append(xs, EL_STR("fn(s: String, suf: String) -> Bool"));
  xs = native_list_append(xs, EL_STR("str_to_upper"));
  xs = native_list_append(xs, EL_STR("fn(s: String) -> String"));
  xs = native_list_append(xs, EL_STR("str_to_lower"));
  xs = native_list_append(xs, EL_STR("fn(s: String) -> String"));
  xs = native_list_append(xs, EL_STR("str_split"));
  xs = native_list_append(xs, EL_STR("fn(s: String, sep: String) -> [String]"));
  xs = native_list_append(xs, EL_STR("str_trim"));
  xs = native_list_append(xs, EL_STR("fn(s: String) -> String"));
  xs = native_list_append(xs, EL_STR("str_replace"));
  xs = native_list_append(xs, EL_STR("fn(s: String, from: String, to: String) -> String"));
  xs = native_list_append(xs, EL_STR("str_index_of"));
  xs = native_list_append(xs, EL_STR("fn(s: String, sub: String) -> Int"));
  xs = native_list_append(xs, EL_STR("float_to_str"));
  xs = native_list_append(xs, EL_STR("fn(f: Float) -> String"));
  xs = native_list_append(xs, EL_STR("str_to_float"));
  xs = native_list_append(xs, EL_STR("fn(s: String) -> Float"));
  xs = native_list_append(xs, EL_STR("math_sqrt"));
  xs = native_list_append(xs, EL_STR("fn(n: Float) -> Float"));
  xs = native_list_append(xs, EL_STR("math_log"));
  xs = native_list_append(xs, EL_STR("fn(n: Float) -> Float"));
  xs = native_list_append(xs, EL_STR("math_sin"));
  xs = native_list_append(xs, EL_STR("fn(n: Float) -> Float"));
  xs = native_list_append(xs, EL_STR("math_cos"));
  xs = native_list_append(xs, EL_STR("fn(n: Float) -> Float"));
  xs = native_list_append(xs, EL_STR("math_pi"));
  xs = native_list_append(xs, EL_STR("fn() -> Float"));
  xs = native_list_append(xs, EL_STR("el_abs"));
  xs = native_list_append(xs, EL_STR("fn(n: Int) -> Int"));
  xs = native_list_append(xs, EL_STR("el_min"));
  xs = native_list_append(xs, EL_STR("fn(a: Int, b: Int) -> Int"));
  xs = native_list_append(xs, EL_STR("el_max"));
  xs = native_list_append(xs, EL_STR("fn(a: Int, b: Int) -> Int"));
  xs = native_list_append(xs, EL_STR("el_list_empty"));
  xs = native_list_append(xs, EL_STR("fn() -> [Any]"));
  xs = native_list_append(xs, EL_STR("el_list_len"));
  xs = native_list_append(xs, EL_STR("fn(l: [Any]) -> Int"));
  xs = native_list_append(xs, EL_STR("el_list_get"));
  xs = native_list_append(xs, EL_STR("fn(l: [Any], i: Int) -> Any"));
  xs = native_list_append(xs, EL_STR("el_list_append"));
  xs = native_list_append(xs, EL_STR("fn(l: [Any], v: Any) -> [Any]"));
  xs = native_list_append(xs, EL_STR("list_join"));
  xs = native_list_append(xs, EL_STR("fn(l: [String], sep: String) -> String"));
  xs = native_list_append(xs, EL_STR("list_range"));
  xs = native_list_append(xs, EL_STR("fn(start: Int, end: Int) -> [Int]"));
  xs = native_list_append(xs, EL_STR("el_map_get"));
  xs = native_list_append(xs, EL_STR("fn(m: Map, k: String) -> Any"));
  xs = native_list_append(xs, EL_STR("el_map_set"));
  xs = native_list_append(xs, EL_STR("fn(m: Map, k: String, v: Any) -> Map"));
  xs = native_list_append(xs, EL_STR("json_parse"));
  xs = native_list_append(xs, EL_STR("fn(s: String) -> Any"));
  xs = native_list_append(xs, EL_STR("json_stringify"));
  xs = native_list_append(xs, EL_STR("fn(v: Any) -> String"));
  xs = native_list_append(xs, EL_STR("json_get_string"));
  xs = native_list_append(xs, EL_STR("fn(j: String, k: String) -> String"));
  xs = native_list_append(xs, EL_STR("json_get_int"));
  xs = native_list_append(xs, EL_STR("fn(j: String, k: String) -> Int"));
  xs = native_list_append(xs, EL_STR("json_get_bool"));
  xs = native_list_append(xs, EL_STR("fn(j: String, k: String) -> Bool"));
  xs = native_list_append(xs, EL_STR("http_get"));
  xs = native_list_append(xs, EL_STR("fn(url: String) -> String"));
  xs = native_list_append(xs, EL_STR("http_post"));
  xs = native_list_append(xs, EL_STR("fn(url: String, body: String) -> String"));
  xs = native_list_append(xs, EL_STR("fs_read"));
  xs = native_list_append(xs, EL_STR("fn(path: String) -> String"));
  xs = native_list_append(xs, EL_STR("fs_write"));
  xs = native_list_append(xs, EL_STR("fn(path: String, content: String) -> Bool"));
  xs = native_list_append(xs, EL_STR("fs_list"));
  xs = native_list_append(xs, EL_STR("fn(path: String) -> [String]"));
  xs = native_list_append(xs, EL_STR("env"));
  xs = native_list_append(xs, EL_STR("fn(key: String) -> String"));
  xs = native_list_append(xs, EL_STR("uuid_new"));
  xs = native_list_append(xs, EL_STR("fn() -> String"));
  xs = native_list_append(xs, EL_STR("time_now"));
  xs = native_list_append(xs, EL_STR("fn() -> Int"));
  xs = native_list_append(xs, EL_STR("time_now_utc"));
  xs = native_list_append(xs, EL_STR("fn() -> Int"));
  xs = native_list_append(xs, EL_STR("sleep_ms"));
  xs = native_list_append(xs, EL_STR("fn(ms: Int) -> Void"));
  xs = native_list_append(xs, EL_STR("engram_node"));
  xs = native_list_append(xs, EL_STR("fn(content: String, kind: String, salience: Float) -> String"));
  xs = native_list_append(xs, EL_STR("engram_search"));
  xs = native_list_append(xs, EL_STR("fn(q: String, limit: Int) -> [Map]"));
  xs = native_list_append(xs, EL_STR("engram_activate"));
  xs = native_list_append(xs, EL_STR("fn(q: String, depth: Int) -> [Map]"));
  xs = native_list_append(xs, EL_STR("engram_neighbors"));
  xs = native_list_append(xs, EL_STR("fn(id: String) -> [Map]"));
  xs = native_list_append(xs, EL_STR("engram_connect"));
  xs = native_list_append(xs, EL_STR("fn(from: String, to: String, w: Float, rel: String) -> Void"));
  xs = native_list_append(xs, EL_STR("dharma_connect"));
  xs = native_list_append(xs, EL_STR("fn(cgi: String) -> String"));
  xs = native_list_append(xs, EL_STR("dharma_send"));
  xs = native_list_append(xs, EL_STR("fn(channel: String, content: String) -> String"));
  xs = native_list_append(xs, EL_STR("dharma_emit"));
  xs = native_list_append(xs, EL_STR("fn(event: String, payload: String) -> Void"));
  xs = native_list_append(xs, EL_STR("dharma_field"));
  xs = native_list_append(xs, EL_STR("fn(event: String) -> Map"));
  xs = native_list_append(xs, EL_STR("llm_call"));
  xs = native_list_append(xs, EL_STR("fn(model: String, prompt: String) -> String"));
  return xs;
  return 0;
}

el_val_t keyword_doc(el_val_t kw) {
  if (str_eq(kw, EL_STR("let"))) {
    return EL_STR("let name: Type = expr — declare an immutable binding.");
  }
  if (str_eq(kw, EL_STR("fn"))) {
    return EL_STR("fn name(params) -> Ret { body } — define a function.");
  }
  if (str_eq(kw, EL_STR("type"))) {
    return EL_STR("type Name { field: Type } — struct definition.");
  }
  if (str_eq(kw, EL_STR("enum"))) {
    return EL_STR("enum Name { Variant1, Variant2(Type) } — enum definition.");
  }
  if (str_eq(kw, EL_STR("match"))) {
    return EL_STR("match expr { Pattern => body } — pattern match.");
  }
  if (str_eq(kw, EL_STR("return"))) {
    return EL_STR("return value — exit the enclosing function.");
  }
  if (str_eq(kw, EL_STR("if"))) {
    return EL_STR("if cond { ... } else { ... } — conditional.");
  }
  if (str_eq(kw, EL_STR("for"))) {
    return EL_STR("for item in collection { ... } — iterate.");
  }
  if (str_eq(kw, EL_STR("while"))) {
    return EL_STR("while cond { ... } — loop while cond is truthy.");
  }
  if (str_eq(kw, EL_STR("import"))) {
    return EL_STR("import \"path.el\" — pull in another module.");
  }
  if (str_eq(kw, EL_STR("from"))) {
    return EL_STR("from module import { Name } — selective import.");
  }
  if (str_eq(kw, EL_STR("vessel"))) {
    return EL_STR("vessel \"name\" { ... } — manifest declaration.");
  }
  if (str_eq(kw, EL_STR("cgi"))) {
    return EL_STR("cgi \"name\" { dharma_id: ... } — CGI identity.");
  }
  if (str_eq(kw, EL_STR("activate"))) {
    return EL_STR("activate Type where \"query\" — spreading-activation retrieval.");
  }
  if (str_eq(kw, EL_STR("sealed"))) {
    return EL_STR("sealed { ... } — capability-restricted block.");
  }
  if (str_eq(kw, EL_STR("protocol"))) {
    return EL_STR("protocol Name { fn method(self) -> R } — trait-like.");
  }
  if (str_eq(kw, EL_STR("impl"))) {
    return EL_STR("impl Protocol for Type { ... } — protocol implementation.");
  }
  if (str_eq(kw, EL_STR("with"))) {
    return EL_STR("with retry/fallback clause.");
  }
  if (str_eq(kw, EL_STR("retry"))) {
    return EL_STR("retry times N — retry policy.");
  }
  if (str_eq(kw, EL_STR("fallback"))) {
    return EL_STR("fallback { default } — fallback on failure.");
  }
  if (str_eq(kw, EL_STR("true"))) {
    return EL_STR("Boolean literal.");
  }
  if (str_eq(kw, EL_STR("false"))) {
    return EL_STR("Boolean literal.");
  }
  return EL_STR("");
  return 0;
}

el_val_t first_ident_in(el_val_t rest) {
  el_val_t trimmed = str_trim(rest);
  el_val_t n = str_len(trimmed);
  if (n == 0) {
    return EL_STR("");
  }
  el_val_t chars = native_string_chars(trimmed);
  el_val_t i = 0;
  el_val_t total = native_list_len(chars);
  while (i < total) {
    el_val_t ch = native_list_get(chars, i);
    if (!is_ident_ch(ch)) {
      return str_slice(trimmed, 0, i);
    }
    i = (i + 1);
  }
  return trimmed;
  return 0;
}

el_val_t scan_decls(el_val_t source) {
  el_val_t lines = str_split(source, EL_STR("\n"));
  el_val_t n = native_list_len(lines);
  el_val_t types = native_list_empty();
  el_val_t kinds = native_list_empty();
  el_val_t fns = native_list_empty();
  el_val_t tlines = native_list_empty();
  el_val_t flines = native_list_empty();
  el_val_t i = 0;
  while (i < n) {
    el_val_t line = native_list_get(lines, i);
    el_val_t trimmed = str_trim(line);
    el_val_t lineno = (i + 1);
    if (str_starts_with(trimmed, EL_STR("fn "))) {
      el_val_t name = first_ident_in(str_slice(trimmed, 3, str_len(trimmed)));
      if (!str_eq(name, EL_STR(""))) {
        fns = native_list_append(fns, name);
        flines = native_list_append(flines, lineno);
      }
    } else {
      if (str_starts_with(trimmed, EL_STR("type "))) {
        el_val_t name = first_ident_in(str_slice(trimmed, 5, str_len(trimmed)));
        if (!str_eq(name, EL_STR(""))) {
          types = native_list_append(types, name);
          kinds = native_list_append(kinds, EL_STR("type"));
          tlines = native_list_append(tlines, lineno);
        }
      } else {
        if (str_starts_with(trimmed, EL_STR("enum "))) {
          el_val_t name = first_ident_in(str_slice(trimmed, 5, str_len(trimmed)));
          if (!str_eq(name, EL_STR(""))) {
            types = native_list_append(types, name);
            kinds = native_list_append(kinds, EL_STR("enum"));
            tlines = native_list_append(tlines, lineno);
          }
        } else {
          if (str_starts_with(trimmed, EL_STR("protocol "))) {
            el_val_t name = first_ident_in(str_slice(trimmed, 9, str_len(trimmed)));
            if (!str_eq(name, EL_STR(""))) {
              types = native_list_append(types, name);
              kinds = native_list_append(kinds, EL_STR("protocol"));
              tlines = native_list_append(tlines, lineno);
            }
          }
        }
      }
    }
    i = (i + 1);
  }
  return el_map_new(5, "types", types, "type_kind", kinds, "fns", fns, "type_lines", tlines, "fn_lines", flines);
  return 0;
}

el_val_t list_contains_str(el_val_t xs, el_val_t needle) {
  el_val_t n = native_list_len(xs);
  el_val_t i = 0;
  while (i < n) {
    if (str_eq(native_list_get(xs, i), needle)) {
      return 1;
    }
    i = (i + 1);
  }
  return 0;
  return 0;
}

el_val_t prefix_score(el_val_t label, el_val_t prefix) {
  if (str_eq(prefix, EL_STR(""))) {
    return el_from_float(0.5);
  }
  el_val_t lower_label = str_to_lower(label);
  el_val_t lower_prefix = str_to_lower(prefix);
  if (str_starts_with(lower_label, lower_prefix)) {
    el_val_t lp = str_len(lower_prefix);
    el_val_t ll = str_len(lower_label);
    if (ll == 0) {
      return el_from_float(0.0);
    }
    return (int_to_float(lp) / int_to_float(ll));
  }
  return el_from_float(0.0);
  return 0;
}

el_val_t completion_json(el_val_t label, el_val_t kind, el_val_t detail, el_val_t doc, el_val_t score) {
  el_val_t parts = native_list_empty();
  parts = native_list_append(parts, json_kv_str(EL_STR("label"), label));
  parts = native_list_append(parts, json_kv_str(EL_STR("kind"), kind));
  parts = native_list_append(parts, json_kv_str(EL_STR("detail"), detail));
  if (str_eq(doc, EL_STR(""))) {
    parts = native_list_append(parts, json_kv_raw(EL_STR("documentation"), EL_STR("null")));
  } else {
    parts = native_list_append(parts, json_kv_str(EL_STR("documentation"), doc));
  }
  parts = native_list_append(parts, json_kv_float(EL_STR("score"), score));
  return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}"));
  return 0;
}

el_val_t lsp_complete(el_val_t source, el_val_t pos) {
  el_val_t prefix = extract_prefix(source, pos);
  el_val_t lower_prefix = str_to_lower(prefix);
  el_val_t prefix_empty = str_eq(prefix, EL_STR(""));
  el_val_t items = native_list_empty();
  el_val_t kws = keywords_list();
  el_val_t nk = native_list_len(kws);
  el_val_t i = 0;
  while (i < nk) {
    el_val_t kw = native_list_get(kws, i);
    if (prefix_empty) {
      el_val_t s = prefix_score(kw, prefix);
      items = native_list_append(items, completion_json(kw, EL_STR("keyword"), EL_STR("keyword"), keyword_doc(kw), s));
    } else {
      if (str_starts_with(kw, prefix)) {
        el_val_t s = prefix_score(kw, prefix);
        items = native_list_append(items, completion_json(kw, EL_STR("keyword"), EL_STR("keyword"), keyword_doc(kw), s));
      }
    }
    i = (i + 1);
  }
  el_val_t bts = builtin_type_names();
  el_val_t nb = native_list_len(bts);
  i = 0;
  while (i < nb) {
    el_val_t name = native_list_get(bts, i);
    el_val_t lname = str_to_lower(name);
    if (prefix_empty) {
      el_val_t s = (el_from_float(0.5) + el_from_float(0.1));
      items = native_list_append(items, completion_json(name, EL_STR("type"), builtin_type_doc(name), el_str_concat(EL_STR("Built-in type: "), builtin_type_doc(name)), s));
    } else {
      if (str_starts_with(lname, lower_prefix)) {
        el_val_t s = el_str_concat(prefix_score(name, prefix), el_from_float(0.1));
        items = native_list_append(items, completion_json(name, EL_STR("type"), builtin_type_doc(name), el_str_concat(EL_STR("Built-in type: "), builtin_type_doc(name)), s));
      }
    }
    i = (i + 1);
  }
  el_val_t decls = scan_decls(source);
  el_val_t utypes = el_get_field(decls, EL_STR("types"));
  el_val_t ukinds = el_get_field(decls, EL_STR("type_kind"));
  el_val_t nu = native_list_len(utypes);
  i = 0;
  while (i < nu) {
    el_val_t name = native_list_get(utypes, i);
    el_val_t kind = native_list_get(ukinds, i);
    el_val_t lname = str_to_lower(name);
    el_val_t include = 0;
    if (prefix_empty) {
      include = 1;
    }
    if (str_starts_with(lname, lower_prefix)) {
      include = 1;
    }
    if (include) {
      el_val_t s = el_str_concat(prefix_score(name, prefix), el_from_float(0.15));
      items = native_list_append(items, completion_json(name, EL_STR("type"), el_str_concat(el_str_concat(kind, EL_STR(" ")), name), el_str_concat(el_str_concat(el_str_concat(EL_STR("User-defined "), kind), EL_STR(" ")), name), s));
    }
    i = (i + 1);
  }
  el_val_t bfns = builtin_fns();
  el_val_t nf = native_list_len(bfns);
  i = 0;
  while (i < nf) {
    el_val_t name = native_list_get(bfns, i);
    el_val_t sig = native_list_get(bfns, (i + 1));
    el_val_t lname = str_to_lower(name);
    el_val_t include = 0;
    if (prefix_empty) {
      include = 1;
    }
    if (str_starts_with(lname, lower_prefix)) {
      include = 1;
    }
    if (include) {
      el_val_t s = el_str_concat(prefix_score(name, prefix), el_from_float(0.11));
      items = native_list_append(items, completion_json(name, EL_STR("function"), sig, el_str_concat(el_str_concat(el_str_concat(EL_STR("Built-in: "), name), EL_STR(" :: ")), sig), s));
    }
    i = (i + 2);
  }
  el_val_t ufns = el_get_field(decls, EL_STR("fns"));
  el_val_t nuf = native_list_len(ufns);
  i = 0;
  while (i < nuf) {
    el_val_t name = native_list_get(ufns, i);
    el_val_t lname = str_to_lower(name);
    el_val_t include = 0;
    if (prefix_empty) {
      include = 1;
    }
    if (str_starts_with(lname, lower_prefix)) {
      include = 1;
    }
    if (include) {
      el_val_t s = el_str_concat(prefix_score(name, prefix), el_from_float(0.12));
      items = native_list_append(items, completion_json(name, EL_STR("function"), el_str_concat(EL_STR("fn "), name), el_str_concat(EL_STR("User function: "), name), s));
    }
    i = (i + 1);
  }
  return json_array_of(items);
  return 0;
}

el_val_t lsp_hover(el_val_t source, el_val_t pos) {
  el_val_t token = extract_token(source, pos);
  if (str_eq(token, EL_STR(""))) {
    return EL_STR("null");
  }
  el_val_t doc = builtin_type_doc(token);
  if (!str_eq(doc, EL_STR(""))) {
    el_val_t parts = native_list_empty();
    parts = native_list_append(parts, json_kv_str(EL_STR("type_name"), token));
    parts = native_list_append(parts, json_kv_str(EL_STR("documentation"), doc));
    parts = native_list_append(parts, json_kv_raw(EL_STR("engram_node_type"), EL_STR("null")));
    return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}"));
  }
  el_val_t decls = scan_decls(source);
  el_val_t utypes = el_get_field(decls, EL_STR("types"));
  el_val_t ukinds = el_get_field(decls, EL_STR("type_kind"));
  el_val_t nu = native_list_len(utypes);
  el_val_t i = 0;
  while (i < nu) {
    el_val_t name = native_list_get(utypes, i);
    if (str_eq(name, token)) {
      el_val_t kind = native_list_get(ukinds, i);
      el_val_t parts = native_list_empty();
      parts = native_list_append(parts, json_kv_str(EL_STR("type_name"), token));
      parts = native_list_append(parts, json_kv_str(EL_STR("documentation"), el_str_concat(el_str_concat(kind, EL_STR(" ")), token)));
      parts = native_list_append(parts, json_kv_raw(EL_STR("engram_node_type"), EL_STR("null")));
      return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}"));
    }
    i = (i + 1);
  }
  el_val_t ufns = el_get_field(decls, EL_STR("fns"));
  el_val_t nuf = native_list_len(ufns);
  i = 0;
  while (i < nuf) {
    el_val_t name = native_list_get(ufns, i);
    if (str_eq(name, token)) {
      el_val_t parts = native_list_empty();
      parts = native_list_append(parts, json_kv_str(EL_STR("type_name"), token));
      parts = native_list_append(parts, json_kv_str(EL_STR("documentation"), el_str_concat(EL_STR("fn "), token)));
      parts = native_list_append(parts, json_kv_raw(EL_STR("engram_node_type"), EL_STR("null")));
      return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}"));
    }
    i = (i + 1);
  }
  el_val_t bfns = builtin_fns();
  el_val_t nf = native_list_len(bfns);
  i = 0;
  while (i < nf) {
    el_val_t name = native_list_get(bfns, i);
    if (str_eq(name, token)) {
      el_val_t sig = native_list_get(bfns, (i + 1));
      el_val_t parts = native_list_empty();
      parts = native_list_append(parts, json_kv_str(EL_STR("type_name"), token));
      parts = native_list_append(parts, json_kv_str(EL_STR("documentation"), el_str_concat(el_str_concat(el_str_concat(EL_STR("fn "), token), EL_STR(" :: ")), sig)));
      parts = native_list_append(parts, json_kv_raw(EL_STR("engram_node_type"), EL_STR("null")));
      return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}"));
    }
    i = (i + 2);
  }
  el_val_t kdoc = keyword_doc(token);
  if (!str_eq(kdoc, EL_STR(""))) {
    el_val_t parts = native_list_empty();
    parts = native_list_append(parts, json_kv_str(EL_STR("type_name"), token));
    parts = native_list_append(parts, json_kv_str(EL_STR("documentation"), kdoc));
    parts = native_list_append(parts, json_kv_raw(EL_STR("engram_node_type"), EL_STR("null")));
    return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}"));
  }
  return EL_STR("null");
  return 0;
}

el_val_t diag_json(el_val_t message, el_val_t severity, el_val_t line, el_val_t col) {
  el_val_t parts = native_list_empty();
  parts = native_list_append(parts, json_kv_str(EL_STR("message"), message));
  parts = native_list_append(parts, json_kv_str(EL_STR("severity"), severity));
  if (line > 0) {
    parts = native_list_append(parts, json_kv_int(EL_STR("line"), line));
    parts = native_list_append(parts, json_kv_int(EL_STR("col"), col));
  } else {
    parts = native_list_append(parts, json_kv_raw(EL_STR("line"), EL_STR("null")));
    parts = native_list_append(parts, json_kv_raw(EL_STR("col"), EL_STR("null")));
  }
  return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}"));
  return 0;
}

el_val_t lsp_diagnostics(el_val_t source) {
  el_val_t chars = native_string_chars(source);
  el_val_t n = native_list_len(chars);
  el_val_t diags = native_list_empty();
  el_val_t depth_brace = 0;
  el_val_t depth_paren = 0;
  el_val_t depth_brack = 0;
  el_val_t i = 0;
  el_val_t line = 1;
  el_val_t col = 1;
  el_val_t in_string = 0;
  el_val_t string_start_line = 0;
  el_val_t string_start_col = 0;
  el_val_t in_comment = 0;
  while (i < n) {
    el_val_t ch = native_list_get(chars, i);
    if (in_comment) {
      if (str_eq(ch, EL_STR("\n"))) {
        in_comment = 0;
        line = (line + 1);
        col = 1;
      } else {
        col = (col + 1);
      }
      i = (i + 1);
    } else {
      if (in_string) {
        if (str_eq(ch, EL_STR("\\"))) {
          i = (i + 1);
          col = (col + 1);
          if (i < n) {
            el_val_t esc = native_list_get(chars, i);
            if (str_eq(esc, EL_STR("\n"))) {
              line = (line + 1);
              col = 1;
            } else {
              col = (col + 1);
            }
            i = (i + 1);
          }
        } else {
          if (str_eq(ch, EL_STR("\""))) {
            in_string = 0;
            i = (i + 1);
            col = (col + 1);
          } else {
            if (str_eq(ch, EL_STR("\n"))) {
              line = (line + 1);
              col = 1;
              i = (i + 1);
            } else {
              col = (col + 1);
              i = (i + 1);
            }
          }
        }
      } else {
        if (str_eq(ch, EL_STR("\""))) {
          in_string = 1;
          string_start_line = line;
          string_start_col = col;
          i = (i + 1);
          col = (col + 1);
        } else {
          if (str_eq(ch, EL_STR("/"))) {
            el_val_t next_i = (i + 1);
            el_val_t is_line_comment = 0;
            if (next_i < n) {
              el_val_t next_ch = native_list_get(chars, next_i);
              if (str_eq(next_ch, EL_STR("/"))) {
                is_line_comment = 1;
              }
            }
            if (is_line_comment) {
              in_comment = 1;
              i = (i + 2);
              col = (col + 2);
            } else {
              i = (i + 1);
              col = (col + 1);
            }
          } else {
            if (str_eq(ch, EL_STR("{"))) {
              depth_brace = (depth_brace + 1);
              i = (i + 1);
              col = (col + 1);
            } else {
              if (str_eq(ch, EL_STR("}"))) {
                depth_brace = (depth_brace - 1);
                if (depth_brace < 0) {
                  diags = native_list_append(diags, diag_json(EL_STR("unmatched `}`"), EL_STR("error"), line, col));
                  depth_brace = 0;
                }
                i = (i + 1);
                col = (col + 1);
              } else {
                if (str_eq(ch, EL_STR("("))) {
                  depth_paren = (depth_paren + 1);
                  i = (i + 1);
                  col = (col + 1);
                } else {
                  if (str_eq(ch, EL_STR(")"))) {
                    depth_paren = (depth_paren - 1);
                    if (depth_paren < 0) {
                      diags = native_list_append(diags, diag_json(EL_STR("unmatched `)`"), EL_STR("error"), line, col));
                      depth_paren = 0;
                    }
                    i = (i + 1);
                    col = (col + 1);
                  } else {
                    if (str_eq(ch, EL_STR("["))) {
                      depth_brack = (depth_brack + 1);
                      i = (i + 1);
                      col = (col + 1);
                    } else {
                      if (str_eq(ch, EL_STR("]"))) {
                        depth_brack = (depth_brack - 1);
                        if (depth_brack < 0) {
                          diags = native_list_append(diags, diag_json(EL_STR("unmatched `]`"), EL_STR("error"), line, col));
                          depth_brack = 0;
                        }
                        i = (i + 1);
                        col = (col + 1);
                      } else {
                        if (str_eq(ch, EL_STR("\n"))) {
                          line = (line + 1);
                          col = 1;
                          i = (i + 1);
                        } else {
                          i = (i + 1);
                          col = (col + 1);
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  if (in_string) {
    diags = native_list_append(diags, diag_json(EL_STR("unterminated string literal"), EL_STR("error"), string_start_line, string_start_col));
  }
  if (depth_brace > 0) {
    diags = native_list_append(diags, diag_json(EL_STR("unclosed `{` (missing `}`)"), EL_STR("error"), 0, 0));
  }
  if (depth_paren > 0) {
    diags = native_list_append(diags, diag_json(EL_STR("unclosed `(` (missing `)`)"), EL_STR("error"), 0, 0));
  }
  if (depth_brack > 0) {
    diags = native_list_append(diags, diag_json(EL_STR("unclosed `[` (missing `]`)"), EL_STR("error"), 0, 0));
  }
  return json_array_of(diags);
  return 0;
}

el_val_t outline_item_json(el_val_t kind, el_val_t name, el_val_t line) {
  el_val_t parts = native_list_empty();
  parts = native_list_append(parts, json_kv_str(EL_STR("kind"), kind));
  parts = native_list_append(parts, json_kv_str(EL_STR("name"), name));
  parts = native_list_append(parts, json_kv_int(EL_STR("line"), line));
  return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}"));
  return 0;
}

el_val_t lsp_outline(el_val_t source) {
  el_val_t lines = str_split(source, EL_STR("\n"));
  el_val_t n = native_list_len(lines);
  el_val_t items = native_list_empty();
  el_val_t i = 0;
  while (i < n) {
    el_val_t line = native_list_get(lines, i);
    el_val_t trimmed = str_trim(line);
    el_val_t lineno = (i + 1);
    if (str_starts_with(trimmed, EL_STR("fn "))) {
      el_val_t name = first_ident_in(str_slice(trimmed, 3, str_len(trimmed)));
      if (!str_eq(name, EL_STR(""))) {
        items = native_list_append(items, outline_item_json(EL_STR("fn"), name, lineno));
      }
    } else {
      if (str_starts_with(trimmed, EL_STR("type "))) {
        el_val_t name = first_ident_in(str_slice(trimmed, 5, str_len(trimmed)));
        if (!str_eq(name, EL_STR(""))) {
          items = native_list_append(items, outline_item_json(EL_STR("type"), name, lineno));
        }
      } else {
        if (str_starts_with(trimmed, EL_STR("enum "))) {
          el_val_t name = first_ident_in(str_slice(trimmed, 5, str_len(trimmed)));
          if (!str_eq(name, EL_STR(""))) {
            items = native_list_append(items, outline_item_json(EL_STR("enum"), name, lineno));
          }
        } else {
          if (str_starts_with(trimmed, EL_STR("protocol "))) {
            el_val_t name = first_ident_in(str_slice(trimmed, 9, str_len(trimmed)));
            if (!str_eq(name, EL_STR(""))) {
              items = native_list_append(items, outline_item_json(EL_STR("protocol"), name, lineno));
            }
          } else {
            if (str_starts_with(trimmed, EL_STR("impl "))) {
              el_val_t rest = str_trim(str_slice(trimmed, 5, str_len(trimmed)));
              el_val_t brace = str_index_of(rest, EL_STR("{"));
              el_val_t name = rest;
              if (brace > 0) {
                name = str_trim(str_slice(rest, 0, brace));
              }
              if (!str_eq(name, EL_STR(""))) {
                items = native_list_append(items, outline_item_json(EL_STR("impl"), name, lineno));
              }
            }
          }
        }
      }
    }
    i = (i + 1);
  }
  return json_array_of(items);
  return 0;
}

el_val_t format_strip_trailing(el_val_t line) {
  el_val_t n = str_len(line);
  if (n == 0) {
    return line;
  }
  el_val_t chars = native_string_chars(line);
  el_val_t i = (n - 1);
  el_val_t trimming = 1;
  while (trimming) {
    if (i < 0) {
      trimming = 0;
    } else {
      el_val_t ch = native_list_get(chars, i);
      if (str_eq(ch, EL_STR(" "))) {
        i = (i - 1);
      } else {
        if (str_eq(ch, EL_STR("\t"))) {
          i = (i - 1);
        } else {
          trimming = 0;
        }
      }
    }
  }
  return str_slice(line, 0, (i + 1));
  return 0;
}

el_val_t lsp_format(el_val_t source) {
  el_val_t lines = str_split(source, EL_STR("\n"));
  el_val_t n = native_list_len(lines);
  el_val_t cleaned = native_list_empty();
  el_val_t i = 0;
  el_val_t blank_run = 0;
  while (i < n) {
    el_val_t line = native_list_get(lines, i);
    el_val_t stripped = format_strip_trailing(line);
    if (str_eq(stripped, EL_STR(""))) {
      blank_run = (blank_run + 1);
      if (blank_run <= 2) {
        cleaned = native_list_append(cleaned, EL_STR(""));
      }
    } else {
      blank_run = 0;
      cleaned = native_list_append(cleaned, stripped);
    }
    i = (i + 1);
  }
  el_val_t joined = list_join(cleaned, EL_STR("\n"));
  el_val_t final = joined;
  if (!str_ends_with(final, EL_STR("\n"))) {
    final = el_str_concat(final, EL_STR("\n"));
  }
  el_val_t changed = EL_STR("false");
  if (!str_eq(final, source)) {
    changed = EL_STR("true");
  }
  return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{"), json_kv_str(EL_STR("content"), final)), EL_STR(",")), json_kv_raw(EL_STR("changed"), changed)), EL_STR("}"));
  return 0;
}

el_val_t lsp_type_graph(el_val_t source) {
  el_val_t nodes = native_list_empty();
  el_val_t edges = native_list_empty();
  el_val_t bts = builtin_type_names();
  el_val_t nb = native_list_len(bts);
  el_val_t i = 0;
  while (i < nb) {
    el_val_t name = native_list_get(bts, i);
    el_val_t parts = native_list_empty();
    parts = native_list_append(parts, json_kv_str(EL_STR("id"), name));
    parts = native_list_append(parts, json_kv_str(EL_STR("name"), name));
    parts = native_list_append(parts, json_kv_str(EL_STR("kind"), EL_STR("builtin")));
    parts = native_list_append(parts, json_kv_raw(EL_STR("fields"), EL_STR("[]")));
    nodes = native_list_append(nodes, el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}")));
    i = (i + 1);
  }
  el_val_t decls = scan_decls(source);
  el_val_t utypes = el_get_field(decls, EL_STR("types"));
  el_val_t ukinds = el_get_field(decls, EL_STR("type_kind"));
  el_val_t tlines = el_get_field(decls, EL_STR("type_lines"));
  el_val_t nu = native_list_len(utypes);
  i = 0;
  el_val_t bts_dup = builtin_type_names();
  while (i < nu) {
    el_val_t name = native_list_get(utypes, i);
    el_val_t kind = native_list_get(ukinds, i);
    if (!list_contains_str(bts_dup, name)) {
      el_val_t fields = collect_struct_fields(source, name);
      el_val_t field_jsons = native_list_empty();
      el_val_t fn2 = native_list_len(fields);
      el_val_t j = 0;
      while (j < fn2) {
        field_jsons = native_list_append(field_jsons, json_str(native_list_get(fields, j)));
        j = (j + 1);
      }
      el_val_t parts = native_list_empty();
      parts = native_list_append(parts, json_kv_str(EL_STR("id"), name));
      parts = native_list_append(parts, json_kv_str(EL_STR("name"), name));
      parts = native_list_append(parts, json_kv_str(EL_STR("kind"), kind));
      parts = native_list_append(parts, json_kv_raw(EL_STR("fields"), json_array_of(field_jsons)));
      nodes = native_list_append(nodes, el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}")));
      j = 0;
      while (j < fn2) {
        el_val_t field_str = native_list_get(fields, j);
        el_val_t target = referenced_type(field_str, utypes);
        if (!str_eq(EL_NULL, EL_STR(""))) {
          el_val_t eparts = native_list_empty();
          eparts = native_list_append(eparts, json_kv_str(EL_STR("from"), name));
          eparts = native_list_append(eparts, json_kv_str(EL_STR("to"), EL_NULL));
          eparts = native_list_append(eparts, json_kv_str(EL_STR("label"), EL_STR("field")));
          edges = native_list_append(edges, el_str_concat(el_str_concat(EL_STR("{"), list_join(eparts, EL_STR(","))), EL_STR("}")));
        }
        j = (j + 1);
      }
    }
    i = (i + 1);
  }
  return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{"), json_kv_raw(EL_STR("nodes"), json_array_of(nodes))), EL_STR(",")), json_kv_raw(EL_STR("edges"), json_array_of(edges))), EL_STR("}"));
  return 0;
}

el_val_t collect_struct_fields(el_val_t source, el_val_t type_name) {
  el_val_t lines = str_split(source, EL_STR("\n"));
  el_val_t n = native_list_len(lines);
  el_val_t fields = native_list_empty();
  el_val_t inside = 0;
  el_val_t i = 0;
  el_val_t header_a = el_str_concat(EL_STR("type "), type_name);
  el_val_t header_b = el_str_concat(EL_STR("enum "), type_name);
  el_val_t header_c = el_str_concat(EL_STR("protocol "), type_name);
  while (i < n) {
    el_val_t line = native_list_get(lines, i);
    el_val_t trimmed = str_trim(line);
    if (!inside) {
      if (str_starts_with(trimmed, header_a)) {
        inside = 1;
      }
      if (str_starts_with(trimmed, header_b)) {
        inside = 1;
      }
      if (str_starts_with(trimmed, header_c)) {
        inside = 1;
      }
    } else {
      if (str_starts_with(trimmed, EL_STR("}"))) {
        return fields;
      }
      if (!str_eq(trimmed, EL_STR("{"))) {
        if (!str_eq(trimmed, EL_STR(""))) {
          if (!str_starts_with(trimmed, EL_STR("//"))) {
            el_val_t entry = trimmed;
            if (str_ends_with(entry, EL_STR(","))) {
              entry = str_slice(entry, 0, (str_len(entry) - 1));
            }
            fields = native_list_append(fields, str_trim(entry));
          }
        }
      }
    }
    i = (i + 1);
  }
  return fields;
  return 0;
}

el_val_t referenced_type(el_val_t field_str, el_val_t known_types) {
  el_val_t colon = str_index_of(field_str, EL_STR(":"));
  if (colon < 0) {
    return EL_STR("");
  }
  el_val_t after = str_trim(str_slice(field_str, (colon + 1), str_len(field_str)));
  el_val_t trimmed_l = after;
  if (str_starts_with(trimmed_l, EL_STR("["))) {
    el_val_t end = str_index_of(trimmed_l, EL_STR("]"));
    if (end > 1) {
      trimmed_l = str_trim(str_slice(trimmed_l, 1, end));
    }
  }
  el_val_t ident = first_ident_in(trimmed_l);
  if (str_eq(ident, EL_STR(""))) {
    return EL_STR("");
  }
  el_val_t bts = builtin_type_names();
  if (list_contains_str(bts, ident)) {
    return ident;
  }
  if (list_contains_str(known_types, ident)) {
    return ident;
  }
  return EL_STR("");
  return 0;
}

el_val_t lsp_definition(el_val_t source, el_val_t pos) {
  el_val_t word = extract_token(source, pos);
  if (str_eq(word, EL_STR(""))) {
    return EL_STR("null");
  }
  el_val_t patterns = native_list_empty();
  patterns = native_list_append(patterns, el_str_concat(EL_STR("fn "), word));
  patterns = native_list_append(patterns, el_str_concat(EL_STR("type "), word));
  patterns = native_list_append(patterns, el_str_concat(EL_STR("enum "), word));
  patterns = native_list_append(patterns, el_str_concat(EL_STR("protocol "), word));
  el_val_t np = native_list_len(patterns);
  el_val_t lines = str_split(source, EL_STR("\n"));
  el_val_t nl = native_list_len(lines);
  el_val_t i = 0;
  while (i < nl) {
    el_val_t line = native_list_get(lines, i);
    el_val_t j = 0;
    while (j < np) {
      el_val_t pat = native_list_get(patterns, j);
      el_val_t idx = str_index_of(line, pat);
      if (idx >= 0) {
        el_val_t parts = native_list_empty();
        parts = native_list_append(parts, json_kv_int(EL_STR("line"), (i + 1)));
        parts = native_list_append(parts, json_kv_int(EL_STR("col"), (idx + 1)));
        parts = native_list_append(parts, json_kv_str(EL_STR("snippet"), str_trim(line)));
        return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}"));
      }
      j = (j + 1);
    }
    i = (i + 1);
  }
  return EL_STR("null");
  return 0;
}

el_val_t lsp_references(el_val_t source, el_val_t pos) {
  el_val_t word = extract_token(source, pos);
  if (str_eq(word, EL_STR(""))) {
    return EL_STR("[]");
  }
  el_val_t lines = str_split(source, EL_STR("\n"));
  el_val_t nl = native_list_len(lines);
  el_val_t refs = native_list_empty();
  el_val_t i = 0;
  while (i < nl) {
    el_val_t line = native_list_get(lines, i);
    el_val_t line_chars = native_string_chars(line);
    el_val_t llen = native_list_len(line_chars);
    el_val_t wlen = str_len(word);
    el_val_t off = 0;
    el_val_t scanning = 1;
    while (scanning) {
      el_val_t idx = str_index_of(str_slice(line, off, str_len(line)), word);
      if (idx < 0) {
        scanning = 0;
      } else {
        el_val_t abs = (off + idx);
        el_val_t before_ok = 1;
        if (abs > 0) {
          el_val_t prev = native_list_get(line_chars, (abs - 1));
          if (is_ident_ch(prev)) {
            before_ok = 0;
          }
        }
        el_val_t after_ok = 1;
        el_val_t after_pos = (abs + wlen);
        if (after_pos < llen) {
          el_val_t nxt = native_list_get(line_chars, after_pos);
          if (is_ident_ch(nxt)) {
            after_ok = 0;
          }
        }
        if (before_ok) {
          if (after_ok) {
            el_val_t parts = native_list_empty();
            parts = native_list_append(parts, json_kv_int(EL_STR("line"), (i + 1)));
            parts = native_list_append(parts, json_kv_int(EL_STR("col"), (abs + 1)));
            parts = native_list_append(parts, json_kv_str(EL_STR("snippet"), str_trim(line)));
            refs = native_list_append(refs, el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}")));
          }
        }
        off = (abs + wlen);
        if (off >= str_len(line)) {
          scanning = 0;
        }
      }
    }
    i = (i + 1);
  }
  return json_array_of(refs);
  return 0;
}

el_val_t lsp_jsonrpc(el_val_t method, el_val_t params_json) {
  el_val_t source = json_get_string(params_json, EL_STR("source"));
  el_val_t pos = json_get_int(params_json, EL_STR("pos"));
  if (str_eq(method, EL_STR("textDocument/completion"))) {
    return lsp_complete(source, pos);
  }
  if (str_eq(method, EL_STR("textDocument/hover"))) {
    return lsp_hover(source, pos);
  }
  if (str_eq(method, EL_STR("textDocument/diagnostic"))) {
    return lsp_diagnostics(source);
  }
  if (str_eq(method, EL_STR("textDocument/formatting"))) {
    return lsp_format(source);
  }
  if (str_eq(method, EL_STR("textDocument/documentSymbol"))) {
    return lsp_outline(source);
  }
  if (str_eq(method, EL_STR("textDocument/definition"))) {
    return lsp_definition(source, pos);
  }
  if (str_eq(method, EL_STR("textDocument/references"))) {
    return lsp_references(source, pos);
  }
  if (str_eq(method, EL_STR("el/typeGraph"))) {
    return lsp_type_graph(source);
  }
  return el_str_concat(el_str_concat(EL_STR("{\"error\":\"unknown method: "), json_escape(method)), EL_STR("\"}"));
  return 0;
}

el_val_t read_headers(void) {
  el_val_t content_len = 0;
  el_val_t scanning = 1;
  while (scanning) {
    el_val_t line = readline();
    if (str_eq(line, EL_STR(""))) {
      scanning = 0;
    } else {
      el_val_t cl_marker = EL_STR("Content-Length:");
      if (str_starts_with(line, cl_marker)) {
        el_val_t val = str_trim(str_slice(line, str_len(cl_marker), str_len(line)));
        content_len = str_to_int(val);
      }
    }
  }
  return el_map_new(1, "content_length", content_len);
  return 0;
}

el_val_t lsp_stdio_loop(void) {
  el_val_t running = 1;
  while (running) {
    el_val_t headers = read_headers();
    el_val_t body = readline();
    if (str_eq(body, EL_STR(""))) {
      running = 0;
    } else {
      el_val_t method = json_get_string(body, EL_STR("method"));
      el_val_t id_val = json_get_raw(body, EL_STR("id"));
      if (str_eq(id_val, EL_STR(""))) {
        id_val = EL_STR("null");
      }
      el_val_t params_raw = json_get_raw(body, EL_STR("params"));
      el_val_t result = lsp_jsonrpc(method, params_raw);
      el_val_t resp = el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"jsonrpc\":\"2.0\",\"id\":"), id_val), EL_STR(",\"result\":")), result), EL_STR("}"));
      el_val_t frame = el_str_concat(el_str_concat(el_str_concat(EL_STR("Content-Length: "), int_to_str(str_len(resp))), EL_STR("\r\n\r\n")), resp);
      print(frame);
    }
  }
  return 0;
}

el_val_t lsp_main(void) {
  el_val_t argv = args();
  el_val_t n = native_list_len(argv);
  if (n > 0) {
    el_val_t cmd = native_list_get(argv, 0);
    if (str_eq(cmd, EL_STR("version"))) {
      println(EL_STR("el-lsp 0.1.0"));
      return 0;
    }
    if (str_eq(cmd, EL_STR("probe"))) {
      el_val_t sample = EL_STR("fn main() -> Void { let x: Int = 42 }");
      println(lsp_diagnostics(sample));
      return 0;
    }
  }
  lsp_stdio_loop();
  return 0;
}

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] <name>"));
  return 0;
}

el_val_t jstr(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 {
          if (str_eq(ch, EL_STR("\r"))) {
            out = el_str_concat(out, EL_STR("\\r"));
          } else {
            if (str_eq(ch, EL_STR("\t"))) {
              out = el_str_concat(out, EL_STR("\\t"));
            } else {
              out = el_str_concat(out, ch);
            }
          }
        }
      }
    }
    i = (i + 1);
  }
  return el_str_concat(out, EL_STR("\""));
  return 0;
}

el_val_t jerr(el_val_t msg) {
  return el_str_concat(el_str_concat(EL_STR("{\"error\":"), jstr(msg)), EL_STR("}"));
  return 0;
}

el_val_t ok_obj(void) {
  return EL_STR("{\"ok\":true}");
  return 0;
}

el_val_t url_decode(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(" "));
      i = (i + 1);
    } else {
      if (str_eq(ch, EL_STR("%"))) {
        if ((i + 2) < n) {
          el_val_t hex = el_str_concat(native_list_get(chars, (i + 1)), native_list_get(chars, (i + 2)));
          el_val_t code = hex_to_int(hex);
          out = el_str_concat(out, char_from_code(code));
          i = (i + 3);
        } else {
          out = el_str_concat(out, ch);
          i = (i + 1);
        }
      } else {
        out = el_str_concat(out, ch);
        i = (i + 1);
      }
    }
  }
  return out;
  return 0;
}

el_val_t hex_digit(el_val_t ch) {
  if (str_eq(ch, EL_STR("0"))) {
    return 0;
  }
  if (str_eq(ch, EL_STR("1"))) {
    return 1;
  }
  if (str_eq(ch, EL_STR("2"))) {
    return 2;
  }
  if (str_eq(ch, EL_STR("3"))) {
    return 3;
  }
  if (str_eq(ch, EL_STR("4"))) {
    return 4;
  }
  if (str_eq(ch, EL_STR("5"))) {
    return 5;
  }
  if (str_eq(ch, EL_STR("6"))) {
    return 6;
  }
  if (str_eq(ch, EL_STR("7"))) {
    return 7;
  }
  if (str_eq(ch, EL_STR("8"))) {
    return 8;
  }
  if (str_eq(ch, EL_STR("9"))) {
    return 9;
  }
  if (str_eq(ch, EL_STR("a"))) {
    return 10;
  }
  if (str_eq(ch, EL_STR("A"))) {
    return 10;
  }
  if (str_eq(ch, EL_STR("b"))) {
    return 11;
  }
  if (str_eq(ch, EL_STR("B"))) {
    return 11;
  }
  if (str_eq(ch, EL_STR("c"))) {
    return 12;
  }
  if (str_eq(ch, EL_STR("C"))) {
    return 12;
  }
  if (str_eq(ch, EL_STR("d"))) {
    return 13;
  }
  if (str_eq(ch, EL_STR("D"))) {
    return 13;
  }
  if (str_eq(ch, EL_STR("e"))) {
    return 14;
  }
  if (str_eq(ch, EL_STR("E"))) {
    return 14;
  }
  if (str_eq(ch, EL_STR("f"))) {
    return 15;
  }
  return (/* if-expr */ ((str_eq(ch, EL_STR("F"))) ? (el_val_t)1 : (el_val_t)0) - 1);
  return 0;
}

el_val_t hex_to_int(el_val_t hex) {
  el_val_t chars = native_string_chars(hex);
  el_val_t n = native_list_len(chars);
  el_val_t acc = 0;
  el_val_t i = 0;
  while (i < n) {
    el_val_t d = hex_digit(native_list_get(chars, i));
    if (d < 0) {
      return acc;
    }
    acc = el_str_concat((acc * 16), d);
    i = (i + 1);
  }
  return acc;
  return 0;
}

el_val_t char_from_code(el_val_t code) {
  el_val_t table = EL_STR(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
  if (code < 32) {
    return EL_STR(" ");
  }
  if (code > 126) {
    return EL_STR("?");
  }
  return str_slice(table, (code - 32), (code - 31));
  return 0;
}

el_val_t split_path_query(el_val_t path) {
  el_val_t q = str_index_of(path, EL_STR("?"));
  if (q < 0) {
    return el_map_new(2, "path", path, "query", EL_STR(""));
  }
  return el_map_new(2, "path", str_slice(path, 0, q), "query", str_slice(path, (q + 1), str_len(path)));
  return 0;
}

el_val_t query_get(el_val_t query, el_val_t key) {
  if (str_eq(query, EL_STR(""))) {
    return EL_STR("");
  }
  el_val_t pairs = str_split(query, EL_STR("&"));
  el_val_t n = native_list_len(pairs);
  el_val_t prefix = el_str_concat(key, EL_STR("="));
  el_val_t i = 0;
  while (i < n) {
    el_val_t pair = native_list_get(pairs, i);
    if (str_starts_with(pair, prefix)) {
      return url_decode(str_slice(pair, str_len(prefix), str_len(pair)));
    }
    if (str_eq(pair, key)) {
      return EL_STR("");
    }
    i = (i + 1);
  }
  return EL_STR("");
  return 0;
}

el_val_t cfg_port(void) {
  el_val_t p = env(EL_STR("EL_IDE_PORT"));
  if (str_eq(p, EL_STR(""))) {
    return 7771;
  }
  return str_to_int(p);
  return 0;
}

el_val_t cfg_project_path(void) {
  el_val_t p = env(EL_STR("EL_IDE_PROJECT_PATH"));
  if (str_eq(p, EL_STR(""))) {
    return EL_STR(".");
  }
  return p;
  return 0;
}

el_val_t cfg_engram_url(void) {
  el_val_t u = env(EL_STR("EL_ENGRAM_URL"));
  if (str_eq(u, EL_STR(""))) {
    return EL_STR("http://localhost:8742");
  }
  return u;
  return 0;
}

el_val_t cfg_el_binary(void) {
  el_val_t b = env(EL_STR("EL_BINARY"));
  if (str_eq(b, EL_STR(""))) {
    return EL_STR("el");
  }
  return b;
  return 0;
}

el_val_t safe_join(el_val_t rel) {
  el_val_t root = cfg_project_path();
  if (str_contains(rel, EL_STR(".."))) {
    return EL_STR("");
  }
  if (str_starts_with(rel, EL_STR("/"))) {
    return EL_STR("");
  }
  if (str_eq(rel, EL_STR("."))) {
    return root;
  }
  return el_str_concat(el_str_concat(root, EL_STR("/")), rel);
  return 0;
}

el_val_t route_status(void) {
  el_val_t parts = native_list_empty();
  parts = native_list_append(parts, el_str_concat(EL_STR("\"project_name\":"), jstr(cfg_project_path())));
  parts = native_list_append(parts, el_str_concat(EL_STR("\"project_path\":"), jstr(cfg_project_path())));
  parts = native_list_append(parts, el_str_concat(EL_STR("\"version\":"), jstr(EL_STR("0.1.0"))));
  parts = native_list_append(parts, el_str_concat(EL_STR("\"engine\":"), jstr(EL_STR("el-ide"))));
  return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}"));
  return 0;
}

el_val_t route_config(void) {
  el_val_t parts = native_list_empty();
  parts = native_list_append(parts, el_str_concat(EL_STR("\"port\":"), int_to_str(cfg_port())));
  parts = native_list_append(parts, el_str_concat(EL_STR("\"project_path\":"), jstr(cfg_project_path())));
  parts = native_list_append(parts, el_str_concat(EL_STR("\"engram_url\":"), jstr(cfg_engram_url())));
  parts = native_list_append(parts, el_str_concat(EL_STR("\"el_binary\":"), jstr(cfg_el_binary())));
  return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}"));
  return 0;
}

el_val_t route_files_list(el_val_t query) {
  el_val_t rel = query_get(query, EL_STR("path"));
  if (str_eq(rel, EL_STR(""))) {
    rel = EL_STR(".");
  }
  el_val_t target = safe_join(rel);
  if (str_eq(EL_NULL, EL_STR(""))) {
    return jerr(EL_STR("path escapes project root"));
  }
  el_val_t entries = fs_list(EL_NULL);
  el_val_t n = native_list_len(entries);
  el_val_t out = EL_STR("[");
  el_val_t i = 0;
  while (i < n) {
    el_val_t name = native_list_get(entries, i);
    el_val_t skip = 0;
    if (str_starts_with(name, EL_STR("."))) {
      skip = 1;
    }
    if (str_eq(name, EL_STR("target"))) {
      skip = 1;
    }
    if (str_eq(name, EL_STR("node_modules"))) {
      skip = 1;
    }
    if (str_eq(name, EL_STR("dist"))) {
      skip = 1;
    }
    if (!skip) {
      if (i > 0) {
        if (!str_eq(out, EL_STR("["))) {
          out = el_str_concat(out, EL_STR(","));
        }
      }
      el_val_t dot = str_index_of(name, EL_STR("."));
      el_val_t is_dir = EL_STR("false");
      if (dot < 0) {
        is_dir = EL_STR("true");
      }
      out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("{\"name\":")), jstr(name)), EL_STR(",\"path\":")), jstr(el_str_concat(el_str_concat(rel, EL_STR("/")), name))), EL_STR(",\"is_dir\":")), is_dir), EL_STR(",\"children\":null}"));
    }
    i = (i + 1);
  }
  return el_str_concat(out, EL_STR("]"));
  return 0;
}

el_val_t route_file_read(el_val_t query) {
  el_val_t rel = query_get(query, EL_STR("path"));
  if (str_eq(rel, EL_STR(""))) {
    return jerr(EL_STR("missing path parameter"));
  }
  el_val_t target = safe_join(rel);
  if (str_eq(EL_NULL, EL_STR(""))) {
    return jerr(EL_STR("path escapes project root"));
  }
  el_val_t content = fs_read(EL_NULL);
  return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"path\":"), jstr(rel)), EL_STR(",\"content\":")), jstr(content)), EL_STR("}"));
  return 0;
}

el_val_t route_file_write(el_val_t body) {
  el_val_t rel = json_get_string(body, EL_STR("path"));
  el_val_t content = json_get_string(body, EL_STR("content"));
  if (str_eq(rel, EL_STR(""))) {
    return jerr(EL_STR("missing path"));
  }
  el_val_t target = safe_join(rel);
  if (str_eq(EL_NULL, EL_STR(""))) {
    return jerr(EL_STR("path escapes project root"));
  }
  el_val_t ok = fs_write(EL_NULL, content);
  if (ok) {
    return ok_obj();
  }
  return jerr(EL_STR("cannot write file"));
  return 0;
}

el_val_t route_lsp_complete(el_val_t query) {
  el_val_t source = query_get(query, EL_STR("source"));
  el_val_t pos = str_to_int(query_get(query, EL_STR("pos")));
  return lsp_complete(source, pos);
  return 0;
}

el_val_t route_lsp_hover(el_val_t query) {
  el_val_t source = query_get(query, EL_STR("source"));
  el_val_t pos = str_to_int(query_get(query, EL_STR("pos")));
  return lsp_hover(source, pos);
  return 0;
}

el_val_t route_lsp_errors(el_val_t query) {
  el_val_t source = query_get(query, EL_STR("source"));
  return lsp_diagnostics(source);
  return 0;
}

el_val_t route_lsp_jsonrpc(el_val_t body) {
  el_val_t method = json_get_string(body, EL_STR("method"));
  el_val_t params_raw = json_get_raw(body, EL_STR("params"));
  el_val_t id_val = json_get_raw(body, EL_STR("id"));
  if (str_eq(id_val, EL_STR(""))) {
    id_val = EL_STR("null");
  }
  el_val_t result = lsp_jsonrpc(method, params_raw);
  return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"jsonrpc\":\"2.0\",\"id\":"), id_val), EL_STR(",\"result\":")), result), EL_STR("}"));
  return 0;
}

el_val_t route_outline(el_val_t query) {
  el_val_t source = query_get(query, EL_STR("source"));
  return lsp_outline(source);
  return 0;
}

el_val_t route_definition(el_val_t query) {
  el_val_t source = query_get(query, EL_STR("source"));
  el_val_t pos = str_to_int(query_get(query, EL_STR("pos")));
  return lsp_definition(source, pos);
  return 0;
}

el_val_t route_references(el_val_t query) {
  el_val_t source = query_get(query, EL_STR("source"));
  el_val_t pos = str_to_int(query_get(query, EL_STR("pos")));
  return lsp_references(source, pos);
  return 0;
}

el_val_t route_format(el_val_t body) {
  el_val_t content = json_get_string(body, EL_STR("content"));
  return lsp_format(content);
  return 0;
}

el_val_t route_type_graph(el_val_t query) {
  el_val_t source = query_get(query, EL_STR("source"));
  return lsp_type_graph(source);
  return 0;
}

el_val_t active_theme(void) {
  el_val_t t = state_get(EL_STR("ide/active_theme"));
  if (str_eq(t, EL_STR(""))) {
    return EL_STR("dark");
  }
  return t;
  return 0;
}

el_val_t route_themes_list(void) {
  el_val_t active = active_theme();
  el_val_t names = native_list_empty();
  names = native_list_append(names, EL_STR("dark"));
  names = native_list_append(names, EL_STR("light"));
  names = native_list_append(names, EL_STR("neuron"));
  names = native_list_append(names, EL_STR("high-contrast"));
  el_val_t labels = native_list_empty();
  labels = native_list_append(labels, EL_STR("Dark"));
  labels = native_list_append(labels, EL_STR("Light"));
  labels = native_list_append(labels, EL_STR("Neuron"));
  labels = native_list_append(labels, EL_STR("High Contrast"));
  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(","));
    }
    el_val_t nm = native_list_get(names, i);
    el_val_t lbl = native_list_get(labels, i);
    el_val_t act = EL_STR("false");
    if (str_eq(nm, active)) {
      act = EL_STR("true");
    }
    out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("{\"name\":")), jstr(nm)), EL_STR(",\"label\":")), jstr(lbl)), EL_STR(",\"active\":")), act), EL_STR("}"));
    i = (i + 1);
  }
  return el_str_concat(out, EL_STR("]"));
  return 0;
}

el_val_t route_themes_set(el_val_t body) {
  el_val_t name = json_get_string(body, EL_STR("name"));
  el_val_t valid = 0;
  if (str_eq(name, EL_STR("dark"))) {
    valid = 1;
  }
  if (str_eq(name, EL_STR("light"))) {
    valid = 1;
  }
  if (str_eq(name, EL_STR("neuron"))) {
    valid = 1;
  }
  if (str_eq(name, EL_STR("high-contrast"))) {
    valid = 1;
  }
  if (!valid) {
    return jerr(el_str_concat(EL_STR("unknown theme: "), name));
  }
  state_set(EL_STR("ide/active_theme"), name);
  return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"active\":"), jstr(name)), EL_STR("}"));
  return 0;
}

el_val_t route_plugins_list(void) {
  return plugin_list_json();
  return 0;
}

el_val_t route_plugins_install(el_val_t body) {
  return plugin_install(json_get_string(body, EL_STR("name")));
  return 0;
}

el_val_t route_plugins_remove(el_val_t body) {
  return plugin_remove(json_get_string(body, EL_STR("name")));
  return 0;
}

el_val_t route_plugins_enable(el_val_t body) {
  return plugin_enable(json_get_string(body, EL_STR("name")));
  return 0;
}

el_val_t route_plugins_disable(el_val_t body) {
  return plugin_disable(json_get_string(body, EL_STR("name")));
  return 0;
}

el_val_t route_snippets(void) {
  el_val_t snips = native_list_empty();
  snips = native_list_append(snips, EL_STR("{\"label\":\"fn\",\"description\":\"Function definition\",\"body\":\"fn ${1:name}(${2:params}) -> ${3:Ret} {\\n\\t${4:body}\\n}\"}"));
  snips = native_list_append(snips, EL_STR("{\"label\":\"type\",\"description\":\"Struct\",\"body\":\"type ${1:Name} {\\n\\t${2:field}: ${3:Type},\\n}\"}"));
  snips = native_list_append(snips, EL_STR("{\"label\":\"enum\",\"description\":\"Enum\",\"body\":\"enum ${1:Name} {\\n\\t${2:Variant},\\n}\"}"));
  snips = native_list_append(snips, EL_STR("{\"label\":\"match\",\"description\":\"Match expression\",\"body\":\"match ${1:expr} {\\n\\t${2:pat} => ${3:body},\\n\\t_ => ${4:default},\\n}\"}"));
  snips = native_list_append(snips, EL_STR("{\"label\":\"let\",\"description\":\"Let binding\",\"body\":\"let ${1:name}: ${2:Type} = ${3:value}\"}"));
  snips = native_list_append(snips, EL_STR("{\"label\":\"if\",\"description\":\"If expression\",\"body\":\"if ${1:cond} {\\n\\t${2:body}\\n}\"}"));
  snips = native_list_append(snips, EL_STR("{\"label\":\"for\",\"description\":\"For loop\",\"body\":\"for ${1:item} in ${2:iter} {\\n\\t${3:body}\\n}\"}"));
  snips = native_list_append(snips, EL_STR("{\"label\":\"activate\",\"description\":\"Activate\",\"body\":\"activate ${1:Type}\"}"));
  return el_str_concat(el_str_concat(EL_STR("["), list_join(snips, EL_STR(","))), EL_STR("]"));
  return 0;
}

el_val_t settings_path(void) {
  el_val_t home = env(EL_STR("HOME"));
  if (str_eq(home, EL_STR(""))) {
    home = EL_STR("/tmp");
  }
  return el_str_concat(home, EL_STR("/.el-ide/settings.json"));
  return 0;
}

el_val_t default_settings_json(void) {
  return EL_STR("{\"theme\":\"dark\",\"fontSize\":13,\"tabSize\":4,\"wordWrap\":false,\"formatOnSave\":false,\"vimMode\":false,\"minimap\":true,\"elBinaryPath\":\"el\",\"engramUrl\":\"http://localhost:8742\",\"stickyScroll\":true,\"lineNumbers\":true,\"bracketMatching\":true}");
  return 0;
}

el_val_t route_settings_get(void) {
  el_val_t s = fs_read(settings_path());
  if (str_eq(s, EL_STR(""))) {
    return default_settings_json();
  }
  return s;
  return 0;
}

el_val_t route_settings_save(el_val_t body) {
  el_val_t blob = json_get_raw(body, EL_STR("settings"));
  if (str_eq(blob, EL_STR(""))) {
    return jerr(EL_STR("missing settings"));
  }
  fs_write(settings_path(), blob);
  return blob;
  return 0;
}

el_val_t route_settings_reset(void) {
  fs_write(settings_path(), default_settings_json());
  return default_settings_json();
  return 0;
}

el_val_t route_build_or_run(el_val_t action, el_val_t body) {
  el_val_t rel_file = json_get_string(body, EL_STR("file"));
  if (str_eq(rel_file, EL_STR(""))) {
    rel_file = EL_STR("src/main.el");
  }
  el_val_t target = safe_join(rel_file);
  if (str_eq(EL_NULL, EL_STR(""))) {
    return jerr(EL_STR("path escapes project root"));
  }
  return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"event\":\"info\",\"action\":"), jstr(action)), EL_STR(",\"file\":")), jstr(rel_file)), EL_STR(",\"note\":\"el-ide-server: synchronous build requires a runtime exec primitive — el binary not invoked\"}"));
  return 0;
}

el_val_t route_build(el_val_t body) {
  return route_build_or_run(EL_STR("build"), body);
  return 0;
}

el_val_t route_run(el_val_t body) {
  return route_build_or_run(EL_STR("run"), body);
  return 0;
}

el_val_t route_reason(el_val_t body) {
  el_val_t url = el_str_concat(cfg_engram_url(), EL_STR("/api/reason"));
  el_val_t resp = http_post(url, body);
  if (str_eq(resp, EL_STR(""))) {
    return jerr(EL_STR("engram unreachable"));
  }
  return resp;
  return 0;
}

el_val_t route_activate_preview(el_val_t query) {
  el_val_t type_name = query_get(query, EL_STR("type_name"));
  el_val_t q = query_get(query, EL_STR("query"));
  el_val_t url = el_str_concat(cfg_engram_url(), EL_STR("/api/activate-preview"));
  el_val_t body = el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"type_name\":"), jstr(type_name)), EL_STR(",\"query\":")), jstr(q)), EL_STR(",\"limit\":5}"));
  el_val_t resp = http_post(url, body);
  if (str_eq(resp, EL_STR(""))) {
    return EL_STR("{\"count\":0,\"nodes\":[],\"connected\":false}");
  }
  return resp;
  return 0;
}

el_val_t route_index(void) {
  el_val_t path = el_str_concat(cfg_project_path(), EL_STR("/ide/index.html"));
  el_val_t html = fs_read(path);
  if (!str_eq(html, EL_STR(""))) {
    return html;
  }
  return el_str_concat(el_str_concat(EL_STR("<!doctype html><html><head><title>el-ide</title></head><body><h1>el-ide</h1><p>Backend running. UI bundle missing at <code>"), path), EL_STR("</code>.</p></body></html>"));
  return 0;
}

el_val_t route_health(void) {
  return EL_STR("{\"status\":\"ok\",\"engine\":\"el-ide-server\"}");
  return 0;
}

el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
  el_val_t parsed = split_path_query(path);
  el_val_t route = el_get_field(parsed, EL_STR("path"));
  el_val_t query = el_get_field(parsed, EL_STR("query"));
  if (str_eq(route, EL_STR("/"))) {
    return route_index();
  }
  if (str_eq(route, EL_STR("/health"))) {
    return route_health();
  }
  if (str_eq(route, EL_STR("/api/status"))) {
    return route_status();
  }
  if (str_eq(route, EL_STR("/api/config"))) {
    return route_config();
  }
  if (str_eq(route, EL_STR("/api/files"))) {
    return route_files_list(query);
  }
  if (str_eq(route, EL_STR("/api/file"))) {
    if (str_eq(method, EL_STR("GET"))) {
      return route_file_read(query);
    }
    if (str_eq(method, EL_STR("POST"))) {
      return route_file_write(body);
    }
  }
  if (str_eq(route, EL_STR("/api/search"))) {
    el_val_t q = query_get(query, EL_STR("q"));
    return el_str_concat(el_str_concat(EL_STR("{\"query\":"), jstr(q)), EL_STR(",\"results\":[]}"));
  }
  if (str_eq(route, EL_STR("/api/lsp/complete"))) {
    return route_lsp_complete(query);
  }
  if (str_eq(route, EL_STR("/api/lsp/hover"))) {
    return route_lsp_hover(query);
  }
  if (str_eq(route, EL_STR("/api/lsp/errors"))) {
    return route_lsp_errors(query);
  }
  if (str_eq(route, EL_STR("/api/lsp/activate-preview"))) {
    return route_activate_preview(query);
  }
  if (str_eq(route, EL_STR("/api/lsp/jsonrpc"))) {
    return route_lsp_jsonrpc(body);
  }
  if (str_eq(route, EL_STR("/api/type-graph"))) {
    return route_type_graph(query);
  }
  if (str_eq(route, EL_STR("/api/outline"))) {
    return route_outline(query);
  }
  if (str_eq(route, EL_STR("/api/definition"))) {
    return route_definition(query);
  }
  if (str_eq(route, EL_STR("/api/references"))) {
    return route_references(query);
  }
  if (str_eq(route, EL_STR("/api/format"))) {
    return route_format(body);
  }
  if (str_eq(route, EL_STR("/api/completions/snippet"))) {
    return route_snippets();
  }
  if (str_eq(route, EL_STR("/api/themes"))) {
    return route_themes_list();
  }
  if (str_eq(route, EL_STR("/api/themes/active"))) {
    return route_themes_set(body);
  }
  if (str_eq(route, EL_STR("/api/plugins"))) {
    return route_plugins_list();
  }
  if (str_eq(route, EL_STR("/api/plugins/install"))) {
    return route_plugins_install(body);
  }
  if (str_eq(route, EL_STR("/api/plugins/remove"))) {
    return route_plugins_remove(body);
  }
  if (str_eq(route, EL_STR("/api/plugins/enable"))) {
    return route_plugins_enable(body);
  }
  if (str_eq(route, EL_STR("/api/plugins/disable"))) {
    return route_plugins_disable(body);
  }
  if (str_eq(route, EL_STR("/api/settings"))) {
    if (str_eq(method, EL_STR("GET"))) {
      return route_settings_get();
    }
    if (str_eq(method, EL_STR("POST"))) {
      return route_settings_save(body);
    }
    if (str_eq(method, EL_STR("DELETE"))) {
      return route_settings_reset();
    }
  }
  if (str_eq(route, EL_STR("/api/build"))) {
    return route_build(body);
  }
  if (str_eq(route, EL_STR("/api/run"))) {
    return route_run(body);
  }
  if (str_eq(route, EL_STR("/api/reason"))) {
    return route_reason(body);
  }
  if (str_eq(route, EL_STR("/api/git/status"))) {
    return EL_STR("[]");
  }
  if (str_eq(route, EL_STR("/api/git/diff"))) {
    return EL_STR("\"\"");
  }
  return el_str_concat(el_str_concat(EL_STR("{\"error\":\"not found\",\"path\":"), jstr(route)), EL_STR("}"));
  return 0;
}

el_val_t server_main(void) {
  plugin_init();
  el_val_t port = cfg_port();
  println(el_str_concat(EL_STR("[el-ide-server] project="), cfg_project_path()));
  println(el_str_concat(EL_STR("[el-ide-server] engram="), cfg_engram_url()));
  println(el_str_concat(EL_STR("[el-ide-server] listening on http://0.0.0.0:"), int_to_str(port)));
  http_set_handler(EL_STR("handle_request"));
  http_serve(port, EL_STR("handle_request"));
  return 0;
}

int main(int argc, char** argv) {
  el_runtime_init_args(argc, argv);
  server_main();
  return 0;
}

