Skip to content

Commit 7bb1595

Browse files
add new spoiler extension
1 parent 7fc530e commit 7bb1595

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

extensions/core-extensions.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "autolink.h"
33
#include "mutex.h"
44
#include "strikethrough.h"
5+
#include "spoiler.h"
56
#include "table.h"
67
#include "tagfilter.h"
78
#include "tasklist.h"
@@ -15,6 +16,7 @@ static int core_extensions_registration(cmark_plugin *plugin) {
1516
cmark_plugin_register_syntax_extension(plugin, create_autolink_extension());
1617
cmark_plugin_register_syntax_extension(plugin, create_tagfilter_extension());
1718
cmark_plugin_register_syntax_extension(plugin, create_tasklist_extension());
19+
cmark_plugin_register_syntax_extension(plugin, create_spoiler_extension());
1820
return 1;
1921
}
2022

extensions/spoiler.c

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#include "spoiler.h"
2+
#include <parser.h>
3+
#include <render.h>
4+
5+
cmark_node_type CMARK_NODE_SPOILER;
6+
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) && (delims == 2)) {
31+
cmark_inline_parser_push_delimiter(inline_parser, character, left_flanking,
32+
right_flanking, res);
33+
}
34+
35+
return res;
36+
}
37+
38+
static delimiter *insert(cmark_syntax_extension *self, cmark_parser *parser,
39+
cmark_inline_parser *inline_parser, delimiter *opener,
40+
delimiter *closer) {
41+
cmark_node *spoiler;
42+
cmark_node *tmp, *next;
43+
delimiter *delim, *tmp_delim;
44+
delimiter *res = closer->next;
45+
46+
spoiler = opener->inl_text;
47+
48+
if (opener->inl_text->as.literal.len != closer->inl_text->as.literal.len)
49+
goto done;
50+
51+
if (!cmark_node_set_type(spoiler, CMARK_NODE_SPOILER))
52+
goto done;
53+
54+
cmark_node_set_syntax_extension(spoiler, self);
55+
56+
tmp = cmark_node_next(opener->inl_text);
57+
58+
while (tmp) {
59+
if (tmp == closer->inl_text)
60+
break;
61+
next = cmark_node_next(tmp);
62+
cmark_node_append_child(spoiler, tmp);
63+
tmp = next;
64+
}
65+
66+
spoiler->end_column = closer->inl_text->start_column + closer->inl_text->as.literal.len - 1;
67+
cmark_node_free(closer->inl_text);
68+
69+
delim = closer;
70+
while (delim != NULL && delim != opener) {
71+
tmp_delim = delim->previous;
72+
cmark_inline_parser_remove_delimiter(inline_parser, delim);
73+
delim = tmp_delim;
74+
}
75+
76+
cmark_inline_parser_remove_delimiter(inline_parser, opener);
77+
78+
done:
79+
return res;
80+
}
81+
82+
static const char *get_type_string(cmark_syntax_extension *extension,
83+
cmark_node *node) {
84+
return node->type == CMARK_NODE_SPOILER ? "spoiler" : "<unknown>";
85+
}
86+
87+
static int can_contain(cmark_syntax_extension *extension, cmark_node *node,
88+
cmark_node_type child_type) {
89+
if (node->type != CMARK_NODE_SPOILER)
90+
return false;
91+
92+
return CMARK_NODE_TYPE_INLINE_P(child_type);
93+
}
94+
95+
static void commonmark_render(cmark_syntax_extension *extension,
96+
cmark_renderer *renderer, cmark_node *node,
97+
cmark_event_type ev_type, int options) {
98+
renderer->out(renderer, node, "||", false, LITERAL);
99+
}
100+
101+
static void html_render(cmark_syntax_extension *extension,
102+
cmark_html_renderer *renderer, cmark_node *node,
103+
cmark_event_type ev_type, int options) {
104+
bool entering = (ev_type == CMARK_EVENT_ENTER);
105+
if (entering) {
106+
cmark_strbuf_puts(renderer->html, "<span class=\"spoiler\">");
107+
} else {
108+
cmark_strbuf_puts(renderer->html, "</span>");
109+
}
110+
}
111+
112+
static void plaintext_render(cmark_syntax_extension *extension,
113+
cmark_renderer *renderer, cmark_node *node,
114+
cmark_event_type ev_type, int options) {
115+
renderer->out(renderer, node, "~", false, LITERAL);
116+
}
117+
118+
cmark_syntax_extension *create_spoiler_extension(void) {
119+
cmark_syntax_extension *ext = cmark_syntax_extension_new("spoiler");
120+
cmark_llist *special_chars = NULL;
121+
122+
cmark_syntax_extension_set_get_type_string_func(ext, get_type_string);
123+
cmark_syntax_extension_set_can_contain_func(ext, can_contain);
124+
cmark_syntax_extension_set_commonmark_render_func(ext, commonmark_render);
125+
cmark_syntax_extension_set_html_render_func(ext, html_render);
126+
cmark_syntax_extension_set_plaintext_render_func(ext, plaintext_render);
127+
CMARK_NODE_SPOILER = cmark_syntax_extension_add_node(1);
128+
129+
cmark_syntax_extension_set_match_inline_func(ext, match);
130+
cmark_syntax_extension_set_inline_from_delim_func(ext, insert);
131+
132+
cmark_mem *mem = cmark_get_default_mem_allocator();
133+
special_chars = cmark_llist_append(mem, special_chars, (void *)'|');
134+
cmark_syntax_extension_set_special_inline_chars(ext, special_chars);
135+
136+
cmark_syntax_extension_set_emphasis(ext, 1);
137+
138+
return ext;
139+
}

extensions/spoiler.h

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

0 commit comments

Comments
 (0)