markly 0.15.3 → 0.16.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 (41) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/context/index.yaml +3 -3
  4. data/ext/markly/blocks.c +24 -6
  5. data/ext/markly/cmark-gfm.h +8 -0
  6. data/ext/markly/cmark.c +1 -1
  7. data/ext/markly/commonmark.c +13 -0
  8. data/ext/markly/extconf.rb +6 -1
  9. data/ext/markly/{table.c → extensions/table.c} +91 -46
  10. data/ext/markly/front_matter.c +141 -0
  11. data/ext/markly/front_matter.h +24 -0
  12. data/ext/markly/html.c +3 -0
  13. data/ext/markly/latex.c +3 -0
  14. data/ext/markly/man.c +3 -0
  15. data/ext/markly/markly.c +6 -0
  16. data/ext/markly/node.c +9 -2
  17. data/ext/markly/node.h +1 -0
  18. data/ext/markly/parser.h +19 -0
  19. data/ext/markly/plaintext.c +3 -0
  20. data/ext/markly/xml.c +7 -0
  21. data/lib/markly/flags.rb +7 -1
  22. data/lib/markly/version.rb +2 -2
  23. data/readme.md +12 -6
  24. data/releases.md +6 -0
  25. data.tar.gz.sig +0 -0
  26. metadata +22 -20
  27. metadata.gz.sig +0 -0
  28. /data/ext/markly/{autolink.c → extensions/autolink.c} +0 -0
  29. /data/ext/markly/{autolink.h → extensions/autolink.h} +0 -0
  30. /data/ext/markly/{cmark-gfm-core-extensions.h → extensions/cmark-gfm-core-extensions.h} +0 -0
  31. /data/ext/markly/{cmark-gfm-extensions_export.h → extensions/cmark-gfm-extensions_export.h} +0 -0
  32. /data/ext/markly/{core-extensions.c → extensions/core-extensions.c} +0 -0
  33. /data/ext/markly/{ext_scanners.c → extensions/ext_scanners.c} +0 -0
  34. /data/ext/markly/{ext_scanners.h → extensions/ext_scanners.h} +0 -0
  35. /data/ext/markly/{strikethrough.c → extensions/strikethrough.c} +0 -0
  36. /data/ext/markly/{strikethrough.h → extensions/strikethrough.h} +0 -0
  37. /data/ext/markly/{table.h → extensions/table.h} +0 -0
  38. /data/ext/markly/{tagfilter.c → extensions/tagfilter.c} +0 -0
  39. /data/ext/markly/{tagfilter.h → extensions/tagfilter.h} +0 -0
  40. /data/ext/markly/{tasklist.c → extensions/tasklist.c} +0 -0
  41. /data/ext/markly/{tasklist.h → extensions/tasklist.h} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76b9702e08b93aa97c2fe8b02d0ac1faea07b8037cf093c48d87b4b7e260806a
4
- data.tar.gz: b3cad63b1de5e67e2160c72731f28cea94d02fbe999c5b2f2201f3e94b6c84b7
3
+ metadata.gz: fda5c52f4ac015dde2e9f22e2a76467ddcb655d197897f7bbd76c7da235f2991
4
+ data.tar.gz: 936fdce7bd2fde2339e48bf43a9b4f9ae8c4b552844de6cf69bd445965477a3f
5
5
  SHA512:
6
- metadata.gz: 701981fe8e9c6a229cc93042a40b4600e9d627903dd5f50e80089e2766c506ceb780c9feb3cb0ca3cdb1621be0e58db5cff0795925c8b42324d4b763312abaf0
7
- data.tar.gz: 5500f4778efd6753dd6ffc9a5717c372c4a0d422db38695466bb2ddd9bb16a3a1ba138c34382f9ed3278c4a69ff75a29172ce581ce0138a20e4ab3b6c9b2be8a
6
+ metadata.gz: 74eae720741b8b4aee9e4d01d92d25d53cbd92f46066a4eb341881907a9c5291102b1cb10c4c59c81b2192adc4156bd6eaf619e51836ef661be03dde450f4415
7
+ data.tar.gz: 1080d1c3a72e238325e017974e34c1ad7ed56174811adcbf1c97d6b860566dd4886ebb71a9a634e8bc05826afd71045b4ef72edb622740ff72af8d6b887534ae
checksums.yaml.gz.sig CHANGED
Binary file
data/context/index.yaml CHANGED
@@ -3,9 +3,9 @@
3
3
  ---
4
4
  description: CommonMark parser and renderer. Written in C, wrapped in Ruby.
5
5
  metadata:
6
- documentation_uri: https://ioquatix.github.io/markly/
7
- funding_uri: https://github.com/sponsors/ioquatix/
8
- source_code_uri: https://github.com/ioquatix/markly.git
6
+ documentation_uri: https://socketry.github.io/markly/
7
+ funding_uri: https://github.com/sponsors/socketry/
8
+ source_code_uri: https://github.com/socketry/markly.git
9
9
  files:
10
10
  - path: getting-started.md
11
11
  title: Getting Started
data/ext/markly/blocks.c CHANGED
@@ -23,6 +23,7 @@
23
23
  #include "houdini.h"
24
24
  #include "buffer.h"
25
25
  #include "footnotes.h"
26
+ #include "front_matter.h"
26
27
 
27
28
  #define CODE_INDENT 4
28
29
  #define TAB_STOP 4
@@ -68,16 +69,12 @@ static CMARK_INLINE bool S_is_line_end_char(char c) {
68
69
  return (c == '\n' || c == '\r');
69
70
  }
70
71
 
71
- static CMARK_INLINE bool S_is_space_or_tab(char c) {
72
- return (c == ' ' || c == '\t');
73
- }
74
-
75
72
  // Returns true if block is being finalized on the same line it ends.
76
73
  // This happens for:
77
74
  // - Document node (special case)
78
75
  // - Fenced code blocks (end on the closing fence line)
79
76
  // - Setext headings (end on the underline)
80
- // - HTML blocks types 1-5 per CommonMark spec §4.6 (end on the line
77
+ // - HTML block types 1-5 per CommonMark spec §4.6 (end on the line
81
78
  // containing the closing marker)
82
79
  // - Any block finalized on the same line it started (e.g., single-line blocks)
