use std::{any::Any, cell::Ref, ops::Range, rc::Rc}; use crate::{compiler::compiler::{Compiler, Target}, document::{document::Document, element::{ElemKind, Element}}, parser::{parser::Parser, rule::Rule, source::{Cursor, Source, Token, VirtualSource}}}; use ariadne::{Label, Report, ReportKind}; use regex::Regex; use super::paragraph::Paragraph; #[derive(Debug)] pub struct ListEntry { location: Token, numbering: Vec<(bool, usize)>, content: Vec>, // TODO bullet_maker : FnMut<...> } impl ListEntry { pub fn new(location: Token, numbering: Vec<(bool, usize)>, content: Vec>) -> Self { Self { location, numbering, content } } } #[derive(Debug)] pub struct List { location: Token, entries: Vec } impl List { pub fn new(location: Token) -> Self { Self { location, entries: Vec::new() } } pub fn push(&mut self, entry: ListEntry) { self.location.range = self.location.start()..entry.location.end(); self.entries.push(entry); } } impl Element for List { fn location(&self) -> &Token { &self.location } fn kind(&self) -> ElemKind { ElemKind::Block } fn element_name(&self) -> &'static str { "List" } fn to_string(&self) -> String { format!("{self:#?}") } fn compile(&self, compiler: &Compiler, document: &Document) -> Result { match compiler.target() { Target::HTML => { let mut result = String::new(); //TODO: Do something about indexing let mut current_list: Vec = vec![]; let mut match_stack = |result: &mut String, target: &Vec<(bool, usize)>| { // Find index after which current_list and target differ let mut match_idx = 0usize; for i in 0..current_list.len() { if i >= target.len() || current_list[i] != target[i].0 { break } else { match_idx = i+1; } } // Close until same match for _ in match_idx..current_list.len() { result.push_str(["", ""][current_list.pop().unwrap() as usize]); } // Open for i in match_idx..target.len() { result.push_str(["
    ", "
      "][target[i].0 as usize]); current_list.push(target[i].0); } }; match self.entries.iter() .try_for_each(|ent| { match_stack(&mut result, &ent.numbering); result.push_str("
    1. "); match ent.content.iter().enumerate() .try_for_each(|(idx, elem)| { match elem.compile(compiler, document) { Err(e) => Err(e), Ok(s) => { result.push_str(s.as_str()); Ok(()) } } }) { Err(e) => Err(e), _ => { result.push_str("
    2. "); Ok(()) } } }) { Err(e) => return Err(e), _ => {} } match_stack(&mut result, &Vec::<(bool, usize)>::new()); Ok(result) } Target::LATEX => Err("Unimplemented compiler".to_string()) } } } /* impl Element for ListEntry { fn location(&self) -> &Token { &self.location } fn kind(&self) -> ElemKind { ElemKind::Inline } fn element_name(&self) -> &'static str { "List" } fn to_string(&self) -> String { format!("{self:#?}") } fn compile(&self, compiler: &Compiler) -> Result { lazy_static! { static ref STATE_NAME : &'static str = "list.state"; static ref LIST_OPEN : [&'static str; 2] = ["
        ", "
          "]; static ref LIST_CLOSE : [&'static str; 2] = ["
      ", "
    "]; } // TODO: State.shouldpreserve? // Called upon every element //let state = compiler.get_state_mut::(*STATE_NAME) //.or_else(|| { // compiler.insert_state(STATE_NAME.to_string(), Box::new(ListState(Vec::new())) as Box); // compiler.get_state_mut::(*STATE_NAME) //}).unwrap(); match compiler.target() { Target::HTML => { let mut result = String::new(); //TODO: Do something about indexing //&self.numbering.iter() // .zip(&state.0) // .for_each(|((wants_numbered, _), is_numbered)| // { // // }); result.push_str("
  • "); match self.content.iter() .try_for_each(|ent| match ent.compile(compiler) { Err(e) => Err(e), Ok(s) => Ok(result.push_str(s.as_str())), }) { Err(e) => return Err(e), _ => {} } result.push_str("
  • "); //result.push_str(LIST_OPEN[self.numbered as usize]); //self.entries.iter() // .for_each(|(_index, entry)| // result.push_str(format!("
  • {}
  • ", compiler.compile(entry)).as_str())); //result.push_str(LIST_CLOSE[self.numbered as usize]); Ok(result) } Target::LATEX => Err("Unimplemented compiler".to_string()) } } } */ pub struct ListRule { start_re: Regex, continue_re: Regex } impl ListRule { pub fn new() -> Self { Self { start_re: Regex::new(r"(?:^|\n)(?:[^\S\r\n]+)([*-]+).*").unwrap(), continue_re: Regex::new(r"(?:^|\n)([^\S\r\n]+).*").unwrap(), } } fn parse_depth(depth: &str, document: &Document) -> Vec<(bool, usize)> { let mut parsed = vec![]; let prev_entry = document.last_element::(true) .and_then(|list| Ref::filter_map(list, |m| m.entries.last() ).ok() ) .and_then(|entry| Ref::filter_map(entry, |e| Some(&e.numbering)).ok() ); let mut continue_match = true; depth.chars().enumerate().for_each(|(idx, c)| { let number = prev_entry.as_ref() .and_then(|v| { if !continue_match { return None } let numbered = c == '-'; match v.get(idx) { None => None, Some((prev_numbered, prev_idx)) => { if *prev_numbered != numbered { continue_match = false; None } // New depth else if idx+1 == v.len() { Some(prev_idx+1) } // Increase from previous else { Some(*prev_idx) } // Do nothing } } }) .or(Some(0usize)) .unwrap(); match c { '*' => parsed.push((false, number)), '-' => parsed.push((true, number)), _ => panic!("Unimplemented") } }); return parsed; } } impl Rule for ListRule { fn name(&self) -> &'static str { "List" } fn next_match(&self, cursor: &Cursor) -> Option<(usize, Box)> { self.start_re.find_at(cursor.source.content(), cursor.pos) .map_or(None, |m| Some((m.start(), Box::new([false;0]) as Box)) ) } fn on_match<'a>(&self, parser: &dyn Parser, document: &'a Document<'a>, cursor: Cursor, _match_data: Option>) -> (Cursor, Vec, Range)>>) { let mut reports = vec![]; let content = cursor.source.content(); let (end_cursor, numbering, source) = match self.start_re.captures_at(content, cursor.pos) { None => panic!("Unknown error"), Some(caps) => { let mut end_pos = caps.get(0).unwrap().end(); let mut spacing = None; // Spacing used to continue list entry loop { // If another entry starts on the next line, don't continue matching match self.next_match(&cursor.at(end_pos)) { Some((pos, _)) => { if pos == end_pos { break } } None => {}, } // Continue matching as current entry match self.continue_re.captures_at(content, end_pos) { None => break, Some(continue_caps) => { if continue_caps.get(0).unwrap().start() != end_pos { break } // Get the spacing let cap_spacing = continue_caps.get(1).unwrap(); match &spacing { None => spacing = Some(cap_spacing.range()), Some(spacing) => 'some: { if content[cap_spacing.range()] == content[spacing.clone()] { break 'some } reports.push( Report::build(ReportKind::Warning, cursor.source.clone(), continue_caps.get(1).unwrap().start()) .with_message("Invalid list entry spacing") .with_label( Label::new((cursor.source.clone(), cap_spacing.range())) .with_message("Spacing for list entries must match") .with_color(parser.colors().warning)) .with_label( Label::new((cursor.source.clone(), spacing.clone())) .with_message("Previous spacing") .with_color(parser.colors().warning)) .finish()); }, } end_pos = continue_caps.get(0).unwrap().end(); } } } let start_pos = caps.get(1).unwrap().end(); let source = VirtualSource::new( Token::new(start_pos..end_pos, cursor.source.clone()), "List Entry".to_string(), content.as_str()[start_pos..end_pos].to_string(), ); (cursor.at(end_pos), ListRule::parse_depth(caps.get(1).unwrap().as_str(), document), source) }, }; let parsed_entry = parser.parse(Rc::new(source), Some(&document)); let mut parsed_paragraph = parsed_entry.last_element_mut::(false).unwrap(); // Extract content from paragraph let entry = ListEntry::new( Token::new(cursor.pos..end_cursor.pos, cursor.source.clone()), numbering, std::mem::replace(&mut parsed_paragraph.content, Vec::new()) ); // Ger previous list, if none insert a new list let mut list = match document.last_element_mut::(false) { Some(last) => last, None => { parser.push(document, Box::new(List::new( Token::new(cursor.pos..end_cursor.pos, cursor.source.clone())))); document.last_element_mut::(false).unwrap() } }; list.push(entry); (end_cursor, reports) } }