Add SmartArrows

Signed-off-by: trivernis <trivernis@protonmail.com>
feature/epub-rendering
trivernis 4 years ago
parent 0db3c62c57
commit 1a4ec92aff
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

2
Cargo.lock generated

@ -1277,7 +1277,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "snekdown" name = "snekdown"
version = "0.28.0" version = "0.29.0"
dependencies = [ dependencies = [
"asciimath-rs 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", "asciimath-rs 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",

@ -1,6 +1,6 @@
[package] [package]
name = "snekdown" name = "snekdown"
version = "0.28.0" version = "0.29.0"
authors = ["trivernis <trivernis@protonmail.com>"] authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018" edition = "2018"
license-file = "LICENSE" license-file = "LICENSE"

@ -148,17 +148,31 @@ Example:
```toml ```toml
# bibliography.bib.toml # bibliography.bib.toml
[Meta]
author = "Snek" author = "Snek"
published = "2020" published = "2020"
test-key = ["test value", "test value 2"] test-key = ["test value", "test value 2"]
[imports] # those files won't get imported
ignored-imports = ["style.css"] # those files won't get imported ignored-imports = ["style.css"]
included-stylesheets = ["style2.css"] # stylesheets that should be included
included-configs = [] # other metadata files that should be included # stylesheets that should be included
included-bibliography = ["mybib.toml"] # bibliography that should be included included-stylesheets = ["style2.css"]
included-glossary = ["myglossary.toml"] #glossary that sould be included
# other metadata files that should be included
included-configs = []
# bibliography that should be included
included-bibliography = ["mybib.toml"]
# glossary that sould be included
included-glossary = ["myglossary.toml"]
# if external sources (images, stylesheets, MathJax)
# should be embedded into the document (default true)
embed-external = true
# If SmartArrows should be used (default true)
smart-arrows = true
``` ```
The `[Section]` keys are not relevant as the structure gets flattened before the values are read. The `[Section]` keys are not relevant as the structure gets flattened before the values are read.
@ -287,6 +301,14 @@ $$$
The expression get's converted into MathML which is then converted by MathJax when loaded in The expression get's converted into MathML which is then converted by MathJax when loaded in
the browser. the browser.
## Smart Arrows
Snekdown automatically renders the sequences `-->`, `==>`, `<--`, `<==`, `<-->`, `<==>` as
their respective unicode arrows (similar to [markdown-it-smartarrows](https://github.com/adam-p/markdown-it-smartarrows)).
This behavior can be turned off by setting the config parameter `smart-arrows` to `false`
(the config needs to be imported before the arrows are used for that to work).
## Roadmap ## Roadmap
The end goal is to have a markup language with features similar to LaTeX. The end goal is to have a markup language with features similar to LaTeX.

@ -183,6 +183,7 @@ pub enum Inline {
TemplateVar(Arc<RwLock<TemplateVariable>>), TemplateVar(Arc<RwLock<TemplateVariable>>),
CharacterCode(CharacterCode), CharacterCode(CharacterCode),
LineBreak, LineBreak,
Arrow(Arrow),
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -289,6 +290,16 @@ pub struct CharacterCode {
pub(crate) code: String, pub(crate) code: String,
} }
#[derive(Clone, Debug)]
pub enum Arrow {
RightArrow,
LeftArrow,
LeftRightArrow,
BigRightArrow,
BigLeftArrow,
BigLeftRightArrow,
}
// implementations // implementations
impl Document { impl Document {

@ -81,6 +81,15 @@ pub(crate) const CHARACTER_STOP: char = SEMICOLON;
pub(crate) const GLOSSARY_REF_START: char = TILDE; pub(crate) const GLOSSARY_REF_START: char = TILDE;
// Arrows
pub(crate) const A_RIGHT_ARROW: &'static [char] = &['-', '-', '>'];
pub(crate) const A_LEFT_ARROW: &'static [char] = &['<', '-', '-'];
pub(crate) const A_LEFT_RIGHT_ARROW: &'static [char] = &['<', '-', '-', '>'];
pub(crate) const A_BIG_RIGHT_ARROW: &'static [char] = &['=', '=', '>'];
pub(crate) const A_BIG_LEFT_ARROW: &'static [char] = &['<', '=', '='];
pub(crate) const A_BIG_LEFT_RIGHT_ARROW: &'static [char] = &['<', '=', '=', '>'];
// groups // groups
pub(crate) const QUOTES: [char; 2] = [SINGLE_QUOTE, DOUBLE_QUOTE]; pub(crate) const QUOTES: [char; 2] = [SINGLE_QUOTE, DOUBLE_QUOTE];
@ -113,6 +122,15 @@ pub(crate) const INLINE_SPECIAL_CHARS: &'static [char] = &[
MATH, MATH,
]; ];
pub(crate) const INLINE_SPECIAL_SEQUENCES: &'static [&'static [char]] = &[
A_BIG_LEFT_RIGHT_ARROW,
A_BIG_LEFT_ARROW,
A_BIG_RIGHT_ARROW,
A_RIGHT_ARROW,
A_LEFT_ARROW,
A_LEFT_RIGHT_ARROW,
];
pub(crate) const LIST_SPECIAL_CHARS: [char; 14] = [ pub(crate) const LIST_SPECIAL_CHARS: [char; 14] = [
MINUS, PLUS, ASTERISK, O, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', MINUS, PLUS, ASTERISK, O, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
]; ];

@ -49,7 +49,7 @@ code {
} }
code pre { code pre {
font-family: "Fira Code", monospace; font-family: "Fira Code", "Mono", monospace;
padding: 0.8em 0.2em; padding: 0.8em 0.2em;
background-color: #EEE !important; background-color: #EEE !important;
border-radius: 0.25em; border-radius: 0.25em;
@ -133,4 +133,8 @@ blockquote {
text-decoration: none; text-decoration: none;
color: inherit; color: inherit;
border-bottom: 1px dotted #000; border-bottom: 1px dotted #000;
}
.arrow {
font-family: "Fira Code", "Mono", monospace;
} }

@ -65,6 +65,7 @@ impl ToHtml for Inline {
Inline::LineBreak => writer.write("<br>".to_string()), Inline::LineBreak => writer.write("<br>".to_string()),
Inline::CharacterCode(code) => code.to_html(writer), Inline::CharacterCode(code) => code.to_html(writer),
Inline::GlossaryReference(gloss) => gloss.lock().unwrap().to_html(writer), Inline::GlossaryReference(gloss) => gloss.lock().unwrap().to_html(writer),
Inline::Arrow(a) => a.to_html(writer),
} }
} }
} }
@ -687,3 +688,19 @@ impl ToHtml for GlossaryReference {
Ok(()) Ok(())
} }
} }
impl ToHtml for Arrow {
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
writer.write("<span class=\"arrow\">".to_string())?;
match self {
Arrow::RightArrow => writer.write("&xrarr;".to_string()),
Arrow::LeftArrow => writer.write("&xlarr;".to_string()),
Arrow::LeftRightArrow => writer.write("&xharr;".to_string()),
Arrow::BigRightArrow => writer.write("&xrArr;".to_string()),
Arrow::BigLeftArrow => writer.write("&xlArr;".to_string()),
Arrow::BigLeftRightArrow => writer.write("&xhArr;".to_string()),
}?;
writer.write("</span>".to_string())
}
}