83
80
  static CMARK_INLINE bool S_ends_on_current_line(cmark_parser *parser, cmark_node *b) {
@@ -100,6 +97,10 @@ static CMARK_INLINE bool S_ends_on_current_line(cmark_parser *parser, cmark_node
100
97
  b->start_line == parser->line_number;
101
98
  }
102
99
 
100
+ static CMARK_INLINE bool S_is_space_or_tab(char c) {
101
+ return (c == ' ' || c == '\t');
102
+ }
103
+
103
104
  static void S_parser_feed(cmark_parser *parser, const unsigned char *buffer,
104
105
  size_t len, bool eof);
105
106
 
@@ -159,6 +160,8 @@ static void cmark_parser_reset(cmark_parser *parser) {
159
160
 
160
161
  cmark_strbuf_init(parser->mem, &parser->curline, 256);
161
162
  cmark_strbuf_init(parser->mem, &parser->linebuf, 0);
163
+ cmark_strbuf_init(parser->mem, &parser->front_matter_buf, 0);
164
+ cmark_strbuf_init(parser->mem, &parser->front_matter_info, 0);
162
165
 
163
166
  cmark_node *document = make_document(parser->mem);
164
167
 
@@ -189,6 +192,8 @@ void cmark_parser_free(cmark_parser *parser) {
189
192
  cmark_parser_dispose(parser);
190
193
  cmark_strbuf_free(&parser->curline);
191
194
  cmark_strbuf_free(&parser->linebuf);
195
+ cmark_strbuf_free(&parser->front_matter_buf);
196
+ cmark_strbuf_free(&parser->front_matter_info);
192
197
  cmark_llist_free(parser->mem, parser->syntax_extensions);
193
198
  cmark_llist_free(parser->mem, parser->inline_syntax_extensions);
194
199
  mem->free(parser);
@@ -1244,7 +1249,8 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
1244
1249
  parser->first_nonspace + 1);
1245
1250
  S_advance_offset(parser, input, input->len - 1 - parser->offset, false);
1246
1251
  } else if (!indented &&
1247
- parser->options & CMARK_OPT_FOOTNOTES &&
1252
+ (parser->options & CMARK_OPT_FOOTNOTES) &&
1253
+ depth < MAX_LIST_DEPTH &&
1248
1254
  (matched = scan_footnote_definition(input, parser->first_nonspace))) {
1249
1255
  cmark_chunk c = cmark_chunk_dup(input, parser->first_nonspace + 2, matched - 2);
1250
1256
 
@@ -1516,6 +1522,10 @@ static void S_process_line(cmark_parser *parser, const unsigned char *buffer,
1516
1522
 
1517
1523
  parser->line_number++;
1518
1524
 
1525
+ if ((parser->options & CMARK_OPT_FRONT_MATTER) &&
1526
+ cmark_front_matter_process_line(parser, &input))
1527
+ goto finished;
1528
+
1519
1529
  last_matched_container = check_open_blocks(parser, &input, &all_matched);
1520
1530
 
1521
1531
  if (!last_matched_container)
@@ -1556,12 +1566,20 @@ cmark_node *cmark_parser_finish(cmark_parser *parser) {
1556
1566
  cmark_strbuf_clear(&parser->linebuf);
1557
1567
  }
1558
1568
 
1569
+ // If front matter scanning was still active when the document ended, no
1570
+ // closing delimiter was found. The entire document (after the opening ---)
1571
+ // is treated as front matter.
1572
+ if ((parser->options & CMARK_OPT_FRONT_MATTER) && parser->front_matter_scanning)
1573
+ cmark_front_matter_process_line(parser, NULL);
1574
+
1559
1575
  finalize_document(parser);
1560
1576
 
1561
1577
  cmark_consolidate_text_nodes(parser->root);
1562
1578
 
1563
1579
  cmark_strbuf_free(&parser->curline);
1564
1580
  cmark_strbuf_free(&parser->linebuf);
1581
+ cmark_strbuf_free(&parser->front_matter_buf);
1582
+ cmark_strbuf_free(&parser->front_matter_info);
1565
1583
 
1566
1584
  #if CMARK_DEBUG_NODES
1567
1585
  if (cmark_node_check(parser->root, stderr)) {
@@ -53,6 +53,7 @@ typedef enum {
53
53
  CMARK_NODE_HEADING = CMARK_NODE_TYPE_BLOCK | 0x0009,
54
54
  CMARK_NODE_THEMATIC_BREAK = CMARK_NODE_TYPE_BLOCK | 0x000a,
55
55
  CMARK_NODE_FOOTNOTE_DEFINITION = CMARK_NODE_TYPE_BLOCK | 0x000b,
56
+ CMARK_NODE_FRONT_MATTER = CMARK_NODE_TYPE_BLOCK | 0x000c,
56
57
 
57
58
  /* Inline */
58
59
  CMARK_NODE_TEXT = CMARK_NODE_TYPE_INLINE | 0x0001,
@@ -768,6 +769,13 @@ char *cmark_render_latex_with_mem(cmark_node *root, int options, int width, cmar
768
769
  */
769
770
  #define CMARK_OPT_FULL_INFO_STRING (1 << 16)
770
771
 
772
+ /** Parse front matter ("---" delimited block at the start of the document)
773
+ * and expose it as a CMARK_NODE_FRONT_MATTER node. The raw content between
774
+ * the delimiters is available via cmark_node_get_literal(); how it is
775
+ * interpreted (e.g. as YAML, TOML, JSON) is left to the caller.
776
+ */
777
+ #define CMARK_OPT_FRONT_MATTER (1 << 18)
778
+
771
779
  /**
772
780
  * ## Version information
773
781
  */
data/ext/markly/cmark.c CHANGED
@@ -7,7 +7,7 @@
7
7
  #include "cmark-gfm.h"
8
8
  #include "buffer.h"
9
9
 
10
- cmark_node_type CMARK_NODE_LAST_BLOCK = CMARK_NODE_FOOTNOTE_DEFINITION;
10
+ cmark_node_type CMARK_NODE_LAST_BLOCK = CMARK_NODE_FRONT_MATTER;
11
11
  cmark_node_type CMARK_NODE_LAST_INLINE = CMARK_NODE_FOOTNOTE_REFERENCE;
12
12
 
13
13
  int cmark_version(void) { return CMARK_GFM_VERSION; }
@@ -492,6 +492,19 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
492
492
  }
493
493
  break;
494
494
 
495
+ case CMARK_NODE_FRONT_MATTER:
496
+ if (entering) {
497
+ const char *info = cmark_node_get_fence_info(node);
498
+ BLANKLINE();
499
+ LIT("---");
500
+ if (info && *info) { LIT(" "); OUT(info, false, LITERAL); }
501
+ LIT("\n");
502
+ OUT(cmark_node_get_literal(node), false, LITERAL);
503
+ LIT("---\n");
504
+ BLANKLINE();
505
+ }
506
+ break;
507
+
495
508
  default:
496
509
  assert(false);
497
510
  break;
@@ -6,12 +6,17 @@
6
6
  # Copyright, 2015-2019, by Garen Torikian.
7
7
  # Copyright, 2016-2017, by Yuki Izumi.
8
8
  # Copyright, 2017, by Ashe Connor.
9
- # Copyright, 2020-2025, by Samuel Williams.
9
+ # Copyright, 2020-2026, by Samuel Williams.
10
10
 
11
11
  require "mkmf"
12
12
 
13
13
  append_cflags(["-O3", "-Wall", "-Wno-unknown-pragmas", "-std=c99"])
14
14
 
15
+ # Include the extensions/ subdirectory, mirroring the cmark-gfm source layout.
16
+ $INCFLAGS << " -I$(srcdir)/extensions"
17
+ $VPATH << "$(srcdir)/extensions"
18
+ $srcs = (Dir["#{$srcdir}/*.c"] + Dir["#{$srcdir}/extensions/*.c"]).map{|f| File.basename(f)}
19
+
15
20
  gem_name = File.basename(__dir__)
16
21
  extension_name = "markly"
17
22
 
@@ -11,6 +11,9 @@
11
11
  #include "table.h"
12
12
  #include "cmark-gfm-core-extensions.h"
13
13
 
14
+ // Limit to prevent a malicious input from causing a denial of service.
15
+ #define MAX_AUTOCOMPLETED_CELLS 0x80000
16
+
14
17
  // Custom node flag, initialized in `create_table_extension`.
15
18
  static cmark_node_internal_flags CMARK_NODE__TABLE_VISITED;
16
19
 
@@ -31,6 +34,8 @@ typedef struct {
31
34
  typedef struct {
32
35
  uint16_t n_columns;
33
36
  uint8_t *alignments;
37
+ int n_rows;
38
+ int n_nonempty_cells;
34
39
  } node_table;
35
40
 
36
41
  typedef struct {
@@ -83,6 +88,33 @@ static int set_n_table_columns(cmark_node *node, uint16_t n_columns) {
83
88
  return 1;
84
89
  }
85
90
 
91
+ // Increment the number of rows in the table. Also update n_nonempty_cells,
92
+ // which keeps track of the number of cells which were parsed from the
93
+ // input file. (If one of the rows is too short, then the trailing cells
94
+ // are autocompleted. Autocompleted cells are not counted in n_nonempty_cells.)
95
+ // The purpose of this is to prevent a malicious input from generating a very
96
+ // large number of autocompleted cells, which could cause a denial of service
97
+ // vulnerability.
98
+ static int incr_table_row_count(cmark_node *node, int i) {
99
+ if (!node || node->type != CMARK_NODE_TABLE) {
100
+ return 0;
101
+ }
102
+
103
+ ((node_table *)node->as.opaque)->n_rows++;
104
+ ((node_table *)node->as.opaque)->n_nonempty_cells += i;
105
+ return 1;
106
+ }
107
+
108
+ // Calculate the number of autocompleted cells.
109
+ static int get_n_autocompleted_cells(cmark_node *node) {
110
+ if (!node || node->type != CMARK_NODE_TABLE) {
111
+ return 0;
112
+ }
113
+
114
+ const node_table *nt = (node_table *)node->as.opaque;
115
+ return (nt->n_columns * nt->n_rows) - nt->n_nonempty_cells;
116
+ }
117
+
86
118
  static uint8_t *get_table_alignments(cmark_node *node) {
87
119
  if (!node || node->type != CMARK_NODE_TABLE)
88
120
  return 0;
@@ -98,6 +130,23 @@ static int set_table_alignments(cmark_node *node, uint8_t *alignments) {
98
130
  return 1;
99
131
  }
100
132
 
133
+ static uint8_t get_cell_alignment(cmark_node *node) {
134
+ if (!node || node->type != CMARK_NODE_TABLE_CELL)
135
+ return 0;
136
+
137
+ const uint8_t *alignments = get_table_alignments(node->parent->parent);
138
+ int i = node->as.cell_index;
139
+ return alignments[i];
140
+ }
141
+
142
+ static int set_cell_index(cmark_node *node, int i) {
143
+ if (!node || node->type != CMARK_NODE_TABLE_CELL)
144
+ return 0;
145
+
146
+ node->as.cell_index = i;
147
+ return 1;
148
+ }
149
+
101
150
  static cmark_strbuf *unescape_pipes(cmark_mem *mem, unsigned char *string, bufsize_t len)
102
151
  {
103
152
  cmark_strbuf *res = (cmark_strbuf *)mem->calloc(1, sizeof(cmark_strbuf));
@@ -257,7 +306,7 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
257
306
  unsigned char *input, int len) {
258
307
  cmark_node *table_header;
259
308
  table_row *header_row = NULL;
260
- table_row *marker_row = NULL;
309
+ table_row *delimiter_row = NULL;
261
310
  node_table_row *ntr;
262
311
  const char *parent_string;
263
312
  uint16_t i;
@@ -270,16 +319,16 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
270
319
  return parent_container;
271
320
  }
272
321
 
273
- // Since scan_table_start was successful, we must have a marker row.
274
- marker_row = row_from_string(self, parser,
275
- input + cmark_parser_get_first_nonspace(parser),
276
- len - cmark_parser_get_first_nonspace(parser));
322
+ // Since scan_table_start was successful, we must have a delimiter row.
323
+ delimiter_row = row_from_string(
324
+ self, parser, input + cmark_parser_get_first_nonspace(parser),
325
+ len - cmark_parser_get_first_nonspace(parser));
277
326
  // assert may be optimized out, don't rely on it for security boundaries
278
- if (!marker_row) {
327
+ if (!delimiter_row) {
279
328
  return parent_container;
280
329
  }
281
-
282
- assert(marker_row);
330
+
331
+ assert(delimiter_row);
283
332
 
284
333
  cmark_arena_push();
285
334
 
@@ -289,8 +338,8 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
289
338
  parent_string = cmark_node_get_string_content(parent_container);
290
339
  header_row = row_from_string(self, parser, (unsigned char *)parent_string,
291
340
  (int)strlen(parent_string));
292
- if (!header_row || header_row->n_columns != marker_row->n_columns) {
293
- free_table_row(parser->mem, marker_row);
341
+ if (!header_row || header_row->n_columns != delimiter_row->n_columns) {
342
+ free_table_row(parser->mem, delimiter_row);
294
343
  free_table_row(parser->mem, header_row);
295
344
  cmark_arena_pop();
296
345
  parent_container->flags |= CMARK_NODE__TABLE_VISITED;
@@ -298,14 +347,14 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
298
347
  }
299
348
 
300
349
  if (cmark_arena_pop()) {
301
- marker_row = row_from_string(
350
+ delimiter_row = row_from_string(
302
351
  self, parser, input + cmark_parser_get_first_nonspace(parser),
303
352
  len - cmark_parser_get_first_nonspace(parser));
304
353
  header_row = row_from_string(self, parser, (unsigned char *)parent_string,
305
354
  (int)strlen(parent_string));
306
355
  // 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);
356
+ if (!delimiter_row || !header_row || header_row->n_columns != delimiter_row->n_columns) {
357
+ free_table_row(parser->mem, delimiter_row);
309
358
  free_table_row(parser->mem, header_row);
310
359
  return parent_container;
311
360
  }
@@ -313,7 +362,7 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
313
362
 
314
363
  if (!cmark_node_set_type(parent_container, CMARK_NODE_TABLE)) {
315
364
  free_table_row(parser->mem, header_row);
316
- free_table_row(parser->mem, marker_row);
365
+ free_table_row(parser->mem, delimiter_row);
317
366
  return parent_container;
318
367
  }
319
368
 
@@ -326,12 +375,12 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
326
375
  parent_container->as.opaque = parser->mem->calloc(1, sizeof(node_table));
327
376
  set_n_table_columns(parent_container, header_row->n_columns);
328
377
 
329
- // allocate alignments based on marker_row->n_columns
330
- // since we populate the alignments array based on marker_row->cells
378
+ // allocate alignments based on delimiter_row->n_columns
379
+ // since we populate the alignments array based on delimiter_row->cells
331
380
  uint8_t *alignments =
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];
381
+ (uint8_t *)parser->mem->calloc(delimiter_row->n_columns, sizeof(uint8_t));
382
+ for (i = 0; i < delimiter_row->n_columns; ++i) {
383
+ node_cell *node = &delimiter_row->cells[i];
335
384
  bool left = node->buf->ptr[0] == ':', right = node->buf->ptr[node->buf->size - 1] == ':';
336
385
 
337
386
  if (left && right)
@@ -353,25 +402,26 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
353
402
  table_header->as.opaque = ntr = (node_table_row *)parser->mem->calloc(1, sizeof(node_table_row));
354
403
  ntr->is_header = true;
355
404
 
356
- {
357
- for (i = 0; i < header_row->n_columns; ++i) {
358
- node_cell *cell = &header_row->cells[i];
359
- cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
360
- CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
361
- header_cell->start_line = header_cell->end_line = parent_container->start_line;
362
- header_cell->internal_offset = cell->internal_offset;
363
- header_cell->end_column = parent_container->start_column + cell->end_offset;
364
- cmark_node_set_string_content(header_cell, (char *) cell->buf->ptr);
365
- cmark_node_set_syntax_extension(header_cell, self);
366
- }
405
+ for (i = 0; i < header_row->n_columns; ++i) {
406
+ node_cell *cell = &header_row->cells[i];
407
+ cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
408
+ CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
409
+ header_cell->start_line = header_cell->end_line = parent_container->start_line;
410
+ header_cell->internal_offset = cell->internal_offset;
411
+ header_cell->end_column = parent_container->start_column + cell->end_offset;
412
+ cmark_node_set_string_content(header_cell, (char *) cell->buf->ptr);
413
+ cmark_node_set_syntax_extension(header_cell, self);
414
+ set_cell_index(header_cell, i);
367
415
  }
368
416
 
417
+ incr_table_row_count(parent_container, i);
418
+
369
419
  cmark_parser_advance_offset(
370
420
  parser, (char *)input,
371
421
  (int)strlen((char *)input) - 1 - cmark_parser_get_offset(parser), false);
372
422
 
373
423
  free_table_row(parser->mem, header_row);
374
- free_table_row(parser->mem, marker_row);
424
+ free_table_row(parser->mem, delimiter_row);
375
425
  return parent_container;
376
426
  }
377
427
 
@@ -385,6 +435,10 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
385
435
  if (cmark_parser_is_blank(parser))
386
436
  return NULL;
387
437
 
438
+ if (get_n_autocompleted_cells(parent_container) > MAX_AUTOCOMPLETED_CELLS) {
439
+ return NULL;
440
+ }
441
+
388
442
  table_row_block =
389
443
  cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
390
444
  parent_container->start_column);
@@ -412,12 +466,16 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
412
466
  node->end_column = parent_container->start_column + cell->end_offset;
413
467
  cmark_node_set_string_content(node, (char *) cell->buf->ptr);
414
468
  cmark_node_set_syntax_extension(node, self);
469
+ set_cell_index(node, i);
415
470
  }
416
471
 
472
+ incr_table_row_count(parent_container, i);
473
+
417
474
  for (; i < table_columns; ++i) {
418
475
  cmark_node *node = cmark_parser_add_child(
419
476
  parser, table_row_block, CMARK_NODE_TABLE_CELL, 0);
420
477
  cmark_node_set_syntax_extension(node, self);
478
+ set_cell_index(node, i);
421
479
  }
422
480
  }
423
481
 
@@ -602,13 +660,7 @@ static const char *xml_attr(cmark_syntax_extension *extension,
602
660
  cmark_node *node) {
603
661
  if (node->type == CMARK_NODE_TABLE_CELL) {
604
662
  if (cmark_gfm_extensions_get_table_row_is_header(node->parent)) {
605
- uint8_t *alignments = get_table_alignments(node->parent->parent);
606
- int i = 0;
607
- cmark_node *n;
608
- for (n = node->parent->first_child; n; n = n->next, ++i)
609
- if (n == node)
610
- break;
611
- switch (alignments[i]) {
663
+ switch (get_cell_alignment(node)) {
612
664
  case 'l': return " align=\"left\"";
613
665
  case 'c': return " align=\"center\"";
614
666
  case 'r': return " align=\"right\"";
@@ -696,7 +748,6 @@ static void html_render(cmark_syntax_extension *extension,
696
748
  cmark_event_type ev_type, int options) {
697
749
  bool entering = (ev_type == CMARK_EVENT_ENTER);
698
750
  cmark_strbuf *html = renderer->html;
699
- cmark_node *n;
700
751
 
701
752
  // XXX: we just monopolise renderer->opaque.
702
753
  struct html_table_state *table_state =
@@ -745,7 +796,6 @@ static void html_render(cmark_syntax_extension *extension,
745
796
  }
746
797
  }
747
798
  } else if (node->type == CMARK_NODE_TABLE_CELL) {
748
- uint8_t *alignments = get_table_alignments(node->parent->parent);
749
799
  if (entering) {
750
800
  cmark_html_render_cr(html);
751
801
  if (table_state->in_table_header) {
@@ -754,12 +804,7 @@ static void html_render(cmark_syntax_extension *extension,
754
804
  cmark_strbuf_puts(html, "<td");
755
805
  }
756
806
 
757
- int i = 0;
758
- for (n = node->parent->first_child; n; n = n->next, ++i)
759
- if (n == node)
760
- break;
761
-
762
- switch (alignments[i]) {
807
+ switch (get_cell_alignment(node)) {
763
808
  case 'l': html_table_add_align(html, "left", options); break;
764
809
  case 'c': html_table_add_align(html, "center", options); break;
765
810
  case 'r': html_table_add_align(html, "right", options); break;
@@ -0,0 +1,141 @@
1
+ #include "front_matter.h"
2
+ #include "cmark-gfm.h"
3
+
4
+ #include <string.h>
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // Delimiter and info string parsing
8
+ // ---------------------------------------------------------------------------
9
+
10
+ // Return true if `input` is an opening front matter delimiter: "---" followed
11
+ // by an optional info string and a newline. No leading whitespace before
12
+ // "---" is permitted.
13
+ //
14
+ // Note: some tools (e.g. Jekyll) also accept "..." as a closing delimiter,
15
+ // derived from the YAML document-end marker. We intentionally do not support
16
+ // it here because this implementation is format-agnostic — the content between
17
+ // the delimiters may be YAML, TOML, JSON, or anything else. "..." has no
18
+ // meaning outside of YAML, so "---" is the only unambiguous delimiter.
19
+ static bool is_opening_delimiter(cmark_chunk *input) {
20
+ const unsigned char *p = input->data;
21
+ if (input->len < 3 || !(p[0] == '-' && p[1] == '-' && p[2] == '-'))
22
+ return false;
23
+ // A fourth consecutive dash produces a longer thematic break, not a
24
+ // front matter delimiter.
25
+ if (input->len > 3 && p[3] == '-')
26
+ return false;
27
+ return true;
28
+ }
29
+
30
+ // Return true if `input` is a closing front matter delimiter: exactly "---"
31
+ // with optional trailing whitespace then a newline. An info string is not
32
+ // permitted on the closing delimiter.
33
+ static bool is_closing_delimiter(cmark_chunk *input) {
34
+ const unsigned char *p = input->data;
35
+ int len = input->len;
36
+
37
+ if (len < 3 || !(p[0] == '-' && p[1] == '-' && p[2] == '-'))
38
+ return false;
39
+
40
+ for (int i = 3; i < len; i++) {
41
+ if (p[i] == '\n' || p[i] == '\r')
42
+ return true;
43
+ if (p[i] != ' ' && p[i] != '\t')
44
+ return false;
45
+ }
46
+ return true;
47
+ }
48
+
49
+ // Extract the optional info string from an opening delimiter line, e.g.
50
+ // "--- yaml\n" yields "yaml". Returns a zero-length chunk if absent.
51
+ static cmark_chunk parse_info(cmark_chunk *input) {
52
+ const unsigned char *p = input->data + 3;
53
+ int len = input->len - 3;
54
+
55
+ while (len > 0 && (*p == ' ' || *p == '\t')) { p++; len--; }
56
+ while (len > 0 && (p[len-1] == '\n' || p[len-1] == '\r' ||
57
+ p[len-1] == ' ' || p[len-1] == '\t'))
58
+ len--;
59
+
60
+ return (cmark_chunk){ .data = (unsigned char *)p,
61
+ .len = (bufsize_t)len,
62
+ .alloc = 0 };
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Node creation
67
+ // ---------------------------------------------------------------------------
68
+
69
+ static void create_front_matter_node(cmark_parser *parser) {
70
+ cmark_node *node =
71
+ cmark_node_new_with_mem(CMARK_NODE_FRONT_MATTER, parser->mem);
72
+
73
+ // Store identically to a code block: info string + literal content.
74
+ cmark_node_set_fence_info(node,
75
+ parser->front_matter_info.size > 0
76
+ ? (const char *)parser->front_matter_info.ptr
77
+ : "");
78
+
79
+ cmark_node_set_literal(node,
80
+ parser->front_matter_buf.size > 0
81
+ ? (const char *)parser->front_matter_buf.ptr
82
+ : "");
83
+
84
+ node->start_line = 1;
85
+ node->start_column = 1;
86
+ node->end_line = parser->line_number;
87
+ node->end_column = 3;
88
+
89
+ cmark_node *first = cmark_node_first_child(parser->root);
90
+ if (first)
91
+ cmark_node_insert_before(first, node);
92
+ else
93
+ cmark_node_append_child(parser->root, node);
94
+
95
+ parser->front_matter_scanning = false;
96
+ cmark_strbuf_clear(&parser->front_matter_buf);
97
+ cmark_strbuf_clear(&parser->front_matter_info);
98
+ }
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // State machine — called from S_process_line in blocks.c
102
+ // ---------------------------------------------------------------------------
103
+
104
+ bool cmark_front_matter_process_line(cmark_parser *parser, cmark_chunk *input) {
105
+ // NULL signals end-of-document: the whole document is the front matter.
106
+ if (input == NULL) {
107
+ create_front_matter_node(parser);
108
+ return true;
109
+ }
110
+
111
+ // Adjust for any offset already consumed (e.g. a UTF-8 BOM on line 1).
112
+ cmark_chunk adjusted = {
113
+ .data = input->data + parser->offset,
114
+ .len = input->len - parser->offset,
115
+ .alloc = 0,
116
+ };
117
+ input = &adjusted;
118
+
119
+ if (parser->line_number == 1) {
120
+ if (is_opening_delimiter(input)) {
121
+ parser->front_matter_scanning = true;
122
+ // Capture optional info string (e.g. "yaml" from "--- yaml\n").
123
+ cmark_chunk info = parse_info(input);
124
+ if (info.len > 0)
125
+ cmark_strbuf_put(&parser->front_matter_info, info.data, info.len);
126
+ }
127
+ return parser->front_matter_scanning;
128
+ }
129
+
130
+ if (!parser->front_matter_scanning)
131
+ return false;
132
+
133
+ if (is_closing_delimiter(input)) {
134
+ create_front_matter_node(parser);
135
+ return true;
136
+ }
137
+
138
+ // Accumulate this content line.
139
+ cmark_strbuf_put(&parser->front_matter_buf, input->data, input->len);
140
+ return true;
141
+ }
@@ -0,0 +1,24 @@
1
+ #ifndef CMARK_FRONT_MATTER_H
2
+ #define CMARK_FRONT_MATTER_H
3
+
4
+ #ifdef __cplusplus
5
+ extern "C" {
6
+ #endif
7
+
8
+ #include "cmark-gfm.h"
9
+ #include "parser.h"
10
+ #include "chunk.h"
11
+
12
+ // Called from S_process_line in blocks.c for every line when
13
+ // CMARK_OPT_FRONT_MATTER is set. Drives the front matter state machine
14
+ // stored directly on the parser (front_matter_scanning / front_matter_buf).
15
+ //
16
+ // Returns true if the line was consumed by the front matter scanner and
17
+ // should not be passed to the normal block parser.
18
+ bool cmark_front_matter_process_line(cmark_parser *parser, cmark_chunk *input);
19
+
20
+ #ifdef __cplusplus
21
+ }
22
+ #endif
23
+
24
+ #endif
data/ext/markly/html.c CHANGED
@@ -457,6 +457,9 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
457
457
  }
458
458
  break;
459
459
 
460
+ case CMARK_NODE_FRONT_MATTER:
461
+ break;
462
+
460
463
  default:
461
464
  assert(false);
462
465
  break;
data/ext/markly/latex.c CHANGED
@@ -451,6 +451,9 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
451
451
  // TODO
452
452
  break;
453
453
 
454
+ case CMARK_NODE_FRONT_MATTER:
455
+ break;
456
+
454
457
  default:
455
458
  assert(false);
456
459
  break;
data/ext/markly/man.c CHANGED
@@ -257,6 +257,9 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
257
257
  // TODO
258
258
  break;
259
259
 
260
+ case CMARK_NODE_FRONT_MATTER:
261
+ break;
262
+
260
263
  default:
261
264
  assert(false);
262
265
  break;
data/ext/markly/markly.c CHANGED
@@ -6,6 +6,7 @@
6
6
  #include "parser.h"
7
7
  #include "syntax_extension.h"
8
8
  #include "cmark-gfm-core-extensions.h"
9
+ #include "front_matter.h"
9
10
 
10
11
  static VALUE rb_Markly;
11
12
  static VALUE rb_Markly_Error;
@@ -34,6 +35,7 @@ static VALUE sym_link;
34
35
  static VALUE sym_image;
35
36
  static VALUE sym_footnote_reference;
36
37
  static VALUE sym_footnote_definition;
38
+ static VALUE sym_front_matter;
37
39
 
38
40
  static VALUE sym_bullet_list;
39
41
  static VALUE sym_ordered_list;
@@ -416,6 +418,9 @@ static VALUE rb_node_get_type(VALUE self) {
416
418
  case CMARK_NODE_FOOTNOTE_DEFINITION:
417
419
  symbol = sym_footnote_definition;
418
420
  break;
421
+ case CMARK_NODE_FRONT_MATTER:
422
+ symbol = sym_front_matter;
423
+ break;
419
424
  default:
420
425
  if (node->extension) {
421
426
  s = node->extension->get_type_string_func(node->extension, node);
@@ -1185,6 +1190,7 @@ __attribute__((visibility("default"))) void Init_markly(void) {
1185
1190
  sym_image = ID2SYM(rb_intern("image"));
1186
1191
  sym_footnote_reference = ID2SYM(rb_intern("footnote_reference"));
1187
1192
  sym_footnote_definition = ID2SYM(rb_intern("footnote_definition"));
1193
+ sym_front_matter = ID2SYM(rb_intern("front_matter"));
1188
1194
 
1189
1195
  sym_bullet_list = ID2SYM(rb_intern("bullet_list"));
1190
1196
  sym_ordered_list = ID2SYM(rb_intern("ordered_list"));
data/ext/markly/node.c CHANGED
@@ -148,6 +148,7 @@ cmark_node *cmark_node_new(cmark_node_type type) {
148
148
  static void free_node_as(cmark_node *node) {
149
149
  switch (node->type) {
150
150
  case CMARK_NODE_CODE_BLOCK:
151
+ case CMARK_NODE_FRONT_MATTER:
151
152
  cmark_chunk_free(NODE_MEM(node), &node->as.code.info);
152
153
  cmark_chunk_free(NODE_MEM(node), &node->as.code.literal);
153
154
  break;
@@ -288,6 +289,8 @@ const char *cmark_node_get_type_string(cmark_node *node) {
288
289
  return "link";
289
290
  case CMARK_NODE_IMAGE:
290
291
  return "image";
292
+ case CMARK_NODE_FRONT_MATTER:
293
+ return "front_matter";
291
294
  }
292
295
 
293
296
  return "<unknown>";
@@ -381,6 +384,7 @@ const char *cmark_node_get_literal(cmark_node *node) {
381
384
  return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.literal);
382
385
 
383
386
  case CMARK_NODE_CODE_BLOCK:
387
+ case CMARK_NODE_FRONT_MATTER:
384
388
  return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.code.literal);
385
389
 
386
390
  default:
@@ -405,6 +409,7 @@ int cmark_node_set_literal(cmark_node *node, const char *content) {
405
409
  return 1;
406
410
 
407
411
  case CMARK_NODE_CODE_BLOCK:
412
+ case CMARK_NODE_FRONT_MATTER:
408
413
  cmark_chunk_set_cstr(NODE_MEM(node), &node->as.code.literal, content);
409
414
  return 1;
410
415
 
@@ -595,7 +600,8 @@ const char *cmark_node_get_fence_info(cmark_node *node) {
595
600
  return NULL;
596
601
  }
597
602
 
598
- if (node->type == CMARK_NODE_CODE_BLOCK) {
603
+ if (node->type == CMARK_NODE_CODE_BLOCK ||
604
+ node->type == CMARK_NODE_FRONT_MATTER) {
599
605
  return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.code.info);
600
606
  } else {
601
607
  return NULL;
@@ -607,7 +613,8 @@ int cmark_node_set_fence_info(cmark_node *node, const char *info) {
607
613
  return 0;
608
614
  }
609
615
 
610
- if (node->type == CMARK_NODE_CODE_BLOCK) {
616
+ if (node->type == CMARK_NODE_CODE_BLOCK ||
617
+ node->type == CMARK_NODE_FRONT_MATTER) {
611
618
  cmark_chunk_set_cstr(NODE_MEM(node), &node->as.code.info, info);
612
619
  return 1;
613
620
  } else {
data/ext/markly/node.h CHANGED
@@ -105,6 +105,7 @@ struct cmark_node {
105
105
  cmark_link link;
106
106
  cmark_custom custom;
107
107
  int html_block_type;
108
+ int cell_index; // For keeping track of TABLE_CELL table alignments
108
109
  void *opaque;
109
110
  } as;
110
111
  };
data/ext/markly/parser.h CHANGED
@@ -50,6 +50,25 @@ struct cmark_parser {
50
50
  cmark_llist *syntax_extensions;
51
51
  cmark_llist *inline_syntax_extensions;
52
52
  cmark_ispunct_func backslash_ispunct;
53
+
54
+ /* Front matter scanning state (CMARK_OPT_FRONT_MATTER).
55
+ *
56
+ * cmark_front_matter_process_line() is called from S_process_line() in
57
+ * blocks.c immediately after parser->line_number is incremented, so the
58
+ * first line of the document arrives with line_number == 1. The function
59
+ * relies on this: it uses line_number == 1 as the trigger to decide
60
+ * whether the document opens with a front matter block.
61
+ *
62
+ * front_matter_scanning is set to true when a valid opening "---" is seen
63
+ * on line 1 and remains true until the matching closing "---" is found or
64
+ * the document ends. While scanning, each content line is accumulated in
65
+ * front_matter_buf. Both fields are reset to zero/empty by
66
+ * cmark_parser_reset() (via memset + strbuf re-init) and the strbuf is
67
+ * freed explicitly in cmark_parser_finish() and cmark_parser_free().
68
+ */
69
+ bool front_matter_scanning;
70
+ cmark_strbuf front_matter_buf; /* accumulated content lines */
71
+ cmark_strbuf front_matter_info; /* optional format hint from opening "--- <info>" */
53
72
  };
54
73
 
55
74
  #ifdef __cplusplus
@@ -196,6 +196,9 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
196
196
  cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 4);
197
197
  }
198
198
  break;
199
+ case CMARK_NODE_FRONT_MATTER:
200
+ break;
201
+
199
202
  default:
200
203
  assert(false);
201
204
  break;
data/ext/markly/xml.c CHANGED
@@ -64,6 +64,13 @@ static int S_render_node(cmark_node *node, cmark_event_type ev_type,
64
64
  case CMARK_NODE_DOCUMENT:
65
65
  cmark_strbuf_puts(xml, " xmlns=\"http://commonmark.org/xml/1.0\"");
66
66
  break;
67
+ case CMARK_NODE_FRONT_MATTER:
68
+ cmark_strbuf_puts(xml, " xml:space=\"preserve\">");
69
+ escape_xml(xml, node->as.code.literal.data, node->as.code.literal.len);
70
+ cmark_strbuf_puts(xml, "</");
71
+ cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
72
+ literal = true;
73
+ break;
67
74
  case CMARK_NODE_TEXT:
68
75
  case CMARK_NODE_CODE:
69
76
  case CMARK_NODE_HTML_BLOCK:
data/lib/markly/flags.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2020-2023, by Samuel Williams.
4
+ # Copyright, 2020-2026, by Samuel Williams.
5
5
 
6
6
  module Markly
7
7
  # The default parsing system.
@@ -18,8 +18,14 @@ module Markly
18
18
  STRIKETHROUGH_DOUBLE_TILDE = 1 << 14
19
19
  # Allow raw/custom HTML and unsafe links.
20
20
  UNSAFE = 1 << 17
21
+ # Parse front matter ("---" delimited block at start of document).
22
+ # The raw content is available via node.string_content and the optional
23
+ # format hint (e.g. "yaml", "toml") via node.fence_info; interpretation
24
+ # is left to the caller.
25
+ FRONT_MATTER = 1 << 18
21
26
 
22
27
  PARSE_FLAGS = {
28
+ front_matter: FRONT_MATTER,
23
29
  validate_utf8: VALIDATE_UTF8,
24
30
  smart_quotes: SMART,
25
31
  liberal_html_tags: LIBERAL_HTML_TAG,
@@ -4,8 +4,8 @@
4
4
  # Copyright, 2015-2020, by Garen Torikian.
5
5
  # Copyright, 2016-2017, by Yuki Izumi.
6
6
  # Copyright, 2017-2018, by Ashe Connor.
7
- # Copyright, 2020-2025, by Samuel Williams.
7
+ # Copyright, 2020-2026, by Samuel Williams.
8
8
 
9
9
  module Markly
10
- VERSION = "0.15.3"
10
+ VERSION = "0.16.0"
11
11
  end
data/readme.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A parser and abstract syntax tree for Markdown documents (CommonMark compatible) in Ruby. Originally forked from [CommonMarker](https://github.com/gjtorikian/commonmarker). It also includes extensions to the CommonMark spec as documented in the [GitHub Flavored Markdown spec](http://github.github.com/gfm/), such as support for tables, strikethroughs, and autolinking.
4
4
 
5
- [![Development Status](https://github.com/ioquatix/markly/workflows/Test/badge.svg)](https://github.com/ioquatix/markly/actions?workflow=Test)
5
+ [![Development Status](https://github.com/socketry/markly/workflows/Test/badge.svg)](https://github.com/socketry/markly/actions?workflow=Test)
6
6
 
7
7
  ## Motivation
8
8
 
@@ -12,17 +12,23 @@ It should be noted that `commonmarker` re-introduced AST access, but the origina
12
12
 
13
13
  ## Usage
14
14
 
15
- Please see the [project documentation](https://ioquatix.github.io/markly/) for more details.
15
+ Please see the [project documentation](https://socketry.github.io/markly/) for more details.
16
16
 
17
- - [Getting Started](https://ioquatix.github.io/markly/guides/getting-started/index) - This guide explains now to install and use Markly.
17
+ - [Getting Started](https://socketry.github.io/markly/guides/getting-started/index) - This guide explains now to install and use Markly.
18
18
 
19
- - [Abstract Syntax Tree](https://ioquatix.github.io/markly/guides/abstract-syntax-tree/index) - This guide explains how to use Markly's abstract syntax tree (AST) to parse and manipulate Markdown documents.
19
+ - [Abstract Syntax Tree](https://socketry.github.io/markly/guides/abstract-syntax-tree/index) - This guide explains how to use Markly's abstract syntax tree (AST) to parse and manipulate Markdown documents.
20
20
 
21
- - [Headings](https://ioquatix.github.io/markly/guides/headings/index) - This guide explains how to work with headings in Markly, including extracting them for navigation and handling duplicate heading text.
21
+ - [Headings](https://socketry.github.io/markly/guides/headings/index) - This guide explains how to work with headings in Markly, including extracting them for navigation and handling duplicate heading text.
22
22
 
23
23
  ## Releases
24
24
 
25
- Please see the [project releases](https://ioquatix.github.io/markly/releases/index) for all releases.
25
+ Please see the [project releases](https://socketry.github.io/markly/releases/index) for all releases.
26
+
27
+ ### v0.16.0
28
+
29
+ - Update `cmark-gfm` from upstream, including a denial-of-service fix for tables with a large number of autocompleted cells, corrected `end_line` source positions for single-line and multi-line HTML blocks, and a fix for trailing newlines when rendering inline nodes.
30
+ - Add support for front matter (`CMARK_OPT_FRONT_MATTER`): a `---` delimited block at the start of a document is captured as a `CMARK_NODE_FRONT_MATTER` node. The raw content is available via `node.string_content` and an optional format hint (e.g. `"yaml"`, `"toml"`) via `node.fence_info`.
31
+ - Allow `:` in HTML tag names to support XML namespace prefixes (e.g. `<svg:circle>`, `<xhtml:div>`).
26
32
 
27
33
  ### v0.15.1
28
34
 
data/releases.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Releases
2
2
 
3
+ ## v0.16.0
4
+
5
+ - Update `cmark-gfm` from upstream, including a denial-of-service fix for tables with a large number of autocompleted cells, corrected `end_line` source positions for single-line and multi-line HTML blocks, and a fix for trailing newlines when rendering inline nodes.
6
+ - Add support for front matter (`CMARK_OPT_FRONT_MATTER`): a `---` delimited block at the start of a document is captured as a `CMARK_NODE_FRONT_MATTER` node. The raw content is available via `node.string_content` and an optional format hint (e.g. `"yaml"`, `"toml"`) via `node.fence_info`.
7
+ - Allow `:` in HTML tag names to support XML namespace prefixes (e.g. `<svg:circle>`, `<xhtml:div>`).
8
+
3
9
  ## v0.15.1
4
10
 
5
11
  - Add agent context.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: markly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.3
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garen Torikian
@@ -69,16 +69,12 @@ files:
69
69
  - context/headings.md
70
70
  - context/index.yaml
71
71
  - ext/markly/arena.c
72
- - ext/markly/autolink.c
73
- - ext/markly/autolink.h
74
72
  - ext/markly/blocks.c
75
73
  - ext/markly/buffer.c
76
74
  - ext/markly/buffer.h
77
75
  - ext/markly/case_fold_switch.inc
78
76
  - ext/markly/chunk.h
79
- - ext/markly/cmark-gfm-core-extensions.h
80
77
  - ext/markly/cmark-gfm-extension_api.h
81
- - ext/markly/cmark-gfm-extensions_export.h
82
78
  - ext/markly/cmark-gfm.h
83
79
  - ext/markly/cmark-gfm_export.h
84
80
  - ext/markly/cmark-gfm_version.h
@@ -87,13 +83,27 @@ files:
87
83
  - ext/markly/cmark_ctype.h
88
84
  - ext/markly/commonmark.c
89
85
  - ext/markly/config.h
90
- - ext/markly/core-extensions.c
91
86
  - ext/markly/entities.inc
92
- - ext/markly/ext_scanners.c
93
- - ext/markly/ext_scanners.h
94
87
  - ext/markly/extconf.rb
88
+ - ext/markly/extensions/autolink.c
89
+ - ext/markly/extensions/autolink.h
90
+ - ext/markly/extensions/cmark-gfm-core-extensions.h
91
+ - ext/markly/extensions/cmark-gfm-extensions_export.h
92
+ - ext/markly/extensions/core-extensions.c
93
+ - ext/markly/extensions/ext_scanners.c
94
+ - ext/markly/extensions/ext_scanners.h
95
+ - ext/markly/extensions/strikethrough.c
96
+ - ext/markly/extensions/strikethrough.h
97
+ - ext/markly/extensions/table.c
98
+ - ext/markly/extensions/table.h
99
+ - ext/markly/extensions/tagfilter.c
100
+ - ext/markly/extensions/tagfilter.h
101
+ - ext/markly/extensions/tasklist.c
102
+ - ext/markly/extensions/tasklist.h
95
103
  - ext/markly/footnotes.c
96
104
  - ext/markly/footnotes.h
105
+ - ext/markly/front_matter.c
106
+ - ext/markly/front_matter.h
97
107
  - ext/markly/houdini.h
98
108
  - ext/markly/houdini_href_e.c
99
109
  - ext/markly/houdini_html_e.c
@@ -126,16 +136,8 @@ files:
126
136
  - ext/markly/scanners.c
127
137
  - ext/markly/scanners.h
128
138
  - ext/markly/scanners.re
129
- - ext/markly/strikethrough.c
130
- - ext/markly/strikethrough.h
131
139
  - ext/markly/syntax_extension.c
132
140
  - ext/markly/syntax_extension.h
133
- - ext/markly/table.c
134
- - ext/markly/table.h
135
- - ext/markly/tagfilter.c
136
- - ext/markly/tagfilter.h
137
- - ext/markly/tasklist.c
138
- - ext/markly/tasklist.h
139
141
  - ext/markly/utf8.c
140
142
  - ext/markly/utf8.h
141
143
  - ext/markly/xml.c
@@ -150,13 +152,13 @@ files:
150
152
  - license.md
151
153
  - readme.md
152
154
  - releases.md
153
- homepage: https://github.com/ioquatix/markly
155
+ homepage: https://github.com/socketry/markly
154
156
  licenses:
155
157
  - MIT
156
158
  metadata:
157
- documentation_uri: https://ioquatix.github.io/markly/
158
- funding_uri: https://github.com/sponsors/ioquatix/
159
- source_code_uri: https://github.com/ioquatix/markly.git
159
+ documentation_uri: https://socketry.github.io/markly/
160
+ funding_uri: https://github.com/sponsors/socketry/
161
+ source_code_uri: https://github.com/socketry/markly.git
160
162
  rdoc_options: []
161
163
  require_paths:
162
164
  - lib
metadata.gz.sig CHANGED
Binary file
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes