Improve section handling and add Checkboxes

Huge improvements to section handling by postprocessing elements and reordering them,
mergin with imports and so on to get a one-document output with no nesting errors.
pull/1/head
trivernis 5 years ago
parent b2b535c22b
commit 3c88632548

2
Cargo.lock generated

@ -382,7 +382,7 @@ dependencies = [
[[package]]
name = "snekdown"
version = "0.9.1"
version = "0.10.0"
dependencies = [
"chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",

@ -1,6 +1,6 @@
[package]
name = "snekdown"
version = "0.9.1"
version = "0.10.0"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"
license-file = "LICENSE"
@ -16,8 +16,6 @@ crate-type = ["lib"]
name = "snekdown"
path = "src/main.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
crossbeam-utils = "0.7.2"
structopt = "0.3.14"

@ -163,7 +163,7 @@ _Underlined_
The end goal is to have a markdown language similar to LaTeX.
- [ ] Checkboxes
- [x] Checkboxes
- [ ] Emojis (\:emoji:)
- [ ] Bibliography
- [ ] Math
@ -173,5 +173,6 @@ The end goal is to have a markdown language similar to LaTeX.
- [ ] Cross References
- [ ] Title pages
- [ ] Glossary
- [ ] EPUB Rendering (PDF is too hard
- [ ] EPUB Rendering (PDF is too hard)
- [ ] Custom Elements via templates
- [ ] Custom Stylesheets

@ -57,6 +57,7 @@ impl ToHtml for Inline {
Inline::Placeholder(placeholder) => placeholder.lock().unwrap().to_html(),
Inline::Reference(reference) => reference.to_html(),
Inline::Superscript(superscript) => superscript.to_html(),
Inline::Checkbox(checkbox) => checkbox.to_html(),
}
}
}
@ -476,3 +477,13 @@ impl ToHtml for ReferenceEntry {
}
}
}
impl ToHtml for Checkbox {
fn to_html(&self) -> String {
if self.value {
format!("<input type='checkbox' checked disabled>")
} else {
format!("<input type='checkbox'disabled>")
}
}
}

