Skip to content

Formatter

ryl format rewrites YAML files in place to a consistent layout. The formatter edits only the spans that are off-style rather than reprinting the document, so the first run on an existing project produces a small diff, and every later run on formatted files changes nothing.

Biome does not format YAML, so there is no Biome YAML style to migrate.

ryl format

# Format every YAML file under the current directory, in place
ryl format .

# Write nothing; exit 1 if any file would change, listing why
ryl format --check .

# Write nothing; print a unified diff of the changes
ryl format --diff path/to/file.yaml

# Read stdin, write the formatted text to stdout
cat file.yaml | ryl format - --stdin-filename file.yaml

Inputs, file discovery, .gitignore handling and config discovery are the same as ryl check. Unlike ryl check, ryl format needs no config: with none it formats with the defaults below.

--check reports each line ryl format would change as a diagnostic from the rule whose fix changes it, through the same reporters as ryl check, so it honours --output-format and [output] and picks the GitHub format in GitHub Actions.

Already formatted files are skipped on later runs through the content-hash cache. --check can also reuse a clean result; --diff always generates the requested diff.

Guarantees

Every edit ryl format makes is checked by property tests to be:

  • Value-preserving. The document loads to the same data, with the same tags, anchors, aliases, key order and duplicate keys, resolved under the document's YAML version.
  • Comment-safe. Every comment stays beside the node it annotates.
  • Idempotent. Formatting formatted output changes nothing.

A file that does not parse is left alone and reported on stderr as skipped by ryl format.

See File encodings for encoding preservation and --check/--diff behavior when a text patch cannot be emitted. A document-prefix BOM stays before an added --- marker, including in later documents; the BOM does not count toward indentation. Diagnostic columns still count a document-prefix BOM that remains in the source text. The same exit behavior applies when a patch is skipped because the YAML ends in a bare carriage return or the filename cannot be represented in a diff header.

Configuration

ryl format reads the [format] table of the same .ryl.toml, ryl.toml or pyproject.toml ([tool.ryl.format]) that ryl check uses. A YAML config cannot set it, so YAML-configured projects get the defaults.

[format]
quote-style = "single"
line-ending = "lf"
document-start = "preserve"
document-end = "preserve"
fold-long-lines = false
brace-spacing = false
comment-spacing = 2
comment-starting-space = "add"
max-blank-lines = 2
preview = false
sequence-style = "preserve"
mapping-style = "preserve"
indent-sequences = true
dash-on-own-line = false
Key Default Values What it sets
quote-style "single" "single", "double", "preserve" The quote used where a string needs quoting; "preserve" leaves all quoting alone. See Quote style.
line-ending "lf" "lf", "cr-lf", "native", "auto" Line endings, including the final newline; "native" is the platform's, and "auto" keeps the ending most of the file's lines use ("lf" on a tie or a file without line breaks).
document-start "preserve" "add", "preserve" Whether to add a missing --- document start marker.
document-end "preserve" "add", "preserve" Whether to add a missing ... document end marker.
fold-long-lines false true, false Whether to split plain, quoted and folded (>) scalar lines longer than line-length at single spaces. See Long lines.
brace-spacing false true, false Whether to write one space inside non-empty flow mapping braces, { a: 1 }. Empty braces and brackets stay unpadded.
comment-spacing 2 1 to 255 Exact number of spaces between content and an inline comment.
comment-starting-space "add" "add", "preserve" Whether to add a missing space after a comment's #.
max-blank-lines 2 0 to 255 Most blank lines kept in a row between content lines; leading and trailing runs are removed. Scalar content is preserved, including keep-chomping (\|+/>+) tails; clip/strip tails can be trimmed.
indent-sequences true true, false Whether a block sequence under a mapping key is indented past the key; false keeps it flush.
dash-on-own-line false true, false Whether a block mapping in a block sequence starts on the line after its -; false joins them as - name: web.
preview false true, false Opt in to style changes before they become stable. See Preview style.
sequence-style "preserve" "preserve", "block", "flow" Collection style for sequences. See Collection style.
mapping-style "preserve" "preserve", "block", "flow" Collection style for mappings. See Collection style.

The top-level line-length and indent-width keys are shared with ryl check, where they set the defaults for the line-length and indentation rules (see Quick start). ryl format re-indents every block level to indent-width, and uses both as the fold width and continuation indent under fold-long-lines.

