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
9 changes: 9 additions & 0 deletions crates/pgls_pretty_print/src/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,15 @@ pub(crate) fn first_node_location(ast: &NodeEnum) -> Option<i32> {
.min()
}

/// The location of the node of `ast` that starts last in the source. A trailing comment written
/// after `ast` is attached there.
pub(crate) fn last_node_location(ast: &NodeEnum) -> Option<i32> {
ast.iter()
.filter_map(|node| node_location(&node))
.filter(|location| *location >= 0)
.max()
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
21 changes: 18 additions & 3 deletions crates/pgls_pretty_print/src/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,25 @@ impl EventEmitter {

/// Emits and consumes comments that follow `location`, if any.
pub fn take_trailing_comments_at(&mut self, location: i32) {
let Some(comments) = self.trailing_comments.remove(&location) else {
return;
};
let comments = self.remove_trailing_comments_at(location);
self.emit_trailing_comments(comments);
}

/// Consumes the comments that follow `location` without emitting them, so that the caller
/// can emit them somewhere else with `emit_trailing_comments`.
pub fn remove_trailing_comments_at(&mut self, location: i32) -> Vec<Comment> {
self.trailing_comments.remove(&location).unwrap_or_default()
}

/// Puts back comments taken with `remove_trailing_comments_at`.
pub fn restore_trailing_comments_at(&mut self, location: i32, comments: Vec<Comment>) {
if !comments.is_empty() {
self.trailing_comments.insert(location, comments);
}
}

/// Emits comments that follow the node just emitted.
pub fn emit_trailing_comments(&mut self, comments: Vec<Comment>) {
for comment in comments {
if comment.after_separator {
self.comments_after_separator.push(comment);
Expand Down
4 changes: 4 additions & 0 deletions crates/pgls_pretty_print/src/nodes/case_when.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ pub(super) fn emit_case_when(e: &mut EventEmitter, n: &CaseWhen) {

e.token(TokenKind::WHEN_KW);

// A condition that breaks, an AND chain for instance, continues one level deeper than WHEN, so
// that its operators don't read as siblings of WHEN.
if let Some(ref expr) = n.expr {
e.space();
e.indent_start();
super::emit_node(expr, e);
e.indent_end();
}

let expanded = matches!(e.config().layout, crate::Layout::Expanded);
Expand Down
7 changes: 2 additions & 5 deletions crates/pgls_pretty_print/src/nodes/common_table_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ pub(super) fn emit_common_table_expr(e: &mut EventEmitter, n: &CommonTableExpr)
emit_parenthesized_list(e, &n.aliascolnames, super::emit_node);
}

if matches!(e.config().layout, crate::Layout::Expanded) {
e.space();
} else {
e.line(LineType::SoftOrSpace);
}
// `name as (` stays on one line; a body that doesn't fit breaks after the parenthesis.
e.space();
e.token(TokenKind::AS_KW);
e.space();

Expand Down
4 changes: 1 addition & 3 deletions crates/pgls_pretty_print/src/nodes/constraint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,7 @@ pub(super) fn emit_constraint(e: &mut EventEmitter, n: &Constraint) {
e.space();
e.token(TokenKind::WHERE_KW);
e.space();
e.token(TokenKind::L_PAREN);
super::emit_clause_condition(e, where_clause);
e.token(TokenKind::R_PAREN);
super::emit_parenthesized_condition(e, GroupKind::Constraint, where_clause);
}

emit_deferrable(e, n, &mut continued);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ pub(super) fn emit_create_foreign_table_stmt(e: &mut EventEmitter, n: &CreateFor
e.token(TokenKind::L_PAREN);
if !base.table_elts.is_empty() {
e.indent_start();
e.line(crate::emitter::LineType::SoftOrSpace);
e.line(crate::emitter::LineType::Soft);
emit_comma_separated_list(e, &base.table_elts, super::emit_node);
e.indent_end();
e.line(crate::emitter::LineType::SoftOrSpace);
e.line(crate::emitter::LineType::Soft);
}
e.token(TokenKind::R_PAREN);
}
Expand Down
9 changes: 5 additions & 4 deletions crates/pgls_pretty_print/src/nodes/create_stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ fn emit_create_stmt_impl(e: &mut EventEmitter, n: &CreateStmt, with_semicolon: b
}

e.indent_end();
e.line(LineType::SoftOrSpace);
e.line(LineType::Soft);
e.token(TokenKind::R_PAREN);
}
} else {
Expand All @@ -182,10 +182,11 @@ fn emit_create_stmt_impl(e: &mut EventEmitter, n: &CreateStmt, with_semicolon: b
e.space();
e.group_start(GroupKind::List);
e.token(TokenKind::L_PAREN);
// Force break for tables with multiple columns for readability
// Force break for tables with multiple columns for readability. A table without columns
// keeps `()` together.
if multiple_items {
e.line(LineType::Hard);
} else {
} else if has_content {
e.line(LineType::Soft);
}

Expand Down Expand Up @@ -223,7 +224,7 @@ fn emit_create_stmt_impl(e: &mut EventEmitter, n: &CreateStmt, with_semicolon: b

if multiple_items {
e.line(LineType::Hard);
} else {
} else if has_content {
e.line(LineType::Soft);
}
e.token(TokenKind::R_PAREN);
Expand Down
33 changes: 9 additions & 24 deletions crates/pgls_pretty_print/src/nodes/join_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use pgls_query::protobuf::{JoinExpr, JoinType};
use crate::TokenKind;
use crate::emitter::{EventEmitter, GroupKind, LineType};

use super::node_list::{emit_comma_separated_list, emit_parenthesized_list};
use super::node_list::emit_parenthesized_list;
use super::string::emit_identifier_maybe_quoted;

pub(super) fn emit_join_expr(e: &mut EventEmitter, n: &JoinExpr) {
Expand Down Expand Up @@ -102,29 +102,14 @@ pub(super) fn emit_join_expr(e: &mut EventEmitter, n: &JoinExpr) {
e.line(LineType::SoftOrSpace);
e.token(TokenKind::USING_KW);
e.space();
e.token(TokenKind::L_PAREN);
if n.using_clause.len() > 1 {
e.indent_start();
e.line(LineType::SoftOrSpace);
emit_comma_separated_list(e, &n.using_clause, |node, e| {
// For USING clause, String nodes should be identifiers
if let Some(pgls_query::NodeEnum::String(s)) = node.node.as_ref() {
emit_identifier_maybe_quoted(e, &s.sval);
} else {
super::emit_node(node, e);
}
});
e.indent_end();
} else {
emit_comma_separated_list(e, &n.using_clause, |node, e| {
if let Some(pgls_query::NodeEnum::String(s)) = node.node.as_ref() {
emit_identifier_maybe_quoted(e, &s.sval);
} else {
super::emit_node(node, e);
}
});
}
e.token(TokenKind::R_PAREN);
emit_parenthesized_list(e, &n.using_clause, |node, e| {
// For USING clause, String nodes should be identifiers
if let Some(pgls_query::NodeEnum::String(s)) = node.node.as_ref() {
emit_identifier_maybe_quoted(e, &s.sval);
} else {
super::emit_node(node, e);
}
});

// Emit USING alias if present (PostgreSQL 14+)
// Format: USING (col1, col2) AS alias
Expand Down
8 changes: 4 additions & 4 deletions crates/pgls_pretty_print/src/nodes/json_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub(super) fn emit_json_table(e: &mut EventEmitter, n: &JsonTable) {
e.indent_start();

if let Some(context) = n.context_item.as_ref() {
e.line(LineType::SoftOrSpace);
e.line(LineType::Soft);
super::emit_json_value_expr(e, context);
e.token(TokenKind::COMMA);
}
Expand Down Expand Up @@ -60,7 +60,7 @@ pub(super) fn emit_json_table(e: &mut EventEmitter, n: &JsonTable) {
}
});
e.indent_end();
e.line(LineType::SoftOrSpace);
e.line(LineType::Soft);
e.token(TokenKind::R_PAREN);
}

Expand All @@ -70,7 +70,7 @@ pub(super) fn emit_json_table(e: &mut EventEmitter, n: &JsonTable) {
}

e.indent_end();
e.line(LineType::SoftOrSpace);
e.line(LineType::Soft);
e.token(TokenKind::R_PAREN);

if let Some(alias) = n.alias.as_ref() {
Expand Down Expand Up @@ -125,7 +125,7 @@ pub(super) fn emit_json_table_column(e: &mut EventEmitter, col: &JsonTableColumn
}
});
e.indent_end();
e.line(LineType::SoftOrSpace);
e.line(LineType::Soft);
e.token(TokenKind::R_PAREN);
}

Expand Down
9 changes: 7 additions & 2 deletions crates/pgls_pretty_print/src/nodes/node_list.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use pgls_query::Node;

use crate::emitter::{EventEmitter, GroupKind, LineType};
use crate::{CommaStyle, TokenKind};
use crate::{CommaStyle, Layout, TokenKind};

/// Controls the spacing behavior after separators in list helpers
#[derive(Clone, Copy, Default)]
Expand Down Expand Up @@ -130,7 +130,12 @@ pub(super) fn emit_comma_separated_list_with_layout_break<F>(
e.take_own_line_leading_comments_at(location);
}

super::emit_layout_break(e);
// As in `emit_comma_separated_list_with_spacing`, the break before a leading
// comma disappears when the list fits, so a single line reads "a, b".
match e.config().layout {
Layout::Expanded => e.line(LineType::Hard),
Layout::Fit => e.line(LineType::Soft),
}
e.token(TokenKind::COMMA);
e.space();
} else {
Expand Down
4 changes: 1 addition & 3 deletions crates/pgls_pretty_print/src/nodes/publication_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ pub(super) fn emit_publication_table(e: &mut EventEmitter, n: &PublicationTable)
e.line(LineType::SoftOrSpace);
e.token(TokenKind::WHERE_KW);
e.space();
e.token(TokenKind::L_PAREN);
super::emit_clause_condition(e, where_clause);
e.token(TokenKind::R_PAREN);
super::emit_parenthesized_condition(e, GroupKind::PublicationTable, where_clause);
}

e.group_end();
Expand Down
5 changes: 3 additions & 2 deletions crates/pgls_pretty_print/src/nodes/range_table_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ pub(super) fn emit_range_table_func(e: &mut EventEmitter, n: &RangeTableFunc) {
// XMLNAMESPACES clause - emitted before row expression
// Namespaces are stored as ResTarget nodes with name=prefix, val=URI
if !n.namespaces.is_empty() {
e.line(LineType::SoftOrSpace);
e.token(TokenKind::XMLNAMESPACES_KW);
emit_parenthesized_list(e, &n.namespaces, |node, emitter| {
// Each namespace is a ResTarget with val=URI, name=prefix
Expand Down Expand Up @@ -54,7 +53,9 @@ pub(super) fn emit_range_table_func(e: &mut EventEmitter, n: &RangeTableFunc) {
// Row expression (XPath for rows)
// Wrap complex expressions in parentheses to keep them together
if let Some(ref rowexpr) = n.rowexpr {
e.line(LineType::SoftOrSpace);
if !n.namespaces.is_empty() {
e.line(LineType::SoftOrSpace);
}
// Check if rowexpr is a complex expression that needs wrapping
let needs_parens = matches!(
rowexpr.node.as_ref(),
Expand Down
2 changes: 1 addition & 1 deletion crates/pgls_pretty_print/src/nodes/range_tbl_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub(super) fn emit_range_tbl_function(e: &mut EventEmitter, n: &RangeTblFunction
}

e.indent_end();
e.line(LineType::SoftOrSpace);
e.line(LineType::Soft);
e.token(TokenKind::R_PAREN);
}

Expand Down
Loading
Loading