use std::fmt;
pub(self) use super::tkn_tree::{self, walk::walk_tokens_non_ws, SyntaxNode, TomlKind};
mod block;
mod rules;
mod ws;
use block::Block;
use rules::{
indent_after_comma, indent_after_open_brace, lf_after_heading, lf_after_table, none_around_dot,
space_around_eq, space_lf_after_array_open, space_lf_after_comma,
space_lf_after_inline_table_open, space_lf_before_array_close,
space_lf_before_inline_table_close,
};
use ws::WhiteSpace;
type RuleFn = Box<dyn for<'a> Fn(&'a Block, &'a Block) -> Option<WhiteSpace>>;
pub struct Formatter {
blocks: Vec<Block>,
rules: Vec<(TomlKind, RuleFn)>,
formatted: String,
}
impl Formatter {
pub fn new(root: &SyntaxNode) -> Formatter {
Self {
blocks: walk_tokens_non_ws(root).map(Block::new).collect(),
rules: formatter(),
formatted: String::default(),
}
}
pub fn format(mut self) -> Self {
let zipped = self.blocks.iter().zip(self.blocks.iter().skip(1));
for (l_blk, r_blk) in zipped {
let rules = self
.rules
.iter()
.filter(|(kind, _)| *kind == l_blk.kind())
.chain(self.rules.iter().filter(|(kind, _)| *kind == r_blk.kind()))
.map(|(_, func)| func);
for rule in rules {
if let Some(fixed) = rule(l_blk, r_blk) {
r_blk.whitespace.set(fixed);
}
}
}
self.formatted = self
.blocks
.clone()
.into_iter()
.map(|b| b.to_string())
.collect();
if !self.formatted.ends_with('\n') {
self.formatted.push('\n')
}
self
}
}
impl fmt::Debug for Formatter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Formatter")
.field("blocks", &self.blocks)
.field(
"rules",
&self.rules.iter().map(|(k, _fn)| k).collect::<Vec<_>>(),
)
.field("formatted", &self.formatted)
.finish()
}
}
impl fmt::Display for Formatter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.formatted)
}
}
pub fn formatter() -> Vec<(TomlKind, RuleFn)> {
vec![
(TomlKind::OpenBrace, Box::new(lf_after_table) as RuleFn),
(TomlKind::CloseBrace, Box::new(lf_after_heading) as RuleFn),
(TomlKind::Dot, Box::new(none_around_dot) as RuleFn),
(TomlKind::Equal, Box::new(space_around_eq) as RuleFn),
(TomlKind::Comma, Box::new(space_lf_after_comma) as RuleFn),
(
TomlKind::OpenCurly,
Box::new(space_lf_after_inline_table_open) as RuleFn,
),
(
TomlKind::CloseCurly,
Box::new(space_lf_before_inline_table_close) as RuleFn,
),
(
TomlKind::OpenBrace,
Box::new(space_lf_after_array_open) as RuleFn,
),
(
TomlKind::CloseBrace,
Box::new(space_lf_before_array_close) as RuleFn,
),
(
TomlKind::OpenBrace,
Box::new(indent_after_open_brace) as RuleFn,
),
(TomlKind::Comma, Box::new(indent_after_comma) as RuleFn),
]
}