@ -3,7 +3,7 @@ use crate::elements::tokens::*;
use crate::elements::BibReference; use crate::elements::BibReference;
use crate::elements::*; use crate::elements::*;
use crate::parser::block::ParseBlock; use crate::parser::block::ParseBlock;
use crate::references::configuration::keys::BIB_REF_DISPLAY; use crate::references::configuration::keys::{BIB_REF_DISPLAY, SMART_ARROWS};
use crate::references::glossary::GlossaryDisplay; use crate::references::glossary::GlossaryDisplay;
use crate::references::glossary::GlossaryReference; use crate::references::glossary::GlossaryReference;
use crate::references::templates::{GetTemplateVariables, Template, TemplateVariable}; use crate::references::templates::{GetTemplateVariables, Template, TemplateVariable};
@ -36,6 +36,7 @@ pub(crate) trait ParseInline {
fn parse_placeholder(&mut self) -> ParseResult<Arc<RwLock<Placeholder>>>; fn parse_placeholder(&mut self) -> ParseResult<Arc<RwLock<Placeholder>>>;
fn parse_template(&mut self) -> ParseResult<Template>; fn parse_template(&mut self) -> ParseResult<Template>;
fn parse_character_code(&mut self) -> ParseResult<CharacterCode>; fn parse_character_code(&mut self) -> ParseResult<CharacterCode>;
fn parse_arrow(&mut self) -> ParseResult<Arrow>;
} }
impl ParseInline for Parser { impl ParseInline for Parser {
@ -120,6 +121,9 @@ impl ParseInline for Parser {
} else if let Ok(char_code) = self.parse_character_code() { } else if let Ok(char_code) = self.parse_character_code() {
log::trace!("Inline::CharacterCode {}", char_code.code); log::trace!("Inline::CharacterCode {}", char_code.code);
Ok(Inline::CharacterCode(char_code)) Ok(Inline::CharacterCode(char_code))
} else if let Ok(arrow) = self.parse_arrow() {
log::trace!("Inline::Arrow {:?}", arrow);
Ok(Inline::Arrow(arrow))
} else { } else {
let plain = self.parse_plain()?; let plain = self.parse_plain()?;
log::trace!("Inline::Plain {}", plain.value); log::trace!("Inline::Plain {}", plain.value);
@ -434,10 +438,13 @@ impl ParseInline for Parser {
} }
while let Some(ch) = self.ctm.next_char() { while let Some(ch) = self.ctm.next_char() {
let index = self.ctm.get_index();
if self.ctm.check_any(&INLINE_SPECIAL_CHARS) if self.ctm.check_any(&INLINE_SPECIAL_CHARS)
|| self.ctm.check_any(&self.inline_break_at) || self.ctm.check_any(&self.inline_break_at)
|| self.ctm.check_any_sequence(&INLINE_SPECIAL_SEQUENCES)
|| (self.parse_variables && self.ctm.check_char(&TEMP_VAR_OPEN)) || (self.parse_variables && self.ctm.check_char(&TEMP_VAR_OPEN))
{ {
self.ctm.rewind(index);
break; break;
} }
if !self.ctm.check_char(&SPECIAL_ESCAPE) { if !self.ctm.check_char(&SPECIAL_ESCAPE) {
@ -623,4 +630,38 @@ impl ParseInline for Parser {
Ok(CharacterCode { code }) Ok(CharacterCode { code })
} }
/// Parses an arrow
fn parse_arrow(&mut self) -> ParseResult<Arrow> {
if !self
.options
.document
.config
.get_entry(SMART_ARROWS)
.and_then(|e| e.get().as_bool())
.unwrap_or(true)
{
Err(self.ctm.err())
} else if self.ctm.check_sequence(A_LEFT_RIGHT_ARROW) {
self.ctm.seek_one()?;
Ok(Arrow::LeftRightArrow)
} else if self.ctm.check_sequence(A_RIGHT_ARROW) {
self.ctm.seek_one()?;
Ok(Arrow::RightArrow)
} else if self.ctm.check_sequence(A_LEFT_ARROW) {
self.ctm.seek_one()?;
Ok(Arrow::LeftArrow)
} else if self.ctm.check_sequence(A_BIG_LEFT_RIGHT_ARROW) {
self.ctm.seek_one()?;
Ok(Arrow::BigLeftRightArrow)
} else if self.ctm.check_sequence(A_BIG_RIGHT_ARROW) {
self.ctm.seek_one()?;
Ok(Arrow::BigRightArrow)
} else if self.ctm.check_sequence(A_BIG_LEFT_ARROW) {
self.ctm.seek_one()?;
Ok(Arrow::BigLeftArrow)
} else {
Err(self.ctm.err())
}
}
} }

@ -7,3 +7,4 @@ pub const IMP_CONFIGS: &str = "included-configs";
pub const IMP_BIBLIOGRAPHY: &str = "included-bibliography"; pub const IMP_BIBLIOGRAPHY: &str = "included-bibliography";
pub const IMP_GLOSSARY: &str = "included-glossary"; pub const IMP_GLOSSARY: &str = "included-glossary";
pub const EMBED_EXTERNAL: &str = "embed-external"; pub const EMBED_EXTERNAL: &str = "embed-external";
pub const SMART_ARROWS: &str = "smart-arrows";

@ -42,6 +42,14 @@ impl Value {
_ => "".to_string(), _ => "".to_string(),
} }
} }
/// Returns the bool value if the value is a boolean
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(b) => Some(*b),
_ => None,
}
}
} }
impl ConfigEntry { impl ConfigEntry {

Loading…
Cancel
Save