Liking cljdoc? Tell your friends :D

com.blockether.vis.ext.language-clojure.repl-manager

Owned, session-scoped nREPL lifecycle for the Clojure pack.

OWNERSHIP: each vis SESSION owns its own nREPL subprocess(es). The processes atom is keyed by [session-id dir], so two sessions in the same directory get two independent REPLs and neither can see or stop the other's. A managed REPL lives and dies with THIS vis process — there is NO persistent registry and NO PID re-attach across a vis restart. Restarting vis means a fresh REPL, exactly like the Python pack.

PORT: we PICK a free ephemeral port ourselves and pass it to the launcher EXPLICITLY (nrepl.cmdline --port N, lein repl :headless :port N, bb nrepl-server N), so we always KNOW our port without ever reading a .nrepl-port file back. Any stray .nrepl-port a tool drops in the project is deleted after boot — vis never depends on it and never leaves it behind.

ALIASES: a REPL is ALWAYS booted with the project's :dev :test deps + paths on its classpath (full dependency spec), with the user's :main-opts dropped (our synthetic :vis/nrepl-launch alias appends last so -m nrepl.cmdline wins). Unknown :dev/:test aliases are silently ignored by tools.deps, so this is safe in any project.

Starting/stopping is CORE and ALWAYS allowed — never gated behind a flag.

Owned, session-scoped nREPL lifecycle for the Clojure pack.

OWNERSHIP: each vis SESSION owns its own nREPL subprocess(es). The `processes`
atom is keyed by `[session-id dir]`, so two sessions in the same directory get
two independent REPLs and neither can see or stop the other's. A managed REPL
lives and dies with THIS vis process — there is NO persistent registry and NO
PID re-attach across a vis restart. Restarting vis means a fresh REPL, exactly
like the Python pack.

PORT: we PICK a free ephemeral port ourselves and pass it to the launcher
EXPLICITLY (`nrepl.cmdline --port N`, `lein repl :headless :port N`,
`bb nrepl-server N`), so we always KNOW our port without ever reading a
`.nrepl-port` file back. Any stray `.nrepl-port` a tool drops in the project is
deleted after boot — vis never depends on it and never leaves it behind.

ALIASES: a REPL is ALWAYS booted with the project's `:dev :test` deps + paths
on its classpath (full dependency spec), with the user's `:main-opts` dropped
(our synthetic `:vis/nrepl-launch` alias appends last so `-m nrepl.cmdline`
wins). Unknown `:dev`/`:test` aliases are silently ignored by tools.deps, so
this is safe in any project.

Starting/stopping is CORE and ALWAYS allowed — never gated behind a flag.
raw docstring

connect!clj

(connect! session-id dir {:keys [host port]})

Attach THIS session to an EXTERNAL nREPL the USER already runs (their editor jack-in, a clj -M:nrepl they launched themselves) — the OPT-IN inverse of start!: vis never spawns, never kills, and never reaps the process; it only registers the address so eval/test/ctx target it like a managed REPL. Explicit consent only — nothing ever auto-connects or scans for ports.

  • The address is PROBED FIRST (bounded): a dead host:port is REFUSED ("unreachable") instead of registered.
  • An existing live entry for [session-id dir] → "already-running" (stop/detach it first to switch).
  • stop! on the attachment DETACHES only. Model-facing: STRING keys + STRING enum values.
Attach THIS session to an EXTERNAL nREPL the USER already runs (their editor
jack-in, a `clj -M:nrepl` they launched themselves) — the OPT-IN inverse of
`start!`: vis never spawns, never kills, and never reaps the process; it only
registers the address so eval/test/ctx target it like a managed REPL.
Explicit consent only — nothing ever auto-connects or scans for ports.

- The address is PROBED FIRST (bounded): a dead host:port is REFUSED
  ("unreachable") instead of registered.
- An existing live entry for `[session-id dir]` → "already-running"
  (stop/detach it first to switch).
- `stop!` on the attachment DETACHES only.
Model-facing: STRING keys + STRING enum values.
sourceraw docstring

crash-looping?clj

(crash-looping? session-id dir)

True when [session-id dir]'s managed REPL died max-crashes-in-window+ times inside crash-window-ms — autostart must STOP retrying and surface the failure instead of burning another JVM boot per eval.

True when `[session-id dir]`'s managed REPL died `max-crashes-in-window`+
times inside `crash-window-ms` — autostart must STOP retrying and surface the
failure instead of burning another JVM boot per eval.
sourceraw docstring

ensure-repl-for-dir!clj

(ensure-repl-for-dir! session-id dir)

Return the live REPL info for [session-id dir], AUTOSTARTING one (with the default :dev :test aliases) when the session owns none for dir — OR when the recorded process is alive but UNREACHABLE (a boot that never bound its port, or a wedged server thread that proc-alive? alone cannot see): such a stale process is stopped and REPLACED. A still-booting process is given the REMAINING cold-boot window (see health-probe-ms) before being judged wedged.

Two deliberate REFUSALS, both surfaced as start!-style STRING-keyed results (callers tell live info apart by :port vs "result"):

  • EXTERNAL attachment unreachable → "external-unreachable": vis NEVER silently replaces the user's explicitly-attached REPL with a managed spawn — reconnect or detach is the user's call.
  • Crash loop (crash-looping?, VS Code semantics) → "crash-looping": autostart is suspended instead of burning a JVM boot per eval; an explicit stop/restart resets the guard.
Return the live REPL info for `[session-id dir]`, AUTOSTARTING one (with the
default :dev :test aliases) when the session owns none for `dir` — OR when the
recorded process is alive but UNREACHABLE (a boot that never bound its port, or
a wedged server thread that `proc-alive?` alone cannot see): such a stale
process is stopped and REPLACED. A still-booting process is given the
REMAINING cold-boot window (see `health-probe-ms`) before being judged wedged.

Two deliberate REFUSALS, both surfaced as start!-style STRING-keyed results
(callers tell live info apart by `:port` vs `"result"`):
  - EXTERNAL attachment unreachable → "external-unreachable": vis NEVER
    silently replaces the user's explicitly-attached REPL with a managed
    spawn — reconnect or detach is the user's call.
  - Crash loop (`crash-looping?`, VS Code semantics) → "crash-looping":
    autostart is suspended instead of burning a JVM boot per eval; an
    explicit stop/restart resets the guard.
sourceraw docstring

healthclj

(health session-id dir)

Coarse LIVE health of THIS session's REPL for dir: :up — process alive (or external attachment) AND the port answers :starting — managed process alive, port not answering yet :failed — no live process but an UNEXPECTED death is on record :down — nothing managed (intentional stop / external gone away) An EXTERNAL attachment has no process to watch, so its health IS the probe: :up or :down, never :starting/:failed. Used as the resource registry's :health-fn, so footer/F4/ctx status tracks reality instead of the status frozen at registration time.

Coarse LIVE health of THIS session's REPL for `dir`:
  :up       — process alive (or external attachment) AND the port answers
  :starting — managed process alive, port not answering yet
  :failed   — no live process but an UNEXPECTED death is on record
  :down     — nothing managed (intentional stop / external gone away)
An EXTERNAL attachment has no process to watch, so its health IS the probe:
:up or :down, never :starting/:failed. Used as the resource registry's
`:health-fn`, so footer/F4/ctx status tracks reality instead of the status
frozen at registration time.
sourceraw docstring

home-relativizeclj

(home-relativize dir)

Collapse a leading user-home prefix to ~, so a REPL id reads ~/vis instead of the noisy machine-absolute /Users/you/vis. Paths outside home (and blanks) pass through unchanged.

Collapse a leading user-home prefix to `~`, so a REPL id reads `~/vis` instead
of the noisy machine-absolute `/Users/you/vis`. Paths outside home (and blanks)
pass through unchanged.
sourceraw docstring

id-ofclj

(id-of dir)

Stable session-resource id for the REPL rooted at dir. The dir is CANONICALIZED first — so .., a trailing slash, and symlinks all collapse to ONE id per physical dir (no nrepl:.../vis vs nrepl:.../vis/.. near-duplicates spawning twin REPLs) — then its home prefix is homogenized to ~, so a REPL always addresses as nrepl:~/vis.

Stable session-resource id for the REPL rooted at `dir`. The dir is CANONICALIZED
first — so `..`, a trailing slash, and symlinks all collapse to ONE id per
physical dir (no `nrepl:.../vis` vs `nrepl:.../vis/..` near-duplicates spawning
twin REPLs) — then its home prefix is homogenized to `~`, so a REPL always
addresses as `nrepl:~/vis`.
sourceraw docstring

inherited-jvm-optsclj

(inherited-jvm-opts dir aliases)