Without indent-width, an inline value under an explicit block key (? key followed by : value) uses 2 if the value establishes the file's consistent indentation step. An explicit key directly inside a sequence item does not establish that step. Otherwise, ryl format keeps each file's own width: of the widths 2 to 8, the one whose re-indent changes the fewest lines, block scalar bodies and comments included. A tie, or a file the re-indent leaves alone at every width, falls back to 2. A file mostly at 4 with one block at 2 is re-indented to 4. If restyling a collection changes which width best fits the file, the formatter re-detects the width and completes the re-indent in the same run. If the detected width would leave inconsistent indentation, ryl format keeps that document's indentation and reports the sites it cannot fix.

The rest of the layout is fixed:

Concern ryl format writes
Own-line comments Aligned with the content they precede
Flow collections No spaces inside [] or empty {}, and inside other {} per brace-spacing; no space before a comma, one after
Blank lines None at the start or end of the file
Line ends Remove trailing whitespace outside scalar content; add a final newline
After -, ? and : One space, re-indenting a compact collection that hangs on it

Block scalar headers lose trailing spaces and tabs. A header or body at EOF gains a final newline and, with document-end = "add", a ... marker. An indentation-only final line loses its spaces when they are not scalar content. For keep-chomped scalars (|+ or >+), the formatter adds a line break after removing those spaces to preserve the YAML 1.2 reference parser's value. When adding a document end marker, it removes only indentation spaces from a final blank block-scalar line. A root explicit-indent scalar keeps value-bearing spaces; the marker is left unfixed if appending it would change the reference value.

A document that a tab indents, or that re-indenting would parse differently, is left as it is and named on stderr.

Default profile

With no [format] table, ryl format writes the profile below. It preserves existing document start markers, can remove redundant quotes or switch required double quotes to single, and can indent flush sequences. Migration writes quote-style = "preserve" when quoted-strings is disabled in the source preset.

Default Why
quote-style = "single" yamlfix's default; see Quote style for when it applies
line-ending = "lf" The default of prettier
document-start = "preserve" prettier keeps a file's markers as written
document-end = "preserve" No formatter adds ...; prettier keeps it
fold-long-lines = false Prose keeps its line breaks, as under prettier's proseWrap: "preserve"
brace-spacing = false yamlfix also leaves braces unpadded; prettier pads them
comment-spacing = 2 The default of ruff and yamlfix
comment-starting-space = "add" Adds a space after #
max-blank-lines = 2 Keeps short gaps between content
sequence-style = "preserve" Restyling a collection is opt-in
mapping-style = "preserve" Restyling a collection is opt-in
indent-sequences = true prettier also indents them
dash-on-own-line = false prettier writes - name: web
preview = false Preview styles are opt-in, as in ruff
line-length = 80 The default of prettier
indent-width = 2 The default of prettier and yamlfix, for a file that shows no width of its own
Fixed layout above Every formatter probed agrees

Long lines

With fold-long-lines = true, ryl format splits scalar lines longer than the top-level line-length (default 80) at single spaces. Folding applies to plain and quoted values in block context, and folded (>) scalar lines at their content indent. For plain and quoted values, the new line takes the indent of the scalar's existing continuation lines, or else sits indent-width past the column of the collection that owns the value:

# before
items:
  - description: This single line is well over eighty characters wide and so the formatter should fold it.
# after, with fold-long-lines = true
items:
  - description: This single line is well over eighty characters wide and so the
      formatter should fold it.

Folded (>) scalars keep their content indent; more-indented lines stay whole. Keys, flow collections, literal (|) scalars, and words with no space to break at stay as they are. Folding ignores the line-length rule's own options, and ryl check --fix never folds.

Collection style

With sequence-style or mapping-style set to "block", ryl format rewrites flow collections of that kind in block style, indented by indent-width, copying each entry's text. An inline comment after the collection moves to its key's line:

# before
items: [one, two]  # shopping
metadata: {name: example}
# after, with both set to "block"
items:  # shopping
  - one
  - two
metadata:
  name: example

When the width is inferred, a conversion stays in flow style with a notice if the result would fail the indentation rule's consistent-width check. A compact sequence mapping with explicit keys can fix the lint rule's inferred step independently of the formatter's detected width.

