markdown_fmt/
paragraph.rs1use super::*;
2
3const MARKDOWN_HARD_BREAK: &str = " \n";
4
5pub trait ParagraphFormatter: Write {
7 fn new(max_width: Option<usize>, capacity: usize) -> Self;
10
11 fn is_empty(&self) -> bool;
13
14 fn into_buffer(self) -> String;
16}
17
18pub struct Paragraph {
20 buffer: String,
21 max_width: Option<usize>,
22}
23
24impl Write for Paragraph {
25 fn write_str(&mut self, s: &str) -> std::fmt::Result {
26 let is_hard_break = |s: &str| -> bool {
27 s.strip_prefix(" ")
29 .is_some_and(|maybe_hard_break| maybe_hard_break.trim_start_matches(' ').eq("\n"))
30 };
31
32 if self.max_width.is_some() && is_hard_break(s) {
33 self.buffer.push_str(MARKDOWN_HARD_BREAK);
34 return Ok(());
35 }
36
37 if self.max_width.is_some() && s.trim().is_empty() {
38 self.buffer.push(' ');
40 } else {
41 self.buffer.push_str(s);
42 }
43
44 Ok(())
45 }
46}
47
48impl ParagraphFormatter for Paragraph {
49 fn new(max_width: Option<usize>, capacity: usize) -> Self {
50 Self {
51 max_width,
52 buffer: String::with_capacity(capacity),
53 }
54 }
55
56 fn is_empty(&self) -> bool {
57 self.buffer.is_empty()
58 }
59
60 fn into_buffer(mut self) -> String {
61 let rewrite_buffer = std::mem::take(&mut self.buffer);
62
63 let Some(max_width) = self.max_width else {
64 return rewrite_buffer;
66 };
67
68 let all_lines_with_max_width = rewrite_buffer.lines().all(|l| l.len() <= max_width);
69
70 if all_lines_with_max_width {
71 return rewrite_buffer;
73 }
74
75 let mut output_buffer = String::with_capacity(rewrite_buffer.capacity());
76
77 let wrap_options = TextWrapOptions::new(max_width)
78 .break_words(false)
79 .word_separator(textwrap::WordSeparator::AsciiSpace)
80 .wrap_algorithm(textwrap::WrapAlgorithm::FirstFit);
81
82 let mut split_on_hard_breaks = rewrite_buffer.split(MARKDOWN_HARD_BREAK).peekable();
83
84 while let Some(text) = split_on_hard_breaks.next() {
85 let has_next = split_on_hard_breaks.peek().is_some();
86 let wrapped_text = textwrap::fill(text, wrap_options.clone());
87 output_buffer.push_str(&wrapped_text);
88 if has_next {
89 output_buffer.push_str(MARKDOWN_HARD_BREAK);
90 }
91 }
92
93 output_buffer
94 }
95}