Erilang for VS Code
Real language support for Erilang (.eri) files, backed by a genuine
Language Server
process (not logic bolted directly into the extension) -- so the same
server could support any other LSP-capable editor with a thin client, not
just VS Code.
Every feature below calls the real erilang lexer/parser/linter/
formatter (erilang_bridge.py, invoked by the language server) -- nothing
here is a separate reimplementation of the language's own rules.
Features
- Diagnostics -- real-time syntax/lint errors as you save (and, if
enabled, as you type -- see
erilang.lintOnType).
- Hover -- documentation for reserved/contextual keywords, stdlib
members (
math.sqrt, ...), and your own DEFINE/CLASS/method
declarations, including their ## doc comments.
- Context-aware completion -- a genuinely different suggestion set
depending on where the cursor is: stdlib members right after
<namespace>., a CLASS body's own fields/methods, the current
function/method's own parameters and locally SET variables, and every
top-level symbol in the file, plus keywords and stdlib namespaces.
- Signature help -- parameter hints while typing a call, for both
user-defined functions/methods and stdlib functions.
- Document symbols -- the outline view / breadcrumbs /
Ctrl+Shift+O,
covering CLASS/INTERFACE/DEFINE/ENUM/DATA CLASS and methods.
- Format on save / Format Document -- the real
erilang format output.
- Run Erilang File / Run Erilang Project -- runs the real
erilang CLI against the active file (or a workspace folder's
main.eri) in an integrated terminal. Available from the editor
title bar (▷) on an open .eri file, or the Command Palette.
- New Project / Install Package / Uninstall Package -- wrap
the real
erilang init/install/uninstall CLI commands. Install
offers live autocomplete against the real api.erilang.dev registry
search as you type (falling back to installing by exact typed name if
the registry's unreachable or has no matches); Uninstall lists the
packages your project's own erilang.toml already depends on.
F5 / Debugging -- now real breakpoints, stepping, and variable inspection
F5 is a real debugging session, not a run-and-terminate stub: real
breakpoints, real stepping (step over/in/out, continue, pause), and real
variable inspection, at Erilang source lines and Erilang variable names --
never the WebView below, which stays a separate, deliberate action
(Ctrl+Shift+R), so picking one is never an accident of which button/key
you happened to hit.
This works by proxying to a real debugpy session (the same debugger VS
Code's own Python extension is built on) rather than reimplementing
stepping/pausing from scratch, made possible by two real, new pieces:
- A real source map --
erilang/codegen.py's CodeGenerator now
records, for every generated Python line, which real Erilang source
line produced it (CodeGenerator.source_map), hooked at its own
single self.lines.append() choke point (confirmed the only way
generated lines are ever added, across every _gen_* method).
erilang debug-compile <file.eri> -- a new CLI command
(erilang/cli.py) that compiles with that map and writes the
generated Python to a real file on disk (debugAdapter.js's own real
caller): debugpy needs a real file with a real line table to set
breakpoints against, unlike erilang run's own in-memory exec().
debugAdapter.js's ErilangDebugSession is a real DAP client to
debugpy (a raw socket speaking the same Content-Length-framed wire
format VS Code's own DAP client speaks) at the same time it's a real DAP
server to VS Code -- breakpoints/stack frames/variables get translated
through the source map in both directions as they cross that boundary.
Erilang variable names pass through into generated Python unchanged
(confirmed by reading codegen.py directly), so variable inspection
needed almost no extra work -- the one real exception is a name that
collides with a genuine Python keyword, which gets a trailing _
(class -> class_; see _safe_name in codegen.py), reversed for
display.
Known, real, honestly-documented limitations, not silently papered
over (see debugAdapter.js's own top-of-file comment for the fuller
version):
The debugged process runs under Python's normal, full builtins, not
erilang run's own RESTRICTED_BUILTINS sandbox -- a script that
references an undefined name colliding with a real Python builtin
(rare) would behave subtly differently under the debugger than under
erilang run.
Only the single file being debugged is mapped -- an INCLUDEd file's
own lines aren't currently distinguished in the map.
Confirmed working for CLASS methods too (real breakpoints, real
multi-frame stacks across SUPER calls, real self values) via
direct testing, though it wasn't the original minimal target -- not
yet exercised against GATHER/heavy ASYNC code paths.
Run Erilang File with Rendered Output -- runs the file and shows its
real stdout/stderr LIVE, as it happens, plus any chart image(s) it
produced (CHART ... TO "path.png") in a WebView panel, instead of
plain terminal text -- not a re-runnable/editable notebook, but a real,
scripted, incrementally-updated page now, not the old static one-shot
render. Ctrl+Shift+R while an .eri file has focus; Escape while
the panel itself is focused closes it and returns focus to the editor.
Real interactive input() support, Jupyter-input_request-style:
erilang/runtime.py's read_input() writes a real, precise marker to
stderr (flushed) right before its blocking read, but only when
ERILANG_INPUT_SIGNAL is set -- set only by this WebView's own spawn,
so the terminal path (Run Erilang File) is completely unaffected.
The panel watches for that marker, shows a real text box the moment
the program is genuinely blocked waiting for a line, and writes what
you type (plus the real newline) straight to the process's real,
genuinely-open stdin -- the typed text is also echoed into the
transcript, since a piped stdin has no terminal-style echo of its own.
A program that never calls input() is unaffected -- an open,
unwritten stdin pipe doesn't block a normal exit.
Real history, not just the current run: running the same file more
than once reuses the same panel -- each run is its own collapsible
section (a real, semantic <details> element, natively keyboard/
screen-reader-navigable), newest prepended and expanded, older ones
collapsed but still scrollable back to, each with its own real
timestamp. Starting a new run finalizes the previous one for real (not
just visually) -- only the current run's input area is ever live; an
older run's is replaced with "(this run has ended)", since its own
process has already exited. History persists only while the panel
stays open -- the same real scrollback model a terminal already has --
and resets on the next run once you close it. Ctrl+Shift+J (while an
.eri file has focus) brings an already-open history panel for the
current file back into view without starting a new run; if none
exists yet, it says so honestly rather than doing nothing silently.
Requirements
- Nothing to install for diagnostics, hover, completion, signature help,
document symbols, or format-on-save -- this extension bundles its own
real Python runtime (see Architecture below) just for
those, so there's no separate interpreter to have on
PATH and nothing
to pip install.
- The real
erilang CLI on PATH for Run/New Project/Install/
Uninstall and the debugger (or set erilang.cliPath to its full
path) -- these still shell out to your own erilang install; only the
editor-intelligence features above are self-contained.
Settings
| Setting |
Default |
|
erilang.pythonPath |
"" (use the bundled interpreter) |
Advanced: point this at your own Python interpreter instead of the one this extension bundles (must have the erilang package importable). |
erilang.cliPath |
"erilang" |
The real CLI command the Run/New Project/Install/Uninstall commands invoke. |
erilang.lintOnType |
false |
Also lint while typing (debounced), not just on save/open. |
erilang.lintOnTypeDelay |
500 |
Debounce delay (ms) for lintOnType. |
Architecture
extension.js is a thin vscode-languageclient
bootstrap, wiring together everything that genuinely isn't an LSP concept:
runCommands.js -- Run File / Run Project.
debugAdapter.js -- the minimal F5 debug adapter.
outputPanel.js -- the rendered-output WebView.
projectCommands.js -- New Project / Install / Uninstall Package.
server/server.js is the real vscode-languageserver
process the client launches, which is where every editor-intelligence
feature above (diagnostics, hover, completion, signature help, document
symbols, formatting) is actually implemented -- as real LSP request
handlers, calling the same scripts/erilang_bridge.py this project has
always used as its one, real bridge into erilang's own lexer/parser/
linter/formatter. See that script's own docstring, and server/server.js's,
for more detail.
The bundled Python runtime
erilang has never been published to PyPI, so there was never a real
pip install a user could run to make it importable -- and this project
deliberately doesn't want the fact that any of this is built on Python
visible to end users in the first place. Instead, this extension bundles
its own, real, self-contained Python runtime and just enough of erilang
to run the bridge above:
python-runtime/<platform>-<arch>/ -- a real, portable CPython build
from python-build-standalone
(the install_only_stripped variant), fetched per-platform by
scripts/fetch_python_runtime.js and shipped inside the platform-specific
.vsix vsce package --target <platform> produces -- so each user's
download only ever contains the one runtime for their own OS/architecture.
scripts/erilang_vendor/erilang/ -- the real, verified-minimal subset
of the erilang package erilang_bridge.py actually imports (traced by
grepping its own import closure down to the Python stdlib): lexer, parser,
linter, formatter, doc generator, AST nodes, errors, interface checks.
Nothing from erilang.runtime (and none of its heavy third-party
dependencies -- pandas, matplotlib, pygame, ...) is part of this bundle,
since none of it is needed for editor intelligence. scripts/ vendor_erilang.js regenerates this from the main erilang/ package as
part of vscode:prepublish.
pythonRuntime.js -- resolves the bundled interpreter's path for the
current platform/arch and hands it to server.js (via an environment
variable on the language server's own process, since server.js runs
as a separate Node process with no vscode API of its own). An explicit
erilang.pythonPath setting always overrides this, for anyone who wants
their own interpreter instead; "python" on PATH is the last-resort
fallback for a dev checkout or a platform this extension hasn't bundled
a runtime for yet.
This only covers editor intelligence -- Run/Run with Rendered
Output/the debugger still shell out to a real, separately-installed
erilang CLI (erilang.cliPath), since those need the full runtime
(pandas/numpy/matplotlib/pygame/...), not the small bridge-only subset
above.
Regenerating the syntax grammar
If erilang/lexer.py's KEYWORDS, SYMBOLS, or _STRING_ESCAPES
change, regenerate syntaxes/erilang.tmLanguage.json:
python scripts/generate_grammar.py
| |