Empty [] and {} stay flow. A collection that holds a comment, a multi-line entry, a collection or empty key, or an entry some loaders would read differently in block style stays as written, and ryl format says why on stderr. If the document cannot be re-indented safely, flow collections in that document stay flow, with a refusal notice. Block-to-flow conversions and conversions in other documents can still proceed.

When a nested block scalar has only blank body lines before content dedents past its header, ryl format leaves the file unchanged and reports a refusal: the YAML reference parser can still read the dedented text as scalar content. A sibling key at the same indent can trigger the same refusal when a sequence dash may move. The same protection applies when indented # text and blank lines form scalar content. ryl check --fix also reports the file, line and reason on stderr and leaves the file unchanged, apart from independently safe key ordering. In Markdown the refusal covers only the embedded YAML region that holds the scalar; other regions are still formatted and fixed. Compact explicit-key sequences with blank scalar lines are refused when spacing the header would change the scalar's content indent. For other block scalars, re-indentation moves value-bearing blank-line spaces with the scalar header.

With "flow", a block collection of scalars and aliases that is not the document root becomes one flow line, such as items: [one, two], when it holds no comment and the line fits line-length. Block scalars, multi-line or empty entries, and plain scalars holding ,[]{} or starting with : or ? keep it block. Flow collections already written are left alone, even long ones.

Quote style

ryl format quotes a string only where its plain form would load as something else, using quote-style:

# before
a: "x"
b: "123"
c: "it's"
d: "tab\there"
e: 'it''s: x'
f: "say \"hi\": x"
# after, with quote-style = "single"
a: x
b: '123'
c: it's
d: "tab\there"
e: "it's: x"
f: 'say "hi": x'

A string that needs quoting takes whichever quotes avoid an escape: single when it contains ", double when it contains only ', else quote-style. A string that needs escape sequences stays double-quoted. Physical line breaks alone do not require double quotes: conversion to single quotes preserves the continuation lines and blank lines, including their YAML folding. A string that YAML 1.1 loads as a boolean, such as "yes", "NO" or "on", keeps its quotes, so truthy stays clean. So do "y", "Y", "n" and "N", which go-yaml v2, and so Kubernetes, loads as booleans. Mapping keys follow the same rules.

Preview style

preview = true opts in to style changes before they become stable. As in ruff, the stable style changes only in a minor release, by promoting a preview style, while a preview style can change in any release. A fix for invalid output, a changed value or a lost comment can ship in any release. No preview styles exist yet, so preview changes nothing today.

YAML in Markdown

ryl format formats YAML front matter and fenced YAML blocks in Markdown files matched by [files].markdown, as described in YAML in Markdown, or every input given --markdown, as with ryl check.

If formatting would create a line that closes a YAML fence, ryl leaves that region unchanged and names the refusal on stderr. Other regions still format. The same guard applies to editor formatting. When formatting would change a region that mixes CRLF and LF line endings, ryl leaves the region unchanged with a notice and --check / --diff exit 1. An already formatted mixed region is quiet.

Suppression

ryl format honours the same inline directives as ryl check: a # ryl disable-file comment leaves the file alone, and # ryl disable rule:quoted-strings or disable-line rule:quoted-strings stops that rule's formatting where it applies. There is no format-only directive. Lint configuration's [[lint.per-line-ignores]] and [lint.per-file-ignores] suppress lint findings and safe check --fix edits, but do not gate ryl format.

Conflicting lint rules

The formatter's layout is also checked by lint rules, and they stay lint rules: ryl check still reports them, and ryl check --fix still fixes the fixable ones. When one of them is enabled with options that reject what ryl format writes, every ryl format run warns on stderr, for example:

warning: the quoted-strings lint rule's options are incompatible with the formatter's `[format] quote-style = "single"`. Disable quoted-strings when using `ryl format`, or set its options `quote-type = "single"`, `required = "only-when-needed"`, `allow-double-quotes-for-escaping = true`, `allow-quoted-quotes = true`.

--no-warnings silences it. A rule left at its defaults conflicts only where the table says so.

