use sql_dialect_fmt_syntax::{SyntaxKind, SyntaxNode};
use SyntaxKind::*;
use crate::doc::{
concat, empty, group, group_expanded, indent, join, line, soft_line, space, text, Doc,
};
use super::Lowerer;
impl Lowerer {
pub(super) fn lower_window_spec(&mut self, node: &SyntaxNode) -> Doc {
let open_sep = self.sep_before(L_PAREN);
let mut segments = Vec::new();
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() || matches!(token.kind(), L_PAREN | R_PAREN) {
continue;
}
self.reset();
segments.push(self.token(token));
} else if let Some(node) = child.as_node() {
self.reset();
segments.push(self.lower_node(node));
}
}
self.resume_after(R_PAREN);
concat(vec![open_sep, paren_grouped_segments(segments)])
}
pub(super) fn lower_logical_expr(&mut self, node: &SyntaxNode) -> Doc {
let Some(op) = logical_chain_operator(node) else {
return self.lower_children(node);
};
let mut operands = Vec::new();
self.collect_logical_operands(node, op, &mut operands);
if operands.len() < 2 {
return self.lower_children(node);
}
let mut operands = operands.into_iter();
let mut parts = vec![operands.next().expect("len checked")];
let op_doc = self.synth_kw(match op {
AND_KW => "AND",
OR_KW => "OR",
_ => unreachable!("logical_chain_operator only returns AND/OR"),
});
let mut tail = Vec::new();
for operand in operands {
tail.push(line());
tail.push(op_doc.clone());
tail.push(space());
tail.push(operand);
}
parts.push(indent(concat(tail)));
group(concat(parts))
}
fn collect_logical_operands(&mut self, node: &SyntaxNode, op: SyntaxKind, out: &mut Vec<Doc>) {
if node.kind() == BIN_EXPR && logical_chain_operator(node) == Some(op) {
let children: Vec<_> = node.children().collect();
if children.len() == 2 {
self.collect_logical_operands(&children[0], op, out);
self.reset();
self.collect_logical_operands(&children[1], op, out);
return;
}
}
if !out.is_empty() {
self.reset();
}
out.push(self.lower_node(node));
}
pub(super) fn lower_paren_list(&mut self, node: &SyntaxNode) -> Doc {
let open_sep = if node.kind() == COLUMN_LIST && self.prev.is_some() {
space()
} else {
self.sep_before(L_PAREN)
};
let trailing = paren_list_has_trailing_comma(node);
let quantifier = node
.children_with_tokens()
.filter_map(|el| el.into_token())
.find(|t| matches!(t.kind(), DISTINCT_KW | ALL_KW));
let prefix = if let Some(token) = quantifier {
self.reset();
concat(vec![self.token(&token), space()])
} else {
empty()
};
let items = self.lower_items(node.children());
self.resume_after(R_PAREN);
concat(vec![open_sep, bracketed(prefix, items, trailing)])
}
pub(super) fn lower_delimited_list(
&mut self,
node: &SyntaxNode,
open: SyntaxKind,
close: SyntaxKind,
open_text: &'static str,
close_text: &'static str,
) -> Doc {
let open_sep = self.sep_before(open);
let trailing = delimited_list_has_trailing_comma(node, close);
let items = self.lower_items(node.children());
self.resume_after(close);
concat(vec![
open_sep,
delimited(open_text, close_text, empty(), items, trailing),
])
}
pub(super) fn lower_object_field(&mut self, node: &SyntaxNode) -> Doc {
let mut parts = Vec::new();
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
parts.push(self.token(token));
if token.kind() == COLON {
parts.push(space());
self.reset();
}
} else if let Some(node) = child.as_node() {
parts.push(self.lower_node(node));
}
}
concat(parts)
}
pub(super) fn lower_bind_marker(&mut self, node: &SyntaxNode) -> Doc {
let mut parts = Vec::new();
let mut first_significant = true;
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
if first_significant && token.kind() == COLON {
let sep = self.sep_before(IDENT);
self.reset();
parts.push(sep);
}
parts.push(self.token(token));
first_significant = false;
} else if let Some(node) = child.as_node() {
parts.push(self.lower_node(node));
first_significant = false;
}
}
self.resume_after(R_PAREN);
concat(parts)
}
pub(super) fn lower_value_children(&mut self, node: &SyntaxNode) -> Doc {
let doc = self.lower_children(node);
self.resume_after(R_PAREN);
doc
}
pub(super) fn lower_case(&mut self, node: &SyntaxNode) -> Doc {
let mut head = Vec::new(); let mut arms = Vec::new(); let mut end = empty();
let mut else_kw: Option<Doc> = None;
let mut seen_arm = false;
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
match token.kind() {
k if k.is_trivia() => {}
CASE_KW => head.push(self.token(token)),
ELSE_KW => {
self.reset();
else_kw = Some(self.token(token));
}
END_KW => {
self.reset();
end = self.token(token);
}
_ => head.push(self.token(token)),
}
} else if let Some(node) = child.as_node() {
if node.kind() == CASE_WHEN {
seen_arm = true;
self.reset();
arms.push(self.lower_node(node));
} else if let Some(kw) = else_kw.take() {
self.reset();
arms.push(concat(vec![kw, space(), self.lower_node(node)]));
} else if !seen_arm {
self.reset();
head.push(space());
head.push(self.lower_node(node));
}
}
}
let mut body = Vec::new();
for arm in arms {
body.push(line());
body.push(arm);
}
self.prev = Some(END_KW);
self.prev_unary = false;
group(concat(vec![
concat(head),
indent(concat(body)),
line(),
end,
]))
}
pub(super) fn lower_in_expr(&mut self, node: &SyntaxNode) -> Doc {
let elems: Vec<_> = node
.children_with_tokens()
.filter(|el| !el.kind().is_trivia())
.collect();
let mut parts = Vec::new();
let mut i = 0;
while i < elems.len() {
if elems[i].kind() == L_PAREN {
if let Some(inner) = elems.get(i + 1).and_then(|e| e.as_node()) {
let open_sep = self.sep_before(L_PAREN);
if inner.kind() == EXPR_LIST {
let trailing = has_trailing_comma(inner);
let items = self.lower_items(inner.children());
self.resume_after(R_PAREN);
parts.push(concat(vec![open_sep, bracketed(empty(), items, trailing)]));
} else {
self.reset();
let body = self.lower_node(inner);
self.resume_after(R_PAREN);
parts.push(concat(vec![open_sep, text("("), body, text(")")]));
}
i += 2; if elems.get(i).map(|e| e.kind()) == Some(R_PAREN) {
i += 1; }
continue;
}
}
if let Some(token) = elems[i].as_token() {
parts.push(self.token(token));
} else if let Some(node) = elems[i].as_node() {
parts.push(self.lower_node(node));
}
i += 1;
}
concat(parts)
}
pub(super) fn lower_items(&mut self, nodes: impl Iterator<Item = SyntaxNode>) -> Vec<Doc> {
nodes
.map(|item| {
self.reset();
self.lower_node(&item)
})
.collect()
}
}
fn paren_grouped_segments(segments: Vec<Doc>) -> Doc {
if segments.is_empty() {
return text("()");
}
group(concat(vec![
text("("),
indent(concat(vec![soft_line(), join(line(), segments)])),
soft_line(),
text(")"),
]))
}
fn logical_chain_operator(node: &SyntaxNode) -> Option<SyntaxKind> {
node.children_with_tokens()
.filter_map(|el| el.into_token())
.filter(|token| !token.kind().is_trivia())
.map(|token| token.kind())
.find(|kind| matches!(kind, AND_KW | OR_KW))
}
pub(super) fn bracketed(prefix: Doc, items: Vec<Doc>, trailing: bool) -> Doc {
delimited("(", ")", prefix, items, trailing)
}
fn delimited(
open: &'static str,
close: &'static str,
prefix: Doc,
items: Vec<Doc>,
trailing: bool,
) -> Doc {
if items.is_empty() {
return concat(vec![text(open), prefix, text(close)]);
}
let joined = join(item_sep(), items);
let body = if trailing {
concat(vec![soft_line(), joined, text(",")])
} else {
concat(vec![soft_line(), joined])
};
let content = concat(vec![
text(open),
prefix,
indent(body),
soft_line(),
text(close),
]);
if trailing {
group_expanded(content)
} else {
group(content)
}
}
pub(super) fn has_trailing_comma(node: &SyntaxNode) -> bool {
node.children_with_tokens()
.filter(|el| !el.kind().is_trivia())
.last()
.is_some_and(|el| el.kind() == COMMA)
}
pub(super) fn item_sep() -> Doc {
concat(vec![text(","), line()])
}
pub(super) fn paren_list_has_trailing_comma(node: &SyntaxNode) -> bool {
delimited_list_has_trailing_comma(node, R_PAREN)
}
fn delimited_list_has_trailing_comma(node: &SyntaxNode, close: SyntaxKind) -> bool {
let mut prev = None;
let mut last = None;
for el in node.children_with_tokens() {
if !el.kind().is_trivia() {
prev = last;
last = Some(el.kind());
}
}
last == Some(close) && prev == Some(COMMA)
}