File: 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 (135 lines) | stat: -rw-r--r-- 2,616 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
# frozen_string_literal: true

require "set"
require "stringio"

module CommonMarker
  class Renderer
    attr_accessor :in_tight, :warnings, :in_plain

    def initialize(options: :DEFAULT, extensions: [])
      @opts = Config.process_options(options, :render)
      @stream = StringIO.new(+"")
      @need_blocksep = false
      @warnings = Set.new([])
      @in_tight = false
      @in_plain = false
      @tagfilter = extensions.include?(:tagfilter)
    end

    def out(*args)
      args.each do |arg|
        case arg
        when :children
          @node.each { |child| out(child) }
        when Array
          arg.each { |x| render(x) }
        when Node
          render(arg)
        else
          @stream.write(arg)
        end
      end
    end

    def render(node)
      @node = node
      if node.type == :document
        document(node)
        @stream.string
      elsif @in_plain && node.type != :text && node.type != :softbreak
        node.each { |child| render(child) }
      else
        begin
          send(node.type, node)
        rescue NoMethodError => e
          @warnings.add("WARNING: #{node.type} not implemented.")
          raise e
        end
      end
    end

    def document(_node)
      out(:children)
    end

    def code_block(node)
      code_block(node)
    end

    def reference_def(_node); end

    def cr
      return if @stream.string.empty? || @stream.string[-1] == "\n"

      out("\n")
    end

    def blocksep
      out("\n")
    end

    def containersep
      cr unless @in_tight
    end

    def block
      cr
      yield
      cr
    end

    def container(starter, ender)
      out(starter)
      yield
      out(ender)
    end

    def plain
      old_in_plain = @in_plain
      @in_plain = true
      yield
      @in_plain = old_in_plain
    end

    private

    def escape_href(str)
      @node.html_escape_href(str)
    end

    def escape_html(str)
      @node.html_escape_html(str)
    end

    def tagfilter(str)
      if @tagfilter
        str.gsub(
          %r{
            <
            (
            title|textarea|style|xmp|iframe|
            noembed|noframes|script|plaintext
            )
            (?=\s|>|/>)
          }xi,
          '&lt;\1',
        )
      else
        str
      end
    end

    def sourcepos(node)
      return "" unless option_enabled?(:SOURCEPOS)

      s = node.sourcepos
      " data-sourcepos=\"#{s[:start_line]}:#{s[:start_column]}-" \
        "#{s[:end_line]}:#{s[:end_column]}\""
    end

    def option_enabled?(opt)
      (@opts & CommonMarker::Config::OPTS.dig(:render, opt)) != 0
    end
  end
end