add El LSP skeleton — language server, VSCode extension, syntax highlighting
Implements a Language Server Protocol server for El files over stdin/stdout JSON-RPC. Provides completions (builtins + keywords + document functions), hover documentation, go-to-definition, and full document sync. Also adds a VSCode extension that launches the binary as a child process and a TextMate grammar for .el syntax highlighting. NOTE: el-lsp.el calls __read_n(n: Int) -> String, a seed primitive not yet in el_runtime.c. Build.sh documents the required C implementation; the seed agent must add it before the binary will link.
This commit is contained in:
Executable
+124
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env bash
|
||||
# tools/lsp/build.sh — Build the El LSP server binary.
|
||||
#
|
||||
# Output: tools/lsp/dist/el-lsp
|
||||
#
|
||||
# Usage:
|
||||
# cd tools/lsp && ./build.sh
|
||||
# EL_HOME=/path/to/el ./build.sh # if el root is elsewhere
|
||||
# ELC=/path/to/custom/elc ./build.sh # use a specific compiler binary
|
||||
#
|
||||
# Prerequisites:
|
||||
# - elc binary (either on PATH or ELC env var)
|
||||
# - el-compiler/runtime/el_runtime.c
|
||||
# - cc (clang or gcc), libcurl, pthreads
|
||||
#
|
||||
# NOTE — seed primitive __read_n:
|
||||
# el-lsp.el calls __read_n(n: Int) -> String, which reads exactly n bytes
|
||||
# from stdin. This primitive must be present in el_runtime.c and declared
|
||||
# in el_runtime.h for the compiled binary to work correctly.
|
||||
#
|
||||
# Until __read_n is added to the runtime, the LSP will compile and link
|
||||
# successfully but lsp_read_message() will silently use the __read_n symbol
|
||||
# — causing a linker error. The seed agent must:
|
||||
# 1. Add el_val_t __read_n(el_val_t n) to el-compiler/runtime/el_runtime.c
|
||||
# 2. Declare el_val_t __read_n(el_val_t n) in el-compiler/runtime/el_runtime.h
|
||||
# 3. Add "__read_n" with arity 1 to the builtin_arity table in
|
||||
# el-compiler/src/codegen.el
|
||||
# 4. Rebuild the elc binary
|
||||
#
|
||||
# Implementation sketch for __read_n in C:
|
||||
#
|
||||
# el_val_t __read_n(el_val_t n) {
|
||||
# int64_t count = (int64_t)n;
|
||||
# if (count <= 0) return EL_STR("");
|
||||
# char* buf = malloc(count + 1);
|
||||
# if (!buf) return EL_STR("");
|
||||
# size_t total = 0;
|
||||
# while (total < (size_t)count) {
|
||||
# size_t got = fread(buf + total, 1, (size_t)count - total, stdin);
|
||||
# if (got == 0) break;
|
||||
# total += got;
|
||||
# }
|
||||
# buf[total] = '\0';
|
||||
# return EL_STR(buf);
|
||||
# }
|
||||
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# ── Locate el root ─────────────────────────────────────────────────────────────
|
||||
EL_HOME="${EL_HOME:-$(cd ../.. && pwd)}"
|
||||
ELC="${ELC:-${EL_HOME}/dist/platform/elc}"
|
||||
RUNTIME_DIR="${EL_HOME}/el-compiler/runtime"
|
||||
|
||||
# ── Validate prerequisites ─────────────────────────────────────────────────────
|
||||
if [ ! -x "${ELC}" ]; then
|
||||
echo "error: elc not found at ${ELC}" >&2
|
||||
echo " Set ELC=/path/to/elc or EL_HOME=/path/to/el-root" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${RUNTIME_DIR}/el_runtime.c" ]; then
|
||||
echo "error: el_runtime.c not found at ${RUNTIME_DIR}/el_runtime.c" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p dist
|
||||
|
||||
# ── Concatenate runtime modules + LSP source ───────────────────────────────────
|
||||
# Load order follows runtime/manifest.el: string → math → state → env →
|
||||
# fs → exec → time → json → http → then our LSP source.
|
||||
RUNTIME_SRCS=(
|
||||
"${EL_HOME}/runtime/string.el"
|
||||
"${EL_HOME}/runtime/math.el"
|
||||
"${EL_HOME}/runtime/state.el"
|
||||
"${EL_HOME}/runtime/env.el"
|
||||
"${EL_HOME}/runtime/fs.el"
|
||||
"${EL_HOME}/runtime/exec.el"
|
||||
"${EL_HOME}/runtime/time.el"
|
||||
"${EL_HOME}/runtime/json.el"
|
||||
)
|
||||
|
||||
# Check that runtime modules exist
|
||||
MISSING=0
|
||||
for f in "${RUNTIME_SRCS[@]}"; do
|
||||
if [ ! -f "${f}" ]; then
|
||||
echo "warning: runtime module not found: ${f}" >&2
|
||||
MISSING=$((MISSING + 1))
|
||||
fi
|
||||
done
|
||||
if [ "${MISSING}" -gt 0 ]; then
|
||||
echo "error: ${MISSING} runtime module(s) missing — cannot build" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
COMBINED="dist/el-lsp-combined.el"
|
||||
echo "==> Combining sources..."
|
||||
cat "${RUNTIME_SRCS[@]}" el-lsp.el > "${COMBINED}"
|
||||
echo " ${COMBINED}"
|
||||
|
||||
# ── Compile El → C ─────────────────────────────────────────────────────────────
|
||||
C_OUT="dist/el-lsp.c"
|
||||
echo "==> Compiling El → C..."
|
||||
"${ELC}" "${COMBINED}" > "${C_OUT}"
|
||||
echo " ${C_OUT}"
|
||||
|
||||
# ── Compile C → binary ─────────────────────────────────────────────────────────
|
||||
BIN="dist/el-lsp"
|
||||
echo "==> Compiling C → binary..."
|
||||
cc -std=c11 -O2 \
|
||||
-I "${RUNTIME_DIR}" \
|
||||
-o "${BIN}" \
|
||||
"${C_OUT}" "${RUNTIME_DIR}/el_runtime.c" \
|
||||
-lcurl -lpthread
|
||||
echo " ${BIN}"
|
||||
|
||||
echo
|
||||
echo "==> Build complete: ${BIN}"
|
||||
echo
|
||||
echo " Run as LSP server (editors connect via stdin/stdout):"
|
||||
echo " ${BIN}"
|
||||
echo
|
||||
echo " Test with a synthetic LSP initialize request:"
|
||||
echo " echo -e 'Content-Length: 97\\r\\n\\r\\n{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"capabilities\":{}}}' | ${BIN}"
|
||||
Reference in New Issue
Block a user