Liking cljdoc? Tell your friends :D

com.blockether.vis.ext.language-clojure.core

vis-language-clojure — Clojure language handlers for Vis.

Format/test/REPL are exposed through the generic language facade (format, test, repl_eval, repl_start, repl_stop) — format here does parinfer delimiter repair + cljfmt. The pack also registers a cross-cutting op-hook that auto repairs+formats .clj files after the foundation's struct_patch / patch / write, so no separate repair step is needed.

vis-language-clojure — Clojure language handlers for Vis.

Format/test/REPL are exposed through the generic language facade
(`format`, `test`, `repl_eval`, `repl_start`, `repl_stop`) —
`format` here does parinfer delimiter repair + cljfmt. The pack also registers
a cross-cutting op-hook that auto repairs+formats `.clj` files after the
foundation's struct_patch / patch / write, so no separate repair step is
needed.
raw docstring

com.blockether.vis.ext.language-clojure.format

Config-driven Clojure source formatter used by clj/edit for format-on-write and by the format_code language-surface verb.

TWO backends live here, and the choice is TRANSPARENT to the language surface — callers just format; this namespace picks the formatter from the config files present around the target path:

  • zprint — when a .zprint.edn/.zprintrc is found walking UP from the path. The project's zprint options map is applied. This is the canonical, reflowing formatter (matches the repo's :format alias / codestyle).
  • cljfmt — when only a .cljfmt.edn/.cljfmt.clj is found (no zprint), or when neither config exists (cljfmt defaults). Conservative: normalizes indentation + whitespace of MULTI-LINE forms but does NOT reflow a one-liner into multiple lines.

When BOTH configs are present, zprint WINS.

Failure mode: if a backend refuses (parse error, unfamiliar reader macro, anything that throws), the formatter returns the original source unchanged. We never silently corrupt a file because the formatter choked.

Config-driven Clojure source formatter used by `clj/edit` for format-on-write
and by the `format_code` language-surface verb.

TWO backends live here, and the choice is TRANSPARENT to the language
surface — callers just format; this namespace picks the formatter from the
config files present around the target path:

  * zprint  — when a `.zprint.edn`/`.zprintrc` is found walking UP from the
              path. The project's zprint options map is applied. This is the
              canonical, reflowing formatter (matches the repo's `:format`
              alias / codestyle).
  * cljfmt  — when only a `.cljfmt.edn`/`.cljfmt.clj` is found (no zprint), or
              when neither config exists (cljfmt defaults). Conservative:
              normalizes indentation + whitespace of MULTI-LINE forms but does
              NOT reflow a one-liner into multiple lines.

When BOTH configs are present, zprint WINS.

Failure mode: if a backend refuses (parse error, unfamiliar reader macro,
anything that throws), the formatter returns the original source unchanged.
We never silently corrupt a file because the formatter choked.
raw docstring

com.blockether.vis.ext.language-clojure.lint

clj-kondo linting for the Vis language surface.

Runs clj-kondo's programmatic API (clj-kondo.core/run!) — never shells out — over a code string (fed on stdin as -), explicit path(s), or the workspace's default source paths, and returns a uniform result map (STRING keys — crosses the strings-only boundary as a tool :result): {"op" "clj-lint" "error" N "warning" N "info" N "files" N "findings" [...]} where each finding is {"file" "row" "col" "level" "type" "message"}.

clj-kondo linting for the Vis language surface.

Runs clj-kondo's programmatic API (`clj-kondo.core/run!`) — never shells out —
over a code string (fed on stdin as `-`), explicit path(s), or the workspace's
default source paths, and returns a uniform result map (STRING keys — crosses
the strings-only boundary as a tool `:result`):
`{"op" "clj-lint" "error" N "warning" N "info" N "files" N "findings" [...]}`
where each finding is `{"file" "row" "col" "level" "type" "message"}`.
raw docstring

com.blockether.vis.ext.language-clojure.nrepl-client

Thin, observable nREPL client for clj/eval.

