qiita_marker 0.23.2.1 → 0.23.2.2

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: '0579157ad4abb4038246ef4dc4752fd1de6cd281daa619b2ef42adec5b147982'
4
- data.tar.gz: c671815ff61ab460fcf6c3a42a614018a250c62ceaa41ee0aa3e899f34c12b16
3
+ metadata.gz: 15d5d73e2386cf310ae997adc33bfca334a51c241a0345b8d5fddc2af9a988bf
4
+ data.tar.gz: c45e0c3f30cfd5dfad5d8beb4dc0f3ee08fc6bb405e2dfb188623393180fa104
5
5
  SHA512:
6
- metadata.gz: 32f16ce193b82d6a7214c851fdd81126d42d872fdd4508b798ccfc4faba4668793274f88f9ef25e61ae57545041d1062c65169b05cd756eec55df0aa042d0140
7
- data.tar.gz: da050452749a3eee8d998db24aa45d6a8b1a282672283e427f8deca374405bfaebe572e954c7906ceb84bea2ff08ad737da05707b2ed5d378158d2738ac0e318
6
+ metadata.gz: 88d95a7228008d65a57f13d571370c3fe9a3701319e2e6a801a16659c032646d592345e155a23a8ac4f70b8d87a347b152a72b8447399c73285f2b475e3426d5
7
+ data.tar.gz: b3a3a996bfac4705044451fee3ecd7cd29909de05b3a1c9b52f64e9cc9cf14497820b8a74f67f49225c7ab777d87a83e9a0da104fa15219df03a3efb1c054a66
data/README.md CHANGED
@@ -20,6 +20,7 @@ In addition to CommonMarker's options and extensions, the following are availabl
20
20
  | Name | Description |
21
21
  | --- | --- |
22
22
  | `:MENTION_NO_EMPHASIS` | Prevent parsing mentions as emphasis. |
23
+ | `:AUTOLINK_CLASS_NAME` | Append `class="autolink"` to extension's autolinks. |
23
24
 
24
25
  #### Render options
25
26
 
@@ -27,6 +28,7 @@ In addition to CommonMarker's options and extensions, the following are availabl
27
28
  | --- | --- |
28
29
  | `:CODE_DATA_METADATA` | Use `<code data-metadata>` for fenced code blocks. |
29
30
  | `:MENTION_NO_EMPHASIS` | Prevent parsing mentions as emphasis. |
31
+ | `:AUTOLINK_CLASS_NAME` | Append `class="autolink"` to extension's autolinks. |
30
32
 
31
33
  ### Original extensions
32
34
 
@@ -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 *)':');
@@ -227,7 +227,7 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
227
227
  cmark_strbuf_puts(html, "<pre");
228
228
  cmark_html_render_sourcepos(node, html, options);
229
229
  cmark_strbuf_puts(html, "><code data-metadata=\"");
