qiita_marker 0.23.2.1 → 0.23.2.2

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