qiita_marker 0.23.2.0 → 0.23.2.1

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: '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