markly 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/conduct.md +133 -0
  4. data/ext/markly/arena.c +9 -8
  5. data/ext/markly/autolink.c +217 -134
  6. data/ext/markly/blocks.c +40 -4
  7. data/ext/markly/cmark-gfm-core-extensions.h +11 -11
  8. data/ext/markly/cmark-gfm-extension_api.h +1 -0
  9. data/ext/markly/cmark-gfm.h +18 -2
  10. data/ext/markly/cmark-gfm_version.h +2 -2
  11. data/ext/markly/cmark.c +3 -3
  12. data/ext/markly/commonmark.c +33 -38
  13. data/ext/markly/ext_scanners.c +360 -640
  14. data/ext/markly/extconf.rb +8 -1
  15. data/ext/markly/footnotes.c +23 -0
  16. data/ext/markly/footnotes.h +2 -0
  17. data/ext/markly/html.c +60 -23
  18. data/ext/markly/inlines.c +216 -61
  19. data/ext/markly/latex.c +6 -4
  20. data/ext/markly/man.c +7 -11
  21. data/ext/markly/map.c +11 -4
  22. data/ext/markly/map.h +5 -2
  23. data/ext/markly/markly.c +582 -586
  24. data/ext/markly/markly.h +1 -1
  25. data/ext/markly/node.c +76 -10
  26. data/ext/markly/node.h +49 -1
  27. data/ext/markly/parser.h +1 -0
  28. data/ext/markly/plaintext.c +12 -29
  29. data/ext/markly/references.c +1 -0
  30. data/ext/markly/render.c +15 -7
  31. data/ext/markly/scanners.c +13916 -20242
  32. data/ext/markly/scanners.h +8 -0
  33. data/ext/markly/scanners.re +47 -8
  34. data/ext/markly/strikethrough.c +1 -1
  35. data/ext/markly/table.c +143 -74
  36. data/ext/markly/xml.c +2 -1
  37. data/lib/markly/flags.rb +16 -0
  38. data/lib/markly/node/inspect.rb +59 -53
  39. data/lib/markly/node.rb +125 -58
  40. data/lib/markly/renderer/generic.rb +136 -0
  41. data/lib/markly/renderer/html.rb +301 -0
  42. data/lib/markly/version.rb +7 -1
  43. data/lib/markly.rb +38 -32
  44. data/license.md +39 -0
  45. data/readme.md +36 -0
  46. data.tar.gz.sig +0 -0
  47. metadata +63 -31
  48. metadata.gz.sig +0 -0
  49. data/bin/markly +0 -94
  50. data/lib/markly/markly.so +0 -0
  51. data/lib/markly/renderer/html_renderer.rb +0 -281
  52. data/lib/markly/renderer.rb +0 -133
@@ -15,6 +15,10 @@ bufsize_t _scan_autolink_uri(const unsigned char *p);
15
15
  bufsize_t _scan_autolink_email(const unsigned char *p);
16
16
  bufsize_t _scan_html_tag(const unsigned char *p);
17
17
  bufsize_t _scan_liberal_html_tag(const unsigned char *p);
18
+ bufsize_t _scan_html_comment(const unsigned char *p);
19
+ bufsize_t _scan_html_pi(const unsigned char *p);
20
+ bufsize_t _scan_html_declaration(const unsigned char *p);
21
+ bufsize_t _scan_html_cdata(const unsigned char *p);
18
22
  bufsize_t _scan_html_block_start(const unsigned char *p);
19
23
  bufsize_t _scan_html_block_start_7(const unsigned char *p);
20
24
  bufsize_t _scan_html_block_end_1(const unsigned char *p);
@@ -37,6 +41,10 @@ bufsize_t _scan_footnote_definition(const unsigned char *p);
37
41
  #define scan_autolink_email(c, n) _scan_at(&_scan_autolink_email, c, n)
38
42
  #define scan_html_tag(c, n) _scan_at(&_scan_html_tag, c, n)
39
43
  #define scan_liberal_html_tag(c, n) _scan_at(&_scan_liberal_html_tag, c, n)
