use sql_dialect_fmt_syntax::{Dialect, SyntaxKind, SyntaxNode, SyntaxToken};
use SyntaxKind::*;
use crate::doc::{
break_parent, concat, empty, group, group_expanded, hard_line, indent, join, line, line_suffix,
soft_line, space, text, Doc,
};
mod comments;
mod options;
mod routine_body;
mod spacing;
use comments::{directive_comment_same_line_after_stmt, CommentInfo, Comments};
use options::{is_option_flag, is_option_key};
use routine_body::{
format_embedded_body_token, is_create_routine, is_routine_header_word, routine_body_language,
RoutineBodyLanguage,
};
use spacing::{is_value_end, must_separate_to_preserve_tokens, needs_space};
#[derive(Clone, Copy)]
pub(crate) struct Ctx {
pub uppercase_keywords: bool,
pub line_width: usize,
pub indent_width: usize,
pub dialect: Dialect,
}
pub(crate) fn lower_source(root: &SyntaxNode, ctx: Ctx) -> Doc {
let source = root.text().to_string();
let mut parts = Vec::new();
let mut emitted = false;
let mut last_stmt_end: Option<usize> = None;
for stmt in root.children() {
if emitted {
parts.push(hard_line());
parts.push(hard_line());
}
let lowered = lower_stmt(&stmt, ctx);
parts.push(lowered.body);
parts.push(text(";"));
for comment in lowered.end_comments {
if comment.is_line && comment.is_directive {
parts.push(space());
parts.push(text(comment.text));
} else {
parts.push(hard_line());
parts.push(text(comment.text));
}
}
emitted = true;
last_stmt_end = Some(stmt.text_range().end().into());
}
let mut need_break = emitted;
for token in root
.children_with_tokens()
.filter_map(|el| el.into_token())
.filter(|t| t.kind().is_comment())
{
if need_break {
if directive_comment_same_line_after_stmt(&source, last_stmt_end, &token) {
parts.push(space());
} else {
parts.push(hard_line());
}
}
parts.push(text(token.text().trim_end().to_string()));
need_break = true;
}
concat(parts)
}
fn lower_stmt(stmt: &SyntaxNode, ctx: Ctx) -> LoweredStmt {
let mut comments = Comments::build(stmt);
let end_comments = comments.take_statement_end_comments(stmt);
let mut low = Lowerer::new(ctx, comments);
let prefix = low.statement_leading(stmt);
let body = match stmt.kind() {
SELECT_STMT => low.lower_select(stmt),
_ => low.lower_node(stmt),
};
let body = if low.comments.all_placed() {
concat(vec![prefix, body])
} else {
trimmed_text(stmt)
};
LoweredStmt { body, end_comments }
}
struct LoweredStmt {
body: Doc,
end_comments: Vec<CommentInfo>,
}
struct Lowerer {
ctx: Ctx,
comments: Comments,
prev: Option<SyntaxKind>,
prev_unary: bool,
line_comment_pending: bool,
}
impl Lowerer {
fn new(ctx: Ctx, comments: Comments) -> Self {
Lowerer {
ctx,
comments,
prev: None,
prev_unary: false,
line_comment_pending: false,
}
}
fn sep_before(&self, cur: SyntaxKind) -> Doc {
match self.prev {
Some(prev) if must_separate_to_preserve_tokens(prev, cur) => space(),
Some(prev) if !self.prev_unary && needs_space(prev, cur) => space(),
_ => empty(),
}
}
fn advance(&mut self, kind: SyntaxKind) {
self.prev_unary =
matches!(kind, PLUS | MINUS) && self.prev.is_none_or(|p| !is_value_end(p));
self.prev = Some(kind);
}
fn reset(&mut self) {
self.prev = None;
self.prev_unary = false;
self.line_comment_pending = false;
}
fn resume_after(&mut self, kind: SyntaxKind) {
self.prev = Some(kind);
self.prev_unary = false;
}
fn statement_leading(&mut self, stmt: &SyntaxNode) -> Doc {
let first = stmt
.descendants_with_tokens()
.filter_map(|el| el.into_token())
.find(|t| !t.kind().is_trivia());
let Some(token) = first else {
return empty();
};
let mut parts = Vec::new();
for comment in self.comments.take_leading(&token) {
parts.push(text(comment.text));
parts.push(hard_line());
}
concat(parts)
}
fn token(&mut self, token: &SyntaxToken) -> Doc {
self.token_cased(token, false)
}
fn token_cased(&mut self, token: &SyntaxToken, force_keyword: bool) -> Doc {
self.token_rendered(token, keyword_text_forced(token, self.ctx, force_keyword))
}
fn token_rendered(&mut self, token: &SyntaxToken, rendered: Doc) -> Doc {
let leading = self.comments.take_leading(token);
let trailing = self.comments.take_trailing(token);
let mut parts = Vec::new();
if self.line_comment_pending {
parts.push(hard_line());
self.prev = None;
self.prev_unary = false;
self.line_comment_pending = false;
}
let has_leading = !leading.is_empty();
if has_leading && self.prev.is_some() {
parts.push(hard_line());
self.prev = None;
self.prev_unary = false;
}
for comment in leading {
parts.push(text(comment.text));
parts.push(hard_line());
}
let sep = if has_leading {
empty()
} else {
self.sep_before(token.kind())
};
self.advance(token.kind());
parts.push(sep);
parts.push(rendered);
for comment in trailing {
if comment.is_line {
parts.push(line_suffix(concat(vec![space(), text(comment.text)])));
parts.push(break_parent());
self.line_comment_pending = true;
} else {
parts.push(space());
parts.push(text(comment.text));
}
}
concat(parts)
}
fn synth_kw(&self, word: &str) -> Doc {
if self.ctx.uppercase_keywords {
text(word.to_ascii_uppercase())
} else {
text(word.to_ascii_lowercase())
}
}
fn lower_select(&mut self, select: &SyntaxNode) -> Doc {
let mut head = Vec::new();
let mut list = None;
let mut clauses = Vec::new();
for child in select.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
head.push(self.token(token));
} else if let Some(node) = child.into_node() {
if node.kind() == SELECT_LIST {
list = Some(node);
} else if is_select_clause(node.kind()) {
clauses.push(node);
}
}
}
let magic_comma = list.as_ref().is_some_and(has_trailing_comma);
let list_doc = list
.as_ref()
.map(|list| self.lower_select_list(list, magic_comma))
.unwrap_or_else(empty);
let inner = concat(vec![concat(head), indent(concat(vec![line(), list_doc]))]);
let header = if magic_comma {
group_expanded(inner)
} else {
group(inner)
};
let mut parts = vec![header];
for clause in clauses {
parts.push(hard_line());
self.reset(); parts.push(self.lower_clause(&clause));
}
concat(parts)
}
fn lower_select_list(&mut self, list: &SyntaxNode, trailing_comma: bool) -> Doc {
let items = self.lower_items(list.children().filter(|n| n.kind() == SELECT_ITEM));
let mut doc = join(item_sep(), items);
if trailing_comma {
doc = concat(vec![doc, text(",")]);
}
doc
}
fn lower_clausal(
&mut self,
node: &SyntaxNode,
break_token: impl Fn(SyntaxKind) -> bool,
block_node: impl Fn(SyntaxKind) -> bool,
) -> 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;
}
if break_token(token.kind()) {
parts.push(hard_line());
self.reset();
}
parts.push(self.token(token));
} else if let Some(node) = child.as_node() {
if block_node(node.kind()) {
parts.push(hard_line());
self.reset();
parts.push(self.lower_query(node));
} else {
parts.push(self.lower_node(node));
}
}
}
concat(parts)
}
fn lower_insert(&mut self, node: &SyntaxNode) -> Doc {
self.lower_clausal(
node,
|k| k == ELSE_KW,
|k| {
matches!(
k,
INSERT_WHEN
| INTO_CLAUSE
| VALUES_CLAUSE
| SELECT_STMT
| SET_OP
| SUBQUERY
| WITH_QUERY
)
},
)
}
fn lower_update(&mut self, node: &SyntaxNode) -> Doc {
self.lower_clausal(
node,
|_| false,
|k| matches!(k, SET_CLAUSE | FROM_CLAUSE | WHERE_CLAUSE),
)
}
fn lower_delete(&mut self, node: &SyntaxNode) -> Doc {
self.lower_clausal(node, |_| false, |k| k == WHERE_CLAUSE)
}
fn lower_copy(&mut self, node: &SyntaxNode) -> Doc {
self.lower_clausal(node, |k| k == FROM_KW, |k| k == COPY_OPTION)
}
fn lower_merge(&mut self, node: &SyntaxNode) -> Doc {
self.lower_clausal(node, |k| matches!(k, USING_KW | ON_KW), |k| k == MERGE_WHEN)
}
fn lower_optimize(&mut self, node: &SyntaxNode) -> Doc {
self.lower_clausal(
node,
|_| false,
|k| matches!(k, WHERE_CLAUSE | ZORDER_CLAUSE),
)
}
fn lower_cache(&mut self, node: &SyntaxNode) -> Doc {
self.lower_clausal(
node,
|_| false,
|k| {
matches!(
k,
SELECT_STMT | SET_OP | WITH_QUERY | SUBQUERY | VALUES_CLAUSE
)
},
)
}
fn lower_create(&mut self, node: &SyntaxNode) -> Doc {
if is_create_routine(node) {
return self.lower_create_routine(node);
}
let has_props = node.children().any(|c| {
matches!(
c.kind(),
OBJECT_PROPERTY | STREAM_SOURCE | TASK_AFTER | SEMANTIC_VIEW_CLAUSE
)
});
if has_props {
return self.lower_create_object(node);
}
self.lower_clausal(
node,
|_| false,
|k| {
matches!(
k,
SELECT_STMT | SET_OP | WITH_QUERY | SUBQUERY | VALUES_CLAUSE
)
},
)
}
fn lower_create_routine(&mut self, node: &SyntaxNode) -> Doc {
let body_language = routine_body_language(node).unwrap_or(RoutineBodyLanguage::Sql);
let mut parts = Vec::new();
let mut prev_sig = None;
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
if matches!(token.kind(), DOLLAR_STRING | STRING) && prev_sig == Some(AS_KW) {
if let Some(formatted) =
format_embedded_body_token(token.text(), body_language, self.ctx)
{
parts.push(self.token_rendered(token, text(formatted)));
prev_sig = Some(token.kind());
continue;
}
}
if token.kind() == L_PAREN && prev_sig == Some(TABLE_KW) {
parts.push(space());
}
parts.push(self.token_cased(token, is_routine_header_word(token)));
prev_sig = Some(token.kind());
} else if let Some(node) = child.as_node() {
if node.kind() == BLOCK_STMT {
parts.push(hard_line());
self.reset();
}
parts.push(self.lower_node(node));
prev_sig = None;
}
}
concat(parts)
}
fn lower_create_object(&mut self, node: &SyntaxNode) -> Doc {
let mut head = Vec::new();
let mut clauses = Vec::new();
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() || token.kind() == AS_KW {
continue; }
head.push(self.token(token));
} else if let Some(node) = child.into_node() {
match node.kind() {
NAME | NAME_REF | COLUMN_DEF_LIST => head.push(self.lower_node(&node)),
OBJECT_PROPERTY | STREAM_SOURCE | TASK_AFTER | SEMANTIC_VIEW_CLAUSE => {
self.reset();
clauses.push(concat(vec![hard_line(), self.lower_node(&node)]));
}
_ => {
self.reset();
clauses.push(concat(vec![
hard_line(),
self.synth_kw("AS"),
hard_line(),
self.lower_query(&node),
]));
}
}
}
}
concat(vec![concat(head), indent(concat(clauses))])
}
fn lower_grant(&mut self, node: &SyntaxNode) -> Doc {
let mut head = Vec::new();
let mut clauses = Vec::new();
let mut seen_clause = false;
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
if seen_clause {
clauses.push(self.token(token));
} else {
head.push(self.token(token));
}
} else if let Some(node) = child.into_node() {
match node.kind() {
PRIV_LIST => head.push(self.lower_node(&node)),
GRANT_TARGET | GRANTEE => {
seen_clause = true;
self.reset();
clauses.push(concat(vec![hard_line(), self.lower_node(&node)]));
}
_ => head.push(self.lower_node(&node)),
}
}
}
concat(vec![concat(head), indent(concat(clauses))])
}
fn lower_block_body(&mut self, list: &SyntaxNode) -> Doc {
let mut parts = Vec::new();
for stmt in list.children() {
parts.push(hard_line());
self.reset();
parts.push(self.lower_node(&stmt));
parts.push(text(";"));
}
indent(concat(parts))
}
fn lower_block(&mut self, node: &SyntaxNode) -> Doc {
let mut parts = Vec::new();
let mut started = false;
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
match token.kind() {
BEGIN_KW => {
if started {
parts.push(hard_line());
}
self.reset();
parts.push(self.token(token));
started = true;
}
END_KW => {
parts.push(hard_line());
self.reset();
parts.push(self.token(token));
}
_ => parts.push(self.token(token)),
}
} else if let Some(node) = child.as_node() {
match node.kind() {
DECLARE_SECTION => {
parts.push(self.lower_declare_section(node));
started = true;
}
STMT_LIST => parts.push(self.lower_block_body(node)),
EXCEPTION_SECTION => {
parts.push(hard_line());
self.reset();
parts.push(self.lower_exception_section(node));
}
_ => parts.push(self.lower_node(node)), }
}
}
concat(parts)
}
fn lower_declare_section(&mut self, node: &SyntaxNode) -> Doc {
let mut head = empty();
let mut body = Vec::new();
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
if token.kind() == DECLARE_KW {
self.reset();
head = self.token(token);
}
} else if let Some(node) = child.as_node() {
body.push(hard_line());
self.reset();
body.push(self.lower_node(node));
body.push(text(";"));
}
}
concat(vec![head, indent(concat(body))])
}
fn lower_if(&mut self, node: &SyntaxNode) -> Doc {
let mut parts = Vec::new();
self.reset();
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
match token.kind() {
ELSEIF_KW | ELSE_KW | END_KW => {
parts.push(hard_line());
self.reset();
parts.push(self.token(token));
}
_ => parts.push(self.token(token)), }
} else if let Some(node) = child.as_node() {
if node.kind() == STMT_LIST {
parts.push(self.lower_block_body(node));
} else {
parts.push(self.lower_node(node)); }
}
}
concat(parts)
}
fn lower_case_stmt(&mut self, node: &SyntaxNode) -> Doc {
let mut parts = Vec::new();
let mut first_case = true;
let mut pending_else = false;
self.reset();
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
match token.kind() {
CASE_KW if first_case => {
first_case = false;
parts.push(self.token(token));
}
ELSE_KW => {
self.reset();
parts.push(indent(concat(vec![hard_line(), self.token(token)])));
pending_else = true;
}
END_KW => {
parts.push(hard_line());
self.reset();
parts.push(self.token(token));
pending_else = false;
}
_ => parts.push(self.token(token)), }
} else if let Some(node) = child.as_node() {
match node.kind() {
CASE_STMT_WHEN => {
self.reset();
parts.push(indent(concat(vec![
hard_line(),
self.lower_case_stmt_when(node),
])));
pending_else = false;
}
STMT_LIST if pending_else => {
parts.push(indent(self.lower_block_body(node)));
pending_else = false;
}
_ => parts.push(self.lower_node(node)), }
}
}
concat(parts)
}
fn lower_case_stmt_when(&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));
} else if let Some(node) = child.as_node() {
if node.kind() == STMT_LIST {
parts.push(self.lower_block_body(node));
} else {
parts.push(self.lower_node(node));
}
}
}
concat(parts)
}
fn lower_loop(&mut self, node: &SyntaxNode) -> Doc {
let mut parts = Vec::new();
self.reset();
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
match token.kind() {
END_KW | UNTIL_KW => {
parts.push(hard_line());
self.reset();
parts.push(self.token(token));
}
_ => parts.push(self.token(token)),
}
} else if let Some(node) = child.as_node() {
if node.kind() == STMT_LIST {
parts.push(self.lower_block_body(node));
} else {
parts.push(self.lower_node(node)); }
}
}
concat(parts)
}
fn lower_exception_section(&mut self, node: &SyntaxNode) -> Doc {
let mut head = empty();
let mut body = Vec::new();
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
if token.kind() == EXCEPTION_KW {
self.reset();
head = self.token(token);
}
} else if let Some(node) = child.as_node() {
body.push(hard_line());
self.reset();
body.push(self.lower_exception_when(node));
}
}
concat(vec![head, indent(concat(body))])
}
fn lower_exception_when(&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));
} else if let Some(node) = child.as_node() {
if node.kind() == STMT_LIST {
parts.push(self.lower_block_body(node));
} else {
parts.push(self.lower_node(node));
}
}
}
concat(parts)
}
fn lower_stage_ref(&mut self, node: &SyntaxNode) -> Doc {
let sep = self.sep_before(AT);
self.resume_after(IDENT);
concat(vec![sep, trimmed_text(node)])
}
fn lower_copy_location(&mut self, node: &SyntaxNode) -> Doc {
let doc = concat(vec![space(), trimmed_text(node)]);
self.resume_after(IDENT);
doc
}
fn lower_column_def_list(&mut self, node: &SyntaxNode) -> Doc {
let trailing = paren_list_has_trailing_comma(node);
let defs: Vec<Doc> = node
.children()
.filter(|n| n.kind() == COLUMN_DEF)
.map(|def| {
self.reset();
self.lower_node(&def)
})
.collect();
self.resume_after(R_PAREN);
bracketed(empty(), defs, trailing)
}
fn lower_match_recognize(&mut self, node: &SyntaxNode) -> Doc {
let mut head = Vec::new();
let mut clauses = 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;
}
head.push(self.token(token));
} else if let Some(node) = child.as_node() {
self.reset();
clauses.push(concat(vec![hard_line(), self.lower_node(node)]));
}
}
self.resume_after(R_PAREN);
concat(vec![
concat(head),
space(),
text("("),
indent(concat(clauses)),
hard_line(),
text(")"),
])
}
fn lower_pattern_clause(&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)); } else if let Some(node) = child.as_node() {
parts.push(concat(vec![space(), trimmed_text(node)]));
self.resume_after(R_PAREN);
}
}
concat(parts)
}
fn lower_flow(&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() == FLOW_PIPE {
parts.push(hard_line());
parts.push(text("->>"));
parts.push(space());
}
} else if let Some(node) = child.as_node() {
self.reset();
parts.push(self.lower_query(node));
}
}
concat(parts)
}
fn lower_clause(&mut self, clause: &SyntaxNode) -> Doc {
match clause.kind() {
FROM_CLAUSE => self.lower_from(clause),
ORDER_BY_CLAUSE | GROUP_BY_CLAUSE => self.lower_keyword_item_list(clause),
_ => self.lower_node(clause),
}
}
fn lower_from(&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));
} else if let Some(node) = child.as_node() {
if matches!(node.kind(), JOIN | LATERAL_VIEW) {
parts.push(hard_line());
self.reset();
}
parts.push(self.lower_node(node));
}
}
concat(parts)
}
fn lower_keyword_item_list(&mut self, node: &SyntaxNode) -> Doc {
let mut head = Vec::new();
let mut items = Vec::new();
let mut seen_item = false;
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
if !seen_item {
head.push(self.token(token));
}
} else if let Some(node) = child.as_node() {
seen_item = true;
self.reset();
items.push(self.lower_node(node));
}
}
if items.is_empty() {
return concat(head);
}
let body = indent(concat(vec![line(), join(item_sep(), items)]));
group(concat(vec![concat(head), body]))
}
fn lower_node(&mut self, node: &SyntaxNode) -> Doc {
match node.kind() {
ARG_LIST | VALUES_ROW | COLUMN_LIST | LAMBDA_PARAMS => self.lower_paren_list(node),
IN_EXPR => self.lower_in_expr(node),
CASE_EXPR => self.lower_case(node),
SUBQUERY => self.lower_subquery(node),
WITH_QUERY => self.lower_with_query(node),
SET_OP => self.lower_set_op(node),
INSERT_STMT => self.lower_insert(node),
UPDATE_STMT => self.lower_update(node),
DELETE_STMT => self.lower_delete(node),
MERGE_STMT => self.lower_merge(node),
OPTIMIZE_STMT => self.lower_optimize(node),
CACHE_STMT => self.lower_cache(node),
VACUUM_STMT | UNCACHE_STMT | REFRESH_STMT | DESCRIBE_HISTORY_STMT => {
self.lower_children(node)
}
CREATE_STMT => self.lower_create(node),
GRANT_STMT | REVOKE_STMT => self.lower_grant(node),
COPY_STMT => self.lower_copy(node),
COPY_OPTION | OBJECT_PROPERTY => self.lower_option_node(node),
SEMANTIC_VIEW_CLAUSE => self.lower_semantic_view_clause(node),
COPY_LOCATION => self.lower_copy_location(node),
STAGE_REF => self.lower_stage_ref(node),
COLUMN_DEF_LIST => concat(vec![space(), self.lower_column_def_list(node)]),
MATCH_RECOGNIZE => self.lower_match_recognize(node),
PATTERN_CLAUSE => self.lower_pattern_clause(node),
FLOW_STMT => self.lower_flow(node),
BLOCK_STMT => self.lower_block(node),
IF_STMT => self.lower_if(node),
CASE_STMT => self.lower_case_stmt(node),
LOOP_STMT => self.lower_loop(node),
DECLARE_SECTION => self.lower_declare_section(node),
EXCEPTION_SECTION => self.lower_exception_section(node),
SET_CLAUSE | VALUES_CLAUSE => self.lower_keyword_item_list(node),
_ => self.lower_children(node),
}
}
fn lower_option_node(&mut self, node: &SyntaxNode) -> Doc {
let sig: Vec<SyntaxKind> = node
.children_with_tokens()
.filter_map(|el| el.into_token())
.filter(|t| !t.kind().is_trivia())
.map(|t| t.kind())
.collect();
let mut parts = Vec::new();
let mut sig_idx: usize = 0;
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
let prev = sig_idx.checked_sub(1).map(|i| sig[i]);
let next = sig.get(sig_idx + 1).copied();
let force = self.is_option_key_position(token, prev, next);
parts.push(self.token_cased(token, force));
sig_idx += 1;
} else if let Some(node) = child.as_node() {
parts.push(self.lower_node(node));
}
}
concat(parts)
}
fn is_option_key_position(
&self,
token: &SyntaxToken,
prev: Option<SyntaxKind>,
next: Option<SyntaxKind>,
) -> bool {
if !matches!(token.kind(), IDENT | CONTEXTUAL_KEYWORD) {
return false;
}
if prev == Some(EQ) {
return false;
}
let word = token.text();
if is_option_flag(word) {
return true;
}
matches!(next, Some(EQ | WITH_KW | BY_KW)) && is_option_key(word)
}
fn lower_semantic_view_clause(&mut self, node: &SyntaxNode) -> Doc {
let mut head = Vec::new();
let mut items = Vec::new();
let mut saw_parens = false;
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() || token.kind() == COMMA {
continue;
}
match token.kind() {
L_PAREN => saw_parens = true,
R_PAREN => {}
_ if !saw_parens => head.push(self.token(token)),
_ => head.push(self.token(token)),
}
} else if let Some(node) = child.as_node() {
if node.kind() == SEMANTIC_VIEW_ITEM {
self.reset();
items.push(self.lower_node(node));
} else {
head.push(self.lower_node(node));
}
}
}
if !saw_parens {
return concat(head);
}
self.resume_after(R_PAREN);
let body = if items.is_empty() {
text("()")
} else {
concat(vec![
text("("),
indent(concat(vec![
hard_line(),
join(concat(vec![text(","), hard_line()]), items),
])),
hard_line(),
text(")"),
])
};
concat(vec![concat(head), space(), body])
}
fn lower_children(&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));
} else if let Some(node) = child.as_node() {
parts.push(self.lower_node(node));
}
}
concat(parts)
}
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)])
}
fn lower_query(&mut self, node: &SyntaxNode) -> Doc {
match node.kind() {
SELECT_STMT => self.lower_select(node),
_ => self.lower_node(node),
}
}
fn lower_subquery(&mut self, node: &SyntaxNode) -> Doc {
let open_sep = self.sep_before(L_PAREN);
let inner = node.children().next();
self.reset();
let body = inner.map(|n| self.lower_query(&n)).unwrap_or_else(empty);
self.resume_after(R_PAREN);
let content = concat(vec![
text("("),
indent(concat(vec![soft_line(), body])),
soft_line(),
text(")"),
]);
concat(vec![open_sep, group(content)])
}
fn lower_set_op(&mut self, node: &SyntaxNode) -> Doc {
let mut operands = Vec::new();
let mut ops = Vec::new();
let mut op_started = false;
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() {
continue;
}
if !op_started {
self.reset();
op_started = true;
}
ops.push(self.token(token));
} else if let Some(node) = child.as_node() {
self.reset();
op_started = false;
operands.push(self.lower_query(node));
}
}
let mut operands = operands.into_iter();
let mut parts = Vec::new();
if let Some(lhs) = operands.next() {
parts.push(lhs);
}
parts.push(hard_line());
parts.push(concat(ops));
if let Some(rhs) = operands.next() {
parts.push(hard_line());
parts.push(rhs);
}
concat(parts)
}
fn lower_with_query(&mut self, node: &SyntaxNode) -> Doc {
let mut parts = Vec::new();
for child in node.children() {
if child.kind() == WITH_CLAUSE {
self.reset();
parts.push(self.lower_with_clause(&child));
} else {
parts.push(hard_line());
self.reset();
parts.push(self.lower_query(&child));
}
}
concat(parts)
}
fn lower_with_clause(&mut self, node: &SyntaxNode) -> Doc {
let mut head = Vec::new(); let mut ctes = Vec::new();
for child in node.children_with_tokens() {
if let Some(token) = child.as_token() {
if token.kind().is_trivia() || token.kind() == COMMA {
continue;
}
if ctes.is_empty() {
head.push(self.token(token));
}
} else if let Some(node) = child.as_node() {
self.reset();
ctes.push(self.lower_node(node));
}
}
concat(vec![
concat(head),
space(),
join(concat(vec![text(","), hard_line()]), ctes),
])
}
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,
]))
}
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)
}
fn lower_items(&mut self, nodes: impl Iterator<Item = SyntaxNode>) -> Vec<Doc> {
nodes
.map(|item| {
self.reset();
self.lower_node(&item)
})
.collect()
}
}
fn bracketed(prefix: Doc, items: Vec<Doc>, trailing: bool) -> Doc {
if items.is_empty() {
return concat(vec![text("("), prefix, text(")")]);
}
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("("),
prefix,
indent(body),
soft_line(),
text(")"),
]);
if trailing {
group_expanded(content)
} else {
group(content)
}
}
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)
}
fn is_select_clause(kind: SyntaxKind) -> bool {
matches!(
kind,
FROM_CLAUSE
| WHERE_CLAUSE
| GROUP_BY_CLAUSE
| HAVING_CLAUSE
| QUALIFY_CLAUSE
| ORDER_BY_CLAUSE
| LIMIT_CLAUSE
| OFFSET_CLAUSE
| START_WITH_CLAUSE
| CONNECT_BY_CLAUSE
)
}
fn item_sep() -> Doc {
concat(vec![text(","), line()])
}
fn paren_list_has_trailing_comma(node: &SyntaxNode) -> 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(R_PAREN) && prev == Some(COMMA)
}
fn keyword_text_forced(token: &SyntaxToken, ctx: Ctx, force_keyword: bool) -> Doc {
let is_keyword =
force_keyword || token.kind().is_keyword() || token.kind() == CONTEXTUAL_KEYWORD;
if ctx.uppercase_keywords && is_keyword {
text(token.text().to_ascii_uppercase())
} else {
text(token.text().to_string())
}
}
fn trimmed_text(node: &SyntaxNode) -> Doc {
let mut s = node.text().to_string();
s.truncate(s.trim_end().len());
let start = s.len() - s.trim_start().len();
if start > 0 {
s.drain(..start);
}
text(s)
}