speclj.line-filter

*chosen-characteristics*

dynamic

*chosen-descriptions*

dynamic

*run-unmatched*

dynamic

active?

(active?)
True once *chosen-characteristics* has been bound (even to an empty set).
Callers bind this explicitly when `*line-targets*` is non-empty.

filtered-file?

(filtered-file? component)
True when this component's :file is named by some entry in *line-targets*.
Untargeted files run through normal focus/tag logic; only files with an
active line-target get narrowed.

pass-line-filter?

(pass-line-filter? component)
Only call this for components in `filtered-file?` files; untargeted files
should be gated by the normal focus/tag logic instead. Characteristics
pass iff they're in *chosen-characteristics*; Descriptions pass iff they
sit on the ancestor chain of a chosen characteristic.

resolve-targets

(resolve-targets descriptions line-targets)
Walks the description tree once and resolves each [user-path line-num]
target into a set of chosen Characteristic instances plus a list of
unmatched target strings (targets whose file isn't loaded).

Returns {:chosen #{characteristics...} :unmatched ["path:line" ...]}

split-line-spec

(split-line-spec s)
Splits "path:line" → [path line-int]. Returns [s nil] when s has no
numeric :N suffix. Preserves Windows drive letters like "C:\\foo.clj"
by requiring the matched path to be longer than one char and to not end
with a colon (which would mean a bare drive letter or "foo::N").

with-chosen

(with-chosen descriptions body-fn)
If `*line-targets*` is non-empty, resolves them against `descriptions`,
records any unmatched targets into `*run-unmatched*` (when bound), and
invokes body-fn with `*chosen-characteristics*` and
`*chosen-descriptions*` bound. Otherwise just invokes body-fn.