44
+ #define scan_html_comment(c, n) _scan_at(&_scan_html_comment, c, n)
45
+ #define scan_html_pi(c, n) _scan_at(&_scan_html_pi, c, n)
46
+ #define scan_html_declaration(c, n) _scan_at(&_scan_html_declaration, c, n)
47
+ #define scan_html_cdata(c, n) _scan_at(&_scan_html_cdata, c, n)
40
48
  #define scan_html_block_start(c, n) _scan_at(&_scan_html_block_start, c, n)
41
49
  #define scan_html_block_start_7(c, n) _scan_at(&_scan_html_block_start_7, c, n)
42
50
  #define scan_html_block_end_1(c, n) _scan_at(&_scan_html_block_end_1, c, n)
@@ -54,16 +54,15 @@ bufsize_t _scan_at(bufsize_t (*scanner)(const unsigned char *), cmark_chunk *c,
54
54
  opentag = tagname attribute* spacechar* [/]? [>];
55
55
  closetag = [/] tagname spacechar* [>];
56
56
 
57
- htmlcomment = "!---->" | ("!--" ([-]? [^\x00>-]) ([-]? [^\x00-])* "-->");
57
+ htmlcomment = "--" ([^\x00-]+ | "-" [^\x00-] | "--" [^\x00>])* "-->";
58
58
 
59
- processinginstruction = "?" ([^?>\x00]+ | [?][^>\x00] | [>])* "?>";
59
+ processinginstruction = ([^?>\x00]+ | [?][^>\x00] | [>])+;
60
60
 
61
- declaration = "!" [A-Z]+ spacechar+ [^>\x00]* ">";
61
+ declaration = [A-Z]+ spacechar+ [^>\x00]*;
62
62
 
63
- cdata = "![CDATA[" ([^\]\x00]+ | "]" [^\]\x00] | "]]" [^>\x00])* "]]>";
63
+ cdata = "CDATA[" ([^\]\x00]+ | "]" [^\]\x00] | "]]" [^>\x00])*;
64
64
 
65
- htmltag = opentag | closetag | htmlcomment | processinginstruction |
66
- declaration | cdata;
65
+ htmltag = opentag | closetag;
67
66
 
68
67
  in_parens_nosp = [(] (reg_char|escaped_char|[\\])* [)];
69
68
 
@@ -133,6 +132,46 @@ bufsize_t _scan_liberal_html_tag(const unsigned char *p)
133
132
  */
134
133
  }
135
134
 
135
+ bufsize_t _scan_html_comment(const unsigned char *p)
136
+ {
137
+ const unsigned char *marker = NULL;
138
+ const unsigned char *start = p;
139
+ /*!re2c
140
+ htmlcomment { return (bufsize_t)(p - start); }
141
+ * { return 0; }
142
+ */
143
+ }
144
+
145
+ bufsize_t _scan_html_pi(const unsigned char *p)
146
+ {
147
+ const unsigned char *marker = NULL;
148
+ const unsigned char *start = p;
149
+ /*!re2c
150
+ processinginstruction { return (bufsize_t)(p - start); }
151
+ * { return 0; }
152
+ */
153
+ }
154
+
155
+ bufsize_t _scan_html_declaration(const unsigned char *p)
156
+ {
157
+ const unsigned char *marker = NULL;
158
+ const unsigned char *start = p;
159
+ /*!re2c
160
+ declaration { return (bufsize_t)(p - start); }
161
+ * { return 0; }
162
+ */
163
+ }
164
+
165
+ bufsize_t _scan_html_cdata(const unsigned char *p)
166
+ {
167
+ const unsigned char *marker = NULL;
168
+ const unsigned char *start = p;
169
+ /*!re2c
170
+ cdata { return (bufsize_t)(p - start); }
171
+ * { return 0; }
172
+ */
173
+ }
174
+
136
175
  // Try to match an HTML block tag start line, returning
137
176
  // an integer code for the type of block (1-6, matching the spec).
138
177
  // #7 is handled by a separate function, below.
