Repository navigation
fix(pretty_print): indent a nested CASE from its THEN or ELSE - #935
Merged
Merged
Conversation
…functions break sensibly
…print-grant-admin-option
…int-long-identifiers
This was referenced Oct 24, 2026
psteinroe
added a commit
that referenced
this pull request
Oct 26, 2026
Stacked on #893. Follow-up to #884. Expanded CASE expressions lost their alignment when `CASE` started left of the current indent, and a broken `WHEN` condition continued at the `WHEN` column. **Alignment from where CASE starts** The renderer stored an alignment as an offset from the indent of the level it started at, and clamped a negative offset to zero. When the text on a line starts left of that indent, the offset is negative. That happens for `select case` once the select list has opened its indent, or `on case` in a join, and `WHEN` and `END` then drifted right: ```sql -- layout: expanded, clauseBodyStyle: compact, indentSize: 8 -- before select case when a then 1 else 2 end from t; -- after select case when a then 1 else 2 end from t; ``` An alignment now keeps the column `CASE` starts at together with the indent level at that point. A continuation line starts at that column plus one indent per level opened since. When the offset is not negative this gives the same columns as before, so the existing expanded CASE output is unchanged. With `indentStyle: "tabs"` an aligned line is written as one tab per indent level and spaces for the rest, as before. When the column is left of the indent, it uses only as many tabs as fit before the column: `\t when` instead of `\t\t when`. **WHEN conditions are indented** A `WHEN` condition that breaks continued at the column of `WHEN`, so `and` read as a sibling of `when`. In expanded layout every AND/OR breaks, so this affected every multi-operand condition: ```sql -- before case when aaaa = 1 and bbbb = 2 then 'x' -- after case when aaaa = 1 and bbbb = 2 then 'x' ``` The condition is now one indent level in from `WHEN`, the same way a WHERE condition is. Fit layout does the same once the condition breaks. Expanded layout still breaks at every AND/OR in a WHEN condition. I left that alone because it is what expanded layout does everywhere else, and the reporter of #884 uses it that way. **Docs** `docs/features/formatting.md` now describes the expanded CASE layout and what tabs do to alignment: the columns only line up when the editor's tab width equals `indentSize`. A test checks that the example in the docs is what the formatter prints. **Tests and snapshot changes** New tests in `case_layout.rs`: CASE after `on` with `indentSize: 4`, `select case` with `indentSize: 8` in spaces and tabs, the exact tab and space output after a leading comma, and WHEN condition continuation in expanded and fit layout. Two tests in `comment_layout.rs` now expect the `AND` of a WHEN condition, and the comment before it, one tab further in. The only snapshot that changes is `expanded_case_expr_200`, where the AND lines of two WHEN conditions move one level in. **Not covered** A CASE nested in the THEN of another CASE still moves right by about 9 columns per level in expanded layout. Twenty levels reach 184 columns at width 100. Three or four levels stay within the width, so I didn't change the layout for this. **Follow-ups:** The nested CASE drift in expanded layout is fixed in #935.
psteinroe
changed the base branch from
fix/pretty-print-comment-anchors
to
main
October 26, 2026 10:40
psteinroe
added a commit
that referenced
this pull request
Oct 26, 2026
…der the keyword (#938) Stacked on #935. Follow-up to #896, which listed this as not covered. A condition that stays on its keyword line (WHERE and HAVING with `clauseBodyStyle: compact`, and ON in every config) was indented for its continuation lines. When the condition only breaks inside the parentheses of a subquery, it has no continuation lines of its own. The indent then put the subquery two levels in and the closing parenthesis one level in, under nothing: ```sql -- layout: expanded, clauseBodyStyle: compact -- before select * from public.permissions where exists ( select 1 from public.permission_set_permissions as rp where rp.permission_name = permissions.name ); -- after select * from public.permissions where exists ( select 1 from public.permission_set_permissions as rp where rp.permission_name = permissions.name ); ``` The same happened to `on exists (` in a join with the default config. **What changed** `emit_clause_condition` (compact) and the ON of a join leave out the continuation indent when the condition breaks only inside a subquery. That covers: - a subquery link: `exists (...)`, `x in (...)`, `x = any (...)`, `(...)`, `array(...)`; - `x <op> (subquery)` or `x <op> array(subquery)`, where `x` is a column, parameter or constant; - `not` in front of either. Everything else keeps the indent, including AND/OR chains, whose operands still continue one level in, with a subquery in them closing at that level. **Snapshot changes** All reviewed; one category: the body and `)` of a subquery opened on a compact `where`/`having` line, or on an `on` line, move one level left, so `)` closes under the keyword. This is in `compact_select_list*`, `scalar_subquery_expanded`, `sublink_bool_groups_expanded` and, for ON with the default config, `predicate`. New tests in `subquery_layout.rs` cover `where exists (`, `where x = (`, a chain with an `exists` operand, and `on x = (`. **Not covered** A lone select item in compact layout has the same pattern: `select coalesce(` indents its arguments two levels and closes one level in. That is the select-list indent from #865 and would need the same rule there.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #934.
In expanded layout, a CASE nested in the THEN of another CASE moved right by
thenplus two indent levels per level of nesting. #884 aligns each CASE's branches with the column it starts at, and a nested CASE starts afterthen. With the default indent, twenty levels reached 184 columns at width 100. Finding P16 of the release review.What changed
A CASE written as the result of a
THENorELSEno longer aligns with its own column. It is indented from the line of its keyword: its branches one level in, andENDunderTHENorELSE. Each level now costs two indents, the minimum for a WHEN and its THEN:else case when ... else case ...chains read the same way. A CASE anywhere else, afterselector a leading comma for instance, still aligns with the column it starts at, which is what #884 was about. Fit layout doesn't align, so it is unchanged.This changes the nested layout that #884's
expanded_nested_case_keeps_each_branch_at_its_own_indenttest asserted, and the test now expects the new layout. I preferred this over breaking afterthenfor every nested CASE: that would add a line and a level for every nested CASE, also the shallow ones that fit today. The docs section on expanded CASE describes the nested layout.Tests and snapshot changes
case_layout.rs: the updated nested test, a CASE nested in ELSE, and twenty levels nested in THEN staying within width 100.expanded_case_alignment(added in test(pretty_print): fuzz the option matrix in CI #912), whose inner branches move left to the new indent.