Language-neutral STRUCTURAL ZIPPER over the tree-sitter pack (306+ langs) —
the unified cursor the name-based structural ops were missing.
A node's location is a STATELESS PATH: a vector of NAMED-child indices from
the root (e.g. [2 0] = first named child of the third named child of the
file). Stateless means it round-trips cleanly through async tool calls — no
live native cursor to keep between calls. Relative moves (down/up/next/prev)
are pure path arithmetic on top, so the model navigates like a rewrite-clj
zipper but over EVERY language tree-sitter understands.
Edits splice the target node's UTF-8 byte range and RE-PARSE, refusing a
result that introduces a syntax error the original didn't have — the same
safety contract as structural. Pairs with the name-based ops: locate a def
by name, then walk into it by path.
All native handles (Parser/Tree/Node) are opened and closed inside each call; only plain Clojure data escapes.
Language-neutral STRUCTURAL ZIPPER over the tree-sitter pack (306+ langs) — the unified cursor the name-based `structural` ops were missing. A node's location is a STATELESS PATH: a vector of NAMED-child indices from the root (e.g. `[2 0]` = first named child of the third named child of the file). Stateless means it round-trips cleanly through async tool calls — no live native cursor to keep between calls. Relative moves (down/up/next/prev) are pure path arithmetic on top, so the model navigates like a rewrite-clj zipper but over EVERY language tree-sitter understands. Edits splice the target node's UTF-8 byte range and RE-PARSE, refusing a result that introduces a syntax error the original didn't have — the same safety contract as `structural`. Pairs with the name-based ops: locate a def by name, then walk into it by path. All native handles (Parser/Tree/Node) are opened and closed inside each call; only plain Clojure data escapes.
(describe-syntax-errors lang source)One-line, model-actionable summary of the ERROR/MISSING nodes in source
(parsed as lang), or nil when it parses clean. Names WHERE the parser broke
and, when tree-sitter knows it, WHICH delimiter it expected — so a rejected
edit stops the model from blind paren-counting.
One-line, model-actionable summary of the ERROR/MISSING nodes in `source` (parsed as `lang`), or nil when it parses clean. Names WHERE the parser broke and, when tree-sitter knows it, WHICH delimiter it expected — so a rejected edit stops the model from blind paren-counting.
(edit lang source at op code)Splice the node at at. op ∈ #{:replace :insert-before :insert-after
:append-child :prepend-child} with code (clojure.zip vocabulary). RE-PARSES
and refuses a result that introduces a syntax error the original file didn't
have. Returns {:ok? true :new-source S} or {:error …}.
:append-child / :prepend-child insert after the LAST / before the FIRST named
child of the node at at (delete = :replace with "").
Splice the node at `at`. `op` ∈ #{:replace :insert-before :insert-after
:append-child :prepend-child} with `code` (clojure.zip vocabulary). RE-PARSES
and refuses a result that introduces a syntax error the original file didn't
have. Returns `{:ok? true :new-source S}` or `{:error …}`.
:append-child / :prepend-child insert after the LAST / before the FIRST named
child of the node at `at` (delete = :replace with "").(error-nodes lang source)Every ERROR / MISSING node tree-sitter finds in source (parsed as lang),
as [{:line :col :end-line :end-col :kind :missing? :text} …] in document
order (1-based line, 0-based col). Empty when the source parses clean or the
language can't be parsed. Public so an edit guard can turn a bare
"N syntax error(s)" rejection into a LOCATED, actionable message — a MISSING
node even NAMES the delimiter the parser expected (:kind = ], ), …).
Every ERROR / MISSING node tree-sitter finds in `source` (parsed as `lang`),
as [{:line :col :end-line :end-col :kind :missing? :text} …] in document
order (1-based line, 0-based col). Empty when the source parses clean or the
language can't be parsed. Public so an edit guard can turn a bare
"N syntax error(s)" rejection into a LOCATED, actionable message — a MISSING
node even NAMES the delimiter the parser expected (`:kind` = `]`, `)`, …).(inspect lang source at)Structural view of the node at at (vector of named-child indices) in
source (language lang): its kind, line span, text, s-expression, and a
pick-list of immediate named children with indices. at = [] is the file
root. Pure data.
Structural view of the node at `at` (vector of named-child indices) in `source` (language `lang`): its kind, line span, text, s-expression, and a pick-list of immediate named children with indices. `at = []` is the file root. Pure data.
(moves-available lang source path)Which cursor moves are possible from path, so the model sees its options
instead of probing — down (HAS named children), up, left / right (a sibling
exists), next / prev (depth-first) — plus its index among siblings and the
sibling count (so lefts = index, rights = siblings-1-index).
Which cursor moves are possible from `path`, so the model sees its options instead of probing — down (HAS named children), up, left / right (a sibling exists), next / prev (depth-first) — plus its `index` among siblings and the sibling count (so `lefts` = index, `rights` = siblings-1-index).
(navigate lang source at moves)Resolve at (a named-child index path) + a sequence of relative moves
against the ACTUAL tree → {:ok? true :path [...]} or {:error …}. The full
clojure.zip / rewrite-clj cursor vocabulary (single-letter aliases):
sibling/parent/child : up|u|t down|d|b left|l right|r leftmost|first
rightmost|last root|home {child: i}
depth-first : next|n prev|p
search (rewrite-clj) : {find: "text"} {find_kind: "if_statement"}
Boundary / not-found moves FAIL CLOSED instead of silently going nowhere.
Resolve `at` (a named-child index path) + a sequence of relative `moves`
against the ACTUAL tree → {:ok? true :path [...]} or {:error …}. The full
clojure.zip / rewrite-clj cursor vocabulary (single-letter aliases):
sibling/parent/child : up|u|t down|d|b left|l right|r leftmost|first
rightmost|last root|home {child: i}
depth-first : next|n prev|p
search (rewrite-clj) : {find: "text"} {find_kind: "if_statement"}
Boundary / not-found moves FAIL CLOSED instead of silently going nowhere.(syntax-broken? lang source)True when source parses to a tree whose root carries an ERROR node (nil when
the language can't be parsed at all — fail-open, so callers never block on an
unparseable input). Public so patch can re-parse its result and refuse a
syntax-breaking edit, the same guard struct_patch/symbol_rename already run.
True when `source` parses to a tree whose root carries an ERROR node (nil when the language can't be parsed at all — fail-open, so callers never block on an unparseable input). Public so `patch` can re-parse its result and refuse a syntax-breaking edit, the same guard `struct_patch`/`symbol_rename` already run.
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 |