Class: Papercraft::Compiler

Inherits:
Sirop::Sourcifier
  • Object
show all
Defined in:
lib/papercraft/compiler.rb

Defined Under Namespace

Modules: AuxMethods

Constant Summary collapse

DEFER_PREFIX_EMPTY =
"; __parts__ = []"
DEFER_PREFIX_NOT_EMPTY =
"; __parts__ = [__buffer__.dup]; __buffer__.clear"
DEFER_POSTFIX =
";__parts__.each { |p| p.is_a?(Proc) ? p.(__buffer__) : (__buffer__ << p) }"

Instance Method Summary collapse

Constructor Details

#initializeCompiler

Returns a new instance of Compiler.



36
37
38
39
# File 'lib/papercraft/compiler.rb', line 36

def initialize
  super
  @html_buffer = +''
end

Instance Method Details

#compile(node) ⇒ Object



41
42
43
44
45
46
47
48
49
# File 'lib/papercraft/compiler.rb', line 41

def compile(node)
  @root_node = node
  inject_buffer_parameter(node)

  @buffer.clear
  @html_buffer.clear
  visit(node)
  @buffer
end

#embed_visit(node, pre = '', post = '') ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/papercraft/compiler.rb', line 55

def embed_visit(node, pre = '', post = '')
  tmp_last_loc_start = @last_loc_start
  tmp_last_loc_end = @last_loc_end
  @last_loc_start = loc_start(node.location)
  @last_loc_end = loc_end(node.location)

  @embed_mode = true
  tmp_buffer = @buffer
  @buffer = +''
  visit(node)
  @embed_mode = false
  @html_buffer << "#{pre}#{@buffer}#{post}"
  @buffer = tmp_buffer

  @last_loc_start = tmp_last_loc_start
  @last_loc_end = tmp_last_loc_end
end

#emit_code(loc, semicolon: false) ⇒ Object



85
86
87
88
# File 'lib/papercraft/compiler.rb', line 85

def emit_code(loc, semicolon: false)
  flush_html_buffer if !@embed_mode
  super
end

#emit_html(str) ⇒ Object



90
91
92
# File 'lib/papercraft/compiler.rb', line 90

def emit_html(str)
  @html_buffer << str
end

#emit_html_deferred(node) ⇒ Object

Raises:

  • (NotImplementedError)


231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/papercraft/compiler.rb', line 231

def emit_html_deferred(node)
  raise NotImplementedError, "#defer in embed mode is not supported in compiled templates" if @embed_mode

  block = node.block
  return if not block

  setup_defer_mode if !@defer_mode

  flush_html_buffer
  @buffer << ';__parts__ << ->(__b__) '
  @defer_proc_mode = true
  visit(node.block)
  @defer_proc_mode = nil
end

#emit_html_emit(node) ⇒ Object



224
225
226
227
228
229
# File 'lib/papercraft/compiler.rb', line 224

def emit_html_emit(node)
  args = node.arguments&.arguments
  return nil if !args

  embed_visit(node.arguments, '#{Papercraft.render_emit_call(', ')}')
end

#emit_html_tag(node) ⇒ Object



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/papercraft/compiler.rb', line 199

def emit_html_tag(node)
  inner_text, attrs = tag_args(node)
  block = node.block

  if inner_text
    emit_tag_open(node, attrs)
    emit_tag_inner_text(inner_text)
    emit_tag_close(node)
  elsif block
    emit_tag_open(node, attrs)
    visit(block.body)
    @html_location_start ||= node.block.closing_loc
    emit_tag_close(node)
  else
    emit_tag_open_close(node, attrs)
  end
end

#emit_html_text(node) ⇒ Object



217
218
219
220
221
222
# File 'lib/papercraft/compiler.rb', line 217

def emit_html_text(node)
  args = node.arguments&.arguments
  return nil if !args

  emit_tag_inner_text(args[0])
end

#emit_tag_attribute_node(node, key = false) ⇒ Object



188
189
190
191
192
193
194
195
196
197
# File 'lib/papercraft/compiler.rb', line 188

def emit_tag_attribute_node(node, key = false)
  case node
  when Prism::StringNode, Prism::SymbolNode
    value = node.unescaped
    value = Papercraft.format_html_attr(value) if key
    @html_buffer << value
  else
    tag_attr_embed_visit(node, key)
  end
end

#emit_tag_attributes(node, attrs) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/papercraft/compiler.rb', line 173

