markly 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/markly/blocks.c +13 -2
- data/ext/markly/cmark-gfm_version.h +2 -2
- data/ext/markly/commonmark.c +14 -4
- data/ext/markly/ext_scanners.c +360 -640
- data/ext/markly/footnotes.c +23 -0
- data/ext/markly/footnotes.h +2 -0
- data/ext/markly/html.c +40 -19
- data/ext/markly/inlines.c +69 -11
- data/ext/markly/node.h +7 -0
- data/ext/markly/scanners.c +9484 -19346
- data/ext/markly/table.c +71 -52
- data/lib/markly/markly.bundle +0 -0
- data/lib/markly/renderer/generic.rb +131 -0
- data/lib/markly/renderer/html.rb +282 -0
- data/lib/markly/version.rb +1 -1
- data/lib/markly.rb +2 -2
- metadata +6 -6
- data/lib/markly/markly.so +0 -0
- data/lib/markly/renderer/html_renderer.rb +0 -281
- data/lib/markly/renderer.rb +0 -133
data/ext/markly/footnotes.c
CHANGED
@@ -38,3 +38,26 @@ void cmark_footnote_create(cmark_map *map, cmark_node *node) {
|
|
38
38
|
cmark_map *cmark_footnote_map_new(cmark_mem *mem) {
|
39
39
|
return cmark_map_new(mem, footnote_free);
|
40
40
|
}
|
41
|
+
|
42
|
+
// Before calling `cmark_map_free` on a map with `cmark_footnotes`, first
|
43
|
+
// unlink all of the footnote nodes before freeing their memory.
|
44
|
+
//
|
45
|
+
// Sometimes, two (unused) footnote nodes can end up referencing each other,
|
46
|
+
// which as they get freed up by calling `cmark_map_free` -> `footnote_free` ->
|
47
|
+
// etc, can lead to a use-after-free error.
|
48
|
+
//
|
49
|
+
// Better to `unlink` every footnote node first, setting their next, prev, and
|
50
|
+
// parent pointers to NULL, and only then walk thru & free them up.
|
51
|
+
void cmark_unlink_footnotes_map(cmark_map *map) {
|
52
|
+
cmark_map_entry *ref;
|
53
|
+
cmark_map_entry *next;
|
54
|
+
|
55
|
+
ref = map->refs;
|
56
|
+
while(ref) {
|
57
|
+
next = ref->next;
|
58
|
+
if (((cmark_footnote *)ref)->node) {
|
59
|
+
cmark_node_unlink(((cmark_footnote *)ref)->node);
|
60
|
+
}
|
61
|
+
ref = next;
|
62
|
+
}
|
63
|
+
}
|
data/ext/markly/footnotes.h
CHANGED
data/ext/markly/html.c
CHANGED
@@ -59,16 +59,30 @@ static void filter_html_block(cmark_html_renderer *renderer, uint8_t *data, size
|
|
59
59
|
cmark_strbuf_put(html, data, (bufsize_t)len);
|
60
60
|
}
|
61
61
|
|
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) {
|
63
63
|
if (renderer->written_footnote_ix >= renderer->footnote_ix)
|
64
64
|
return false;
|
65
65
|
renderer->written_footnote_ix = renderer->footnote_ix;
|
66
66
|
|
67
|
-
cmark_strbuf_puts(html, "<a href=\"#fnref");
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
67
|
+
cmark_strbuf_puts(html, "<a href=\"#fnref-");
|
68
|
+
houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
|
69
|
+
cmark_strbuf_puts(html, "\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩</a>");
|
70
|
+
|
71
|
+
if (node->footnote.def_count > 1)
|
72
|
+
{
|
73
|
+
for(int i = 2; i <= node->footnote.def_count; i++) {
|
74
|
+
char n[32];
|
75
|
+
snprintf(n, sizeof(n), "%d", i);
|
76
|
+
|
77
|
+
cmark_strbuf_puts(html, " <a href=\"#fnref-");
|
78
|
+
houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
|
79
|
+
cmark_strbuf_puts(html, "-");
|
80
|
+
cmark_strbuf_puts(html, n);
|
81
|
+
cmark_strbuf_puts(html, "\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩<sup class=\"footnote-ref\">");
|
82
|
+
cmark_strbuf_puts(html, n);
|
83
|
+
cmark_strbuf_puts(html, "</sup></a>");
|
84
|
+
}
|
85
|
+
}
|
72
86
|
|
73
87
|
return true;
|
74
88
|
}
|
@@ -273,7 +287,7 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
|
|
273
287
|
} else {
|
274
288
|
if (parent->type == CMARK_NODE_FOOTNOTE_DEFINITION && node->next == NULL) {
|
275
289
|
cmark_strbuf_putc(html, ' ');
|
276
|
-
S_put_footnote_backref(renderer, html);
|
290
|
+
S_put_footnote_backref(renderer, html, parent);
|
277
291
|
}
|
278
292
|
cmark_strbuf_puts(html, "</p>\n");
|
279
293
|
}
|
@@ -392,16 +406,15 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
|
|
392
406
|
case CMARK_NODE_FOOTNOTE_DEFINITION:
|
393
407
|
if (entering) {
|
394
408
|
if (renderer->footnote_ix == 0) {
|
395
|
-
cmark_strbuf_puts(html, "<section class=\"footnotes\">\n<ol>\n");
|
409
|
+
cmark_strbuf_puts(html, "<section class=\"footnotes\" data-footnotes>\n<ol>\n");
|
396
410
|
}
|
397
411
|
++renderer->footnote_ix;
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
cmark_strbuf_puts(html, n);
|
412
|
+
|
413
|
+
cmark_strbuf_puts(html, "<li id=\"fn-");
|
414
|
+
houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
|
402
415
|
cmark_strbuf_puts(html, "\">\n");
|
403
416
|
} else {
|
404
|
-
if (S_put_footnote_backref(renderer, html)) {
|
417
|
+
if (S_put_footnote_backref(renderer, html, node)) {
|
405
418
|
cmark_strbuf_putc(html, '\n');
|
406
419
|
}
|
407
420
|
cmark_strbuf_puts(html, "</li>\n");
|
@@ -410,12 +423,20 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
|
|
410
423
|
|
411
424
|
case CMARK_NODE_FOOTNOTE_REFERENCE:
|
412
425
|
if (entering) {
|
413
|
-
cmark_strbuf_puts(html, "<sup class=\"footnote-ref\"><a href=\"#fn");
|
414
|
-
|
415
|
-
cmark_strbuf_puts(html, "\" id=\"fnref");
|
416
|
-
|
417
|
-
|
418
|
-
|
426
|
+
cmark_strbuf_puts(html, "<sup class=\"footnote-ref\"><a href=\"#fn-");
|
427
|
+
houdini_escape_href(html, node->parent_footnote_def->as.literal.data, node->parent_footnote_def->as.literal.len);
|
428
|
+
cmark_strbuf_puts(html, "\" id=\"fnref-");
|
429
|
+
houdini_escape_href(html, node->parent_footnote_def->as.literal.data, node->parent_footnote_def->as.literal.len);
|
430
|
+
|
431
|
+
if (node->footnote.ref_ix > 1) {
|
432
|
+
char n[32];
|
433
|
+
snprintf(n, sizeof(n), "%d", node->footnote.ref_ix);
|
434
|
+
cmark_strbuf_puts(html, "-");
|
435
|
+
cmark_strbuf_puts(html, n);
|
436
|
+
}
|
437
|
+
|
438
|
+
cmark_strbuf_puts(html, "\" data-footnote-ref>");
|
439
|
+
houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
|
419
440
|
cmark_strbuf_puts(html, "</a></sup>");
|
420
441
|
}
|
421
442
|
break;
|
data/ext/markly/inlines.c
CHANGED
@@ -1137,19 +1137,77 @@ noMatch:
|
|
1137
1137
|
// What if we're a footnote link?
|
1138
1138
|
if (parser->options & CMARK_OPT_FOOTNOTES &&
|
1139
1139
|
opener->inl_text->next &&
|
1140
|
-
opener->inl_text->next->type == CMARK_NODE_TEXT
|
1141
|
-
|
1140
|
+
opener->inl_text->next->type == CMARK_NODE_TEXT) {
|
1141
|
+
|
1142
1142
|
cmark_chunk *literal = &opener->inl_text->next->as.literal;
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1143
|
+
|
1144
|
+
// look back to the opening '[', and skip ahead to the next character
|
1145
|
+
// if we're looking at a '[^' sequence, and there is other text or nodes
|
1146
|
+
// after the ^, let's call it a footnote reference.
|
1147
|
+
if ((literal->len > 0 && literal->data[0] == '^') && (literal->len > 1 || opener->inl_text->next->next)) {
|
1148
|
+
|
1149
|
+
// Before we got this far, the `handle_close_bracket` function may have
|
1150
|
+
// advanced the current state beyond our footnote's actual closing
|
1151
|
+
// bracket, ie if it went looking for a `link_label`.
|
1152
|
+
// Let's just rewind the subject's position:
|
1153
|
+
subj->pos = initial_pos;
|
1154
|
+
|
1155
|
+
cmark_node *fnref = make_simple(subj->mem, CMARK_NODE_FOOTNOTE_REFERENCE);
|
1156
|
+
|
1157
|
+
// the start and end of the footnote ref is the opening and closing brace
|
1158
|
+
// i.e. the subject's current position, and the opener's start_column
|
1159
|
+
int fnref_end_column = subj->pos + subj->column_offset + subj->block_offset;
|
1160
|
+
int fnref_start_column = opener->inl_text->start_column;
|
1161
|
+
|
1162
|
+
// any given node delineates a substring of the line being processed,
|
1163
|
+
// with the remainder of the line being pointed to thru its 'literal'
|
1164
|
+
// struct member.
|
1165
|
+
// here, we copy the literal's pointer, moving it past the '^' character
|
1166
|
+
// for a length equal to the size of footnote reference text.
|
1167
|
+
// i.e. end_col minus start_col, minus the [ and the ^ characters
|
1168
|
+
//
|
1169
|
+
// this copies the footnote reference string, even if between the
|
1170
|
+
// `opener` and the subject's current position there are other nodes
|
1171
|
+
//
|
1172
|
+
// (first, check for underflows)
|
1173
|
+
if ((fnref_start_column + 2) <= fnref_end_column) {
|
1174
|
+
fnref->as.literal = cmark_chunk_dup(literal, 1, (fnref_end_column - fnref_start_column) - 2);
|
1175
|
+
} else {
|
1176
|
+
fnref->as.literal = cmark_chunk_dup(literal, 1, 0);
|
1177
|
+
}
|
1178
|
+
|
1179
|
+
fnref->start_line = fnref->end_line = subj->line;
|
1180
|
+
fnref->start_column = fnref_start_column;
|
1181
|
+
fnref->end_column = fnref_end_column;
|
1182
|
+
|
1183
|
+
// we then replace the opener with this new fnref node, the net effect
|
1184
|
+
// being replacing the opening '[' text node with a `^footnote-ref]` node.
|
1185
|
+
cmark_node_insert_before(opener->inl_text, fnref);
|
1186
|
+
|
1152
1187
|
process_emphasis(parser, subj, opener->previous_delimiter);
|
1188
|
+
// sometimes, the footnote reference text gets parsed into multiple nodes
|
1189
|
+
// i.e. '[^example]' parsed into '[', '^exam', 'ple]'.
|
1190
|
+
// this happens for ex with the autolink extension. when the autolinker
|
1191
|
+
// finds the 'w' character, it will split the text into multiple nodes
|
1192
|
+
// in hopes of being able to match a 'www.' substring.
|
1193
|
+
//
|
1194
|
+
// because this function is called one character at a time via the
|
1195
|
+
// `parse_inlines` function, and the current subj->pos is pointing at the
|
1196
|
+
// closing ] brace, and because we copy all the text between the [ ]
|
1197
|
+
// braces, we should be able to safely ignore and delete any nodes after
|
1198
|
+
// the opener->inl_text->next.
|
1199
|
+
//
|
1200
|
+
// therefore, here we walk thru the list and free them all up
|
1201
|
+
cmark_node *next_node;
|
1202
|
+
cmark_node *current_node = opener->inl_text->next;
|
1203
|
+
while(current_node) {
|
1204
|
+
next_node = current_node->next;
|
1205
|
+
cmark_node_free(current_node);
|
1206
|
+
current_node = next_node;
|
1207
|
+
}
|
1208
|
+
|
1209
|
+
cmark_node_free(opener->inl_text);
|
1210
|
+
|
1153
1211
|
pop_bracket(subj);
|
1154
1212
|
return NULL;
|
1155
1213
|
}
|
data/ext/markly/node.h
CHANGED