markly 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,9 @@
1
+ #ifndef CMARK_GFM_STRIKETHROUGH_H
2
+ #define CMARK_GFM_STRIKETHROUGH_H
3
+
4
+ #include "cmark-gfm-core-extensions.h"
5
+
6
+ extern cmark_node_type CMARK_NODE_STRIKETHROUGH;
7
+ cmark_syntax_extension *create_strikethrough_extension(void);
8
+
9
+ #endif
@@ -0,0 +1,149 @@
1
+ #include <stdlib.h>
2
+ #include <assert.h>
3
+
4
+ #include "cmark-gfm.h"
5
+ #include "syntax_extension.h"
6
+ #include "buffer.h"
7
+
8
+ extern cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR;
9
+
10
+ static cmark_mem *_mem = &CMARK_DEFAULT_MEM_ALLOCATOR;
11
+
12
+ void cmark_syntax_extension_free(cmark_mem *mem, cmark_syntax_extension *extension) {
13
+ if (extension->free_function && extension->priv) {
14
+ extension->free_function(mem, extension->priv);
15
+ }
16
+
17
+ cmark_llist_free(mem, extension->special_inline_chars);
18
+ mem->free(extension->name);
19
+ mem->free(extension);
20
+ }
21
+
22
+ cmark_syntax_extension *cmark_syntax_extension_new(const char *name) {
23
+ cmark_syntax_extension *res = (cmark_syntax_extension *) _mem->calloc(1, sizeof(cmark_syntax_extension));
24
+ res->name = (char *) _mem->calloc(1, sizeof(char) * (strlen(name)) + 1);
25
+ strcpy(res->name, name);
26
+ return res;
27
+ }
28
+
29
+ cmark_node_type cmark_syntax_extension_add_node(int is_inline) {
30
+ cmark_node_type *ref = !is_inline ? &CMARK_NODE_LAST_BLOCK : &CMARK_NODE_LAST_INLINE;
31
+
32
+ if ((*ref & CMARK_NODE_VALUE_MASK) == CMARK_NODE_VALUE_MASK) {
33
+ assert(false);
34
+ return (cmark_node_type) 0;
35
+ }
36
+
37
+ return *ref = (cmark_node_type) ((int) *ref + 1);
38
+ }
39
+
40
+ void cmark_syntax_extension_set_emphasis(cmark_syntax_extension *extension,
41
+ int emphasis) {
42
+ extension->emphasis = emphasis == 1;
43
+ }
44
+
45
+ void cmark_syntax_extension_set_open_block_func(cmark_syntax_extension *extension,
46
+ cmark_open_block_func func) {
47
+ extension->try_opening_block = func;
48
+ }
49
+
50
+ void cmark_syntax_extension_set_match_block_func(cmark_syntax_extension *extension,
51
+ cmark_match_block_func func) {
52
+ extension->last_block_matches = func;
53
+ }
54
+
55
+ void cmark_syntax_extension_set_match_inline_func(cmark_syntax_extension *extension,
56
+ cmark_match_inline_func func) {
57
+ extension->match_inline = func;
58
+ }
59
+
60
+ void cmark_syntax_extension_set_inline_from_delim_func(cmark_syntax_extension *extension,
61
+ cmark_inline_from_delim_func func) {
62
+ extension->insert_inline_from_delim = func;
63
+ }
64
+
65
+ void cmark_syntax_extension_set_special_inline_chars(cmark_syntax_extension *extension,
66
+ cmark_llist *special_chars) {
67
+ extension->special_inline_chars = special_chars;
68
+ }
69
+
70
+ void cmark_syntax_extension_set_get_type_string_func(cmark_syntax_extension *extension,
71
+ cmark_get_type_string_func func) {
72
+ extension->get_type_string_func = func;
73
+ }
74
+
75
+ void cmark_syntax_extension_set_can_contain_func(cmark_syntax_extension *extension,
76
+ cmark_can_contain_func func) {
77
+ extension->can_contain_func = func;
78
+ }
79
+
80
+ void cmark_syntax_extension_set_contains_inlines_func(cmark_syntax_extension *extension,
81
+ cmark_contains_inlines_func func) {
82
+ extension->contains_inlines_func = func;
83
+ }
84
+
85
+ void cmark_syntax_extension_set_commonmark_render_func(cmark_syntax_extension *extension,
86
+ cmark_common_render_func func) {
87
+ extension->commonmark_render_func = func;
88
+ }
89
+
90
+ void cmark_syntax_extension_set_plaintext_render_func(cmark_syntax_extension *extension,
91
+ cmark_common_render_func func) {
92
+ extension->plaintext_render_func = func;
93
+ }
94
+
95
+ void cmark_syntax_extension_set_latex_render_func(cmark_syntax_extension *extension,
96
+ cmark_common_render_func func) {
97
+ extension->latex_render_func = func;
98
+ }
99
+
100
+ void cmark_syntax_extension_set_xml_attr_func(cmark_syntax_extension *extension,
101
+ cmark_xml_attr_func func) {
102
+ extension->xml_attr_func = func;
103
+ }
104
+
105
+ void cmark_syntax_extension_set_man_render_func(cmark_syntax_extension *extension,
106
+ cmark_common_render_func func) {
107
+ extension->man_render_func = func;
108
+ }
109
+
110
+ void cmark_syntax_extension_set_html_render_func(cmark_syntax_extension *extension,
111
+ cmark_html_render_func func) {
112
+ extension->html_render_func = func;
113
+ }
114
+
115
+ void cmark_syntax_extension_set_html_filter_func(cmark_syntax_extension *extension,
116
+ cmark_html_filter_func func) {
117
+ extension->html_filter_func = func;
118
+ }
119
+
120
+ void cmark_syntax_extension_set_postprocess_func(cmark_syntax_extension *extension,
121
+ cmark_postprocess_func func) {
122
+ extension->postprocess_func = func;
123
+ }
124
+
125
+ void cmark_syntax_extension_set_private(cmark_syntax_extension *extension,
126
+ void *priv,
127
+ cmark_free_func free_func) {
128
+ extension->priv = priv;
129
+ extension->free_function = free_func;
130
+ }
131
+
132
+ void *cmark_syntax_extension_get_private(cmark_syntax_extension *extension) {
133
+ return extension->priv;
134
+ }
135
+
136
+ void cmark_syntax_extension_set_opaque_alloc_func(cmark_syntax_extension *extension,
137
+ cmark_opaque_alloc_func func) {
138
+ extension->opaque_alloc_func = func;
139
+ }
140
+
141
+ void cmark_syntax_extension_set_opaque_free_func(cmark_syntax_extension *extension,
142
+ cmark_opaque_free_func func) {
143
+ extension->opaque_free_func = func;
144
+ }
145
+
146
+ void cmark_syntax_extension_set_commonmark_escape_func(cmark_syntax_extension *extension,
147
+ cmark_commonmark_escape_func func) {
148
+ extension->commonmark_escape_func = func;
149
+ }
@@ -0,0 +1,34 @@
1
+ #ifndef CMARK_SYNTAX_EXTENSION_H
2
+ #define CMARK_SYNTAX_EXTENSION_H
3
+
4
+ #include "cmark-gfm.h"
5
+ #include "cmark-gfm-extension_api.h"
6
+ #include "config.h"
7
+
8
+ struct cmark_syntax_extension {
9
+ cmark_match_block_func last_block_matches;
10
+ cmark_open_block_func try_opening_block;
11
+ cmark_match_inline_func match_inline;
12
+ cmark_inline_from_delim_func insert_inline_from_delim;
13
+ cmark_llist * special_inline_chars;
14
+ char * name;
15
+ void * priv;
16
+ bool emphasis;
17
+ cmark_free_func free_function;
18
+ cmark_get_type_string_func get_type_string_func;
19
+ cmark_can_contain_func can_contain_func;
20
+ cmark_contains_inlines_func contains_inlines_func;
21
+ cmark_common_render_func commonmark_render_func;
22
+ cmark_common_render_func plaintext_render_func;
23
+ cmark_common_render_func latex_render_func;
24
+ cmark_xml_attr_func xml_attr_func;
25
+ cmark_common_render_func man_render_func;
26
+ cmark_html_render_func html_render_func;
27
+ cmark_html_filter_func html_filter_func;
28
+ cmark_postprocess_func postprocess_func;
29
+ cmark_opaque_alloc_func opaque_alloc_func;
30
+ cmark_opaque_free_func opaque_free_func;
31
+ cmark_commonmark_escape_func commonmark_escape_func;
32
+ };
33
+
34
+ #endif
@@ -0,0 +1,803 @@
1
+ #include <cmark-gfm-extension_api.h>
2
+ #include <html.h>
3
+ #include <inlines.h>
4
+ #include <parser.h>
5
+ #include <references.h>
6
+ #include <string.h>
7
+ #include <render.h>
8
+
9
+ #include "ext_scanners.h"
10
+ #include "strikethrough.h"
11
+ #include "table.h"
12
+ #include "cmark-gfm-core-extensions.h"
13
+
14
+ cmark_node_type CMARK_NODE_TABLE, CMARK_NODE_TABLE_ROW,
15
+ CMARK_NODE_TABLE_CELL;
16
+
17
+ typedef struct {
18
+ uint16_t n_columns;
19
+ int paragraph_offset;
20
+ cmark_llist *cells;
21
+ } table_row;
22
+
23
+ typedef struct {
24
+ uint16_t n_columns;
25
+ uint8_t *alignments;
26
+ } node_table;
27
+
28
+ typedef struct {
29
+ bool is_header;
30
+ } node_table_row;
31
+
32
+ typedef struct {
33
+ cmark_strbuf *buf;
34
+ int start_offset, end_offset, internal_offset;
35
+ } node_cell;
36
+
37
+ static void free_table_cell(cmark_mem *mem, void *data) {
38
+ node_cell *cell = (node_cell *)data;
39
+ cmark_strbuf_free((cmark_strbuf *)cell->buf);
40
+ mem->free(cell->buf);
41
+ mem->free(cell);
42
+ }
43
+
44
+ static void free_table_row(cmark_mem *mem, table_row *row) {
45
+ if (!row)
46
+ return;
47
+
48
+ cmark_llist_free_full(mem, row->cells, (cmark_free_func)free_table_cell);
49
+
50
+ mem->free(row);
51
+ }
52
+
53
+ static void free_node_table(cmark_mem *mem, void *ptr) {
54
+ node_table *t = (node_table *)ptr;
55
+ mem->free(t->alignments);
56
+ mem->free(t);
57
+ }
58
+
59
+ static void free_node_table_row(cmark_mem *mem, void *ptr) {
60
+ mem->free(ptr);
61
+ }
62
+
63
+ static int get_n_table_columns(cmark_node *node) {
64
+ if (!node || node->type != CMARK_NODE_TABLE)
65
+ return -1;
66
+
67
+ return (int)((node_table *)node->as.opaque)->n_columns;
68
+ }
69
+
70
+ static int set_n_table_columns(cmark_node *node, uint16_t n_columns) {
71
+ if (!node || node->type != CMARK_NODE_TABLE)
72
+ return 0;
73
+
74
+ ((node_table *)node->as.opaque)->n_columns = n_columns;
75
+ return 1;
76
+ }
77
+
78
+ static uint8_t *get_table_alignments(cmark_node *node) {
79
+ if (!node || node->type != CMARK_NODE_TABLE)
80
+ return 0;
81
+
82
+ return ((node_table *)node->as.opaque)->alignments;
83
+ }
84
+
85
+ static int set_table_alignments(cmark_node *node, uint8_t *alignments) {
86
+ if (!node || node->type != CMARK_NODE_TABLE)
87
+ return 0;
88
+
89
+ ((node_table *)node->as.opaque)->alignments = alignments;
90
+ return 1;
91
+ }
92
+
93
+ static cmark_strbuf *unescape_pipes(cmark_mem *mem, unsigned char *string, bufsize_t len)
94
+ {
95
+ cmark_strbuf *res = (cmark_strbuf *)mem->calloc(1, sizeof(cmark_strbuf));
96
+ bufsize_t r, w;
97
+
98
+ cmark_strbuf_init(mem, res, len + 1);
99
+ cmark_strbuf_put(res, string, len);
100
+ cmark_strbuf_putc(res, '\0');
101
+
102
+ for (r = 0, w = 0; r < len; ++r) {
103
+ if (res->ptr[r] == '\\' && res->ptr[r + 1] == '|')
104
+ r++;
105
+
106
+ res->ptr[w++] = res->ptr[r];
107
+ }
108
+
109
+ cmark_strbuf_truncate(res, w);
110
+
111
+ return res;
112
+ }
113
+
114
+ static table_row *row_from_string(cmark_syntax_extension *self,
115
+ cmark_parser *parser, unsigned char *string,
116
+ int len) {
117
+ table_row *row = NULL;
118
+ bufsize_t cell_matched = 1, pipe_matched = 1, offset;
119
+ int cell_end_offset;
120
+
121
+ row = (table_row *)parser->mem->calloc(1, sizeof(table_row));
122
+ row->n_columns = 0;
123
+ row->cells = NULL;
124
+
125
+ offset = scan_table_cell_end(string, len, 0);
126
+
127
+ // Parse the cells of the row. Stop if we reach the end of the input, or if we
128
+ // cannot detect any more cells.
129
+ while (offset < len && (cell_matched || pipe_matched)) {
130
+ cell_matched = scan_table_cell(string, len, offset);
131
+ pipe_matched = scan_table_cell_end(string, len, offset + cell_matched);
132
+
133
+ if (cell_matched || pipe_matched) {
134
+ cell_end_offset = offset + cell_matched - 1;
135
+
136
+ if (string[cell_end_offset] == '\n' || string[cell_end_offset] == '\r') {
137
+ row->paragraph_offset = cell_end_offset;
138
+
139
+ cmark_llist_free_full(parser->mem, row->cells, (cmark_free_func)free_table_cell);
140
+ row->cells = NULL;
141
+ row->n_columns = 0;
142
+ } else {
143
+ cmark_strbuf *cell_buf = unescape_pipes(parser->mem, string + offset,
144
+ cell_matched);
145
+ cmark_strbuf_trim(cell_buf);
146
+
147
+ node_cell *cell = (node_cell *)parser->mem->calloc(1, sizeof(*cell));
148
+ cell->buf = cell_buf;
149
+ cell->start_offset = offset;
150
+ cell->end_offset = cell_end_offset;
151
+
152
+ while (cell->start_offset > 0 && string[cell->start_offset - 1] != '|') {
153
+ --cell->start_offset;
154
+ ++cell->internal_offset;
155
+ }
156
+
157
+ row->n_columns += 1;
158
+ row->cells = cmark_llist_append(parser->mem, row->cells, cell);
159
+ }
160
+ }
161
+
162
+ offset += cell_matched + pipe_matched;
163
+
164
+ if (!pipe_matched) {
165
+ pipe_matched = scan_table_row_end(string, len, offset);
166
+ offset += pipe_matched;
167
+ }
168
+ }
169
+
170
+ if (offset != len || !row->n_columns) {
171
+ free_table_row(parser->mem, row);
172
+ row = NULL;
173
+ }
174
+
175
+ return row;
176
+ }
177
+
178
+ static void try_inserting_table_header_paragraph(cmark_parser *parser,
179
+ cmark_node *parent_container,
180
+ unsigned char *parent_string,
181
+ int paragraph_offset) {
182
+ cmark_node *paragraph;
183
+ cmark_strbuf *paragraph_content;
184
+
185
+ paragraph = cmark_node_new_with_mem(CMARK_NODE_PARAGRAPH, parser->mem);
186
+
187
+ paragraph_content = unescape_pipes(parser->mem, parent_string, paragraph_offset);
188
+ cmark_strbuf_trim(paragraph_content);
189
+ cmark_node_set_string_content(paragraph, (char *) paragraph_content->ptr);
190
+ cmark_strbuf_free(paragraph_content);
191
+ parser->mem->free(paragraph_content);
192
+
193
+ if (!cmark_node_insert_before(parent_container, paragraph)) {
194
+ parser->mem->free(paragraph);
195
+ }
196
+ }
197
+
198
+ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
199
+ cmark_parser *parser,
200
+ cmark_node *parent_container,
201
+ unsigned char *input, int len) {
202
+ bufsize_t matched =
203
+ scan_table_start(input, len, cmark_parser_get_first_nonspace(parser));
204
+ cmark_node *table_header;
205
+ table_row *header_row = NULL;
206
+ table_row *marker_row = NULL;
207
+ node_table_row *ntr;
208
+ const char *parent_string;
209
+ uint16_t i;
210
+
211
+ if (!matched)
212
+ return parent_container;
213
+
214
+ parent_string = cmark_node_get_string_content(parent_container);
215
+
216
+ cmark_arena_push();
217
+
218
+ header_row = row_from_string(self, parser, (unsigned char *)parent_string,
219
+ (int)strlen(parent_string));
220
+
221
+ if (!header_row) {
222
+ free_table_row(parser->mem, header_row);
223
+ cmark_arena_pop();
224
+ return parent_container;
225
+ }
226
+
227
+ marker_row = row_from_string(self, parser,
228
+ input + cmark_parser_get_first_nonspace(parser),
229
+ len - cmark_parser_get_first_nonspace(parser));
230
+
231
+ assert(marker_row);
232
+
233
+ if (header_row->n_columns != marker_row->n_columns) {
234
+ free_table_row(parser->mem, header_row);
235
+ free_table_row(parser->mem, marker_row);
236
+ cmark_arena_pop();
237
+ return parent_container;
238
+ }
239
+
240
+ if (cmark_arena_pop()) {
241
+ header_row = row_from_string(self, parser, (unsigned char *)parent_string,
242
+ (int)strlen(parent_string));
243
+ marker_row = row_from_string(self, parser,
244
+ input + cmark_parser_get_first_nonspace(parser),
245
+ len - cmark_parser_get_first_nonspace(parser));
246
+ }
247
+
248
+ if (!cmark_node_set_type(parent_container, CMARK_NODE_TABLE)) {
249
+ free_table_row(parser->mem, header_row);
250
+ free_table_row(parser->mem, marker_row);
251
+ return parent_container;
252
+ }
253
+
254
+ if (header_row->paragraph_offset) {
255
+ try_inserting_table_header_paragraph(parser, parent_container, (unsigned char *)parent_string,
256
+ header_row->paragraph_offset);
257
+ }
258
+
259
+ cmark_node_set_syntax_extension(parent_container, self);
260
+
261
+ parent_container->as.opaque = parser->mem->calloc(1, sizeof(node_table));
262
+
263
+ set_n_table_columns(parent_container, header_row->n_columns);
264
+
265
+ uint8_t *alignments =
266
+ (uint8_t *)parser->mem->calloc(header_row->n_columns, sizeof(uint8_t));
267
+ cmark_llist *it = marker_row->cells;
268
+ for (i = 0; it; it = it->next, ++i) {
269
+ node_cell *node = (node_cell *)it->data;
270
+ bool left = node->buf->ptr[0] == ':', right = node->buf->ptr[node->buf->size - 1] == ':';
271
+
272
+ if (left && right)
273
+ alignments[i] = 'c';
274
+ else if (left)
275
+ alignments[i] = 'l';
276
+ else if (right)
277
+ alignments[i] = 'r';
278
+ }
279
+ set_table_alignments(parent_container, alignments);
280
+
281
+ table_header =
282
+ cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
283
+ parent_container->start_column);
284
+ cmark_node_set_syntax_extension(table_header, self);
285
+ table_header->end_column = parent_container->start_column + (int)strlen(parent_string) - 2;
286
+ table_header->start_line = table_header->end_line = parent_container->start_line;
287
+
288
+ table_header->as.opaque = ntr = (node_table_row *)parser->mem->calloc(1, sizeof(node_table_row));
289
+ ntr->is_header = true;
290
+
291
+ {
292
+ cmark_llist *tmp;
293
+
294
+ for (tmp = header_row->cells; tmp; tmp = tmp->next) {
295
+ node_cell *cell = (node_cell *) tmp->data;
296
+ cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
297
+ CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
298
+ header_cell->start_line = header_cell->end_line = parent_container->start_line;
299
+ header_cell->internal_offset = cell->internal_offset;
300
+ header_cell->end_column = parent_container->start_column + cell->end_offset;
301
+ cmark_node_set_string_content(header_cell, (char *) cell->buf->ptr);
302
+ cmark_node_set_syntax_extension(header_cell, self);
303
+ }
304
+ }
305
+
306
+ cmark_parser_advance_offset(
307
+ parser, (char *)input,
308
+ (int)strlen((char *)input) - 1 - cmark_parser_get_offset(parser), false);
309
+
310
+ free_table_row(parser->mem, header_row);
311
+ free_table_row(parser->mem, marker_row);
312
+ return parent_container;
313
+ }
314
+
315
+ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
316
+ cmark_parser *parser,
317
+ cmark_node *parent_container,
318
+ unsigned char *input, int len) {
319
+ cmark_node *table_row_block;
320
+ table_row *row;
321
+
322
+ if (cmark_parser_is_blank(parser))
323
+ return NULL;
324
+
325
+ table_row_block =
326
+ cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
327
+ parent_container->start_column);
328
+ cmark_node_set_syntax_extension(table_row_block, self);
329
+ table_row_block->end_column = parent_container->end_column;
330
+ table_row_block->as.opaque = parser->mem->calloc(1, sizeof(node_table_row));
331
+
332
+ row = row_from_string(self, parser, input + cmark_parser_get_first_nonspace(parser),
333
+ len - cmark_parser_get_first_nonspace(parser));
334
+
335
+ {
336
+ cmark_llist *tmp;
337
+ int i, table_columns = get_n_table_columns(parent_container);
338
+
339
+ for (tmp = row->cells, i = 0; tmp && i < table_columns; tmp = tmp->next, ++i) {
340
+ node_cell *cell = (node_cell *) tmp->data;
341
+ cmark_node *node = cmark_parser_add_child(parser, table_row_block,
342
+ CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
343
+ node->internal_offset = cell->internal_offset;
344
+ node->end_column = parent_container->start_column + cell->end_offset;
345
+ cmark_node_set_string_content(node, (char *) cell->buf->ptr);
346
+ cmark_node_set_syntax_extension(node, self);
347
+ }
348
+
349
+ for (; i < table_columns; ++i) {
350
+ cmark_node *node = cmark_parser_add_child(
351
+ parser, table_row_block, CMARK_NODE_TABLE_CELL, 0);
352
+ cmark_node_set_syntax_extension(node, self);
353
+ }
354
+ }
355
+
356
+ free_table_row(parser->mem, row);
357
+
358
+ cmark_parser_advance_offset(parser, (char *)input,
359
+ len - 1 - cmark_parser_get_offset(parser), false);
360
+
361
+ return table_row_block;
362
+ }
363
+
364
+ static cmark_node *try_opening_table_block(cmark_syntax_extension *self,
365
+ int indented, cmark_parser *parser,
366
+ cmark_node *parent_container,
367
+ unsigned char *input, int len) {
368
+ cmark_node_type parent_type = cmark_node_get_type(parent_container);
369
+
370
+ if (!indented && parent_type == CMARK_NODE_PARAGRAPH) {
371
+ return try_opening_table_header(self, parser, parent_container, input, len);
372
+ } else if (!indented && parent_type == CMARK_NODE_TABLE) {
373
+ return try_opening_table_row(self, parser, parent_container, input, len);
374
+ }
375
+
376
+ return NULL;
377
+ }
378
+
379
+ static int matches(cmark_syntax_extension *self, cmark_parser *parser,
380
+ unsigned char *input, int len,
381
+ cmark_node *parent_container) {
382
+ int res = 0;
383
+
384
+ if (cmark_node_get_type(parent_container) == CMARK_NODE_TABLE) {
385
+ cmark_arena_push();
386
+ table_row *new_row = row_from_string(
387
+ self, parser, input + cmark_parser_get_first_nonspace(parser),
388
+ len - cmark_parser_get_first_nonspace(parser));
389
+ if (new_row && new_row->n_columns)
390
+ res = 1;
391
+ free_table_row(parser->mem, new_row);
392
+ cmark_arena_pop();
393
+ }
394
+
395
+ return res;
396
+ }
397
+
398
+ static const char *get_type_string(cmark_syntax_extension *self,
399
+ cmark_node *node) {
400
+ if (node->type == CMARK_NODE_TABLE) {
401
+ return "table";
402
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
403
+ if (((node_table_row *)node->as.opaque)->is_header)
404
+ return "table_header";
405
+ else
406
+ return "table_row";
407
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
408
+ return "table_cell";
409
+ }
410
+
411
+ return "<unknown>";
412
+ }
413
+
414
+ static int can_contain(cmark_syntax_extension *extension, cmark_node *node,
415
+ cmark_node_type child_type) {
416
+ if (node->type == CMARK_NODE_TABLE) {
417
+ return child_type == CMARK_NODE_TABLE_ROW;
418
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
419
+ return child_type == CMARK_NODE_TABLE_CELL;
420
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
421
+ return child_type == CMARK_NODE_TEXT || child_type == CMARK_NODE_CODE ||
422
+ child_type == CMARK_NODE_EMPH || child_type == CMARK_NODE_STRONG ||
423
+ child_type == CMARK_NODE_LINK || child_type == CMARK_NODE_IMAGE ||
424
+ child_type == CMARK_NODE_STRIKETHROUGH ||
425
+ child_type == CMARK_NODE_HTML_INLINE ||
426
+ child_type == CMARK_NODE_FOOTNOTE_REFERENCE;
427
+ }
428
+ return false;
429
+ }
430
+
431
+ static int contains_inlines(cmark_syntax_extension *extension,
432
+ cmark_node *node) {
433
+ return node->type == CMARK_NODE_TABLE_CELL;
434
+ }
435
+
436
+ static void commonmark_render(cmark_syntax_extension *extension,
437
+ cmark_renderer *renderer, cmark_node *node,
438
+ cmark_event_type ev_type, int options) {
439
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
440
+
441
+ if (node->type == CMARK_NODE_TABLE) {
442
+ renderer->blankline(renderer);
443
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
444
+ if (entering) {
445
+ renderer->cr(renderer);
446
+ renderer->out(renderer, node, "|", false, LITERAL);
447
+ }
448
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
449
+ if (entering) {
450
+ renderer->out(renderer, node, " ", false, LITERAL);
451
+ } else {
452
+ renderer->out(renderer, node, " |", false, LITERAL);
453
+ if (((node_table_row *)node->parent->as.opaque)->is_header &&
454
+ !node->next) {
455
+ int i;
456
+ uint8_t *alignments = get_table_alignments(node->parent->parent);
457
+ uint16_t n_cols =
458
+ ((node_table *)node->parent->parent->as.opaque)->n_columns;
459
+ renderer->cr(renderer);
460
+ renderer->out(renderer, node, "|", false, LITERAL);
461
+ for (i = 0; i < n_cols; i++) {
462
+ switch (alignments[i]) {
463
+ case 0: renderer->out(renderer, node, " --- |", false, LITERAL); break;
464
+ case 'l': renderer->out(renderer, node, " :-- |", false, LITERAL); break;
465
+ case 'c': renderer->out(renderer, node, " :-: |", false, LITERAL); break;
466
+ case 'r': renderer->out(renderer, node, " --: |", false, LITERAL); break;
467
+ }
468
+ }
469
+ renderer->cr(renderer);
470
+ }
471
+ }
472
+ } else {
473
+ assert(false);
474
+ }
475
+ }
476
+
477
+ static void latex_render(cmark_syntax_extension *extension,
478
+ cmark_renderer *renderer, cmark_node *node,
479
+ cmark_event_type ev_type, int options) {
480
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
481
+
482
+ if (node->type == CMARK_NODE_TABLE) {
483
+ if (entering) {
484
+ int i;
485
+ uint16_t n_cols;
486
+ uint8_t *alignments = get_table_alignments(node);
487
+
488
+ renderer->cr(renderer);
489
+ renderer->out(renderer, node, "\\begin{table}", false, LITERAL);
490
+ renderer->cr(renderer);
491
+ renderer->out(renderer, node, "\\begin{tabular}{", false, LITERAL);
492
+
493
+ n_cols = ((node_table *)node->as.opaque)->n_columns;
494
+ for (i = 0; i < n_cols; i++) {
495
+ switch(alignments[i]) {
496
+ case 0:
497
+ case 'l':
498
+ renderer->out(renderer, node, "l", false, LITERAL);
499
+ break;
500
+ case 'c':
501
+ renderer->out(renderer, node, "c", false, LITERAL);
502
+ break;
503
+ case 'r':
504
+ renderer->out(renderer, node, "r", false, LITERAL);
505
+ break;
506
+ }
507
+ }
508
+ renderer->out(renderer, node, "}", false, LITERAL);
509
+ renderer->cr(renderer);
510
+ } else {
511
+ renderer->out(renderer, node, "\\end{tabular}", false, LITERAL);
512
+ renderer->cr(renderer);
513
+ renderer->out(renderer, node, "\\end{table}", false, LITERAL);
514
+ renderer->cr(renderer);
515
+ }
516
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
517
+ if (!entering) {
518
+ renderer->cr(renderer);
519
+ }
520
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
521
+ if (!entering) {
522
+ if (node->next) {
523
+ renderer->out(renderer, node, " & ", false, LITERAL);
524
+ } else {
525
+ renderer->out(renderer, node, " \\\\", false, LITERAL);
526
+ }
527
+ }
528
+ } else {
529
+ assert(false);
530
+ }
531
+ }
532
+
533
+ static const char *xml_attr(cmark_syntax_extension *extension,
534
+ cmark_node *node) {
535
+ if (node->type == CMARK_NODE_TABLE_CELL) {
536
+ if (cmark_gfm_extensions_get_table_row_is_header(node->parent)) {
537
+ uint8_t *alignments = get_table_alignments(node->parent->parent);
538
+ int i = 0;
539
+ cmark_node *n;
540
+ for (n = node->parent->first_child; n; n = n->next, ++i)
541
+ if (n == node)
542
+ break;
543
+ switch (alignments[i]) {
544
+ case 'l': return " align=\"left\"";
545
+ case 'c': return " align=\"center\"";
546
+ case 'r': return " align=\"right\"";
547
+ }
548
+ }
549
+ }
550
+
551
+ return NULL;
552
+ }
553
+
554
+ static void man_render(cmark_syntax_extension *extension,
555
+ cmark_renderer *renderer, cmark_node *node,
556
+ cmark_event_type ev_type, int options) {
557
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
558
+
559
+ if (node->type == CMARK_NODE_TABLE) {
560
+ if (entering) {
561
+ int i;
562
+ uint16_t n_cols;
563
+ uint8_t *alignments = get_table_alignments(node);
564
+
565
+ renderer->cr(renderer);
566
+ renderer->out(renderer, node, ".TS", false, LITERAL);
567
+ renderer->cr(renderer);
568
+ renderer->out(renderer, node, "tab(@);", false, LITERAL);
569
+ renderer->cr(renderer);
570
+
571
+ n_cols = ((node_table *)node->as.opaque)->n_columns;
572
+
573
+ for (i = 0; i < n_cols; i++) {
574
+ switch (alignments[i]) {
575
+ case 'l':
576
+ renderer->out(renderer, node, "l", false, LITERAL);
577
+ break;
578
+ case 0:
579
+ case 'c':
580
+ renderer->out(renderer, node, "c", false, LITERAL);
581
+ break;
582
+ case 'r':
583
+ renderer->out(renderer, node, "r", false, LITERAL);
584
+ break;
585
+ }
586
+ }
587
+
588
+ if (n_cols) {
589
+ renderer->out(renderer, node, ".", false, LITERAL);
590
+ renderer->cr(renderer);
591
+ }
592
+ } else {
593
+ renderer->out(renderer, node, ".TE", false, LITERAL);
594
+ renderer->cr(renderer);
595
+ }
596
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
597
+ if (!entering) {
598
+ renderer->cr(renderer);
599
+ }
600
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
601
+ if (!entering && node->next) {
602
+ renderer->out(renderer, node, "@", false, LITERAL);
603
+ }
604
+ } else {
605
+ assert(false);
606
+ }
607
+ }
608
+
609
+ static void html_table_add_align(cmark_strbuf* html, const char* align, int options) {
610
+ if (options & CMARK_OPT_TABLE_PREFER_STYLE_ATTRIBUTES) {
611
+ cmark_strbuf_puts(html, " style=\"text-align: ");
612
+ cmark_strbuf_puts(html, align);
613
+ cmark_strbuf_puts(html, "\"");
614
+ } else {
615
+ cmark_strbuf_puts(html, " align=\"");
616
+ cmark_strbuf_puts(html, align);
617
+ cmark_strbuf_puts(html, "\"");
618
+ }
619
+ }
620
+
621
+ struct html_table_state {
622
+ unsigned need_closing_table_body : 1;
623
+ unsigned in_table_header : 1;
624
+ };
625
+
626
+ static void html_render(cmark_syntax_extension *extension,
627
+ cmark_html_renderer *renderer, cmark_node *node,
628
+ cmark_event_type ev_type, int options) {
629
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
630
+ cmark_strbuf *html = renderer->html;
631
+ cmark_node *n;
632
+
633
+ // XXX: we just monopolise renderer->opaque.
634
+ struct html_table_state *table_state =
635
+ (struct html_table_state *)&renderer->opaque;
636
+
637
+ if (node->type == CMARK_NODE_TABLE) {
638
+ if (entering) {
639
+ cmark_html_render_cr(html);
640
+ cmark_strbuf_puts(html, "<table");
641
+ cmark_html_render_sourcepos(node, html, options);
642
+ cmark_strbuf_putc(html, '>');
643
+ table_state->need_closing_table_body = false;
644
+ } else {
645
+ if (table_state->need_closing_table_body) {
646
+ cmark_html_render_cr(html);
647
+ cmark_strbuf_puts(html, "</tbody>");
648
+ cmark_html_render_cr(html);
649
+ }
650
+ table_state->need_closing_table_body = false;
651
+ cmark_html_render_cr(html);
652
+ cmark_strbuf_puts(html, "</table>");
653
+ cmark_html_render_cr(html);
654
+ }
655
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
656
+ if (entering) {
657
+ cmark_html_render_cr(html);
658
+ if (((node_table_row *)node->as.opaque)->is_header) {
659
+ table_state->in_table_header = 1;
660
+ cmark_strbuf_puts(html, "<thead>");
661
+ cmark_html_render_cr(html);
662
+ } else if (!table_state->need_closing_table_body) {
663
+ cmark_strbuf_puts(html, "<tbody>");
664
+ cmark_html_render_cr(html);
665
+ table_state->need_closing_table_body = 1;
666
+ }
667
+ cmark_strbuf_puts(html, "<tr");
668
+ cmark_html_render_sourcepos(node, html, options);
669
+ cmark_strbuf_putc(html, '>');
670
+ } else {
671
+ cmark_html_render_cr(html);
672
+ cmark_strbuf_puts(html, "</tr>");
673
+ if (((node_table_row *)node->as.opaque)->is_header) {
674
+ cmark_html_render_cr(html);
675
+ cmark_strbuf_puts(html, "</thead>");
676
+ table_state->in_table_header = false;
677
+ }
678
+ }
679
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
680
+ uint8_t *alignments = get_table_alignments(node->parent->parent);
681
+ if (entering) {
682
+ cmark_html_render_cr(html);
683
+ if (table_state->in_table_header) {
684
+ cmark_strbuf_puts(html, "<th");
685
+ } else {
686
+ cmark_strbuf_puts(html, "<td");
687
+ }
688
+
689
+ int i = 0;
690
+ for (n = node->parent->first_child; n; n = n->next, ++i)
691
+ if (n == node)
692
+ break;
693
+
694
+ switch (alignments[i]) {
695
+ case 'l': html_table_add_align(html, "left", options); break;
696
+ case 'c': html_table_add_align(html, "center", options); break;
697
+ case 'r': html_table_add_align(html, "right", options); break;
698
+ }
699
+
700
+ cmark_html_render_sourcepos(node, html, options);
701
+ cmark_strbuf_putc(html, '>');
702
+ } else {
703
+ if (table_state->in_table_header) {
704
+ cmark_strbuf_puts(html, "</th>");
705
+ } else {
706
+ cmark_strbuf_puts(html, "</td>");
707
+ }
708
+ }
709
+ } else {
710
+ assert(false);
711
+ }
712
+ }
713
+
714
+ static void opaque_alloc(cmark_syntax_extension *self, cmark_mem *mem, cmark_node *node) {
715
+ if (node->type == CMARK_NODE_TABLE) {
716
+ node->as.opaque = mem->calloc(1, sizeof(node_table));
717
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
718
+ node->as.opaque = mem->calloc(1, sizeof(node_table_row));
719
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
720
+ node->as.opaque = mem->calloc(1, sizeof(node_cell));
721
+ }
722
+ }
723
+
724
+ static void opaque_free(cmark_syntax_extension *self, cmark_mem *mem, cmark_node *node) {
725
+ if (node->type == CMARK_NODE_TABLE) {
726
+ free_node_table(mem, node->as.opaque);
727
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
728
+ free_node_table_row(mem, node->as.opaque);
729
+ }
730
+ }
731
+
732
+ static int escape(cmark_syntax_extension *self, cmark_node *node, int c) {
733
+ return
734
+ node->type != CMARK_NODE_TABLE &&
735
+ node->type != CMARK_NODE_TABLE_ROW &&
736
+ node->type != CMARK_NODE_TABLE_CELL &&
737
+ c == '|';
738
+ }
739
+
740
+ cmark_syntax_extension *create_table_extension(void) {
741
+ cmark_syntax_extension *self = cmark_syntax_extension_new("table");
742
+
743
+ cmark_syntax_extension_set_match_block_func(self, matches);
744
+ cmark_syntax_extension_set_open_block_func(self, try_opening_table_block);
745
+ cmark_syntax_extension_set_get_type_string_func(self, get_type_string);
746
+ cmark_syntax_extension_set_can_contain_func(self, can_contain);
747
+ cmark_syntax_extension_set_contains_inlines_func(self, contains_inlines);
748
+ cmark_syntax_extension_set_commonmark_render_func(self, commonmark_render);
749
+ cmark_syntax_extension_set_plaintext_render_func(self, commonmark_render);
750
+ cmark_syntax_extension_set_latex_render_func(self, latex_render);
751
+ cmark_syntax_extension_set_xml_attr_func(self, xml_attr);
752
+ cmark_syntax_extension_set_man_render_func(self, man_render);
753
+ cmark_syntax_extension_set_html_render_func(self, html_render);
754
+ cmark_syntax_extension_set_opaque_alloc_func(self, opaque_alloc);
755
+ cmark_syntax_extension_set_opaque_free_func(self, opaque_free);
756
+ cmark_syntax_extension_set_commonmark_escape_func(self, escape);
757
+ CMARK_NODE_TABLE = cmark_syntax_extension_add_node(0);
758
+ CMARK_NODE_TABLE_ROW = cmark_syntax_extension_add_node(0);
759
+ CMARK_NODE_TABLE_CELL = cmark_syntax_extension_add_node(0);
760
+
761
+ return self;
762
+ }
763
+
764
+ uint16_t cmark_gfm_extensions_get_table_columns(cmark_node *node) {
765
+ if (node->type != CMARK_NODE_TABLE)
766
+ return 0;
767
+
768
+ return ((node_table *)node->as.opaque)->n_columns;
769
+ }
770
+
771
+ uint8_t *cmark_gfm_extensions_get_table_alignments(cmark_node *node) {
772
+ if (node->type != CMARK_NODE_TABLE)
773
+ return 0;
774
+
775
+ return ((node_table *)node->as.opaque)->alignments;
776
+ }
777
+
778
+ int cmark_gfm_extensions_set_table_columns(cmark_node *node, uint16_t n_columns) {
779
+ return set_n_table_columns(node, n_columns);
780
+ }
781
+
782
+ int cmark_gfm_extensions_set_table_alignments(cmark_node *node, uint16_t ncols, uint8_t *alignments) {
783
+ uint8_t *a = (uint8_t *)cmark_node_mem(node)->calloc(1, ncols);
784
+ memcpy(a, alignments, ncols);
785
+ return set_table_alignments(node, a);
786
+ }
787
+
788
+ int cmark_gfm_extensions_get_table_row_is_header(cmark_node *node)
789
+ {
790
+ if (!node || node->type != CMARK_NODE_TABLE_ROW)
791
+ return 0;
792
+
793
+ return ((node_table_row *)node->as.opaque)->is_header;
794
+ }
795
+
796
+ int cmark_gfm_extensions_set_table_row_is_header(cmark_node *node, int is_header)
797
+ {
798
+ if (!node || node->type != CMARK_NODE_TABLE_ROW)
799
+ return 0;
800
+
801
+ ((node_table_row *)node->as.opaque)->is_header = (is_header != 0);
802
+ return 1;
803
+ }