qiita_marker 0.23.2.0 → 0.23.2.1

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: '0579157ad4abb4038246ef4dc4752fd1de6cd281daa619b2ef42adec5b147982'
4
+ data.tar.gz: c671815ff61ab460fcf6c3a42a614018a250c62ceaa41ee0aa3e899f34c12b16
5
5
  SHA512:
6
- metadata.gz: c74fe17ddee587a42758795f9a9ff9bd5982b7c284a247615af864e6fdad8dd1b6708b55eadbec2b9eaf893e3d9eeb5e03c72d62da78598322b90d01adc8cc84
7
- data.tar.gz: f8180555767d3aa6134f7dae895d06fdda9f4b655d41d6872b6234dbe073787debd4fd0b6956da261f149b28d821d95c6e0b947e7049307a92267205e24a5a9c
6
+ metadata.gz: 32f16ce193b82d6a7214c851fdd81126d42d872fdd4508b798ccfc4faba4668793274f88f9ef25e61ae57545041d1062c65169b05cd756eec55df0aa042d0140
7
+ data.tar.gz: da050452749a3eee8d998db24aa45d6a8b1a282672283e427f8deca374405bfaebe572e954c7906ceb84bea2ff08ad737da05707b2ed5d378158d2738ac0e318
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright 2021 Increments Inc.
3
+ Copyright 2021 Qiita Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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,27 @@ 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
+
24
+ #### Render options
25
+
26
+ | Name | Description |
27
+ | --- | --- |
28
+ | `:CODE_DATA_METADATA` | Use `<code data-metadata>` for fenced code blocks. |
29
+ | `:MENTION_NO_EMPHASIS` | Prevent parsing mentions as emphasis. |
30
+
31
+ ### Original extensions
32
+
33
+ - `:custom_block` - This provides support for customizable blocks.
34
+
14
35
  ## Contributing
15
36
 
16
37
  If you have suggestion or modification to this repository, please create an Issue or Pull Request.
@@ -45,6 +66,11 @@ $ docker compose run --rm app ./script/bootstrap
45
66
  $ docker compose run --rm rake test