@@ -140,7 +179,7 @@ bufsize_t _scan_html_block_start(const unsigned char *p)
140
179
  {
141
180
  const unsigned char *marker = NULL;
142
181
  /*!re2c
143
- [<] ('script'|'pre'|'style') (spacechar | [>]) { return 1; }
182
+ [<] ('script'|'pre'|'textarea'|'style') (spacechar | [>]) { return 1; }
144
183
  '<!--' { return 2; }
145
184
  '<?' { return 3; }
146
185
  '<!' [A-Z] { return 4; }
@@ -167,7 +206,7 @@ bufsize_t _scan_html_block_end_1(const unsigned char *p)
167
206
  const unsigned char *marker = NULL;
168
207
  const unsigned char *start = p;
169
208
  /*!re2c
170
- [^\n\x00]* [<] [/] ('script'|'pre'|'style') [>] { return (bufsize_t)(p - start); }
209
+ [^\n\x00]* [<] [/] ('script'|'pre'|'textarea'|'style') [>] { return (bufsize_t)(p - start); }
171
210
  * { return 0; }
172
211
  */
173
212
  }
@@ -67,6 +67,7 @@ static delimiter *insert(cmark_syntax_extension *self, cmark_parser *parser,
67
67
  strikethrough->end_column = closer->inl_text->start_column + closer->inl_text->as.literal.len - 1;
68
68
  cmark_node_free(closer->inl_text);
69
69
 
70
+ done:
70
71
  delim = closer;
71
72
  while (delim != NULL && delim != opener) {
72
73
  tmp_delim = delim->previous;
@@ -76,7 +77,6 @@ static delimiter *insert(cmark_syntax_extension *self, cmark_parser *parser,
76
77
 
77
78
  cmark_inline_parser_remove_delimiter(inline_parser, opener);
78
79
 
79
- done:
80
80
  return res;
81
81
  }
82
82
 
data/ext/markly/table.c CHANGED
@@ -11,13 +11,21 @@
11
11
  #include "table.h"
12
12
  #include "cmark-gfm-core-extensions.h"
13
13
 
14
+ // Custom node flag, initialized in `create_table_extension`.
15
+ static cmark_node_internal_flags CMARK_NODE__TABLE_VISITED;
16
+
14
17
  cmark_node_type CMARK_NODE_TABLE, CMARK_NODE_TABLE_ROW,
15
18
  CMARK_NODE_TABLE_CELL;
16
19
 
20
+ typedef struct {
21
+ cmark_strbuf *buf;
22
+ int start_offset, end_offset, internal_offset;
23
+ } node_cell;
24
+
17
25
  typedef struct {
18
26
  uint16_t n_columns;
19
27
  int paragraph_offset;
20
- cmark_llist *cells;
28
+ node_cell *cells;
21
29
  } table_row;
22
30
 
23
31
  typedef struct {
@@ -29,24 +37,24 @@ typedef struct {
29
37
  bool is_header;
30
38
  } node_table_row;
31
39
 
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;
40
+ static void free_table_cell(cmark_mem *mem, node_cell *cell) {
39
41
  cmark_strbuf_free((cmark_strbuf *)cell->buf);
40
42
  mem->free(cell->buf);
41
- mem->free(cell);
43
+ }
44
+
45
+ static void free_row_cells(cmark_mem *mem, table_row *row) {
46
+ while (row->n_columns > 0) {
47
+ free_table_cell(mem, &row->cells[--row->n_columns]);
48
+ }
49
+ mem->free(row->cells);
50
+ row->cells = NULL;
42
51
  }
43
52
 
44
53
  static void free_table_row(cmark_mem *mem, table_row *row) {
45
54
  if (!row)
46
55
  return;
47
56
 
48
- cmark_llist_free_full(mem, row->cells, (cmark_free_func)free_table_cell);
49
-
57
+ free_row_cells(mem, row);
50
58
  mem->free(row);
51
59
  }
52
60
 
@@ -111,63 +119,111 @@ static cmark_strbuf *unescape_pipes(cmark_mem *mem, unsigned char *string, bufsi
111
119
  return res;
112
120
  }
113
121
 
122
+ // Adds a new cell to the end of the row. A pointer to the new cell is returned
123
+ // for the caller to initialize.
124
+ static node_cell* append_row_cell(cmark_mem *mem, table_row *row) {
125
+ const uint32_t n_columns = row->n_columns + 1;
126
+ // realloc when n_columns is a power of 2
127
+ if ((n_columns & (n_columns-1)) == 0) {
128
+ // make sure we never wrap row->n_columns
129
+ // offset will != len and our exit will clean up as intended
130
+ if (n_columns > UINT16_MAX) {
131
+ return NULL;
132
+ }
133
+ // Use realloc to double the size of the buffer.
134
+ row->cells = (node_cell *)mem->realloc(row->cells, (2 * n_columns - 1) * sizeof(node_cell));
135
+ }
136
+ row->n_columns = (uint16_t)n_columns;
137
+ return &row->cells[n_columns-1];
138
+ }
139
+
114
140
  static table_row *row_from_string(cmark_syntax_extension *self,
115
141
  cmark_parser *parser, unsigned char *string,
116
142
  int len) {
143
+ // Parses a single table row. It has the following form:
144
+ // `delim? table_cell (delim table_cell)* delim? newline`
145
+ // Note that cells are allowed to be empty.
146
+ //
147
+ // From the GitHub-flavored Markdown specification:
148
+ //
149
+ // > Each row consists of cells containing arbitrary text, in which inlines
150
+ // > are parsed, separated by pipes (|). A leading and trailing pipe is also
151
+ // > recommended for clarity of reading, and if there’s otherwise parsing
152
+ // > ambiguity.
153
+
117
154
  table_row *row = NULL;
118
155
  bufsize_t cell_matched = 1, pipe_matched = 1, offset;
119
- int cell_end_offset;
156
+ int expect_more_cells = 1;
157
+ int row_end_offset = 0;
158
+ int int_overflow_abort = 0;
120
159
 
121
160
  row = (table_row *)parser->mem->calloc(1, sizeof(table_row));
122
161
  row->n_columns = 0;
123
162
  row->cells = NULL;
124
163
 
164
+ // Scan past the (optional) leading pipe.
125
165
  offset = scan_table_cell_end(string, len, 0);
126
166
 
127
167
  // Parse the cells of the row. Stop if we reach the end of the input, or if we
128
168
  // cannot detect any more cells.
129
- while (offset < len && (cell_matched || pipe_matched)) {
169
+ while (offset < len && expect_more_cells) {
130
170
  cell_matched = scan_table_cell(string, len, offset);
131
171
  pipe_matched = scan_table_cell_end(string, len, offset + cell_matched);
132
172
 
133
173
  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);
174
+ // We are guaranteed to have a cell, since (1) either we found some
175
+ // content and cell_matched, or (2) we found an empty cell followed by a
176
+ // pipe.
177
+ cmark_strbuf *cell_buf = unescape_pipes(parser->mem, string + offset,
178
+ cell_matched);
179
+ cmark_strbuf_trim(cell_buf);
180
+
181
+ node_cell *cell = append_row_cell(parser->mem, row);
182
+ if (!cell) {
183
+ int_overflow_abort = 1;
184
+ cmark_strbuf_free(cell_buf);
185
+ parser->mem->free(cell_buf);
186
+ break;
187
+ }
188
+ cell->buf = cell_buf;
189
+ cell->start_offset = offset;
190
+ cell->end_offset = offset + cell_matched - 1;
191
+ cell->internal_offset = 0;
192
+
193
+ while (cell->start_offset > row->paragraph_offset && string[cell->start_offset - 1] != '|') {
194
+ --cell->start_offset;
195
+ ++cell->internal_offset;
159
196
  }
160
197
  }
161
198
 
162
199
  offset += cell_matched + pipe_matched;
163
200
 
164
- if (!pipe_matched) {
165
- pipe_matched = scan_table_row_end(string, len, offset);
166
- offset += pipe_matched;
201
+ if (pipe_matched) {
202
+ expect_more_cells = 1;
203
+ } else {
204
+ // We've scanned the last cell. Check if we have reached the end of the row
205
+ row_end_offset = scan_table_row_end(string, len, offset);
206
+ offset += row_end_offset;
207
+
208
+ // If the end of the row is not the end of the input,
209
+ // the row is not a real row but potentially part of the paragraph
210
+ // preceding the table.
211
+ if (row_end_offset && offset != len) {
212
+ row->paragraph_offset = offset;
213
+
214
+ free_row_cells(parser->mem, row);
215
+
216
+ // Scan past the (optional) leading pipe.
217
+ offset += scan_table_cell_end(string, len, offset);
218
+
219
+ expect_more_cells = 1;
220
+ } else {
221
+ expect_more_cells = 0;
222
+ }
167
223
  }
168
224
  }
169
225
 
170
- if (offset != len || !row->n_columns) {
226
+ if (offset != len || row->n_columns == 0 || int_overflow_abort) {
171
227
  free_table_row(parser->mem, row);
172
228
  row = NULL;
173
229
  }
@@ -199,8 +255,6 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
199
255
  cmark_parser *parser,
200
256
  cmark_node *parent_container,
201
257
  unsigned char *input, int len) {
202
- bufsize_t matched =
203
- scan_table_start(input, len, cmark_parser_get_first_nonspace(parser));
204
258
  cmark_node *table_header;
205
259
  table_row *header_row = NULL;
206
260
  table_row *marker_row = NULL;
@@ -208,41 +262,53 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
208
262
  const char *parent_string;
209
263
  uint16_t i;
210
264
 
211
- if (!matched)
265
+ if (parent_container->flags & CMARK_NODE__TABLE_VISITED) {
212
266
  return parent_container;
267
+ }
213
268
 
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();
269
+ if (!scan_table_start(input, len, cmark_parser_get_first_nonspace(parser))) {
224
270
  return parent_container;
225
271
  }
226
272
 
273
+ // Since scan_table_start was successful, we must have a marker row.
227
274
  marker_row = row_from_string(self, parser,
228
275
  input + cmark_parser_get_first_nonspace(parser),
229
276
  len - cmark_parser_get_first_nonspace(parser));
230
-
277
+ // assert may be optimized out, don't rely on it for security boundaries
278
+ if (!marker_row) {
279
+ return parent_container;
280
+ }
281
+
231
282
  assert(marker_row);
232
283
 
233
- if (header_row->n_columns != marker_row->n_columns) {
234
- free_table_row(parser->mem, header_row);
284
+ cmark_arena_push();
285
+
286
+ // Check for a matching header row. We call `row_from_string` with the entire
287
+ // (potentially long) parent container as input, but this should be safe since
288
+ // `row_from_string` bails out early if it does not find a row.
289
+ parent_string = cmark_node_get_string_content(parent_container);
290
+ header_row = row_from_string(self, parser, (unsigned char *)parent_string,
291
+ (int)strlen(parent_string));
292
+ if (!header_row || header_row->n_columns != marker_row->n_columns) {
235
293
  free_table_row(parser->mem, marker_row);
294
+ free_table_row(parser->mem, header_row);
236
295
  cmark_arena_pop();
296
+ parent_container->flags |= CMARK_NODE__TABLE_VISITED;
237
297
  return parent_container;
238
298
  }
239
299
 
240
300
  if (cmark_arena_pop()) {
301
+ marker_row = row_from_string(
302
+ self, parser, input + cmark_parser_get_first_nonspace(parser),
303
+ len - cmark_parser_get_first_nonspace(parser));
241
304
  header_row = row_from_string(self, parser, (unsigned char *)parent_string,
242
305
  (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));
306
+ // row_from_string can return NULL, add additional check to ensure n_columns match
307
+ if (!marker_row || !header_row || header_row->n_columns != marker_row->n_columns) {
308
+ free_table_row(parser->mem, marker_row);
309
+ free_table_row(parser->mem, header_row);
310
+ return parent_container;
311
+ }
246
312
  }
247
313
 
248
314
  if (!cmark_node_set_type(parent_container, CMARK_NODE_TABLE)) {
@@ -257,16 +323,15 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
257
323
  }
258
324
 
259
325
  cmark_node_set_syntax_extension(parent_container, self);
260
-
261
326
  parent_container->as.opaque = parser->mem->calloc(1, sizeof(node_table));
262
-
263
327
  set_n_table_columns(parent_container, header_row->n_columns);
264
328
 
329
+ // allocate alignments based on marker_row->n_columns
330
+ // since we populate the alignments array based on marker_row->cells
265
331
  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;
332
+ (uint8_t *)parser->mem->calloc(marker_row->n_columns, sizeof(uint8_t));
333
+ for (i = 0; i < marker_row->n_columns; ++i) {
334
+ node_cell *node = &marker_row->cells[i];
270
335
  bool left = node->buf->ptr[0] == ':', right = node->buf->ptr[node->buf->size - 1] == ':';
271
336
 
272
337
  if (left && right)
@@ -289,10 +354,8 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
289
354
  ntr->is_header = true;
290
355
 
291
356
  {
292
- cmark_llist *tmp;
293
-
294
- for (tmp = header_row->cells; tmp; tmp = tmp->next) {
295
- node_cell *cell = (node_cell *) tmp->data;
357
+ for (i = 0; i < header_row->n_columns; ++i) {
358
+ node_cell *cell = &header_row->cells[i];
296
359
  cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
297
360
  CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
298
361
  header_cell->start_line = header_cell->end_line = parent_container->start_line;
@@ -332,12 +395,17 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
332
395
  row = row_from_string(self, parser, input + cmark_parser_get_first_nonspace(parser),
333
396
  len - cmark_parser_get_first_nonspace(parser));
334
397
 
398
+ if (!row) {
399
+ // clean up the dangling node
400
+ cmark_node_free(table_row_block);
401
+ return NULL;
402
+ }
403
+
335
404
  {
336
- cmark_llist *tmp;
337
405
  int i, table_columns = get_n_table_columns(parent_container);
338
406
 
339
- for (tmp = row->cells, i = 0; tmp && i < table_columns; tmp = tmp->next, ++i) {
340
- node_cell *cell = (node_cell *) tmp->data;
407
+ for (i = 0; i < row->n_columns && i < table_columns; ++i) {
408
+ node_cell *cell = &row->cells[i];
341
409
  cmark_node *node = cmark_parser_add_child(parser, table_row_block,
342
410
  CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
343
411
  node->internal_offset = cell->internal_offset;
@@ -740,6 +808,7 @@ static int escape(cmark_syntax_extension *self, cmark_node *node, int c) {
740
808
  cmark_syntax_extension *create_table_extension(void) {
741
809
  cmark_syntax_extension *self = cmark_syntax_extension_new("table");
742
810
 
811
+ cmark_register_node_flag(&CMARK_NODE__TABLE_VISITED);
743
812
  cmark_syntax_extension_set_match_block_func(self, matches);
744
813
  cmark_syntax_extension_set_open_block_func(self, try_opening_table_block);
745
814
  cmark_syntax_extension_set_get_type_string_func(self, get_type_string);
data/ext/markly/xml.c CHANGED
@@ -11,6 +11,7 @@
11
11
  #include "syntax_extension.h"
12
12
 
13
13
  #define BUFFER_SIZE 100
14
+ #define MAX_INDENT 40
14
15
 
15
16
  // Functions to convert cmark_nodes to XML strings.
16
17
 
@@ -26,7 +27,7 @@ struct render_state {
26
27
 
27
28
  static CMARK_INLINE void indent(struct render_state *state) {
28
29
  int i;
29
- for (i = 0; i < state->indent; i++) {
30
+ for (i = 0; i < state->indent && i < MAX_INDENT; i++) {
30
31
  cmark_strbuf_putc(state->xml, ' ');
31
32
  }
32
33
  }
data/lib/markly/flags.rb CHANGED
@@ -1,12 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Released under the MIT License.
4
+ # Copyright, 2020-2023, by Samuel Williams.
5
+
3
6
  module Markly
7
+ # The default parsing system.
4
8
  DEFAULT = 0
9
+ # Replace illegal sequences with the replacement character `U+FFFD`.
5
10
  VALIDATE_UTF8 = 1 << 9
11
+ # Use smart punctuation (curly quotes, etc.).
6
12
  SMART = 1 << 10
13
+ # Support liberal parsing of inline HTML tags.
7
14
  LIBERAL_HTML_TAG = 1 << 12
15
+ # Parse footnotes.
8
16
  FOOTNOTES = 1 << 13
17
+ # Support strikethrough using double tildes.
9
18
  STRIKETHROUGH_DOUBLE_TILDE = 1 << 14
19
+ # Allow raw/custom HTML and unsafe links.
10
20
  UNSAFE = 1 << 17
11
21
 
12
22
  PARSE_FLAGS = {
@@ -18,11 +28,17 @@ module Markly
18
28
  unsafe: UNSAFE,
19
29
  }
20
30
 
31
+ # Include source position in rendered HTML.
21
32
  SOURCE_POSITION = 1 << 1
33
+ # Treat `\n` as hardbreaks (by adding `<br/>`).
22
34
  HARD_BREAKS = 1 << 2
35
+ # Translate `\n` in the source to a single whitespace.
23
36
  NO_BREAKS = 1 << 4
37
+ # Use GitHub-style `<pre lang>` for fenced code blocks.
24
38
  GITHUB_PRE_LANG = 1 << 11
39
+ # Use `style` insted of `align` for table cells.
25
40
  TABLE_PREFER_STYLE_ATTRIBUTES = 1 << 15
41
+ # Include full info strings of code blocks in separate attribute.
26
42
  FULL_INFO_STRING = 1 << 16
27
43
 
28
44
  RENDER_FLAGS = {
@@ -1,59 +1,65 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Released under the MIT License.
4
+ # Copyright, 2017, by Goro Fuji.
5
+ # Copyright, 2017-2019, by Garen Torikian.
6
+ # Copyright, 2020, by Olle Jonsson.
7
+ # Copyright, 2020-2023, by Samuel Williams.
8
+
3
9
  require 'pp'
4
10
 
5
11
  module Markly
6
- class Node
7
- module Inspect
8
- PP_INDENT_SIZE = 2
9
-
10
- def inspect
11
- PP.pp(self, +'', Float::INFINITY)
12
- end
13
-
14
- # @param printer [PrettyPrint] pp
15
- def pretty_print(printer)
16
- printer.group(PP_INDENT_SIZE, "#<#{self.class}(#{type}):", '>') do
17
- printer.breakable
18
-
19
- attrs = %i[
20
- source_position
21
- string_content
22
- url
23
- title
24
- header_level
25
- list_type
26
- list_start
27
- list_tight
28
- fence_info
29
- ].map do |name|
30
- begin
31
- [name, __send__(name)]
32
- rescue Error
33
- nil
34
- end
35
- end.compact
36
-
37
- printer.seplist(attrs) do |name, value|
38
- printer.text "#{name}="
39
- printer.pp value
40
- end
41
-
42
- if first_child
43
- printer.breakable
44
- printer.group(PP_INDENT_SIZE) do
45
- children = []
46
- node = first_child
47
- while node
48
- children << node
49
- node = node.next
50
- end
51
- printer.text 'children='
52
- printer.pp children
53
- end
54
- end
55
- end
56
- end
57
- end
58
- end
12
+ class Node
13
+ module Inspect
14
+ PP_INDENT_SIZE = 2
15
+
16
+ def inspect
17
+ PP.pp(self, +'', Float::INFINITY)
18
+ end
19
+
20
+ # @param printer [PrettyPrint] pp
21
+ def pretty_print(printer)
22
+ printer.group(PP_INDENT_SIZE, "#<#{self.class}(#{type}):", '>') do
23
+ printer.breakable
24
+
25
+ attrs = %i[
26
+ source_position
27
+ string_content
28
+ url
29
+ title
30
+ header_level
31
+ list_type
32
+ list_start
33
+ list_tight
34
+ fence_info
35
+ ].map do |name|
36
+ begin
37
+ [name, __send__(name)]
38
+ rescue Error
39
+ nil
40
+ end
41
+ end.compact
42
+
43
+ printer.seplist(attrs) do |name, value|
44
+ printer.text "#{name}="
45
+ printer.pp value
46
+ end
47
+
48
+ if first_child
49
+ printer.breakable
50
+ printer.group(PP_INDENT_SIZE) do
51
+ children = []
52
+ node = first_child
53
+ while node
54
+ children << node
55
+ node = node.next
56
+ end
57
+ printer.text 'children='
58
+ printer.pp children
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
59
65
  end