diff --git a/docs-src/02-markdown/06-task-lists.md b/docs-src/02-markdown/06-task-lists.md new file mode 100644 index 0000000..65afe2d --- /dev/null +++ b/docs-src/02-markdown/06-task-lists.md @@ -0,0 +1,11 @@ +--- +title = "Task Lists" +--- + +You can also use [GFM](https://help.github.com/en/github/writing-on-github)-style [task lists](https://github.blog/2013-01-09-task-lists-in-gfm-issues-pulls-comments/) to indicate a TODO list: + +- [ ] a task list item +- [ ] list syntax required +- [ ] normal **formatting** +- [ ] incomplete +- [x] completed diff --git a/src/latex.rs b/src/latex.rs index 16cf9e7..70b5bf2 100644 --- a/src/latex.rs +++ b/src/latex.rs @@ -57,24 +57,44 @@ fn format_text<'a>(node: &'a comrak::nodes::AstNode<'a>, output: &mut String) { output.push_str(&escape_text(text)); } }, - NodeValue::Code(text) => { - if let Ok(text) = std::str::from_utf8(text) { - output.push_str("\\texttt{"); - output.push_str(&escape_text(text)); - output.push_str("}"); - } - }, + //NodeValue::Code(text) => { + // if let Ok(text) = std::str::from_utf8(text) { + // output.push_str("\\texttt{"); + // output.push_str(&escape_text(text)); + // output.push_str("}"); + // } + //}, + //NodeValue::Emph => { + // output.push_str("\\emph{"); + // for child in node.children() { format_text(child, output); } + // output.push_str("}"); + //}, + //NodeValue::Strong => { + // output.push_str("\\textbf{"); + // for child in node.children() { format_text(child, output); } + // output.push_str("}"); + //}, + //NodeValue::Strikethrough => { + // output.push_str("\\sout{"); + // for child in node.children() { format_text(child, output); } + // output.push_str("}"); + //}, + //NodeValue::Superscript => { + // output.push_str("\\textsuperscript{"); + // for child in node.children() { format_text(child, output); } + // output.push_str("}"); + //}, _ => for child in node.children() { format_text(child, output); }, } } -fn format_node<'a>(node: &'a comrak::nodes::AstNode<'a>, output: &mut String) { +fn format_node<'a>(section_offset: u32, node: &'a comrak::nodes::AstNode<'a>, output: &mut String) { use comrak::nodes::NodeValue; match &node.data.borrow().value { - NodeValue::Document => for child in node.children() { format_node(child, output); }, + NodeValue::Document => for child in node.children() { format_node(section_offset, child, output); }, NodeValue::BlockQuote => { output.push_str("\\begin{quote}\n"); - for child in node.children() { format_node(child, output); } + for child in node.children() { format_node(section_offset, child, output); } output.push_str("\\end{quote}\n"); }, NodeValue::List(node_list) => { @@ -82,7 +102,7 @@ fn format_node<'a>(node: &'a comrak::nodes::AstNode<'a>, output: &mut String) { comrak::nodes::ListType::Bullet => output.push_str("\\begin{itemize}\n"), comrak::nodes::ListType::Ordered => output.push_str("\\begin{enumerate}\n"), } - for child in node.children() { format_node(child, output); } + for child in node.children() { format_node(section_offset, child, output); } match node_list.list_type { comrak::nodes::ListType::Bullet => output.push_str("\\end{itemize}\n"), comrak::nodes::ListType::Ordered => output.push_str("\\end{enumerate}\n"), @@ -90,15 +110,19 @@ fn format_node<'a>(node: &'a comrak::nodes::AstNode<'a>, output: &mut String) { }, NodeValue::Item(_) => { output.push_str("\\item "); - for child in node.children() { format_node(child, output); } + let mut item: String = String::default(); + for child in node.children() { format_node(section_offset, child, &mut item); } + output.push_str(item.trim()); output.push_str("\n"); }, NodeValue::DescriptionList => { output.push_str("\\begin{description}\n"); - for child in node.children() { format_node(child, output); } + let mut items: String = String::default(); + for child in node.children() { format_node(section_offset, child, &mut items); } + output.push_str(&items.replace("\n\n\n", "\n").replace("\n\n", "\n")); output.push_str("\\end{description}\n"); }, - NodeValue::DescriptionItem(_) => for child in node.children() { format_node(child, output); }, + NodeValue::DescriptionItem(_) => for child in node.children() { format_node(section_offset, child, output); }, NodeValue::DescriptionTerm => { output.push_str("\\item ["); let mut term: String = String::default(); @@ -107,7 +131,7 @@ fn format_node<'a>(node: &'a comrak::nodes::AstNode<'a>, output: &mut String) { output.push_str("] "); }, NodeValue::DescriptionDetails => { - for child in node.children() { format_node(child, output); } + for child in node.children() { format_node(section_offset, child, output); } output.push_str("\n"); }, NodeValue::CodeBlock(node_code_block) => { @@ -133,23 +157,26 @@ fn format_node<'a>(node: &'a comrak::nodes::AstNode<'a>, output: &mut String) { output.push_str("\\end{minted}\n\\end{absolutelynopagebreak}\n\n"); }, NodeValue::HtmlBlock(node_html_block) => { - + log::warn!("can't handle html block, rendering it as syntax..."); + let source = std::str::from_utf8(&node_html_block.literal).expect("valid utf-8"); + output.push_str("\\begin{minted}[breaklines]{text}\n"); + output.push_str(source); + output.push_str("\\end{minted}\n\n"); }, NodeValue::Paragraph => { for child in node.children() { - format_node(child, output); + format_node(section_offset, child, output); } output.push_str("\n\n"); }, NodeValue::Heading(node_heading) => { - match node_heading.level { + match node_heading.level + section_offset { 1 => output.push_str("\\section{"), 2 => output.push_str("\\subsection{"), 3 => output.push_str("\\subsubsection{"), 4 => output.push_str("\\paragraph{"), 5 => output.push_str("\\subparagraph{"), - 6 => output.push_str("\\textbf{"), - _ => unreachable!() + _ => output.push_str("\\textbf{"), } for child in node.children() { format_text(child, output); } output.push_str("}"); @@ -174,8 +201,16 @@ fn format_node<'a>(node: &'a comrak::nodes::AstNode<'a>, output: &mut String) { output.push_str(&escape_text(text)); } }, - NodeValue::TaskItem(bool) => { - + NodeValue::TaskItem(checked) => { + let mut item: String = String::default(); + for child in node.children() { format_node(section_offset, child, &mut item); } + if *checked { + output.push_str(r"$\text{\rlap{$\checkmark$}}\square$ "); + } + else { + output.push_str(r"$\square$ "); + } + output.push_str(item.trim()); }, NodeValue::SoftBreak => { output.push_str("\n"); @@ -191,30 +226,41 @@ fn format_node<'a>(node: &'a comrak::nodes::AstNode<'a>, output: &mut String) { } }, NodeValue::HtmlInline(text) => { - + let source = std::str::from_utf8(&text).expect("valid utf-8"); + log::warn!("can't handle inline html `{}`, rendering it as syntax...", source); + output.push_str("\\mintinline{html}{"); + output.push_str(&escape_text(source)); + output.push_str("}"); }, NodeValue::Emph => { output.push_str("\\emph{"); - for child in node.children() { format_node(child, output); } + for child in node.children() { format_node(section_offset, child, output); } output.push_str("}"); }, NodeValue::Strong => { output.push_str("\\textbf{"); - for child in node.children() { format_node(child, output); } + for child in node.children() { format_node(section_offset, child, output); } output.push_str("}"); }, NodeValue::Strikethrough => { output.push_str("\\sout{"); - for child in node.children() { format_node(child, output); } + for child in node.children() { format_node(section_offset, child, output); } output.push_str("}"); }, NodeValue::Superscript => { output.push_str("\\textsuperscript{"); - for child in node.children() { format_node(child, output); } + for child in node.children() { format_node(section_offset, child, output); } output.push_str("}"); }, NodeValue::Link(node_link) => { - + let url = std::str::from_utf8(&node_link.url).expect("valid utf-8"); + output.push_str("\\href{"); + output.push_str(&escape_text(url)); + output.push_str("}{"); + for child in node.children() { format_text(child, output); } + output.push_str("}\\footnote{\\url{"); + output.push_str(&escape_text(url)); + output.push_str("}}"); }, NodeValue::Image(node_link) => { @@ -225,7 +271,7 @@ fn format_node<'a>(node: &'a comrak::nodes::AstNode<'a>, output: &mut String) { } } -fn format_markdown(src: &str) -> Result> { +fn format_markdown(section_offset: u32, src: &str) -> Result> { let arena = comrak::Arena::new(); let root = comrak::parse_document( &arena, @@ -233,7 +279,7 @@ fn format_markdown(src: &str) -> Result> { &COMRAK_OPTIONS); let mut latex: String = String::with_capacity(src.len()); - format_node(&root, &mut latex); + format_node(section_offset, &root, &mut latex); Ok(latex) } @@ -252,11 +298,11 @@ pub fn build, POut: AsRef>(src: PIn, dest: POut) -> Resul let mut book = super::load_book(&src)?; // then convert all the markdown - book.description = format_markdown(&book.description)?; + book.description = format_markdown(0, &book.description)?; for chapter in book.chapters.iter_mut() { - chapter.contents = format_markdown(&chapter.contents)?; + chapter.contents = format_markdown(0, &chapter.contents)?; for section in chapter.sections.iter_mut() { - section.contents = format_markdown(§ion.contents)?; + section.contents = format_markdown(1, §ion.contents)?; } } diff --git a/src/latex/filters.rs b/src/latex/filters.rs index 0293805..4dde58d 100644 --- a/src/latex/filters.rs +++ b/src/latex/filters.rs @@ -3,3 +3,7 @@ use chrono::prelude::*; pub fn human_date(d: &DateTime) -> askama::Result { Ok(d.format("%b %e, %Y").to_string()) } + +pub fn year(d: &DateTime) -> askama::Result { + Ok(d.format("%Y").to_string()) +} diff --git a/templates/book.tex b/templates/book.tex index 7926cb9..c6c1ff1 100644 --- a/templates/book.tex +++ b/templates/book.tex @@ -29,10 +29,18 @@ {\par\xdef\tpd{\the\prevdepth}\egroup \prevdepth=\tpd} -%\makeatletter -%\renewcommand{\frontmatter}{\cleardoublepage\@mainmatterfalse} -%\renewcommand{\mainmatter}{\cleardoublepage\@mainmattertrue} -%\makeatother +% urls +\usepackage[hidelinks]{hyperref} + +% extra symbols +\usepackage{amsmath} +\usepackage{amssymb} + +% make the TOC work with our preface +\makeatletter +\renewcommand{\frontmatter}{\cleardoublepage\@mainmatterfalse} +\renewcommand{\mainmatter}{\cleardoublepage\@mainmattertrue} +\makeatother \title{ {{ front.title }} } \author{ {{ front.author }} } @@ -40,10 +48,22 @@ \begin{document} \frontmatter - +\pagestyle{empty} \maketitle +\clearpage + +% copyright +\pagestyle{empty} +\topskip0pt +\vspace*{\fill} +\begin{center} +\textcopyright{} {{ front.pubdate|year }} {{ front.author }} +\end{center} +\vspace*{\fill} +\clearpage \tableofcontents +\cleardoublepage \chapter{Preface} @@ -57,7 +77,7 @@ {{ chapter.contents }} {% for section in chapter.sections %} -\chapter{ {{ section.front.title }} } +\section{ {{ section.front.title }} } {{ section.contents }} {% endfor %}