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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/index.yaml +3 -3
- data/ext/markly/blocks.c +24 -6
- data/ext/markly/cmark-gfm.h +8 -0
- data/ext/markly/cmark.c +1 -1
- data/ext/markly/commonmark.c +13 -0
- data/ext/markly/extconf.rb +6 -1
- data/ext/markly/{table.c → extensions/table.c} +91 -46
- data/ext/markly/front_matter.c +141 -0
- data/ext/markly/front_matter.h +24 -0
- data/ext/markly/html.c +3 -0
- data/ext/markly/latex.c +3 -0
- data/ext/markly/man.c +3 -0
- data/ext/markly/markly.c +6 -0
- data/ext/markly/node.c +9 -2
- data/ext/markly/node.h +1 -0
- data/ext/markly/parser.h +19 -0
- data/ext/markly/plaintext.c +3 -0
- data/ext/markly/xml.c +7 -0
- data/lib/markly/flags.rb +7 -1
- data/lib/markly/version.rb +2 -2
- data/readme.md +12 -6
- data/releases.md +6 -0
- data.tar.gz.sig +0 -0
- metadata +22 -20
- metadata.gz.sig +0 -0
- /data/ext/markly/{autolink.c → extensions/autolink.c} +0 -0
- /data/ext/markly/{autolink.h → extensions/autolink.h} +0 -0
- /data/ext/markly/{cmark-gfm-core-extensions.h → extensions/cmark-gfm-core-extensions.h} +0 -0
- /data/ext/markly/{cmark-gfm-extensions_export.h → extensions/cmark-gfm-extensions_export.h} +0 -0
- /data/ext/markly/{core-extensions.c → extensions/core-extensions.c} +0 -0
- /data/ext/markly/{ext_scanners.c → extensions/ext_scanners.c} +0 -0
- /data/ext/markly/{ext_scanners.h → extensions/ext_scanners.h} +0 -0
- /data/ext/markly/{strikethrough.c → extensions/strikethrough.c} +0 -0
- /data/ext/markly/{strikethrough.h → extensions/strikethrough.h} +0 -0
- /data/ext/markly/{table.h → extensions/table.h} +0 -0
- /data/ext/markly/{tagfilter.c → extensions/tagfilter.c} +0 -0
- /data/ext/markly/{tagfilter.h → extensions/tagfilter.h} +0 -0
- /data/ext/markly/{tasklist.c → extensions/tasklist.c} +0 -0
- /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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fda5c52f4ac015dde2e9f22e2a76467ddcb655d197897f7bbd76c7da235f2991
|
|
4
|
+
data.tar.gz: 936fdce7bd2fde2339e48bf43a9b4f9ae8c4b552844de6cf69bd445965477a3f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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://
|
|
7
|
-
funding_uri: https://github.com/sponsors/
|
|
8
|
-
source_code_uri: https://github.com/
|
|
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
|
|
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)) {
|
data/ext/markly/cmark-gfm.h
CHANGED
|
@@ -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 =
|
|
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; }
|
data/ext/markly/commonmark.c
CHANGED
|
@@ -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;
|
data/ext/markly/extconf.rb
CHANGED
|
@@ -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-
|
|
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 *
|
|
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
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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 (!
|
|
327
|
+
if (!delimiter_row) {
|
|
279
328
|
return parent_container;
|
|
280
329
|
}
|
|
281
|
-
|
|
282
|
-
assert(
|
|
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 !=
|
|
293
|
-
free_table_row(parser->mem,
|
|
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
|
-
|
|
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 (!
|
|
308
|
-
free_table_row(parser->mem,
|
|
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,
|
|
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
|
|
330
|
-
// since we populate the alignments array based on
|
|
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(
|
|
333
|
-
for (i = 0; i <
|
|
334
|
-
node_cell *node = &
|
|
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
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
data/ext/markly/latex.c
CHANGED
data/ext/markly/man.c
CHANGED
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
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
|
data/ext/markly/plaintext.c
CHANGED
|
@@ -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-
|
|
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,
|
data/lib/markly/version.rb
CHANGED
|
@@ -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-
|
|
7
|
+
# Copyright, 2020-2026, by Samuel Williams.
|
|
8
8
|
|
|
9
9
|
module Markly
|
|
10
|
-
VERSION = "0.
|
|
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
|
-
[](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://
|
|
15
|
+
Please see the [project documentation](https://socketry.github.io/markly/) for more details.
|
|
16
16
|
|
|
17
|
-
- [Getting Started](https://
|
|
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://
|
|
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://
|
|
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://
|
|
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.
|
|
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/
|
|
155
|
+
homepage: https://github.com/socketry/markly
|
|
154
156
|
licenses:
|
|
155
157
|
- MIT
|
|
156
158
|
metadata:
|
|
157
|
-
documentation_uri: https://
|
|
158
|
-
funding_uri: https://github.com/sponsors/
|
|
159
|
-
source_code_uri: https://github.com/
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|