markly 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/conduct.md +133 -0
  4. data/ext/markly/arena.c +9 -8
  5. data/ext/markly/autolink.c +217 -134
  6. data/ext/markly/blocks.c +27 -2
  7. data/ext/markly/cmark-gfm-core-extensions.h +11 -11
  8. data/ext/markly/cmark-gfm-extension_api.h +1 -0
  9. data/ext/markly/cmark-gfm.h +18 -2
  10. data/ext/markly/cmark.c +3 -3
  11. data/ext/markly/commonmark.c +19 -34
  12. data/ext/markly/extconf.rb +8 -1
  13. data/ext/markly/html.c +22 -6
  14. data/ext/markly/inlines.c +148 -51
  15. data/ext/markly/latex.c +6 -4
  16. data/ext/markly/man.c +7 -11
  17. data/ext/markly/map.c +11 -4
  18. data/ext/markly/map.h +5 -2
  19. data/ext/markly/markly.c +582 -586
  20. data/ext/markly/markly.h +1 -1
  21. data/ext/markly/node.c +76 -10
  22. data/ext/markly/node.h +42 -1
  23. data/ext/markly/parser.h +1 -0
  24. data/ext/markly/plaintext.c +12 -29
  25. data/ext/markly/references.c +1 -0
  26. data/ext/markly/render.c +15 -7
  27. data/ext/markly/scanners.c +13916 -10380
  28. data/ext/markly/scanners.h +8 -0
  29. data/ext/markly/scanners.re +47 -8
  30. data/ext/markly/strikethrough.c +1 -1
  31. data/ext/markly/table.c +81 -31
  32. data/ext/markly/xml.c +2 -1
  33. data/lib/markly/flags.rb +16 -0
  34. data/lib/markly/node/inspect.rb +59 -53
  35. data/lib/markly/node.rb +125 -58
  36. data/lib/markly/renderer/generic.rb +129 -124
  37. data/lib/markly/renderer/html.rb +294 -275
  38. data/lib/markly/version.rb +7 -1
  39. data/lib/markly.rb +36 -30
  40. data/license.md +39 -0
  41. data/readme.md +36 -0
  42. data.tar.gz.sig +0 -0
  43. metadata +61 -29
  44. metadata.gz.sig +0 -0
  45. data/bin/markly +0 -94
  46. data/lib/markly/markly.bundle +0 -0
@@ -1,282 +1,301 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Released under the MIT License.
4
+ # Copyright, 2015-2020, by Garen Torikian.
5
+ # Copyright, 2015, by Nick Wellnhofer.
6
+ # Copyright, 2017, by Yuki Izumi.
7
+ # Copyright, 2017-2019, by Ashe Connor.
8
+ # Copyright, 2018, by Michael Camilleri.
9
+ # Copyright, 2020-2023, by Samuel Williams.
10
+
3
11
  require_relative 'generic'
4
12
  require 'cgi'
5
13
 
6
14
  module Markly
