qiita_marker 0.23.2.0 → 0.23.2.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b4222f9db082f683cdabb0d9d42a255ab4b689fe19cdb178efe4feccea10439d
4
- data.tar.gz: 49e29b6cee35cb99e550f65ed830e6cd83448c7b058c09c3383c97722df86b5c
3
+ metadata.gz: ad4193d920fb95f2c24307d15220150746187c0d74a3d470690e318b2d99d0d6
4
+ data.tar.gz: b5d1fb774bfafe83550bc0beba25fdc9d8cf67612bb3f4818e3d8e83be58cce8
5
5
  SHA512:
6
- metadata.gz: c74fe17ddee587a42758795f9a9ff9bd5982b7c284a247615af864e6fdad8dd1b6708b55eadbec2b9eaf893e3d9eeb5e03c72d62da78598322b90d01adc8cc84
7
- data.tar.gz: f8180555767d3aa6134f7dae895d06fdda9f4b655d41d6872b6234dbe073787debd4fd0b6956da261f149b28d821d95c6e0b947e7049307a92267205e24a5a9c
6
+ metadata.gz: 690f8870c83b4bf02a799dfd6206ef1c893f22acb22c91110b5523dbe0c35c8f5b3f2276926828697f3cc90497ac9a0302fe09c5fd2b33aca21213c586e94c42
7
+ data.tar.gz: 674aa47fc8e97954c20a98648307cd0c96dfda31e29f975333c3fdd4d8baf2ad059c759f4db7e7bc3dbe652d2085c224b5baa6359065524d64c5092c4c38f00d
data/LICENSE.txt CHANGED
@@ -1,21 +1,23 @@
1
- MIT License
1
+ Copyright (c) 2015 Garen J. Torikian
2
+ Copyright (c) 2021 Qiita Inc.
2
3
 
3
- Copyright 2021 Increments Inc.
4
+ MIT License
4
5
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
11
13
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
14
16
 
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Qiita Marker
2
2
 