@ -50,7 +50,7 @@ impl CharStateMachine for Parser {
/// char at the indexes position
fn next_char(&mut self) -> Option<char> {
self.index += 1;
self.previous_char = self.current_char;
self.current_char = *self.text.get(self.index)?;
Some(self.current_char)
@ -65,7 +65,12 @@ impl CharStateMachine for Parser {
fn revert_to(&mut self, index: usize) -> Result<(), ParseError> {
if let Some(char) = self.text.get(index) {
self.index = index;
self.current_char = char.clone();
self.current_char = *char;
if index > self.text.len() {
if let Some(ch) = self.text.get(index - 1) {
self.previous_char = *ch;
}
}
Ok(())
} else {
Err(ParseError::new_with_message(index, "failed to revert"))
@ -112,10 +117,8 @@ impl CharStateMachine for Parser {
if self.index == 0 {
return false;
}
if let Some(previous_char) = self.text.get(self.index - 1) {
if previous_char == &SPECIAL_ESCAPE {
return true;
}
if self.previous_char == SPECIAL_ESCAPE {
return true;
}
return false;
}

@ -1,5 +1,6 @@
use crate::parsing::placeholders::BibEntry;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
pub const SECTION: &str = "section";
@ -10,21 +11,6 @@ pub const CODE_BLOCK: &str = "code_block";
pub const QUOTE: &str = "quote";
pub const IMPORT: &str = "import";
macro_rules! test_block {
($block:expr, $block_type:expr) => {
match $block {
Block::Section(_) if $block_type == SECTION => true,
Block::List(_) if $block_type == LIST => true,
Block::Table(_) if $block_type == TABLE => true,
Block::Paragraph(_) if $block_type == PARAGRAPH => true,
Block::CodeBlock(_) if $block_type == CODE_BLOCK => true,
Block::Quote(_) if $block_type == QUOTE => true,
Block::Import(_) if $block_type == IMPORT => true,
_ => false,
}
};
}
#[derive(Clone, Debug)]
pub enum MetadataValue {
String(String),
@ -170,6 +156,7 @@ pub enum Inline {
Image(Image),
Placeholder(Arc<Mutex<Placeholder>>),
Reference(Reference),
Checkbox(Checkbox),
}
#[derive(Clone, Debug)]
@ -207,6 +194,11 @@ pub struct SuperscriptText {
pub(crate) value: Box<Inline>,
}
#[derive(Clone, Debug)]
pub struct Checkbox {
pub(crate) value: bool,
}
#[derive(Clone, Debug)]
pub struct Url {
pub description: Option<String>,
@ -308,27 +300,6 @@ impl Document {
self.placeholders.push(placeholder);
}
pub fn find(&self, block_type: &str, nested: bool) -> Vec<&Block> {
let mut found = Vec::new();
let mut found_self = self
.elements
.iter()
.filter(|e| {
if nested {
match e {
Block::Section(sec) => found.append(&mut sec.find(block_type, nested)),
_ => {}
}
}
test_block!(e, block_type)
})
.collect();
found.append(&mut found_self);
found
}
pub fn create_toc(&self, ordered: bool) -> List {
let mut list = List::new();
list.ordered = ordered;
@ -374,6 +345,61 @@ impl Document {
None
}
}
/// Processes section and import elements
///
/// if it encounters a section it checks if the sections is of smaller order than the previous one
/// if thats the case it grabs the previous one and adds the section to its children
///
/// if it encounters an import, it loads the imports top elements to its own
pub fn postprocess_imports(&mut self) {
let mut new_order: Vec<Block> = Vec::with_capacity(self.elements.len());
self.elements.reverse();
let mut count: usize = 0;
let mut last_section: Option<(u8, usize)> = None;
while let Some(element) = self.elements.pop() {
match element {
Block::Section(sec) => {
if let Some((last_size, last_pos)) = last_section {
if sec.header.size > last_size {
let last = new_order.get_mut(last_pos).unwrap();
if let Block::Section(p_sec) = last {
p_sec.add_section(sec);
continue;
}
}
}
last_section = Some((sec.header.size, count));
new_order.push(Block::Section(sec));
}
Block::Import(imp) => {
let arc_anchor = Arc::clone(&imp.anchor);
let anchor = &mut arc_anchor.lock().unwrap();
if let Some(doc) = &mut anchor.document {
self.placeholders.append(&mut doc.placeholders);
doc.elements.reverse();
self.elements.append(&mut doc.elements);
anchor.document = None;
continue;
} else {
new_order.push(Block::Import(imp));
}
}
_ => {
if let Some((_, last_pos)) = last_section {
let last = new_order.get_mut(last_pos).unwrap();
if let Block::Section(p_sec) = last {
p_sec.add_element(element);
continue;
}
}
new_order.push(element)
}
}
count += 1;
}
self.elements = new_order;
}
}
impl Section {
@ -389,26 +415,6 @@ impl Section {
self.elements.push(element)
}
pub fn find(&self, block_type: &str, nested: bool) -> Vec<&Block> {
let mut found = Vec::new();
let mut found_self = self
.elements
.iter()
.filter(|e| {
if nested {
match e {
Block::Section(sec) => found.append(&mut sec.find(block_type, nested)),
_ => {}
}
}
test_block!(e, block_type)
})
.collect();
found.append(&mut found_self);
found
}
pub fn get_toc_list(&self, ordered: bool) -> List {
let mut list = List::new();
self.elements.iter().for_each(|e| {
@ -431,6 +437,42 @@ impl Section {
false
}
}
/// adds a child section to the section
/// It either adds it directly to its elements or iterates through its children to
/// add it to the fitting one
pub(crate) fn add_section(&mut self, section: Section) {
if section.header.size == self.header.size + 1 {
self.elements.push(Block::Section(section))
} else {
let has_parent = Mutex::new(AtomicBool::new(true));
let iterator = self.elements.iter_mut().rev().filter(|e| {
if let Block::Section(sec) = e {
if sec.header.size > section.header.size {
has_parent.lock().unwrap().store(true, Ordering::Relaxed);
true
} else {
false
}
} else {
false
}
});
if has_parent.lock().unwrap().load(Ordering::Relaxed) {
for sec in iterator {
if let Block::Section(sec) = sec {
if sec.header.size < section.header.size {
sec.add_section(section);
break;
}
}
}
} else {
self.elements.push(Block::Section(section));
}
}
}
}
impl Header {

@ -8,6 +8,7 @@ pub(crate) trait ParseInline {
fn parse_inline(&mut self) -> Result<Inline, ParseError>;
fn parse_image(&mut self) -> Result<Image, ParseError>;
fn parse_url(&mut self, short_syntax: bool) -> Result<Url, ParseError>;
fn parse_checkbox(&mut self) -> Result<Checkbox, ParseError>;
fn parse_bold(&mut self) -> Result<BoldText, ParseError>;
fn parse_italic(&mut self) -> Result<ItalicText, ParseError>;
fn parse_striked(&mut self) -> Result<StrikedText, ParseError>;
@ -41,6 +42,8 @@ impl ParseInline for Parser {
Ok(Inline::Striked(striked))
} else if let Ok(superscript) = self.parse_superscript() {
Ok(Inline::Superscript(superscript))
} else if let Ok(checkbox) = self.parse_checkbox() {
Ok(Inline::Checkbox(checkbox))
} else {
Ok(Inline::Plain(self.parse_plain()?))
}
@ -100,6 +103,25 @@ impl ParseInline for Parser {
}
}
/// parses a markdown checkbox
fn parse_checkbox(&mut self) -> Result<Checkbox, ParseError> {
let start_index = self.index;
self.assert_special(&CHECK_OPEN, start_index)?;
self.skip_char();
let checked = if self.check_special(&CHECK_CHECKED) {
true
} else if self.check_special(&SPACE) {
false
} else {
return Err(self.revert_with_error(start_index));
};
self.skip_char();
self.assert_special(&CHECK_CLOSE, start_index)?;
self.skip_char();
Ok(Checkbox { value: checked })
}
/// parses bold text with must start with two asterisks
fn parse_bold(&mut self) -> Result<BoldText, ParseError> {
let start_index = self.index;

@ -103,6 +103,7 @@ pub struct Parser {
is_child: bool,
pub(crate) inline_break_at: Vec<char>,
document: Document,
pub(crate) previous_char: char,
}
impl Parser {
@ -126,7 +127,7 @@ impl Parser {
is_child: bool,
) -> Self {
let mut text: Vec<char> = text.chars().collect();
text.append(&mut vec!['\n', ' ', '\n']); // push space and newline of eof. it fixes stuff and I don't know why.
text.append(&mut vec!['\n', ' ']); // it fixes stuff and I don't know why.
let current_char = text.get(0).unwrap().clone();
if let Some(path) = path.clone() {
let path_info = Path::new(&path);
@ -146,6 +147,7 @@ impl Parser {
paths,
wg: WaitGroup::new(),
is_child,
previous_char: ' ',
inline_break_at: Vec::new(),
document: Document::new(!is_child),
}
@ -255,6 +257,8 @@ impl Parser {
let wg = self.wg.clone();
self.wg = WaitGroup::new();
wg.wait();
self.document.postprocess_imports();
if !self.is_child {
self.document.process_placeholders();
}

@ -24,7 +24,6 @@ macro_rules! inline {
}
pub(crate) trait ProcessPlaceholders {
fn combine_placeholders(&mut self);
fn process_placeholders(&mut self);
fn process_definitions(&mut self);
fn add_bib_entry(&mut self, key: String, value: BibEntry) -> Arc<Mutex<BibEntry>>;
@ -102,22 +101,8 @@ const P_TIME: &str = "time";
const P_DATETIME: &str = "datetime";
impl ProcessPlaceholders for Document {
fn combine_placeholders(&mut self) {
let mut placeholders = Vec::new();
self.elements.iter().for_each(|e| {
if let Block::Import(import) = e {
let anchor = import.anchor.lock().unwrap();
if let Some(doc) = &anchor.document {
placeholders.append(&mut doc.placeholders.clone())
}
}
});
self.placeholders.append(&mut placeholders);
}
/// parses all placeholders and assigns values to them
fn process_placeholders(&mut self) {
self.combine_placeholders();
self.process_definitions();
lazy_static::lazy_static! {static ref RE_REF: Regex = Regex::new(r"^ref:(.*)$").unwrap();}
self.placeholders.iter().for_each(|p| {

@ -42,6 +42,9 @@ pub(crate) const IMPORT_OPEN: char = R_BRACKET;
pub(crate) const IMPORT_CLOSE: char = L_BRACKET;
pub(crate) const PHOLDER_OPEN: char = R_BRACKET;
pub(crate) const PHOLDER_CLOSE: char = L_BRACKET;
pub(crate) const CHECK_OPEN: char = R_BRACKET;
pub(crate) const CHECK_CLOSE: char = L_BRACKET;
pub(crate) const CHECK_CHECKED: char = X;
pub(crate) const ITALIC: char = ASTERISK;
pub(crate) const MONOSPACE: char = BACKTICK;

Loading…
Cancel
Save