Rule Conflicts with ryl format when
braces forbid is set and [format] mapping-style is not "preserve", min-spaces-inside-empty is above 0, or min-spaces-inside to max-spaces-inside excludes the brace-spacing padding (0, or 1 when true)
brackets forbid is set and [format] sequence-style is not "preserve", or min-spaces-inside or min-spaces-inside-empty is above 0
commas min-spaces-after is above 1, or max-spaces-after is 0
comments min-spaces-from-content is above [format] comment-spacing, or max-spaces-from-content is below it (-1, the default, is unlimited); or ignore-shebangs = false while require-starting-space = true under [format] comment-starting-space = "add", since a shebang keeps its #!
comments-indentation Never
document-start present = false with [format] document-start = "add"
document-end present = false with [format] document-end = "add"
empty-lines max is below [format] max-blank-lines
new-line-at-end-of-file Never
new-lines type resolves to a different ending from [format] line-ending, or line-ending = "auto", which writes either
quoted-strings See below; never under quote-style = "preserve"
trailing-spaces Never
colons max-spaces-after is 0
hyphens max-spaces-after is 0, or dash-on-own-line = true while [format] dash-on-own-line = false
indentation spaces is a number other than indent-width, or any number while indent-width is unset; or indent-sequences is the opposite of [format] indent-sequences
line-length Never; folding is opt-in and leaves lines it cannot break

The table compares formatter settings with lint options; it does not promise every finding can be fixed. A keep-chomped (|+/>+) tail can exceed empty-lines max, and trailing spaces inside a block scalar can remain findings after ryl format and ryl check --fix because removing them would change the scalar value.

quoted-strings accepts the formatter's output when required is "only-when-needed" or false, extra-required is empty, and quote-type is either "any" or matches quote-style. A quote-type of "single" or "double" also needs allow-quoted-quotes = true, for strings quoted to avoid an escape, and "single" needs allow-double-quotes-for-escaping = true, for the strings that need escapes. The rule's defaults (required = true) conflict:

[lint.rules]
quoted-strings = "enable"

These quoted-strings configs accept the formatter's output:

[lint.rules.quoted-strings]
quote-type = "any"
required = "only-when-needed"
[lint.rules.quoted-strings]
quote-type = "single"
required = "only-when-needed"
allow-double-quotes-for-escaping = true
allow-quoted-quotes = true
[format]
quote-style = "double"

[lint.rules.quoted-strings]
quote-type = "double"
required = "only-when-needed"
allow-quoted-quotes = true

A YAML config cannot set allow-double-quotes-for-escaping, so there use quote-type: any:

rules:
  quoted-strings:
    quote-type: any
    required: only-when-needed

The other conflicts follow the table:

[format]
document-start = "add"

[lint.rules.document-start]
present = false
[lint.rules.document-start]
present = false
[format]
brace-spacing = true

[lint.rules.braces]
min-spaces-inside = 1
max-spaces-inside = 1
min-spaces-inside-empty = 0
max-spaces-inside-empty = 0
[lint.rules.commas]
max-spaces-after = -1

[lint.rules.comments]
max-spaces-from-content = -1

The other eleven rules check things ryl format never changes, so they never conflict with it: anchors, block-scalar-chomping, empty-values, float-values, key-duplicates, key-ordering, merge-keys, octal-values, tags, truthy and unicode-line-breaks. truthy and key-ordering keep their ryl check --fix fixes.

To lint alongside ryl format, start from the recommended starter config, which enables only rules the formatter never touches and leaves layout to ryl format --check.

Running with ryl check --fix

Run ryl check --fix before ryl format, in a pre-commit or prek config as on the command line, so the formatter has the last word on layout; ruff gives the same order for ruff check --fix and ruff format. Re-indenting can still push a line past line-length, which the line-length rule then reports (see Conflicting lint rules), so lint a formatted file rather than relying on the check that ran before formatting.

Exit codes

Code ryl format ryl format --check / --diff
0 Files formatted, or nothing to do No file would change; a symlinked input is refused on stderr but still exits 0
1 Not used At least one file would change, a mixed-newline Markdown region needs formatting, or a ragged Markdown region retains layout findings
2 A usage or config error, an unreadable or unwritable file, or a file no [files] glob matches A usage or config error, an unreadable file, or a file no [files] glob matches

A failed write names the file on stderr; formatting continues with the remaining files. A file skipped for a syntax error does not change the exit code.