AtlatestRepositorysigil-nrepl
1# sigil-nrepl
2
3Network REPL server for
4[Sigil](https://codeberg.org/sigil/sigil) — provides a JSON-RPC-style
5nREPL endpoint that editors and live-development tools connect to.
6Built on `(sigil repl)` for prompt rendering and command parsing, and
7on `(sigil socket)` for transport.
8
9Long-running Sigil apps (live-crafter, cinder-cantata, game servers,
10etc.) embed an nREPL server so the developer can evaluate code, switch
11modules, inspect state, and iterate on running code without restarting
12the process.
14## Modules
16| Module | Purpose |
17|------------------------|------------------------------------------------------------------------------|
18| `(sigil nrepl)` | nREPL server: connection handling, session state, op dispatch (eval/doc/...)|
19| `(sigil nrepl client)` | nREPL client: connect, send requests, named-connection registry |
21## Wire protocol
23Messages are length-prefixed S-expressions:
25```
26<length:u32-be><sexp>
27```
29Request format: `(request :id <string> :op <symbol> [:module <string>] [...params])`
31Response format: `(response :id <string> :status <symbol> [...results])`
33Ops supported: `eval`, `abort` (alias `interrupt`), `complete`, `doc`,
34`describe`, `macroexpand`, `switch-module`, `modules`, plus the debug-protocol
35ops (`debug-policy`, `debug-state`, `debug-frames`, `debug-restarts`,
36`debug-quit`).
38### Interrupt / abort
40Expression evals run cooperatively — one preemptive-yield slice per
41`nrepl-process-pending` — so a CPU-bound eval (e.g. `(let loop () (loop))`) does
42not freeze the host's loop. Interrupt one from a second connection:
44```
45(request :id <abort-id> :op abort :target-id <eval-request-id>)
46```
48The abort responder gets `:status ok :aborted <eval-request-id>`; the
49interrupted eval receives `:status error :code "interrupted"`; the session
50survives and evaluates normally afterward. (A single top-level form that mixes
51a top-level `define` with a long loop runs on the immediate, non-abortable path
52— define at top level, then run, as separate requests.)
54### Output streaming
56Output written during an eval is streamed back incrementally as it happens,
57before the final response, carrying the original eval request id:
59```
60(response :id <eval-id> :status out :out <string>) ; stdout
61(response :id <eval-id> :status err :err <string>) ; stderr
62```
64followed by the normal terminal `(response :id <eval-id> :status ok :value …)`.
65The bundled `(sigil nrepl client)` transparently drains these frames and returns
66the terminal response from `nrepl-eval`.
68## Embedding an nREPL server
70```scheme
71(import (sigil nrepl))
73(define server (nrepl-start port: 7888))
75;; In your app's main loop, drain pending requests:
76(nrepl-process-pending server)
78;; On shutdown:
79(nrepl-stop server)
80```
82## Connecting from a client
84```scheme
85(import (sigil nrepl client))
87(define conn (nrepl-connect "127.0.0.1" 7888))
88(nrepl-eval conn "(+ 1 2)") ; => ((status . ok) (value . "3"))
89(nrepl-switch-module conn "(my app)")
90(nrepl-disconnect conn)
91```
93## Build and test
95```
96sigil deps install
97sigil build
98sigil test --report
99```
101Local development against an unreleased sigil monorepo or against a
102local sigil-repl checkout:
104```
105sigil build --redirects ./dev-redirects.sgl
106```
108## License
110BSD-3-Clause. See `LICENSE` in the sigil monorepo for the canonical copy.