Repository: kivikakk/comrak (Rust)

Issue:
footnotes plus superscript interact weirdly
From the title you can probably tell that this is related to #58.

The [documentation for `ext_footnotes`](https://docs.rs/comrak/newest/comrak/struct.ComrakOptions.html#structfield.ext_footnotes) has this example:

```rust
let options = ComrakOptions {
  ext_footnotes: true,
  ..ComrakOptions::default()
};
assert_eq!(markdown_to_html("Hi[^x].\n\n[^x]: A greeting.\n", &options),
           "<p>Hi<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">1</a></sup>.</p>\n<section class=\"footnotes\">\n<ol>\n<li id=\"fn1\">\n<p>A greeting. <a href=\"#fnref1\" class=\"footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n");
```

However, this does not work when you also have `ext_superscript` enabled.

```rust
use comrak::*;

fn main() {
    let options = ComrakOptions {
        ext_superscript: true,
        ext_footnotes: true,
      ..ComrakOptions::default()
    };
    assert_eq!(markdown_to_html("Hi[^x].\n\n[^x]: A greeting.\n", &options),
               "<p>Hi<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">1</a></sup>.</p>\n<section class=\"footnotes\">\n<ol>\n<li id=\"fn1\">\n<p>A greeting. <a href=\"#fnref1\" class=\"footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n");
}
```

Instead of the expected string, you just get `"<p>Hi[^x].</p>\n"`.


Discussion:
Has any progress been made on this issue?
It's not something I've looked into; it is indeed still happening.
> It's not something I've looked into; it is indeed still happening.

Ah ok, I'll stay tuned and disable superscript for now.



Patch:
diff --git a/src/parser/inlines.rs b/src/parser/inlines.rs
index 46fdd157..21379808 100644
--- a/src/parser/inlines.rs
+++ b/src/parser/inlines.rs
@@ -26,6 +26,7 @@ pub struct Subject<'a: 'd, 'r, 'o, 'd, 'i, 'c: 'subj, 'subj> {
     delimiter_arena: &'d Arena<Delimiter<'a, 'd>>,
     last_delimiter: Option<&'d Delimiter<'a, 'd>>,
     brackets: Vec<Bracket<'a, 'd>>,
+    within_brackets: bool,
     pub backticks: [usize; MAXBACKTICKS + 1],
     pub scanned_for_backticks: bool,
     special_chars: [bool; 256],
@@ -74,6 +75,7 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
             delimiter_arena,
             last_delimiter: None,
             brackets: vec![],
+            within_brackets: false,
             backticks: [0; MAXBACKTICKS + 1],
             scanned_for_backticks: false,
             special_chars: [false; 256],
@@ -128,9 +130,13 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
                 self.pos += 1;
                 let inl = make_inline(self.arena, NodeValue::Text(b"[".to_vec()));
                 self.push_bracket(false, inl);
+                self.within_brackets = true;
                 Some(inl)
             }
-            ']' => self.handle_close_bracket(),
+            ']' => {
+                self.within_brackets = false;
+                self.handle_close_bracket()
+            }
             '!' => {
                 self.pos += 1;
                 if self.peek_char() == Some(&(b'[')) && self.peek_char_n(1) != Some(&(b'^')) {
@@ -143,7 +149,9 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
                 }
             }
             '~' if self.options.extension.strikethrough => Some(self.handle_delim(b'~')),
-            '^' if self.options.extension.superscript => Some(self.handle_delim(b'^')),
+            '^' if self.options.extension.superscript && !self.within_brackets => {
+                Some(self.handle_delim(b'^'))
+            }
             _ => {
                 let endpos = self.find_special_char();
                 let mut contents = self.input[self.pos..endpos].to_vec();
@@ -452,7 +460,11 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
     pub fn find_special_char(&self) -> usize {
         for n in self.pos..self.input.len() {
             if self.special_chars[self.input[n] as usize] {
-                return n;
+                if self.input[n] == b'^' && self.within_brackets {
+                    // NO OP
+                } else {
+                    return n;
+                }
             }
             if self.options.parse.smart && self.smart_chars[self.input[n] as usize] {
                 return n;


Test patch:
diff --git a/src/tests.rs b/src/tests.rs
index 252dc79f..a356b11b 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -1068,6 +1068,42 @@ fn footnote_in_table() {
         ));
 }
 
+#[test]
+fn footnote_with_superscript() {
+    html_opts!(
+        [extension.superscript, extension.footnotes],
+        concat!(
+            "Here is a footnote reference.[^1]\n",
+            "\n",
+            "Here is a longer footnote reference.[^ref]\n",
+            "\n",
+            "e = mc^2^.\n",
+            "\n",
+            "[^1]: Here is the footnote.\n",
+            "[^ref]: Here is another footnote.\n",
+        ),
+        concat!(
+            "<p>Here is a footnote reference.<sup class=\"footnote-ref\"><a href=\"#fn1\" \
+             id=\"fnref1\">1</a></sup></p>\n",
+            "<p>Here is a longer footnote reference.<sup class=\"footnote-ref\"><a href=\"#fn2\" \
+             id=\"fnref2\">2</a></sup></p>\n",
+            "<p>e = mc<sup>2</sup>.</p>\n",
+            "<section class=\"footnotes\">\n",
+            "<ol>\n",
+            "<li id=\"fn1\">\n",
+            "<p>Here is the footnote. <a href=\"#fnref1\" \
+             class=\"footnote-backref\">↩</a></p>\n",
+            "</li>\n",
+            "<li id=\"fn2\">\n",
+            "<p>Here is another footnote. <a href=\"#fnref2\" \
+             class=\"footnote-backref\">↩</a></p>\n",
+            "</li>\n",
+            "</ol>\n",
+            "</section>\n"
+        ),
+    );
+}
+
 #[test]
 fn regression_back_to_back_ranges() {
     html(