Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions crates/pgls_pretty_print/src/nodes/case_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,28 @@ use crate::{
};

pub(super) fn emit_case_expr(e: &mut EventEmitter, n: &CaseExpr) {
emit_case_expr_impl(e, n, true);
}

/// Emits a branch result, `then <result>` or `else <result>`.
///
/// In expanded layout a CASE aligns its branches with the column it starts at. A CASE nested as a
/// result would start after `then `, so every level would move the next one right by the keyword
/// and two indents, and a few levels run past the line width. A nested CASE is therefore indented
/// from the line of its THEN or ELSE instead: its branches one level in, `end` under `then`.
pub(super) fn emit_case_result(e: &mut EventEmitter, result: &pgls_query::protobuf::Node) {
super::emit_node_with(result, e, |node, e| match node {
pgls_query::NodeEnum::CaseExpr(case) => emit_case_expr_impl(e, case, false),
node => super::emit_node_enum(node, e),
});
}

fn emit_case_expr_impl(e: &mut EventEmitter, n: &CaseExpr, align: bool) {
e.group_start(GroupKind::CaseExpr);

let expanded = matches!(e.config().layout, crate::Layout::Expanded);
if expanded {
let align = expanded && align;
if align {
e.align_start();
}
e.token(TokenKind::CASE_KW);
Expand All @@ -35,14 +53,14 @@ pub(super) fn emit_case_expr(e: &mut EventEmitter, n: &CaseExpr) {
super::emit_layout_break(e);
e.token(TokenKind::ELSE_KW);
e.space();
super::emit_node(defresult, e);
emit_case_result(e, defresult);
}

e.indent_end();
super::emit_layout_break(e);
e.token(TokenKind::END_KW);

if expanded {
if align {
e.align_end();
}
e.group_end();
Expand Down
2 changes: 1 addition & 1 deletion crates/pgls_pretty_print/src/nodes/case_when.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub(super) fn emit_case_when(e: &mut EventEmitter, n: &CaseWhen) {

if let Some(ref result) = n.result {
e.space();
super::emit_node(result, e);
super::case_expr::emit_case_result(e, result);
}
if expanded {
e.indent_end();
Expand Down
42 changes: 38 additions & 4 deletions crates/pgls_pretty_print/tests/case_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ fn expanded_nested_case_keeps_each_branch_at_its_own_indent() {
\tCASE status\n\
\t\tWHEN 'A'\n\
\t\t\tTHEN CASE\n\
\t\t\t\t WHEN enabled\n\
\t\t\t\t\t THEN 'active'\n\
\t\t\t\t ELSE 'paused'\n\
\t\t\t END\n\
\t\t\t\tWHEN enabled\n\
\t\t\t\t\tTHEN 'active'\n\
\t\t\t\tELSE 'paused'\n\
\t\t\tEND\n\
\t\tELSE 'closed'\n\
\tEND\n\
;";
Expand Down Expand Up @@ -249,3 +249,37 @@ fn the_documented_expanded_case_layout() {
"select\n id\n , case\n when status = 'active'\n and deleted_at is null\n then 'live'\n else 'archived'\n end as state\nfrom\n accounts;"
);
}

#[test]
fn expanded_case_nested_in_else_is_indented_from_else() {
let sql = "SELECT CASE WHEN a THEN 1 ELSE CASE WHEN b THEN 2 ELSE 3 END END;";
let expected = "SELECT\n\
\tCASE\n\
\t\tWHEN a\n\
\t\t\tTHEN 1\n\
\t\tELSE CASE\n\
\t\t\tWHEN b\n\
\t\t\t\tTHEN 2\n\
\t\t\tELSE 3\n\
\t\tEND\n\
\tEND\n\
;";
assert_expanded_layout(sql, expected);
}

/// Every level of a CASE nested in THEN moves right by two indents, so twenty levels stay within
/// a width of 100 with the default indent.
#[test]
fn deeply_nested_expanded_case_stays_within_the_width() {
let mut sql = String::from("0");
for level in 0..20 {
sql = format!("case when c{level} > {level} then {sql} else {level} end");
}
let config = FormatConfig {
layout: Layout::Expanded,
..Default::default()
};
let output = format_idempotently(&format!("select {sql} as x from t"), &config);
let widest = output.lines().map(str::len).max().unwrap();
assert!(widest <= 100, "{widest} columns:\n{output}");
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ from accounts;
select case kind
when 'a'
then case
when x > 1
then 'big'
else 'small'
end
when x > 1
then 'big'
else 'small'
end
else 'other'
end
from t;
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ from accounts;
select case kind
when 'a'
then case
when x > 1
then 'big'
else 'small'
end
when x > 1
then 'big'
else 'small'
end
else 'other'
end
from t;
6 changes: 4 additions & 2 deletions docs/features/formatting.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,10 @@ where
With `layout: "expanded"`, a `CASE` expression puts every `WHEN` and `ELSE`
on its own line, one indent level in from where `CASE` starts, `THEN` one
level further in, and `END` back under `CASE`. This holds wherever the `CASE`
starts, after a leading comma or `THEN` for instance. A `WHEN` condition that
breaks continues one level in from `WHEN`:
starts, after a leading comma for instance. A `CASE` nested as the result of a
`THEN` or `ELSE` is indented from that line instead, with `END` under `THEN` or
`ELSE`, so that deep nesting doesn't run past the line width. A `WHEN`
condition that breaks continues one level in from `WHEN`:

```sql
select
Expand Down
Loading