use std::convert::Infallible;
use eure_tree::prelude::*;
use eure_tree::tree::{LineNumbers, TerminalData};
use crate::config::FormatConfig;
use crate::doc::Doc;
pub struct FormatBuilder<'a> {
input: &'a str,
#[allow(dead_code)] config: &'a FormatConfig,
line_numbers: LineNumbers<'a>,
}
impl<'a> FormatBuilder<'a> {
pub fn new(input: &'a str, _tree: &'a Cst, config: &'a FormatConfig) -> Self {
Self {
input,
config,
line_numbers: LineNumbers::new(input),
}
}
pub fn build(&self, tree: &Cst) -> Doc {
let mut visitor = FormatVisitor::new(self.input, &self.line_numbers);
let _ = tree.visit_from_root(&mut visitor);
visitor.finish()
}
}
struct FormatVisitor<'a> {
input: &'a str,
line_numbers: &'a LineNumbers<'a>,
docs: Vec<Doc>,
seen_newline: bool,
last_content_end: Option<u32>,
at_start: bool,
pending_newline: bool,
newline_count: u32,
context: Vec<FormatContext>,
object_needs_leading_space: bool,
object_open_doc_count: Option<usize>,
object_needs_comma_before_next: bool,
section_binding_content_start: Option<usize>,
}
#[derive(Clone, Copy, PartialEq)]
enum FormatContext {
Root,
Eure,
Binding,
ValueBinding,
SectionBinding,
SectionBody,
Object,
Section,
Keys,
}
impl<'a> FormatVisitor<'a> {
fn new(input: &'a str, line_numbers: &'a LineNumbers<'a>) -> Self {
Self {
input,
line_numbers,
docs: Vec::new(),
seen_newline: true, last_content_end: None,
at_start: true,
pending_newline: false,
newline_count: 0,
context: vec![FormatContext::Root],
object_needs_leading_space: false,
object_open_doc_count: None,
object_needs_comma_before_next: false,
section_binding_content_start: None,
}
}
fn finish(mut self) -> Doc {
if !self.docs.is_empty() {
self.docs.push(Doc::hardline());
}
Doc::concat_all(self.docs)
}
fn current_context(&self) -> FormatContext {
self.context.last().copied().unwrap_or(FormatContext::Root)
}
fn in_object(&self) -> bool {
self.current_context() == FormatContext::Object
}
fn in_section_binding(&self) -> bool {
self.current_context() == FormatContext::SectionBinding
}
fn in_section_body(&self) -> bool {
self.current_context() == FormatContext::SectionBody
}
fn in_block(&self) -> bool {
self.in_section_binding() || self.in_section_body()
}
fn push_context(&mut self, ctx: FormatContext) {
self.context.push(ctx);
}
fn pop_context(&mut self) {
self.context.pop();
}
fn get_text(&self, data: TerminalData) -> String {
match data {
TerminalData::Input(span) => {
self.input[span.start as usize..span.end as usize].to_string()
}
TerminalData::Dynamic(_) => {
String::new()
}
}
}
fn count_newlines(&self, text: &str) -> u32 {
let mut count = 0;
let mut chars = text.chars().peekable();
while let Some(ch) = chars.next() {
if ch == '\n' {
count += 1;
} else if ch == '\r' {
count += 1;
if matches!(chars.peek(), Some('\n')) {
chars.next();
}
}
}
count
}
fn emit_terminal(&mut self, data: TerminalData) {
let text = self.get_text(data);
if !text.is_empty() {
self.flush_newline();
self.emit_object_leading_space();
self.docs.push(Doc::text(text));
self.at_start = false;
}
self.mark_content(data);
}
fn get_span_end(&self, data: TerminalData) -> Option<u32> {
match data {
TerminalData::Input(span) => Some(span.end),
_ => None,
}
}
fn get_span_start(&self, data: TerminalData) -> Option<u32> {
match data {
TerminalData::Input(span) => Some(span.start),
_ => None,
}
}
fn same_line(&self, pos1: u32, pos2: u32) -> bool {
self.line_numbers.get_char_info(pos1).line_number
== self.line_numbers.get_char_info(pos2).line_number
}
fn request_newline(&mut self) {
if !self.at_start {
self.pending_newline = true;
}
}
fn has_pending_blank_line(&self) -> bool {
self.newline_count >= 2
}
fn reset_newline_count(&mut self) {
self.newline_count = 0;
}
fn flush_newline(&mut self) {
if self.pending_newline {
if self.has_pending_blank_line() {
self.docs.push(Doc::hardline());
}
self.docs.push(Doc::hardline());
self.pending_newline = false;
}
self.reset_newline_count();
}
fn emit_object_leading_space(&mut self) {
if self.object_needs_comma_before_next {
self.docs.push(Doc::text(", "));
self.object_needs_comma_before_next = false;
} else if self.object_needs_leading_space {
self.docs.push(Doc::text(" "));
self.object_needs_leading_space = false;
}
}
fn emit(&mut self, doc: Doc) {
if !matches!(doc, Doc::Nil) {
self.flush_newline();
self.emit_object_leading_space();
self.docs.push(doc);
self.at_start = false;
}
}
fn emit_text(&mut self, text: impl Into<String>) {
let text: String = text.into();
if !text.is_empty() {
self.flush_newline();
self.emit_object_leading_space();
self.docs.push(Doc::text(text));
self.at_start = false;
}
}
fn handle_comment(&mut self, data: TerminalData) {
let text = self.get_text(data);
let span_start = self.get_span_start(data);
let span_end = self.get_span_end(data);
let is_trailing = match (self.last_content_end, span_start) {
(Some(prev_end), Some(start)) => self.same_line(prev_end, start),
_ => false,
};
if is_trailing {
self.docs.push(Doc::text(" "));
self.emit_text(text);
} else {
self.emit_text(text);
self.request_newline();
}
self.last_content_end = span_end;
self.seen_newline = false;
}
fn mark_content(&mut self, data: TerminalData) {
self.last_content_end = self.get_span_end(data);
self.seen_newline = false;
self.at_start = false;
}
}
impl<F: CstFacade> CstVisitor<F> for FormatVisitor<'_> {
type Error = Infallible;
fn visit_new_line_terminal(
&mut self,
_terminal: NewLine,
_data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.seen_newline = true;
self.newline_count += 1;
Ok(())
}
fn visit_whitespace_terminal(
&mut self,
_terminal: Whitespace,
_data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
Ok(())
}
fn visit_line_comment_terminal(
&mut self,
_terminal: LineComment,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
let span_start = self.get_span_start(data);
let span_end = self.get_span_end(data);
let text = text.trim_end_matches(['\n', '\r']);
let is_trailing = match (self.last_content_end, span_start) {
(Some(prev_end), Some(start)) => self.same_line(prev_end, start),
_ => false,
};
if is_trailing {
self.docs.push(Doc::text(" "));
self.emit_text(text);
self.request_newline();
} else {
self.emit_text(text);
self.request_newline();
}
self.last_content_end = span_end;
self.seen_newline = true; Ok(())
}
fn visit_block_comment_terminal(
&mut self,
_terminal: BlockComment,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.handle_comment(data);
Ok(())
}
fn visit_eure(
&mut self,
handle: EureHandle,
view: EureView,
tree: &F,
) -> Result<(), Self::Error> {
self.push_context(FormatContext::Eure);
self.visit_eure_super(handle, view, tree)?;
self.pop_context();
Ok(())
}
fn visit_binding(
&mut self,
handle: BindingHandle,
view: BindingView,
tree: &F,
) -> Result<(), Self::Error> {
self.request_newline();
self.push_context(FormatContext::Binding);
self.visit_binding_super(handle, view, tree)?;
self.pop_context();
Ok(())
}
fn visit_value_binding(
&mut self,
handle: ValueBindingHandle,
view: ValueBindingView,
tree: &F,
) -> Result<(), Self::Error> {
self.push_context(FormatContext::ValueBinding);
self.visit_value_binding_super(handle, view, tree)?;
self.pop_context();
Ok(())
}
fn visit_section(
&mut self,
handle: SectionHandle,
view: SectionView,
tree: &F,
) -> Result<(), Self::Error> {
self.request_newline();
self.push_context(FormatContext::Section);
self.visit_section_super(handle, view, tree)?;
self.pop_context();
Ok(())
}
fn visit_keys(
&mut self,
handle: KeysHandle,
view: KeysView,
tree: &F,
) -> Result<(), Self::Error> {
self.push_context(FormatContext::Keys);
self.visit_keys_super(handle, view, tree)?;
self.pop_context();
Ok(())
}
fn visit_object(
&mut self,
handle: ObjectHandle,
view: ObjectView,
tree: &F,
) -> Result<(), Self::Error> {
self.push_context(FormatContext::Object);
self.visit_object_super(handle, view, tree)?;
self.pop_context();
Ok(())
}
fn visit_section_binding(
&mut self,
handle: SectionBindingHandle,
view: SectionBindingView,
tree: &F,
) -> Result<(), Self::Error> {
self.push_context(FormatContext::SectionBinding);
self.visit_section_binding_super(handle, view, tree)?;
self.pop_context();
Ok(())
}
fn visit_section_body(
&mut self,
handle: SectionBodyHandle,
view: SectionBodyView,
tree: &F,
) -> Result<(), Self::Error> {
self.push_context(FormatContext::SectionBody);
self.visit_section_body_super(handle, view, tree)?;
self.pop_context();
Ok(())
}
fn visit_newline_head(
&mut self,
_handle: NewlineHeadHandle,
view: NewlineHeadView,
tree: &F,
) -> Result<(), Self::Error> {
self.seen_newline = true;
self.newline_count += 1;
self.visit_newline_head_opt_handle(view.newline_head_opt, tree)?;
Ok(())
}
fn visit_flat_root_binding(
&mut self,
handle: FlatRootBindingHandle,
view: FlatRootBindingView,
tree: &F,
) -> Result<(), Self::Error> {
self.request_newline();
self.at_start = true;
self.visit_flat_root_binding_super(handle, view, tree)?;
Ok(())
}
fn visit_bind_terminal(
&mut self,
_terminal: Bind,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
if self.at_start {
self.emit_text("= ");
} else {
self.emit_text(" = ");
}
self.mark_content(data);
Ok(())
}
fn visit_newline_bind_terminal(
&mut self,
_terminal: NewlineBind,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
let newlines = self.count_newlines(&text);
if newlines > 0 {
self.seen_newline = true;
self.newline_count += newlines;
self.request_newline();
}
let bind_text = if newlines > 0 { "= " } else { " = " };
self.emit_text(bind_text);
self.mark_content(data);
Ok(())
}
fn visit_at_terminal(
&mut self,
_terminal: At,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_text("@ ");
self.mark_content(data);
Ok(())
}
fn visit_dot_terminal(
&mut self,
_terminal: Dot,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_text(".");
self.mark_content(data);
Ok(())
}
fn visit_l_brace_terminal(
&mut self,
_terminal: LBrace,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
if self.in_block() {
self.emit_text(" {");
self.mark_content(data);
self.section_binding_content_start = Some(self.docs.len());
self.at_start = true;
self.pending_newline = false;
} else if self.in_object() {
self.emit_text("{");
self.object_needs_leading_space = true;
self.object_open_doc_count = Some(self.docs.len());
self.mark_content(data);
} else {
self.emit_text("{");
self.mark_content(data);
}
Ok(())
}
fn visit_r_brace_terminal(
&mut self,
_terminal: RBrace,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
if self.in_block() {
if let Some(start_idx) = self.section_binding_content_start.take() {
let content_docs: Vec<Doc> = self.docs.drain(start_idx..).collect();
if !content_docs.is_empty() {
let content = Doc::concat_all(content_docs);
self.docs.push(Doc::indent(Doc::hardline().concat(content)));
self.docs.push(Doc::hardline());
}
}
self.emit_text("}");
} else if self.in_object() {
let has_content = self
.object_open_doc_count
.map(|count| self.docs.len() > count)
.unwrap_or(false);
self.object_needs_leading_space = false;
self.object_open_doc_count = None;
self.object_needs_comma_before_next = false;
if has_content {
self.emit_text(" }");
} else {
self.emit_text("}");
}
} else {
self.emit_text("}");
}
self.mark_content(data);
Ok(())
}
fn visit_l_bracket_terminal(
&mut self,
_terminal: LBracket,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_text("[");
self.mark_content(data);
Ok(())
}
fn visit_r_bracket_terminal(
&mut self,
_terminal: RBracket,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_text("]");
self.mark_content(data);
Ok(())
}
fn visit_l_paren_terminal(
&mut self,
_terminal: LParen,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_text("(");
self.mark_content(data);
Ok(())
}
fn visit_r_paren_terminal(
&mut self,
_terminal: RParen,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_text(")");
self.mark_content(data);
Ok(())
}
fn visit_comma_terminal(
&mut self,
_terminal: Comma,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
if self.in_object() {
self.object_needs_comma_before_next = true;
} else {
self.emit_text(", ");
}
self.mark_content(data);
Ok(())
}
fn visit_map_bind_terminal(
&mut self,
_terminal: MapBind,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_text(" => ");
self.mark_content(data);
Ok(())
}
fn visit_ident_terminal(
&mut self,
_terminal: Ident,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_terminal(data);
Ok(())
}
fn visit_integer_terminal(
&mut self,
_terminal: Integer,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_terminal(data);
Ok(())
}
fn visit_float_terminal(
&mut self,
_terminal: Float,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_terminal(data);
Ok(())
}
fn visit_true_terminal(
&mut self,
_terminal: True,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_text("true");
self.mark_content(data);
Ok(())
}
fn visit_false_terminal(
&mut self,
_terminal: False,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_text("false");
self.mark_content(data);
Ok(())
}
fn visit_null_terminal(
&mut self,
_terminal: Null,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_text("null");
self.mark_content(data);
Ok(())
}
fn visit_hole_terminal(
&mut self,
_terminal: Hole,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_terminal(data);
Ok(())
}
fn visit_str_terminal(
&mut self,
_terminal: Str,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_terminal(data);
Ok(())
}
fn visit_hash_terminal(
&mut self,
_terminal: Hash,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_text("#");
self.mark_content(data);
Ok(())
}
fn visit_esc_terminal(
&mut self,
_terminal: Esc,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_text(" \\");
self.emit(Doc::hardline());
self.mark_content(data);
Ok(())
}
fn visit_code_block_start_3_terminal(
&mut self,
_terminal: CodeBlockStart3,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_code_block_start_4_terminal(
&mut self,
_terminal: CodeBlockStart4,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_code_block_start_5_terminal(
&mut self,
_terminal: CodeBlockStart5,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_code_block_start_6_terminal(
&mut self,
_terminal: CodeBlockStart6,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_code_block_end_3_terminal(
&mut self,
_terminal: CodeBlockEnd3,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_code_block_end_4_terminal(
&mut self,
_terminal: CodeBlockEnd4,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_code_block_end_5_terminal(
&mut self,
_terminal: CodeBlockEnd5,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_code_block_end_6_terminal(
&mut self,
_terminal: CodeBlockEnd6,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_no_backtick_terminal(
&mut self,
_terminal: NoBacktick,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_backtick_2_terminal(
&mut self,
_terminal: Backtick2,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_backtick_3_terminal(
&mut self,
_terminal: Backtick3,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_backtick_4_terminal(
&mut self,
_terminal: Backtick4,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_backtick_5_terminal(
&mut self,
_terminal: Backtick5,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_inline_code_1_terminal(
&mut self,
_terminal: InlineCode1,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_lit_str_terminal(
&mut self,
_terminal: LitStr,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_lit_str_1_start_terminal(
&mut self,
_terminal: LitStr1Start,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_lit_str_2_start_terminal(
&mut self,
_terminal: LitStr2Start,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_lit_str_3_start_terminal(
&mut self,
_terminal: LitStr3Start,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_lit_str_1_end_terminal(
&mut self,
_terminal: LitStr1End,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_lit_str_2_end_terminal(
&mut self,
_terminal: LitStr2End,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_lit_str_3_end_terminal(
&mut self,
_terminal: LitStr3End,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_s_quote_terminal(
&mut self,
_terminal: SQuote,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_no_s_quote_terminal(
&mut self,
_terminal: NoSQuote,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_delim_code_start_1_terminal(
&mut self,
_terminal: DelimCodeStart1,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_delim_code_start_2_terminal(
&mut self,
_terminal: DelimCodeStart2,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_delim_code_start_3_terminal(
&mut self,
_terminal: DelimCodeStart3,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_delim_code_end_1_terminal(
&mut self,
_terminal: DelimCodeEnd1,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_delim_code_end_2_terminal(
&mut self,
_terminal: DelimCodeEnd2,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_delim_code_end_3_terminal(
&mut self,
_terminal: DelimCodeEnd3,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_backtick_delim_terminal(
&mut self,
_terminal: BacktickDelim,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_text_start_terminal(
&mut self,
_terminal: TextStart,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit_text(":");
self.mark_content(data);
Ok(())
}
fn visit_newline_text_start_terminal(
&mut self,
_terminal: NewlineTextStart,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
let newlines = self.count_newlines(&text);
if newlines > 0 {
self.seen_newline = true;
self.newline_count += newlines;
self.request_newline();
}
self.emit_text(":");
self.mark_content(data);
Ok(())
}
fn visit_text_terminal(
&mut self,
_terminal: Text,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
fn visit_grammar_newline_terminal(
&mut self,
_terminal: GrammarNewline,
_data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
self.emit(Doc::hardline());
self.seen_newline = true;
Ok(())
}
fn visit_ws_terminal(
&mut self,
_terminal: Ws,
data: TerminalData,
_tree: &F,
) -> Result<(), Self::Error> {
let text = self.get_text(data);
self.emit_text(text);
self.mark_content(data);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn format(input: &str) -> String {
format_width(input, 100)
}
fn format_width(input: &str, width: usize) -> String {
let cst = eure_parol::parse(input).expect("parse failed");
let config = FormatConfig::default().with_max_width(width);
let builder = FormatBuilder::new(input, &cst, &config);
let doc = builder.build(&cst);
crate::printer::Printer::new(config).print(&doc)
}
#[test]
fn test_simple_binding() {
let input = "a = 1";
let output = format(input);
assert_eq!(output, "a = 1\n");
}
#[test]
fn test_nested_binding() {
let input = "a.b = 1";
let output = format(input);
assert_eq!(output, "a.b = 1\n");
}
#[test]
fn test_array_binding() {
let input = "= [1, 2, 3]";
let output = format(input);
assert_eq!(output, "= [1, 2, 3]\n");
}
#[test]
fn test_object_binding() {
let input = "= {b => 1}";
let output = format(input);
assert_eq!(output, "= { b => 1 }\n");
}
#[test]
fn test_section() {
let input = "@ foo\na = 1";
let output = format(input);
assert_eq!(output, "@ foo\na = 1\n");
}
#[test]
fn test_section_binding_block() {
let input = "a { b = 1 }";
let output = format(input);
assert_eq!(output, "a {\n b = 1\n}\n");
}
#[test]
fn test_section_with_block() {
let input = "@ a { b = 1 }";
let output = format(input);
assert_eq!(output, "@ a {\n b = 1\n}\n");
}
#[test]
fn test_empty_array() {
let input = "= []";
let output = format(input);
assert_eq!(output, "= []\n");
}
#[test]
fn test_empty_object() {
let input = "= {}";
let output = format(input);
assert_eq!(output, "= {}\n");
}
#[test]
fn test_object_inline_with_commas() {
let input = "= {a => 1, b => 2}";
let output = format(input);
assert_eq!(output, "= { a => 1, b => 2 }\n");
}
#[test]
fn test_object_with_trailing_comma() {
let input = "= { a => 1, b => 2, }";
let output = format(input);
assert_eq!(output, "= { a => 1, b => 2 }\n");
}
#[test]
fn test_object_with_array_value_trailing_comma() {
let input = "= {\n name => \"Bob\",\n items => [1, 2, 3],\n}";
let output = format(input);
assert_eq!(output, "= { name => \"Bob\", items => [1, 2, 3] }\n");
}
#[test]
fn test_line_comment_standalone() {
let input = "// this is a comment\na = 1";
let output = format(input);
assert_eq!(output, "// this is a comment\na = 1\n");
}
#[test]
fn test_line_comment_trailing() {
let input = "a = 1 // trailing comment";
let output = format(input);
assert_eq!(output, "a = 1 // trailing comment\n");
}
#[test]
fn test_block_comment() {
let input = "/* block comment */\na = 1";
let output = format(input);
assert_eq!(output, "/* block comment */\na = 1\n");
}
#[test]
fn test_comment_between_bindings() {
let input = "a = 1\n// comment\nb = 2";
let output = format(input);
assert_eq!(output, "a = 1\n// comment\nb = 2\n");
}
}