markly 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/bin/markly +94 -0
  3. data/ext/markly/arena.c +103 -0
  4. data/ext/markly/autolink.c +425 -0
  5. data/ext/markly/autolink.h +8 -0
  6. data/ext/markly/blocks.c +1585 -0
  7. data/ext/markly/buffer.c +278 -0
  8. data/ext/markly/buffer.h +116 -0
  9. data/ext/markly/case_fold_switch.inc +4327 -0
  10. data/ext/markly/chunk.h +135 -0
  11. data/ext/markly/cmark-gfm-core-extensions.h +54 -0
  12. data/ext/markly/cmark-gfm-extension_api.h +736 -0
  13. data/ext/markly/cmark-gfm-extensions_export.h +42 -0
  14. data/ext/markly/cmark-gfm.h +817 -0
  15. data/ext/markly/cmark-gfm_export.h +42 -0
  16. data/ext/markly/cmark-gfm_version.h +7 -0
  17. data/ext/markly/cmark.c +55 -0
  18. data/ext/markly/cmark_ctype.c +44 -0
  19. data/ext/markly/cmark_ctype.h +33 -0
  20. data/ext/markly/commonmark.c +519 -0
  21. data/ext/markly/config.h +76 -0
  22. data/ext/markly/core-extensions.c +27 -0
  23. data/ext/markly/entities.inc +2138 -0
  24. data/ext/markly/ext_scanners.c +1159 -0
  25. data/ext/markly/ext_scanners.h +24 -0
  26. data/ext/markly/extconf.rb +7 -0
  27. data/ext/markly/footnotes.c +40 -0
  28. data/ext/markly/footnotes.h +25 -0
  29. data/ext/markly/houdini.h +57 -0
  30. data/ext/markly/houdini_href_e.c +100 -0
  31. data/ext/markly/houdini_html_e.c +66 -0
  32. data/ext/markly/houdini_html_u.c +149 -0
  33. data/ext/markly/html.c +465 -0
  34. data/ext/markly/html.h +27 -0
  35. data/ext/markly/inlines.c +1633 -0
  36. data/ext/markly/inlines.h +29 -0
  37. data/ext/markly/iterator.c +159 -0
  38. data/ext/markly/iterator.h +26 -0
  39. data/ext/markly/latex.c +466 -0
  40. data/ext/markly/linked_list.c +37 -0
  41. data/ext/markly/man.c +278 -0
  42. data/ext/markly/map.c +122 -0
  43. data/ext/markly/map.h +41 -0
  44. data/ext/markly/markly.c +1226 -0
  45. data/ext/markly/markly.h +16 -0
  46. data/ext/markly/node.c +979 -0
  47. data/ext/markly/node.h +118 -0
  48. data/ext/markly/parser.h +58 -0
  49. data/ext/markly/plaintext.c +235 -0
  50. data/ext/markly/plugin.c +36 -0
  51. data/ext/markly/plugin.h +34 -0
  52. data/ext/markly/references.c +42 -0
  53. data/ext/markly/references.h +26 -0
  54. data/ext/markly/registry.c +63 -0
  55. data/ext/markly/registry.h +24 -0
  56. data/ext/markly/render.c +205 -0
  57. data/ext/markly/render.h +62 -0
  58. data/ext/markly/scanners.c +20382 -0
  59. data/ext/markly/scanners.h +62 -0
  60. data/ext/markly/scanners.re +326 -0
  61. data/ext/markly/strikethrough.c +167 -0
  62. data/ext/markly/strikethrough.h +9 -0
  63. data/ext/markly/syntax_extension.c +149 -0
  64. data/ext/markly/syntax_extension.h +34 -0
  65. data/ext/markly/table.c +803 -0
  66. data/ext/markly/table.h +12 -0
  67. data/ext/markly/tagfilter.c +60 -0
  68. data/ext/markly/tagfilter.h +8 -0
  69. data/ext/markly/tasklist.c +156 -0
  70. data/ext/markly/tasklist.h +8 -0
  71. data/ext/markly/utf8.c +317 -0
  72. data/ext/markly/utf8.h +35 -0
  73. data/ext/markly/xml.c +181 -0
  74. data/lib/markly.rb +43 -0
  75. data/lib/markly/flags.rb +37 -0
  76. data/lib/markly/markly.so +0 -0
  77. data/lib/markly/node.rb +70 -0
  78. data/lib/markly/node/inspect.rb +59 -0
  79. data/lib/markly/renderer.rb +133 -0
  80. data/lib/markly/renderer/html_renderer.rb +252 -0
  81. data/lib/markly/version.rb +5 -0
  82. metadata +211 -0
