Skip to content

Commit

Permalink
Add support for subscript extension
Browse files Browse the repository at this point in the history
  • Loading branch information
Meow authored and liamwhite committed Oct 31, 2024
1 parent b27a3dd commit 6bc19f1
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 8 deletions.
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

0 comments on commit 6bc19f1

Please sign in to comment.