forked from mars/tml
185 lines
5.9 KiB
Rust
185 lines
5.9 KiB
Rust
use crate::style::Stylesheet;
|
|
use crate::tag::*;
|
|
|
|
impl TextTag {
|
|
pub fn layout<'a>(
|
|
&self,
|
|
style: &Stylesheet,
|
|
options: impl Into<textwrap::Options<'a>>,
|
|
) -> Vec<String> {
|
|
let styled = self.style(style);
|
|
textwrap::wrap(&styled, options)
|
|
.into_iter()
|
|
.map(|s| s.to_string())
|
|
.collect()
|
|
}
|
|
|
|
pub fn style_children(&self, style: &Stylesheet) -> String {
|
|
let mut string = String::new();
|
|
|
|
for child in self.children.iter() {
|
|
let child_string = match child {
|
|
TextNode::Tag(tag) => tag.style(style),
|
|
TextNode::Text(string) => string.to_owned(),
|
|
};
|
|
|
|
string.push_str(&child_string);
|
|
}
|
|
|
|
string
|
|
}
|
|
|
|
pub fn style(&self, style: &Stylesheet) -> String {
|
|
let string = self.style_children(style);
|
|
|
|
use TextTagKind::*;
|
|
let painter = match self.kind {
|
|
Plain => return string,
|
|
Underline => style.underline,
|
|
Bold => style.bold,
|
|
Italic => style.italic,
|
|
Strikethrough => style.strikethrough,
|
|
};
|
|
|
|
painter.paint(string).to_string()
|
|
}
|
|
}
|
|
|
|
impl BlockTag {
|
|
pub fn layout(&self, style: &Stylesheet, available_width: usize) -> Vec<String> {
|
|
use BlockTagKind::*;
|
|
match self.kind {
|
|
H1 | H2 | H3 => {
|
|
const INDENT: usize = 4;
|
|
|
|
let styled = self.style_text_children(style);
|
|
|
|
let bullets = match self.kind {
|
|
H1 => "# ",
|
|
H2 => "## ",
|
|
H3 => "### ",
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
let indent: String = std::iter::repeat(" ").take(INDENT).collect();
|
|
|
|
let options = textwrap::Options::new(available_width)
|
|
.initial_indent(&bullets)
|
|
.subsequent_indent(&indent);
|
|
|
|
let lines = textwrap::wrap(&styled, options);
|
|
let mut max_len = 0;
|
|
|
|
lines
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(index, line)| {
|
|
let mut line = line.to_string();
|
|
max_len = max_len.max(line.len());
|
|
|
|
let painter = if index == lines.len() - 1 {
|
|
while line.len() < max_len {
|
|
line.push(' ');
|
|
}
|
|
|
|
&style.header_style_floor
|
|
} else {
|
|
&style.header_style_float
|
|
};
|
|
|
|
painter.paint(line).to_string()
|
|
})
|
|
.collect()
|
|
}
|
|
P => self.layout_text_children(style, available_width),
|
|
Ul => self.layout_list_items(style, available_width, false),
|
|
Ol => self.layout_list_items(style, available_width, true),
|
|
Li | Tr | Td => panic!("{:?} tag cannot be directly laid out", self.kind),
|
|
_ => unimplemented!(),
|
|
}
|
|
}
|
|
|
|
pub fn layout_list_items(
|
|
&self,
|
|
style: &Stylesheet,
|
|
available_width: usize,
|
|
ordered: bool,
|
|
) -> Vec<String> {
|
|
let indent = style.list_indent;
|
|
let mut lines = Vec::new();
|
|
let mut index = 1;
|
|
let spacer: String = std::iter::repeat(" ").take(indent).collect();
|
|
|
|
let prefix = format!("{:>width$} ", style.ul_prefix, width = indent - 2);
|
|
let mut prefix = style.bullet.paint(prefix).to_string();
|
|
|
|
let available_width = if available_width > indent {
|
|
available_width - indent
|
|
} else {
|
|
0
|
|
};
|
|
|
|
for child in self.children.iter() {
|
|
if ordered {
|
|
prefix = format!("{:>width$}. ", index, width = indent - 2);
|
|
prefix = style.bullet.paint(prefix).to_string();
|
|
}
|
|
|
|
let options = textwrap::Options::new(available_width)
|
|
.initial_indent(&prefix)
|
|
.subsequent_indent(&spacer);
|
|
|
|
match child {
|
|
BlockNode::Text(tag) => {
|
|
index += 1;
|
|
let mut li = tag.layout(style, options);
|
|
lines.append(&mut li);
|
|
}
|
|
BlockNode::Block(tag) => {
|
|
let li = match tag.kind {
|
|
BlockTagKind::Ol => tag.layout_list_items(style, available_width, true),
|
|
BlockTagKind::Ul => tag.layout_list_items(style, available_width, false),
|
|
BlockTagKind::Li => {
|
|
index += 1;
|
|
let mut li = tag.layout_text_children(style, options);
|
|
lines.append(&mut li);
|
|
continue;
|
|
}
|
|
_ => panic!("Unexpected {:?} in {:?}", tag.kind, self.kind),
|
|
};
|
|
|
|
lines.extend(li.into_iter().map(|line| format!("{}{}", spacer, line)));
|
|
}
|
|
BlockNode::Leaf(_) => panic!("Unexpected leaf tag in {:?}", self.kind),
|
|
}
|
|
}
|
|
|
|
lines
|
|
}
|
|
|
|
pub fn layout_text_children<'a>(
|
|
&self,
|
|
style: &Stylesheet,
|
|
options: impl Into<textwrap::Options<'a>>,
|
|
) -> Vec<String> {
|
|
let styled = self.style_text_children(style);
|
|
textwrap::wrap(&styled, options)
|
|
.into_iter()
|
|
.map(|s| s.to_string())
|
|
.collect()
|
|
}
|
|
|
|
pub fn style_text_children(&self, style: &Stylesheet) -> String {
|
|
let mut string = String::new();
|
|
|
|
for child in self.children.iter() {
|
|
match child {
|
|
BlockNode::Text(tag) => string.push_str(&tag.style(style)),
|
|
_ => panic!("{:?} node attempted to style a non-text child", self.kind),
|
|
}
|
|
}
|
|
|
|
string
|
|
}
|
|
}
|