qiita_marker 0.23.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +50 -0
  4. data/Rakefile +113 -0
  5. data/bin/qiita_marker +123 -0
  6. data/ext/qiita_marker/arena.c +103 -0
  7. data/ext/qiita_marker/autolink.c +425 -0
  8. data/ext/qiita_marker/autolink.h +8 -0
  9. data/ext/qiita_marker/blocks.c +1596 -0
  10. data/ext/qiita_marker/buffer.c +278 -0
  11. data/ext/qiita_marker/buffer.h +116 -0
  12. data/ext/qiita_marker/case_fold_switch.inc +4327 -0
  13. data/ext/qiita_marker/chunk.h +135 -0
  14. data/ext/qiita_marker/cmark-gfm-core-extensions.h +54 -0
  15. data/ext/qiita_marker/cmark-gfm-extension_api.h +736 -0
  16. data/ext/qiita_marker/cmark-gfm-extensions_export.h +42 -0
  17. data/ext/qiita_marker/cmark-gfm.h +817 -0
  18. data/ext/qiita_marker/cmark-gfm_export.h +42 -0
  19. data/ext/qiita_marker/cmark-gfm_version.h +7 -0
  20. data/ext/qiita_marker/cmark.c +55 -0
  21. data/ext/qiita_marker/cmark_ctype.c +44 -0
  22. data/ext/qiita_marker/cmark_ctype.h +33 -0
  23. data/ext/qiita_marker/commonmark.c +529 -0
  24. data/ext/qiita_marker/config.h +76 -0
  25. data/ext/qiita_marker/core-extensions.c +27 -0
  26. data/ext/qiita_marker/entities.inc +2138 -0
  27. data/ext/qiita_marker/ext_scanners.c +879 -0
  28. data/ext/qiita_marker/ext_scanners.h +24 -0
  29. data/ext/qiita_marker/extconf.rb +7 -0
  30. data/ext/qiita_marker/footnotes.c +63 -0
  31. data/ext/qiita_marker/footnotes.h +27 -0
  32. data/ext/qiita_marker/houdini.h +57 -0
  33. data/ext/qiita_marker/houdini_href_e.c +100 -0
  34. data/ext/qiita_marker/houdini_html_e.c +66 -0
  35. data/ext/qiita_marker/houdini_html_u.c +149 -0
  36. data/ext/qiita_marker/html.c +486 -0
  37. data/ext/qiita_marker/html.h +27 -0
  38. data/ext/qiita_marker/inlines.c +1691 -0
  39. data/ext/qiita_marker/inlines.h +29 -0
  40. data/ext/qiita_marker/iterator.c +159 -0
  41. data/ext/qiita_marker/iterator.h +26 -0
  42. data/ext/qiita_marker/latex.c +466 -0
  43. data/ext/qiita_marker/linked_list.c +37 -0
  44. data/ext/qiita_marker/man.c +278 -0
  45. data/ext/qiita_marker/map.c +122 -0
  46. data/ext/qiita_marker/map.h +41 -0
  47. data/ext/qiita_marker/node.c +979 -0
  48. data/ext/qiita_marker/node.h +125 -0
  49. data/ext/qiita_marker/parser.h +58 -0
  50. data/ext/qiita_marker/plaintext.c +235 -0
  51. data/ext/qiita_marker/plugin.c +36 -0
  52. data/ext/qiita_marker/plugin.h +34 -0
  53. data/ext/qiita_marker/qiita_marker.c +1321 -0
  54. data/ext/qiita_marker/qiita_marker.h +16 -0
  55. data/ext/qiita_marker/references.c +42 -0
  56. data/ext/qiita_marker/references.h +26 -0
  57. data/ext/qiita_marker/registry.c +63 -0
  58. data/ext/qiita_marker/registry.h +24 -0
  59. data/ext/qiita_marker/render.c +205 -0
  60. data/ext/qiita_marker/render.h +62 -0
  61. data/ext/qiita_marker/scanners.c +10520 -0
  62. data/ext/qiita_marker/scanners.h +62 -0
  63. data/ext/qiita_marker/scanners.re +341 -0
  64. data/ext/qiita_marker/strikethrough.c +167 -0
  65. data/ext/qiita_marker/strikethrough.h +9 -0
  66. data/ext/qiita_marker/syntax_extension.c +149 -0
  67. data/ext/qiita_marker/syntax_extension.h +34 -0
  68. data/ext/qiita_marker/table.c +822 -0
  69. data/ext/qiita_marker/table.h +12 -0
  70. data/ext/qiita_marker/tagfilter.c +60 -0
  71. data/ext/qiita_marker/tagfilter.h +8 -0
  72. data/ext/qiita_marker/tasklist.c +156 -0
  73. data/ext/qiita_marker/tasklist.h +8 -0
  74. data/ext/qiita_marker/utf8.c +317 -0
  75. data/ext/qiita_marker/utf8.h +35 -0
  76. data/ext/qiita_marker/xml.c +181 -0
  77. data/lib/qiita_marker/config.rb +52 -0
  78. data/lib/qiita_marker/node/inspect.rb +57 -0
  79. data/lib/qiita_marker/node.rb +83 -0
  80. data/lib/qiita_marker/renderer/html_renderer.rb +252 -0
  81. data/lib/qiita_marker/renderer.rb +135 -0
  82. data/lib/qiita_marker/version.rb +5 -0
  83. data/lib/qiita_marker.rb +45 -0
  84. data/qiita_marker.gemspec +40 -0
  85. data/test/benchmark.rb +32 -0
  86. data/test/fixtures/curly.md +1 -0
  87. data/test/fixtures/dingus.md +10 -0
  88. data/test/fixtures/strong.md +1 -0
  89. data/test/fixtures/table.md +10 -0
  90. data/test/test_attributes.rb +24 -0
  91. data/test/test_basics.rb +35 -0
  92. data/test/test_commands.rb +72 -0
  93. data/test/test_commonmark.rb +36 -0
  94. data/test/test_doc.rb +130 -0
  95. data/test/test_encoding.rb +23 -0
  96. data/test/test_extensions.rb +116 -0
  97. data/test/test_footnotes.rb +60 -0
  98. data/test/test_gc.rb +47 -0
  99. data/test/test_helper.rb +71 -0
  100. data/test/test_linebreaks.rb +15 -0
  101. data/test/test_maliciousness.rb +262 -0
  102. data/test/test_node.rb +89 -0
  103. data/test/test_options.rb +37 -0
  104. data/test/test_pathological_inputs.rb +94 -0
  105. data/test/test_plaintext.rb +46 -0
  106. data/test/test_renderer.rb +47 -0
  107. data/test/test_smartpunct.rb +27 -0
  108. data/test/test_spec.rb +30 -0
  109. data/test/test_tasklists.rb +43 -0
  110. data/test/test_xml.rb +107 -0
  111. metadata +313 -0
@@ -0,0 +1,822 @@
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
+ // Parses a single table row. It has the following form:
118
+ // `delim? table_cell (delim table_cell)* delim? newline`
119
+ // Note that cells are allowed to be empty.
120
+ //
121
+ // From the GitHub-flavored Markdown specification:
122
+ //
123
+ // > Each row consists of cells containing arbitrary text, in which inlines
124
+ // > are parsed, separated by pipes (|). A leading and trailing pipe is also
125
+ // > recommended for clarity of reading, and if there’s otherwise parsing
126
+ // > ambiguity.
127
+
128
+ table_row *row = NULL;
129
+ bufsize_t cell_matched = 1, pipe_matched = 1, offset;
130
+ int expect_more_cells = 1;
131
+ int row_end_offset = 0;
132
+
133
+ row = (table_row *)parser->mem->calloc(1, sizeof(table_row));
134
+ row->n_columns = 0;
135
+ row->cells = NULL;
136
+
137
+ // Scan past the (optional) leading pipe.
138
+ offset = scan_table_cell_end(string, len, 0);
139
+
140
+ // Parse the cells of the row. Stop if we reach the end of the input, or if we
141
+ // cannot detect any more cells.
142
+ while (offset < len && expect_more_cells) {
143
+ cell_matched = scan_table_cell(string, len, offset);
144
+ pipe_matched = scan_table_cell_end(string, len, offset + cell_matched);
145
+
146
+ if (cell_matched || pipe_matched) {
147
+ // We are guaranteed to have a cell, since (1) either we found some
148
+ // content and cell_matched, or (2) we found an empty cell followed by a
149
+ // pipe.
150
+ cmark_strbuf *cell_buf = unescape_pipes(parser->mem, string + offset,
151
+ cell_matched);
152
+ cmark_strbuf_trim(cell_buf);
153
+
154
+ node_cell *cell = (node_cell *)parser->mem->calloc(1, sizeof(*cell));
155
+ cell->buf = cell_buf;
156
+ cell->start_offset = offset;
157
+ cell->end_offset = offset + cell_matched - 1;
158
+
159
+ while (cell->start_offset > 0 && string[cell->start_offset - 1] != '|') {
160
+ --cell->start_offset;
161
+ ++cell->internal_offset;
162
+ }
163
+
164
+ row->n_columns += 1;
165
+ row->cells = cmark_llist_append(parser->mem, row->cells, cell);
166
+ }
167
+
168
+ offset += cell_matched + pipe_matched;
169
+
170
+ if (pipe_matched) {
171
+ expect_more_cells = 1;
172
+ } else {
173
+ // We've scanned the last cell. Check if we have reached the end of the row
174
+ row_end_offset = scan_table_row_end(string, len, offset);
175
+ offset += row_end_offset;
176
+
177
+ // If the end of the row is not the end of the input,
178
+ // the row is not a real row but potentially part of the paragraph
179
+ // preceding the table.
180
+ if (row_end_offset && offset != len) {
181
+ row->paragraph_offset = offset;
182
+
183
+ cmark_llist_free_full(parser->mem, row->cells, (cmark_free_func)free_table_cell);
184
+ row->cells = NULL;
185
+ row->n_columns = 0;
186
+
187
+ // Scan past the (optional) leading pipe.
188
+ offset += scan_table_cell_end(string, len, offset);
189
+
190
+ expect_more_cells = 1;
191
+ } else {
192
+ expect_more_cells = 0;
193
+ }
194
+ }
195
+ }
196
+
197
+ if (offset != len || row->n_columns == 0) {
198
+ free_table_row(parser->mem, row);
199
+ row = NULL;
200
+ }
201
+
202
+ return row;
203
+ }
204
+
205
+ static void try_inserting_table_header_paragraph(cmark_parser *parser,
206
+ cmark_node *parent_container,
207
+ unsigned char *parent_string,
208
+ int paragraph_offset) {
209
+ cmark_node *paragraph;
210
+ cmark_strbuf *paragraph_content;
211
+
212
+ paragraph = cmark_node_new_with_mem(CMARK_NODE_PARAGRAPH, parser->mem);
213
+
214
+ paragraph_content = unescape_pipes(parser->mem, parent_string, paragraph_offset);
215
+ cmark_strbuf_trim(paragraph_content);
216
+ cmark_node_set_string_content(paragraph, (char *) paragraph_content->ptr);
217
+ cmark_strbuf_free(paragraph_content);
218
+ parser->mem->free(paragraph_content);
219
+
220
+ if (!cmark_node_insert_before(parent_container, paragraph)) {
221
+ parser->mem->free(paragraph);
222
+ }
223
+ }
224
+
225
+ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
226
+ cmark_parser *parser,
227
+ cmark_node *parent_container,
228
+ unsigned char *input, int len) {
229
+ cmark_node *table_header;
230
+ table_row *header_row = NULL;
231
+ table_row *marker_row = NULL;
232
+ node_table_row *ntr;
233
+ const char *parent_string;
234
+ uint16_t i;
235
+
236
+ if (!scan_table_start(input, len, cmark_parser_get_first_nonspace(parser))) {
237
+ return parent_container;
238
+ }
239
+
240
+ // Since scan_table_start was successful, we must have a marker row.
241
+ marker_row = row_from_string(self, parser,
242
+ input + cmark_parser_get_first_nonspace(parser),
243
+ len - cmark_parser_get_first_nonspace(parser));
244
+ assert(marker_row);
245
+
246
+ cmark_arena_push();
247
+
248
+ // Check for a matching header row. We call `row_from_string` with the entire
249
+ // (potentially long) parent container as input, but this should be safe since
250
+ // `row_from_string` bails out early if it does not find a row.
251
+ parent_string = cmark_node_get_string_content(parent_container);
252
+ header_row = row_from_string(self, parser, (unsigned char *)parent_string,
253
+ (int)strlen(parent_string));
254
+ if (!header_row || header_row->n_columns != marker_row->n_columns) {
255
+ free_table_row(parser->mem, marker_row);
256
+ free_table_row(parser->mem, header_row);
257
+ cmark_arena_pop();
258
+ return parent_container;
259
+ }
260
+
261
+ if (cmark_arena_pop()) {
262
+ marker_row = row_from_string(
263
+ self, parser, input + cmark_parser_get_first_nonspace(parser),
264
+ len - cmark_parser_get_first_nonspace(parser));
265
+ header_row = row_from_string(self, parser, (unsigned char *)parent_string,
266
+ (int)strlen(parent_string));
267
+ }
268
+
269
+ if (!cmark_node_set_type(parent_container, CMARK_NODE_TABLE)) {
270
+ free_table_row(parser->mem, header_row);
271
+ free_table_row(parser->mem, marker_row);
272
+ return parent_container;
273
+ }
274
+
275
+ if (header_row->paragraph_offset) {
276
+ try_inserting_table_header_paragraph(parser, parent_container, (unsigned char *)parent_string,
277
+ header_row->paragraph_offset);
278
+ }
279
+
280
+ cmark_node_set_syntax_extension(parent_container, self);
281
+ parent_container->as.opaque = parser->mem->calloc(1, sizeof(node_table));
282
+ set_n_table_columns(parent_container, header_row->n_columns);
283
+
284
+ uint8_t *alignments =
285
+ (uint8_t *)parser->mem->calloc(header_row->n_columns, sizeof(uint8_t));
286
+ cmark_llist *it = marker_row->cells;
287
+ for (i = 0; it; it = it->next, ++i) {
288
+ node_cell *node = (node_cell *)it->data;
289
+ bool left = node->buf->ptr[0] == ':', right = node->buf->ptr[node->buf->size - 1] == ':';
290
+
291
+ if (left && right)
292
+ alignments[i] = 'c';
293
+ else if (left)
294
+ alignments[i] = 'l';
295
+ else if (right)
296
+ alignments[i] = 'r';
297
+ }
298
+ set_table_alignments(parent_container, alignments);
299
+
300
+ table_header =
301
+ cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
302
+ parent_container->start_column);
303
+ cmark_node_set_syntax_extension(table_header, self);
304
+ table_header->end_column = parent_container->start_column + (int)strlen(parent_string) - 2;
305
+ table_header->start_line = table_header->end_line = parent_container->start_line;
306
+
307
+ table_header->as.opaque = ntr = (node_table_row *)parser->mem->calloc(1, sizeof(node_table_row));
308
+ ntr->is_header = true;
309
+
310
+ {
311
+ cmark_llist *tmp;
312
+
313
+ for (tmp = header_row->cells; tmp; tmp = tmp->next) {
314
+ node_cell *cell = (node_cell *) tmp->data;
315
+ cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
316
+ CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
317
+ header_cell->start_line = header_cell->end_line = parent_container->start_line;
318
+ header_cell->internal_offset = cell->internal_offset;
319
+ header_cell->end_column = parent_container->start_column + cell->end_offset;
320
+ cmark_node_set_string_content(header_cell, (char *) cell->buf->ptr);
321
+ cmark_node_set_syntax_extension(header_cell, self);
322
+ }
323
+ }
324
+
325
+ cmark_parser_advance_offset(
326
+ parser, (char *)input,
327
+ (int)strlen((char *)input) - 1 - cmark_parser_get_offset(parser), false);
328
+
329
+ free_table_row(parser->mem, header_row);
330
+ free_table_row(parser->mem, marker_row);
331
+ return parent_container;
332
+ }
333
+
334
+ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
335
+ cmark_parser *parser,
336
+ cmark_node *parent_container,
337
+ unsigned char *input, int len) {
338
+ cmark_node *table_row_block;
339
+ table_row *row;
340
+
341
+ if (cmark_parser_is_blank(parser))
342
+ return NULL;
343
+
344
+ table_row_block =
345
+ cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
346
+ parent_container->start_column);
347
+ cmark_node_set_syntax_extension(table_row_block, self);
348
+ table_row_block->end_column = parent_container->end_column;
349
+ table_row_block->as.opaque = parser->mem->calloc(1, sizeof(node_table_row));
350
+
351
+ row = row_from_string(self, parser, input + cmark_parser_get_first_nonspace(parser),
352
+ len - cmark_parser_get_first_nonspace(parser));
353
+
354
+ {
355
+ cmark_llist *tmp;
356
+ int i, table_columns = get_n_table_columns(parent_container);
357
+
358
+ for (tmp = row->cells, i = 0; tmp && i < table_columns; tmp = tmp->next, ++i) {
359
+ node_cell *cell = (node_cell *) tmp->data;
360
+ cmark_node *node = cmark_parser_add_child(parser, table_row_block,
361
+ CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
362
+ node->internal_offset = cell->internal_offset;
363
+ node->end_column = parent_container->start_column + cell->end_offset;
364
+ cmark_node_set_string_content(node, (char *) cell->buf->ptr);
365
+ cmark_node_set_syntax_extension(node, self);
366
+ }
367
+
368
+ for (; i < table_columns; ++i) {
369
+ cmark_node *node = cmark_parser_add_child(
370
+ parser, table_row_block, CMARK_NODE_TABLE_CELL, 0);
371
+ cmark_node_set_syntax_extension(node, self);
372
+ }
373
+ }
374
+
375
+ free_table_row(parser->mem, row);
376
+
377
+ cmark_parser_advance_offset(parser, (char *)input,
378
+ len - 1 - cmark_parser_get_offset(parser), false);
379
+
380
+ return table_row_block;
381
+ }
382
+
383
+ static cmark_node *try_opening_table_block(cmark_syntax_extension *self,
384
+ int indented, cmark_parser *parser,
385
+ cmark_node *parent_container,
386
+ unsigned char *input, int len) {
387
+ cmark_node_type parent_type = cmark_node_get_type(parent_container);
388
+
389
+ if (!indented && parent_type == CMARK_NODE_PARAGRAPH) {
390
+ return try_opening_table_header(self, parser, parent_container, input, len);
391
+ } else if (!indented && parent_type == CMARK_NODE_TABLE) {
392
+ return try_opening_table_row(self, parser, parent_container, input, len);
393
+ }
394
+
395
+ return NULL;
396
+ }
397
+
398
+ static int matches(cmark_syntax_extension *self, cmark_parser *parser,
399
+ unsigned char *input, int len,
400
+ cmark_node *parent_container) {
401
+ int res = 0;
402
+
403
+ if (cmark_node_get_type(parent_container) == CMARK_NODE_TABLE) {
404
+ cmark_arena_push();
405
+ table_row *new_row = row_from_string(
406
+ self, parser, input + cmark_parser_get_first_nonspace(parser),
407
+ len - cmark_parser_get_first_nonspace(parser));
408
+ if (new_row && new_row->n_columns)
409
+ res = 1;
410
+ free_table_row(parser->mem, new_row);
411
+ cmark_arena_pop();
412
+ }
413
+
414
+ return res;
415
+ }
416
+
417
+ static const char *get_type_string(cmark_syntax_extension *self,
418
+ cmark_node *node) {
419
+ if (node->type == CMARK_NODE_TABLE) {
420
+ return "table";
421
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
422
+ if (((node_table_row *)node->as.opaque)->is_header)
423
+ return "table_header";
424
+ else
425
+ return "table_row";
426
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
427
+ return "table_cell";
428
+ }
429
+
430
+ return "<unknown>";
431
+ }
432
+
433
+ static int can_contain(cmark_syntax_extension *extension, cmark_node *node,
434
+ cmark_node_type child_type) {
435
+ if (node->type == CMARK_NODE_TABLE) {
436
+ return child_type == CMARK_NODE_TABLE_ROW;
437
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
438
+ return child_type == CMARK_NODE_TABLE_CELL;
439
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
440
+ return child_type == CMARK_NODE_TEXT || child_type == CMARK_NODE_CODE ||
441
+ child_type == CMARK_NODE_EMPH || child_type == CMARK_NODE_STRONG ||
442
+ child_type == CMARK_NODE_LINK || child_type == CMARK_NODE_IMAGE ||
443
+ child_type == CMARK_NODE_STRIKETHROUGH ||
444
+ child_type == CMARK_NODE_HTML_INLINE ||
445
+ child_type == CMARK_NODE_FOOTNOTE_REFERENCE;
446
+ }
447
+ return false;
448
+ }
449
+
450
+ static int contains_inlines(cmark_syntax_extension *extension,
451
+ cmark_node *node) {
452
+ return node->type == CMARK_NODE_TABLE_CELL;
453
+ }
454
+
455
+ static void commonmark_render(cmark_syntax_extension *extension,
456
+ cmark_renderer *renderer, cmark_node *node,
457
+ cmark_event_type ev_type, int options) {
458
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
459
+
460
+ if (node->type == CMARK_NODE_TABLE) {
461
+ renderer->blankline(renderer);
462
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
463
+ if (entering) {
464
+ renderer->cr(renderer);
465
+ renderer->out(renderer, node, "|", false, LITERAL);
466
+ }
467
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
468
+ if (entering) {
469
+ renderer->out(renderer, node, " ", false, LITERAL);
470
+ } else {
471
+ renderer->out(renderer, node, " |", false, LITERAL);
472
+ if (((node_table_row *)node->parent->as.opaque)->is_header &&
473
+ !node->next) {
474
+ int i;
475
+ uint8_t *alignments = get_table_alignments(node->parent->parent);
476
+ uint16_t n_cols =
477
+ ((node_table *)node->parent->parent->as.opaque)->n_columns;
478
+ renderer->cr(renderer);
479
+ renderer->out(renderer, node, "|", false, LITERAL);
480
+ for (i = 0; i < n_cols; i++) {
481
+ switch (alignments[i]) {
482
+ case 0: renderer->out(renderer, node, " --- |", false, LITERAL); break;
483
+ case 'l': renderer->out(renderer, node, " :-- |", false, LITERAL); break;
484
+ case 'c': renderer->out(renderer, node, " :-: |", false, LITERAL); break;
485
+ case 'r': renderer->out(renderer, node, " --: |", false, LITERAL); break;
486
+ }
487
+ }
488
+ renderer->cr(renderer);
489
+ }
490
+ }
491
+ } else {
492
+ assert(false);
493
+ }
494
+ }
495
+
496
+ static void latex_render(cmark_syntax_extension *extension,
497
+ cmark_renderer *renderer, cmark_node *node,
498
+ cmark_event_type ev_type, int options) {
499
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
500
+
501
+ if (node->type == CMARK_NODE_TABLE) {
502
+ if (entering) {
503
+ int i;
504
+ uint16_t n_cols;
505
+ uint8_t *alignments = get_table_alignments(node);
506
+
507
+ renderer->cr(renderer);
508
+ renderer->out(renderer, node, "\\begin{table}", false, LITERAL);
509
+ renderer->cr(renderer);
510
+ renderer->out(renderer, node, "\\begin{tabular}{", false, LITERAL);
511
+
512
+ n_cols = ((node_table *)node->as.opaque)->n_columns;
513
+ for (i = 0; i < n_cols; i++) {
514
+ switch(alignments[i]) {
515
+ case 0:
516
+ case 'l':
517
+ renderer->out(renderer, node, "l", false, LITERAL);
518
+ break;
519
+ case 'c':
520
+ renderer->out(renderer, node, "c", false, LITERAL);
521
+ break;
522
+ case 'r':
523
+ renderer->out(renderer, node, "r", false, LITERAL);
524
+ break;
525
+ }
526
+ }
527
+ renderer->out(renderer, node, "}", false, LITERAL);
528
+ renderer->cr(renderer);
529
+ } else {
530
+ renderer->out(renderer, node, "\\end{tabular}", false, LITERAL);
531
+ renderer->cr(renderer);
532
+ renderer->out(renderer, node, "\\end{table}", false, LITERAL);
533
+ renderer->cr(renderer);
534
+ }
535
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
536
+ if (!entering) {
537
+ renderer->cr(renderer);
538
+ }
539
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
540
+ if (!entering) {
541
+ if (node->next) {
542
+ renderer->out(renderer, node, " & ", false, LITERAL);
543
+ } else {
544
+ renderer->out(renderer, node, " \\\\", false, LITERAL);
545
+ }
546
+ }
547
+ } else {
548
+ assert(false);
549
+ }
550
+ }
551
+
552
+ static const char *xml_attr(cmark_syntax_extension *extension,
553
+ cmark_node *node) {
554
+ if (node->type == CMARK_NODE_TABLE_CELL) {
555
+ if (cmark_gfm_extensions_get_table_row_is_header(node->parent)) {
556
+ uint8_t *alignments = get_table_alignments(node->parent->parent);
557
+ int i = 0;
558
+ cmark_node *n;
559
+ for (n = node->parent->first_child; n; n = n->next, ++i)
560
+ if (n == node)
561
+ break;
562
+ switch (alignments[i]) {
563
+ case 'l': return " align=\"left\"";
564
+ case 'c': return " align=\"center\"";
565
+ case 'r': return " align=\"right\"";
566
+ }
567
+ }
568
+ }
569
+
570
+ return NULL;
571
+ }
572
+
573
+ static void man_render(cmark_syntax_extension *extension,
574
+ cmark_renderer *renderer, cmark_node *node,
575
+ cmark_event_type ev_type, int options) {
576
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
577
+
578
+ if (node->type == CMARK_NODE_TABLE) {
579
+ if (entering) {
580
+ int i;
581
+ uint16_t n_cols;
582
+ uint8_t *alignments = get_table_alignments(node);
583
+
584
+ renderer->cr(renderer);
585
+ renderer->out(renderer, node, ".TS", false, LITERAL);
586
+ renderer->cr(renderer);
587
+ renderer->out(renderer, node, "tab(@);", false, LITERAL);
588
+ renderer->cr(renderer);
589
+
590
+ n_cols = ((node_table *)node->as.opaque)->n_columns;
591
+
592
+ for (i = 0; i < n_cols; i++) {
593
+ switch (alignments[i]) {
594
+ case 'l':
595
+ renderer->out(renderer, node, "l", false, LITERAL);
596
+ break;
597
+ case 0:
598
+ case 'c':
599
+ renderer->out(renderer, node, "c", false, LITERAL);
600
+ break;
601
+ case 'r':
602
+ renderer->out(renderer, node, "r", false, LITERAL);
603
+ break;
604
+ }
605
+ }
606
+
607
+ if (n_cols) {
608
+ renderer->out(renderer, node, ".", false, LITERAL);
609
+ renderer->cr(renderer);
610
+ }
611
+ } else {
612
+ renderer->out(renderer, node, ".TE", false, LITERAL);
613
+ renderer->cr(renderer);
614
+ }
615
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
616
+ if (!entering) {
617
+ renderer->cr(renderer);
618
+ }
619
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
620
+ if (!entering && node->next) {
621
+ renderer->out(renderer, node, "@", false, LITERAL);
622
+ }
623
+ } else {
624
+ assert(false);
625
+ }
626
+ }
627
+
628
+ static void html_table_add_align(cmark_strbuf* html, const char* align, int options) {
629
+ if (options & CMARK_OPT_TABLE_PREFER_STYLE_ATTRIBUTES) {
630
+ cmark_strbuf_puts(html, " style=\"text-align: ");
631
+ cmark_strbuf_puts(html, align);
632
+ cmark_strbuf_puts(html, "\"");
633
+ } else {
634
+ cmark_strbuf_puts(html, " align=\"");
635
+ cmark_strbuf_puts(html, align);
636
+ cmark_strbuf_puts(html, "\"");
637
+ }
638
+ }
639
+
640
+ struct html_table_state {
641
+ unsigned need_closing_table_body : 1;
642
+ unsigned in_table_header : 1;
643
+ };
644
+
645
+ static void html_render(cmark_syntax_extension *extension,
646
+ cmark_html_renderer *renderer, cmark_node *node,
647
+ cmark_event_type ev_type, int options) {
648
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
649
+ cmark_strbuf *html = renderer->html;
650
+ cmark_node *n;
651
+
652
+ // XXX: we just monopolise renderer->opaque.
653
+ struct html_table_state *table_state =
654
+ (struct html_table_state *)&renderer->opaque;
655
+
656
+ if (node->type == CMARK_NODE_TABLE) {
657
+ if (entering) {
658
+ cmark_html_render_cr(html);
659
+ cmark_strbuf_puts(html, "<table");
660
+ cmark_html_render_sourcepos(node, html, options);
661
+ cmark_strbuf_putc(html, '>');
662
+ table_state->need_closing_table_body = false;
663
+ } else {
664
+ if (table_state->need_closing_table_body) {
665
+ cmark_html_render_cr(html);
666
+ cmark_strbuf_puts(html, "</tbody>");
667
+ cmark_html_render_cr(html);
668
+ }
669
+ table_state->need_closing_table_body = false;
670
+ cmark_html_render_cr(html);
671
+ cmark_strbuf_puts(html, "</table>");
672
+ cmark_html_render_cr(html);
673
+ }
674
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
675
+ if (entering) {
676
+ cmark_html_render_cr(html);
677
+ if (((node_table_row *)node->as.opaque)->is_header) {
678
+ table_state->in_table_header = 1;
679
+ cmark_strbuf_puts(html, "<thead>");
680
+ cmark_html_render_cr(html);
681
+ } else if (!table_state->need_closing_table_body) {
682
+ cmark_strbuf_puts(html, "<tbody>");
683
+ cmark_html_render_cr(html);
684
+ table_state->need_closing_table_body = 1;
685
+ }
686
+ cmark_strbuf_puts(html, "<tr");
687
+ cmark_html_render_sourcepos(node, html, options);
688
+ cmark_strbuf_putc(html, '>');
689
+ } else {
690
+ cmark_html_render_cr(html);
691
+ cmark_strbuf_puts(html, "</tr>");
692
+ if (((node_table_row *)node->as.opaque)->is_header) {
693
+ cmark_html_render_cr(html);
694
+ cmark_strbuf_puts(html, "</thead>");
695
+ table_state->in_table_header = false;
696
+ }
697
+ }
698
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
699
+ uint8_t *alignments = get_table_alignments(node->parent->parent);
700
+ if (entering) {
701
+ cmark_html_render_cr(html);
702
+ if (table_state->in_table_header) {
703
+ cmark_strbuf_puts(html, "<th");
704
+ } else {
705
+ cmark_strbuf_puts(html, "<td");
706
+ }
707
+
708
+ int i = 0;
709
+ for (n = node->parent->first_child; n; n = n->next, ++i)
710
+ if (n == node)
711
+ break;
712
+
713
+ switch (alignments[i]) {
714
+ case 'l': html_table_add_align(html, "left", options); break;
715
+ case 'c': html_table_add_align(html, "center", options); break;
716
+ case 'r': html_table_add_align(html, "right", options); break;
717
+ }
718
+
719
+ cmark_html_render_sourcepos(node, html, options);
720
+ cmark_strbuf_putc(html, '>');
721
+ } else {
722
+ if (table_state->in_table_header) {
723
+ cmark_strbuf_puts(html, "</th>");
724
+ } else {
725
+ cmark_strbuf_puts(html, "</td>");
726
+ }
727
+ }
728
+ } else {
729
+ assert(false);
730
+ }
731
+ }
732
+
733
+ static void opaque_alloc(cmark_syntax_extension *self, cmark_mem *mem, cmark_node *node) {
734
+ if (node->type == CMARK_NODE_TABLE) {
735
+ node->as.opaque = mem->calloc(1, sizeof(node_table));
736
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
737
+ node->as.opaque = mem->calloc(1, sizeof(node_table_row));
738
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
739
+ node->as.opaque = mem->calloc(1, sizeof(node_cell));
740
+ }
741
+ }
742
+
743
+ static void opaque_free(cmark_syntax_extension *self, cmark_mem *mem, cmark_node *node) {
744
+ if (node->type == CMARK_NODE_TABLE) {
745
+ free_node_table(mem, node->as.opaque);
746
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
747
+ free_node_table_row(mem, node->as.opaque);
748
+ }
749
+ }
750
+
751
+ static int escape(cmark_syntax_extension *self, cmark_node *node, int c) {
752
+ return
753
+ node->type != CMARK_NODE_TABLE &&
754
+ node->type != CMARK_NODE_TABLE_ROW &&
755
+ node->type != CMARK_NODE_TABLE_CELL &&
756
+ c == '|';
757
+ }
758
+
759
+ cmark_syntax_extension *create_table_extension(void) {
760
+ cmark_syntax_extension *self = cmark_syntax_extension_new("table");
761
+
762
+ cmark_syntax_extension_set_match_block_func(self, matches);
763
+ cmark_syntax_extension_set_open_block_func(self, try_opening_table_block);
764
+ cmark_syntax_extension_set_get_type_string_func(self, get_type_string);
765
+ cmark_syntax_extension_set_can_contain_func(self, can_contain);
766
+ cmark_syntax_extension_set_contains_inlines_func(self, contains_inlines);
767
+ cmark_syntax_extension_set_commonmark_render_func(self, commonmark_render);
768
+ cmark_syntax_extension_set_plaintext_render_func(self, commonmark_render);
769
+ cmark_syntax_extension_set_latex_render_func(self, latex_render);
770
+ cmark_syntax_extension_set_xml_attr_func(self, xml_attr);
771
+ cmark_syntax_extension_set_man_render_func(self, man_render);
772
+ cmark_syntax_extension_set_html_render_func(self, html_render);
773
+ cmark_syntax_extension_set_opaque_alloc_func(self, opaque_alloc);
774
+ cmark_syntax_extension_set_opaque_free_func(self, opaque_free);
775
+ cmark_syntax_extension_set_commonmark_escape_func(self, escape);
776
+ CMARK_NODE_TABLE = cmark_syntax_extension_add_node(0);
777
+ CMARK_NODE_TABLE_ROW = cmark_syntax_extension_add_node(0);
778
+ CMARK_NODE_TABLE_CELL = cmark_syntax_extension_add_node(0);
779
+
780
+ return self;
781
+ }
782
+
783
+ uint16_t cmark_gfm_extensions_get_table_columns(cmark_node *node) {
784
+ if (node->type != CMARK_NODE_TABLE)
785
+ return 0;
786
+
787
+ return ((node_table *)node->as.opaque)->n_columns;
788
+ }
789
+
790
+ uint8_t *cmark_gfm_extensions_get_table_alignments(cmark_node *node) {
791
+ if (node->type != CMARK_NODE_TABLE)
792
+ return 0;
793
+
794
+ return ((node_table *)node->as.opaque)->alignments;
795
+ }
796
+
797
+ int cmark_gfm_extensions_set_table_columns(cmark_node *node, uint16_t n_columns) {
798
+ return set_n_table_columns(node, n_columns);
799
+ }
800
+
801
+ int cmark_gfm_extensions_set_table_alignments(cmark_node *node, uint16_t ncols, uint8_t *alignments) {
802
+ uint8_t *a = (uint8_t *)cmark_node_mem(node)->calloc(1, ncols);
803
+ memcpy(a, alignments, ncols);
804
+ return set_table_alignments(node, a);
805
+ }
806
+
807
+ int cmark_gfm_extensions_get_table_row_is_header(cmark_node *node)
808
+ {
809
+ if (!node || node->type != CMARK_NODE_TABLE_ROW)
810
+ return 0;
811
+
812
+ return ((node_table_row *)node->as.opaque)->is_header;
813
+ }
814
+
815
+ int cmark_gfm_extensions_set_table_row_is_header(cmark_node *node, int is_header)
816
+ {
817
+ if (!node || node->type != CMARK_NODE_TABLE_ROW)
818
+ return 0;
819
+
820
+ ((node_table_row *)node->as.opaque)->is_header = (is_header != 0);
821
+ return 1;
822
+ }