all2md.linter.fixes

Auto-fix framework for the linter.

A fix is a closure attached to a Violation. The rule’s check() captures the offending AST node and emits a Violation whose fix field carries a LintFix — the framework calls LintFix.apply to mutate the captured node in place. There is no second fix() method on LintRule, so check and fix cannot drift.

Most fixes mutate Text.content and never touch FixContext. Structural fixes (e.g. removing an empty heading) call fctx.remove(node) to detach a node from its parent — the framework handles the parent lookup lazily.

class all2md.linter.fixes.FixSafety

Bases: IntEnum

Safety classification for an auto-fix.

Lower numeric value means safer / less invasive. apply_fixes uses <= max_safety to decide which fixes to apply, so SAFE fixes run under both --fix and --fix-unsafe while SUGGESTED fixes run only under --fix-unsafe.

SAFE = 1
SUGGESTED = 2
MANUAL = 3
property label: str

Lowercase label suitable for human output (‘safe’, ‘suggested’, ‘manual’).

__new__(value)
class all2md.linter.fixes.LintFix

Bases: object

A fix attached to a Violation.

Parameters:
  • target (Node) – The AST node this fix mutates or removes. Used for conflict detection (two fixes targeting the same node — the first one wins).

  • apply (Callable[[FixContext], None]) – In-place mutation of the AST. For text mutations the callback usually closes over target and rewrites target.content. For structural fixes the callback uses ctx.remove(target) or ctx.replace(old, new).

  • safety (FixSafety) – How aggressively to apply this fix.

  • description (str) – Short human-readable description of what the fix does, for reporters and logs.

target: Node
apply: Callable[[FixContext], None]
safety: FixSafety
description: str
__init__(target: Node, apply: Callable[[FixContext], None], safety: FixSafety, description: str = '') None
class all2md.linter.fixes.AppliedFix

Bases: object

Record of a fix that was (or would have been) applied.

rule_code: str
line: int | None
description: str
safety: FixSafety
to_dict() dict

Serialise to a plain dict for the JSON reporter.

__init__(rule_code: str, line: int | None, description: str, safety: FixSafety) None
class all2md.linter.fixes.FixContext

Bases: object

Mutation primitives passed to LintFix.apply.

The parent map is built lazily on first use, so fixes that only mutate Text.content never pay for it.

Initialise a context for document.

The parent map is not built until the first call to parent_of(), remove(), or replace().

__init__(document: Document) None

Initialise a context for document.

The parent map is not built until the first call to parent_of(), remove(), or replace().

parent_of(node: Node) Node | None

Return the parent of node in this context’s document, or None.

remove(node: Node) bool

Detach node from its parent.

Returns True on success, False if the node has no parent or the parent’s container does not hold it (e.g. the node has already been detached by an earlier fix in the same run).

replace(old: Node, new: Node) bool

Replace old with new in its parent’s container.

Returns True on success. Plumbed for future structural fixes; none of the v2.0 SAFE fixes use it.

all2md.linter.fixes.apply_fixes(doc: Document, violations: list['Violation'], max_safety: FixSafety) tuple[list[AppliedFix], list[AppliedFix]]

Apply every fix attached to violations whose safety is <= max_safety.

Conflict policy: when two fixes target the same node (by id()), the first one (in deterministic outer-to-inner, top-to-bottom order) is applied; subsequent fixes targeting that node are deferred and returned in skipped_conflicts. Users re-run --fix to converge.

Returns:

Two lists of AppliedFix records — the first describes fixes that ran, the second describes fixes that were deferred because an earlier fix already touched their target.

Return type:

(applied, skipped_conflicts)