@@ -0,0 +1,42 @@
1
+
2
+ #ifndef CMARK_GFM_EXPORT_H
3
+ #define CMARK_GFM_EXPORT_H
4
+
5
+ #ifdef CMARK_GFM_STATIC_DEFINE
6
+ # define CMARK_GFM_EXPORT
7
+ # define CMARK_GFM_NO_EXPORT
8
+ #else
9
+ # ifndef CMARK_GFM_EXPORT
10
+ # ifdef libcmark_gfm_EXPORTS
11
+ /* We are building this library */
12
+ # define CMARK_GFM_EXPORT __attribute__((visibility("default")))
13
+ # else
14
+ /* We are using this library */
15
+ # define CMARK_GFM_EXPORT __attribute__((visibility("default")))
16
+ # endif
17
+ # endif
18
+
19
+ # ifndef CMARK_GFM_NO_EXPORT
20
+ # define CMARK_GFM_NO_EXPORT __attribute__((visibility("hidden")))
21
+ # endif
22
+ #endif
23
+
24
+ #ifndef CMARK_GFM_DEPRECATED
25
+ # define CMARK_GFM_DEPRECATED __attribute__ ((__deprecated__))
26
+ #endif
27
+
28
+ #ifndef CMARK_GFM_DEPRECATED_EXPORT
29
+ # define CMARK_GFM_DEPRECATED_EXPORT CMARK_GFM_EXPORT CMARK_GFM_DEPRECATED
30
+ #endif
31
+
32
+ #ifndef CMARK_GFM_DEPRECATED_NO_EXPORT
33
+ # define CMARK_GFM_DEPRECATED_NO_EXPORT CMARK_GFM_NO_EXPORT CMARK_GFM_DEPRECATED
34
+ #endif
35
+
36
+ #if 0 /* DEFINE_NO_DEPRECATED */
37
+ # ifndef CMARK_GFM_NO_DEPRECATED
38
+ # define CMARK_GFM_NO_DEPRECATED
39
+ # endif
40
+ #endif
41
+
42
+ #endif /* CMARK_GFM_EXPORT_H */
@@ -0,0 +1,7 @@
1
+ #ifndef CMARK_GFM_VERSION_H
2
+ #define CMARK_GFM_VERSION_H
3
+
4
+ #define CMARK_GFM_VERSION ((0 << 24) | (29 << 16) | (0 << 8) | 0)
5
+ #define CMARK_GFM_VERSION_STRING "0.29.0.gfm.0"
6
+
7
+ #endif
@@ -0,0 +1,55 @@
1
+ #include <stdlib.h>
2
+ #include <assert.h>
3
+ #include <stdio.h>
4
+ #include "registry.h"
5
+ #include "node.h"
6
+ #include "houdini.h"
7
+ #include "cmark-gfm.h"
8
+ #include "buffer.h"
9
+
10
+ cmark_node_type CMARK_NODE_LAST_BLOCK = CMARK_NODE_FOOTNOTE_DEFINITION;
11
+ cmark_node_type CMARK_NODE_LAST_INLINE = CMARK_NODE_FOOTNOTE_REFERENCE;
12
+
13
+ int cmark_version() { return CMARK_GFM_VERSION; }
14
+
15
+ const char *cmark_version_string() { return CMARK_GFM_VERSION_STRING; }
16
+
17
+ static void *xcalloc(size_t nmem, size_t size) {
18
+ void *ptr = calloc(nmem, size);
19
+ if (!ptr) {
20
+ fprintf(stderr, "[cmark] calloc returned null pointer, aborting\n");
21
+ abort();
22
+ }
23
+ return ptr;
24
+ }
25
+
26
+ static void *xrealloc(void *ptr, size_t size) {
27
+ void *new_ptr = realloc(ptr, size);
28
+ if (!new_ptr) {
29
+ fprintf(stderr, "[cmark] realloc returned null pointer, aborting\n");
30
+ abort();
31
+ }
32
+ return new_ptr;
33
+ }
34
+
35
+ static void xfree(void *ptr) {
36
+ free(ptr);
37
+ }
38
+
39
+ cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR = {xcalloc, xrealloc, xfree};
40
+
41
+ cmark_mem *cmark_get_default_mem_allocator() {
42
+ return &CMARK_DEFAULT_MEM_ALLOCATOR;
43
+ }
44
+
45
+ char *cmark_markdown_to_html(const char *text, size_t len, int options) {
46
+ cmark_node *doc;
47
+ char *result;
48
+
49
+ doc = cmark_parse_document(text, len, options);
50
+
51
+ result = cmark_render_html(doc, options, NULL);
52
+ cmark_node_free(doc);
53
+
54
+ return result;
55
+ }
@@ -0,0 +1,44 @@
1
+ #include <stdint.h>
2
+
3
+ #include "cmark_ctype.h"
4
+
5
+ /** 1 = space, 2 = punct, 3 = digit, 4 = alpha, 0 = other
6
+ */
7
+ static const uint8_t cmark_ctype_class[256] = {
8
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
9
+ /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
10
+ /* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
11
+ /* 2 */ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
12
+ /* 3 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2,
13
+ /* 4 */ 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
14
+ /* 5 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2,
15
+ /* 6 */ 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
16
+ /* 7 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 0,
17
+ /* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
18
+ /* 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
19
+ /* a */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
20
+ /* b */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
21
+ /* c */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
22
+ /* d */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
23
+ /* e */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24
+ /* f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
25
+
26
+ /**
27
+ * Returns 1 if c is a "whitespace" character as defined by the spec.
28
+ */
29
+ int cmark_isspace(char c) { return cmark_ctype_class[(uint8_t)c] == 1; }
30
+
31
+ /**
32
+ * Returns 1 if c is an ascii punctuation character.
33
+ */
34
+ int cmark_ispunct(char c) { return cmark_ctype_class[(uint8_t)c] == 2; }
35
+
36
+ int cmark_isalnum(char c) {
37
+ uint8_t result;
38
+ result = cmark_ctype_class[(uint8_t)c];
39
+ return (result == 3 || result == 4);
40
+ }
41
+
42
+ int cmark_isdigit(char c) { return cmark_ctype_class[(uint8_t)c] == 3; }
43
+
44
+ int cmark_isalpha(char c) { return cmark_ctype_class[(uint8_t)c] == 4; }
@@ -0,0 +1,33 @@
1
+ #ifndef CMARK_CMARK_CTYPE_H
2
+ #define CMARK_CMARK_CTYPE_H
3
+
4
+ #ifdef __cplusplus
5
+ extern "C" {
6
+ #endif
7
+
8
+ #include "cmark-gfm_export.h"
9
+
10
+ /** Locale-independent versions of functions from ctype.h.
11
+ * We want cmark to behave the same no matter what the system locale.
12
+ */
13
+
14
+ CMARK_GFM_EXPORT
15
+ int cmark_isspace(char c);
16
+
17
+ CMARK_GFM_EXPORT
18
+ int cmark_ispunct(char c);
19
+
20
+ CMARK_GFM_EXPORT
21
+ int cmark_isalnum(char c);
22
+
23
+ CMARK_GFM_EXPORT
24
+ int cmark_isdigit(char c);
25
+
26
+ CMARK_GFM_EXPORT
27
+ int cmark_isalpha(char c);
28
+
29
+ #ifdef __cplusplus
30
+ }
31
+ #endif
32
+
33
+ #endif
@@ -0,0 +1,519 @@
1
+ #include <stdlib.h>
2
+ #include <stdio.h>
3
+ #include <string.h>
4
+ #include <stdint.h>
5
+ #include <assert.h>
6
+
7
+ #include "config.h"
8
+ #include "cmark-gfm.h"
9
+ #include "node.h"
10
+ #include "buffer.h"
11
+ #include "utf8.h"
12
+ #include "scanners.h"
13
+ #include "render.h"
14
+ #include "syntax_extension.h"
15
+
16
+ #define OUT(s, wrap, escaping) renderer->out(renderer, node, s, wrap, escaping)
17
+ #define LIT(s) renderer->out(renderer, node, s, false, LITERAL)
18
+ #define CR() renderer->cr(renderer)
19
+ #define BLANKLINE() renderer->blankline(renderer)
20
+ #define ENCODED_SIZE 20
21
+ #define LISTMARKER_SIZE 20
22
+
23
+ // Functions to convert cmark_nodes to commonmark strings.
24
+
25
+ static CMARK_INLINE void outc(cmark_renderer *renderer, cmark_node *node,
26
+ cmark_escaping escape,
27
+ int32_t c, unsigned char nextc) {
28
+ bool needs_escaping = false;
29
+ bool follows_digit =
30
+ renderer->buffer->size > 0 &&
31
+ cmark_isdigit(renderer->buffer->ptr[renderer->buffer->size - 1]);
32
+ char encoded[ENCODED_SIZE];
33
+
34
+ needs_escaping =
35
+ c < 0x80 && escape != LITERAL &&
36
+ ((escape == NORMAL &&
37
+ (c < 0x20 ||
38
+ c == '*' || c == '_' || c == '[' || c == ']' || c == '#' || c == '<' ||
39
+ c == '>' || c == '\\' || c == '`' || c == '~' || c == '!' ||
40
+ (c == '&' && cmark_isalpha(nextc)) || (c == '!' && nextc == '[') ||
41
+ (renderer->begin_content && (c == '-' || c == '+' || c == '=') &&
42
+ // begin_content doesn't get set to false til we've passed digits
43
+ // at the beginning of line, so...
44
+ !follows_digit) ||
45
+ (renderer->begin_content && (c == '.' || c == ')') && follows_digit &&
46
+ (nextc == 0 || cmark_isspace(nextc))))) ||
47
+ (escape == URL &&
48
+ (c == '`' || c == '<' || c == '>' || cmark_isspace((char)c) || c == '\\' ||
49
+ c == ')' || c == '(')) ||
50
+ (escape == TITLE &&
51
+ (c == '`' || c == '<' || c == '>' || c == '"' || c == '\\')));
52
+
53
+ if (needs_escaping) {
54
+ if (escape == URL && cmark_isspace((char)c)) {
55
+ // use percent encoding for spaces
56
+ snprintf(encoded, ENCODED_SIZE, "%%%2X", c);
57
+ cmark_strbuf_puts(renderer->buffer, encoded);
58
+ renderer->column += 3;
59
+ } else if (cmark_ispunct((char)c)) {
60
+ cmark_render_ascii(renderer, "\\");
61
+ cmark_render_code_point(renderer, c);
62
+ } else { // render as entity
63
+ snprintf(encoded, ENCODED_SIZE, "&#%d;", c);
64
+ cmark_strbuf_puts(renderer->buffer, encoded);
65
+ renderer->column += (int)strlen(encoded);
66
+ }
67
+ } else {
68
+ cmark_render_code_point(renderer, c);
69
+ }
70
+ }
71
+
72
+ static int longest_backtick_sequence(const char *code) {
73
+ int longest = 0;
74
+ int current = 0;
75
+ size_t i = 0;
76
+ size_t code_len = strlen(code);
77
+ while (i <= code_len) {
78
+ if (code[i] == '`') {
79
+ current++;
80
+ } else {
81
+ if (current > longest) {
82
+ longest = current;
83
+ }
84
+ current = 0;
85
+ }
86
+ i++;
87
+ }
88
+ return longest;
89
+ }
90
+
91
+ static int shortest_unused_backtick_sequence(const char *code) {
92
+ // note: if the shortest sequence is >= 32, this returns 32
93
+ // so as not to overflow the bit array.
94
+ uint32_t used = 1;
95
+ int current = 0;
96
+ size_t i = 0;
97
+ size_t code_len = strlen(code);
98
+ while (i <= code_len) {
99
+ if (code[i] == '`') {
100
+ current++;
101
+ } else {
102
+ if (current > 0 && current < 32) {
103
+ used |= (1U << current);
104
+ }
105
+ current = 0;
106
+ }
107
+ i++;
108
+ }
109
+ // return number of first bit that is 0:
110
+ i = 0;
111
+ while (i < 32 && used & 1) {
112
+ used = used >> 1;
113
+ i++;
114
+ }
115
+ return (int)i;
116
+ }
117
+
118
+ static bool is_autolink(cmark_node *node) {
119
+ cmark_chunk *title;
120
+ cmark_chunk *url;
121
+ cmark_node *link_text;
122
+ char *realurl;
123
+ int realurllen;
124
+
125
+ if (node->type != CMARK_NODE_LINK) {
126
+ return false;
127
+ }
128
+
129
+ url = &node->as.link.url;
130
+ if (url->len == 0 || scan_scheme(url, 0) == 0) {
131
+ return false;
132
+ }
133
+
134
+ title = &node->as.link.title;
135
+ // if it has a title, we can't treat it as an autolink:
136
+ if (title->len > 0) {
137
+ return false;
138
+ }
139
+
140
+ link_text = node->first_child;
141
+ if (link_text == NULL) {
142
+ return false;
143
+ }
144
+ cmark_consolidate_text_nodes(link_text);
145
+ realurl = (char *)url->data;
146
+ realurllen = url->len;
147
+ if (strncmp(realurl, "mailto:", 7) == 0) {
148
+ realurl += 7;
149
+ realurllen -= 7;
150
+ }
151
+ return (realurllen == link_text->as.literal.len &&
152
+ strncmp(realurl, (char *)link_text->as.literal.data,
153
+ link_text->as.literal.len) == 0);
154
+ }
155
+
156
+ // if node is a block node, returns node.
157
+ // otherwise returns first block-level node that is an ancestor of node.
158
+ // if there is no block-level ancestor, returns NULL.
159
+ static cmark_node *get_containing_block(cmark_node *node) {
160
+ while (node) {
161
+ if (CMARK_NODE_BLOCK_P(node)) {
162
+ return node;
163
+ } else {
164
+ node = node->parent;
165
+ }
166
+ }
167
+ return NULL;
168
+ }
169
+
170
+ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
171
+ cmark_event_type ev_type, int options) {
172
+ cmark_node *tmp;
173
+ int list_number;
174
+ cmark_delim_type list_delim;
175
+ int numticks;
176
+ bool extra_spaces;
177
+ int i;
178
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
179
+ const char *info, *code, *title;
180
+ char fencechar[2] = {'\0', '\0'};
181
+ size_t info_len, code_len;
182
+ char listmarker[LISTMARKER_SIZE];
183
+ char *emph_delim;
184
+ bool first_in_list_item;
185
+ bufsize_t marker_width;
186
+ bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options) &&
187
+ !(CMARK_OPT_HARDBREAKS & options);
188
+
189
+ // Don't adjust tight list status til we've started the list.
190
+ // Otherwise we loose the blank line between a paragraph and
191
+ // a following list.
192
+ if (!(node->type == CMARK_NODE_ITEM && node->prev == NULL && entering)) {
193
+ tmp = get_containing_block(node);
194
+ renderer->in_tight_list_item =
195
+ tmp && // tmp might be NULL if there is no containing block
196
+ ((tmp->type == CMARK_NODE_ITEM &&
197
+ cmark_node_get_list_tight(tmp->parent)) ||
198
+ (tmp && tmp->parent && tmp->parent->type == CMARK_NODE_ITEM &&
199
+ cmark_node_get_list_tight(tmp->parent->parent)));
200
+ }
201
+
202
+ if (node->extension && node->extension->commonmark_render_func) {
203
+ node->extension->commonmark_render_func(node->extension, renderer, node, ev_type, options);
204
+ return 1;
205
+ }
206
+
207
+ switch (node->type) {
208
+ case CMARK_NODE_DOCUMENT:
209
+ break;
210
+
211
+ case CMARK_NODE_BLOCK_QUOTE:
212
+ if (entering) {
213
+ LIT("> ");
214
+ renderer->begin_content = true;
215
+ cmark_strbuf_puts(renderer->prefix, "> ");
216
+ } else {
217
+ cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 2);
218
+ BLANKLINE();
219
+ }
220
+ break;
221
+
222
+ case CMARK_NODE_LIST:
223
+ if (!entering && node->next && (node->next->type == CMARK_NODE_CODE_BLOCK ||
224
+ node->next->type == CMARK_NODE_LIST)) {
225
+ // this ensures that a following indented code block or list will be
226
+ // inteprereted correctly.
227
+ CR();
228
+ LIT("<!-- end list -->");
229
+ BLANKLINE();
230
+ }
231
+ break;
232
+
233
+ case CMARK_NODE_ITEM:
234
+ if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
235
+ marker_width = 4;
236
+ } else {
237
+ list_number = cmark_node_get_list_start(node->parent);
238
+ list_delim = cmark_node_get_list_delim(node->parent);
239
+ tmp = node;
240
+ while (tmp->prev) {
241
+ tmp = tmp->prev;
242
+ list_number += 1;
243
+ }
244
+ // we ensure a width of at least 4 so
245
+ // we get nice transition from single digits
246
+ // to double
247
+ snprintf(listmarker, LISTMARKER_SIZE, "%d%s%s", list_number,
248
+ list_delim == CMARK_PAREN_DELIM ? ")" : ".",
249
+ list_number < 10 ? " " : " ");
250
+ marker_width = (bufsize_t)strlen(listmarker);
251
+ }
252
+ if (entering) {
253
+ if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
254
+ LIT(" - ");
255
+ renderer->begin_content = true;
256
+ } else {
257
+ LIT(listmarker);
258
+ renderer->begin_content = true;
259
+ }
260
+ for (i = marker_width; i--;) {
261
+ cmark_strbuf_putc(renderer->prefix, ' ');
262
+ }
263
+ } else {
264
+ cmark_strbuf_truncate(renderer->prefix,
265
+ renderer->prefix->size - marker_width);
266
+ CR();
267
+ }
268
+ break;
269
+
270
+ case CMARK_NODE_HEADING:
271
+ if (entering) {
272
+ for (i = cmark_node_get_heading_level(node); i > 0; i--) {
273
+ LIT("#");
274
+ }
275
+ LIT(" ");
276
+ renderer->begin_content = true;
277
+ renderer->no_linebreaks = true;
278
+ } else {
279
+ renderer->no_linebreaks = false;
280
+ BLANKLINE();
281
+ }
282
+ break;
283
+
284
+ case CMARK_NODE_CODE_BLOCK:
285
+ first_in_list_item = node->prev == NULL && node->parent &&
286
+ node->parent->type == CMARK_NODE_ITEM;
287
+
288
+ if (!first_in_list_item) {
289
+ BLANKLINE();
290
+ }
291
+ info = cmark_node_get_fence_info(node);
292
+ info_len = strlen(info);
293
+ fencechar[0] = strchr(info, '`') == NULL ? '`' : '~';
294
+ code = cmark_node_get_literal(node);
295
+ code_len = strlen(code);
296
+ // use indented form if no info, and code doesn't
297
+ // begin or end with a blank line, and code isn't
298
+ // first thing in a list item
299
+ if (info_len == 0 && (code_len > 2 && !cmark_isspace(code[0]) &&
300
+ !(cmark_isspace(code[code_len - 1]) &&
301
+ cmark_isspace(code[code_len - 2]))) &&
302
+ !first_in_list_item) {
303
+ LIT(" ");
304
+ cmark_strbuf_puts(renderer->prefix, " ");
305
+ OUT(cmark_node_get_literal(node), false, LITERAL);
306
+ cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 4);
307
+ } else {
308
+ numticks = longest_backtick_sequence(code) + 1;
309
+ if (numticks < 3) {
310
+ numticks = 3;
311
+ }
312
+ for (i = 0; i < numticks; i++) {
313
+ LIT(fencechar);
314
+ }
315
+ LIT(" ");
316
+ OUT(info, false, LITERAL);
317
+ CR();
318
+ OUT(cmark_node_get_literal(node), false, LITERAL);
319
+ CR();
320
+ for (i = 0; i < numticks; i++) {
321
+ LIT(fencechar);
322
+ }
323
+ }
324
+ BLANKLINE();
325
+ break;
326
+
327
+ case CMARK_NODE_HTML_BLOCK:
328
+ BLANKLINE();
329
+ OUT(cmark_node_get_literal(node), false, LITERAL);
330
+ BLANKLINE();
331
+ break;
332
+
333
+ case CMARK_NODE_CUSTOM_BLOCK:
334
+ BLANKLINE();
335
+ OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
336
+ false, LITERAL);
337
+ BLANKLINE();
338
+ break;
339
+
340
+ case CMARK_NODE_THEMATIC_BREAK:
341
+ BLANKLINE();
342
+ LIT("-----");
343
+ BLANKLINE();
344
+ break;
345
+
346
+ case CMARK_NODE_PARAGRAPH:
347
+ if (!entering) {
348
+ BLANKLINE();
349
+ }
350
+ break;
351
+
352
+ case CMARK_NODE_TEXT:
353
+ OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
354
+ break;
355
+
356
+ case CMARK_NODE_LINEBREAK:
357
+ if (!(CMARK_OPT_HARDBREAKS & options)) {
358
+ LIT(" ");
359
+ }
360
+ CR();
361
+ break;
362
+
363
+ case CMARK_NODE_SOFTBREAK:
364
+ if (CMARK_OPT_HARDBREAKS & options) {
365
+ LIT(" ");
366
+ CR();
367
+ } else if (!renderer->no_linebreaks && renderer->width == 0 &&
368
+ !(CMARK_OPT_HARDBREAKS & options) &&
369
+ !(CMARK_OPT_NOBREAKS & options)) {
370
+ CR();
371
+ } else {
372
+ OUT(" ", allow_wrap, LITERAL);
373
+ }
374
+ break;
375
+
376
+ case CMARK_NODE_CODE:
377
+ code = cmark_node_get_literal(node);
378
+ code_len = strlen(code);
379
+ numticks = shortest_unused_backtick_sequence(code);
380
+ extra_spaces = code_len == 0 ||
381
+ code[0] == '`' || code[code_len - 1] == '`' ||
382
+ code[0] == ' ' || code[code_len - 1] == ' ';
383
+ for (i = 0; i < numticks; i++) {
384
+ LIT("`");
385
+ }
386
+ if (extra_spaces) {
387
+ LIT(" ");
388
+ }
389
+ OUT(cmark_node_get_literal(node), allow_wrap, LITERAL);
390
+ if (extra_spaces) {
391
+ LIT(" ");
392
+ }
393
+ for (i = 0; i < numticks; i++) {
394
+ LIT("`");
395
+ }
396
+ break;
397
+
398
+ case CMARK_NODE_HTML_INLINE:
399
+ OUT(cmark_node_get_literal(node), false, LITERAL);
400
+ break;
401
+
402
+ case CMARK_NODE_CUSTOM_INLINE:
403
+ OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
404
+ false, LITERAL);
405
+ break;
406
+
407
+ case CMARK_NODE_STRONG:
408
+ if (entering) {
409
+ LIT("**");
410
+ } else {
411
+ LIT("**");
412
+ }
413
+ break;
414
+
415
+ case CMARK_NODE_EMPH:
416
+ // If we have EMPH(EMPH(x)), we need to use *_x_*
417
+ // because **x** is STRONG(x):
418
+ if (node->parent && node->parent->type == CMARK_NODE_EMPH &&
419
+ node->next == NULL && node->prev == NULL) {
420
+ emph_delim = "_";
421
+ } else {
422
+ emph_delim = "*";
423
+ }
424
+ if (entering) {
425
+ LIT(emph_delim);
426
+ } else {
427
+ LIT(emph_delim);
428
+ }
429
+ break;
430
+
431
+ case CMARK_NODE_LINK:
432
+ if (is_autolink(node)) {
433
+ if (entering) {
434
+ LIT("<");
435
+ if (strncmp(cmark_node_get_url(node), "mailto:", 7) == 0) {
436
+ LIT((const char *)cmark_node_get_url(node) + 7);
437
+ } else {
438
+ LIT((const char *)cmark_node_get_url(node));
439
+ }
440
+ LIT(">");
441
+ // return signal to skip contents of node...
442
+ return 0;
443
+ }
444
+ } else {
445
+ if (entering) {
446
+ LIT("[");
447
+ } else {
448
+ LIT("](");
449
+ OUT(cmark_node_get_url(node), false, URL);
450
+ title = cmark_node_get_title(node);
451
+ if (strlen(title) > 0) {
452
+ LIT(" \"");
453
+ OUT(title, false, TITLE);
454
+ LIT("\"");
455
+ }
456
+ LIT(")");
457
+ }
458
+ }
459
+ break;
460
+
461
+ case CMARK_NODE_IMAGE:
462
+ if (entering) {
463
+ LIT("![");
464
+ } else {
465
+ LIT("](");
466
+ OUT(cmark_node_get_url(node), false, URL);
467
+ title = cmark_node_get_title(node);
468
+ if (strlen(title) > 0) {
469
+ OUT(" \"", allow_wrap, LITERAL);
470
+ OUT(title, false, TITLE);
471
+ LIT("\"");
472
+ }
473
+ LIT(")");
474
+ }
475
+ break;
476
+
477
+ case CMARK_NODE_FOOTNOTE_REFERENCE:
478
+ if (entering) {
479
+ LIT("[^");
480
+ OUT(cmark_chunk_to_cstr(renderer->mem, &node->as.literal), false, LITERAL);
481
+ LIT("]");
482
+ }
483
+ break;
484
+
485
+ case CMARK_NODE_FOOTNOTE_DEFINITION:
486
+ if (entering) {
487
+ renderer->footnote_ix += 1;
488
+ LIT("[^");
489
+ char n[32];
490
+ snprintf(n, sizeof(n), "%d", renderer->footnote_ix);
491
+ OUT(n, false, LITERAL);
492
+ LIT("]:\n");
493
+
494
+ cmark_strbuf_puts(renderer->prefix, " ");
495
+ } else {
496
+ cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 4);
497
+ }
498
+ break;
499
+
500
+ default:
501
+ assert(false);
502
+ break;
503
+ }
504
+
505
+ return 1;
506
+ }
507
+
508
+ char *cmark_render_commonmark(cmark_node *root, int options, int width) {
509
+ return cmark_render_commonmark_with_mem(root, options, width, cmark_node_mem(root));
510
+ }
511
+
512
+ char *cmark_render_commonmark_with_mem(cmark_node *root, int options, int width, cmark_mem *mem) {
513
+ if (options & CMARK_OPT_HARDBREAKS) {
514
+ // disable breaking on width, since it has
515
+ // a different meaning with OPT_HARDBREAKS
516
+ width = 0;
517
+ }
518
+ return cmark_render(mem, root, options, width, outc, S_render_node);
519
+ }