JVM options a nested project should INHERIT from an ancestor deps.edn.

The nREPL is launched with -M:dev:test:vis/nrepl-launch, so any :jvm-opts dir's OWN deps.edn declares for those aliases already reach the JVM — in that case nothing is inherited (returns nil, keeping the top-level project unchanged).

But a NESTED project whose deps.edn declares no such aliases (e.g. an extension with a bare {:deps …} map) would otherwise boot a BARE JVM — missing the workspace's flags (--enable-native-access, --enable-preview, --sun-misc-unsafe-memory-access=allow, …) that its code needs, so tests crash before they run. For that case we walk UP from dir to the nearest ancestor whose deps.edn declares :jvm-opts for aliases and return them, so the nested nREPL inherits the workspace's JVM options.

JVM options a nested project should INHERIT from an ancestor deps.edn.

The nREPL is launched with `-M:dev:test:vis/nrepl-launch`, so any `:jvm-opts`
`dir`'s OWN deps.edn declares for those aliases already reach the JVM — in that
case nothing is inherited (returns nil, keeping the top-level project unchanged).

But a NESTED project whose deps.edn declares no such aliases (e.g. an extension
with a bare `{:deps …}` map) would otherwise boot a BARE JVM — missing the
workspace's flags (`--enable-native-access`, `--enable-preview`,
`--sun-misc-unsafe-memory-access=allow`, …) that its code needs, so tests crash
before they run. For that case we walk UP from `dir` to the nearest ancestor
whose deps.edn declares `:jvm-opts` for `aliases` and return them, so the nested
nREPL inherits the workspace's JVM options.
sourceraw docstring

last-failureclj

(last-failure session-id dir)

The last UNEXPECTED launcher death recorded for [session-id dir], or nil. STRING-keyed ("exit" "at" "log" "log_tail") — safe to splice into model-facing results.

The last UNEXPECTED launcher death recorded for `[session-id dir]`, or nil.
STRING-keyed ("exit" "at" "log" "log_tail") — safe to splice into
model-facing results.
sourceraw docstring

launcher-forclj

(launcher-for dir aliases port)

Subprocess command to boot a project nREPL in dir on the EXPLICIT port, honouring aliases (deps.edn aliases / lein profiles). Returns {:tool kw :cmd [strings]} or nil when no known Clojure build file is present.

Subprocess command to boot a project nREPL in `dir` on the EXPLICIT `port`,
honouring `aliases` (deps.edn aliases / lein profiles). Returns
`{:tool kw :cmd [strings]}` or nil when no known Clojure build file is present.
sourceraw docstring

nrepl-versionclj

source

repl-by-idclj

(repl-by-id session-id id)

The session's live REPL info matching resource id, or nil.

The session's live REPL info matching resource `id`, or nil.
sourceraw docstring

resolve-target!clj

(resolve-target! session-id id default-dir)

Resolve the RUNNING REPL an eval should hit for session-id. id is an optional explicit resource id; default-dir picks the implicit default among several live REPLs. Returns {:id :dir :port}.

Rules (the ownership contract):

  • explicit id → that REPL (throws :clj/unknown-repl-id if no such live REPL);
  • id = default (any case) → sentinel, treated as no explicit id (below);
  • 0 REPLs → throw :clj/no-repl (start one with repl, e.g. repl("clojure"));
  • 1 REPL → use it (the implicit default);
  • 1 REPLs → use the DEFAULT: the REPL owning default-dir (the workspace root) when present, else the first (dir-sorted). Never throws on ambiguity — eval always resolves and the result reports which REPL ran it, so the model can pass an explicit id to override.

Resolve the RUNNING REPL an eval should hit for `session-id`.
`id` is an optional explicit resource id; `default-dir`
picks the implicit default among several live REPLs. Returns `{:id :dir :port}`.

Rules (the ownership contract):
  - explicit `id` → that REPL (throws :clj/unknown-repl-id if no such live REPL);
  - `id` = `default` (any case) → sentinel, treated as no explicit id (below);
  - 0 REPLs       → throw :clj/no-repl (start one with repl, e.g. repl("clojure"));
  - 1 REPL        → use it (the implicit default);
  - >1 REPLs      → use the DEFAULT: the REPL owning `default-dir` (the
                    workspace root) when present, else the first (dir-sorted).
                    Never throws on ambiguity — eval always resolves and the
                    result reports which REPL ran it, so the model can pass an
                    explicit `id` to override.
