markly 0.5.1 → 0.7.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/ext/markly/blocks.c +13 -2
- data/ext/markly/cmark-gfm_version.h +2 -2
- data/ext/markly/commonmark.c +14 -4
- data/ext/markly/ext_scanners.c +360 -640
- data/ext/markly/extconf.rb +10 -2
- data/ext/markly/footnotes.c +23 -0
- data/ext/markly/footnotes.h +2 -0
- data/ext/markly/html.c +40 -19
- data/ext/markly/inlines.c +69 -11
- data/ext/markly/markly.c +1 -1
- data/ext/markly/node.h +7 -0
- data/ext/markly/scanners.c +9484 -19346
- data/ext/markly/table.c +71 -52
- data/lib/markly/markly.bundle +0 -0
- data/lib/markly/renderer/generic.rb +131 -0
- data/lib/markly/renderer/html.rb +282 -0
- data/lib/markly/version.rb +1 -1
- data/lib/markly.rb +3 -3
- metadata +6 -5
- data/lib/markly/renderer/html_renderer.rb +0 -277
- data/lib/markly/renderer.rb +0 -133
@@ -1,277 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'cgi'
|
4
|
-
|
5
|
-
module Markly
|
6
|
-
class HTMLRenderer < Renderer
|
7
|
-
def initialize(ids: false, **options)
|
8
|
-
super(**options)
|
9
|
-
|
10
|
-
@ids = ids
|
11
|
-
@section = nil
|
12
|
-
end
|
13
|
-
|
14
|
-
def document(_)
|
15
|
-
@section = false
|
16
|
-
super
|
17
|
-
out("</ol>\n</section>\n") if @written_footnote_ix
|
18
|
-
out("</section>") if @section
|
19
|
-
end
|
20
|
-
|
21
|
-
def id_for(node)
|
22
|
-
if @ids
|
23
|
-
id = node.to_plaintext.chomp.downcase.gsub(/\s+/, '-')
|
24
|
-
|
25
|
-
return " id=\"#{CGI.escape_html id}\""
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def header(node)
|
30
|
-
block do
|
31
|
-
out('</section>') if @section
|
32
|
-
@section = true
|
33
|
-
out("<section #{id_for(node)}>")
|
34
|
-
out('<h', node.header_level, "#{source_position(node)}>", :children,
|
35
|
-
'</h', node.header_level, '>')
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def paragraph(node)
|
40
|
-
if @in_tight && node.parent.type != :blockquote
|
41
|
-
out(:children)
|
42
|
-
else
|
43
|
-
block do
|
44
|
-
container("<p#{source_position(node)}>", '</p>') do
|
45
|
-
out(:children)
|
46
|
-
if node.parent.type == :footnote_definition && node.next.nil?
|
47
|
-
out(' ')
|
48
|
-
out_footnote_backref
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def list(node)
|
56
|
-
old_in_tight = @in_tight
|
57
|
-
@in_tight = node.list_tight
|
58
|
-
|
59
|
-
block do
|
60
|
-
if node.list_type == :bullet_list
|
61
|
-
container("<ul#{source_position(node)}>\n", '</ul>') do
|
62
|
-
out(:children)
|
63
|
-
end
|
64
|
-
else
|
65
|
-
start = if node.list_start == 1
|
66
|
-
"<ol#{source_position(node)}>\n"
|
67
|
-
else
|
68
|
-
"<ol start=\"#{node.list_start}\"#{source_position(node)}>\n"
|
69
|
-
end
|
70
|
-
container(start, '</ol>') do
|
71
|
-
out(:children)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
@in_tight = old_in_tight
|
77
|
-
end
|
78
|
-
|
79
|
-
def list_item(node)
|
80
|
-
block do
|
81
|
-
tasklist_data = tasklist(node)
|
82
|
-
container("<li#{source_position(node)}#{tasklist_data}>#{' ' if tasklist?(node)}", '</li>') do
|
83
|
-
out(:children)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def tasklist(node)
|
89
|
-
return '' unless tasklist?(node)
|
90
|
-
|
91
|
-
state = if checked?(node)
|
92
|
-
'checked="" disabled=""'
|
93
|
-
else
|
94
|
-
'disabled=""'
|
95
|
-
end
|
96
|
-
"><input type=\"checkbox\" #{state} /"
|
97
|
-
end
|
98
|
-
|
99
|
-
def blockquote(node)
|
100
|
-
block do
|
101
|
-
container("<blockquote#{source_position(node)}>\n", '</blockquote>') do
|
102
|
-
out(:children)
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def hrule(node)
|
108
|
-
block do
|
109
|
-
out("<hr#{source_position(node)} />")
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def code_block(node)
|
114
|
-
block do
|
115
|
-
if flag_enabled?(GITHUB_PRE_LANG)
|
116
|
-
out("<pre#{source_position(node)}")
|
117
|
-
out(' lang="', node.fence_info.split(/\s+/)[0], '"') if node.fence_info && !node.fence_info.empty?
|
118
|
-
out('><code>')
|
119
|
-
else
|
120
|
-
out("<pre#{source_position(node)}><code")
|
121
|
-
if node.fence_info && !node.fence_info.empty?
|
122
|
-
out(' class="language-', node.fence_info.split(/\s+/)[0], '">')
|
123
|
-
else
|
124
|
-
out('>')
|
125
|
-
end
|
126
|
-
end
|
127
|
-
out(escape_html(node.string_content))
|
128
|
-
out('</code></pre>')
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def html(node)
|
133
|
-
block do
|
134
|
-
if flag_enabled?(UNSAFE)
|
135
|
-
out(tagfilter(node.string_content))
|
136
|
-
else
|
137
|
-
out('<!-- raw HTML omitted -->')
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def inline_html(node)
|
143
|
-
if flag_enabled?(UNSAFE)
|
144
|
-
out(tagfilter(node.string_content))
|
145
|
-
else
|
146
|
-
out('<!-- raw HTML omitted -->')
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def emph(_)
|
151
|
-
out('<em>', :children, '</em>')
|
152
|
-
end
|
153
|
-
|
154
|
-
def strong(_)
|
155
|
-
out('<strong>', :children, '</strong>')
|
156
|
-
end
|
157
|
-
|
158
|
-
def link(node)
|
159
|
-
out('<a href="', node.url.nil? ? '' : escape_href(node.url), '"')
|
160
|
-
out(' title="', escape_html(node.title), '"') if node.title && !node.title.empty?
|
161
|
-
out('>', :children, '</a>')
|
162
|
-
end
|
163
|
-
|
164
|
-
def image(node)
|
165
|
-
out('<img src="', escape_href(node.url), '"')
|
166
|
-
plain do
|
167
|
-
out(' alt="', :children, '"')
|
168
|
-
end
|
169
|
-
out(' title="', escape_html(node.title), '"') if node.title && !node.title.empty?
|
170
|
-
out(' />')
|
171
|
-
end
|
172
|
-
|
173
|
-
def text(node)
|
174
|
-
out(escape_html(node.string_content))
|
175
|
-
end
|
176
|
-
|
177
|
-
def code(node)
|
178
|
-
out('<code>')
|
179
|
-
out(escape_html(node.string_content))
|
180
|
-
out('</code>')
|
181
|
-
end
|
182
|
-
|
183
|
-
def linebreak(_node)
|
184
|
-
out("<br />\n")
|
185
|
-
end
|
186
|
-
|
187
|
-
def softbreak(_)
|
188
|
-
if flag_enabled?(HARD_BREAKS)
|
189
|
-
out("<br />\n")
|
190
|
-
elsif flag_enabled?(NO_BREAKS)
|
191
|
-
out(' ')
|
192
|
-
else
|
193
|
-
out("\n")
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
def table(node)
|
198
|
-
@alignments = node.table_alignments
|
199
|
-
@needs_close_tbody = false
|
200
|
-
out("<table#{source_position(node)}>\n", :children)
|
201
|
-
out("</tbody>\n") if @needs_close_tbody
|
202
|
-
out("</table>\n")
|
203
|
-
end
|
204
|
-
|
205
|
-
def table_header(node)
|
206
|
-
@column_index = 0
|
207
|
-
|
208
|
-
@in_header = true
|
209
|
-
out("<thead>\n<tr#{source_position(node)}>\n", :children, "</tr>\n</thead>\n")
|
210
|
-
@in_header = false
|
211
|
-
end
|
212
|
-
|
213
|
-
def table_row(node)
|
214
|
-
@column_index = 0
|
215
|
-
if !@in_header && !@needs_close_tbody
|
216
|
-
@needs_close_tbody = true
|
217
|
-
out("<tbody>\n")
|
218
|
-
end
|
219
|
-
out("<tr#{source_position(node)}>\n", :children, "</tr>\n")
|
220
|
-
end
|
221
|
-
|
222
|
-
def table_cell(node)
|
223
|
-
align = case @alignments[@column_index]
|
224
|
-
when :left then ' align="left"'
|
225
|
-
when :right then ' align="right"'
|
226
|
-
when :center then ' align="center"'
|
227
|
-
else; ''
|
228
|
-
end
|
229
|
-
out(@in_header ? "<th#{align}#{source_position(node)}>" : "<td#{align}#{source_position(node)}>", :children, @in_header ? "</th>\n" : "</td>\n")
|
230
|
-
@column_index += 1
|
231
|
-
end
|
232
|
-
|
233
|
-
def strikethrough(_)
|
234
|
-
out('<del>', :children, '</del>')
|
235
|
-
end
|
236
|
-
|
237
|
-
def footnote_reference(node)
|
238
|
-
out("<sup class=\"footnote-ref\"><a href=\"#fn#{node.string_content}\" id=\"fnref#{node.string_content}\">#{node.string_content}</a></sup>")
|
239
|
-
end
|
240
|
-
|
241
|
-
def footnote_definition(_)
|
242
|
-
unless @footnote_ix
|
243
|
-
out("<section class=\"footnotes\">\n<ol>\n")
|
244
|
-
@footnote_ix = 0
|
245
|
-
end
|
246
|
-
|
247
|
-
@footnote_ix += 1
|
248
|
-
out("<li id=\"fn#{@footnote_ix}\">\n", :children)
|
249
|
-
out("\n") if out_footnote_backref
|
250
|
-
out("</li>\n")
|
251
|
-
# </ol>
|
252
|
-
# </section>
|
253
|
-
end
|
254
|
-
|
255
|
-
private
|
256
|
-
|
257
|
-
def out_footnote_backref
|
258
|
-
return false if @written_footnote_ix == @footnote_ix
|
259
|
-
|
260
|
-
@written_footnote_ix = @footnote_ix
|
261
|
-
|
262
|
-
out("<a href=\"#fnref#{@footnote_ix}\" class=\"footnote-backref\">↩</a>")
|
263
|
-
true
|
264
|
-
end
|
265
|
-
|
266
|
-
def tasklist?(node)
|
267
|
-
node.type_string == 'tasklist'
|
268
|
-
end
|
269
|
-
|
270
|
-
def checked?(node)
|
271
|
-
node.tasklist_item_checked?
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
# Legacy.
|
276
|
-
HtmlRenderer = HTMLRenderer
|
277
|
-
end
|
data/lib/markly/renderer.rb
DELETED
@@ -1,133 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'set'
|
4
|
-
require 'stringio'
|
5
|
-
|
6
|
-
module Markly
|
7
|
-
class Renderer
|
8
|
-
attr_accessor :in_tight, :warnings, :in_plain
|
9
|
-
def initialize(flags: DEFAULT, extensions: [])
|
10
|
-
@flags = flags
|
11
|
-
@stream = StringIO.new(+'')
|
12
|
-
@need_blocksep = false
|
13
|
-
@warnings = Set.new
|
14
|
-
@in_tight = false
|
15
|
-
@in_plain = false
|
16
|
-
@tagfilter = extensions.include?(:tagfilter)
|
17
|
-
end
|
18
|
-
|
19
|
-
def out(*args)
|
20
|
-
args.each do |arg|
|
21
|
-
if arg == :children
|
22
|
-
@node.each { |child| out(child) }
|
23
|
-
elsif arg.is_a?(Array)
|
24
|
-
arg.each { |x| render(x) }
|
25
|
-
elsif arg.is_a?(Node)
|
26
|
-
render(arg)
|
27
|
-
else
|
28
|
-
@stream.write(arg)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def render(node)
|
34
|
-
@node = node
|
35
|
-
if node.type == :document
|
36
|
-
document(node)
|
37
|
-
@stream.string
|
38
|
-
elsif @in_plain && node.type != :text && node.type != :softbreak
|
39
|
-
node.each { |child| render(child) }
|
40
|
-
else
|
41
|
-
begin
|
42
|
-
send(node.type, node)
|
43
|
-
rescue NoMethodError => e
|
44
|
-
@warnings.add("WARNING: #{node.type} not implemented.")
|
45
|
-
raise e
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def document(_node)
|
51
|
-
out(:children)
|
52
|
-
end
|
53
|
-
|
54
|
-
def code_block(node)
|
55
|
-
code_block(node)
|
56
|
-
end
|
57
|
-
|
58
|
-
def reference_def(_node); end
|
59
|
-
|
60
|
-
def cr
|
61
|
-
return if @stream.string.empty? || @stream.string[-1] == "\n"
|
62
|
-
|
63
|
-
out("\n")
|
64
|
-
end
|
65
|
-
|
66
|
-
def blocksep
|
67
|
-
out("\n")
|
68
|
-
end
|
69
|
-
|
70
|
-
def containersep
|
71
|
-
cr unless @in_tight
|
72
|
-
end
|
73
|
-
|
74
|
-
def block
|
75
|
-
cr
|
76
|
-
yield
|
77
|
-
cr
|
78
|
-
end
|
79
|
-
|
80
|
-
def container(starter, ender)
|
81
|
-
out(starter)
|
82
|
-
yield
|
83
|
-
out(ender)
|
84
|
-
end
|
85
|
-
|
86
|
-
def plain
|
87
|
-
old_in_plain = @in_plain
|
88
|
-
@in_plain = true
|
89
|
-
yield
|
90
|
-
@in_plain = old_in_plain
|
91
|
-
end
|
92
|
-
|
93
|
-
private
|
94
|
-
|
95
|
-
def escape_href(str)
|
96
|
-
@node.html_escape_href(str)
|
97
|
-
end
|
98
|
-
|
99
|
-
def escape_html(str)
|
100
|
-
@node.html_escape_html(str)
|
101
|
-
end
|
102
|
-
|
103
|
-
def tagfilter(str)
|
104
|
-
if @tagfilter
|
105
|
-
str.gsub(
|
106
|
-
%r{
|
107
|
-
<
|
108
|
-
(
|
109
|
-
title|textarea|style|xmp|iframe|
|
110
|
-
noembed|noframes|script|plaintext
|
111
|
-
)
|
112
|
-
(?=\s|>|/>)
|
113
|
-
}xi,
|
114
|
-
'<\1'
|
115
|
-
)
|
116
|
-
else
|
117
|
-
str
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def source_position(node)
|
122
|
-
return '' unless flag_enabled?(SOURCE_POSITION)
|
123
|
-
|
124
|
-
s = node.source_position
|
125
|
-
" data-sourcepos=\"#{s[:start_line]}:#{s[:start_column]}-" \
|
126
|
-
"#{s[:end_line]}:#{s[:end_column]}\""
|
127
|
-
end
|
128
|
-
|
129
|
-
def flag_enabled?(flag)
|
130
|
-
(@flags & flag) != 0
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|