1use anyhow::Result;
2
3use crate::{att, AId, HtmlAllocator, Node, ToASlice, NBSP};
4
5use super::autolink;
6
7pub struct SoftPre {
10 pub tabs_to_nbsp: Option<u8>,
13
14 pub autolink: bool,
16
17 pub input_line_separator: &'static str,
19
20 pub trailing_br: bool,
22}
23
24impl Default for SoftPre {
25 fn default() -> Self {
26 Self {
27 tabs_to_nbsp: Some(8),
28 autolink: true,
29 input_line_separator: "\n",
30 trailing_br: true,
31 }
32 }
33}
34
35impl SoftPre {
36 pub fn format(&self, text: &str, html: &HtmlAllocator) -> Result<AId<Node>> {
37 let mut formatted_body = html.new_vec();
38 let mut iter = text.split(self.input_line_separator).peekable();
39 while let Some(line) = iter.next() {
40 let mut formatted_line = if self.autolink {
41 autolink(html, line)?
42 } else {
43 html.text(line)?.to_aslice(html)?
44 };
45
46 if let Some(n) = self.tabs_to_nbsp {
47 let mut items = html.new_vec();
48 for id in formatted_line.iter_aid(html) {
49 match html.get_node(id).expect("todo: when can this fail?") {
50 Node::String(s) => {
51 let mut s2 = String::new();
52 for c in s.chars() {
53 if c == '\t' {
54 for _ in 0..n {
55 s2.push_str(NBSP)
56 }
57 } else {
58 s2.push(c)
59 }
60 }
61 items.push(html.text(s2)?)?;
62 }
63 _ => items.push(id)?,
64 }
65 }
66 formatted_line = items.as_slice();
67 }
68
69 formatted_body.append(formatted_line)?;
75
76 if self.trailing_br || iter.peek().is_some() {
77 formatted_body.push(html.br([], [])?)?;
78 }
79 }
80 html.span([att("class", "soft_pre")], formatted_body)
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use crate::Print;
87
88 use super::*;
89
90 #[test]
91 fn t_softpre() -> Result<()> {
92 let softpre = SoftPre::default();
93 let html = HtmlAllocator::new(1000, std::sync::Arc::new(""));
94 let t = |s| -> String {
95 softpre
96 .format(s, &html)
97 .unwrap()
98 .to_html_fragment_string(&html)
99 .unwrap()
100 };
101 assert_eq!(t("foo bar"), "<span class=\"soft_pre\">foo bar<br></span>");
102 assert_eq!(t("foo bar\n\tbaz"), "<span class=\"soft_pre\">foo bar<br>\u{a0}\u{a0}\u{a0}\u{a0}\u{a0}\u{a0}\u{a0}\u{a0}baz<br></span>");
103 Ok(())
104 }
105}