46
67
  ```
47
68
 
69
+ ### Versioning policy
70
+
71
+ Qiita Marker follows CommonMarker's updates by merging the upstream changes.
72
+ 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.
73
+
48
74
  ## License
49
75
 
50
76
  Please see [LICENSE.txt](/LICENSE.txt).
@@ -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, first_tag);
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,19 @@
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
+ #ifdef __cplusplus
16
+ }
17
+ #endif
18
+
19
+ #endif
@@ -0,0 +1,278 @@
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
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
161
+ return
162
+ // Block
163
+ child_type == CMARK_NODE_LIST || child_type == CMARK_NODE_PARAGRAPH ||
164
+ // Inline
165
+ child_type == CMARK_NODE_TEXT || child_type == CMARK_NODE_CODE ||
166
+ child_type == CMARK_NODE_HTML_INLINE || child_type == CMARK_NODE_EMPH ||
167
+ child_type == CMARK_NODE_STRONG || child_type == CMARK_NODE_LINK ||
168
+ child_type == CMARK_NODE_IMAGE ||
169
+ child_type == CMARK_NODE_FOOTNOTE_REFERENCE ||
170
+ child_type == CMARK_NODE_STRIKETHROUGH;
171
+ }
172
+
173
+ return 0;
174
+ }
175
+
176
+ static int contains_inlines(cmark_syntax_extension *self, cmark_node *node) {
177
+ cmark_node_type node_type = cmark_node_get_type(node);
178
+
179
+ return node_type == CMARK_NODE_QFM_CUSTOM_BLOCK;
180
+ }
181
+
182
+ static void plaintext_render(cmark_syntax_extension *self,
183
+ cmark_renderer *renderer, cmark_node *node,
184
+ cmark_event_type ev_type, int options) {
185
+ cmark_node_type node_type = cmark_node_get_type(node);
186
+
187
+ if (node_type != CMARK_NODE_QFM_CUSTOM_BLOCK) {
188
+ assert(false);
189
+ }
190
+ }
191
+
192
+ static void html_render(cmark_syntax_extension *self,
193
+ cmark_html_renderer *renderer, cmark_node *node,
194
+ cmark_event_type ev_type, int options) {
195
+ cmark_node_type node_type = cmark_node_get_type(node);
196
+
197
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
198
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
199
+ cmark_strbuf *html = renderer->html;
200
+
201
+ if (entering) {
202
+ cmark_html_render_cr(html);
203
+ cmark_strbuf_puts(html,
204
+ "<div data-type=\"customblock\" data-metadata=\"");
205
+ cmark_chunk *info = get_qfm_custom_block_info(node);
206
+ escape_html(html, info->data, info->len);
207
+ cmark_strbuf_putc(html, '"');
208
+ cmark_html_render_sourcepos(node, html, options);
209
+ cmark_strbuf_putc(html, '>');
210
+ } else {
211
+ cmark_html_render_cr(html);
212
+ cmark_strbuf_puts(html, "</div>");
213
+ cmark_html_render_cr(html);
214
+ }
215
+ } else {
216
+ assert(false);
217
+ }
218
+ }
219
+
220
+ static const char *xml_attr(cmark_syntax_extension *self, cmark_node *node) {
221
+ cmark_node_type node_type = cmark_node_get_type(node);
222
+
223
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
224
+ cmark_chunk *info = get_qfm_custom_block_info(node);
225
+ cmark_mem *mem = node->content.mem;
226
+
227
+ cmark_strbuf *xml_attr_buff = mem->calloc(1, sizeof(cmark_strbuf));
228
+ ((node_qfm_custom_block *)node->as.opaque)->xml_attr_buff = xml_attr_buff;
229
+ cmark_strbuf_init(
230
+ mem, xml_attr_buff,
231
+ 17 + info->len); // `17` is length of ` data-metadata="` and `"`.
232
+ cmark_strbuf_puts(xml_attr_buff, " data-metadata=\"");
233
+ cmark_strbuf_puts(xml_attr_buff, (char *)info->data);
234
+ cmark_strbuf_putc(xml_attr_buff, '"');
235
+
236
+ return (char *)xml_attr_buff->ptr;
237
+ }
238
+
239
+ return NULL;
240
+ }
241
+
242
+ static void opaque_alloc(cmark_syntax_extension *self, cmark_mem *mem,
243
+ cmark_node *node) {
244
+ cmark_node_type node_type = cmark_node_get_type(node);
245
+
246
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
247
+ node->as.opaque = mem->calloc(1, sizeof(node_qfm_custom_block));
248
+ }
249
+ }
250
+
251
+ static void opaque_free(cmark_syntax_extension *self, cmark_mem *mem,
252
+ cmark_node *node) {
253
+ cmark_node_type node_type = cmark_node_get_type(node);
254
+
255
+ if (node_type == CMARK_NODE_QFM_CUSTOM_BLOCK) {
256
+ free_node_qfm_custom_block(mem, node->as.opaque);
257
+ }
258
+ }
259
+
260
+ cmark_syntax_extension *create_qfm_custom_block_extension(void) {
261
+ cmark_syntax_extension *self = cmark_syntax_extension_new("custom_block");
262
+
263
+ cmark_syntax_extension_set_match_block_func(self, matches);
264
+ cmark_syntax_extension_set_open_block_func(
265
+ self, try_opening_qfm_custom_block_block);
266
+ cmark_syntax_extension_set_get_type_string_func(self, get_type_string);
267
+ cmark_syntax_extension_set_can_contain_func(self, can_contain);
268
+ cmark_syntax_extension_set_contains_inlines_func(self, contains_inlines);
269
+ cmark_syntax_extension_set_commonmark_render_func(self, plaintext_render);
270
+ cmark_syntax_extension_set_plaintext_render_func(self, plaintext_render);
271
+ cmark_syntax_extension_set_xml_attr_func(self, xml_attr);
272
+ cmark_syntax_extension_set_html_render_func(self, html_render);
273
+ cmark_syntax_extension_set_opaque_alloc_func(self, opaque_alloc);
274
+ cmark_syntax_extension_set_opaque_free_func(self, opaque_free);
275
+ CMARK_NODE_QFM_CUSTOM_BLOCK = cmark_syntax_extension_add_node(0);
276
+
277
+ return self;
278
+ }
@@ -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
@@ -0,0 +1,37 @@
1
+ #include "cmark_ctype.h"
2
+ #include "cmark-gfm.h"
3
+ #include "config.h"
4
+
5
+ static bool is_wordchar(char c) {
6
+ return cmark_isalnum(c) || c == '_' || c == '-';
7
+ }
8
+
9
+ bool is_part_of_mention(unsigned char *data, bufsize_t offset) {
10
+ int i;
11
+ int lookbehind_limit = (int)-offset;
12
+ char character;
13
+
14
+ for (i = 0; i >= lookbehind_limit; i--) {
15
+ character = data[i];
16
+
17
+ if (is_wordchar(character)) {
18
+ // Continue lookbehind.
19
+ } else if (character == '@') {
20
+ if (i == offset) {
21
+ // The "@" is at beginning of the text. (e.g. "@foo")
22
+ return true;
23
+ } else {
24
+ // Check if the previous character of the "@" is alphanumeric or not.
25
+ // " @foo" and "あ@foo" are mentions.
26
+ // "a@foo" is not a mention.
27
+ char prev_character = data[i - 1];
28
+ return !cmark_isalnum(prev_character);
29
+ }
30
+ } else {
31
+ // Found non-mention character, so this is not a mention.
32
+ return false;
33
+ }
34
+ }
35
+
36
+ return false;
37
+ }
@@ -0,0 +1,8 @@
1
+ #ifndef QFM_MENTION_NO_EMPHASIS
2
+ #define QFM_MENTION_NO_EMPHASIS
3
+
4
+ #include "cmark-gfm.h"
5
+
6
+ bool is_part_of_mention(unsigned char *data, bufsize_t offset);
7
+
8
+ #endif
@@ -0,0 +1,291 @@
1
+ /* Generated by re2c 1.3 */
2
+
3
+ #include "qfm_scanners.h"
4
+ #include <stdlib.h>
5
+
6
+ bufsize_t _qfm_scan_at(bufsize_t (*scanner)(const unsigned char *),
7
+ unsigned char *ptr, int len, bufsize_t offset) {
8
+ bufsize_t res;
9
+
10
+ if (ptr == NULL || offset >= len) {
11
+ return 0;
12
+ } else {
13
+ unsigned char lim = ptr[len];
14
+
15
+ ptr[len] = '\0';
16
+ res = scanner(ptr + offset);
17
+ ptr[len] = lim;
18
+ }
19
+
20
+ return res;
21
+ }
22
+
23
+ // Scan an opening qfm_custom_block fence.
24
+ bufsize_t _scan_open_qfm_custom_block_fence(const unsigned char *p) {
25
+ const unsigned char *marker = NULL;
26
+ const unsigned char *start = p;
27
+
28
+ {
29
+ unsigned char yych;
30
+ static const unsigned char yybm[] = {
31
+ 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0, 128, 128, 0,
32
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
33
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
34
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
35
+ 128, 128, 64, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
36
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
37
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
38
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
39
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
40
+ 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
41
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
42
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
43
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
44
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
46
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
47
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
48
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
49
+ 0, 0, 0, 0,
50
+ };
51
+ yych = *p;
52
+ if (yych == ':')
53
+ goto yy4;
54
+ ++p;
55
+ yy3 : { return 0; }
56
+ yy4:
57
+ yych = *(marker = ++p);
58
+ if (yych != ':')
59
+ goto yy3;
60
+ yych = *++p;
61
+ if (yybm[0 + yych] & 64) {
62
+ goto yy7;
63
+ }
64
+ yy6:
65
+ p = marker;
66
+ goto yy3;
67
+ yy7:
68
+ yych = *++p;
69
+ if (yybm[0 + yych] & 64) {
70
+ goto yy7;
71
+ }
72
+ if (yych <= 0xDF) {
73
+ if (yych <= '\f') {
74
+ if (yych <= 0x00)
75
+ goto yy6;
76
+ if (yych == '\n') {
77
+ marker = p;
78
+ goto yy11;
79
+ }
80
+ marker = p;
81
+ } else {
82
+ if (yych <= '\r') {
83
+ marker = p;
84
+ goto yy11;
85
+ }
86
+ if (yych <= 0x7F) {
87
+ marker = p;
88
+ goto yy9;
89
+ }
90
+ if (yych <= 0xC1)
91
+ goto yy6;
92
+ marker = p;
93
+ goto yy13;
94
+ }
95
+ } else {
96
+ if (yych <= 0xEF) {
97
+ if (yych <= 0xE0) {
98
+ marker = p;
99
+ goto yy14;
100
+ }
101
+ if (yych == 0xED) {
102
+ marker = p;
103
+ goto yy16;
104
+ }
105
+ marker = p;
106
+ goto yy15;
107
+ } else {
108
+ if (yych <= 0xF0) {
109
+ marker = p;
110
+ goto yy17;
111
+ }
112
+ if (yych <= 0xF3) {
113
+ marker = p;
114
+ goto yy18;
115
+ }
116
+ if (yych <= 0xF4) {
117
+ marker = p;
118
+ goto yy19;
119
+ }
120
+ goto yy6;
121
+ }
122
+ }
123
+ yy9:
124
+ yych = *++p;
125
+ if (yybm[0 + yych] & 128) {
126
+ goto yy9;
127
+ }
128
+ if (yych <= 0xEC) {
129
+ if (yych <= 0xC1) {
130
+ if (yych <= 0x00)
131
+ goto yy6;
132
+ if (yych >= 0x0E)
133
+ goto yy6;
134
+ } else {
135
+ if (yych <= 0xDF)
136
+ goto yy13;
137
+ if (yych <= 0xE0)
138
+ goto yy14;
139
+ goto yy15;
140
+ }
141
+ } else {
142
+ if (yych <= 0xF0) {
143
+ if (yych <= 0xED)
144
+ goto yy16;
145
+ if (yych <= 0xEF)
146
+ goto yy15;
147
+ goto yy17;
148
+ } else {
149
+ if (yych <= 0xF3)
150
+ goto yy18;
151
+ if (yych <= 0xF4)
152
+ goto yy19;
153
+ goto yy6;
154
+ }
155
+ }
156
+ yy11:
157
+ ++p;
158
+ p = marker;
159
+ { return (bufsize_t)(p - start); }
160
+ yy13:
161
+ yych = *++p;
162
+ if (yych <= 0x7F)
163
+ goto yy6;
164
+ if (yych <= 0xBF)
165
+ goto yy9;
166
+ goto yy6;
167
+ yy14:
168
+ yych = *++p;
169
+ if (yych <= 0x9F)
170
+ goto yy6;
171
+ if (yych <= 0xBF)
172
+ goto yy13;
173
+ goto yy6;
174
+ yy15:
175
+ yych = *++p;
176
+ if (yych <= 0x7F)
177
+ goto yy6;
178
+ if (yych <= 0xBF)
179
+ goto yy13;
180
+ goto yy6;
181
+ yy16:
182
+ yych = *++p;
183
+ if (yych <= 0x7F)
184
+ goto yy6;
185
+ if (yych <= 0x9F)
186
+ goto yy13;
187
+ goto yy6;
188
+ yy17:
189
+ yych = *++p;
190
+ if (yych <= 0x8F)
191
+ goto yy6;
192
+ if (yych <= 0xBF)
193
+ goto yy15;
194
+ goto yy6;
195
+ yy18:
196
+ yych = *++p;
197
+ if (yych <= 0x7F)
198
+ goto yy6;
199
+ if (yych <= 0xBF)
200
+ goto yy15;
201
+ goto yy6;
202
+ yy19:
203
+ yych = *++p;
204
+ if (yych <= 0x7F)
205
+ goto yy6;
206
+ if (yych <= 0x8F)
207
+ goto yy15;
208
+ goto yy6;
209
+ }
210
+ }
211
+
212
+ // Scan a closing qfm_custom_block fence with length at least len.
213
+ bufsize_t _scan_close_qfm_custom_block_fence(const unsigned char *p) {
214
+ const unsigned char *marker = NULL;
215
+ const unsigned char *start = p;
216
+
217
+ {
218
+ unsigned char yych;
219
+ static const unsigned char yybm[] = {
220
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
221
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
222
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0,
223
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
224
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
225
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
226
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
227
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
228
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
229
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
230
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
231
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
232
+ };
233
+ yych = *p;
234
+ if (yych == ':')
235
+ goto yy24;
236
+ ++p;
237
+ yy23 : { return 0; }
238
+ yy24:
239
+ yych = *(marker = ++p);
240
+ if (yych != ':')
241
+ goto yy23;
242
+ yych = *++p;
243
+ if (yybm[0 + yych] & 64) {
244
+ goto yy27;
245
+ }
246
+ yy26:
247
+ p = marker;
248
+ goto yy23;
249
+ yy27:
250
+ yych = *++p;
251
+ if (yybm[0 + yych] & 64) {
252
+ goto yy27;
253
+ }
254
+ if (yych <= '\f') {
255
+ if (yych <= 0x08)
256
+ goto yy26;
257
+ if (yych <= '\t') {
258
+ marker = p;
259
+ goto yy29;
260
+ }
261
+ if (yych <= '\n') {
262
+ marker = p;
263
+ goto yy31;
264
+ }
265
+ goto yy26;
266
+ } else {
267
+ if (yych <= '\r') {
268
+ marker = p;
269
+ goto yy31;
270
+ }
271
+ if (yych != ' ')
272
+ goto yy26;
273
+ marker = p;
274
+ }
275
+ yy29:
276
+ yych = *++p;
277
+ if (yybm[0 + yych] & 128) {
278
+ goto yy29;
279
+ }
280
+ if (yych <= 0x08)
281
+ goto yy26;
282
+ if (yych <= '\n')
283
+ goto yy31;
284
+ if (yych != '\r')
285
+ goto yy26;
286
+ yy31:
287
+ ++p;
288
+ p = marker;
289
+ { return (bufsize_t)(p - start); }
290
+ }
291
+ }
@@ -0,0 +1,20 @@
1
+ #include "chunk.h"
2
+ #include "cmark-gfm.h"
3
+
4
+ #ifdef __cplusplus
5
+ extern "C" {
6
+ #endif
7
+
8
+ bufsize_t _qfm_scan_at(bufsize_t (*scanner)(const unsigned char *),
9
+ unsigned char *ptr, int len, bufsize_t offset);
10
+ bufsize_t _scan_open_qfm_custom_block_fence(const unsigned char *p);
11
+ bufsize_t _scan_close_qfm_custom_block_fence(const unsigned char *p);
12
+
13
+ #define scan_open_qfm_custom_block_fence(c, l, n) \
14
+ _qfm_scan_at(&_scan_open_qfm_custom_block_fence, c, l, n)
15
+ #define scan_close_qfm_custom_block_fence(c, l, n) \
16
+ _qfm_scan_at(&_scan_close_qfm_custom_block_fence, c, l, n)
17
+
18
+ #ifdef __cplusplus
19
+ }
20
+ #endif
@@ -0,0 +1,50 @@
1
+ /*!re2c re2c:flags:no-debug-info = 1; */
2
+ /*!re2c re2c:indent:string = ' '; */
3
+
4
+ #include "qfm_scanners.h"
5
+ #include <stdlib.h>
6
+
7
+ bufsize_t _qfm_scan_at(bufsize_t (*scanner)(const unsigned char *),
8
+ unsigned char *ptr, int len, bufsize_t offset) {
9
+ bufsize_t res;
10
+
11
+ if (ptr == NULL || offset >= len) {
12
+ return 0;
13
+ } else {
14
+ unsigned char lim = ptr[len];
15
+
16
+ ptr[len] = '\0';
17
+ res = scanner(ptr + offset);
18
+ ptr[len] = lim;
19
+ }
20
+
21
+ return res;
22
+ }
23
+
24
+ /*!re2c
25
+ re2c:define:YYCTYPE = "unsigned char";
26
+ re2c:define:YYCURSOR = p;
27
+ re2c:define:YYMARKER = marker;
28
+ re2c:define:YYCTXMARKER = marker;
29
+ re2c:yyfill:enable = 0;
30
+ */
31
+
32
+ // Scan an opening qfm_custom_block fence.
33
+ bufsize_t _scan_open_qfm_custom_block_fence(const unsigned char *p) {
34
+ const unsigned char *marker = NULL;
35
+ const unsigned char *start = p;
36
+ /*!re2c
37
+ [:]{3,} / [^:\r\n\x00]*[\r\n] { return (bufsize_t)(p - start); }
38
+ * { return 0; }
39
+ */
40
+ }
41
+
42
+ // Scan a closing qfm_custom_block fence with length at least len.
43
+ bufsize_t _scan_close_qfm_custom_block_fence(const unsigned char *p) {
44
+ const unsigned char *marker = NULL;
45
+ const unsigned char *start = p;
46
+ /*!re2c
47
+ [:]{3,} / [ \t]*[\r\n] { return (bufsize_t)(p - start); }
48
+ * { return 0; }
49
+ */
50
+ }
@@ -13,7 +13,8 @@ module QiitaMarker
13
13
  SMART: (1 << 10),
14
14
  LIBERAL_HTML_TAG: (1 << 12),
15
15
  FOOTNOTES: (1 << 13),
16
- STRIKETHROUGH_DOUBLE_TILDE: (1 << 14)
16
+ STRIKETHROUGH_DOUBLE_TILDE: (1 << 14),
17
+ MENTION_NO_EMPHASIS: (1 << 26)
17
18
  }.freeze,
18
19
  render: {
19
20
  DEFAULT: 0,
@@ -28,7 +29,9 @@ module QiitaMarker
28
29
  FOOTNOTES: (1 << 13),
29
30
  STRIKETHROUGH_DOUBLE_TILDE: (1 << 14),
30
31
  TABLE_PREFER_STYLE_ATTRIBUTES: (1 << 15),
31
- FULL_INFO_STRING: (1 << 16)
32
+ FULL_INFO_STRING: (1 << 16),
33
+ CODE_DATA_METADATA: (1 << 25),
34
+ MENTION_NO_EMPHASIS: (1 << 26)
32
35
  }.freeze,
33
36
  format: %i[html xml commonmark plaintext].freeze
34
37
  }.freeze
@@ -94,6 +94,10 @@ module QiitaMarker
94
94
  out("<pre#{sourcepos(node)}")
95
95
  out(' lang="', node.fence_info.split(/\s+/)[0], '"') if node.fence_info && !node.fence_info.empty?
96
96
  out('><code>')
97
+ elsif option_enabled?(:CODE_DATA_METADATA)
98
+ out("<pre#{sourcepos(node)}><code")
99
+ out(' data-metadata="', node.fence_info.split(/\s+/)[0], '"') if node.fence_info && !node.fence_info.empty?
100
+ out('>')
97
101
  else
98
102
  out("<pre#{sourcepos(node)}><code")
99
103
  if node.fence_info && !node.fence_info.empty?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QiitaMarker
4
- VERSION = '0.23.2.0'
4
+ VERSION = '0.23.2.1'
5
5
  end
data/qiita_marker.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.version = QiitaMarker::VERSION
10
10
  s.summary = 'Qiita Marker is a Ruby library for Markdown processing, based on CommonMarker.'
11
11
  s.description = 'A Ruby library that is the core module of the Qiita-specified markdown processor.'
12
- s.authors = ['Increments Inc.']
12
+ s.authors = ['Qiita Inc.']
13
13
  s.homepage = 'https://github.com/increments/qiita_marker'
14
14
  s.license = 'MIT'
15
15
 
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require('test_helper')
4
+
5
+ class TestQfmCodeDataMetadata < Minitest::Test
6
+ def setup
7
+ text = <<~MD
8
+ ```ruby:example.rb
9
+ puts :foo
10
+ ```
11
+ MD
12
+ @doc = QiitaMarker.render_doc(text, :DEFAULT, [])
13
+ @expected = <<~HTML
14
+ <pre><code data-metadata="ruby:example.rb">puts :foo
15
+ </code></pre>
16
+ HTML
17
+ end
18
+
19
+ def test_to_html
20
+ assert_equal(@expected, @doc.to_html(:CODE_DATA_METADATA))
21
+ end
22
+
23
+ def test_html_renderer
24
+ assert_equal(@expected, QiitaMarker::HtmlRenderer.new(options: :CODE_DATA_METADATA).render(@doc))
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require('test_helper')
4
+
5
+ class TestQfmCustomBlock < Minitest::Test
6
+ def setup
7
+ text = <<~MD
8
+ :::foo bar
9
+ message
10
+ :::
11
+ MD
12
+ @doc = QiitaMarker.render_doc(text, :DEFAULT, %i[custom_block])
13
+ @expected = <<~HTML
14
+ <div data-type="customblock" data-metadata="foo bar">
15
+ <p>message</p>
16
+ </div>
17
+ HTML
18
+ end
19
+
20
+ def test_to_html
21
+ assert_equal(@expected, @doc.to_html(:DEFAULT, %i[custom_block]))
22
+ end
23
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class TestQfmMentionNoEmphasis < Minitest::Test
6
+ describe 'with mention_no_emphasis option' do
7
+ [
8
+ ['@_username_', false],
9
+ ['@__username__', false],
10
+ ['@___username___', false],
11
+ ['@user__name__', false],
12
+ ['@some__user__name__', false],
13
+ [' @_username_', false],
14
+ ['あ@_username_', false],
15
+ ['A@_username_', true],
16
+ ['@*username*', true],
17
+ ['_foo_', true],
18
+ ['_', false],
19
+ ['_foo @username_', false],
20
+ ['__foo @username__', false],
21
+ ['___foo @username___', false]
22
+ ].each do |text, emphasize|
23
+ describe "with text #{text.inspect}" do
24
+ if emphasize
25
+ it 'emphasizes the text' do
26
+ QiitaMarker.render_html(text, :MENTION_NO_EMPHASIS).tap do |out|
27
+ assert_match(/(<em>|<strong>)/, out.chomp)
28
+ end
29
+ end
30
+ else
31
+ it 'does not emphasize the text' do
32
+ QiitaMarker.render_html(text, :MENTION_NO_EMPHASIS).tap do |out|
33
+ assert_match "<p>#{text.strip}</p>", out.chomp
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ describe 'without mention_no_emphasis option' do
42
+ describe 'with text "@_username_"' do
43
+ text = '@_username_'
44
+ it 'emphasizes the text' do
45
+ QiitaMarker.render_html(text, :DEFAULT, %i[]).tap do |out|
46
+ assert_match(/<em>/, out.chomp)
47
+ end
48
+ end
49
+ end
50
+
51
+ describe 'with text "_foo @username_"' do
52
+ text = '_foo @username_'
53
+ it 'emphasizes the text' do
54
+ QiitaMarker.render_html(text, :DEFAULT, %i[]).tap do |out|
55
+ assert_match(/<em>/, out.chomp)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qiita_marker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.23.2.0
4
+ version: 0.23.2.1
5
5
  platform: ruby
6
6
  authors:
7
- - Increments Inc.
7
+ - Qiita Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-26 00:00:00.000000000 Z
11
+ date: 2021-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: awesome_print
@@ -196,6 +196,14 @@ files:
196
196
  - ext/qiita_marker/plaintext.c
197
197
  - ext/qiita_marker/plugin.c
198
198
  - ext/qiita_marker/plugin.h
199
+ - ext/qiita_marker/qfm.h
200
+ - ext/qiita_marker/qfm_custom_block.c
201
+ - ext/qiita_marker/qfm_custom_block.h
202
+ - ext/qiita_marker/qfm_mention_no_emphasis.c
203
+ - ext/qiita_marker/qfm_mention_no_emphasis.h
204
+ - ext/qiita_marker/qfm_scanners.c
205
+ - ext/qiita_marker/qfm_scanners.h
206
+ - ext/qiita_marker/qfm_scanners.re
199
207
  - ext/qiita_marker/qiita_marker.c
200
208
  - ext/qiita_marker/qiita_marker.h
201
209
  - ext/qiita_marker/references.c
@@ -249,6 +257,9 @@ files:
249
257
  - test/test_options.rb
250
258
  - test/test_pathological_inputs.rb
251
259
  - test/test_plaintext.rb
260
+ - test/test_qfm_code_data_metadata.rb
261
+ - test/test_qfm_custom_block.rb
262
+ - test/test_qfm_mention_no_emphasis.rb
252
263
  - test/test_renderer.rb
253
264
  - test/test_smartpunct.rb
254
265
  - test/test_spec.rb
@@ -306,6 +317,9 @@ test_files:
306
317
  - test/test_options.rb
307
318
  - test/test_pathological_inputs.rb
308
319
  - test/test_plaintext.rb
320
+ - test/test_qfm_code_data_metadata.rb
321
+ - test/test_qfm_custom_block.rb
322
+ - test/test_qfm_mention_no_emphasis.rb
309
323
  - test/test_renderer.rb
310
324
  - test/test_smartpunct.rb
311
325
  - test/test_spec.rb