Class: CodeSlide::Analyzer

Inherits:
Object
  • Object
show all
Defined in:
lib/code_slide/analyzer.rb

Constant Summary collapse

ENTITY =
{
  'lt'   => '<',
  'gt'   => '>',
  'amp'  => '&'
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(text, options = {}) ⇒ Analyzer

Returns a new instance of Analyzer.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/code_slide/analyzer.rb', line 13

def initialize(text, options = {})
  @styles = options[:styles] ||
            (options[:theme] && ThemeManager.load_theme(options[:theme])) ||
            ThemeManager.load_theme(:light)

  line_numbers = options[:line_numbers] && :inline
  bold_every   = options[:bold_every] || nil
  line_start   = options[:line_number_start] || 1

  text = _preprocess_text(text)
  _compute_gutter(line_numbers, line_start)
  html = _generate_html(text, options[:lang],
                        line_numbers, line_start, bold_every)

  _format_html(html)
end

Instance Attribute Details

#elementsObject (readonly)

Returns the value of attribute elements.



7
8
9
# File 'lib/code_slide/analyzer.rb', line 7

def elements
  @elements
end

#gutter_widthObject (readonly)

Returns the value of attribute gutter_width.



11
12
13
# File 'lib/code_slide/analyzer.rb', line 11

def gutter_width
  @gutter_width
end

#heightObject (readonly)

Returns the value of attribute height.



9
10
11
# File 'lib/code_slide/analyzer.rb', line 9

def height
  @height
end

#stylesObject (readonly)

Returns the value of attribute styles.



10
11
12
# File 'lib/code_slide/analyzer.rb', line 10

def styles
  @styles
end

#widthObject (readonly)

Returns the value of attribute width.



8
9
10
# File 'lib/code_slide/analyzer.rb', line 8

def width
  @width
end

Instance Method Details

#_compute_gutter(line_numbers, line_start) ⇒ Object



30
31
32
33
34
35
36
# File 'lib/code_slide/analyzer.rb', line 30

def _compute_gutter(line_numbers, line_start)
  @gutter_width = 0
  return unless line_numbers

  max = line_start + height
  @gutter_width = Math.log10(max + 1).ceil + 1
end

#_format(parent) ⇒ Object



69
70
71
72
73
74
75
76
# File 'lib/code_slide/analyzer.rb', line 69

def _format(parent)
  style = _lookup_class(parent['class'])
  @current_attributes.push(_merge_style(style))
  parent.children.each { |child| _format_node(child) }
ensure
  attrs = @current_attributes.pop
  @elements.last[:text] << attrs[:after] if attrs[:after]
end

#_format_html(html) ⇒ Object



60
61
62
63
64
65
66
67
# File 'lib/code_slide/analyzer.rb', line 60

def _format_html(html)
  doc = Nokogiri::HTML.fragment(html)

  @current_attributes = [{}]
  @elements = []

  _format(doc)
end

#_format_node(node) ⇒ Object



78
79
80
81
82
83
84
85
86
# File 'lib/code_slide/analyzer.rb', line 78

def _format_node(node)
  if node.text?
    @elements << _style(node)
  elsif node.element?
    _format(node)
  else
    raise 'unknown node type'
  end
end

#_generate_html(text, lang, line_numbers, line_start, bold_every) ⇒ Object



38
39
40
41
42
43
44
45
46
# File 'lib/code_slide/analyzer.rb', line 38

def _generate_html(text, lang, line_numbers, line_start, bold_every)
  CodeRay.scan(text, lang).
    html(line_numbers: line_numbers,
         line_number_anchors: false,
         line_number_start: line_start,
         bold_every: bold_every).
    gsub(%r{</strong>}, '</span>').
    gsub(/<strong>/, '<span class="highlight">')
end

#_lookup_class(class_name) ⇒ Object



109
110
111
112
113
114
115
116
117
# File 'lib/code_slide/analyzer.rb', line 109

def _lookup_class(class_name)
  return (@styles[:default] || {}) unless class_name

  class_name && (@styles[class_name] || @styles[class_name.to_sym]) ||
    begin
      warn "no definition for #{class_name}"
      { class: class_name }
    end
end

#_merge_style(style) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/code_slide/analyzer.rb', line 94

def _merge_style(style)
  styles = @current_attributes.last[:styles]
  if style[:styles]
    styles ||= []
    styles = [*style[:styles], *styles]
  end

  @current_attributes.last.merge(style).tap do |attrs|
    attrs[:styles] = styles if styles

    # 'after' is never inherited
    attrs[:after] = nil unless style[:after]
  end
end

#_preprocess_text(text) ⇒ Object

removes leading and trailing blank lines



49
50
51
52
53
54
55
56
57
58
# File 'lib/code_slide/analyzer.rb', line 49

def _preprocess_text(text)
  lines = text.lines.map(&:rstrip)
  lines.delete_at(0) while lines[0].empty?
  lines.delete_at(-1) while lines[-1].empty?

  @width = lines.map(&:length).max
  @height = lines.length

  lines.join("\n")
end

#_style(node) ⇒ Object



88
89
90
91
92
# File 'lib/code_slide/analyzer.rb', line 88

def _style(node)
  text = _text(node)
  attrs = @current_attributes.last
  attrs.merge(text: text)
end

#_text(text) ⇒ Object



125
126
127
128
129
130
131
132
# File 'lib/code_slide/analyzer.rb', line 125

def _text(text)
  text.to_s.
    tr(' ', "\u00A0").
    gsub(/\&(.*?);/) do |match|
      entity = Regexp.last_match(1)
      ENTITY[entity.downcase] || match
    end
end