Skip to content

Commit c55225f

Browse files
Yuki IzumiYuki Izumi
authored andcommitted
Strikethrough extension from c068469 reworked
This is quite straightforward; we do take care in other extensions (i.e. autolink) to ensure tildes are left for the strikethrough extension to consume.
1 parent 0445d10 commit c55225f

File tree

9 files changed

+197
-6
lines changed

9 files changed

+197
-6
lines changed

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ afl:
8282
-o test/afl_results \
8383
-x test/fuzzing_dictionary \
8484
-t 100 \
85-
$(CMARK) -e table $(CMARK_OPTS)
85+
$(CMARK) -e table -e strikethrough $(CMARK_OPTS)
8686

8787
libFuzzer:
8888
@[ -n "$(LIB_FUZZER_PATH)" ] || { echo '$$LIB_FUZZER_PATH not set'; false; }
@@ -164,9 +164,9 @@ $(ALLTESTS): $(SPEC) $(EXTENSIONS_SPEC)
164164
leakcheck: $(ALLTESTS)
165165
for format in html man xml latex commonmark; do \
166166
for opts in "" "--smart"; do \
167-
echo "cmark -t $$format -e table $$opts" ; \
168-
valgrind -q --leak-check=full --dsymutil=yes --suppressions=suppressions --error-exitcode=1 $(PROG) -t $$format -e table $$opts $(ALLTESTS) >/dev/null || exit 1;\
169-
done; \
167+
echo "cmark -t $$format -e table -e strikethrough $$opts" ; \
168+
valgrind -q --leak-check=full --dsymutil=yes --suppressions=suppressions --error-exitcode=1 $(PROG) -t $$format -e table -e strikethrough $$opts $(ALLTESTS) >/dev/null || exit 1;\
169+
done; \
170170
done;
171171

172172
fuzztest:

