commonmarker 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of commonmarker might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/ext/commonmarker/cmark/CMakeLists.txt +10 -4
- data/ext/commonmarker/cmark/Makefile +5 -5
- data/ext/commonmarker/cmark/api_test/CMakeLists.txt +1 -1
- data/ext/commonmarker/cmark/api_test/main.c +16 -0
- data/ext/commonmarker/cmark/build/CMakeCache.txt +3 -4
- data/ext/commonmarker/cmark/build/CMakeFiles/2.8.10.1/CMakeSystem.cmake +4 -4
- data/ext/commonmarker/cmark/build/CMakeFiles/CMakeError.log +12 -12
- data/ext/commonmarker/cmark/build/CMakeFiles/CMakeOutput.log +97 -142
- data/ext/commonmarker/cmark/build/CMakeFiles/Makefile.cmake +0 -1
- data/ext/commonmarker/cmark/build/api_test/CMakeFiles/api_test.dir/build.make +1 -1
- data/ext/commonmarker/cmark/build/api_test/CMakeFiles/api_test.dir/link.txt +1 -1
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/DependInfo.cmake +1 -1
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/build.make +23 -23
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/cmake_clean.cmake +2 -2
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/link.txt +1 -1
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/blocks.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/buffer.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/cmark.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/commonmark.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/houdini_html_u.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/html.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/inlines.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/node.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/references.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/render.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/scanners.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/utf8.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/xml.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/cmake_install.cmake +3 -3
- data/ext/commonmarker/cmark/build/src/cmark_version.h +2 -2
- data/ext/commonmarker/cmark/build/src/config.h +6 -6
- data/ext/commonmarker/cmark/build/src/libcmark.a +0 -0
- data/ext/commonmarker/cmark/build/src/libcmark.pc +1 -1
- data/ext/commonmarker/cmark/build/testdir/CTestTestfile.cmake +4 -4
- data/ext/commonmarker/cmark/changelog.txt +46 -0
- data/ext/commonmarker/cmark/man/man3/cmark.3 +21 -20
- data/ext/commonmarker/cmark/src/CMakeLists.txt +4 -6
- data/ext/commonmarker/cmark/src/bench.h +8 -8
- data/ext/commonmarker/cmark/src/blocks.c +917 -947
- data/ext/commonmarker/cmark/src/buffer.c +213 -288
- data/ext/commonmarker/cmark/src/buffer.h +19 -21
- data/ext/commonmarker/cmark/src/chunk.h +78 -82
- data/ext/commonmarker/cmark/src/cmark.c +9 -17
- data/ext/commonmarker/cmark/src/cmark.h +113 -157
- data/ext/commonmarker/cmark/src/cmark_ctype.c +24 -35
- data/ext/commonmarker/cmark/src/commonmark.c +390 -425
- data/ext/commonmarker/cmark/src/config.h.in +6 -6
- data/ext/commonmarker/cmark/src/houdini.h +21 -15
- data/ext/commonmarker/cmark/src/houdini_href_e.c +50 -57
- data/ext/commonmarker/cmark/src/houdini_html_e.c +36 -51
- data/ext/commonmarker/cmark/src/houdini_html_u.c +119 -124
- data/ext/commonmarker/cmark/src/html.c +289 -307
- data/ext/commonmarker/cmark/src/inlines.c +976 -1030
- data/ext/commonmarker/cmark/src/inlines.h +4 -2
- data/ext/commonmarker/cmark/src/iterator.c +96 -126
- data/ext/commonmarker/cmark/src/iterator.h +5 -5
- data/ext/commonmarker/cmark/src/latex.c +379 -401
- data/ext/commonmarker/cmark/src/main.c +168 -175
- data/ext/commonmarker/cmark/src/man.c +212 -226
- data/ext/commonmarker/cmark/src/node.c +746 -839
- data/ext/commonmarker/cmark/src/node.h +47 -48
- data/ext/commonmarker/cmark/src/parser.h +14 -14
- data/ext/commonmarker/cmark/src/references.c +101 -111
- data/ext/commonmarker/cmark/src/references.h +10 -8
- data/ext/commonmarker/cmark/src/render.c +144 -167
- data/ext/commonmarker/cmark/src/render.h +22 -41
- data/ext/commonmarker/cmark/src/scanners.c +27695 -20903
- data/ext/commonmarker/cmark/src/scanners.h +2 -1
- data/ext/commonmarker/cmark/src/scanners.re +1 -1
- data/ext/commonmarker/cmark/src/utf8.c +276 -419
- data/ext/commonmarker/cmark/src/utf8.h +6 -6
- data/ext/commonmarker/cmark/src/xml.c +129 -144
- data/ext/commonmarker/cmark/test/CMakeLists.txt +4 -4
- data/ext/commonmarker/cmark/test/smart_punct.txt +8 -0
- data/ext/commonmarker/cmark/test/spec.txt +109 -47
- data/lib/commonmarker/version.rb +1 -1
- metadata +2 -2
@@ -19,1036 +19,1006 @@
|
|
19
19
|
|
20
20
|
#define peek_at(i, n) (i)->data[n]
|
21
21
|
|
22
|
-
static inline bool
|
23
|
-
|
24
|
-
{
|
25
|
-
return (c == '\n' || c == '\r');
|
22
|
+
static inline bool S_is_line_end_char(char c) {
|
23
|
+
return (c == '\n' || c == '\r');
|
26
24
|
}
|
27
25
|
|
28
|
-
static void
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
return e;
|
26
|
+
static void S_parser_feed(cmark_parser *parser, const unsigned char *buffer,
|
27
|
+
size_t len, bool eof);
|
28
|
+
|
29
|
+
static void S_process_line(cmark_parser *parser, const unsigned char *buffer,
|
30
|
+
bufsize_t bytes);
|
31
|
+
|
32
|
+
static cmark_node *make_block(cmark_node_type tag, int start_line,
|
33
|
+
int start_column) {
|
34
|
+
cmark_node *e;
|
35
|
+
|
36
|
+
e = (cmark_node *)calloc(1, sizeof(*e));
|
37
|
+
if (e != NULL) {
|
38
|
+
e->type = tag;
|
39
|
+
e->open = true;
|
40
|
+
e->start_line = start_line;
|
41
|
+
e->start_column = start_column;
|
42
|
+
e->end_line = start_line;
|
43
|
+
cmark_strbuf_init(&e->string_content, 32);
|
44
|
+
}
|
45
|
+
|
46
|
+
return e;
|
51
47
|
}
|
52
48
|
|
53
49
|
// Create a root document node.
|
54
|
-
static cmark_node*
|
55
|
-
|
56
|
-
|
57
|
-
return e;
|
50
|
+
static cmark_node *make_document() {
|
51
|
+
cmark_node *e = make_block(NODE_DOCUMENT, 1, 1);
|
52
|
+
return e;
|
58
53
|
}
|
59
54
|
|
60
|
-
cmark_parser *cmark_parser_new(int options)
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
return parser;
|
55
|
+
cmark_parser *cmark_parser_new(int options) {
|
56
|
+
cmark_parser *parser = (cmark_parser *)malloc(sizeof(cmark_parser));
|
57
|
+
cmark_node *document = make_document();
|
58
|
+
cmark_strbuf *line = (cmark_strbuf *)malloc(sizeof(cmark_strbuf));
|
59
|
+
cmark_strbuf *buf = (cmark_strbuf *)malloc(sizeof(cmark_strbuf));
|
60
|
+
cmark_strbuf_init(line, 256);
|
61
|
+
cmark_strbuf_init(buf, 0);
|
62
|
+
|
63
|
+
parser->refmap = cmark_reference_map_new();
|
64
|
+
parser->root = document;
|
65
|
+
parser->current = document;
|
66
|
+
parser->line_number = 0;
|
67
|
+
parser->offset = 0;
|
68
|
+
parser->column = 0;
|
69
|
+
parser->first_nonspace = 0;
|
70
|
+
parser->first_nonspace_column = 0;
|
71
|
+
parser->indent = 0;
|
72
|
+
parser->blank = false;
|
73
|
+
parser->curline = line;
|
74
|
+
parser->last_line_length = 0;
|
75
|
+
parser->linebuf = buf;
|
76
|
+
parser->options = options;
|
77
|
+
|
78
|
+
return parser;
|
85
79
|
}
|
86
80
|
|
87
|
-
void cmark_parser_free(cmark_parser *parser)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
free(parser);
|
81
|
+
void cmark_parser_free(cmark_parser *parser) {
|
82
|
+
cmark_strbuf_free(parser->curline);
|
83
|
+
free(parser->curline);
|
84
|
+
cmark_strbuf_free(parser->linebuf);
|
85
|
+
free(parser->linebuf);
|
86
|
+
cmark_reference_map_free(parser->refmap);
|
87
|
+
free(parser);
|
95
88
|
}
|
96
89
|
|
97
|
-
static cmark_node*
|
98
|
-
finalize(cmark_parser *parser, cmark_node* b);
|
90
|
+
static cmark_node *finalize(cmark_parser *parser, cmark_node *b);
|
99
91
|
|
100
92
|
// Returns true if line has only space characters, else false.
|
101
|
-
static bool is_blank(cmark_strbuf *s, bufsize_t offset)
|
102
|
-
{
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
return true;
|
93
|
+
static bool is_blank(cmark_strbuf *s, bufsize_t offset) {
|
94
|
+
while (offset < s->size) {
|
95
|
+
switch (s->ptr[offset]) {
|
96
|
+
case '\r':
|
97
|
+
case '\n':
|
98
|
+
return true;
|
99
|
+
case ' ':
|
100
|
+
offset++;
|
101
|
+
break;
|
102
|
+
case '\t':
|
103
|
+
offset++;
|
104
|
+
break;
|
105
|
+
default:
|
106
|
+
return false;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
return true;
|
120
111
|
}
|
121
112
|
|
122
|
-
static inline bool can_contain(cmark_node_type parent_type,
|
123
|
-
{
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
(parent_type == NODE_LIST && child_type == NODE_ITEM) );
|
113
|
+
static inline bool can_contain(cmark_node_type parent_type,
|
114
|
+
cmark_node_type child_type) {
|
115
|
+
return (parent_type == NODE_DOCUMENT || parent_type == NODE_BLOCK_QUOTE ||
|
116
|
+
parent_type == NODE_ITEM ||
|
117
|
+
(parent_type == NODE_LIST && child_type == NODE_ITEM));
|
128
118
|
}
|
129
119
|
|
130
|
-
static inline bool accepts_lines(cmark_node_type block_type)
|
131
|
-
|
132
|
-
|
133
|
-
block_type == NODE_HEADER ||
|
134
|
-
block_type == NODE_CODE_BLOCK);
|
120
|
+
static inline bool accepts_lines(cmark_node_type block_type) {
|
121
|
+
return (block_type == NODE_PARAGRAPH || block_type == NODE_HEADER ||
|
122
|
+
block_type == NODE_CODE_BLOCK);
|
135
123
|
}
|
136
124
|
|
137
|
-
static void add_line(cmark_node*
|
138
|
-
|
139
|
-
|
140
|
-
cmark_strbuf_put(&node->string_content, ch->data + offset, ch->len - offset);
|
125
|
+
static void add_line(cmark_node *node, cmark_chunk *ch, bufsize_t offset) {
|
126
|
+
assert(node->open);
|
127
|
+
cmark_strbuf_put(&node->string_content, ch->data + offset, ch->len - offset);
|
141
128
|
}
|
142
129
|
|
143
|
-
static void remove_trailing_blank_lines(cmark_strbuf *ln)
|
144
|
-
|
145
|
-
|
146
|
-
unsigned char c;
|
147
|
-
|
148
|
-
for (i = ln->size - 1; i >= 0; --i) {
|
149
|
-
c = ln->ptr[i];
|
130
|
+
static void remove_trailing_blank_lines(cmark_strbuf *ln) {
|
131
|
+
bufsize_t i;
|
132
|
+
unsigned char c;
|
150
133
|
|
151
|
-
|
152
|
-
|
153
|
-
}
|
134
|
+
for (i = ln->size - 1; i >= 0; --i) {
|
135
|
+
c = ln->ptr[i];
|
154
136
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
}
|
137
|
+
if (c != ' ' && c != '\t' && !S_is_line_end_char(c))
|
138
|
+
break;
|
139
|
+
}
|
159
140
|
|
141
|
+
if (i < 0) {
|
142
|
+
cmark_strbuf_clear(ln);
|
143
|
+
return;
|
144
|
+
}
|
160
145
|
|
161
|
-
|
162
|
-
|
146
|
+
for (; i < ln->size; ++i) {
|
147
|
+
c = ln->ptr[i];
|
163
148
|
|
164
|
-
|
165
|
-
|
149
|
+
if (!S_is_line_end_char(c))
|
150
|
+
continue;
|
166
151
|
|
167
|
-
|
168
|
-
|
169
|
-
|
152
|
+
cmark_strbuf_truncate(ln, i);
|
153
|
+
break;
|
154
|
+
}
|
170
155
|
}
|
171
156
|
|
172
157
|
// Check to see if a node ends with a blank line, descending
|
173
158
|
// if needed into lists and sublists.
|
174
|
-
static bool ends_with_blank_line(cmark_node*
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
return false;
|
159
|
+
static bool ends_with_blank_line(cmark_node *node) {
|
160
|
+
cmark_node *cur = node;
|
161
|
+
while (cur != NULL) {
|
162
|
+
if (cur->last_line_blank) {
|
163
|
+
return true;
|
164
|
+
}
|
165
|
+
if (cur->type == NODE_LIST || cur->type == NODE_ITEM) {
|
166
|
+
cur = cur->last_child;
|
167
|
+
} else {
|
168
|
+
cur = NULL;
|
169
|
+
}
|
170
|
+
}
|
171
|
+
return false;
|
188
172
|
}
|
189
173
|
|
190
174
|
// Break out of all containing lists
|
191
|
-
static int break_out_of_lists(cmark_parser *parser, cmark_node **
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
return 0;
|
175
|
+
static int break_out_of_lists(cmark_parser *parser, cmark_node **bptr) {
|
176
|
+
cmark_node *container = *bptr;
|
177
|
+
cmark_node *b = parser->root;
|
178
|
+
// find first containing NODE_LIST:
|
179
|
+
while (b && b->type != NODE_LIST) {
|
180
|
+
b = b->last_child;
|
181
|
+
}
|
182
|
+
if (b) {
|
183
|
+
while (container && container != b) {
|
184
|
+
container = finalize(parser, container);
|
185
|
+
}
|
186
|
+
finalize(parser, b);
|
187
|
+
*bptr = b->parent;
|
188
|
+
}
|
189
|
+
return 0;
|
207
190
|
}
|
208
191
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
break;
|
318
|
-
|
319
|
-
default:
|
320
|
-
break;
|
321
|
-
}
|
322
|
-
return parent;
|
192
|
+
static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
|
193
|
+
bufsize_t pos;
|
194
|
+
cmark_node *item;
|
195
|
+
cmark_node *subitem;
|
196
|
+
cmark_node *parent;
|
197
|
+
|
198
|
+
parent = b->parent;
|
199
|
+
|
200
|
+
assert(b->open); // shouldn't call finalize on closed blocks
|
201
|
+
b->open = false;
|
202
|
+
|
203
|
+
if (parser->curline->size == 0) {
|
204
|
+
// end of input - line number has not been incremented
|
205
|
+
b->end_line = parser->line_number;
|
206
|
+
b->end_column = parser->last_line_length;
|
207
|
+
} else if (b->type == NODE_DOCUMENT ||
|
208
|
+
(b->type == NODE_CODE_BLOCK && b->as.code.fenced) ||
|
209
|
+
(b->type == NODE_HEADER && b->as.header.setext)) {
|
210
|
+
b->end_line = parser->line_number;
|
211
|
+
b->end_column = parser->curline->size;
|
212
|
+
if (b->end_column && parser->curline->ptr[b->end_column - 1] == '\n')
|
213
|
+
b->end_column -= 1;
|
214
|
+
if (b->end_column && parser->curline->ptr[b->end_column - 1] == '\r')
|
215
|
+
b->end_column -= 1;
|
216
|
+
} else {
|
217
|
+
b->end_line = parser->line_number - 1;
|
218
|
+
b->end_column = parser->last_line_length;
|
219
|
+
}
|
220
|
+
|
221
|
+
switch (b->type) {
|
222
|
+
case NODE_PARAGRAPH:
|
223
|
+
while (cmark_strbuf_at(&b->string_content, 0) == '[' &&
|
224
|
+
(pos = cmark_parse_reference_inline(&b->string_content,
|
225
|
+
parser->refmap))) {
|
226
|
+
|
227
|
+
cmark_strbuf_drop(&b->string_content, pos);
|
228
|
+
}
|
229
|
+
if (is_blank(&b->string_content, 0)) {
|
230
|
+
// remove blank node (former reference def)
|
231
|
+
cmark_node_free(b);
|
232
|
+
}
|
233
|
+
break;
|
234
|
+
|
235
|
+
case NODE_CODE_BLOCK:
|
236
|
+
if (!b->as.code.fenced) { // indented code
|
237
|
+
remove_trailing_blank_lines(&b->string_content);
|
238
|
+
cmark_strbuf_putc(&b->string_content, '\n');
|
239
|
+
} else {
|
240
|
+
|
241
|
+
// first line of contents becomes info
|
242
|
+
for (pos = 0; pos < b->string_content.size; ++pos) {
|
243
|
+
if (S_is_line_end_char(b->string_content.ptr[pos]))
|
244
|
+
break;
|
245
|
+
}
|
246
|
+
assert(pos < b->string_content.size);
|
247
|
+
|
248
|
+
cmark_strbuf tmp = GH_BUF_INIT;
|
249
|
+
houdini_unescape_html_f(&tmp, b->string_content.ptr, pos);
|
250
|
+
cmark_strbuf_trim(&tmp);
|
251
|
+
cmark_strbuf_unescape(&tmp);
|
252
|
+
b->as.code.info = cmark_chunk_buf_detach(&tmp);
|
253
|
+
|
254
|
+
if (b->string_content.ptr[pos] == '\r')
|
255
|
+
pos += 1;
|
256
|
+
if (b->string_content.ptr[pos] == '\n')
|
257
|
+
pos += 1;
|
258
|
+
cmark_strbuf_drop(&b->string_content, pos);
|
259
|
+
}
|
260
|
+
b->as.code.literal = cmark_chunk_buf_detach(&b->string_content);
|
261
|
+
break;
|
262
|
+
|
263
|
+
case NODE_HTML:
|
264
|
+
b->as.literal = cmark_chunk_buf_detach(&b->string_content);
|
265
|
+
break;
|
266
|
+
|
267
|
+
case NODE_LIST: // determine tight/loose status
|
268
|
+
b->as.list.tight = true; // tight by default
|
269
|
+
item = b->first_child;
|
270
|
+
|
271
|
+
while (item) {
|
272
|
+
// check for non-final non-empty list item ending with blank line:
|
273
|
+
if (item->last_line_blank && item->next) {
|
274
|
+
b->as.list.tight = false;
|
275
|
+
break;
|
276
|
+
}
|
277
|
+
// recurse into children of list item, to see if there are
|
278
|
+
// spaces between them:
|
279
|
+
subitem = item->first_child;
|
280
|
+
while (subitem) {
|
281
|
+
if (ends_with_blank_line(subitem) && (item->next || subitem->next)) {
|
282
|
+
b->as.list.tight = false;
|
283
|
+
break;
|
284
|
+
}
|
285
|
+
subitem = subitem->next;
|
286
|
+
}
|
287
|
+
if (!(b->as.list.tight)) {
|
288
|
+
break;
|
289
|
+
}
|
290
|
+
item = item->next;
|
291
|
+
}
|
292
|
+
|
293
|
+
break;
|
294
|
+
|
295
|
+
default:
|
296
|
+
break;
|
297
|
+
}
|
298
|
+
return parent;
|
323
299
|
}
|
324
300
|
|
325
301
|
// Add a node as child of another. Return pointer to child.
|
326
|
-
static cmark_node*
|
327
|
-
cmark_node_type block_type, int start_column)
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
return child;
|
302
|
+
static cmark_node *add_child(cmark_parser *parser, cmark_node *parent,
|
303
|
+
cmark_node_type block_type, int start_column) {
|
304
|
+
assert(parent);
|
305
|
+
|
306
|
+
// if 'parent' isn't the kind of node that can accept this child,
|
307
|
+
// then back up til we hit a node that can.
|
308
|
+
while (!can_contain(parent->type, block_type)) {
|
309
|
+
parent = finalize(parser, parent);
|
310
|
+
}
|
311
|
+
|
312
|
+
cmark_node *child = make_block(block_type, parser->line_number, start_column);
|
313
|
+
child->parent = parent;
|
314
|
+
|
315
|
+
if (parent->last_child) {
|
316
|
+
parent->last_child->next = child;
|
317
|
+
child->prev = parent->last_child;
|
318
|
+
} else {
|
319
|
+
parent->first_child = child;
|
320
|
+
child->prev = NULL;
|
321
|
+
}
|
322
|
+
parent->last_child = child;
|
323
|
+
return child;
|
349
324
|
}
|
350
325
|
|
351
|
-
|
352
326
|
// Walk through node and all children, recursively, parsing
|
353
327
|
// string content into inline content where appropriate.
|
354
|
-
static void process_inlines(cmark_node*
|
355
|
-
{
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
cmark_iter_free(iter);
|
328
|
+
static void process_inlines(cmark_node *root, cmark_reference_map *refmap,
|
329
|
+
int options) {
|
330
|
+
cmark_iter *iter = cmark_iter_new(root);
|
331
|
+
cmark_node *cur;
|
332
|
+
cmark_event_type ev_type;
|
333
|
+
|
334
|
+
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
335
|
+
cur = cmark_iter_get_node(iter);
|
336
|
+
if (ev_type == CMARK_EVENT_ENTER) {
|
337
|
+
if (cur->type == NODE_PARAGRAPH || cur->type == NODE_HEADER) {
|
338
|
+
cmark_parse_inlines(cur, refmap, options);
|
339
|
+
}
|
340
|
+
}
|
341
|
+
}
|
342
|
+
|
343
|
+
cmark_iter_free(iter);
|
371
344
|
}
|
372
345
|
|
373
346
|
// Attempts to parse a list item marker (bullet or enumerated).
|
374
347
|
// On success, returns length of the marker, and populates
|
375
348
|
// data with the details. On failure, returns 0.
|
376
|
-
static bufsize_t parse_list_marker(cmark_chunk *input, bufsize_t pos,
|
377
|
-
{
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
349
|
+
static bufsize_t parse_list_marker(cmark_chunk *input, bufsize_t pos,
|
350
|
+
cmark_list **dataptr) {
|
351
|
+
unsigned char c;
|
352
|
+
bufsize_t startpos;
|
353
|
+
cmark_list *data;
|
354
|
+
|
355
|
+
startpos = pos;
|
356
|
+
c = peek_at(input, pos);
|
357
|
+
|
358
|
+
if (c == '*' || c == '-' || c == '+') {
|
359
|
+
pos++;
|
360
|
+
if (!cmark_isspace(peek_at(input, pos))) {
|
361
|
+
return 0;
|
362
|
+
}
|
363
|
+
data = (cmark_list *)calloc(1, sizeof(*data));
|
364
|
+
if (data == NULL) {
|
365
|
+
return 0;
|
366
|
+
} else {
|
367
|
+
data->marker_offset = 0; // will be adjusted later
|
368
|
+
data->list_type = CMARK_BULLET_LIST;
|
369
|
+
data->bullet_char = c;
|
370
|
+
data->start = 1;
|
371
|
+
data->delimiter = CMARK_PERIOD_DELIM;
|
372
|
+
data->tight = false;
|
373
|
+
}
|
374
|
+
} else if (cmark_isdigit(c)) {
|
375
|
+
int start = 0;
|
376
|
+
int digits = 0;
|
377
|
+
|
378
|
+
do {
|
379
|
+
start = (10 * start) + (peek_at(input, pos) - '0');
|
380
|
+
pos++;
|
381
|
+
digits++;
|
382
|
+
// We limit to 9 digits to avoid overflow,
|
383
|
+
// assuming max int is 2^31 - 1
|
384
|
+
// This also seems to be the limit for 'start' in some browsers.
|
385
|
+
} while (digits < 9 && cmark_isdigit(peek_at(input, pos)));
|
386
|
+
|
387
|
+
c = peek_at(input, pos);
|
388
|
+
if (c == '.' || c == ')') {
|
389
|
+
pos++;
|
390
|
+
if (!cmark_isspace(peek_at(input, pos))) {
|
391
|
+
return 0;
|
392
|
+
}
|
393
|
+
data = (cmark_list *)calloc(1, sizeof(*data));
|
394
|
+
if (data == NULL) {
|
395
|
+
return 0;
|
396
|
+
} else {
|
397
|
+
data->marker_offset = 0; // will be adjusted later
|
398
|
+
data->list_type = CMARK_ORDERED_LIST;
|
399
|
+
data->bullet_char = 0;
|
400
|
+
data->start = start;
|
401
|
+
data->delimiter = (c == '.' ? CMARK_PERIOD_DELIM : CMARK_PAREN_DELIM);
|
402
|
+
data->tight = false;
|
403
|
+
}
|
404
|
+
} else {
|
405
|
+
return 0;
|
406
|
+
}
|
407
|
+
|
408
|
+
} else {
|
409
|
+
return 0;
|
410
|
+
}
|
411
|
+
|
412
|
+
*dataptr = data;
|
413
|
+
return (pos - startpos);
|
441
414
|
}
|
442
415
|
|
443
416
|
// Return 1 if list item belongs in list, else 0.
|
444
|
-
static int lists_match(cmark_list *list_data, cmark_list *item_data)
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
list_data->bullet_char == item_data->bullet_char);
|
417
|
+
static int lists_match(cmark_list *list_data, cmark_list *item_data) {
|
418
|
+
return (list_data->list_type == item_data->list_type &&
|
419
|
+
list_data->delimiter == item_data->delimiter &&
|
420
|
+
// list_data->marker_offset == item_data.marker_offset &&
|
421
|
+
list_data->bullet_char == item_data->bullet_char);
|
450
422
|
}
|
451
423
|
|
452
|
-
static cmark_node *finalize_document(cmark_parser *parser)
|
453
|
-
{
|
454
|
-
|
455
|
-
|
456
|
-
}
|
424
|
+
static cmark_node *finalize_document(cmark_parser *parser) {
|
425
|
+
while (parser->current != parser->root) {
|
426
|
+
parser->current = finalize(parser, parser->current);
|
427
|
+
}
|
457
428
|
|
458
|
-
|
459
|
-
|
429
|
+
finalize(parser, parser->root);
|
430
|
+
process_inlines(parser->root, parser->refmap, parser->options);
|
460
431
|
|
461
|
-
|
432
|
+
return parser->root;
|
462
433
|
}
|
463
434
|
|
464
|
-
cmark_node *cmark_parse_file(FILE *f, int options)
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
return document;
|
435
|
+
cmark_node *cmark_parse_file(FILE *f, int options) {
|
436
|
+
unsigned char buffer[4096];
|
437
|
+
cmark_parser *parser = cmark_parser_new(options);
|
438
|
+
size_t bytes;
|
439
|
+
cmark_node *document;
|
440
|
+
|
441
|
+
while ((bytes = fread(buffer, 1, sizeof(buffer), f)) > 0) {
|
442
|
+
bool eof = bytes < sizeof(buffer);
|
443
|
+
S_parser_feed(parser, buffer, bytes, eof);
|
444
|
+
if (eof) {
|
445
|
+
break;
|
446
|
+
}
|
447
|
+
}
|
448
|
+
|
449
|
+
document = cmark_parser_finish(parser);
|
450
|
+
cmark_parser_free(parser);
|
451
|
+
return document;
|
482
452
|
}
|
483
453
|
|
484
|
-
cmark_node *cmark_parse_document(const char *buffer, size_t len, int options)
|
485
|
-
|
486
|
-
|
487
|
-
cmark_node *document;
|
454
|
+
cmark_node *cmark_parse_document(const char *buffer, size_t len, int options) {
|
455
|
+
cmark_parser *parser = cmark_parser_new(options);
|
456
|
+
cmark_node *document;
|
488
457
|
|
489
|
-
|
458
|
+
S_parser_feed(parser, (const unsigned char *)buffer, len, true);
|
490
459
|
|
491
|
-
|
492
|
-
|
493
|
-
|
460
|
+
document = cmark_parser_finish(parser);
|
461
|
+
cmark_parser_free(parser);
|
462
|
+
return document;
|
494
463
|
}
|
495
464
|
|
496
|
-
void
|
497
|
-
|
498
|
-
{
|
499
|
-
S_parser_feed(parser, (const unsigned char *)buffer, len, false);
|
465
|
+
void cmark_parser_feed(cmark_parser *parser, const char *buffer, size_t len) {
|
466
|
+
S_parser_feed(parser, (const unsigned char *)buffer, len, false);
|
500
467
|
}
|
501
468
|
|
502
|
-
static void
|
503
|
-
|
504
|
-
|
505
|
-
{
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
buffer += chunk_len;
|
553
|
-
}
|
469
|
+
static void S_parser_feed(cmark_parser *parser, const unsigned char *buffer,
|
470
|
+
size_t len, bool eof) {
|
471
|
+
const unsigned char *end = buffer + len;
|
472
|
+
static const uint8_t repl[] = {239, 191, 189};
|
473
|
+
|
474
|
+
while (buffer < end) {
|
475
|
+
const unsigned char *eol;
|
476
|
+
bufsize_t chunk_len;
|
477
|
+
bool process = false;
|
478
|
+
for (eol = buffer; eol < end; ++eol) {
|
479
|
+
if (S_is_line_end_char(*eol)) {
|
480
|
+
process = true;
|
481
|
+
break;
|
482
|
+
}
|
483
|
+
if (*eol == '\0' && eol < end) {
|
484
|
+
break;
|
485
|
+
}
|
486
|
+
}
|
487
|
+
if (eol >= end && eof) {
|
488
|
+
process = true;
|
489
|
+
}
|
490
|
+
|
491
|
+
chunk_len = cmark_strbuf_check_bufsize(eol - buffer);
|
492
|
+
if (process) {
|
493
|
+
if (parser->linebuf->size > 0) {
|
494
|
+
cmark_strbuf_put(parser->linebuf, buffer, chunk_len);
|
495
|
+
S_process_line(parser, parser->linebuf->ptr, parser->linebuf->size);
|
496
|
+
cmark_strbuf_clear(parser->linebuf);
|
497
|
+
} else {
|
498
|
+
S_process_line(parser, buffer, chunk_len);
|
499
|
+
}
|
500
|
+
} else {
|
501
|
+
if (eol < end && *eol == '\0') {
|
502
|
+
// omit NULL byte
|
503
|
+
cmark_strbuf_put(parser->linebuf, buffer, chunk_len);
|
504
|
+
// add replacement character
|
505
|
+
cmark_strbuf_put(parser->linebuf, repl, 3);
|
506
|
+
chunk_len += 1; // so we advance the buffer past NULL
|
507
|
+
} else {
|
508
|
+
cmark_strbuf_put(parser->linebuf, buffer, chunk_len);
|
509
|
+
}
|
510
|
+
}
|
511
|
+
|
512
|
+
buffer += chunk_len;
|
513
|
+
// skip over line ending characters:
|
514
|
+
if (buffer < end && *buffer == '\r')
|
515
|
+
buffer++;
|
516
|
+
if (buffer < end && *buffer == '\n')
|
517
|
+
buffer++;
|
518
|
+
}
|
554
519
|
}
|
555
520
|
|
556
|
-
static void chop_trailing_hashtags(cmark_chunk *ch)
|
557
|
-
|
558
|
-
bufsize_t n, orig_n;
|
521
|
+
static void chop_trailing_hashtags(cmark_chunk *ch) {
|
522
|
+
bufsize_t n, orig_n;
|
559
523
|
|
560
|
-
|
561
|
-
|
524
|
+
cmark_chunk_rtrim(ch);
|
525
|
+
orig_n = n = ch->len - 1;
|
562
526
|
|
563
|
-
|
564
|
-
|
565
|
-
|
527
|
+
// if string ends in space followed by #s, remove these:
|
528
|
+
while (n >= 0 && peek_at(ch, n) == '#')
|
529
|
+
n--;
|
566
530
|
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
531
|
+
// Check for a space before the final #s:
|
532
|
+
if (n != orig_n && n >= 0 &&
|
533
|
+
(peek_at(ch, n) == ' ' || peek_at(ch, n) == '\t')) {
|
534
|
+
ch->len = n;
|
535
|
+
cmark_chunk_rtrim(ch);
|
536
|
+
}
|
573
537
|
}
|
574
538
|
|
575
|
-
static void
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
parser->indent = parser->first_nonspace_column - parser->column;
|
601
|
-
parser->blank = S_is_line_end_char(peek_at(input, parser->first_nonspace));
|
539
|
+
static void S_find_first_nonspace(cmark_parser *parser, cmark_chunk *input) {
|
540
|
+
char c;
|
541
|
+
int chars_to_tab = TAB_STOP - (parser->column % TAB_STOP);
|
542
|
+
|
543
|
+
parser->first_nonspace = parser->offset;
|
544
|
+
parser->first_nonspace_column = parser->column;
|
545
|
+
while ((c = peek_at(input, parser->first_nonspace))) {
|
546
|
+
if (c == ' ') {
|
547
|
+
parser->first_nonspace += 1;
|
548
|
+
parser->first_nonspace_column += 1;
|
549
|
+
chars_to_tab = chars_to_tab - 1;
|
550
|
+
if (chars_to_tab == 0) {
|
551
|
+
chars_to_tab = TAB_STOP;
|
552
|
+
}
|
553
|
+
} else if (c == '\t') {
|
554
|
+
parser->first_nonspace += 1;
|
555
|
+
parser->first_nonspace_column += chars_to_tab;
|
556
|
+
chars_to_tab = TAB_STOP;
|
557
|
+
} else {
|
558
|
+
break;
|
559
|
+
}
|
560
|
+
}
|
561
|
+
|
562
|
+
parser->indent = parser->first_nonspace_column - parser->column;
|
563
|
+
parser->blank = S_is_line_end_char(peek_at(input, parser->first_nonspace));
|
602
564
|
}
|
603
565
|
|
604
|
-
static void
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
}
|
566
|
+
static void S_advance_offset(cmark_parser *parser, cmark_chunk *input,
|
567
|
+
bufsize_t count, bool columns) {
|
568
|
+
char c;
|
569
|
+
int chars_to_tab;
|
570
|
+
while (count > 0 && (c = peek_at(input, parser->offset))) {
|
571
|
+
if (c == '\t') {
|
572
|
+
chars_to_tab = 4 - (parser->column % TAB_STOP);
|
573
|
+
parser->column += chars_to_tab;
|
574
|
+
parser->offset += 1;
|
575
|
+
count -= (columns ? chars_to_tab : 1);
|
576
|
+
} else {
|
577
|
+
parser->offset += 1;
|
578
|
+
parser->column += 1; // assume ascii; block starts are ascii
|
579
|
+
count -= 1;
|
580
|
+
}
|
581
|
+
}
|
621
582
|
}
|
622
583
|
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
584
|
+
static void S_process_line(cmark_parser *parser, const unsigned char *buffer,
|
585
|
+
bufsize_t bytes) {
|
586
|
+
cmark_node *last_matched_container;
|
587
|
+
bufsize_t matched = 0;
|
588
|
+
int lev = 0;
|
589
|
+
int i;
|
590
|
+
cmark_list *data = NULL;
|
591
|
+
bool all_matched = true;
|
592
|
+
cmark_node *container;
|
593
|
+
bool indented;
|
594
|
+
cmark_chunk input;
|
595
|
+
bool maybe_lazy;
|
596
|
+
|
597
|
+
if (parser->options & CMARK_OPT_VALIDATE_UTF8) {
|
598
|
+
cmark_utf8proc_check(parser->curline, buffer, bytes);
|
599
|
+
} else {
|
600
|
+
cmark_strbuf_put(parser->curline, buffer, bytes);
|
601
|
+
}
|
602
|
+
// ensure line ends with a newline:
|
603
|
+
if (bytes == 0 || !S_is_line_end_char(parser->curline->ptr[bytes - 1])) {
|
604
|
+
cmark_strbuf_putc(parser->curline, '\n');
|
605
|
+
}
|
606
|
+
parser->offset = 0;
|
607
|
+
parser->column = 0;
|
608
|
+
parser->blank = false;
|
609
|
+
|
610
|
+
input.data = parser->curline->ptr;
|
611
|
+
input.len = parser->curline->size;
|
612
|
+
|
613
|
+
// container starts at the document root.
|
614
|
+
container = parser->root;
|
615
|
+
|
616
|
+
parser->line_number++;
|
617
|
+
|
618
|
+
// for each containing node, try to parse the associated line start.
|
619
|
+
// bail out on failure: container will point to the last matching node.
|
620
|
+
|
621
|
+
while (container->last_child && container->last_child->open) {
|
622
|
+
container = container->last_child;
|
623
|
+
|
624
|
+
S_find_first_nonspace(parser, &input);
|
625
|
+
|
626
|
+
if (container->type == NODE_BLOCK_QUOTE) {
|
627
|
+
matched =
|
628
|
+
parser->indent <= 3 && peek_at(&input, parser->first_nonspace) == '>';
|
629
|
+
if (matched) {
|
630
|
+
S_advance_offset(parser, &input, parser->indent + 1, true);
|
631
|
+
if (peek_at(&input, parser->offset) == ' ')
|
632
|
+
parser->offset++;
|
633
|
+
} else {
|
634
|
+
all_matched = false;
|
635
|
+
}
|
636
|
+
|
637
|
+
} else if (container->type == NODE_ITEM) {
|
638
|
+
if (parser->indent >=
|
639
|
+
container->as.list.marker_offset + container->as.list.padding) {
|
640
|
+
S_advance_offset(parser, &input, container->as.list.marker_offset +
|
641
|
+
container->as.list.padding,
|
642
|
+
true);
|
643
|
+
} else if (parser->blank && container->first_child != NULL) {
|
644
|
+
// if container->first_child is NULL, then the opening line
|
645
|
+
// of the list item was blank after the list marker; in this
|
646
|
+
// case, we are done with the list item.
|
647
|
+
S_advance_offset(parser, &input,
|
648
|
+
parser->first_nonspace - parser->offset, false);
|
649
|
+
} else {
|
650
|
+
all_matched = false;
|
651
|
+
}
|
652
|
+
|
653
|
+
} else if (container->type == NODE_CODE_BLOCK) {
|
654
|
+
|
655
|
+
if (!container->as.code.fenced) { // indented
|
656
|
+
if (parser->indent >= CODE_INDENT) {
|
657
|
+
S_advance_offset(parser, &input, CODE_INDENT, true);
|
658
|
+
} else if (parser->blank) {
|
659
|
+
S_advance_offset(parser, &input,
|
660
|
+
parser->first_nonspace - parser->offset, false);
|
661
|
+
} else {
|
662
|
+
all_matched = false;
|
663
|
+
}
|
664
|
+
} else { // fenced
|
665
|
+
matched = 0;
|
666
|
+
if (parser->indent <= 3 && (peek_at(&input, parser->first_nonspace) ==
|
667
|
+
container->as.code.fence_char)) {
|
668
|
+
matched = scan_close_code_fence(&input, parser->first_nonspace);
|
669
|
+
}
|
670
|
+
if (matched >= container->as.code.fence_length) {
|
671
|
+
// closing fence - and since we're at
|
672
|
+
// the end of a line, we can return:
|
673
|
+
all_matched = false;
|
674
|
+
S_advance_offset(parser, &input, matched, false);
|
675
|
+
parser->current = finalize(parser, container);
|
676
|
+
goto finished;
|
677
|
+
} else {
|
678
|
+
// skip opt. spaces of fence parser->offset
|
679
|
+
i = container->as.code.fence_offset;
|
680
|
+
while (i > 0 && peek_at(&input, parser->offset) == ' ') {
|
681
|
+
S_advance_offset(parser, &input, 1, false);
|
682
|
+
i--;
|
683
|
+
}
|
684
|
+
}
|
685
|
+
}
|
686
|
+
} else if (container->type == NODE_HEADER) {
|
687
|
+
|
688
|
+
// a header can never contain more than one line
|
689
|
+
all_matched = false;
|
690
|
+
|
691
|
+
} else if (container->type == NODE_HTML) {
|
692
|
+
|
693
|
+
switch (container->as.html_block_type) {
|
694
|
+
case 1:
|
695
|
+
case 2:
|
696
|
+
case 3:
|
697
|
+
case 4:
|
698
|
+
case 5:
|
699
|
+
// these types of blocks can accept blanks
|
700
|
+
break;
|
701
|
+
case 6:
|
702
|
+
case 7:
|
703
|
+
if (parser->blank) {
|
704
|
+
all_matched = false;
|
705
|
+
}
|
706
|
+
break;
|
707
|
+
default:
|
708
|
+
fprintf(stderr, "Error (%s:%d): Unknown HTML block type %d\n", __FILE__,
|
709
|
+
__LINE__, container->as.html_block_type);
|
710
|
+
exit(1);
|
711
|
+
}
|
712
|
+
|
713
|
+
} else if (container->type == NODE_PARAGRAPH) {
|
714
|
+
|
715
|
+
if (parser->blank) {
|
716
|
+
all_matched = false;
|
717
|
+
}
|
718
|
+
}
|
719
|
+
|
720
|
+
if (!all_matched) {
|
721
|
+
container = container->parent; // back up to last matching node
|
722
|
+
break;
|
723
|
+
}
|
724
|
+
}
|
725
|
+
|
726
|
+
last_matched_container = container;
|
727
|
+
|
728
|
+
// check to see if we've hit 2nd blank line, break out of list:
|
729
|
+
if (parser->blank && container->last_line_blank) {
|
730
|
+
break_out_of_lists(parser, &container);
|
731
|
+
}
|
732
|
+
|
733
|
+
maybe_lazy = parser->current->type == NODE_PARAGRAPH;
|
734
|
+
// try new container starts:
|
735
|
+
while (container->type != NODE_CODE_BLOCK && container->type != NODE_HTML) {
|
736
|
+
|
737
|
+
S_find_first_nonspace(parser, &input);
|
738
|
+
indented = parser->indent >= CODE_INDENT;
|
739
|
+
|
740
|
+
if (!indented && peek_at(&input, parser->first_nonspace) == '>') {
|
741
|
+
|
742
|
+
S_advance_offset(parser, &input,
|
743
|
+
parser->first_nonspace + 1 - parser->offset, false);
|
744
|
+
// optional following character
|
745
|
+
if (peek_at(&input, parser->offset) == ' ')
|
746
|
+
S_advance_offset(parser, &input, 1, false);
|
747
|
+
container =
|
748
|
+
add_child(parser, container, NODE_BLOCK_QUOTE, parser->offset + 1);
|
749
|
+
|
750
|
+
} else if (!indented && (matched = scan_atx_header_start(
|
751
|
+
&input, parser->first_nonspace))) {
|
752
|
+
|
753
|
+
S_advance_offset(parser, &input,
|
754
|
+
parser->first_nonspace + matched - parser->offset,
|
755
|
+
false);
|
756
|
+
container = add_child(parser, container, NODE_HEADER, parser->offset + 1);
|
757
|
+
|
758
|
+
bufsize_t hashpos =
|
759
|
+
cmark_chunk_strchr(&input, '#', parser->first_nonspace);
|
760
|
+
int level = 0;
|
761
|
+
|
762
|
+
while (peek_at(&input, hashpos) == '#') {
|
763
|
+
level++;
|
764
|
+
hashpos++;
|
765
|
+
}
|
766
|
+
container->as.header.level = level;
|
767
|
+
container->as.header.setext = false;
|
768
|
+
|
769
|
+
} else if (!indented && (matched = scan_open_code_fence(
|
770
|
+
&input, parser->first_nonspace))) {
|
771
|
+
|
772
|
+
container = add_child(parser, container, NODE_CODE_BLOCK,
|
773
|
+
parser->first_nonspace + 1);
|
774
|
+
container->as.code.fenced = true;
|
775
|
+
container->as.code.fence_char = peek_at(&input, parser->first_nonspace);
|
776
|
+
container->as.code.fence_length = matched;
|
777
|
+
container->as.code.fence_offset = (int8_t)(parser->first_nonspace - parser->offset);
|
778
|
+
container->as.code.info = cmark_chunk_literal("");
|
779
|
+
S_advance_offset(parser, &input,
|
780
|
+
parser->first_nonspace + matched - parser->offset,
|
781
|
+
false);
|
782
|
+
|
783
|
+
} else if (!indented && ((matched = scan_html_block_start(
|
784
|
+
&input, parser->first_nonspace)) ||
|
785
|
+
(container->type != NODE_PARAGRAPH &&
|
786
|
+
(matched = scan_html_block_start_7(
|
787
|
+
&input, parser->first_nonspace))))) {
|
788
|
+
|
789
|
+
container =
|
790
|
+
add_child(parser, container, NODE_HTML, parser->first_nonspace + 1);
|
791
|
+
container->as.html_block_type = matched;
|
792
|
+
// note, we don't adjust parser->offset because the tag is part of the
|
793
|
+
// text
|
794
|
+
|
795
|
+
} else if (!indented && container->type == NODE_PARAGRAPH &&
|
796
|
+
(lev =
|
797
|
+
scan_setext_header_line(&input, parser->first_nonspace)) &&
|
798
|
+
// check that there is only one line in the paragraph:
|
799
|
+
(cmark_strbuf_strrchr(
|
800
|
+
&container->string_content, '\n',
|
801
|
+
cmark_strbuf_len(&container->string_content) - 2) < 0)) {
|
802
|
+
|
803
|
+
container->type = NODE_HEADER;
|
804
|
+
container->as.header.level = lev;
|
805
|
+
container->as.header.setext = true;
|
806
|
+
S_advance_offset(parser, &input, input.len - 1 - parser->offset, false);
|
807
|
+
|
808
|
+
} else if (!indented &&
|
809
|
+
!(container->type == NODE_PARAGRAPH && !all_matched) &&
|
810
|
+
(matched = scan_hrule(&input, parser->first_nonspace))) {
|
811
|
+
|
812
|
+
// it's only now that we know the line is not part of a setext header:
|
813
|
+
container =
|
814
|
+
add_child(parser, container, NODE_HRULE, parser->first_nonspace + 1);
|
815
|
+
S_advance_offset(parser, &input, input.len - 1 - parser->offset, false);
|
816
|
+
|
817
|
+
} else if ((matched =
|
818
|
+
parse_list_marker(&input, parser->first_nonspace, &data)) &&
|
819
|
+
(!indented || container->type == NODE_LIST)) {
|
820
|
+
// Note that we can have new list items starting with >= 4
|
821
|
+
// spaces indent, as long as the list container is still open.
|
822
|
+
|
823
|
+
// compute padding:
|
824
|
+
S_advance_offset(parser, &input,
|
825
|
+
parser->first_nonspace + matched - parser->offset,
|
826
|
+
false);
|
827
|
+
i = 0;
|
828
|
+
while (i <= 5 && peek_at(&input, parser->offset + i) == ' ') {
|
829
|
+
i++;
|
830
|
+
}
|
831
|
+
// i = number of spaces after marker, up to 5
|
832
|
+
if (i >= 5 || i < 1 ||
|
833
|
+
S_is_line_end_char(peek_at(&input, parser->offset))) {
|
834
|
+
data->padding = matched + 1;
|
835
|
+
if (i > 0) {
|
836
|
+
S_advance_offset(parser, &input, 1, false);
|
837
|
+
}
|
838
|
+
} else {
|
839
|
+
data->padding = matched + i;
|
840
|
+
S_advance_offset(parser, &input, i, true);
|
841
|
+
}
|
842
|
+
|
843
|
+
// check container; if it's a list, see if this list item
|
844
|
+
// can continue the list; otherwise, create a list container.
|
845
|
+
|
846
|
+
data->marker_offset = parser->indent;
|
847
|
+
|
848
|
+
if (container->type != NODE_LIST ||
|
849
|
+
!lists_match(&container->as.list, data)) {
|
850
|
+
container =
|
851
|
+
add_child(parser, container, NODE_LIST, parser->first_nonspace + 1);
|
852
|
+
|
853
|
+
memcpy(&container->as.list, data, sizeof(*data));
|
854
|
+
}
|
855
|
+
|
856
|
+
// add the list item
|
857
|
+
container =
|
858
|
+
add_child(parser, container, NODE_ITEM, parser->first_nonspace + 1);
|
859
|
+
/* TODO: static */
|
860
|
+
memcpy(&container->as.list, data, sizeof(*data));
|
861
|
+
free(data);
|
862
|
+
|
863
|
+
} else if (indented && !maybe_lazy && !parser->blank) {
|
864
|
+
S_advance_offset(parser, &input, CODE_INDENT, true);
|
865
|
+
container =
|
866
|
+
add_child(parser, container, NODE_CODE_BLOCK, parser->offset + 1);
|
867
|
+
container->as.code.fenced = false;
|
868
|
+
container->as.code.fence_char = 0;
|
869
|
+
container->as.code.fence_length = 0;
|
870
|
+
container->as.code.fence_offset = 0;
|
871
|
+
container->as.code.info = cmark_chunk_literal("");
|
872
|
+
|
873
|
+
} else {
|
874
|
+
break;
|
875
|
+
}
|
876
|
+
|
877
|
+
if (accepts_lines(container->type)) {
|
878
|
+
// if it's a line container, it can't contain other containers
|
879
|
+
break;
|
880
|
+
}
|
881
|
+
maybe_lazy = false;
|
882
|
+
}
|
883
|
+
|
884
|
+
// what remains at parser->offset is a text line. add the text to the
|
885
|
+
// appropriate container.
|
886
|
+
|
887
|
+
S_find_first_nonspace(parser, &input);
|
888
|
+
|
889
|
+
if (parser->blank && container->last_child) {
|
890
|
+
container->last_child->last_line_blank = true;
|
891
|
+
}
|
892
|
+
|
893
|
+
// block quote lines are never blank as they start with >
|
894
|
+
// and we don't count blanks in fenced code for purposes of tight/loose
|
895
|
+
// lists or breaking out of lists. we also don't set last_line_blank
|
896
|
+
// on an empty list item.
|
897
|
+
container->last_line_blank =
|
898
|
+
(parser->blank && container->type != NODE_BLOCK_QUOTE &&
|
899
|
+
container->type != NODE_HEADER &&
|
900
|
+
container->type != NODE_HRULE &&
|
901
|
+
!(container->type == NODE_CODE_BLOCK && container->as.code.fenced) &&
|
902
|
+
!(container->type == NODE_ITEM && container->first_child == NULL &&
|
903
|
+
container->start_line == parser->line_number));
|
904
|
+
|
905
|
+
cmark_node *cont = container;
|
906
|
+
while (cont->parent) {
|
907
|
+
cont->parent->last_line_blank = false;
|
908
|
+
cont = cont->parent;
|
909
|
+
}
|
910
|
+
|
911
|
+
if (parser->current != last_matched_container &&
|
912
|
+
container == last_matched_container && !parser->blank &&
|
913
|
+
parser->current->type == NODE_PARAGRAPH &&
|
914
|
+
cmark_strbuf_len(&parser->current->string_content) > 0) {
|
915
|
+
|
916
|
+
add_line(parser->current, &input, parser->offset);
|
917
|
+
|
918
|
+
} else { // not a lazy continuation
|
919
|
+
|
920
|
+
// finalize any blocks that were not matched and set cur to container:
|
921
|
+
while (parser->current != last_matched_container) {
|
922
|
+
parser->current = finalize(parser, parser->current);
|
923
|
+
assert(parser->current != NULL);
|
924
|
+
}
|
925
|
+
|
926
|
+
if (container->type == NODE_CODE_BLOCK) {
|
927
|
+
|
928
|
+
add_line(container, &input, parser->offset);
|
929
|
+
|
930
|
+
} else if (container->type == NODE_HTML) {
|
931
|
+
|
932
|
+
add_line(container, &input, parser->offset);
|
933
|
+
|
934
|
+
int matches_end_condition;
|
935
|
+
switch (container->as.html_block_type) {
|
936
|
+
case 1:
|
937
|
+
// </script>, </style>, </pre>
|
938
|
+
matches_end_condition =
|
939
|
+
scan_html_block_end_1(&input, parser->first_nonspace);
|
940
|
+
break;
|
941
|
+
case 2:
|
942
|
+
// -->
|
943
|
+
matches_end_condition =
|
944
|
+
scan_html_block_end_2(&input, parser->first_nonspace);
|
945
|
+
break;
|
946
|
+
case 3:
|
947
|
+
// ?>
|
948
|
+
matches_end_condition =
|
949
|
+
scan_html_block_end_3(&input, parser->first_nonspace);
|
950
|
+
break;
|
951
|
+
case 4:
|
952
|
+
// >
|
953
|
+
matches_end_condition =
|
954
|
+
scan_html_block_end_4(&input, parser->first_nonspace);
|
955
|
+
break;
|
956
|
+
case 5:
|
957
|
+
// ]]>
|
958
|
+
matches_end_condition =
|
959
|
+
scan_html_block_end_5(&input, parser->first_nonspace);
|
960
|
+
break;
|
961
|
+
default:
|
962
|
+
matches_end_condition = 0;
|
963
|
+
break;
|
964
|
+
}
|
965
|
+
|
966
|
+
if (matches_end_condition) {
|
967
|
+
container = finalize(parser, container);
|
968
|
+
assert(parser->current != NULL);
|
969
|
+
}
|
970
|
+
|
971
|
+
} else if (parser->blank) {
|
972
|
+
|
973
|
+
// ??? do nothing
|
974
|
+
|
975
|
+
} else if (accepts_lines(container->type)) {
|
976
|
+
|
977
|
+
if (container->type == NODE_HEADER &&
|
978
|
+
container->as.header.setext == false) {
|
979
|
+
chop_trailing_hashtags(&input);
|
980
|
+
}
|
981
|
+
add_line(container, &input, parser->first_nonspace);
|
982
|
+
|
983
|
+
} else {
|
984
|
+
// create paragraph container for line
|
985
|
+
container = add_child(parser, container, NODE_PARAGRAPH,
|
986
|
+
parser->first_nonspace + 1);
|
987
|
+
add_line(container, &input, parser->first_nonspace);
|
988
|
+
}
|
989
|
+
|
990
|
+
parser->current = container;
|
991
|
+
}
|
1019
992
|
finished:
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
993
|
+
parser->last_line_length = parser->curline->size;
|
994
|
+
if (parser->last_line_length &&
|
995
|
+
parser->curline->ptr[parser->last_line_length - 1] == '\n')
|
996
|
+
parser->last_line_length -= 1;
|
997
|
+
if (parser->last_line_length &&
|
998
|
+
parser->curline->ptr[parser->last_line_length - 1] == '\r')
|
999
|
+
parser->last_line_length -= 1;
|
1000
|
+
|
1001
|
+
cmark_strbuf_clear(parser->curline);
|
1030
1002
|
}
|
1031
1003
|
|
1032
|
-
cmark_node *cmark_parser_finish(cmark_parser *parser)
|
1033
|
-
{
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
cmark_strbuf_clear(parser->linebuf);
|
1038
|
-
}
|
1004
|
+
cmark_node *cmark_parser_finish(cmark_parser *parser) {
|
1005
|
+
if (parser->linebuf->size) {
|
1006
|
+
S_process_line(parser, parser->linebuf->ptr, parser->linebuf->size);
|
1007
|
+
cmark_strbuf_clear(parser->linebuf);
|
1008
|
+
}
|
1039
1009
|
|
1040
|
-
|
1010
|
+
finalize_document(parser);
|
1041
1011
|
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1012
|
+
if (parser->options & CMARK_OPT_NORMALIZE) {
|
1013
|
+
cmark_consolidate_text_nodes(parser->root);
|
1014
|
+
}
|
1045
1015
|
|
1046
|
-
|
1016
|
+
cmark_strbuf_free(parser->curline);
|
1047
1017
|
|
1048
1018
|
#if CMARK_DEBUG_NODES
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1019
|
+
if (cmark_node_check(parser->root, stderr)) {
|
1020
|
+
abort();
|
1021
|
+
}
|
1052
1022
|
#endif
|
1053
|
-
|
1023
|
+
return parser->root;
|
1054
1024
|
}
|