Skip to content

Commit

Permalink
Merge pull request #488 from liamwhite/strikethrough-subscript-unify
Browse files Browse the repository at this point in the history
Add support for subscript extension
  • Loading branch information
kivikakk authored Nov 20, 2024
2 parents 0f0f659 + 3d772f9 commit fc4292c
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 8 deletions.
7 changes: 6 additions & 1 deletion src/cm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
NodeValue::Math(ref math) => self.format_math(math, allow_wrap, entering),
NodeValue::WikiLink(ref nl) => return self.format_wikilink(nl, entering),
NodeValue::Underline => self.format_underline(),
NodeValue::Subscript => self.format_subscript(),
NodeValue::SpoileredText => self.format_spoiler(),
NodeValue::EscapedTag(ref net) => self.format_escaped_tag(net),
};
Expand Down Expand Up @@ -712,7 +713,7 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
}

fn format_strikethrough(&mut self) {
write!(self, "~").unwrap();
write!(self, "~~").unwrap();
}

fn format_superscript(&mut self) {
Expand All @@ -723,6 +724,10 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
write!(self, "__").unwrap();
}

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

fn format_spoiler(&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 @@ -1139,6 +1139,18 @@ impl<'o> HtmlFormatter<'o> {
self.output.write_all(b"</u>")?;
}
}
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::SpoileredText => {
// 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 @@ -184,6 +184,7 @@ enum Extension {
WikilinksTitleAfterPipe,
WikilinksTitleBeforePipe,
Underline,
Subscript,
Spoiler,
Greentext,
}
Expand Down Expand Up @@ -267,6 +268,7 @@ fn main() -> Result<(), Box<dyn Error>> {
.wikilinks_title_after_pipe(exts.contains(&Extension::WikilinksTitleAfterPipe))
.wikilinks_title_before_pipe(exts.contains(&Extension::WikilinksTitleBeforePipe))
.underline(exts.contains(&Extension::Underline))
.subscript(exts.contains(&Extension::Subscript))
.spoiler(exts.contains(&Extension::Spoiler))
.greentext(exts.contains(&Extension::Greentext))
.maybe_front_matter_delimiter(cli.front_matter_delimiter);
Expand Down
7 changes: 7 additions & 0 deletions src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ pub enum NodeValue {
/// **Inline**. Underline. Enabled with `underline` option.
Underline,

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

/// **Inline**. Spoilered text. Enabled with `spoiler` option.
SpoileredText,

Expand Down Expand Up @@ -514,6 +517,7 @@ impl NodeValue {
NodeValue::Math(..) => "math",
NodeValue::WikiLink(..) => "wikilink",
NodeValue::Underline => "underline",
NodeValue::Subscript => "subscript",
NodeValue::SpoileredText => "spoiler",
NodeValue::EscapedTag(_) => "escaped_tag",
}
Expand Down Expand Up @@ -764,6 +768,7 @@ pub fn can_contain_type<'a>(node: &'a AstNode<'a>, child: &NodeValue) -> bool {
| NodeValue::Superscript
| NodeValue::SpoileredText
| NodeValue::Underline
| NodeValue::Subscript
// XXX: this is quite a hack: the EscapedTag _contains_ whatever was
// possibly going to fall into the spoiler. This should be fixed in
// inlines.
Expand Down Expand Up @@ -791,6 +796,7 @@ pub fn can_contain_type<'a>(node: &'a AstNode<'a>, child: &NodeValue) -> bool {
| NodeValue::Superscript
| NodeValue::SpoileredText
| NodeValue::Underline
| NodeValue::Subscript
),

#[cfg(feature = "shortcodes")]
Expand All @@ -810,6 +816,7 @@ pub fn can_contain_type<'a>(node: &'a AstNode<'a>, child: &NodeValue) -> bool {
| NodeValue::Superscript
| NodeValue::SpoileredText
| NodeValue::Underline
| NodeValue::Subscript
| NodeValue::ShortCode(..)
),

