use rowan::{NodeOrToken, SyntaxElement, SyntaxToken};
use super::context::FormatContext;
use super::core::FormatError;
use super::ir::Ir;
use super::trivia::{is_trivia, split_lines};
use crate::syntax::{RLanguage, SyntaxKind, SyntaxNode};
type FormatExprElementFn =
fn(&SyntaxElement<RLanguage>, usize, FormatContext) -> Result<String, FormatError>;
type IrLineFn = fn(&[SyntaxElement<RLanguage>], usize, FormatContext) -> Result<Ir, FormatError>;
pub(super) fn block_statement_elements(
node: &SyntaxNode,
) -> Result<Vec<SyntaxElement<RLanguage>>, FormatError> {
let elements: Vec<_> = node.children_with_tokens().collect();
let open_idx = elements
.iter()
.position(|el| matches!(el, NodeOrToken::Token(tok) if tok.kind() == SyntaxKind::LBRACE))
.ok_or_else(|| FormatError::AmbiguousConstruct {
context: "missing '{' in block",
snippet: node.text().to_string(),
})?;
let close_idx = elements
.iter()
.rposition(|el| matches!(el, NodeOrToken::Token(tok) if tok.kind() == SyntaxKind::RBRACE))
.ok_or_else(|| FormatError::AmbiguousConstruct {
context: "missing '}' in block",
snippet: node.text().to_string(),
})?;
if close_idx <= open_idx {
return Err(FormatError::AmbiguousConstruct {
context: "invalid block bounds",
snippet: node.text().to_string(),
});
}
Ok(elements[open_idx + 1..close_idx].to_vec())
}
pub(super) fn ir_block_expr_with_prefixed_comments(
node: &SyntaxNode,
indent: usize,
ctx: FormatContext,
prefixed_comments: &[String],
ir_line: IrLineFn,
) -> Result<Ir, FormatError> {
let lines = split_lines(block_statement_elements(node)?, "block body")?;
let mut items: Vec<Ir> = Vec::new();
for comment in prefixed_comments {
items.push(Ir::text(comment.clone()));
}
for line in &lines {
items.push(ir_line(line, indent + 1, ctx)?);
}
if items.is_empty() {
return Ok(Ir::text("{}"));
}
let body = Ir::concat(
items
.into_iter()
.map(|it| Ir::concat([Ir::hard_line(), it])),
);
Ok(Ir::concat([
Ir::text("{"),
Ir::indent(body),
Ir::hard_line(),
Ir::text("}"),
]))
}
pub(super) fn format_expr_segment(
elements: &[SyntaxElement<RLanguage>],
context: &'static str,
indent: usize,
ctx: FormatContext,
format_expr_element: FormatExprElementFn,
) -> Result<String, FormatError> {
let significant: Vec<_> = elements
.iter()
.filter(|el| !is_trivia(el.kind()))
.cloned()
.collect();
if significant.len() != 1 {
return Err(FormatError::AmbiguousConstruct {
context,
snippet: snippet_from_elements(elements),
});
}
format_expr_element(&significant[0], indent, ctx)
}
pub(super) fn format_atom_token(token: &SyntaxToken<RLanguage>) -> Result<String, FormatError> {
match token.kind() {
SyntaxKind::IDENT
| SyntaxKind::INT
| SyntaxKind::FLOAT
| SyntaxKind::COMPLEX
| SyntaxKind::STRING
| SyntaxKind::BANG => Ok(token.text().to_string()),
kind => Err(FormatError::UnsupportedConstruct {
kind,
snippet: token.text().to_string(),
}),
}
}
pub(super) fn snippet_from_elements(elements: &[SyntaxElement<RLanguage>]) -> String {
elements
.iter()
.map(|el| match el {
NodeOrToken::Node(node) => node.text().to_string(),
NodeOrToken::Token(tok) => tok.text().to_string(),
})
.collect::<String>()
}