qiita-markdown 0.44.1 → 1.0.0
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 +4 -4
- data/.github/workflows/test.yml +2 -2
- data/.rubocop.yml +0 -4
- data/.rubocop_todo.yml +6 -44
- data/CHANGELOG.md +10 -0
- data/README.md +3 -1
- data/lib/qiita/markdown/filters/checkbox.rb +5 -1
- data/lib/qiita/markdown/filters/custom_block.rb +7 -6
- data/lib/qiita/markdown/filters/final_sanitizer.rb +8 -2
- data/lib/qiita/markdown/filters/heading_anchor.rb +44 -0
- data/lib/qiita/markdown/filters/html_toc.rb +67 -0
- data/lib/qiita/markdown/filters/qiita_marker.rb +55 -0
- data/lib/qiita/markdown/filters/user_input_sanitizer.rb +14 -9
- data/lib/qiita/markdown/processor.rb +2 -1
- data/lib/qiita/markdown/summary_processor.rb +1 -1
- data/lib/qiita/markdown/version.rb +1 -1
- data/lib/qiita/markdown.rb +4 -5
- data/qiita-markdown.gemspec +2 -3
- data/spec/qiita/markdown/filters/checkbox_spec.rb +28 -0
- data/spec/qiita/markdown/filters/heading_anchor_spec.rb +73 -0
- data/spec/qiita/markdown/filters/html_toc_spec.rb +223 -0
- data/spec/qiita/markdown/filters/qiita_marker_spec.rb +60 -0
- data/spec/qiita/markdown/processor_spec.rb +48 -54
- data/spec/qiita/markdown/summary_processor_spec.rb +2 -2
- metadata +23 -39
- data/benchmark/heading_anchor_rendering.rb +0 -248
- data/benchmark/sample.md +0 -317
- data/lib/qiita/markdown/filters/greenmat.rb +0 -38
- data/lib/qiita/markdown/greenmat/heading_rendering.rb +0 -61
- data/lib/qiita/markdown/greenmat/html_renderer.rb +0 -60
- data/lib/qiita/markdown/greenmat/html_toc_renderer.rb +0 -78
- data/spec/qiita/markdown/filters/greenmat_spec.rb +0 -15
- 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&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&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&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&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"><div></a>
|
150
|
-
</li>
|
151
|
-
</ul>
|
152
|
-
EOS
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|