qiita_marker 0.23.2.0 → 0.23.2.3

Sign up to get free protection for your applications and to get access to all the features.
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