Connection model:

  • One nrepl.core/connect socket per [host port] key, cached on a defonce atom so we survive (require :reload) during development.
  • On every eval! we open a fresh client-session against the cached connection. Sessions are not shared across calls so a caller's *1/*e/dynamic var binding never leaks across Vis tool invocations.
  • Stale / closed sockets are detected (IOException / nil message stream) and the entry is evicted; the next call re-dials.

Returned shape (success) — STRING keys (crosses the strings-only boundary as a tool :result; enrichment adds "error_message"/"error_data"/"trace"): {"value" "42" ; pr-str of the LAST form's value, or nil "values" ["1" "42"] ; pr-str of every emitted value "out" "hello\n" ; stdout aggregated "err" "" ; stderr aggregated "ns" "user" ; final ns name "status" #{"done"} ; nREPL status set (strings) "ex" nil ; exception class name, when status :ex "root_ex" nil ; root exception class name "ms" 12 ; wall-clock duration "port" 7888 "timed_out" false}

Failure paths throw ex-info with :type :clj/nrepl-* so the Vis tool wrapper can surface a clean error to the model.

Thin, observable nREPL client for `clj/eval`.

Connection model:
  * One `nrepl.core/connect` socket per `[host port]` key, cached
    on a `defonce` atom so we survive `(require :reload)` during
    development.
  * On every `eval!` we open a fresh `client-session` against the
    cached connection. Sessions are *not* shared across calls so a
    caller's `*1`/`*e`/dynamic var binding never leaks across
    Vis tool invocations.
  * Stale / closed sockets are detected (`IOException` / `nil`
    message stream) and the entry is evicted; the next call
    re-dials.

Returned shape (success) — STRING keys (crosses the strings-only boundary
as a tool `:result`; enrichment adds "error_message"/"error_data"/"trace"):
  {"value"      "42"          ; pr-str of the LAST form's value, or nil
   "values"     ["1" "42"]   ; pr-str of every emitted value
   "out"        "hello\n"     ; stdout aggregated
   "err"        ""             ; stderr aggregated
   "ns"         "user"         ; final *ns* name
   "status"     #{"done"}      ; nREPL status set (strings)
   "ex"         nil              ; exception class name, when status :ex
   "root_ex"    nil              ; root exception class name
   "ms"         12               ; wall-clock duration
   "port"       7888
   "timed_out" false}

Failure paths throw `ex-info` with `:type :clj/nrepl-*` so the
Vis tool wrapper can surface a clean error to the model.
raw docstring

com.blockether.vis.ext.language-clojure.nrepl-ctx

Per-turn :ext/ctx-fn contribution for the Clojure pack.

Instead of forcing the model to call clj_repl() over and over, the engine injects live nREPL state into context as standing knowledge, nested UNDER the active language so a polyglot repo accumulates :languages {:clojure {...} :typescript {...}}:

{"session_env" {"languages" {"clojure" {"nrepl" {"default" <id|nil> "repls" [{"id" "dir" "port" "aliases" "tool" "status" "managed" ["dialect" "versions"]} ...]}}}}}

OWNERSHIP: we surface ONLY the REPLs THIS session started + owns (from repl-manager/session-repls). There is no external-port discovery and no .nrepl-port scanning — a REPL vis did not start is not vis's to show or stop.

The default is the id of the SINGLE owned REPL (nil when zero or many): with one REPL that id is the implicit eval target; with several, eval still resolves WITHOUT the model naming an id — it defaults to the workspace-root REPL (else the first) and the eval result reports which REPL ran under its repl field, so the model can pass an explicit id to override. Each REPL is mirrored into the session resource registry (footer badge

  • F4 stop/restart) and carries a liveness status from a per-turn probe.

All best-effort: any failure degrades to an empty contribution and never blocks the render.

Per-turn `:ext/ctx-fn` contribution for the Clojure pack.

Instead of forcing the model to call `clj_repl()` over and over, the engine
injects live nREPL state into context as standing knowledge, nested UNDER the
active language so a polyglot repo accumulates
`:languages {:clojure {...} :typescript {...}}`:

  {"session_env" {"languages" {"clojure"
     {"nrepl" {"default" <id|nil>
               "repls"   [{"id" "dir" "port" "aliases" "tool"
                           "status" "managed" ["dialect" "versions"]} ...]}}}}}

OWNERSHIP: we surface ONLY the REPLs THIS session started + owns (from
`repl-manager/session-repls`). There is no external-port discovery and no
`.nrepl-port` scanning — a REPL vis did not start is not vis's to show or stop.

The `default` is the id of the SINGLE owned REPL (nil when zero or many): with
one REPL that id is the implicit eval target; with several, eval still resolves
WITHOUT the model naming an id — it defaults to the workspace-root REPL (else the
first) and the eval result reports which REPL ran under its `repl` field, so the
model can pass an explicit `id` to override. Each REPL is mirrored into the
session resource registry (footer badge
+ F4 stop/restart) and carries a liveness `status` from a per-turn probe.

All best-effort: any failure degrades to an empty contribution and never
blocks the render.
raw docstring

com.blockether.vis.ext.language-clojure.paren-repair

Pure-Clojure delimiter repair for Clojure source the model hand-wrote.

Ported from bhauman/clojure-mcp-light (clojure-mcp-light.delimiter-repair, Apache-2.0): detect a real delimiter error with edamame, then repair via parinferish indent-mode — parinfer trusts the INDENTATION to place the missing / extra ( [ {, which matches how the model intended the code to nest. The parinfer-rust shell path + stats/json bits from upstream are dropped; this is the pure JVM path only.

fix-delimiters is the entry point used by the clj_paren_repair tool.

Pure-Clojure delimiter repair for Clojure source the model hand-wrote.

Ported from bhauman/clojure-mcp-light (`clojure-mcp-light.delimiter-repair`,
Apache-2.0): detect a real delimiter error with edamame, then repair via
parinferish indent-mode — parinfer trusts the INDENTATION to place the missing
/ extra `( [ {`, which matches how the model intended the code to nest. The
parinfer-rust shell path + stats/json bits from upstream are dropped; this is
the pure JVM path only.

`fix-delimiters` is the entry point used by the `clj_paren_repair` tool.
raw docstring

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

com.blockether.vis.ext.language-clojure.test-runner

Run a namespace's tests over the live nREPL (the fast inner loop) or, when no nREPL is reachable, by shelling clojure -M:test (the suite gate).

The in-REPL path is FRAMEWORK-AGNOSTIC: a ns whose vars carry clojure.test :test metadata runs through clojure.test/run-tests; otherwise it is treated as lazytest and run through lazytest.runner/run-tests. Either way the result is a uniform STRING-keyed map (crosses the strings-only boundary) with "mode" (repl or cli), "framework", "ns", "total", "pass", "fail" and "failures" [{"ns" "test" "message" "file" "line"} ...].

run-form is the code EVALED on the target nREPL. It is a quoted form (not a call into this namespace) so it works against ANY project's nREPL, including hosts that do not have the vis extension on their classpath.

Run a namespace's tests over the live nREPL (the fast inner loop) or, when no
nREPL is reachable, by shelling clojure -M:test (the suite gate).

The in-REPL path is FRAMEWORK-AGNOSTIC: a ns whose vars carry clojure.test
:test metadata runs through clojure.test/run-tests; otherwise it is treated
as lazytest and run through lazytest.runner/run-tests. Either way the result
is a uniform STRING-keyed map (crosses the strings-only boundary) with
"mode" (repl or cli), "framework", "ns", "total", "pass", "fail" and
"failures" [{"ns" "test" "message" "file" "line"} ...].

run-form is the code EVALED on the target nREPL. It is a quoted form (not a
call into this namespace) so it works against ANY project's nREPL, including
hosts that do not have the vis extension on their classpath.
raw 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