def emit_tag_attributes(node, attrs)
  attrs.elements.each do |e|
    emit_html(" ")

    if e.is_a?(Prism::AssocSplatNode)
      embed_visit(e.value, '#{Papercraft.format_html_attrs(', ')}')
    else
      emit_tag_attribute_node(e.key, true)
      emit_html('=\"')
      emit_tag_attribute_node(e.value)
      emit_html('\"')
    end  
  end
end

#emit_tag_close(node) ⇒ Object



154
155
156
# File 'lib/papercraft/compiler.rb', line 154

def emit_tag_close(node)
  emit_html("</#{node.name}>")
end

#emit_tag_inner_text(node) ⇒ Object



164
165
166
167
168
169
170
171
# File 'lib/papercraft/compiler.rb', line 164

def emit_tag_inner_text(node)
  case node
  when Prism::StringNode, Prism::SymbolNode
    @html_buffer << CGI.escapeHTML(node.unescaped)
  else
    html_embed_visit(node)
  end
end

#emit_tag_open(node, attrs) ⇒ Object



148
149
150
151
152
# File 'lib/papercraft/compiler.rb', line 148

def emit_tag_open(node, attrs)
  emit_html("<#{node.name}")
  emit_tag_attributes(node, attrs) if attrs
  emit_html(">")
end

#emit_tag_open_close(node, attrs) ⇒ Object



158
159
160
161
162
# File 'lib/papercraft/compiler.rb', line 158

def emit_tag_open_close(node, attrs)
  emit_html("<#{node.name}")
  emit_tag_attributes(node, attrs) if attrs
  emit_html("/>")
end

#flush_html_bufferObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/papercraft/compiler.rb', line 94

def flush_html_buffer
  return if @html_buffer.empty?

  if @last_loc_start
    adjust_whitespace(@html_location_start) if @html_location_start
  end
  if @defer_proc_mode
    @buffer << "__b__ << \"#{@html_buffer}\""
  elsif @defer_mode
    @buffer << "__parts__ << \"#{@html_buffer}\""
  else
    @buffer << "__buffer__ << \"#{@html_buffer}\""
  end
  @html_buffer.clear
  @last_loc_end = loc_end(@html_location_end) if @html_location_end

  @html_location_start = nil
  @html_location_end = nil
end

#html_embed_visit(node) ⇒ Object



73
74
75
# File 'lib/papercraft/compiler.rb', line 73

def html_embed_visit(node)
  embed_visit(node, '#{CGI.escapeHTML((', ').to_s)}')
end

#inject_buffer_parameter(node) ⇒ Object



51
52
53
# File 'lib/papercraft/compiler.rb', line 51

def inject_buffer_parameter(node)
  node.inject_parameters('__buffer__')
end

#setup_defer_modeObject



250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/papercraft/compiler.rb', line 250

def setup_defer_mode
  @defer_mode = true
  if @html_buffer && !@html_buffer.empty?
    @buffer << DEFER_PREFIX_NOT_EMPTY
  else
    @buffer << DEFER_PREFIX_EMPTY
  end

  @root_node.after_body do
    flush_html_buffer
    @buffer << DEFER_POSTFIX
  end
end

#tag_args(node) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/papercraft/compiler.rb', line 135

def tag_args(node)
  args = node.arguments&.arguments
  return nil if !args

  if args[0]&.is_a?(Prism::KeywordHashNode)
    [nil, args[0]]
  elsif args[1]&.is_a?(Prism::KeywordHashNode)
    args
  else
    [args && args[0], nil]
  end
end

#tag_attr_embed_visit(node, key) ⇒ Object



77
78
79
80
81
82
83
# File 'lib/papercraft/compiler.rb', line 77

def tag_attr_embed_visit(node, key)
  if key
    embed_visit(node, '#{Papercraft.format_html_attr(', ')}')
  else
    embed_visit(node, '#{', '}')
  end
end

#visit_call_node(node) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/papercraft/compiler.rb', line 114

def visit_call_node(node)
  return super if node.receiver || @embed_mode

  @html_location_start ||= node.location

  case node.name
  when :text
    emit_html_text(node)
  when :emit
    emit_html_emit(node)
  when :emit_yield
    raise NotImplementedError, "emit_yield is not yet supported in compiled templates"
  when :defer
    emit_html_deferred(node)
  else
    emit_html_tag(node)
  end

  @html_location_end = node.location
end