📄 engine.rb
字号:
return suppress_render end # Checks whether or not +line+ is in a multiline sequence. def is_multiline?(line) # ' '[0] == 32 line && line.length > 1 && line[-1] == MULTILINE_CHAR_VALUE && line[-2] == 32 end # Takes <tt>@precompiled</tt>, a string buffer of Ruby code, and # evaluates it in the context of <tt>@scope_object</tt>, after preparing # <tt>@scope_object</tt>. The code in <tt>@precompiled</tt> populates # <tt>@buffer</tt> with the compiled XHTML code. def compile(&block) # Set the local variables pointing to the buffer buffer = @buffer @scope_object.extend Haml::Helpers @scope_object.instance_eval do @haml_stack ||= Array.new @haml_stack.push(buffer) class << self attr :haml_lineno # :nodoc: end end begin # Evaluate the buffer in the context of the scope object @scope_object.instance_eval @precompiled @scope_object._haml_render &block rescue Exception => e # Get information from the exception and format it so that # Rails can understand it. compile_error = e.message.scan(/\(eval\):([0-9]*):in `[-_a-zA-Z]*': compile error/)[0] filename = "(haml)" if @scope_object.methods.include? "haml_filename" # For some reason that I can't figure out, # @scope_object.methods.include? "haml_filename" && @scope_object.haml_filename # is false when it shouldn't be. Nested if statements work, though. if @scope_object.haml_filename filename = "#{@scope_object.haml_filename}.haml" end end lineno = @scope_object.haml_lineno if compile_error eval_line = compile_error[0].to_i line_marker = @precompiled.split("\n")[0...eval_line].grep(/@haml_lineno = [0-9]*/)[-1] lineno = line_marker.scan(/[0-9]+/)[0].to_i if line_marker end e.backtrace.unshift "#{filename}:#{lineno}" raise e end # Get rid of the current buffer @scope_object.instance_eval do @haml_stack.pop end end # Evaluates <tt>text</tt> in the context of <tt>@scope_object</tt>, but # does not output the result. def push_silent(text, index = nil) if index @precompiled << "@haml_lineno = #{index + 1}\n#{text}\n" else # Not really DRY, but probably faster @precompiled << "#{text}\n" end end # Adds <tt>text</tt> to <tt>@buffer</tt> with appropriate tabulation # without parsing it. def push_text(text) @precompiled << "_hamlout.push_text(#{text.dump}, #{@output_tabs})\n" end # Adds +text+ to <tt>@buffer</tt> while flattening text. def push_flat(text, spaces) tabulation = spaces - @flat_spaces @precompiled << "_hamlout.push_text(#{text.dump}, #{tabulation > -1 ? tabulation : 0}, true)\n" end # Causes <tt>text</tt> to be evaluated in the context of # <tt>@scope_object</tt> and the result to be added to <tt>@buffer</tt>. # # If <tt>flattened</tt> is true, Haml::Helpers#find_and_flatten is run on # the result before it is added to <tt>@buffer</tt> def push_script(text, flattened, block_opened, index) unless options[:suppress_eval] push_silent("haml_temp = #{text}", index) out = "haml_temp = _hamlout.push_script(haml_temp, #{@output_tabs}, #{flattened})\n" if block_opened push_and_tabulate([:loud, out]) else @precompiled << out end end end # Causes <tt>text</tt> to be evaluated, and Haml::Helpers#find_and_flatten # to be run on it afterwards. def push_flat_script(text, block_opened, index) unless text.empty? push_script(text, true, block_opened, index) else start_flat(false) end end # Closes the most recent item in <tt>@to_close_stack</tt>. def close tag, value = @to_close_stack.pop case tag when :script close_block when :comment close_comment value when :element close_tag value when :flat close_flat value when :loud close_loud value end end # Puts a line in <tt>@precompiled</tt> that will add the closing tag of # the most recently opened tag. def close_tag(tag) @output_tabs -= 1 @template_tabs -= 1 @precompiled << "_hamlout.close_tag(#{tag.dump}, #{@output_tabs})\n" end # Closes a Ruby block. def close_block push_silent "end" @template_tabs -= 1 end # Closes a comment. def close_comment(has_conditional) @output_tabs -= 1 @template_tabs -= 1 push_silent "_hamlout.close_comment(#{has_conditional}, #{@output_tabs})" end # Closes a flattened section. def close_flat(in_tag) @flat_spaces = -1 if in_tag close else push_silent('_hamlout.stop_flat') @template_tabs -= 1 end end # Closes a loud Ruby block. def close_loud(command) push_silent "end" @precompiled << command @template_tabs -= 1 end # Parses a line that will render as an XHTML tag, and adds the code that will # render that tag to <tt>@precompiled</tt>. def render_tag(line, index) line.scan(/[%]([-:_a-zA-Z0-9]+)([-_a-zA-Z0-9\.\#]*)(\{.*\})?(\[.*\])?([=\/\~]?)?(.*)?/) do |tag_name, attributes, attributes_hash, object_ref, action, value| value = value.to_s case action when '/' atomic = true when '=', '~' parse = true else value = value.strip end flattened = (action == '~') value_exists = !value.empty? attributes_hash = "nil" unless attributes_hash object_ref = "nil" unless object_ref push_silent "_hamlout.open_tag(#{tag_name.inspect}, #{@output_tabs}, #{atomic.inspect}, #{value_exists.inspect}, #{attributes.inspect}, #{attributes_hash}, #{object_ref}, #{flattened.inspect})" unless atomic push_and_tabulate([:element, tag_name]) @output_tabs += 1 if value_exists if parse push_script(value, flattened, false, index) else push_text(value) end close elsif flattened start_flat(true) end end end end # Renders a line that creates an XHTML tag and has an implicit div because of # <tt>.</tt> or <tt>#</tt>. def render_div(line, index) render_tag('%div' + line, index) end # Renders an XHTML comment. def render_comment(line) conditional, content = line.scan(/\/(\[[a-zA-Z0-9 \.]*\])?(.*)/)[0] content = content.strip try_one_line = !content.empty? push_silent "_hamlout.open_comment(#{try_one_line}, #{conditional.inspect}, #{@output_tabs})" @output_tabs += 1 push_and_tabulate([:comment, !conditional.nil?]) if try_one_line push_text content close end end # Renders an XHTML doctype or XML shebang. def render_doctype(line) line = line[3..-1].lstrip.downcase if line[0...3] == "xml" encoding = line.split[1] || "utf-8" wrapper = @options[:attr_wrapper] doctype = "<?xml version=#{wrapper}1.0#{wrapper} encoding=#{wrapper}#{encoding}#{wrapper} ?>" else version, type = line.scan(/([0-9]\.[0-9])?[\s]*([a-zA-Z]*)/)[0] if version == "1.1" doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' else case type when "strict" doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' when "frameset" doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">' else doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' end end end push_text doctype end # Starts a flattened block. def start_flat(in_tag) # @flat_spaces is the number of indentations in the template # that forms the base of the flattened area if in_tag @to_close_stack.push([:flat, true]) else push_and_tabulate([:flat]) end @flat_spaces = @template_tabs * 2 end # Counts the tabulation of a line. def count_soft_tabs(line) spaces = line.index(/[^ ]/) spaces ? [spaces, spaces/2] : [] end # Pushes value onto <tt>@to_close_stack</tt> and increases # <tt>@template_tabs</tt>. def push_and_tabulate(value) @to_close_stack.push(value) @template_tabs += 1 end endend
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -