Skip to content

Commit bf76871

Browse files
committed
Fix footnote reference label text, and add multiple backrefs.
When a footnote is referenced multiple times, we now insert multiple backrefs linking back to each reference. In order to do this, we had to change how footnote ref link labels work away from an incrementing index, and instead use footnote reference label text *plus* an index.
1 parent 85d8952 commit bf76871

File tree

4 files changed

+58
-19
lines changed

4 files changed

+58
-19
lines changed

src/blocks.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,17 @@ static void process_footnotes(cmark_parser *parser) {
485485
if (!footnote->ix)
486486
footnote->ix = ++ix;
487487

488+
// keep track of a) how many times this footnote def has been
489+
// referenced, and b) which reference count this footnote ref is at
490+
// this is used by renderers when generating links and backreferences.
491+
cur->footnote.ix = ++footnote->node->footnote.count;
492+
493+
// store the footnote reference text label in the footnote ref's node's
494+
// `user_data`, so that renderers can use the label when generating
495+
// links and backreferences.
496+
cur->user_data = parser->mem->calloc(1, (sizeof(char) * cur->as.literal.len) + 1);
497+
memmove(cur->user_data, cur->as.literal.data, cur->as.literal.len);
498+
488499
char n[32];
489500
snprintf(n, sizeof(n), "%d", footnote->ix);
490501
cmark_chunk_free(parser->mem, &cur->as.literal);

src/commonmark.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
477477
case CMARK_NODE_FOOTNOTE_REFERENCE:
478478
if (entering) {
479479
LIT("[^");
480-
OUT(cmark_chunk_to_cstr(renderer->mem, &node->as.literal), false, LITERAL);
480+
OUT(node->user_data, false, LITERAL);
481481
LIT("]");
482482
}
483483
break;
@@ -486,9 +486,11 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
486486
if (entering) {
487487
renderer->footnote_ix += 1;
488488
LIT("[^");
489-
char n[32];
490-
snprintf(n, sizeof(n), "%d", renderer->footnote_ix);
491-
OUT(n, false, LITERAL);
489+
490+
char *str = renderer->mem->calloc(1, (sizeof(char) * node->as.literal.len) + 1);
491+
memmove(str, node->as.literal.data, node->as.literal.len);
492+
493+
OUT(str, false, LITERAL);
492494
LIT("]:\n");
493495

494496
cmark_strbuf_puts(renderer->prefix, " ");

src/html.c

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,31 @@ static void filter_html_block(cmark_html_renderer *renderer, uint8_t *data, size
5959
cmark_strbuf_put(html, data, (bufsize_t)len);
6060
}
6161

62-
static bool S_put_footnote_backref(cmark_html_renderer *renderer, cmark_strbuf *html) {
62+
static bool S_put_footnote_backref(cmark_html_renderer *renderer, cmark_strbuf *html, cmark_node *node) {
6363
if (renderer->written_footnote_ix >= renderer->footnote_ix)
6464
return false;
6565
renderer->written_footnote_ix = renderer->footnote_ix;
6666

67-
cmark_strbuf_puts(html, "<a href=\"#fnref");
68-
char n[32];
69-
snprintf(n, sizeof(n), "%d", renderer->footnote_ix);
70-
cmark_strbuf_puts(html, n);
67+
cmark_strbuf_puts(html, "<a href=\"#fnref:");
68+
cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len);
7169
cmark_strbuf_puts(html, "\" class=\"footnote-backref\">↩</a>");
7270

71+
if (node->footnote.count > 1)
72+
{
73+
for(int i = 2; i <= node->footnote.count; i++) {
74+
char n[32];
75+
snprintf(n, sizeof(n), "%d", i);
76+
77+
cmark_strbuf_puts(html, " <a href=\"#fnref:");
78+
cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len);
79+
cmark_strbuf_puts(html, ":");
80+
cmark_strbuf_put(html, (const unsigned char *)n, strlen(n));
81+
cmark_strbuf_puts(html, "\" class=\"footnote-backref\">↩<sup class=\"footnote-ref\">");
82+
cmark_strbuf_put(html, (const unsigned char *)n, strlen(n));
83+
cmark_strbuf_puts(html, "</sup></a>");
84+
}
85+
}
86+
7387
return true;
7488
}
7589

@@ -273,7 +287,7 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
273287
} else {
274288
if (parent->type == CMARK_NODE_FOOTNOTE_DEFINITION && node->next == NULL) {
275289
cmark_strbuf_putc(html, ' ');
276-
S_put_footnote_backref(renderer, html);
290+
S_put_footnote_backref(renderer, html, parent);
277291
}
278292
cmark_strbuf_puts(html, "</p>\n");
279293
}
@@ -395,13 +409,12 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
395409
cmark_strbuf_puts(html, "<section class=\"footnotes\">\n<ol>\n");
396410
}
397411
++renderer->footnote_ix;
398-
cmark_strbuf_puts(html, "<li id=\"fn");
399-
char n[32];
400-
snprintf(n, sizeof(n), "%d", renderer->footnote_ix);
401-
cmark_strbuf_puts(html, n);
412+
413+
cmark_strbuf_puts(html, "<li id=\"fn:");
414+
cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len);
402415
cmark_strbuf_puts(html, "\">\n");
403416
} else {
404-
if (S_put_footnote_backref(renderer, html)) {
417+
if (S_put_footnote_backref(renderer, html, node)) {
405418
cmark_strbuf_putc(html, '\n');
406419
}
407420
cmark_strbuf_puts(html, "</li>\n");
@@ -410,10 +423,18 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
410423

411424
case CMARK_NODE_FOOTNOTE_REFERENCE:
412425
if (entering) {
413-
cmark_strbuf_puts(html, "<sup class=\"footnote-ref\"><a href=\"#fn");
414-
cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len);
415-
cmark_strbuf_puts(html, "\" id=\"fnref");
416-
cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len);
426+
cmark_strbuf_puts(html, "<sup class=\"footnote-ref\"><a href=\"#fn:");
427+
cmark_strbuf_puts(html, node->user_data);
428+
cmark_strbuf_puts(html, "\" id=\"fnref:");
429+
cmark_strbuf_puts(html, node->user_data);
430+
431+
if (node->footnote.ix > 1) {
432+
char n[32];
433+
snprintf(n, sizeof(n), "%d", node->footnote.ix);
434+
cmark_strbuf_puts(html, ":");
435+
cmark_strbuf_put(html, (const unsigned char *)n, strlen(n));
436+
}
437+
417438
cmark_strbuf_puts(html, "\">");
418439
cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len);
419440
cmark_strbuf_puts(html, "</a></sup>");

src/node.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ struct cmark_node {
7676

7777
cmark_syntax_extension *extension;
7878

79+
union {
80+
int ix;
81+
int count;
82+
} footnote;
83+
7984
union {
8085
cmark_chunk literal;
8186
cmark_list list;

0 commit comments

Comments
 (0)