use crate::markdown::line::{Line, LineType};
use crate::consts::*;
use crate::utils::into_v16;
enum ListOrder {
None, Num, AlphaLow, AlphaUpper, RomanLow, RomanUpper
}
impl ListOrder {
fn opening_tag(&self) -> Vec<u16> {
match self {
ListOrder::None => into_v16("<ul>"),
ListOrder::Num => into_v16("<ol type=\"1\">"),
ListOrder::AlphaLow => into_v16("<ol type=\"a\">"),
ListOrder::AlphaUpper => into_v16("<ol type=\"A\">"),
ListOrder::RomanLow => into_v16("<ol type=\"i\">"),
ListOrder::RomanUpper => into_v16("<ol type=\"I\">"),
}
}
fn closing_tag(&self) -> Vec<u16> {
match self {
ListOrder::None => into_v16("</ul>"),
_ => into_v16("</ol>"),
}
}
}
impl Line {
fn get_list_ordering(&self) -> ListOrder {
match self.line_type {
LineType::UnorderedList => ListOrder::None,
LineType::OrderedList => {
match self.content[0] {
U16_1 => ListOrder::Num,
U16_SMALL_A => ListOrder::AlphaLow,
U16_BIG_A => ListOrder::AlphaUpper,
U16_SMALL_I => ListOrder::RomanLow,
U16_BIG_I => ListOrder::RomanUpper,
_ => panic!()
}
}
_ => panic!()
}
}
fn get_list_content(&self) -> Vec<u16> {
match self.line_type {
LineType::UnorderedList => self.content[2..].to_vec(),
LineType::OrderedList => self.content[3..].to_vec(),
_ => panic!()
}
}
}
pub fn merge_list(lines: &Vec<Line>) -> Vec<Line> {
let mut current_list = vec![];
let mut result = Vec::with_capacity(lines.len());
for ln in lines.iter() {
match ln.line_type {
LineType::UnorderedList | LineType::OrderedList => {
current_list.push(ln.clone());
},
LineType::Paragraph => {
if current_list.len() > 0 {
current_list.push(ln.clone());
}
else {
result.push(ln.clone());
}
},
_ => {
if current_list.len() > 0 {
result.push(construct_list(current_list));
current_list = vec![];
}
result.push(ln.clone());
}
}
}
if current_list.len() > 0 {
result.push(construct_list(current_list));
}
result
}
fn construct_list(mut lines: Vec<Line>) -> Line {
lines = merge_paragraph_to_list(lines);
let (_, content) = construct_list_recursive(&lines, 0, lines[0].indent, lines[0].get_list_ordering());
Line {
line_type: LineType::UnorderedList,
content,
indent: 0
}
}
fn merge_paragraph_to_list(mut lines: Vec<Line>) -> Vec<Line> {
let mut result = Vec::with_capacity(lines.len());
for ind in 0..lines.len() {
if lines[ind].line_type == LineType::Paragraph {
continue;
}
if ind + 1 < lines.len() && lines[ind + 1].line_type == LineType::Paragraph {
lines[ind].content = vec![
lines[ind].content.clone(),
vec![U16_SPACE],
lines[ind + 1].content.clone()
].concat();
}
result.push(lines[ind].clone());
}
result
}
fn construct_list_recursive(lines: &Vec<Line>, begin_index: usize, curr_indent: usize, ordering: ListOrder) -> (usize, Vec<u16>) {
let mut result = vec![];
let mut index = begin_index;
result.push(ordering.opening_tag());
while index < lines.len() {
let ln = &lines[index];
if ln.indent < curr_indent {
result.push(ordering.closing_tag());
return (index, result.concat());
}
else if ln.indent > curr_indent {
result.pop(); let (next_index, content) = construct_list_recursive(lines, index, ln.indent, ln.get_list_ordering());
index = next_index;
result.push(content);
result.push(into_v16("</li>"));
continue;
}
else {
result.push(into_v16("<li>"));
result.push(render_task_list(ln.get_list_content()));
result.push(into_v16("</li>"));
}
index += 1;
}
result.push(ordering.closing_tag());
(lines.len(), result.concat())
}
fn render_task_list(line: Vec<u16>) -> Vec<u16> {
if is_task_list(&line) {
vec![
into_v16("<div class=\""),
if line[1] == U16_SPACE {
into_v16("unchecked_box\">")
} else {
into_v16("checked_box\"><span class=\"checkmark\"></span>")
},
into_v16("</div>"),
line[3..].to_vec()
].concat()
}
else {
line
}
}
fn is_task_list(line: &Vec<u16>) -> bool {
line.len() > 3 && line[0] == U16_LEFT_SQUARE_BRACKET && (line[1] == U16_SPACE || line[1] == U16_SMALL_X || line[1] == U16_BIG_X) && line[2] == U16_RIGHT_SQUARE_BRACKET && line[3] == U16_SPACE
}