Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for subscript extension #480

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/cm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ impl<'a, 'o, 'c> CommonMarkFormatter<'a, 'o, 'c> {
}
NodeValue::Math(ref math) => self.format_math(math, allow_wrap, entering),
NodeValue::WikiLink(ref nl) => return self.format_wikilink(nl, entering),
NodeValue::Subscript => self.format_subscript(),
NodeValue::Underline => self.format_underline(),
NodeValue::SpoileredText => self.format_spoiler(),
NodeValue::EscapedTag(ref net) => self.format_escaped_tag(net),
Expand Down Expand Up @@ -711,6 +712,10 @@ impl<'a, 'o, 'c> CommonMarkFormatter<'a, 'o, 'c> {
write!(self, "^").unwrap();
}

fn format_subscript(&mut self) {
write!(self, "%").unwrap();
}

fn format_underline(&mut self) {
write!(self, "__").unwrap();
}
Expand Down
12 changes: 12 additions & 0 deletions src/html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,18 @@ impl<'o, 'c: 'o> HtmlFormatter<'o, 'c> {
self.output.write_all(b"</a>")?;
}
}
NodeValue::Subscript => {
// Unreliable sourcepos.
if entering {
self.output.write_all(b"<sub")?;
if self.options.render.experimental_inline_sourcepos {
self.render_sourcepos(node)?;
}
self.output.write_all(b">")?;
} else {
self.output.write_all(b"</sub>")?;
}
}
NodeValue::Underline => {
// Unreliable sourcepos.
if entering {
Expand Down
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ enum Extension {
MathCode,
WikilinksTitleAfterPipe,
WikilinksTitleBeforePipe,
Subscript,
Underline,
Spoiler,
Greentext,
Expand Down Expand Up @@ -266,6 +267,7 @@ fn main() -> Result<(), Box<dyn Error>> {
.math_code(exts.contains(&Extension::MathCode))
.wikilinks_title_after_pipe(exts.contains(&Extension::WikilinksTitleAfterPipe))
.wikilinks_title_before_pipe(exts.contains(&Extension::WikilinksTitleBeforePipe))
.subscript(exts.contains(&Extension::Subscript))
.underline(exts.contains(&Extension::Underline))
.spoiler(exts.contains(&Extension::Spoiler))
.greentext(exts.contains(&Extension::Greentext))
Expand Down
7 changes: 7 additions & 0 deletions src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ pub enum NodeValue {
/// **Inline**. A wikilink to some URL.
WikiLink(NodeWikiLink),

/// **Inline**. Subscript. Enabled with `subscript` option.
Subscript,

/// **Inline**. Underline. Enabled with `underline` option.
Underline,

Expand Down Expand Up @@ -513,6 +516,7 @@ impl NodeValue {
NodeValue::Escaped => "escaped",
NodeValue::Math(..) => "math",
NodeValue::WikiLink(..) => "wikilink",
NodeValue::Subscript => "subscript",
NodeValue::Underline => "underline",
NodeValue::SpoileredText => "spoiler",
NodeValue::EscapedTag(_) => "escaped_tag",
Expand Down Expand Up @@ -762,6 +766,7 @@ pub fn can_contain_type<'a>(node: &'a AstNode<'a>, child: &NodeValue) -> bool {
| NodeValue::WikiLink(..)
| NodeValue::Strikethrough
| NodeValue::Superscript
| NodeValue::Subscript
| NodeValue::SpoileredText
| NodeValue::Underline
// XXX: this is quite a hack: the EscapedTag _contains_ whatever was
Expand Down Expand Up @@ -789,6 +794,7 @@ pub fn can_contain_type<'a>(node: &'a AstNode<'a>, child: &NodeValue) -> bool {
| NodeValue::WikiLink(..)
| NodeValue::FootnoteReference(..)
| NodeValue::Superscript
| NodeValue::Subscript
| NodeValue::SpoileredText
| NodeValue::Underline
),
Expand All @@ -808,6 +814,7 @@ pub fn can_contain_type<'a>(node: &'a AstNode<'a>, child: &NodeValue) -> bool {
| NodeValue::WikiLink(..)
| NodeValue::FootnoteReference(..)
| NodeValue::Superscript
| NodeValue::Subscript
| NodeValue::SpoileredText
| NodeValue::Underline
| NodeValue::ShortCode(..)
Expand Down
24 changes: 16 additions & 8 deletions src/parser/inlines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ impl<'a, 'r, 'o, 'c, 'd, 'i> Subject<'a, 'r, 'o, 'c, 'd, 'i> {
if options.extension.shortcodes {
s.special_chars[b':' as usize] = true;
}
if options.extension.subscript {
s.special_chars[b'%' as usize] = true;
}
if options.extension.underline {
s.special_chars[b'_' as usize] = true;
}
Expand Down Expand Up @@ -286,6 +289,7 @@ impl<'a, 'r, 'o, 'c, 'd, 'i> Subject<'a, 'r, 'o, 'c, 'd, 'i> {
Some(self.handle_delim(b'^'))
}
'$' => Some(self.handle_dollars()),
'%' if self.options.extension.subscript => Some(self.handle_delim(b'%')),
'|' if self.options.extension.spoiler => Some(self.handle_delim(b'|')),
_ => {
let endpos = self.find_special_char();
Expand Down Expand Up @@ -384,7 +388,7 @@ impl<'a, 'r, 'o, 'c, 'd, 'i> Subject<'a, 'r, 'o, 'c, 'd, 'i> {
// This array is an important optimization that prevents searching down
// the stack for openers we've previously searched for and know don't
// exist, preventing exponential blowup on pathological cases.
let mut openers_bottom: [usize; 12] = [stack_bottom; 12];
let mut openers_bottom: [usize; 13] = [stack_bottom; 13];

// This is traversing the stack from the top to the bottom, setting `closer` to
// the delimiter directly above `stack_bottom`. In the case where we are processing
Expand All @@ -408,13 +412,14 @@ impl<'a, 'r, 'o, 'c, 'd, 'i> Subject<'a, 'r, 'o, 'c, 'd, 'i> {
let mut mod_three_rule_invoked = false;

let ix = match c.delim_char {
b'|' => 0,
b'~' => 1,
b'^' => 2,
b'"' => 3,
b'\'' => 4,
b'_' => 5,
b'*' => 6 + (if c.can_open { 3 } else { 0 }) + (c.length % 3),
b'%' => 0,
b'|' => 1,
b'~' => 2,
b'^' => 3,
b'"' => 4,
b'\'' => 5,
b'_' => 6,
b'*' => 7 + (if c.can_open { 3 } else { 0 }) + (c.length % 3),
_ => unreachable!(),
};

Expand Down Expand Up @@ -463,6 +468,7 @@ impl<'a, 'r, 'o, 'c, 'd, 'i> Subject<'a, 'r, 'o, 'c, 'd, 'i> {
|| c.delim_char == b'_'
|| (self.options.extension.strikethrough && c.delim_char == b'~')
|| (self.options.extension.superscript && c.delim_char == b'^')
|| (self.options.extension.subscript && c.delim_char == b'%')
|| (self.options.extension.spoiler && c.delim_char == b'|')
{
if opener_found {
Expand Down Expand Up @@ -1105,6 +1111,8 @@ impl<'a, 'r, 'o, 'c, 'd, 'i> Subject<'a, 'r, 'o, 'c, 'd, 'i> {
NodeValue::Strikethrough
} else if self.options.extension.superscript && opener_char == b'^' {
NodeValue::Superscript
} else if self.options.extension.subscript && opener_char == b'%' {
NodeValue::Subscript
} else if self.options.extension.spoiler && opener_char == b'|' {
if use_delims == 2 {
NodeValue::SpoileredText
Expand Down
17 changes: 17 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,23 @@ pub struct ExtensionOptions {
#[builder(default)]
pub wikilinks_title_before_pipe: bool,

/// Enables subscripts using percent signs
///
/// ```md
/// H%2%O
/// ```
///
/// ```
/// # use comrak::{markdown_to_html, Options};
/// let mut options = Options::default();
/// options.extension.subscript = true;
///
/// assert_eq!(markdown_to_html("H%2%O", &options),
/// "<p>H<sub>2</sub>O</p>\n");
/// ```
#[builder(default)]
pub subscript: bool,

/// Enables underlines using double underscores
///
/// ```md
Expand Down
1 change: 1 addition & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mod regressions;
mod shortcodes;
mod spoiler;
mod strikethrough;
mod subscript;
mod superscript;
mod table;
mod tagfilter;
Expand Down
2 changes: 2 additions & 0 deletions src/tests/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ fn exercise_full_api() {
let _extension = extension
.wikilinks_title_after_pipe(true)
.wikilinks_title_before_pipe(true)
.subscript(true)
.underline(true)
.spoiler(true)
.greentext(true);
Expand Down Expand Up @@ -270,6 +271,7 @@ fn exercise_full_api() {
nodes::NodeValue::WikiLink(nl) => {
let _: String = nl.url;
}
nodes::NodeValue::Subscript => {}
nodes::NodeValue::Underline => {}
nodes::NodeValue::SpoileredText => {}
nodes::NodeValue::EscapedTag(data) => {
Expand Down
10 changes: 10 additions & 0 deletions src/tests/subscript.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use super::*;

#[test]
fn subscript() {
html_opts!(
[extension.subscript],
concat!("H%2%O\n"),
concat!("<p>H<sub>2</sub>O</p>\n"),
);
}
1 change: 1 addition & 0 deletions src/xml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ impl<'o, 'c> XmlFormatter<'o, 'c> {
self.escape(nl.url.as_bytes())?;
self.output.write_all(b"\"")?;
}
NodeValue::Subscript => {}
NodeValue::Underline => {}
NodeValue::SpoileredText => {}
NodeValue::EscapedTag(ref data) => {
Expand Down
Loading