use crate::formatter::core::format_with_style;
use crate::formatter::style::{FormatStyle, LineEnding};
pub(super) fn render(
name: &str,
source: &str,
style: FormatStyle,
indent: &str,
) -> Option<Vec<String>> {
let budget = FormatStyle {
line_width: style.line_width.saturating_sub(indent.len()).max(1),
line_ending: LineEnding::Lf,
..style
};
let formatted = format_with_style(source, budget).ok()?;
let lines: Vec<String> = formatted
.trim_end_matches('\n')
.split('\n')
.map(str::to_string)
.collect();
if lines.is_empty() {
return None;
}
if lines.iter().any(|line| line.trim().is_empty()) {
return None;
}
if lines.len() == 1 && name.chars().count() + 2 + lines[0].chars().count() <= style.line_width {
return Some(vec![format!("{name}: {}", lines[0])]);
}
let mut out = Vec::with_capacity(lines.len() + 1);
out.push(format!("{name}:"));
out.extend(lines.into_iter().map(|line| format!("{indent}{line}")));
Some(out)
}
#[cfg(test)]
mod tests {
use super::*;
const INDENT: &str = " ";
fn style() -> FormatStyle {
FormatStyle::default()
}
#[test]
fn a_short_call_stays_inline() {
assert_eq!(
render("Roxygen", "list(markdown = TRUE)", style(), INDENT),
Some(vec!["Roxygen: list(markdown = TRUE)".to_string()])
);
}
#[test]
fn a_long_call_breaks_into_an_indented_block() {
let source = r#"c(person("Aaaaaaaaaa", "Bbbbbbbbbb", email = "aaaaaaaaaa@example.com", role = c("aut", "cre")), person("Cccccccccc", "Dddddddddd", role = "ctb"))"#;
let rendered = render("Authors@R", source, style(), INDENT).expect("formats");
assert_eq!(rendered[0], "Authors@R:");
assert!(rendered[1..].iter().all(|line| line.starts_with(INDENT)));
assert!(rendered.iter().all(|line| line.chars().count() <= 80));
}
#[test]
fn unparseable_r_declines_rather_than_erroring() {
assert_eq!(render("Authors@R", "person(\"Jo\",", style(), INDENT), None);
}
#[test]
fn a_value_that_would_emit_a_blank_line_declines() {
assert_eq!(render("Roxygen", "\"a\n\nb\"", style(), INDENT), None);
}
}