extensions/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ set(STATICLIBRARY "libcmarkextensions_static")
33
set(LIBRARY_SOURCES
44
core-extensions.c
55
table.c
6+
strikethrough.c
67
ext_scanners.c
78
ext_scanners.re
89
ext_scanners.h

extensions/core-extensions.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#include "core-extensions.h"
2+
#include "strikethrough.h"
23
#include "table.h"
34

45
int core_extensions_registration(cmark_plugin *plugin) {
56
cmark_plugin_register_syntax_extension(plugin, create_table_extension());
7+
cmark_plugin_register_syntax_extension(plugin,
8+
create_strikethrough_extension());
69
return 1;
710
}

extensions/strikethrough.c

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#include "strikethrough.h"
2+
#include <parser.h>
3+
4+
cmark_node_type CMARK_NODE_STRIKETHROUGH;
5+
6+
static cmark_node *match(cmark_syntax_extension *self, cmark_parser *parser,
7+
cmark_node *parent, unsigned char character,
8+
cmark_inline_parser *inline_parser) {
9+
cmark_node *res = NULL;
10+
int left_flanking, right_flanking, punct_before, punct_after;
11+
12+
if (character != '~')
13+
return NULL;
14+
15+
cmark_inline_parser_scan_delimiters(inline_parser, 100, '~', &left_flanking,
16+
&right_flanking, &punct_before,
17+
&punct_after);
18+
19+
res = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem);
20+
cmark_node_set_literal(res, "~");
21+
22+
if (left_flanking || right_flanking) {
23+
cmark_inline_parser_push_delimiter(inline_parser, character, left_flanking,
24+
right_flanking, res);
25+
}
26+
27+
return res;
28+
}
29+
30+
static delimiter *insert(cmark_syntax_extension *self, cmark_parser *parser,
31+
cmark_inline_parser *inline_parser, delimiter *opener,
32+
delimiter *closer) {
33+
cmark_node *strikethrough;
34+
cmark_node *tmp, *next;
35+
delimiter *delim, *tmp_delim;
36+
delimiter *res = closer->next;
37+
38+
strikethrough = opener->inl_text;
39+
40+
if (!cmark_node_set_type(strikethrough, CMARK_NODE_STRIKETHROUGH))
41+
goto done;
42+
43+
cmark_node_set_syntax_extension(strikethrough, self);
44+
45+
cmark_node_set_string_content(strikethrough, "~");
46+
tmp = cmark_node_next(opener->inl_text);
47+
48+
while (tmp) {
49+
if (tmp == closer->inl_text)
50+
break;
51+
next = cmark_node_next(tmp);
52+
cmark_node_append_child(strikethrough, tmp);
53+
tmp = next;
54+
}
55+
56+
cmark_node_free(closer->inl_text);
57+
58+
delim = closer;
59+
while (delim != NULL && delim != opener) {
60+
tmp_delim = delim->previous;
61+
cmark_inline_parser_remove_delimiter(inline_parser, delim);
62+
delim = tmp_delim;
63+
}
64+
65+
cmark_inline_parser_remove_delimiter(inline_parser, opener);
66+
67+
done:
68+
return res;
69+
}
70+
71+
static const char *get_type_string(cmark_syntax_extension *extension,
72+
cmark_node *node) {
73+
return node->type == CMARK_NODE_STRIKETHROUGH ? "strikethrough" : "<unknown>";
74+
}
75+
76+
static int can_contain(cmark_syntax_extension *extension, cmark_node *node,
77+
cmark_node_type child_type) {
78+
if (node->type != CMARK_NODE_STRIKETHROUGH)
79+
return false;
80+
81+
return CMARK_NODE_TYPE_INLINE_P(child_type);
82+
}
83+
84+
static void commonmark_render(cmark_syntax_extension *extension,
85+
cmark_renderer *renderer, cmark_node *node,
86+
cmark_event_type ev_type, int options) {
87+
renderer->out(renderer, cmark_node_get_string_content(node), false, LITERAL);
88+
}
89+
90+
static void latex_render(cmark_syntax_extension *extension,
91+
cmark_renderer *renderer, cmark_node *node,
92+
cmark_event_type ev_type, int options) {
93+
// requires \usepackage{ulem}
94+
bool entering = (ev_type == CMARK_EVENT_ENTER);
95+
if (entering) {
96+
renderer->out(renderer, "\\sout{", false, LITERAL);
97+
} else {
98+
renderer->out(renderer, "}", false, LITERAL);
99+
}
100+
}
101+
102+
static void man_render(cmark_syntax_extension *extension,
103+
cmark_renderer *renderer, cmark_node *node,
104+
cmark_event_type ev_type, int options) {
105+
bool entering = (ev_type == CMARK_EVENT_ENTER);
106+
if (entering) {
107+
renderer->cr(renderer);
108+
renderer->out(renderer, ".ST \"", false, LITERAL);
109+
} else {
110+
renderer->out(renderer, "\"", false, LITERAL);
111+
renderer->cr(renderer);
112+
}
113+
}
114+
115+
static void html_render(cmark_syntax_extension *extension,
116+
cmark_html_renderer *renderer, cmark_node *node,
117+
cmark_event_type ev_type, int options) {
118+
bool entering = (ev_type == CMARK_EVENT_ENTER);
119+
if (entering) {
120+
cmark_strbuf_puts(renderer->html, "<del>");
121+
} else {
122+
cmark_strbuf_puts(renderer->html, "</del>");
123+
}
124+
}
125+
126+
cmark_syntax_extension *create_strikethrough_extension(void) {
127+
cmark_syntax_extension *ext = cmark_syntax_extension_new("strikethrough");
128+
cmark_llist *special_chars = NULL;
129+
130+
cmark_syntax_extension_set_get_type_string_func(ext, get_type_string);
131+
cmark_syntax_extension_set_can_contain_func(ext, can_contain);
132+
cmark_syntax_extension_set_commonmark_render_func(ext, commonmark_render);
133+
cmark_syntax_extension_set_latex_render_func(ext, latex_render);
134+
cmark_syntax_extension_set_man_render_func(ext, man_render);
135+
cmark_syntax_extension_set_html_render_func(ext, html_render);
136+
CMARK_NODE_STRIKETHROUGH = cmark_syntax_extension_add_node(1);
137+
138+
cmark_syntax_extension_set_match_inline_func(ext, match);
139+
cmark_syntax_extension_set_inline_from_delim_func(ext, insert);
140+
141+
cmark_mem *mem = cmark_get_default_mem_allocator();
142+
special_chars = cmark_llist_append(mem, special_chars, (void *)'~');
143+
cmark_syntax_extension_set_special_inline_chars(ext, special_chars);
144+
145+
return ext;
146+
}

