ColonTargets
ColonTargets constructs alignment members at every : context the alignment and singleton rules consume. The distinct contexts in the Python grammar that carry a colon worth aligning are listed below, and the walker that finds them is identical across rules, so the walker lives once in ColonTargets and each consuming rule supplies a receiver that handles the discovered members.
Public Surface
ColonTargets lives at src/primitives/colon_targets.rs and is pub(crate). Two consumers use it today:
At 1.0 the trait promotes to pub, so a downstream can implement a :-context rule of its own.
The Contexts
- Dict items.
{key: value, key: value}literals, where eachkey: valuepair contributes a member. - Class fields. Class bodies whose statements are
field: Annotation = default, where each field's annotation colon contributes a member. - Annotated function parameters.
def f(param: T, param: T)signatures, where each annotated parameter contributes a member. - Google / numpy docstring
Args:entries. Docstring structured sections, where eachname: descriptionline contributes a member. - Match-arm cases.
match x: case Pattern: ..., where each case's pattern-to-body colon contributes a member.
Each context resolves a Member for the alignment math, with width being the display-column width of the left-hand side and gap being the whitespace range immediately before the colon.
Internal Surface
The receiver trait carries the per-context handlers, where only handle is required with an empty default body and the others are provided methods a consuming rule overrides on a need-by-need basis:
pub(crate) trait ColonEmitter {
fn handle(&mut self, members: &[aligner::Member]) {}
fn match_arms(&mut self, members: &[aligner::Member]) {
self.handle(members);
}
fn walk(&mut self, source: &Source) where Self: Sized { /* provided */ }
}handle is the catch-all for class fields, docstring args, dict entries, and parameters. match_arms is split out so a rule can opt out of match-arm alignment by overriding it to a no-op (which is what does, since owns the match-arm context), with its default delegating to handle for any rule that wants the unified callback.
walk(source) is the provided driver across source's module body, recursing into nested classes, functions, matches, and expressions so a single call covers the whole tree. A consuming rule never overrides walk, because calling the provided method is enough to drive the receiver across every relevant context.
match_case(source, case) -> Option<aligner::Member> is exposed pub(crate) alongside the trait for
match_case directly.Build Pattern
A rule implementing ColonEmitter carries a single accumulator (typically Vec<Vec<Member>> for grouped members) and pushes into it from each handler. After walk(source) returns, the accumulator carries every group the rule cares about, and the rule emits Vec<Edit> by calling Aligner's emit_group against each group.
How Grouping Works
Each context defines its own grouping shape, because what counts as "adjacent" inside a dict literal differs from what counts as "adjacent" across class-body statements:
- Dict items group by line-adjacency between one key's end and the next item's start. A trailing comment rides with its entry and a
**spreadentry skips the colon scan, neither breaking the run, so the rest of the dict aligns around them. - Class fields group via
line_adjacent_groupsover the class body's statements, treating any non-field: Tstatement as a divider. - Annotated function parameters group via
parameter_split_groups, splitting at the first parameter that does not qualify (an un-annotated argument, a*argsor**kwargs, a/or*separator). - Match arms group one per
matchstatement, with every arm's colon contributing a member. Patterns may span multiple lines, so the alignment column is per-matchrather than per-line-run. - Docstring
Args:entries group one per docstring, with the structured-section parser invoked inline to find the entries.
Each group is handed to the receiver as one &[Member] slice, so the consumer aligns within the group without seeing cross-group state. The seam at the docstring-args context overlaps with Docstring's walker, where the leading-docstring detection lives in both primitives independently. The duplication is deliberate, because the colon walker reaches for the docstring body without standing up a separate DocstringHandler.
Re-Using This Primitive
A new :-context rule implements ColonEmitter, overrides the handlers for the contexts it cares about, and calls walk(source) from inside its apply method. The shared walker, the same-indentation grouping, and the per-context member construction come for free.
Related
Aligneris the math the producedMemberlists feed into.- aligns multi-item groups across every context.
- strips padding from singleton groups.
- owns the match-arm context exclusively.