Expand Down
28 changes: 21 additions & 7 deletions src/parser/inlines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ impl<'a, 'r, 'o, 'd, 'i> Subject<'a, 'r, 'o, 'd, 'i> {
s.special_chars[b':' as usize] = true;
s.special_chars[b'w' as usize] = true;
}
if options.extension.strikethrough {
if options.extension.strikethrough || options.extension.subscript {
s.special_chars[b'~' as usize] = true;
s.skip_chars[b'~' as usize] = true;
}
Expand Down Expand Up @@ -281,7 +281,9 @@ impl<'a, 'r, 'o, 'd, 'i> Subject<'a, 'r, 'o, 'd, 'i> {
))
}
}
'~' if self.options.extension.strikethrough => Some(self.handle_delim(b'~')),
'~' if self.options.extension.strikethrough || self.options.extension.subscript => {
Some(self.handle_delim(b'~'))
}
'^' if self.options.extension.superscript && !self.within_brackets => {
Some(self.handle_delim(b'^'))
}
Expand Down Expand Up @@ -333,7 +335,7 @@ impl<'a, 'r, 'o, 'd, 'i> Subject<'a, 'r, 'o, 'd, 'i> {

// After parsing a block (and sometimes during), this function traverses the
// stack of `Delimiters`, tokens ("*", "_", etc.) that may delimit regions
// of text for special rendering: emphasis, strong, superscript,
// of text for special rendering: emphasis, strong, superscript, subscript,
// spoilertext; looking for pairs of opening and closing delimiters,
// with the goal of placing the intervening nodes into new emphasis,
// etc AST nodes.
Expand Down Expand Up @@ -461,7 +463,8 @@ impl<'a, 'r, 'o, 'd, 'i> Subject<'a, 'r, 'o, 'd, 'i> {
// both get passed.
if c.delim_char == b'*'
|| c.delim_char == b'_'
|| (self.options.extension.strikethrough && c.delim_char == b'~')
|| ((self.options.extension.strikethrough || self.options.extension.subscript)
&& c.delim_char == b'~')
|| (self.options.extension.superscript && c.delim_char == b'^')
|| (self.options.extension.spoiler && c.delim_char == b'|')
{
Expand Down Expand Up @@ -1068,7 +1071,7 @@ impl<'a, 'r, 'o, 'd, 'i> Subject<'a, 'r, 'o, 'd, 'i> {
opener_num_chars -= use_delims;
closer_num_chars -= use_delims;

if self.options.extension.strikethrough
if (self.options.extension.strikethrough || self.options.extension.subscript)
&& opener_char == b'~'
&& (opener_num_chars != closer_num_chars || opener_num_chars > 0)
{
Expand Down Expand Up @@ -1101,8 +1104,19 @@ impl<'a, 'r, 'o, 'd, 'i> Subject<'a, 'r, 'o, 'd, 'i> {
}

let emph = self.make_inline(
if self.options.extension.strikethrough && opener_char == b'~' {
NodeValue::Strikethrough
if self.options.extension.subscript && opener_char == b'~' && use_delims == 1 {
NodeValue::Subscript
} else if opener_char == b'~' {
// Not emphasis
// Unlike for |, these cases have to be handled because they will match
// in the event subscript but not strikethrough is enabled
if self.options.extension.strikethrough {
NodeValue::Strikethrough
} else if use_delims == 1 {
NodeValue::EscapedTag("~".to_owned())
} else {
NodeValue::EscapedTag("~~".to_owned())
}
} else if self.options.extension.superscript && opener_char == b'^' {
NodeValue::Superscript
} else if self.options.extension.spoiler && opener_char == b'|' {
Expand Down
20 changes: 20 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,26 @@ pub struct ExtensionOptions {
#[builder(default)]
pub underline: bool,

/// Enables subscript text using single tildes.
///
/// If the strikethrough option is also enabled, this overrides the single
/// tilde case to output subscript text.
///
/// ```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 spoilers using double vertical bars
///
/// ```md
Expand Down
1 change: 1 addition & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mod rewriter;
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 @@ -72,6 +72,7 @@ fn exercise_full_api() {
.wikilinks_title_after_pipe(true)
.wikilinks_title_before_pipe(true)
.underline(true)
.subscript(true)
.spoiler(true)
.greentext(true);

Expand Down Expand Up @@ -271,6 +272,7 @@ fn exercise_full_api() {
let _: String = nl.url;
}
nodes::NodeValue::Underline => {}
nodes::NodeValue::Subscript => {}
nodes::NodeValue::SpoileredText => {}
nodes::NodeValue::EscapedTag(data) => {
let _: &String = data;
Expand Down
28 changes: 28 additions & 0 deletions src/tests/subscript.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use super::*;

#[test]
fn subscript() {
html_opts!(
[extension.subscript],
concat!("H~2~O\n"),
concat!("<p>H<sub>2</sub>O</p>\n"),
);
}

#[test]
fn strikethrough_and_subscript() {
html_opts!(
[extension.subscript, extension.strikethrough],
concat!("~~H~2~O~~\n"),
concat!("<p><del>H<sub>2</sub>O</del></p>\n"),
);
}

#[test]
fn no_strikethrough_when_only_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 @@ -285,6 +285,7 @@ impl<'o> XmlFormatter<'o> {
self.output.write_all(b"\"")?;
}
NodeValue::Underline => {}
NodeValue::Subscript => {}
NodeValue::SpoileredText => {}
NodeValue::EscapedTag(ref data) => {
self.output.write_all(data.as_bytes())?;
Expand Down

0 comments on commit fc4292c

Please sign in to comment.