extensions/strikethrough.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifndef STRIKETHROUGH_H
2+
#define STRIKETHROUGH_H
3+
4+
#include "core-extensions.h"
5+
6+
extern cmark_node_type CMARK_NODE_STRIKETHROUGH;
7+
cmark_syntax_extension *create_strikethrough_extension(void);
8+
9+
#endif

extensions/table.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <references.h>
55

66
#include "ext_scanners.h"
7+
#include "strikethrough.h"
78
#include "table.h"
89

910
static cmark_node_type CMARK_NODE_TABLE, CMARK_NODE_TABLE_ROW,
@@ -442,6 +443,7 @@ static int can_contain(cmark_syntax_extension *extension, cmark_node *node,
442443
return child_type == CMARK_NODE_TEXT || child_type == CMARK_NODE_CODE ||
443444
child_type == CMARK_NODE_EMPH || child_type == CMARK_NODE_STRONG ||
444445
child_type == CMARK_NODE_LINK || child_type == CMARK_NODE_IMAGE ||
446+
child_type == CMARK_NODE_STRIKETHROUGH ||
445447
child_type == CMARK_NODE_HTML_INLINE;
446448
}
447449
return false;

test/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@ IF (PYTHONINTERP_FOUND)
6363
)
6464

6565
add_test(extensions_executable
66-
${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/spec_tests.py" "--no-normalize" "--spec" "${CMAKE_CURRENT_SOURCE_DIR}/extensions.txt" "--program" "${CMAKE_CURRENT_BINARY_DIR}/../src/cmark -e table"
66+
${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/spec_tests.py" "--no-normalize" "--spec" "${CMAKE_CURRENT_SOURCE_DIR}/extensions.txt" "--program" "${CMAKE_CURRENT_BINARY_DIR}/../src/cmark -e table -e strikethrough"
6767
)
6868

6969
add_test(roundtrip_extensions_executable
7070
${PYTHON_EXECUTABLE}
7171
"${CMAKE_CURRENT_SOURCE_DIR}/roundtrip_tests.py"
7272
"--spec" "${CMAKE_CURRENT_SOURCE_DIR}/extensions.txt"
73-
"--program" "${CMAKE_CURRENT_BINARY_DIR}/../src/cmark -e table"
73+
"--program" "${CMAKE_CURRENT_BINARY_DIR}/../src/cmark -e table -e strikethrough"
7474
)
7575

7676
add_test(regressiontest_executable

test/afl_test_cases/test.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,6 @@ cb
3636
| --- | --- |
3737
| c | `d|` \| e |
3838

39+
google ~~yahoo~~
40+
3941
[f]: /u "t"

test/extensions.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,3 +358,31 @@ This shouldn't assert.
358358
<td> ok <br> sure </td>
359359
</tr></tbody></table>
360360
````````````````````````````````
361+
362+
363+
## Strikethroughs
364+
365+
A well-formed strikethrough.
366+
367+
```````````````````````````````` example
368+
A proper ~strikethrough~.
369+
.
370+
<p>A proper <del>strikethrough</del>.</p>
371+
````````````````````````````````
372+
373+
Some strikethrough edge cases.
374+
375+
```````````````````````````````` example
376+
These are ~not strikethroughs.
377+
378+
No, they are not~
379+
380+
This ~is ~ legit~ isn't ~ legit.
381+
382+
This is just ~~~~~one~~~~~ huge strikethrough.
383+
.
384+
<p>These are ~not strikethroughs.</p>
385+
<p>No, they are not~</p>
386+
<p>This <del>is ~ legit</del> isn't ~ legit.</p>
387+
<p>This is just <del>one</del> huge strikethrough.</p>
388+
````````````````````````````````

0 commit comments

Comments
 (0)