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, and the same repair is
registered as the pack's :balance-fn: the foundation's editors call it with
the WHOLE spliced file when an edit would not parse, and write the repair only
when it stays inside the lines that edit wrote. Both doors are ADD-ONLY: a delimiter
you omitted is added back, one you WROTE is never deleted — a lost opening ( and
one ) too many are the same string, so deleting is a guess that rewrites code, and
the text an edit REPLACED is the only thing that tells the two apart.
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, and the same repair is registered as the pack's `:balance-fn`: the foundation's editors call it with the WHOLE spliced file when an edit would not parse, and write the repair only when it stays inside the lines that edit wrote. Both doors are ADD-ONLY: a delimiter you omitted is added back, one you WROTE is never deleted — a lost opening `(` and one `)` too many are the same string, so deleting is a guess that rewrites code, and the text an edit REPLACED is the only thing that tells the two apart.
(clj-eval-fn env arg)Evaluate Clojure over a RUNNING nREPL in this session. Target resolution:
port → dial it directly (escape hatch);id/repl_id → the REPL registered under that id in THIS session;cwd → the REPL rooted at that directory (when the session owns one);cwd (default: the workspace root) when
present, else the first (dir-sorted);Evaluate Clojure over a RUNNING nREPL in this session. Target resolution:
- explicit `port` → dial it directly (escape hatch);
- `id`/`repl_id` → the REPL registered under that id in THIS session;
- `cwd` → the REPL rooted at that directory (when the session owns one);
- no id, 1 REPL → use it (the implicit default);
- no id, >1 REPLs → the REPL owning `cwd` (default: the workspace root) when
present, else the first (dir-sorted);
- no id, 0 REPLs → error (:clj/no-repl): no running nREPL to hit.
A connect failure surfaces as DATA so the model can repl / wait.(clj-format-fn arg)(clj-format-fn env arg)Format Clojure source via the language facade (format_code). Accepts:
"formatter" ("zprint" | "cljfmt") on a
single file / code string, and the distinct "formatters" set on a batch.Format Clojure source via the language facade (`format_code`). Accepts:
- a raw code string / {"code": ...} -> report changed? + char delta (NO text)
- {"path": "src/foo.clj"} -> format that file IN PLACE
- {"paths": ["src" "test" ...]} -> format those paths IN PLACE; a
DIRECTORY is walked RECURSIVELY (every .clj/.cljs/.cljc/.cljx/.bb under it)
- nothing / {} -> format the whole project's source
roots (every deps.edn module's :paths + test), skipping build/vendor
dirs (target, dist, node_modules, .clj-kondo, .clojure-lsp, .cpcache…)
Paths are resolved against the workspace root when relative. Every result
NAMES the backend that ran: `"formatter"` ("zprint" | "cljfmt") on a
single file / code string, and the distinct `"formatters"` set on a batch.(clj-lint-fn env arg)clj-kondo lint via the language facade (lint_code). Accepts:
path and paths are UNIONED (not shadowing); a target that resolves to
nothing is an ERROR, not a silent clean.
Paths are resolved against the workspace root when relative. Finding "file"
paths are reported RELATIVE to the workspace root (absolute only when outside).Findings come from one or more PROVIDERS, tagged per finding as "provider"
and listed under "providers": "clj-kondo" (static analysis, every
branch) and "general" (the compiler's reflection + boxed-math warnings).
Reflection/boxed-math only exist at compile time, so "general" COMPILES its
target: the code-string snippet, or every source file the lint targets (path /
paths / whole project) — each in a throwaway namespace that is torn down.
clj-kondo lint via the language facade (`lint_code`). Accepts:
- a raw code string / {"code": ...} -> lint it on stdin
- {"path": "src/foo.clj"} -> lint that file
- {"paths": ["src", "test"]} -> lint those paths
- nothing / {} -> lint the whole project's source roots
(every deps.edn module's :paths + test), skipping build/vendor dirs
`path` and `paths` are UNIONED (not shadowing); a target that resolves to
nothing is an ERROR, not a silent `clean`.
Paths are resolved against the workspace root when relative. Finding "file"
paths are reported RELATIVE to the workspace root (absolute only when outside).
Findings come from one or more PROVIDERS, tagged per finding as `"provider"`
and listed under `"providers"`: `"clj-kondo"` (static analysis, every
branch) and `"general"` (the compiler's reflection + boxed-math warnings).
Reflection/boxed-math only exist at compile time, so `"general"` COMPILES its
target: the code-string snippet, or every source file the lint targets (path /
paths / whole project) — each in a throwaway namespace that is torn down.(clj-repair+format code)(clj-repair+format code path)The combined Clojure tidy behind format: the ADD-ONLY delimiter repair
(clj-repair-source) FIRST, THEN indentation via the config-driven formatter
(fmt/format-source picks zprint when a .zprint.edn/.zprintrc is near path,
else cljfmt). Total — returns code unchanged on any failure of either step, and
leaves source whose repair was refused exactly as it was written.
The combined Clojure tidy behind `format`: the ADD-ONLY delimiter repair (`clj-repair-source`) FIRST, THEN indentation via the config-driven formatter (`fmt/format-source` picks zprint when a `.zprint.edn`/`.zprintrc` is near `path`, else cljfmt). Total — returns `code` unchanged on any failure of either step, and leaves source whose repair was refused exactly as it was written.
(clj-repair-source code)The delimiter repair format is allowed to WRITE, and what it must SAY about it.
parinfer repairs whatever it is handed, and a file that lost an opening ( is
character-for-character a file with one ) too many: "repairing" it by DELETING
that ) parses, writes, and silently turns (def defaults {..}) into three loose
top-level forms. So the rule the editors use holds here too — a repair is kept only
when it ADDED delimiters this file omitted (balance/rebalance over the whole file,
the only span a formatter has).
Answers {:code <what to format> :repaired? bool :repairs [note] :why msg?}. On a
refusal the ORIGINAL code goes to the formatter untouched and :why names the
mistake to look for, because a formatter that guesses which of the two happened is
the corruption it was meant to prevent.
The delimiter repair `format` is allowed to WRITE, and what it must SAY about it.
parinfer repairs whatever it is handed, and a file that lost an opening `(` is
character-for-character a file with one `)` too many: "repairing" it by DELETING
that `)` parses, writes, and silently turns `(def defaults {..})` into three loose
top-level forms. So the rule the editors use holds here too — a repair is kept only
when it ADDED delimiters this file omitted (`balance/rebalance` over the whole file,
the only span a formatter has).
Answers `{:code <what to format> :repaired? bool :repairs [note] :why msg?}`. On a
refusal the ORIGINAL code goes to the formatter untouched and `:why` names the
mistake to look for, because a formatter that guesses which of the two happened is
the corruption it was meant to prevent.(register-repl-resource! session-id dir aliases result)Mirror a session's managed nREPL into the session-scoped resource registry so
it shows in ctx (resources) + the footer, and can be stopped by id from the
agent or the UI. No-op without a session or a live spawn. The stop-fn IS the
canonical teardown — the footer and resource_stop both drive repl-manager
through it, scoped to session-id. There is deliberately NO restart thunk:
a REPL is stopped, then started, never silently swapped underneath a caller.
Mirror a session's managed nREPL into the session-scoped resource registry so it shows in ctx (resources) + the footer, and can be stopped by id from the agent or the UI. No-op without a session or a live spawn. The stop-fn IS the canonical teardown — the footer and resource_stop both drive repl-manager through it, scoped to `session-id`. There is deliberately NO restart thunk: a REPL is stopped, then started, never silently swapped underneath a caller.
(repl-start-fn env)(repl-start-fn env op)(repl-start-fn env op opts)Manage THIS session's workspace nREPL(s). The facade verbs repl_start /
repl_status / repl_stop / repl_connect reach this pack as a positional op
STRING (default "status") +
optional opts dict {"cwd": <path>, "aliases": ["dev", "test"]}:
"status" — managed-process view for this session (always allowed)
"start" — start a project nREPL subprocess (always allowed)
"stop" — stop a Vis-managed nREPL / DETACH an external one (always allowed)
"connect" — attach to an EXTERNAL user-started nREPL: opts {"port": N,
"host"?: S (default localhost), "build"?: S}; vis never
spawns/kills it. "build" names a shadow-cljs build and makes it
a ClojureScript REPL: that build is selected in the session every
later repl_eval reuses, so the eval lands in its JS runtime. With
a "build" and no "port", the port is read from the project's own
.shadow-cljs/nrepl.port. An attachment is INDEPENDENT of the
managed REPL for the same "cwd" — both live at once, each under
its own id (nrepl:~/proj and nrepl:~/proj#app), so a
ClojureScript attach never costs you the JVM REPL.
"cwd" runs the REPL in a subdir (e.g. an extension) instead of the workspace
root — that's how MULTIPLE REPLs coexist, each addressed by its id. "aliases"
default to [:dev :test] (full deps/paths, user :main-opts dropped). Live nREPL
state already rides in ctx under :session/env :languages :clojure :nrepl;
this tool acts on it.
Manage THIS session's workspace nREPL(s). The facade verbs `repl_start` /
`repl_status` / `repl_stop` / `repl_connect` reach this pack as a positional op
STRING (default "status") +
optional opts dict `{"cwd": <path>, "aliases": ["dev", "test"]}`:
"status" — managed-process view for this session (always allowed)
"start" — start a project nREPL subprocess (always allowed)
"stop" — stop a Vis-managed nREPL / DETACH an external one (always allowed)
"connect" — attach to an EXTERNAL user-started nREPL: opts {"port": N,
"host"?: S (default localhost), "build"?: S}; vis never
spawns/kills it. "build" names a shadow-cljs build and makes it
a ClojureScript REPL: that build is selected in the session every
later repl_eval reuses, so the eval lands in its JS runtime. With
a "build" and no "port", the port is read from the project's own
.shadow-cljs/nrepl.port. An attachment is INDEPENDENT of the
managed REPL for the same "cwd" — both live at once, each under
its own id (`nrepl:~/proj` and `nrepl:~/proj#app`), so a
ClojureScript attach never costs you the JVM REPL.
"cwd" runs the REPL in a subdir (e.g. an extension) instead of the workspace
root — that's how MULTIPLE REPLs coexist, each addressed by its id. "aliases"
default to [:dev :test] (full deps/paths, user :main-opts dropped). Live nREPL
state already rides in ctx under `:session/env :languages :clojure :nrepl`;
this tool acts on it.cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |