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