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 the model MUST name the id. 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 the model MUST name
the id. 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