qiita-markdown 0.44.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -2
  3. data/.rubocop.yml +0 -4
  4. data/.rubocop_todo.yml +6 -44
  5. data/CHANGELOG.md +10 -0
  6. data/README.md +3 -1
  7. data/lib/qiita/markdown/filters/checkbox.rb +5 -1
  8. data/lib/qiita/markdown/filters/custom_block.rb +7 -6
  9. data/lib/qiita/markdown/filters/final_sanitizer.rb +8 -2
  10. data/lib/qiita/markdown/filters/heading_anchor.rb +44 -0
  11. data/lib/qiita/markdown/filters/html_toc.rb +67 -0
  12. data/lib/qiita/markdown/filters/qiita_marker.rb +55 -0
  13. data/lib/qiita/markdown/filters/user_input_sanitizer.rb +14 -9
  14. data/lib/qiita/markdown/processor.rb +2 -1
  15. data/lib/qiita/markdown/summary_processor.rb +1 -1
  16. data/lib/qiita/markdown/version.rb +1 -1
  17. data/lib/qiita/markdown.rb +4 -5
  18. data/qiita-markdown.gemspec +2 -3
  19. data/spec/qiita/markdown/filters/checkbox_spec.rb +28 -0
  20. data/spec/qiita/markdown/filters/heading_anchor_spec.rb +73 -0
  21. data/spec/qiita/markdown/filters/html_toc_spec.rb +223 -0
  22. data/spec/qiita/markdown/filters/qiita_marker_spec.rb +60 -0
  23. data/spec/qiita/markdown/processor_spec.rb +48 -54
  24. data/spec/qiita/markdown/summary_processor_spec.rb +2 -2
  25. metadata +23 -39
  26. data/benchmark/heading_anchor_rendering.rb +0 -248
  27. data/benchmark/sample.md +0 -317
  28. data/lib/qiita/markdown/filters/greenmat.rb +0 -38
  29. data/lib/qiita/markdown/greenmat/heading_rendering.rb +0 -61
  30. data/lib/qiita/markdown/greenmat/html_renderer.rb +0 -60
  31. data/lib/qiita/markdown/greenmat/html_toc_renderer.rb +0 -78
  32. data/spec/qiita/markdown/filters/greenmat_spec.rb +0 -15
  33. data/spec/qiita/markdown/greenmat/html_toc_renderer_spec.rb +0 -156
