commonmarker 0.23.7.pre1 → 1.0.0.pre.2

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