File: html_renderer.rb

package info (click to toggle)
ruby-commonmarker 0.23.10-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,456 kB
  • sloc: ansic: 10,575; ruby: 1,741; sh: 36; makefile: 22
file content (256 lines) | stat: -rw-r--r-- 6,174 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# frozen_string_literal: true

module CommonMarker
  class HtmlRenderer < Renderer
    def document(_)
      super
      out("</ol>\n</section>\n") if @written_footnote_ix
    end

    def header(node)
      block do
        out("<h", node.header_level, "#{sourcepos(node)}>", :children,
          "</h", node.header_level, ">")
      end
    end

    def paragraph(node)
      if @in_tight && node.parent.type != :blockquote
        out(:children)
      else
        block do
          container("<p#{sourcepos(node)}>", "</p>") do
            out(:children)
            if node.parent.type == :footnote_definition && node.next.nil?
              out(" ")
              out_footnote_backref
            end
          end
        end
      end
    end

    def list(node)
      old_in_tight = @in_tight
      @in_tight = node.list_tight

      block do
        if node.list_type == :bullet_list
          container("<ul#{sourcepos(node)}>\n", "</ul>") do
            out(:children)
          end
        else
          start = if node.list_start == 1
            "<ol#{sourcepos(node)}>\n"
          else
            "<ol start=\"#{node.list_start}\"#{sourcepos(node)}>\n"
          end
          container(start, "</ol>") do
            out(:children)
          end
        end
      end

      @in_tight = old_in_tight
    end

    def list_item(node)
      block do
        tasklist_data = tasklist(node)
        container("<li#{sourcepos(node)}#{tasklist_data}>#{" " if tasklist?(node)}", "</li>") do
          out(:children)
        end
      end
    end

    def tasklist(node)
      return "" unless tasklist?(node)

      state = if checked?(node)
        'checked="" disabled=""'
      else
        'disabled=""'
      end
      "><input type=\"checkbox\" #{state} /"
    end

    def blockquote(node)
      block do
        container("<blockquote#{sourcepos(node)}>\n", "</blockquote>") do
          out(:children)
        end
      end
    end

    def hrule(node)
      block do
        out("<hr#{sourcepos(node)} />")
      end
    end

    def code_block(node)
      block do
        if option_enabled?(:GITHUB_PRE_LANG)
          out("<pre#{sourcepos(node)}")
          out(' lang="', node.fence_info.split(/\s+/)[0], '"') if node.fence_info && !node.fence_info.empty?
          out("><code>")
        else
          out("<pre#{sourcepos(node)}><code")
          if node.fence_info && !node.fence_info.empty?
            out(' class="language-', node.fence_info.split(/\s+/)[0], '">')
          else
            out(">")
          end
        end
        out(escape_html(node.string_content))
        out("</code></pre>")
      end
    end

    def html(node)
      block do
        if option_enabled?(:UNSAFE)
          out(tagfilter(node.string_content))
        else
          out("<!-- raw HTML omitted -->")
        end
      end
    end

    def inline_html(node)
      if option_enabled?(:UNSAFE)
        out(tagfilter(node.string_content))
      else
        out("<!-- raw HTML omitted -->")
      end
    end

    def emph(_)
      out("<em>", :children, "</em>")
    end

    def strong(node)
      if node.parent&.type == :strong
        out(:children)
      else
        out("<strong>", :children, "</strong>")
      end
    end

    def link(node)
      out('<a href="', node.url.nil? ? "" : escape_href(node.url), '"')
      out(' title="', escape_html(node.title), '"') if node.title && !node.title.empty?
      out(">", :children, "</a>")
    end

    def image(node)
      out('<img src="', escape_href(node.url), '"')
      plain do
        out(' alt="', :children, '"')
      end
      out(' title="', escape_html(node.title), '"') if node.title && !node.title.empty?
      out(" />")
    end

    def text(node)
      out(escape_html(node.string_content))
    end

    def code(node)
      out("<code>")
      out(escape_html(node.string_content))
      out("</code>")
    end

    def linebreak(_node)
      out("<br />\n")
    end

    def softbreak(_)
      if option_enabled?(:HARDBREAKS)
        out("<br />\n")
      elsif option_enabled?(:NOBREAKS)
        out(" ")
      else
        out("\n")
      end
    end

    def table(node)
      @alignments = node.table_alignments
      @needs_close_tbody = false
      out("<table#{sourcepos(node)}>\n", :children)
      out("</tbody>\n") if @needs_close_tbody
      out("</table>\n")
    end

    def table_header(node)
      @column_index = 0

      @in_header = true
      out("<thead>\n<tr#{sourcepos(node)}>\n", :children, "</tr>\n</thead>\n")
      @in_header = false
    end

    def table_row(node)
      @column_index = 0
      if !@in_header && !@needs_close_tbody
        @needs_close_tbody = true
        out("<tbody>\n")
      end
      out("<tr#{sourcepos(node)}>\n", :children, "</tr>\n")
    end

    def table_cell(node)
      align = case @alignments[@column_index]
      when :left then ' align="left"'
      when :right then ' align="right"'
      when :center then ' align="center"'
      else; ""
      end
      out(@in_header ? "<th#{align}#{sourcepos(node)}>" : "<td#{align}#{sourcepos(node)}>", :children, @in_header ? "</th>\n" : "</td>\n")
      @column_index += 1
    end

    def strikethrough(_)
      out("<del>", :children, "</del>")
    end

    def footnote_reference(node)
      out("<sup class=\"footnote-ref\"><a href=\"#fn#{node.string_content}\" id=\"fnref#{node.string_content}\">#{node.string_content}</a></sup>")
    end

    def footnote_definition(_)
      unless @footnote_ix
        out("<section class=\"footnotes\">\n<ol>\n")
        @footnote_ix = 0
      end

      @footnote_ix += 1
      out("<li id=\"fn#{@footnote_ix}\">\n", :children)
      out("\n") if out_footnote_backref
      out("</li>\n")
      # </ol>
      # </section>
    end

    private

    def out_footnote_backref
      return false if @written_footnote_ix == @footnote_ix

      @written_footnote_ix = @footnote_ix

      out("<a href=\"#fnref#{@footnote_ix}\" class=\"footnote-backref\">↩</a>")
      true
    end

    def tasklist?(node)
      node.type_string == "tasklist"
    end

    def checked?(node)
      node.tasklist_item_checked?
    end
  end
end