7
- module Renderer
8
- class HTML < Generic
9
- def initialize(ids: false, tight: false, **options)
10
- super(**options)
11
-
12
- @ids = ids
13
- @section = nil
14
- @tight = tight
15
- end
16
-
17
- def document(_)
18
- @section = false
19
- super
20
- out("</ol>\n</section>\n") if @written_footnote_ix
21
- out("</section>") if @section
22
- end
23
-
24
- def id_for(node)
25
- if @ids
26
- id = node.to_plaintext.chomp.downcase.gsub(/\s+/, '-')
27
-
28
- return " id=\"#{CGI.escape_html id}\""
29
- end
30
- end
31
-
32
- def header(node)
33
- block do
34
- if @ids
35
- out('</section>') if @section
36
- @section = true
37
- out("<section#{id_for(node)}>")
38
- end
39
-
40
- out('<h', node.header_level, "#{source_position(node)}>", :children,
41
- '</h', node.header_level, '>')
42
- end
43
- end
44
-
45
- def paragraph(node)
46
- if @tight && node.parent.type != :blockquote
47
- out(:children)
48
- else
49
- block do
50
- container("<p#{source_position(node)}>", '</p>') do
51
- out(:children)
52
- if node.parent.type == :footnote_definition && node.next.nil?
53
- out(' ')
54
- out_footnote_backref
55
- end
56
- end
57
- end
58
- end
59
- end
60
-
61
- def list(node)
62
- old_tight = @tight
63
- @tight = node.list_tight
64
-
65
- block do
66
- if node.list_type == :bullet_list
67
- container("<ul#{source_position(node)}>\n", '</ul>') do
68
- out(:children)
69
- end
70
- else
71
- start = if node.list_start == 1
72
- "<ol#{source_position(node)}>\n"
73
- else
74
- "<ol start=\"#{node.list_start}\"#{source_position(node)}>\n"
75
- end
76
- container(start, '</ol>') do
77
- out(:children)
78
- end
79
- end
80
- end
81
-
82
- @tight = old_tight
83
- end
84
-
85
- def list_item(node)
86
- block do
87
- tasklist_data = tasklist(node)
88
- container("<li#{source_position(node)}#{tasklist_data}>#{' ' if tasklist?(node)}", '</li>') do
89
- out(:children)
90
- end
91
- end
92
- end
93
-
94
- def tasklist(node)
95
- return '' unless tasklist?(node)
96
-
97
- state = if checked?(node)
98
- 'checked="" disabled=""'
99
- else
100
- 'disabled=""'
101
- end
102
- "><input type=\"checkbox\" #{state} /"
103
- end
104
-
105
- def blockquote(node)
106
- block do
107
- container("<blockquote#{source_position(node)}>\n", '</blockquote>') do
108
- out(:children)
109
- end
110
- end
111
- end
112
-
113
- def hrule(node)
114
- block do
115
- out("<hr#{source_position(node)} />")
116
- end
117
- end
118
-
119
- def code_block(node)
120
- block do
121
- if flag_enabled?(GITHUB_PRE_LANG)
122
- out("<pre#{source_position(node)}")
123
- out(' lang="', node.fence_info.split(/\s+/)[0], '"') if node.fence_info && !node.fence_info.empty?
124
- out('><code>')
125
- else
126
- out("<pre#{source_position(node)}><code")
127
- if node.fence_info && !node.fence_info.empty?
128
- out(' class="language-', node.fence_info.split(/\s+/)[0], '">')
129
- else
130
- out('>')
131
- end
132
- end
133
- out(escape_html(node.string_content))
134
- out('</code></pre>')
135
- end
136
- end
137
-
138
- def html(node)
139
- block do
140
- if flag_enabled?(UNSAFE)
141
- out(tagfilter(node.string_content))
142
- else
143
- out('<!-- raw HTML omitted -->')
144
- end
145
- end
146
- end
147
-
148
- def inline_html(node)
149
- if flag_enabled?(UNSAFE)
150
- out(tagfilter(node.string_content))
151
- else
152
- out('<!-- raw HTML omitted -->')
153
- end
154
- end
155
-
156
- def emph(_)
157
- out('<em>', :children, '</em>')
158
- end
159
-
160
- def strong(_)
161
- out('<strong>', :children, '</strong>')
162
- end
163
-
164
- def link(node)
165
- out('<a href="', node.url.nil? ? '' : escape_href(node.url), '"')
166
- out(' title="', escape_html(node.title), '"') if node.title && !node.title.empty?
167
- out('>', :children, '</a>')
168
- end
169
-
170
- def image(node)
171
- out('<img src="', escape_href(node.url), '"')
172
- plain do
173
- out(' alt="', :children, '"')
174
- end
175
- out(' title="', escape_html(node.title), '"') if node.title && !node.title.empty?
176
- out(' />')
177
- end
178
-
179
- def text(node)
180
- out(escape_html(node.string_content))
181
- end
182
-
183
- def code(node)
184
- out('<code>')
185
- out(escape_html(node.string_content))
186
- out('</code>')
187
- end
188
-
189
- def linebreak(_node)
190
- out("<br />\n")
191
- end
192
-
193
- def softbreak(_)
194
- if flag_enabled?(HARD_BREAKS)
195
- out("<br />\n")
196
- elsif flag_enabled?(NO_BREAKS)
197
- out(' ')
198
- else
199
- out("\n")
200
- end
201
- end
202
-
203
- def table(node)
204
- @alignments = node.table_alignments
205
- @needs_close_tbody = false
206
- out("<table#{source_position(node)}>\n", :children)
207
- out("</tbody>\n") if @needs_close_tbody
208
- out("</table>\n")
209
- end
210
-
211
- def table_header(node)
212
- @column_index = 0
213
-
214
- @in_header = true
215
- out("<thead>\n<tr#{source_position(node)}>\n", :children, "</tr>\n</thead>\n")
216
- @in_header = false
217
- end
218
-
219
- def table_row(node)
220
- @column_index = 0
221
- if !@in_header && !@needs_close_tbody
222
- @needs_close_tbody = true
223
- out("<tbody>\n")
224
- end
225
- out("<tr#{source_position(node)}>\n", :children, "</tr>\n")
226
- end
227
-
228
- def table_cell(node)
229
- align = case @alignments[@column_index]
230
- when :left then ' align="left"'
231
- when :right then ' align="right"'
232
- when :center then ' align="center"'
233
- else; ''
234
- end
235
- out(@in_header ? "<th#{align}#{source_position(node)}>" : "<td#{align}#{source_position(node)}>", :children, @in_header ? "</th>\n" : "</td>\n")
236
- @column_index += 1
237
- end
238
-
239
- def strikethrough(_)
240
- out('<del>', :children, '</del>')
241
- end
242
-
243
- def footnote_reference(node)
244
- out("<sup class=\"footnote-ref\"><a href=\"#fn#{node.string_content}\" id=\"fnref#{node.string_content}\">#{node.string_content}</a></sup>")
245
- out(node.to_html)
246
- end
247
-
248
- def footnote_definition(_)
249
- unless @footnote_ix
250
- out("<section class=\"footnotes\" data-footnotes>\n<ol>\n")
251
- @footnote_ix = 0
252
- end
253
-
254
- @footnote_ix += 1
255
- out("<li id=\"fn#{@footnote_ix}\">\n", :children)
256
- out("\n") if out_footnote_backref
257
- out("</li>\n")
258
- # </ol>
259
- # </section>
260
- end
261
-
262
- private
263
-
264
- def out_footnote_backref
265
- return false if @written_footnote_ix == @footnote_ix
266
-
267
- @written_footnote_ix = @footnote_ix
268
-
269
- out("<a href=\"#fnref#{@footnote_ix}\" class=\"footnote-backref\">↩</a>")
270
- true
271
- end
272
-
273
- def tasklist?(node)
274
- node.type_string == 'tasklist'
275
- end
276
-
277
- def checked?(node)
278
- node.tasklist_item_checked?
279
- end
280
- end
281
- end
15
+ module Renderer
16
+ class HTML < Generic
17
+ def initialize(ids: false, tight: false, **options)
18
+ super(**options)
19
+
20
+ @ids = ids
21
+ @section = nil
22
+ @tight = tight
23
+
24
+ @footnotes = {}
25
+ end
26
+
27
+ def document(_)
28
+ @section = false
29
+ super
30
+ out("</ol>\n</section>\n") if @written_footnote_ix
31
+ out("</section>") if @section
32
+ end
33
+
34
+ def id_for(node)
35
+ if @ids
36
+ id = node.to_plaintext.chomp.downcase.gsub(/\s+/, '-')
37
+
38
+ return " id=\"#{CGI.escape_html id}\""
39
+ end
40
+ end
41
+
42
+ def header(node)
43
+ block do
44
+ if @ids
45
+ out('</section>') if @section
46
+ @section = true
47
+ out("<section#{id_for(node)}>")
48
+ end
49
+
50
+ out('<h', node.header_level, "#{source_position(node)}>", :children,
51
+ '</h', node.header_level, '>')
52
+ end
53
+ end
54
+
55
+ def paragraph(node)
56
+ if @tight && node.parent.type != :blockquote
57
+ out(:children)
58
+ else
59
+ block do
60
+ container("<p#{source_position(node)}>", '</p>') do
61
+ out(:children)
62
+ if node.parent.type == :footnote_definition && node.next.nil?
63
+ out(' ')
64
+ out_footnote_backref
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ def list(node)
72
+ old_tight = @tight
73
+ @tight = node.list_tight
74
+
75
+ block do
76
+ if node.list_type == :bullet_list
77
+ container("<ul#{source_position(node)}>\n", '</ul>') do
78
+ out(:children)
79
+ end
80
+ else
81
+ start = if node.list_start == 1
82
+ "<ol#{source_position(node)}>\n"
83
+ else
84
+ "<ol start=\"#{node.list_start}\"#{source_position(node)}>\n"
85
+ end
86
+ container(start, '</ol>') do
87
+ out(:children)
88
+ end
89
+ end
90
+ end
91
+
92
+ @tight = old_tight
93
+ end
94
+
95
+ def list_item(node)
96
+ block do
97
+ tasklist_data = tasklist(node)
98
+ container("<li#{source_position(node)}#{tasklist_data}>#{' ' if tasklist?(node)}", '</li>') do
99
+ out(:children)
100
+ end
101
+ end
102
+ end
103
+
104
+ def tasklist(node)
105
+ return '' unless tasklist?(node)
106
+
107
+ state = if checked?(node)
108
+ 'checked="" disabled=""'
109
+ else
110
+ 'disabled=""'
111
+ end
112
+ "><input type=\"checkbox\" #{state} /"
113
+ end
114
+
115
+ def blockquote(node)
116
+ block do
117
+ container("<blockquote#{source_position(node)}>\n", '</blockquote>') do
118
+ out(:children)
119
+ end
120
+ end
121
+ end
122
+
123
+ def hrule(node)
124
+ block do
125
+ out("<hr#{source_position(node)} />")
126
+ end
127
+ end
128
+
129
+ def code_block(node)
130
+ block do
131
+ if flag_enabled?(GITHUB_PRE_LANG)
132
+ out("<pre#{source_position(node)}")
133
+ out(' lang="', node.fence_info.split(/\s+/)[0], '"') if node.fence_info && !node.fence_info.empty?
134
+ out('><code>')
135
+ else
136
+ out("<pre#{source_position(node)}><code")
137
+ if node.fence_info && !node.fence_info.empty?
138
+ out(' class="language-', node.fence_info.split(/\s+/)[0], '">')
139
+ else
140
+ out('>')
141
+ end
142
+ end
143
+ out(escape_html(node.string_content))
144
+ out('</code></pre>')
145
+ end
146
+ end
147
+
148
+ def html(node)
149
+ block do
150
+ if flag_enabled?(UNSAFE)
151
+ out(tagfilter(node.string_content))
152
+ else
153
+ out('<!-- raw HTML omitted -->')
154
+ end
155
+ end
156
+ end
157
+
158
+ def inline_html(node)
159
+ if flag_enabled?(UNSAFE)
160
+ out(tagfilter(node.string_content))
161
+ else
162
+ out('<!-- raw HTML omitted -->')
163
+ end
164
+ end
165
+
166
+ def emph(node)
167
+ out('<em>', :children, '</em>')
168
+ end
169
+
170
+ def strong(node)
171
+ if node.parent.nil? || node.parent.type == node.type
172
+ out(:children)
173
+ else
174
+ out('<strong>', :children, '</strong>')
175
+ end
176
+ end
177
+
178
+ def link(node)
179
+ out('<a href="', node.url.nil? ? '' : escape_href(node.url), '"')
180
+ out(' title="', escape_html(node.title), '"') if node.title && !node.title.empty?
181
+ out('>', :children, '</a>')
182
+ end
183
+
184
+ def image(node)
185
+ out('<img src="', escape_href(node.url), '"')
186
+ plain do
187
+ out(' alt="', :children, '"')
188
+ end
189
+ out(' title="', escape_html(node.title), '"') if node.title && !node.title.empty?
190
+ out(' />')
191
+ end
192
+
193
+ def text(node)
194
+ out(escape_html(node.string_content))
195
+ end
196
+
197
+ def code(node)
198
+ out('<code>')
199
+ out(escape_html(node.string_content))
200
+ out('</code>')
201
+ end
202
+
203
+ def linebreak(_node)
204
+ out("<br />\n")
205
+ end
206
+
207
+ def softbreak(_)
208
+ if flag_enabled?(HARD_BREAKS)
209
+ out("<br />\n")
210
+ elsif flag_enabled?(NO_BREAKS)
211
+ out(' ')
212
+ else
213
+ out("\n")
214
+ end
215
+ end
216
+
217
+ def table(node)
218
+ @alignments = node.table_alignments
219
+ @needs_close_tbody = false
220
+ out("<table#{source_position(node)}>\n", :children)
221
+ out("</tbody>\n") if @needs_close_tbody
222
+ out("</table>\n")
223
+ end
224
+
225
+ def table_header(node)
226
+ @column_index = 0
227
+
228
+ @in_header = true
229
+ out("<thead>\n<tr#{source_position(node)}>\n", :children, "</tr>\n</thead>\n")
230
+ @in_header = false
231
+ end
232
+
233
+ def table_row(node)
234
+ @column_index = 0
235
+ if !@in_header && !@needs_close_tbody
236
+ @needs_close_tbody = true
237
+ out("<tbody>\n")
238
+ end
239
+ out("<tr#{source_position(node)}>\n", :children, "</tr>\n")
240
+ end
241
+
242
+ def table_cell(node)
243
+ align = case @alignments[@column_index]
244
+ when :left then ' align="left"'
245
+ when :right then ' align="right"'
246
+ when :center then ' align="center"'
247
+ else; ''
248
+ end
249
+ out(@in_header ? "<th#{align}#{source_position(node)}>" : "<td#{align}#{source_position(node)}>", :children, @in_header ? "</th>\n" : "</td>\n")
250
+ @column_index += 1
251
+ end
252
+
253
+ def strikethrough(_)
254
+ out('<del>', :children, '</del>')
255
+ end
256
+
257
+ def footnote_reference(node)
258
+ label = node.parent_footnote_def.string_content
259
+
260
+ out("<sup class=\"footnote-ref\"><a href=\"#fn-#{label}\" id=\"fnref-#{label}\" data-footnote-ref>#{node.string_content}</a></sup>")
261
+ # out(node.to_html)
262
+ end
263
+
264
+ def footnote_definition(node)
265
+ unless @footnote_ix
266
+ out("<section class=\"footnotes\" data-footnotes>\n<ol>\n")
267
+ @footnote_ix = 0
268
+ end
269
+
270
+ @footnote_ix += 1
271
+ label = node.string_content
272
+ @footnotes[@footnote_ix] = label
273
+
274
+ out("<li id=\"fn-#{label}\">\n", :children)
275
+ out("\n") if out_footnote_backref
276
+ out("</li>\n")
277
+ # </ol>
278
+ # </section>
279
+ end
280
+
281
+ private
282
+
283
+ def out_footnote_backref
284
+ return false if @written_footnote_ix == @footnote_ix
285
+
286
+ @written_footnote_ix = @footnote_ix
287
+
288
+ out("<a href=\"#fnref-#{@footnotes[@footnote_ix]}\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"#{@footnote_ix}\" aria-label=\"Back to reference #{@footnote_ix}\">↩</a>")
289
+ true
290
+ end
291
+
292
+ def tasklist?(node)
293
+ node.type_string == 'tasklist'
294
+ end
295
+
296
+ def checked?(node)
297
+ node.tasklist_item_checked?
298
+ end
299
+ end
300
+ end
282
301
  end
@@ -1,5 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Released under the MIT License.
4
+ # Copyright, 2015-2020, by Garen Torikian.
5
+ # Copyright, 2016-2017, by Yuki Izumi.
6
+ # Copyright, 2017-2018, by Ashe Connor.
7
+ # Copyright, 2020-2023, by Samuel Williams.
8
+
3
9
  module Markly
4
- VERSION = '0.7.0'
10
+ VERSION = '0.8.0'
5
11
  end