230
- escape_html(html, node->as.code.info.data, first_tag);
230
+ escape_html(html, node->as.code.info.data, node->as.code.info.len);
231
231
  if (first_tag < node->as.code.info.len &&
232
232
  (options & CMARK_OPT_FULL_INFO_STRING)) {
233
233
  cmark_strbuf_puts(html, "\" data-meta=\"");
@@ -12,6 +12,9 @@ extern "C" {
12
12
  /* Prevent parsing Qiita-style Mentions as emphasis. */
13
13
  #define CMARK_OPT_MENTION_NO_EMPHASIS (1 << 26)
14
14
 
15
+ /* Render autolinks with class name */
16
+ #define CMARK_OPT_AUTOLINK_CLASS_NAME (1 << 27)
17
+
15
18
  #ifdef __cplusplus
16
19
  }
17
20
  #endif
@@ -157,20 +157,7 @@ static int can_contain(cmark_syntax_extension *self, cmark_node *node,
157
157
  cmark_node_type child_type) {
158
158
  cmark_node_type node_type = cmark_node_get_type(node);
159
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;
160
+ return node_type == CMARK_NODE_QFM_CUSTOM_BLOCK;
174
161
  }
175
162
 
176
163
  static int contains_inlines(cmark_syntax_extension *self, cmark_node *node) {
@@ -1,5 +1,5 @@
1
- #include "cmark_ctype.h"
2
1
  #include "cmark-gfm.h"
2
+ #include "cmark_ctype.h"
3
3
  #include "config.h"
4
4
 
5
5
  static bool is_wordchar(char c) {
@@ -14,7 +14,8 @@ module QiitaMarker
14
14
  LIBERAL_HTML_TAG: (1 << 12),
15
15
  FOOTNOTES: (1 << 13),
16
16
  STRIKETHROUGH_DOUBLE_TILDE: (1 << 14),
17
- MENTION_NO_EMPHASIS: (1 << 26)
17
+ MENTION_NO_EMPHASIS: (1 << 26),
18
+ AUTOLINK_CLASS_NAME: (1 << 27)
18
19
  }.freeze,
19
20
  render: {
20
21
  DEFAULT: 0,
@@ -31,7 +32,8 @@ module QiitaMarker
31
32
  TABLE_PREFER_STYLE_ATTRIBUTES: (1 << 15),
32
33
  FULL_INFO_STRING: (1 << 16),
33
34
  CODE_DATA_METADATA: (1 << 25),
34
- MENTION_NO_EMPHASIS: (1 << 26)
35
+ MENTION_NO_EMPHASIS: (1 << 26),
36
+ AUTOLINK_CLASS_NAME: (1 << 27)
35
37
  }.freeze,
36
38
  format: %i[html xml commonmark plaintext].freeze
37
39
  }.freeze
@@ -96,7 +96,7 @@ module QiitaMarker
96
96
  out('><code>')
97
97
  elsif option_enabled?(:CODE_DATA_METADATA)
98
98
  out("<pre#{sourcepos(node)}><code")
99
- out(' data-metadata="', node.fence_info.split(/\s+/)[0], '"') if node.fence_info && !node.fence_info.empty?
99
+ out(' data-metadata="', node.fence_info, '"') if node.fence_info && !node.fence_info.empty?
100
100
  out('>')
101
101
  else
102
102
  out("<pre#{sourcepos(node)}><code")
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QiitaMarker
4
- VERSION = '0.23.2.1'
4
+ VERSION = '0.23.2.2'
5
5
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require('test_helper')
4
+
5
+ describe 'TestQfmAutolinkClassName' do
6
+ let(:options) { %i[AUTOLINK_CLASS_NAME] }
7
+ let(:extensions) { %i[autolink] }
8
+ let(:text) do
9
+ <<~MD
10
+ https://example.com
11
+ <https://example.com>
12
+ [Example](https://example.com)
13
+ test@example.com
14
+ MD
15
+ end
16
+ let(:doc) { QiitaMarker.render_doc(text, options, extensions) }
17
+ let(:expected) do
18
+ <<~HTML
19
+ <p><a href="https://example.com" class="autolink">https://example.com</a>
20
+ <a href="https://example.com">https://example.com</a>
21
+ <a href="https://example.com">Example</a>
22
+ <a href="mailto:test@example.com" class="autolink">test@example.com</a></p>
23
+ HTML
24
+ end
25
+ let(:rendered_html) { doc.to_html(options, extensions) }
26
+
27
+ it "appends class name to extension's autolinks" do
28
+ assert_equal(expected, rendered_html)
29
+ end
30
+
31
+ describe 'without AUTOLINK_CLASS_NAME option' do
32
+ let(:options) { %i[DEFAULT] }
33
+ let(:expected) do
34
+ <<~HTML
35
+ <p><a href="https://example.com">https://example.com</a>
36
+ <a href="https://example.com">https://example.com</a>
37
+ <a href="https://example.com">Example</a>
38
+ <a href="mailto:test@example.com">test@example.com</a></p>
39
+ HTML
40
+ end
41
+
42
+ it "does not append class name to extension's autolink" do
43
+ assert_equal(expected, rendered_html)
44
+ end
45
+ end
46
+
47
+ describe 'without autolink extension' do
48
+ let(:extensions) { %i[] }
49
+ let(:expected) do
50
+ <<~HTML
51
+ <p>https://example.com
52
+ <a href="https://example.com">https://example.com</a>
53
+ <a href="https://example.com">Example</a>
54
+ test@example.com</p>
55
+ HTML
56
+ end
57
+
58
+ it 'does not append class name' do
59
+ assert_equal(expected, rendered_html)
60
+ end
61
+ end
62
+ end
@@ -3,24 +3,39 @@
3
3
  require('test_helper')
4
4
 
5
5
  class TestQfmCodeDataMetadata < Minitest::Test
6
- def setup
6
+ def test_to_html
7
7
  text = <<~MD
8
- ```ruby:example.rb
8
+ ```ruby:example main.rb
9
9
  puts :foo
10
10
  ```
11
11
  MD
12
- @doc = QiitaMarker.render_doc(text, :DEFAULT, [])
13
- @expected = <<~HTML
14
- <pre><code data-metadata="ruby:example.rb">puts :foo
12
+ doc = render_doc(text)
13
+ expected = <<~HTML
14
+ <pre><code data-metadata="ruby:example main.rb">puts :foo
15
15
  </code></pre>
16
16
  HTML
17
+
18
+ assert_equal(expected, doc.to_html(:CODE_DATA_METADATA))
19
+ assert_equal(expected, QiitaMarker::HtmlRenderer.new(options: :CODE_DATA_METADATA).render(doc))
17
20
  end
18
21
 
19
- def test_to_html
20
- assert_equal(@expected, @doc.to_html(:CODE_DATA_METADATA))
22
+ def test_with_character_reference
23
+ text = <<~MD
24
+ ```ruby:example&#x20;main.rb
25
+ puts :foo
26
+ ```
27
+ MD
28
+ doc = render_doc(text)
29
+ expected = <<~HTML
30
+ <pre><code data-metadata="ruby:example main.rb">puts :foo
31
+ </code></pre>
32
+ HTML
33
+
34
+ assert_equal(expected, doc.to_html(:CODE_DATA_METADATA))
35
+ assert_equal(expected, QiitaMarker::HtmlRenderer.new(options: :CODE_DATA_METADATA).render(doc))
21
36
  end
22
37
 
23
- def test_html_renderer
24
- assert_equal(@expected, QiitaMarker::HtmlRenderer.new(options: :CODE_DATA_METADATA).render(@doc))
38
+ def render_doc(markdown)
39
+ QiitaMarker.render_doc(markdown, :DEFAULT, [])
25
40
  end
26
41
  end
@@ -7,17 +7,33 @@ class TestQfmCustomBlock < Minitest::Test
7
7
  text = <<~MD
8
8
  :::foo bar
9
9
  message
10
+
11
+ - list1
12
+ - list2
13
+
14
+ ```ruby
15
+ puts 'hello'
16
+ ```
17
+
18
+ <div>html block</div>
10
19
  :::
11
20
  MD
12
- @doc = QiitaMarker.render_doc(text, :DEFAULT, %i[custom_block])
21
+ @doc = QiitaMarker.render_doc(text, %i[UNSAFE], %i[custom_block])
13
22
  @expected = <<~HTML
14
23
  <div data-type="customblock" data-metadata="foo bar">
15
24
  <p>message</p>
25
+ <ul>
26
+ <li>list1</li>
27
+ <li>list2</li>
28
+ </ul>
29
+ <pre><code class="language-ruby">puts 'hello'
30
+ </code></pre>
31
+ <div>html block</div>
16
32
  </div>
17
33
  HTML
18
34
  end
19
35
 
20
36
  def test_to_html
21
- assert_equal(@expected, @doc.to_html(:DEFAULT, %i[custom_block]))
37
+ assert_equal(@expected, @doc.to_html(%i[UNSAFE], %i[custom_block]))
22
38
  end
23
39
  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.1
4
+ version: 0.23.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Qiita Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-08 00:00:00.000000000 Z
11
+ date: 2022-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: awesome_print
@@ -257,6 +257,7 @@ files:
257
257
  - test/test_options.rb
258
258
  - test/test_pathological_inputs.rb
259
259
  - test/test_plaintext.rb
260
+ - test/test_qfm_autolink_class_name.rb
260
261
  - test/test_qfm_code_data_metadata.rb
261
262
  - test/test_qfm_custom_block.rb
262
263
  - test/test_qfm_mention_no_emphasis.rb
@@ -317,6 +318,7 @@ test_files:
317
318
  - test/test_options.rb
318
319
  - test/test_pathological_inputs.rb
319
320
  - test/test_plaintext.rb
321
+ - test/test_qfm_autolink_class_name.rb
320
322
  - test/test_qfm_code_data_metadata.rb
321
323
  - test/test_qfm_custom_block.rb
322
324
  - test/test_qfm_mention_no_emphasis.rb