Rbtz.CredoChecks.HeexSource (rbtz_credo_checks v0.3.0)

Copy Markdown View Source

Internal helper used by the HEEx-scanning Rbtz Credo checks.

Template extraction

templates/1 returns every ~H sigil string and every .heex template referenced via embed_templates as a list of {contents, line_fn} tuples. line_fn is a function that takes an offset (0-based line index into contents) and returns the line number in the .ex source file to report an issue against.

For ~H sigils, line_fn.(off) = sigil_start_line + off for inline sigils (~H"<x/>") or sigil_start_line + 1 + off for heredoc sigils (~H""" ... """), since the first line of heredoc content sits on the source line after the opening delimiter.

For embed_templates, the template contents live in a separate .html.heex file — Credo's format_issue can only reference lines that exist in the .ex source file, so line_fn.(_) always returns the line of the embed_templates call.

Body capture

capture_interpolation/1 and capture_string/1 extract the inside of class={...} and class="..." attribute bodies respectively, tracking string/escape state so braces nested inside strings or escaped quotes are handled correctly.

Tag walking

walk_tags/3 walks a template line-by-line, tracking multi-line open tags, and returns one record per fully-opened tag ({open_line, trigger, presence}). Callers provide a tag-open detector and a keyword list of attribute detectors; the helper centralises the state machine the various phx-* checks share.

Summary

Functions

Captures the content of a {...} interpolation body.

Captures the content of a "..." string literal body.

Returns the number of \n bytes in binary.

Returns true when text contains an id= (or id / id={) attribute as a standalone token — not as a tail of a hyphenated name such as data-id= or aria-labelledby=.

Returns [{contents, line_fn}] for every HEEx template embedded in the file.

Walks a HEEx template line-by-line and returns one record per fully-opened tag.

Functions

capture_interpolation(input)

Captures the content of a {...} interpolation body.

Call with input pointing to the first byte inside the {. Consumes until the matching close brace at depth 0 and returns {:ok, captured}, or :unterminated if the input runs out first. Braces inside string literals (with backslash escapes) do not affect the depth counter.

capture_string(input)

Captures the content of a "..." string literal body.

Call with input pointing to the first byte inside the opening ". Consumes until the closing (unescaped) " and returns {:ok, captured}, or :unterminated if the input runs out first. The captured content preserves backslash escapes verbatim.

count_newlines(binary)

Returns the number of \n bytes in binary.

Checks that compute a line number by slicing into the template via binary_part/3 use this to translate a byte offset into a line offset.

has_id?(text)

Returns true when text contains an id= (or id / id={) attribute as a standalone token — not as a tail of a hyphenated name such as data-id= or aria-labelledby=.

Shared between the phx_*_without_id checks and the LiveViewFormCanBeRehydrated check so the regex lives in one place.

templates(source_file)

Returns [{contents, line_fn}] for every HEEx template embedded in the file.

walk_tags(arg, detect_open, attr_detectors)

Walks a HEEx template line-by-line and returns one record per fully-opened tag.

detect_open.(line) returns {trigger, rest_of_line} for a tag opener it wants to track, or nil to skip the line. attr_detectors is a keyword list of {key, detector_fn} — each detector_fn.(text) is called with the current text slice and returns truthy if the attribute appears. Presence is sticky: once an attribute is seen on any line of a tag's body, it stays true.

Returns [{open_line, trigger, presence_map}] in source order, where presence_map has one boolean per key in attr_detectors.

Limitation: splits a tag at the first > on any line, so > characters inside attribute strings can prematurely close a tag. This matches the behaviour of the pre-extraction per-check scanners.