3
- [![Build Status](https://github.com/increments/qiita-marker/actions/workflows/test.yml/badge.svg)](https://github.com/increments/qiita-marker/actions/workflows/test.yml)
3
+ [![Build Status](https://github.com/increments/qiita-marker/actions/workflows/test.yml/badge.svg)](https://github.com/increments/qiita-marker/actions/workflows/test.yml) [![Gem Version](https://badge.fury.io/rb/qiita_marker.svg)](https://badge.fury.io/rb/qiita_marker)
4
4
 
5
5
  :warning: This library is still in the testing phase. As such, development may be halted.
6
6
 
@@ -11,6 +11,29 @@ It will be a core module of [Qiita Markdown](https://github.com/increments/qiita
11
11
 
12
12
  Please see [CommonMarker's Usage](https://github.com/gjtorikian/commonmarker#usage).
13
13
 
14
+ In addition to CommonMarker's options and extensions, the following are available in Qiita Marker.
15
+
16
+ ### Original options
17
+
18
+ #### Parse options
19
+
20
+ | Name | Description |
21
+ | --- | --- |
22
+ | `:MENTION_NO_EMPHASIS` | Prevent parsing mentions as emphasis. |
23
+ | `:AUTOLINK_CLASS_NAME` | Append `class="autolink"` to extension's autolinks. |
24
+
25
+ #### Render options
26
+
27
+ | Name | Description |
28
+ | --- | --- |
29
+ | `:CODE_DATA_METADATA` | Use `<code data-metadata>` for fenced code blocks. |
30
+ | `:MENTION_NO_EMPHASIS` | Prevent parsing mentions as emphasis. |
31
+ | `:AUTOLINK_CLASS_NAME` | Append `class="autolink"` to extension's autolinks. |
32
+
33
+ ### Original extensions
34
+
35
+ - `:custom_block` - This provides support for customizable blocks.
36
+
14
37
  ## Contributing
15
38
 
16
39
  If you have suggestion or modification to this repository, please create an Issue or Pull Request.
@@ -45,6 +68,11 @@ $ docker compose run --rm app ./script/bootstrap
45
68
  $ docker compose run --rm rake test
46
69
  ```
47
70
 
71
+ ### Versioning policy
72
+
73
+ Qiita Marker follows CommonMarker's updates by merging the upstream changes.
74
+ The version format is `MAJOR.MINOR.PATCH.FORK`. `MAJOR.MINOR.PATCH` is the same as the version of CommonMarker that Qiita Marker is based on. `FORK` is incremented on each release of Qiita Marker itself and reset to zero when any of `MAJOR.MINOR.PATCH` is bumped.
75
+
48
76
  ## License
49
77
 
50
78
  Please see [LICENSE.txt](/LICENSE.txt).
@@ -1,5 +1,9 @@
1
1
  #include "autolink.h"
2
+ #include "houdini.h"
3
+ #include "qfm.h"
4
+ #include "scanners.h"
2
5
  #include <parser.h>
6
+ #include <render.h>
3
7
  #include <string.h>
4
8
  #include <utf8.h>
5
9
 
@@ -9,6 +13,11 @@
9
13
  #include <strings.h>
10
14
  #endif
11
15
 
16
+ static void escape_html(cmark_strbuf *dest, const unsigned char *source,
17
+ bufsize_t length) {
18
+ houdini_escape_html0(dest, source, length, 0);
19
+ }
20
+
12
21
  static int is_valid_hostchar(const uint8_t *link, size_t link_len) {
13
22
  int32_t ch;
14
23
  int r = cmark_utf8proc_iterate(link, (bufsize_t)link_len, &ch);
@@ -144,7 +153,8 @@ static size_t check_domain(uint8_t *data, size_t size, int allow_short) {
144
153
  }
145
154
 
146
155
  static cmark_node *www_match(cmark_parser *parser, cmark_node *parent,
147
- cmark_inline_parser *inline_parser) {
156
+ cmark_inline_parser *inline_parser,
157
+ cmark_syntax_extension *ext) {
148
158
  cmark_chunk *chunk = cmark_inline_parser_get_chunk(inline_parser);
149
159
  size_t max_rewind = cmark_inline_parser_get_offset(inline_parser);
150
160
  uint8_t *data = chunk->data + max_rewind;
@@ -176,6 +186,9 @@ static cmark_node *www_match(cmark_parser *parser, cmark_node *parent,
176
186
  cmark_inline_parser_set_offset(inline_parser, (int)(max_rewind + link_end));
177
187
 
178
188
  cmark_node *node = cmark_node_new_with_mem(CMARK_NODE_LINK, parser->mem);
189
+ if (parser->options & CMARK_OPT_AUTOLINK_CLASS_NAME) {
190
+ cmark_node_set_syntax_extension(node, ext);
191
+ }
179
192
 
180
193
  cmark_strbuf buf;
181
194
  cmark_strbuf_init(parser->mem, &buf, 10);
@@ -199,7 +212,8 @@ static cmark_node *www_match(cmark_parser *parser, cmark_node *parent,
199
212
  }
200
213
 
201
214
  static cmark_node *url_match(cmark_parser *parser, cmark_node *parent,
202
- cmark_inline_parser *inline_parser) {
215
+ cmark_inline_parser *inline_parser,
216
+ cmark_syntax_extension *ext) {
203
217
  size_t link_end, domain_len;
204
218
  int rewind = 0;
205
219
 
@@ -237,6 +251,9 @@ static cmark_node *url_match(cmark_parser *parser, cmark_node *parent,
237
251
  cmark_node_unput(parent, rewind);
238
252
 
239
253
  cmark_node *node = cmark_node_new_with_mem(CMARK_NODE_LINK, parser->mem);
254
+ if (parser->options & CMARK_OPT_AUTOLINK_CLASS_NAME) {
255
+ cmark_node_set_syntax_extension(node, ext);
256
+ }
240
257
 
241
258
  cmark_chunk url = cmark_chunk_dup(chunk, max_rewind - rewind,
242
259
  (bufsize_t)(link_end + rewind));
@@ -257,10 +274,10 @@ static cmark_node *match(cmark_syntax_extension *ext, cmark_parser *parser,
257
274
  return NULL;
258
275
 
259
276
  if (c == ':')
260
- return url_match(parser, parent, inline_parser);
277
+ return url_match(parser, parent, inline_parser, ext);
261
278
 
262
279
  if (c == 'w')
263
- return www_match(parser, parent, inline_parser);
280
+ return www_match(parser, parent, inline_parser, ext);
264
281
 
265
282
  return NULL;
266
283
 
@@ -269,7 +286,8 @@ static cmark_node *match(cmark_syntax_extension *ext, cmark_parser *parser,
269
286
  // inline was finished in inlines.c.
270
287
  }
271
288
 
272
- static void postprocess_text(cmark_parser *parser, cmark_node *text, int offset, int depth) {
289
+ static void postprocess_text(cmark_parser *parser, cmark_node *text, int offset,
290
+ int depth, cmark_syntax_extension *ext) {
273
291
  // postprocess_text can recurse very deeply if there is a very long line of
274
292
  // '@' only. Stop at a reasonable depth to ensure it cannot crash.
275
293
  if (depth > 1000) return;
@@ -311,7 +329,7 @@ static void postprocess_text(cmark_parser *parser, cmark_node *text, int offset,
311
329
  }
312
330
 
313
331
  if (rewind == 0 || ns > 0) {
314
- postprocess_text(parser, text, max_rewind + 1 + offset, depth + 1);
332
+ postprocess_text(parser, text, max_rewind + 1 + offset, depth + 1, ext);
315
333
  return;
316
334
  }
317
335
 
@@ -331,20 +349,23 @@ static void postprocess_text(cmark_parser *parser, cmark_node *text, int offset,
331
349
 
332
350
  if (link_end < 2 || nb != 1 || np == 0 ||
333
351
  (!cmark_isalpha(data[link_end - 1]) && data[link_end - 1] != '.')) {
334
- postprocess_text(parser, text, max_rewind + 1 + offset, depth + 1);
352
+ postprocess_text(parser, text, max_rewind + 1 + offset, depth + 1, ext);
335
353
  return;
336
354
  }
337
355
 
338
356
  link_end = autolink_delim(data, link_end);
339
357
 
340
358
  if (link_end == 0) {
341
- postprocess_text(parser, text, max_rewind + 1 + offset, depth + 1);
359
+ postprocess_text(parser, text, max_rewind + 1 + offset, depth + 1, ext);
342
360
  return;
343
361
  }
344
362
 
345
363
  cmark_chunk_to_cstr(parser->mem, &text->as.literal);
346
364
 
347
365
  cmark_node *link_node = cmark_node_new_with_mem(CMARK_NODE_LINK, parser->mem);
366
+ if (parser->options & CMARK_OPT_AUTOLINK_CLASS_NAME) {
367
+ cmark_node_set_syntax_extension(link_node, ext);
368
+ }
348
369
  cmark_strbuf buf;
349
370
  cmark_strbuf_init(parser->mem, &buf, 10);
350
371
  cmark_strbuf_puts(&buf, "mailto:");
@@ -373,7 +394,7 @@ static void postprocess_text(cmark_parser *parser, cmark_node *text, int offset,
373
394
  text->as.literal.len = offset + max_rewind - rewind;
374
395
  text->as.literal.data[text->as.literal.len] = 0;
375
396
 
376
- postprocess_text(parser, post, 0, depth + 1);
397
+ postprocess_text(parser, post, 0, depth + 1, ext);
377
398
  }
378
399
 
379
400
  static cmark_node *postprocess(cmark_syntax_extension *ext, cmark_parser *parser, cmark_node *root) {
@@ -400,7 +421,7 @@ static cmark_node *postprocess(cmark_syntax_extension *ext, cmark_parser *parser
400
421
  }
401
422
 
402
423
  if (ev == CMARK_EVENT_ENTER && node->type == CMARK_NODE_TEXT) {
403
- postprocess_text(parser, node, 0, /*depth*/0);
424
+ postprocess_text(parser, node, 0, /*depth*/ 0, ext);
404
425
  }
405
426
  }
406
427
 
@@ -409,12 +430,38 @@ static cmark_node *postprocess(cmark_syntax_extension *ext, cmark_parser *parser
409
430
  return root;
410
431
  }
411
432
 
433
+ static void html_render(cmark_syntax_extension *extension,
434
+ cmark_html_renderer *renderer, cmark_node *node,
435
+ cmark_event_type ev_type, int options) {
436
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
437
+ cmark_strbuf *html = renderer->html;
438
+
439
+ if (entering) {
440
+ cmark_strbuf_puts(html, "<a href=\"");
441
+ if ((options & CMARK_OPT_UNSAFE) ||
442
+ !(scan_dangerous_url(&node->as.link.url, 0))) {
443
+ houdini_escape_href(html, node->as.link.url.data, node->as.link.url.len);
444
+ }
445
+ if (node->as.link.title.len) {
446
+ cmark_strbuf_puts(html, "\" title=\"");
447
+ escape_html(html, node->as.link.title.data, node->as.link.title.len);
448
+ }
449
+ if (options & CMARK_OPT_AUTOLINK_CLASS_NAME) {
450
+ cmark_strbuf_puts(html, "\" class=\"autolink");
451
+ }
452
+ cmark_strbuf_puts(html, "\">");
453
+ } else {
454
+ cmark_strbuf_puts(html, "</a>");
455
+ }
456
+ }
457
+
412
458
  cmark_syntax_extension *create_autolink_extension(void) {
413
459
  cmark_syntax_extension *ext = cmark_syntax_extension_new("autolink");
414
460
  cmark_llist *special_chars = NULL;
415
461
 
416
462
  cmark_syntax_extension_set_match_inline_func(ext, match);
417
463
  cmark_syntax_extension_set_postprocess_func(ext, postprocess);
464
+ cmark_syntax_extension_set_html_render_func(ext, html_render);
418
465
 
419
466
  cmark_mem *mem = cmark_get_default_mem_allocator();
420
467
  special_chars = cmark_llist_append(mem, special_chars, (void *)':');
@@ -6,6 +6,7 @@
6
6
  #include "tasklist.h"
7
7
  #include "registry.h"
8
8
  #include "plugin.h"
9
+ #include "qfm_custom_block.h"
9
10
 
10
11
  static int core_extensions_registration(cmark_plugin *plugin) {
11
12
  cmark_plugin_register_syntax_extension(plugin, create_table_extension());
@@ -14,6 +15,7 @@ static int core_extensions_registration(cmark_plugin *plugin) {
14
15
  cmark_plugin_register_syntax_extension(plugin, create_autolink_extension());
15
16
  cmark_plugin_register_syntax_extension(plugin, create_tagfilter_extension());
16
17
  cmark_plugin_register_syntax_extension(plugin, create_tasklist_extension());
18
+ cmark_plugin_register_syntax_extension(plugin, create_qfm_custom_block_extension());
17
19
  return 1;
18
20
  }
19
21
 
@@ -10,6 +10,7 @@
10
10
  #include "syntax_extension.h"
11
11
  #include "html.h"
12
12
  #include "render.h"
13
+ #include "qfm.h"
13
14
 
14
15
  // Functions to convert cmark_nodes to HTML strings.
15
16
 
@@ -222,6 +223,18 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
222
223
  escape_html(html, node->as.code.info.data + first_tag + 1, node->as.code.info.len - first_tag - 1);
223
224
  }
224
225
  cmark_strbuf_puts(html, "\"><code>");
226
+ } else if (options & CMARK_OPT_CODE_DATA_METADATA) {
227
+ cmark_strbuf_puts(html, "<pre");
228
+ cmark_html_render_sourcepos(node, html, options);
229
+ cmark_strbuf_puts(html, "><code data-metadata=\"");
230
+ escape_html(html, node->as.code.info.data, node->as.code.info.len);
231
+ if (first_tag < node->as.code.info.len &&
232
+ (options & CMARK_OPT_FULL_INFO_STRING)) {
233
+ cmark_strbuf_puts(html, "\" data-meta=\"");
234
+ escape_html(html, node->as.code.info.data + first_tag + 1,
235
+ node->as.code.info.len - first_tag - 1);
236
+ }
237
+ cmark_strbuf_puts(html, "\">");
225
238
  } else {
226
239
  cmark_strbuf_puts(html, "<pre");
227
240
  cmark_html_render_sourcepos(node, html, options);
@@ -13,6 +13,8 @@
13
13
  #include "scanners.h"
14
14
  #include "inlines.h"
15
15
  #include "syntax_extension.h"
16
+ #include "qfm.h"
17
+ #include "qfm_mention_no_emphasis.h"
16
18
 
17
19
  static const char *EMDASH = "\xE2\x80\x94";
18
20
  static const char *ENDASH = "\xE2\x80\x93";
@@ -381,11 +383,10 @@ static cmark_node *handle_backticks(subject *subj, int options) {
381
383
  }
382
384
  }
383
385
 
384
-
385
386
  // Scan ***, **, or * and return number scanned, or 0.
386
387
  // Advances position.
387
388
  static int scan_delims(subject *subj, unsigned char c, bool *can_open,
388
- bool *can_close) {
389
+ bool *can_close, bool mention_no_emphasis) {
389
390
  int numdelims = 0;
390
391
  bufsize_t before_char_pos, after_char_pos;
391
392
  int32_t after_char = 0;
@@ -393,6 +394,15 @@ static int scan_delims(subject *subj, unsigned char c, bool *can_open,
393
394
  int len;
394
395
  bool left_flanking, right_flanking;
395
396
 
397
+ if (mention_no_emphasis &&
398
+ is_part_of_mention(subj->input.data + subj->pos, subj->pos)) {
399
+ numdelims++;
400
+ advance(subj);
401
+ *can_open = false;
402
+ *can_close = false;
403
+ return numdelims;
404
+ }
405
+
396
406
  if (subj->pos == 0) {
397
407
  before_char = 10;
398
408
  } else {
@@ -528,13 +538,14 @@ static void push_bracket(subject *subj, bool image, cmark_node *inl_text) {
528
538
  }
529
539
 
530
540
  // Assumes the subject has a c at the current position.
531
- static cmark_node *handle_delim(subject *subj, unsigned char c, bool smart) {
541
+ static cmark_node *handle_delim(subject *subj, unsigned char c, bool smart,
542
+ bool mention_no_emphasis) {
532
543
  bufsize_t numdelims;
533
544
  cmark_node *inl_text;
534
545
  bool can_open, can_close;
535
546
  cmark_chunk contents;
536
547
 
537
- numdelims = scan_delims(subj, c, &can_open, &can_close);
548
+ numdelims = scan_delims(subj, c, &can_open, &can_close, mention_no_emphasis);
538
549
 
539
550
  if (c == '\'' && smart) {
540
551
  contents = cmark_chunk_literal(RIGHTSINGLEQUOTE);
@@ -1387,7 +1398,8 @@ static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent,
1387
1398
  case '_':
1388
1399
  case '\'':
1389
1400
  case '"':
1390
- new_inl = handle_delim(subj, c, (options & CMARK_OPT_SMART) != 0);
1401
+ new_inl = handle_delim(subj, c, (options & CMARK_OPT_SMART) != 0,
1402
+ (options & CMARK_OPT_MENTION_NO_EMPHASIS) != 0);
1391
1403
  break;
1392
1404
  case '-':
1393
1405
  new_inl = handle_hyphen(subj, (options & CMARK_OPT_SMART) != 0);
@@ -0,0 +1,22 @@
1
+ #ifndef QFM_H
2
+ #define QFM_H
3
+
4
+ #ifdef __cplusplus
5
+ extern "C" {
6
+ #endif
7
+
8
+ /** Use <pre><code data-metadata="x"> tags for code blocks instead of <pre><code
9
+ * class="language-x">. **/
10
+ #define CMARK_OPT_CODE_DATA_METADATA (1 << 25)
11
+
12
+ /* Prevent parsing Qiita-style Mentions as emphasis. */
13
+ #define CMARK_OPT_MENTION_NO_EMPHASIS (1 << 26)
14
+
15
+ /* Render autolinks with class name */
16
+ #define CMARK_OPT_AUTOLINK_CLASS_NAME (1 << 27)
17
+
18
+ #ifdef __cplusplus
19
+ }
20
+ #endif
21
+
22
+ #endif
@@ -0,0 +1,265 @@
1
+ #include <html.h>
2
+ #include <parser.h>
3
+ #include <render.h>
4
+
5
+ #include "cmark-gfm-core-extensions.h"
6
+ #include "houdini.h"
7
+ #include "qfm_custom_block.h"
8
+ #include "qfm_scanners.h"
9
+ #include "strikethrough.h"
10
+
11
+ cmark_node_type CMARK_NODE_QFM_CUSTOM_BLOCK;
12
+
13
+ typedef struct {
14
+ cmark_chunk info;
15
+ bool opening;
16
+ cmark_strbuf *xml_attr_buff;
17
+ } node_qfm_custom_block;
18
+
19
+ static void escape_html(cmark_strbuf *dest, const unsigned char *source,
20
+ bufsize_t length) {
21
+ houdini_escape_html0(dest, source, length, 0);
22
+ }
23
+
24
+ static bool get_qfm_custom_block_opening(cmark_node *node) {
25
+ if (node == NULL) {
26
+ return false;
27
+ }
28
+
29
+ cmark_node_type node_type = cmark_node_get_type(node);
30
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
31
+ return ((node_qfm_custom_block *)node->as.opaque)->opening;
32
+ } else {
33
+ return false;
34
+ }
35
+ }
36
+
37
+ static bool set_qfm_custom_block_opening(cmark_node *node, bool opening) {
38
+ if (node == NULL) {
39
+ return false;
40
+ }
41
+
42
+ cmark_node_type node_type = cmark_node_get_type(node);
43
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
44
+ ((node_qfm_custom_block *)node->as.opaque)->opening = opening;
45
+ return true;
46
+ } else {
47
+ return false;
48
+ }
49
+ }
50
+
51
+ static cmark_chunk *get_qfm_custom_block_info(cmark_node *node) {
52
+ if (node == NULL) {
53
+ return NULL;
54
+ }
55
+
56
+ cmark_node_type node_type = cmark_node_get_type(node);
57
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
58
+ return &((node_qfm_custom_block *)node->as.opaque)->info;
59
+ } else {
60
+ return NULL;
61
+ }
62
+ }
63
+
64
+ static bool set_qfm_custom_block_info(cmark_node *node, const char *info) {
65
+ if (node == NULL) {
66
+ return false;
67
+ }
68
+
69
+ cmark_node_type node_type = cmark_node_get_type(node);
70
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
71
+ cmark_chunk_set_cstr(cmark_node_mem(node),
72
+ &((node_qfm_custom_block *)node->as.opaque)->info,
73
+ info);
74
+ return true;
75
+ } else {
76
+ return false;
77
+ }
78
+ }
79
+
80
+ static void free_node_qfm_custom_block(cmark_mem *mem, void *ptr) {
81
+ node_qfm_custom_block *cb = (node_qfm_custom_block *)ptr;
82
+
83
+ cmark_chunk_free(mem, &cb->info);
84
+ cmark_strbuf_free(cb->xml_attr_buff);
85
+ mem->free(cb);
86
+ }
87
+
88
+ static int matches(cmark_syntax_extension *self, cmark_parser *parser,
89
+ unsigned char *input, int len,
90
+ cmark_node *parent_container) {
91
+ int res = 0;
92
+
93
+ if (get_qfm_custom_block_opening(parent_container)) {
94
+ bufsize_t matched = scan_close_qfm_custom_block_fence(
95
+ input, len, cmark_parser_get_first_nonspace(parser));
96
+
97
+ if (matched > 0) {
98
+ set_qfm_custom_block_opening(parent_container, false);
99
+ cmark_parser_advance_offset(parser, (char *)input,
100
+ len - cmark_parser_get_offset(parser), 0);
101
+ } else {
102
+ res = 1;
103
+ }
104
+ }
105
+
106
+ return res;
107
+ }
108
+
109
+ static cmark_node *try_opening_qfm_custom_block_block(
110
+ cmark_syntax_extension *self, int indented, cmark_parser *parser,
111
+ cmark_node *parent_container, unsigned char *input, int len) {
112
+ cmark_node_type parent_type = cmark_node_get_type(parent_container);
113
+
114
+ if (!indented && parent_type != CMARK_NODE_QFM_CUSTOM_BLOCK) {
115
+ bufsize_t matched = scan_open_qfm_custom_block_fence(
116
+ input, len, cmark_parser_get_first_nonspace(parser));
117
+ if (!matched) {
118
+ return NULL;
119
+ }
120
+
121
+ cmark_node *custom_block_node = cmark_parser_add_child(
122
+ parser, parent_container, CMARK_NODE_QFM_CUSTOM_BLOCK,
123
+ parser->first_nonspace_column);
124
+ custom_block_node->as.opaque = (node_qfm_custom_block *)parser->mem->calloc(
125
+ 1, sizeof(node_qfm_custom_block));
126
+
127
+ cmark_strbuf *info = parser->mem->calloc(1, sizeof(cmark_strbuf));
128
+ cmark_strbuf_init(parser->mem, info, len - matched);
129
+ cmark_strbuf_put(info, input + matched, len - matched);
130
+ cmark_strbuf_trim(info);
131
+
132
+ set_qfm_custom_block_opening(custom_block_node, true);
133
+ set_qfm_custom_block_info(custom_block_node, (char *)info->ptr);
134
+
135
+ cmark_node_set_syntax_extension(custom_block_node, self);
136
+ cmark_parser_advance_offset(parser, (char *)input,
137
+ len - cmark_parser_get_offset(parser), 0);
138
+
139
+ return custom_block_node;
140
+ }
141
+
142
+ return NULL;
143
+ }
144
+
145
+ static const char *get_type_string(cmark_syntax_extension *self,
146
+ cmark_node *node) {
147
+ cmark_node_type node_type = cmark_node_get_type(node);
148
+
149
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
150
+ return "qfm_custom_block";
151
+ }
152
+
153
+ return "<unknown>";
154
+ }
155
+
156
+ static int can_contain(cmark_syntax_extension *self, cmark_node *node,
157
+ cmark_node_type child_type) {
158
+ cmark_node_type node_type = cmark_node_get_type(node);
159
+
160
+ return node_type == CMARK_NODE_QFM_CUSTOM_BLOCK;
161
+ }
162
+
163
+ static int contains_inlines(cmark_syntax_extension *self, cmark_node *node) {
164
+ cmark_node_type node_type = cmark_node_get_type(node);
165
+
166
+ return node_type == CMARK_NODE_QFM_CUSTOM_BLOCK;
167
+ }
168
+
169
+ static void plaintext_render(cmark_syntax_extension *self,
170
+ cmark_renderer *renderer, cmark_node *node,
171
+ cmark_event_type ev_type, int options) {
172
+ cmark_node_type node_type = cmark_node_get_type(node);
173
+
174
+ if (node_type != CMARK_NODE_QFM_CUSTOM_BLOCK) {
175
+ assert(false);
176
+ }
177
+ }
178
+
179
+ static void html_render(cmark_syntax_extension *self,
180
+ cmark_html_renderer *renderer, cmark_node *node,
181
+ cmark_event_type ev_type, int options) {
182
+ cmark_node_type node_type = cmark_node_get_type(node);
183
+
184
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
185
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
186
+ cmark_strbuf *html = renderer->html;
187
+
188
+ if (entering) {
189
+ cmark_html_render_cr(html);
190
+ cmark_strbuf_puts(html,
191
+ "<div data-type=\"customblock\" data-metadata=\"");
192
+ cmark_chunk *info = get_qfm_custom_block_info(node);
193
+ escape_html(html, info->data, info->len);
194
+ cmark_strbuf_putc(html, '"');
195
+ cmark_html_render_sourcepos(node, html, options);
196
+ cmark_strbuf_putc(html, '>');
197
+ } else {
198
+ cmark_html_render_cr(html);
199
+ cmark_strbuf_puts(html, "</div>");
200
+ cmark_html_render_cr(html);
201
+ }
202
+ } else {
203
+ assert(false);
204
+ }
205
+ }
206
+
207
+ static const char *xml_attr(cmark_syntax_extension *self, cmark_node *node) {
208
+ cmark_node_type node_type = cmark_node_get_type(node);
209
+
210
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
211
+ cmark_chunk *info = get_qfm_custom_block_info(node);
212
+ cmark_mem *mem = node->content.mem;
213
+
214
+ cmark_strbuf *xml_attr_buff = mem->calloc(1, sizeof(cmark_strbuf));
215
+ ((node_qfm_custom_block *)node->as.opaque)->xml_attr_buff = xml_attr_buff;
216
+ cmark_strbuf_init(
217
+ mem, xml_attr_buff,
218
+ 17 + info->len); // `17` is length of ` data-metadata="` and `"`.
219
+ cmark_strbuf_puts(xml_attr_buff, " data-metadata=\"");
220
+ cmark_strbuf_puts(xml_attr_buff, (char *)info->data);
221
+ cmark_strbuf_putc(xml_attr_buff, '"');
222
+
223
+ return (char *)xml_attr_buff->ptr;
224
+ }
225
+
226
+ return NULL;
227
+ }
228
+
229
+ static void opaque_alloc(cmark_syntax_extension *self, cmark_mem *mem,
230
+ cmark_node *node) {
231
+ cmark_node_type node_type = cmark_node_get_type(node);
232
+
233
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
234
+ node->as.opaque = mem->calloc(1, sizeof(node_qfm_custom_block));
235
+ }
236
+ }
237
+
238
+ static void opaque_free(cmark_syntax_extension *self, cmark_mem *mem,
239
+ cmark_node *node) {
240
+ cmark_node_type node_type = cmark_node_get_type(node);
241
+
242
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
243
+ free_node_qfm_custom_block(mem, node->as.opaque);
244
+ }
245
+ }
246
+
247
+ cmark_syntax_extension *create_qfm_custom_block_extension(void) {
248
+ cmark_syntax_extension *self = cmark_syntax_extension_new("custom_block");
249
+
250
+ cmark_syntax_extension_set_match_block_func(self, matches);
251
+ cmark_syntax_extension_set_open_block_func(
252
+ self, try_opening_qfm_custom_block_block);
253
+ cmark_syntax_extension_set_get_type_string_func(self, get_type_string);
254
+ cmark_syntax_extension_set_can_contain_func(self, can_contain);
255
+ cmark_syntax_extension_set_contains_inlines_func(self, contains_inlines);
256
+ cmark_syntax_extension_set_commonmark_render_func(self, plaintext_render);
257
+ cmark_syntax_extension_set_plaintext_render_func(self, plaintext_render);
258
+ cmark_syntax_extension_set_xml_attr_func(self, xml_attr);
259
+ cmark_syntax_extension_set_html_render_func(self, html_render);
260
+ cmark_syntax_extension_set_opaque_alloc_func(self, opaque_alloc);
261
+ cmark_syntax_extension_set_opaque_free_func(self, opaque_free);
262
+ CMARK_NODE_QFM_CUSTOM_BLOCK = cmark_syntax_extension_add_node(0);
263
+
264
+ return self;
265
+ }
@@ -0,0 +1,10 @@
1
+ #ifndef QFM_CUSTOM_BLOCK_H
2
+ #define QFM_CUSTOM_BLOCK_H
3
+
4
+ #include "cmark-gfm-core-extensions.h"
5
+
6
+ extern cmark_node_type CMARK_NODE_QFM_CUSTOM_BLOCK;
7
+
8
+ cmark_syntax_extension *create_qfm_custom_block_extension(void);
9
+
10
+ #endif