@@ -1,38 +0,0 @@
1
- module Qiita
2
- module Markdown
3
- module Filters
4
- class Greenmat < HTML::Pipeline::TextFilter
5
- DEFAULT_OPTIONS = {
6
- footnotes: true,
7
- }.freeze
8
-
9
- # @return [Nokogiri::HTML::DocumentFragment]
10
- def call
11
- Nokogiri::HTML.fragment(greenmat.render(@text))
12
- end
13
-
14
- private
15
-
16
- # Memoize.
17
- # @return [Greenmat::Markdown]
18
- def greenmat
19
- @renderer ||= ::Greenmat::Markdown.new(
20
- Qiita::Markdown::Greenmat::HTMLRenderer.new(hard_wrap: true, with_toc_data: true),
21
- autolink: true,
22
- fenced_code_blocks: true,
23
- fenced_custom_blocks: true,
24
- footnotes: options[:footnotes],
25
- no_intra_emphasis: true,
26
- no_mention_emphasis: true,
27
- strikethrough: true,
28
- tables: true,
29
- )
30
- end
31
-
32
- def options
33
- @options ||= DEFAULT_OPTIONS.merge(context[:markdown] || {})
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,61 +0,0 @@
1
- module Qiita
2
- module Markdown
3
- module Greenmat
4
- module HeadingRendering
5
- def heading_counter
6
- @counter ||= Hash.new(0)
7
- end
8
-
9
- class AbstractHeading
10
- attr_reader :raw_body, :level, :counter, :escape_html
11
- alias escape_html? escape_html
12
-
13
- def initialize(raw_body, level, counter, escape_html = false)
14
- @raw_body = raw_body
15
- @level = level
16
- @counter = counter
17
- @escape_html = escape_html
18
- end
19
-
20
- def to_s
21
- raise NotImplementedError
22
- end
23
-
24
- def increment
25
- raise NotImplementedError
26
- end
27
-
28
- private
29
-
30
- def count
31
- counter[id]
32
- end
33
-
34
- def has_count?
35
- count > 0
36
- end
37
-
38
- def body
39
- escape_html? ? CGI.escape_html(raw_body) : raw_body
40
- end
41
-
42
- def id
43
- @id ||= text.downcase.gsub(/[^\p{Word}\- ]/u, "").tr(" ", "-")
44
- end
45
-
46
- def text
47
- Nokogiri::HTML.fragment(raw_body).text
48
- end
49
-
50
- def suffix
51
- has_count? ? "-#{count}" : ""
52
- end
53
-
54
- def suffixed_id
55
- @suffixed_id ||= "#{id}#{suffix}"
56
- end
57
- end
58
- end
59
- end
60
- end
61
- end
@@ -1,60 +0,0 @@
1
- require "uri"
2
-
3
- module Qiita
4
- module Markdown
5
- module Greenmat
6
- class HTMLRenderer < ::Greenmat::Render::HTML
7
- include HeadingRendering
8
-
9
- def initialize(extensions = {})
10
- super
11
- @with_toc_data = extensions[:with_toc_data]
12
- end
13
-
14
- # https://github.com/vmg/redcarpet/blob/v3.2.3/ext/redcarpet/html.c#L76-L116
15
- def autolink(link, link_type)
16
- if link_type == :email
17
- %(<a href="mailto:#{link}" class="autolink">#{link}</a>)
18
- else
19
- %(<a href="#{link}" class="autolink">#{link}</a>)
20
- end
21
- end
22
-
23
- def header(text, level)
24
- heading = heading_class.new(text, level, heading_counter)
25
- heading.to_s.tap do
26
- heading.increment
27
- end
28
- end
29
-
30
- private
31
-
32
- def heading_class
33
- @heading_class ||= (@with_toc_data ? HeadingWithAnchor : Heading)
34
- end
35
-
36
- class Heading < AbstractHeading
37
- # For reference, C implementation of Redcarpet::Render::HTML#header is the following:
38
- # https://github.com/vmg/redcarpet/blob/v3.2.3/ext/redcarpet/html.c#L281-L296
39
- def to_s
40
- "\n<h#{level}>#{body}</h#{level}>\n"
41
- end
42
-
43
- def increment
44
- # No-op
45
- end
46
- end
47
-
48
- class HeadingWithAnchor < AbstractHeading
49
- def to_s
50
- %(\n<h#{level} id="#{suffixed_id}">#{body}</h#{level}>\n)
51
- end
52
-
53
- def increment
54
- counter[id] += 1
55
- end
56
- end
57
- end
58
- end
59
- end
60
- end
@@ -1,78 +0,0 @@
1
- module Qiita
2
- module Markdown
3
- module Greenmat
4
- class HTMLToCRenderer < ::Greenmat::Render::HTML_TOC
5
- include HeadingRendering
6
-
7
- def initialize(extensions = {})
8
- super
9
- @extensions = extensions
10
- @last_level = 0
11
- end
12
-
13
- # https://github.com/vmg/redcarpet/blob/v3.2.3/ext/redcarpet/html.c#L609-L642
14
- def header(text, level)
15
- @level_offset ||= level - 1
16
-
17
- level -= @level_offset
18
- level = 1 if level < 1
19
-
20
- difference = level - @last_level
21
- @last_level = level
22
-
23
- generate_heading_html(text, level, difference)
24
- end
25
-
26
- # https://github.com/vmg/redcarpet/blob/v3.2.3/ext/redcarpet/html.c#L652-L661
27
- def doc_footer
28
- "</li>\n</ul>\n" * @last_level
29
- end
30
-
31
- private
32
-
33
- def generate_heading_html(text, level, level_difference)
34
- html = list_item_preceding_html(level_difference)
35
-
36
- anchor = HeadingAnchor.new(text, level, heading_counter, escape_html?)
37
- html << anchor.to_s
38
- anchor.increment
39
-
40
- html
41
- end
42
-
43
- def list_item_preceding_html(level_difference)
44
- html = case
45
- when level_difference > 0
46
- "<ul>\n" * level_difference
47
- when level_difference < 0
48
- "</li>\n" << ("</ul>\n</li>\n" * level_difference.abs)
49
- else
50
- "</li>\n"
51
- end
52
-
53
- html << "<li>\n"
54
- end
55
-
56
- def escape_html?
57
- @extensions[:escape_html]
58
- end
59
-
60
- class HeadingAnchor < AbstractHeading
61
- def to_s
62
- "<a href=\"##{suffixed_id}\">#{body}</a>\n"
63
- end
64
-
65
- def increment
66
- counter[id] += 1
67
- end
68
-
69
- private
70
-
71
- def body
72
- escape_html? ? CGI.escape_html(text) : raw_body
73
- end
74
- end
75
- end
76
- end
77
- end
78
- end
@@ -1,15 +0,0 @@
1
- describe Qiita::Markdown::Filters::Greenmat do
2
- subject(:filter) do
3
- described_class.new(markdown)
4
- end
5
-
6
- context "with headings" do
7
- let(:markdown) do
8
- "# foo"
9
- end
10
-
11
- it "does not generate FontAwesome classes so that we can say that they're inputted by user" do
12
- expect(filter.call.to_s).to eq(%(\n<h1 id="foo">foo</h1>\n))
13
- end
14
- end
15
- end
@@ -1,156 +0,0 @@
1
- require "active_support/core_ext/string/strip"
2
-
3
- describe Qiita::Markdown::Greenmat::HTMLToCRenderer do
4
- let(:renderer) { described_class.new(extension) }
5
- let(:extension) { {} }
6
- let(:greenmat) { ::Greenmat::Markdown.new(renderer) }
7
- subject(:rendered_html) { greenmat.render(markdown) }
8
-
9
- context "with duplicated heading names" do
10
- let(:markdown) do
11
- <<-EOS.strip_heredoc
12
- # a
13
- ## a
14
- ### a
15
- ### a
16
- EOS
17
- end
18
-
19
- it "renders ToC anchors with unique ids" do
20
- should eq <<-EOS.strip_heredoc
21
- <ul>
22
- <li>
23
- <a href="#a">a</a>
24
- <ul>
25
- <li>
26
- <a href="#a-1">a</a>
27
- <ul>
28
- <li>
29
- <a href="#a-2">a</a>
30
- </li>
31
- <li>
32
- <a href="#a-3">a</a>
33
- </li>
34
- </ul>
35
- </li>
36
- </ul>
37
- </li>
38
- </ul>
39
- EOS
40
- end
41
- end
42
-
43
- context "with a document starting with level 2 heading" do
44
- let(:markdown) do
45
- <<-EOS.strip_heredoc
46
- ## a
47
- ### a
48
- ## a
49
- EOS
50
- end
51
-
52
- it "offsets the heading levels" do
53
- should eq <<-EOS.strip_heredoc
54
- <ul>
55
- <li>
56
- <a href="#a">a</a>
57
- <ul>
58
- <li>
59
- <a href="#a-1">a</a>
60
- </li>
61
- </ul>
62
- </li>
63
- <li>
64
- <a href="#a-2">a</a>
65
- </li>
66
- </ul>
67
- EOS
68
- end
69
- end
70
-
71
- context "with a document starting with level 2 heading but includes level 1 heading at the end" do
72
- let(:markdown) do
73
- <<-EOS.strip_heredoc
74
- ## a
75
- ### a
76
- # a
77
- EOS
78
- end
79
-
80
- it "does not generate invalid list structure" do
81
- should eq <<-EOS.strip_heredoc
82
- <ul>
83
- <li>
84
- <a href="#a">a</a>
85
- <ul>
86
- <li>
87
- <a href="#a-1">a</a>
88
- </li>
89
- </ul>
90
- </li>
91
- <li>
92
- <a href="#a-2">a</a>
93
- </li>
94
- </ul>
95
- EOS
96
- end
97
- end
98
-
99
- context "with heading title including special HTML characters" do
100
- let(:markdown) do
101
- <<-EOS.strip_heredoc
102
- # <b>R&amp;B</b>
103
- EOS
104
- end
105
-
106
- it "generates fragment identifier by sanitizing the characters in the title" do
107
- should eq <<-EOS.strip_heredoc
108
- <ul>
109
- <li>
110
- <a href="#rb"><b>R&amp;B</b></a>
111
- </li>
112
- </ul>
113
- EOS
114
- end
115
- end
116
-
117
- context "with :escape_html extension" do
118
- let(:extension) { { escape_html: true } }
119
-
120
- context "with heading title including HTML tags" do
121
- let(:markdown) do
122
- <<-EOS.strip_heredoc
123
- # <b>R&amp;B</b>
124
- EOS
125
- end
126
-
127
- it "strips HTML characters in heading title" do
128
- should eq <<-EOS.strip_heredoc
129
- <ul>
130
- <li>
131
- <a href="#rb">R&amp;B</a>
132
- </li>
133
- </ul>
134
- EOS
135
- end
136
- end
137
-
138
- context "with heading title including HTML tags inside of code" do
139
- let(:markdown) do
140
- <<-EOS.strip_heredoc
141
- # `<div>`
142
- EOS
143
- end
144
-
145
- it "escapes HTML tags inside of code" do
146
- should eq <<-EOS.strip_heredoc
147
- <ul>
148
- <li>
149
- <a href="#div">&lt;div&gt;</a>
150
- </li>
151
- </ul>
152
- EOS
153
- end
154
- end
155
- end
156
- end