vis-language-clojure — Clojure language handlers for Vis.
Format/test/REPL are exposed through the generic language facade
(format, test, repl_eval, repl, repl_stop) —
format here does parinfer delimiter repair + cljfmt. The pack also registers
a cross-cutting op-hook that parinfer-repairs .clj source rejected by the
foundation's struct_patch, so unbalanced delimiters never fail an edit.
vis-language-clojure — Clojure language handlers for Vis. Format/test/REPL are exposed through the generic language facade (`format`, `test`, `repl_eval`, `repl`, `repl_stop`) — `format` here does parinfer delimiter repair + cljfmt. The pack also registers a cross-cutting op-hook that parinfer-repairs `.clj` source rejected by the foundation's struct_patch, so unbalanced delimiters never fail an edit.
(available-aliases env)Alias names declared in the workspace deps.edn — surfaced to the UI so the
user picks REAL aliases (:dev, :test, …) instead of guessing. Returns a
sorted vec of strings WITHOUT the leading colon; empty on any read/parse
failure or a non-deps project.
Alias names declared in the workspace `deps.edn` — surfaced to the UI so the user picks REAL aliases (`:dev`, `:test`, …) instead of guessing. Returns a sorted vec of strings WITHOUT the leading colon; empty on any read/parse failure or a non-deps project.
(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 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…)
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. The
flat "findings" vector is also grouped under "by-cwd" — nested by
directory to write each path prefix once:
{<dir> {<basename> {"error"/"warning"/"info" [...]}}}.
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. The
flat `"findings"` vector is also grouped under `"by-cwd"` — nested by
directory to write each path prefix once:
`{<dir> {<basename> {"error"/"warning"/"info" [...]}}}`.(clj-patch-no-fail-around _env _op-kw args next)MIDDLEWARE (:around) on patch so an anchored Clojure edit does NOT fail on
unbalanced delimiters. patch is (path edits), so every replacement the call
carries sits in an entry's replace key and the repair maps over the WHOLE
batch: if the call throws against a .clj file, parinfer-repair each
replacement, retry ONCE when any of them changed, and say so on the status line
the model reads. The batch is atomic in the editor, so the retry is the whole
batch again — never a partial re-application. If nothing repaired, or the
retried batch still fails, the ORIGINAL error is surfaced: a real refusal (a
stale anchor, an overlap, a broken parse) is never buried. Non-clj calls pass
straight through to next.
Per repo doctrine the repair stays SYNTAX-ONLY: the smallest mechanical change that restores parseable source, never a semantic rewrite.
MIDDLEWARE (:around) on `patch` so an anchored Clojure edit does NOT fail on unbalanced delimiters. `patch` is `(path edits)`, so every replacement the call carries sits in an entry's `replace` key and the repair maps over the WHOLE batch: if the call throws against a `.clj` file, parinfer-repair each replacement, retry ONCE when any of them changed, and say so on the status line the model reads. The batch is atomic in the editor, so the retry is the whole batch again — never a partial re-application. If nothing repaired, or the retried batch still fails, the ORIGINAL error is surfaced: a real refusal (a stale anchor, an overlap, a broken parse) is never buried. Non-clj calls pass straight through to `next`. Per repo doctrine the repair stays SYNTAX-ONLY: the smallest mechanical change that restores parseable source, never a semantic rewrite.
(clj-repair+format code)(clj-repair+format code path)The combined Clojure tidy used by BOTH format and the post-edit hook:
parinfer delimiter repair FIRST (so unbalanced ( [ { from a raw edit are
fixed), 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.
The combined Clojure tidy used by BOTH `format` and the post-edit hook:
parinfer delimiter repair FIRST (so unbalanced ( [ { from a raw edit are
fixed), 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.(clj-struct-patch-no-fail-around _env _op-kw args next)MIDDLEWARE (:around) on struct_patch so a Clojure structural edit does NOT
fail on unbalanced delimiters. If the call throws and it targeted a .clj
file with a :code form, parinfer-repair the code and retry ONCE. If the
repair changes nothing — or the retried edit still fails — the ORIGINAL error
is surfaced (we never bury a real structural failure). Non-clj / non-code
calls pass straight through to next.
MIDDLEWARE (:around) on struct_patch so a Clojure structural edit does NOT fail on unbalanced delimiters. If the call throws and it targeted a `.clj` file with a `:code` form, parinfer-repair the code and retry ONCE. If the repair changes nothing — or the retried edit still fails — the ORIGINAL error is surfaced (we never bury a real structural failure). Non-clj / non-code calls pass straight through to `next`.
(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). Positional op (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)}; vis never spawns/kills it
"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). Positional op (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)}; vis never spawns/kills it
"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 |