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:
Will Anderson
2026-05-03 15:52:10 -05:00
parent 9d0e1f64d4
commit cefff5b891
6 changed files with 1245 additions and 0 deletions
@@ -0,0 +1,133 @@
{
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "El",
"scopeName": "source.el",
"fileTypes": ["el"],
"patterns": [
{ "include": "#comments" },
{ "include": "#strings" },
{ "include": "#function-definition" },
{ "include": "#keywords" },
{ "include": "#types" },
{ "include": "#constants" },
{ "include": "#numbers" },
{ "include": "#operators" },
{ "include": "#function-call" },
{ "include": "#identifiers" }
],
"repository": {
"comments": {
"name": "comment.line.double-slash.el",
"match": "//.*$"
},
"strings": {
"name": "string.quoted.double.el",
"begin": "\"",
"end": "\"",
"patterns": [
{
"name": "constant.character.escape.el",
"match": "\\\\[nrt\\\\\"']"
}
]
},
"function-definition": {
"comment": "Highlight 'fn name(' — the name gets entity.name.function scope",
"match": "\\bfn\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(",
"captures": {
"0": { "name": "meta.function.el" },
"1": { "name": "entity.name.function.el" }
}
},
"keywords": {
"patterns": [
{
"comment": "Control-flow keywords",
"name": "keyword.control.el",
"match": "\\b(if|else|while|for|in|return|match)\\b"
},
{
"comment": "Declaration keywords",
"name": "keyword.declaration.el",
"match": "\\b(fn|let|type|enum|import|from|as)\\b"
},
{
"comment": "Reserved future keywords",
"name": "keyword.other.el",
"match": "\\b(cgi|vessel|activate|where|sealed|with|test|seed|assert|protocol|impl|retry|times|fallback|reason|parallel|trace|requires|deploy|to|via|target|manager|engine|accessor)\\b"
}
]
},
"types": {
"comment": "Built-in El types",
"name": "support.type.el",
"match": "\\b(String|Int|Float|Bool|Void|Any|Map|List)\\b"
},
"constants": {
"patterns": [
{
"name": "constant.language.boolean.el",
"match": "\\b(true|false)\\b"
}
]
},
"numbers": {
"patterns": [
{
"name": "constant.numeric.float.el",
"match": "\\b[0-9]+\\.[0-9]+\\b"
},
{
"name": "constant.numeric.integer.el",
"match": "\\b[0-9]+\\b"
}
]
},
"operators": {
"patterns": [
{
"name": "keyword.operator.comparison.el",
"match": "(==|!=|<=|>=|<|>)"
},
{
"name": "keyword.operator.logical.el",
"match": "(&&|\\|\\||!)"
},
{
"name": "keyword.operator.assignment.el",
"match": "(?<![=!<>])=(?!=)"
},
{
"name": "keyword.operator.arithmetic.el",
"match": "[+\\-*/%]"
},
{
"name": "keyword.operator.arrow.el",
"match": "->"
}
]
},
"function-call": {
"comment": "Highlight function calls: name( — name gets entity.name.function scope",
"match": "\\b([a-zA-Z_][a-zA-Z0-9_]*)\\s*(?=\\()",
"captures": {
"1": { "name": "entity.name.function.call.el" }
}
},
"identifiers": {
"comment": "General identifiers — lower priority than other rules",
"name": "variable.other.el",
"match": "\\b[a-zA-Z_][a-zA-Z0-9_]*\\b"
}
}
}