sourceraw docstring

session-replsclj

(session-repls session-id)

Live REPLs OWNED by (or ATTACHED to) session-id, as a vec of {:id :dir :port :tool :aliases :pid} (+ :log for managed, :external? :host for attached) sorted by dir. Prunes dead entries as a side effect. This is the SINGLE source of truth for ctx + eval/test targeting — external REPLs enter it ONLY via an explicit connect!, never by discovery.

Live REPLs OWNED by (or ATTACHED to) `session-id`, as a vec of
`{:id :dir :port :tool :aliases :pid}` (+ `:log` for managed, `:external?
:host` for attached) sorted by dir. Prunes dead entries as a side effect.
This is the SINGLE source of truth for ctx + eval/test targeting — external
REPLs enter it ONLY via an explicit `connect!`, never by discovery.
sourceraw docstring

start!clj

(start! session-id dir)
(start! session-id dir {:keys [aliases]})

Self-start a project nREPL subprocess OWNED by session-id in dir. Always allowed — never flag-gated. Default :aliases are [:dev :test] (merged with any explicitly passed). We pick a FREE port, pass it to the launcher, drop any stray .nrepl-port, and wait for OUR port — SYNCHRONOUSLY: the wait ends the moment the launcher dies (fast :failed with exit + log tail), and only a still-alive-but-slow boot can outlive start-deadline-ms (then :starting, with an .onExit watcher recording any later death as a failure).

  • Already ours + alive for [session-id dir] → :already-running.
  • No known build file → :no-launcher.
  • Launcher exits before binding → :failed with exit code + log tail.
  • Else :started (port up) or :starting (still coming up; ctx will show it).

Model-facing: STRING keys + STRING enum values (crosses as a tool :result).

Self-start a project nREPL subprocess OWNED by `session-id` in `dir`.
Always allowed — never flag-gated. Default `:aliases` are [:dev :test] (merged
with any explicitly passed). We pick a FREE port, pass it to the launcher, drop
any stray `.nrepl-port`, and wait for OUR port — SYNCHRONOUSLY: the wait ends
the moment the launcher dies (fast :failed with exit + log tail), and only a
still-alive-but-slow boot can outlive `start-deadline-ms` (then :starting,
with an `.onExit` watcher recording any later death as a failure).

- Already ours + alive for `[session-id dir]` → :already-running.
- No known build file → :no-launcher.
- Launcher exits before binding → :failed with exit code + log tail.
- Else :started (port up) or :starting (still coming up; ctx will show it).

Model-facing: STRING keys + STRING enum values (crosses as a tool `:result`).
sourceraw docstring

statusclj

(status session-id dir)

Live view of THIS session's managed REPL for dir. Always safe. Model-facing: STRING keys + STRING enum values (crosses as a tool :result).

Live view of THIS session's managed REPL for `dir`. Always safe. Model-facing:
STRING keys + STRING enum values (crosses as a tool `:result`).
sourceraw docstring

stop!clj

(stop! session-id dir)

Stop THIS session's REPL for dir. A MANAGED subprocess is destroyed (graceful, then forced); an EXTERNAL attachment is only DETACHED — vis never kills a process it did not spawn. The entry is DEREGISTERED FIRST so the .onExit watcher reads a managed death as an intentional stop, never a failure; any remembered failure/crash history for dir is cleared too. No-op-safe. Model-facing STRING-keyed result.

Stop THIS session's REPL for `dir`. A MANAGED subprocess is destroyed
(graceful, then forced); an EXTERNAL attachment is only DETACHED — vis never
kills a process it did not spawn. The entry is DEREGISTERED FIRST so the
`.onExit` watcher reads a managed death as an intentional stop, never a
failure; any remembered failure/crash history for `dir` is cleared too.
No-op-safe. Model-facing STRING-keyed result.
sourceraw docstring

tail-logclj

(tail-log log-path)
(tail-log log-path n)

Tail a managed nREPL launcher log as line strings, reading ONLY the last tail-read-bytes of the file (never the whole thing). Returns [] when the log does not exist yet or cannot be read; resource viewers treat that as an empty but still log-capable resource.

Tail a managed nREPL launcher log as line strings, reading ONLY the last
`tail-read-bytes` of the file (never the whole thing). Returns [] when the
log does not exist yet or cannot be read; resource viewers treat that as an
empty but still log-capable resource.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close