Thin Clojure adapter over the pack's Java structural-edit engine
(dev.kreuzberg.treesitterlanguagepack.StructuralApi). All the work —
locate the definition by name from the tree-sitter outline, splice its line
span, and re-parse to refuse syntax-breaking edits — lives in Java so it is
language-neutral, reusable from any JVM consumer, and native-image clean.
This namespace only maps vis op keywords onto the Java API.
Thin Clojure adapter over the pack's Java structural-edit engine (`dev.kreuzberg.treesitterlanguagepack.StructuralApi`). All the work — locate the definition by name from the tree-sitter outline, splice its line span, and re-parse to refuse syntax-breaking edits — lives in Java so it is language-neutral, reusable from any JVM consumer, and native-image clean. This namespace only maps vis op keywords onto the Java API.
(edit-source path source {:keys [op target kind code match anchor]})Return the new file content for a structural edit, or throw with an
actionable message (StructuralApi$EditException on missing/ambiguous target,
no match, or a syntax-breaking result). op ∈ #{:replace :insert-before
:insert-after :append :replace-doc :add-doc :replace-node :rename
:move-before :move-after}.
:replace-node replaces the unique sub-expression equal to :match
(optionally scoped to :target); :rename renames identifier :target to
:code; :append ignores :target; :move-before/:move-after relocate
the node named :target next to the node named :anchor.
Return the new file content for a structural edit, or throw with an
actionable message (StructuralApi$EditException on missing/ambiguous target,
no match, or a syntax-breaking result). `op` ∈ #{:replace :insert-before
:insert-after :append :replace-doc :add-doc :replace-node :rename
:move-before :move-after}.
`:replace-node` replaces the unique sub-expression equal to `:match`
(optionally scoped to `:target`); `:rename` renames identifier `:target` to
`:code`; `:append` ignores `:target`; `:move-before`/`:move-after` relocate
the node named `:target` next to the node named `:anchor`.(occurrences path source name)Every occurrence of identifier name in path — the DEFINITION occurrences
ENRICHED — as ONE list (empty if none / unknown language):
{:anchor} ; a plain use {:anchor :is-definition true :kind :visibility :signature ; a DEFINITION :doc :end-anchor} ; span = :anchor..:end-anchor
Every entry's SOLE position is its patch-ready lineno:hash :anchor (the
lineno lives in the anchor — no redundant :line/:column/byte fields). A use is
just that anchor; a definition also carries its kind / visibility (public|
private) / signature / doc-gist and an :end-anchor (:anchor..:end-anchor
is the whole def, patchable in one edit). Syntactic (tree-sitter identifier
boundaries, no scope resolution — so N same-named definitions are each marked).
Definition detection: the FIRST occurrence inside each definition's
:anchor..:end-anchor line span IS its declaration name (findReferences
returns hits in source order), so it survives decorators / attributes above it.
Tracing SEVERAL names through the same file? Call occurrences-in once
instead of this per name.
Every occurrence of identifier `name` in `path` — the DEFINITION occurrences
ENRICHED — as ONE list (empty if none / unknown language):
{:anchor} ; a plain use
{:anchor :is-definition true :kind :visibility :signature ; a DEFINITION
:doc :end-anchor} ; span = :anchor..:end-anchor
Every entry's SOLE position is its patch-ready `lineno:hash` `:anchor` (the
lineno lives in the anchor — no redundant :line/:column/byte fields). A use is
just that anchor; a definition also carries its kind / visibility (public|
private) / signature / doc-gist and an `:end-anchor` (`:anchor`..`:end-anchor`
is the whole def, patchable in one edit). Syntactic (tree-sitter identifier
boundaries, no scope resolution — so N same-named definitions are each marked).
Definition detection: the FIRST occurrence inside each definition's
`:anchor`..`:end-anchor` line span IS its declaration name (findReferences
returns hits in source order), so it survives decorators / attributes above it.
Tracing SEVERAL names through the same file? Call `occurrences-in` once
instead of this per name.(occurrences-in path source names)The BATCH form of occurrences: every occurrence of EACH identifier in
names in path, as {name [entry …]} — a name that never occurs is simply
absent. Entries are exactly what occurrences returns; see its docstring.
ONE parse, ONE line split and ONE definition walk serve the whole batch, so
the cost tracks the FILE, not the name count. Per-name calls re-parse source
for every name, which is what made tracing N names over M files quadratic —
use this whenever more than one name is traced through the same file.
Tracing the same names through MANY files? Call occurrences-in-files: it
hands the whole file set to the pack in one parallel batch.
The BATCH form of `occurrences`: every occurrence of EACH identifier in
`names` in `path`, as `{name [entry …]}` — a name that never occurs is simply
absent. Entries are exactly what `occurrences` returns; see its docstring.
ONE parse, ONE line split and ONE definition walk serve the whole batch, so
the cost tracks the FILE, not the name count. Per-name calls re-parse `source`
for every name, which is what made tracing N names over M files quadratic —
use this whenever more than one name is traced through the same file.
Tracing the same `names` through MANY files? Call `occurrences-in-files`: it
hands the whole file set to the pack in one parallel batch.(occurrences-in-files paths names read-fn)occurrences-in over MANY paths at once — one {:path :occurrences} map per
path, in REQUEST ORDER, traced in ONE pack batch.
read-fn turns a path into its source, so the CALLER keeps path confinement
(vis resolves through safe-path) while the parse and the fan-out stay in the
pack: the reads run on the pack's pool, then every readable file goes into a
single StructuralApi/findReferences over FileSources, which resolves each
language once up front and walks the files in parallel. Each file is read AND
PARSED once per call, not once per name.
TOTAL per path — a read, language or parse failure becomes {:path :error}
(the message) instead of failing the batch, so one unreadable file cannot sink
a repo-wide trace. Returns [] when names is empty.
`occurrences-in` over MANY paths at once — one `{:path :occurrences}` map per
path, in REQUEST ORDER, traced in ONE pack batch.
`read-fn` turns a path into its source, so the CALLER keeps path confinement
(vis resolves through `safe-path`) while the parse and the fan-out stay in the
pack: the reads run on the pack's pool, then every readable file goes into a
single `StructuralApi/findReferences` over `FileSource`s, which resolves each
language once up front and walks the files in parallel. Each file is read AND
PARSED once per call, not once per name.
TOTAL per path — a read, language or parse failure becomes `{:path :error}`
(the message) instead of failing the batch, so one unreadable file cannot sink
a repo-wide trace. Returns `[]` when `names` is empty.(references path source name)Occurrences of identifier name in path as
[{:line :column :start-byte :end-byte :anchor} …] (empty if none / unknown
language). :anchor is the line's <lineno>:<hash> patch anchor, so a hit
can be edited directly with patch — same anchors cat / index emit.
Occurrences of identifier `name` in `path` as
[{:line :column :start-byte :end-byte :anchor} …] (empty if none / unknown
language). `:anchor` is the line's `<lineno>:<hash>` patch anchor, so a hit
can be edited directly with patch — same anchors cat / index emit.(scan-mapv f items)mapv over items across the pack's scan pool, in REQUEST ORDER.
Straight delegation to StructuralApi/mapParallel: workers pull the next index
off a shared cursor, so one huge file cannot strand a worker while the others
idle. The first exception is rethrown AS THROWN (never wrapped in an
ExecutionException) so a tool's :on-error-fn still sees the original
ex-info; every worker is awaited, so no task outlives the call.
`mapv` over `items` across the pack's scan pool, in REQUEST ORDER. Straight delegation to `StructuralApi/mapParallel`: workers pull the next index off a shared cursor, so one huge file cannot strand a worker while the others idle. The first exception is rethrown AS THROWN (never wrapped in an `ExecutionException`) so a tool's `:on-error-fn` still sees the original `ex-info`; every worker is awaited, so no task outlives the call.
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 |