qiita-markdown 0.44.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +2 -2
- data/.rubocop.yml +0 -4
- data/.rubocop_todo.yml +15 -239
- data/CHANGELOG.md +15 -0
- data/README.md +5 -3
- data/lib/qiita/markdown/filters/checkbox.rb +5 -1
- data/lib/qiita/markdown/filters/code_block.rb +13 -13
- data/lib/qiita/markdown/filters/custom_block.rb +8 -6
- data/lib/qiita/markdown/filters/external_link.rb +2 -0
- data/lib/qiita/markdown/filters/final_sanitizer.rb +126 -120
- data/lib/qiita/markdown/filters/footnote.rb +2 -0
- data/lib/qiita/markdown/filters/group_mention.rb +2 -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/image_link.rb +6 -6
- data/lib/qiita/markdown/filters/inline_code_color.rb +8 -8
- data/lib/qiita/markdown/filters/mention.rb +11 -9
- data/lib/qiita/markdown/filters/qiita_marker.rb +55 -0
- data/lib/qiita/markdown/filters/simplify.rb +1 -0
- data/lib/qiita/markdown/filters/syntax_highlight.rb +4 -4
- data/lib/qiita/markdown/filters/truncate.rb +1 -3
- data/lib/qiita/markdown/filters/user_input_sanitizer.rb +34 -29
- data/lib/qiita/markdown/processor.rb +2 -1
- data/lib/qiita/markdown/summary_processor.rb +1 -1
- data/lib/qiita/markdown/transformers/filter_attributes.rb +1 -0
- data/lib/qiita/markdown/transformers/filter_iframe.rb +1 -2
- data/lib/qiita/markdown/transformers/filter_script.rb +1 -1
- data/lib/qiita/markdown/transformers/strip_invalid_node.rb +1 -3
- data/lib/qiita/markdown/version.rb +1 -1
- data/lib/qiita/markdown.rb +4 -5
- data/qiita-markdown.gemspec +7 -8
- 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 +64 -70
- data/spec/qiita/markdown/summary_processor_spec.rb +4 -4
- metadata +80 -102
- 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,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Qiita
|
2
4
|
module Markdown
|
3
5
|
module Filters
|
@@ -9,44 +11,45 @@ module Qiita
|
|
9
11
|
# generated by other filters.
|
10
12
|
#
|
11
13
|
# @see Qiita::Markdown::Filters::UserInputSanitizerr
|
12
|
-
class FinalSanitizer < HTML::Pipeline::Filter
|
14
|
+
class FinalSanitizer < ::HTML::Pipeline::Filter
|
13
15
|
RULE = {
|
14
16
|
attributes: {
|
15
|
-
"a" => [
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
"a" => %w[
|
18
|
+
data-hovercard-target-name
|
19
|
+
data-hovercard-target-type
|
20
|
+
href
|
21
|
+
rel
|
20
22
|
],
|
21
23
|
"blockquote" => Embed::Tweet::ATTRIBUTES,
|
22
|
-
"iframe" => [
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
24
|
+
"iframe" => %w[
|
25
|
+
allowfullscreen
|
26
|
+
frameborder
|
27
|
+
height
|
28
|
+
loading
|
29
|
+
marginheight
|
30
|
+
marginwidth
|
31
|
+
scrolling
|
32
|
+
src
|
33
|
+
style
|
34
|
+
width
|
32
35
|
],
|
33
36
|
"img" => [
|
34
37
|
"src",
|
35
38
|
],
|
36
|
-
"input" => [
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
"input" => %w[
|
40
|
+
checked
|
41
|
+
disabled
|
42
|
+
type
|
40
43
|
],
|
41
|
-
"div" => [
|
42
|
-
|
43
|
-
|
44
|
+
"div" => %w[
|
45
|
+
itemscope
|
46
|
+
itemtype
|
44
47
|
],
|
45
48
|
"p" => Embed::CodePen::ATTRIBUTES,
|
46
|
-
"script" => [
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
"script" => %w[
|
50
|
+
async
|
51
|
+
src
|
52
|
+
type
|
50
53
|
].concat(
|
51
54
|
Embed::SpeekerDeck::ATTRIBUTES,
|
52
55
|
Embed::Docswell::ATTRIBUTES,
|
@@ -60,104 +63,107 @@ module Qiita
|
|
60
63
|
"th" => [
|
61
64
|
"style",
|
62
65
|
],
|
63
|
-
"video" => [
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
"video" => %w[
|
67
|
+
src
|
68
|
+
autoplay
|
69
|
+
controls
|
70
|
+
loop
|
71
|
+
muted
|
72
|
+
poster
|
70
73
|
],
|
71
|
-
all: [
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
74
|
+
all: %w[
|
75
|
+
abbr
|
76
|
+
align
|
77
|
+
alt
|
78
|
+
border
|
79
|
+
cellpadding
|
80
|
+
cellspacing
|
81
|
+
cite
|
82
|
+
class
|
83
|
+
color
|
84
|
+
cols
|
85
|
+
colspan
|
86
|
+
data-lang
|
87
|
+
data-sourcepos
|
88
|
+
datetime
|
89
|
+
height
|
90
|
+
hreflang
|
91
|
+
id
|
92
|
+
itemprop
|
93
|
+
lang
|
94
|
+
name
|
95
|
+
rowspan
|
96
|
+
tabindex
|
97
|
+
target
|
98
|
+
title
|
99
|
+
width
|
96
100
|
],
|
97
101
|
},
|
98
102
|
css: {
|
99
|
-
properties: [
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
+
properties: %w[
|
104
|
+
background-color
|
105
|
+
border
|
106
|
+
text-align
|
103
107
|
],
|
104
108
|
},
|
105
|
-
elements: [
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
109
|
+
elements: %w[
|
110
|
+
a
|
111
|
+
b
|
112
|
+
blockquote
|
113
|
+
br
|
114
|
+
caption
|
115
|
+
code
|
116
|
+
dd
|
117
|
+
del
|
118
|
+
details
|
119
|
+
div
|
120
|
+
dl
|
121
|
+
dt
|
122
|
+
em
|
123
|
+
font
|
124
|
+
h1
|
125
|
+
h2
|
126
|
+
h3
|
127
|
+
h4
|
128
|
+
h5
|
129
|
+
h6
|
130
|
+
h7
|
131
|
+
h8
|
132
|
+
hr
|
133
|
+
i
|
134
|
+
img
|
135
|
+
input
|
136
|
+
ins
|
137
|
+
kbd
|
138
|
+
li
|
139
|
+
ol
|
140
|
+
p
|
141
|
+
pre
|
142
|
+
q
|
143
|
+
rp
|
144
|
+
rt
|
145
|
+
ruby
|
146
|
+
s
|
147
|
+
samp
|
148
|
+
script
|
149
|
+
iframe
|
150
|
+
section
|
151
|
+
span
|
152
|
+
strike
|
153
|
+
strong
|
154
|
+
sub
|
155
|
+
summary
|
156
|
+
sup
|
157
|
+
table
|
158
|
+
tbody
|
159
|
+
td
|
160
|
+
tfoot
|
161
|
+
th
|
162
|
+
thead
|
163
|
+
tr
|
164
|
+
tt
|
165
|
+
ul
|
166
|
+
var
|
161
167
|
],
|
162
168
|
protocols: {
|
163
169
|
"a" => {
|
@@ -200,7 +206,7 @@ module Qiita
|
|
200
206
|
rule[:attributes][:all] = rule[:attributes][:all] + [:data]
|
201
207
|
rule[:elements] = RULE[:elements] + ["video"]
|
202
208
|
rule[:transformers] = rule[:transformers] - [Transformers::FilterScript, Transformers::FilterIframe]
|
203
|
-
end
|
209
|
+
end.freeze
|
204
210
|
|
205
211
|
def call
|
206
212
|
::Sanitize.clean_node!(doc, rule)
|
@@ -6,6 +6,7 @@ module Qiita
|
|
6
6
|
doc.search("sup > a").each do |a|
|
7
7
|
footnote = find_footnote(a)
|
8
8
|
next unless footnote
|
9
|
+
|
9
10
|
a[:title] = footnote.text.gsub(/\A\n/, "").gsub(/ ↩\n\z/, "")
|
10
11
|
end
|
11
12
|
doc
|
@@ -16,6 +17,7 @@ module Qiita
|
|
16
17
|
def find_footnote(a)
|
17
18
|
href = a["href"]
|
18
19
|
return nil if !href || href.match(/\A#fn\d+\z/).nil?
|
20
|
+
|
19
21
|
doc.search(href).first
|
20
22
|
end
|
21
23
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qiita
|
4
|
+
module Markdown
|
5
|
+
module Filters
|
6
|
+
class HeadingAnchor < ::HTML::Pipeline::Filter
|
7
|
+
def call
|
8
|
+
doc.search("h1, h2, h3, h4, h5, h6").each do |heading|
|
9
|
+
heading["id"] = suffixed_id(heading)
|
10
|
+
end
|
11
|
+
|
12
|
+
doc
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def counter
|
18
|
+
@counter ||= ::Hash.new(0)
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_count(id)
|
22
|
+
counter[id]
|
23
|
+
end
|
24
|
+
|
25
|
+
def increment_count(id)
|
26
|
+
counter[id] += 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def heading_id(node)
|
30
|
+
node.text.downcase.gsub(/[^\p{Word}\- ]/u, "").tr(" ", "-")
|
31
|
+
end
|
32
|
+
|
33
|
+
def suffixed_id(node)
|
34
|
+
id = heading_id(node)
|
35
|
+
count = get_count(id)
|
36
|
+
suffix = count.positive? ? "-#{count}" : ""
|
37
|
+
increment_count(id)
|
38
|
+
|
39
|
+
"#{id}#{suffix}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qiita
|
4
|
+
module Markdown
|
5
|
+
module Filters
|
6
|
+
class HtmlToc < ::HTML::Pipeline::Filter
|
7
|
+
# @return [Nokogiri::HTML::DocumentFragment]
|
8
|
+
def call
|
9
|
+
headings = doc.search("h1, h2, h3, h4, h5, h6")
|
10
|
+
return "" if headings.empty?
|
11
|
+
|
12
|
+
toc = %W[<ul>\n]
|
13
|
+
top_level = nil
|
14
|
+
last_level = nil
|
15
|
+
depth = 1
|
16
|
+
|
17
|
+
headings.each do |node|
|
18
|
+
heading_rank = node.name.match(/h(\d)/)[1].to_i
|
19
|
+
|
20
|
+
# The first heading is displayed as the top level.
|
21
|
+
# The following headings, of higher rank than the first, are placed as top level.
|
22
|
+
top_level ||= heading_rank
|
23
|
+
current_level = [heading_rank, top_level].max
|
24
|
+
|
25
|
+
link = toc_with_link(node.text, node.attributes["id"]&.value)
|
26
|
+
toc << (nest_string(last_level, current_level) + link)
|
27
|
+
|
28
|
+
depth += current_level - last_level if last_level
|
29
|
+
|
30
|
+
last_level = current_level
|
31
|
+
end
|
32
|
+
|
33
|
+
toc << ("</li>\n</ul>\n" * depth)
|
34
|
+
toc.join
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# @param text [String]
|
40
|
+
# @param id [String]
|
41
|
+
# @return [String]
|
42
|
+
def toc_with_link(text, id)
|
43
|
+
%(<a href="##{id}">#{CGI.escapeHTML(text)}</a>\n)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param last_level [Integer, nil]
|
47
|
+
# @param current_level [Integer]
|
48
|
+
# @return [String]
|
49
|
+
def nest_string(last_level, current_level)
|
50
|
+
if last_level.nil?
|
51
|
+
return "<li>\n"
|
52
|
+
elsif current_level == last_level
|
53
|
+
return "</li>\n<li>\n"
|
54
|
+
elsif current_level > last_level
|
55
|
+
level_difference = current_level - last_level
|
56
|
+
return "<ul>\n<li>\n" * level_difference
|
57
|
+
elsif current_level < last_level
|
58
|
+
level_difference = last_level - current_level
|
59
|
+
return %(#{"</li>\n</ul>\n" * level_difference}</li>\n<li>\n)
|
60
|
+
end
|
61
|
+
|
62
|
+
""
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -4,12 +4,12 @@ module Qiita
|
|
4
4
|
class ImageLink < HTML::Pipeline::Filter
|
5
5
|
def call
|
6
6
|
doc.search("img").each do |img|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
next if img.ancestors.any? { |ancestor| ancestor.name == "a" }
|
8
|
+
|
9
|
+
outer = Nokogiri::HTML.fragment(%(<a href="#{img['src']}" target="_blank"></a>))
|
10
|
+
inner = img.clone
|
11
|
+
outer.at("a").add_child(inner)
|
12
|
+
img.replace(outer)
|
13
13
|
end
|
14
14
|
doc
|
15
15
|
end
|
@@ -6,15 +6,15 @@ module Qiita
|
|
6
6
|
|
7
7
|
REGEXPS = Regexp.union(
|
8
8
|
/\#(?:\h{3}|\h{6})/,
|
9
|
-
/rgba?\(\s*(?:\d+(
|
10
|
-
/rgba?\(\s*(?:\d+%(
|
11
|
-
/rgba?\(\s*(?:\d
|
12
|
-
|
13
|
-
|
14
|
-
/hsla?\(\s*\d+(?:deg|rad|grad|turn)
|
9
|
+
/rgba?\(\s*(?:\d+(?:,|\s)\s*){2}\d+\s*\)/,
|
10
|
+
/rgba?\(\s*(?:\d+%(?:,|\s)\s*){2}\d+%\s*\)/,
|
11
|
+
/rgba?\(\s*(?:\d+,\s*){3}\d*\.?\d+%?\s*\)/,
|
12
|
+
%r{rgba?\(\s*(?:\d+\s*){2}\d+\s*/\s*\d?\.?\d+%?\s*\)},
|
13
|
+
%r{rgba?\(\s*(?:\d+%\s*){2}\d+%\s*/\s*\d?\.?\d+%?\s*\)},
|
14
|
+
/hsla?\(\s*\d+(?:deg|rad|grad|turn)?,\s*\d+%,\s*\d+%\s*\)/,
|
15
15
|
/hsla?\(\s*\d+(?:deg|rad|grad|turn)?\s+\d+%\s+\d+%\s*\)/,
|
16
|
-
/hsla?\(\s*\d+(?:deg|rad|grad|turn)
|
17
|
-
|
16
|
+
/hsla?\(\s*\d+(?:deg|rad|grad|turn)?,\s*(?:\d+%,\s*){2}\d?\.?\d+%?\s*\)/,
|
17
|
+
%r{hsla?\(\s*\d+(?:deg|rad|grad|turn)?\s+\d+%\s+\d+%\s*/\s*\d?\.?\d+%?\s*\)},
|
18
18
|
)
|
19
19
|
|
20
20
|
COLOR_CODE_PATTERN = /\A\s*(#{REGEXPS})\s*\z/
|
@@ -8,17 +8,17 @@ module Qiita
|
|
8
8
|
class Mention < HTML::Pipeline::MentionFilter
|
9
9
|
IGNORE_PARENTS = ::HTML::Pipeline::MentionFilter::IGNORE_PARENTS + Set["blockquote"]
|
10
10
|
|
11
|
-
MentionPattern =
|
11
|
+
MentionPattern = %r{
|
12
12
|
(?:^|\W)
|
13
|
-
@((
|
14
|
-
(
|
13
|
+
@((?>\w[\w-]{0,30}\w(?:@github)?))
|
14
|
+
(?!/)
|
15
15
|
(?=
|
16
16
|
\.+[ \t\W]|
|
17
17
|
\.+$|
|
18
18
|
[^0-9a-zA-Z_.]|
|
19
19
|
$
|
20
20
|
)
|
21
|
-
|
21
|
+
}ix
|
22
22
|
|
23
23
|
# @note Override to use another IGNORE_PARENTS
|
24
24
|
def call
|
@@ -28,8 +28,10 @@ module Qiita
|
|
28
28
|
content = node.to_html
|
29
29
|
next unless content.include?("@")
|
30
30
|
next if has_ancestor?(node, IGNORE_PARENTS)
|
31
|
+
|
31
32
|
html = mention_link_filter(content, base_url, info_url, username_pattern)
|
32
33
|
next if html == content
|
34
|
+
|
33
35
|
node.replace(html)
|
34
36
|
end
|
35
37
|
doc
|
@@ -38,22 +40,22 @@ module Qiita
|
|
38
40
|
# @note Override to use customized MentionPattern and allowed_usernames logic.
|
39
41
|
def mention_link_filter(text, _, _, _)
|
40
42
|
text.gsub(MentionPattern) do |match|
|
41
|
-
name =
|
43
|
+
name = ::Regexp.last_match(1)
|
42
44
|
case
|
43
45
|
when allowed_usernames && name == "all"
|
44
46
|
result[:mentioned_usernames] |= allowed_usernames
|
45
47
|
match.sub(
|
46
48
|
"@#{name}",
|
47
|
-
%
|
49
|
+
%(<a href="/" class="user-mention" title="#{name}">@#{name}</a>),
|
48
50
|
)
|
49
|
-
when allowed_usernames && !allowed_usernames.include?(name) || name == "all"
|
51
|
+
when (allowed_usernames && !allowed_usernames.include?(name)) || name == "all"
|
50
52
|
match
|
51
53
|
else
|
52
54
|
result[:mentioned_usernames] |= [name]
|
53
55
|
url = File.join(base_url, name)
|
54
56
|
match.sub(
|
55
57
|
"@#{name}",
|
56
|
-
%
|
58
|
+
%(<a href="#{url}" class="user-mention js-hovercard" title="#{name}" data-hovercard-target-type="user" data-hovercard-target-name="#{name}">@#{name}</a>),
|
57
59
|
)
|
58
60
|
end
|
59
61
|
end
|
@@ -66,7 +68,7 @@ module Qiita
|
|
66
68
|
end
|
67
69
|
|
68
70
|
def has_ancestor?(node, tags)
|
69
|
-
super || node.parent.parent && node.parent.parent["class"] == "code-lang"
|
71
|
+
super || (node.parent.parent && node.parent.parent["class"] == "code-lang")
|
70
72
|
end
|
71
73
|
end
|
72
74
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qiita
|
4
|
+
module Markdown
|
5
|
+
module Filters
|
6
|
+
class QiitaMarker < ::HTML::Pipeline::TextFilter
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
footnotes: true,
|
9
|
+
sourcepos: false,
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
# @return [Nokogiri::HTML::DocumentFragment]
|
13
|
+
def call
|
14
|
+
::Nokogiri::HTML.fragment(render(@text))
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# @param text [String]
|
20
|
+
# @return [String]
|
21
|
+
def render(text)
|
22
|
+
::QiitaMarker.render_html(text, qiita_marker_options, qiita_marker_extensions)
|
23
|
+
end
|
24
|
+
|
25
|
+
def qiita_marker_options
|
26
|
+
options_to_append = (options[:footnotes] ? [:FOOTNOTES] : [])
|
27
|
+
.concat(options[:sourcepos] ? [:SOURCEPOS] : [])
|
28
|
+
@qiita_marker_options ||= %i[
|
29
|
+
HARDBREAKS
|
30
|
+
UNSAFE
|
31
|
+
LIBERAL_HTML_TAG
|
32
|
+
STRIKETHROUGH_DOUBLE_TILDE
|
33
|
+
TABLE_PREFER_STYLE_ATTRIBUTES
|
34
|
+
CODE_DATA_METADATA
|
35
|
+
MENTION_NO_EMPHASIS
|
36
|
+
AUTOLINK_CLASS_NAME
|
37
|
+
].concat(options_to_append)
|
38
|
+
end
|
39
|
+
|
40
|
+
def qiita_marker_extensions
|
41
|
+
@qiita_marker_extensions ||= %i[
|
42
|
+
table
|
43
|
+
strikethrough
|
44
|
+
autolink
|
45
|
+
custom_block
|
46
|
+
]
|
47
|
+
end
|
48
|
+
|
49
|
+
def options
|
50
|
+
@options ||= DEFAULT_OPTIONS.merge(context[:markdown] || {})
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -54,7 +54,7 @@ module Qiita
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def call
|
57
|
-
outer = Nokogiri::HTML.fragment(%
|
57
|
+
outer = Nokogiri::HTML.fragment(%(<div class="code-frame" data-lang="#{language}">))
|
58
58
|
frame = outer.at("div")
|
59
59
|
frame.add_child(filename_node) if filename
|
60
60
|
frame.add_child(highlighted_node)
|
@@ -72,7 +72,7 @@ module Qiita
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def filename_node
|
75
|
-
%
|
75
|
+
%(<div class="code-lang"><span class="bold">#{filename}</span></div>)
|
76
76
|
end
|
77
77
|
|
78
78
|
def has_inline_php?
|
@@ -87,7 +87,7 @@ module Qiita
|
|
87
87
|
if specific_language && Rouge::Lexer.find(specific_language)
|
88
88
|
begin
|
89
89
|
highlight(specific_language).presence or raise
|
90
|
-
rescue
|
90
|
+
rescue StandardError
|
91
91
|
highlight(@default_language)
|
92
92
|
end
|
93
93
|
else
|
@@ -100,7 +100,7 @@ module Qiita
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def language_node
|
103
|
-
Nokogiri::HTML.fragment(%
|
103
|
+
Nokogiri::HTML.fragment(%(<div class="code-frame" data-lang="#{language}"></div>))
|
104
104
|
end
|
105
105
|
|
106
106
|
def specific_language
|
@@ -51,9 +51,7 @@ module Qiita
|
|
51
51
|
node.content.each_char.with_index do |char, index|
|
52
52
|
current_char_is_blank = char.strip.empty?
|
53
53
|
|
54
|
-
if !@previous_char_was_blank || !current_char_is_blank
|
55
|
-
@current_length += 1
|
56
|
-
end
|
54
|
+
@current_length += 1 if !@previous_char_was_blank || !current_char_is_blank
|
57
55
|
|
58
56
|
@previous_char_was_blank = current_char_is_blank
|
59
57
|
|