| Module | Haml::Precompiler |
| In: |
lib/haml/precompiler.rb
|
| ELEMENT | = | ?% | Designates an XHTML/XML element. | |
| DIV_CLASS | = | ?. | Designates a <div> element with the given class. | |
| DIV_ID | = | ?# | Designates a <div> element with the given id. | |
| COMMENT | = | ?/ | Designates an XHTML/XML comment. | |
| DOCTYPE | = | ?! | Designates an XHTML doctype or script that is never HTML-escaped. | |
| SCRIPT | = | ?= | Designates script, the result of which is output. | |
| SANITIZE | = | ?& | Designates script that is always HTML-escaped. | |
| FLAT_SCRIPT | = | ?~ | Designates script, the result of which is flattened and output. | |
| SILENT_SCRIPT | = | ?- | Designates script which is run but not output. | |
| SILENT_COMMENT | = | ?# | When following SILENT_SCRIPT, designates a comment that is not output. | |
| ESCAPE | = | ?\\ | Designates a non-parsed line. | |
| FILTER | = | ?: | Designates a block of filtered text. | |
| PLAIN_TEXT | = | -1 | Designates a non-parsed line. Not actually a character. | |
| SPECIAL_CHARACTERS | = | [ ELEMENT, DIV_CLASS, DIV_ID, COMMENT, DOCTYPE, SCRIPT, SANITIZE, FLAT_SCRIPT, SILENT_SCRIPT, ESCAPE, FILTER | Keeps track of the ASCII values of the characters that begin a specially-interpreted line. | |
| MULTILINE_CHAR_VALUE | = | ?| | The value of the character that designates that a line is part of a multiline string. | |
| MULTILINE_STARTERS | = | SPECIAL_CHARACTERS - [?/] | Characters that designate that a multiline string may be about to begin. | |
| MID_BLOCK_KEYWORDS | = | ['else', 'elsif', 'rescue', 'ensure', 'when'] |
Keywords that appear in the middle of a Ruby block with lowered
indentation. If a block has been started using indentation, lowering the
indentation with one of these won‘t end the block. For example:
- if foo
%p yes!
- else
%p no!
The block is ended after %p no!, because else is a member of this array. |
|
| DOCTYPE_REGEX | = | /(\d\.\d)?[\s]*([a-z]*)/i | The Regex that matches a Doctype command. | |
| LITERAL_VALUE_REGEX | = | /^\s*(:(\w*)|(('|")([^\\\#'"]*?)\4))\s*$/ | The Regex that matches a literal string or symbol value | |
| Line | = | Struct.new(:text, :unstripped, :index, :spaces, :tabs) |
This is a class method so it can be accessed from Buffer.
# File lib/haml/precompiler.rb, line 487
487: def self.build_attributes(is_html, attr_wrapper, attributes = {})
488: quote_escape = attr_wrapper == '"' ? """ : "'"
489: other_quote_char = attr_wrapper == '"' ? "'" : '"'
490:
491: result = attributes.collect do |attr, value|
492: next if value.nil?
493:
494: if value == true
495: next " #{attr}" if is_html
496: next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}"
497: elsif value == false
498: next
499: end
500:
501: value = Haml::Helpers.preserve(Haml::Helpers.escape_once(value.to_s))
502: # We want to decide whether or not to escape quotes
503: value.gsub!('"', '"')
504: this_attr_wrapper = attr_wrapper
505: if value.include? attr_wrapper
506: if value.include? other_quote_char
507: value = value.gsub(attr_wrapper, quote_escape)
508: else
509: this_attr_wrapper = other_quote_char
510: end
511: end
512: " #{attr}=#{this_attr_wrapper}#{value}#{this_attr_wrapper}"
513: end
514: result.compact.sort.join
515: end
# File lib/haml/precompiler.rb, line 752
752: def balance(scanner, start, finish, count = 0)
753: str = ''
754: scanner = StringScanner.new(scanner) unless scanner.is_a? StringScanner
755: regexp = Regexp.new("(.*?)[\\#{start.chr}\\#{finish.chr}]")
756: while scanner.scan(regexp)
757: str << scanner.matched
758: count += 1 if scanner.matched[-1] == start
759: count -= 1 if scanner.matched[-1] == finish
760: return [str.strip, scanner.rest] if count == 0
761: end
762:
763: raise SyntaxError.new("Unbalanced brackets.")
764: end
Closes the most recent item in @to_close_stack.
# File lib/haml/precompiler.rb, line 376
376: def close
377: tag, value = @to_close_stack.pop
378: case tag
379: when :script; close_block
380: when :comment; close_comment value
381: when :element; close_tag value
382: when :loud; close_loud value
383: when :filtered; close_filtered value
384: when :haml_comment; close_haml_comment
385: when nil; close_nil
386: end
387: end
Closes a Ruby block.
# File lib/haml/precompiler.rb, line 402
402: def close_block
403: push_silent "end", true
404: @template_tabs -= 1
405: end
Closes a comment.
# File lib/haml/precompiler.rb, line 408
408: def close_comment(has_conditional)
409: @output_tabs -= 1
410: @template_tabs -= 1
411: close_tag = has_conditional ? "<![endif]-->" : "-->"
412: push_text(close_tag, -1)
413: end
Closes a filtered block.
# File lib/haml/precompiler.rb, line 423
423: def close_filtered(filter)
424: @flat_spaces = -1
425: filter.internal_compile(self, @filter_buffer)
426: @filter_buffer = nil
427: @template_tabs -= 1
428: end
# File lib/haml/precompiler.rb, line 430
430: def close_haml_comment
431: @haml_comment = false
432: @template_tabs -= 1
433: end
Closes a loud Ruby block.
# File lib/haml/precompiler.rb, line 416
416: def close_loud(command)
417: push_silent 'end', true
418: @precompiled << command
419: @template_tabs -= 1
420: end
Puts a line in @precompiled that will add the closing tag of the most recently opened tag.
# File lib/haml/precompiler.rb, line 391
391: def close_tag(value)
392: tag, nuke_outer_whitespace, nuke_inner_whitespace = value
393: @output_tabs -= 1 unless nuke_inner_whitespace
394: @template_tabs -= 1
395: rstrip_buffer! if nuke_inner_whitespace
396: push_merged_text("</#{tag}>" + (nuke_outer_whitespace ? "" : "\n"),
397: nuke_inner_whitespace ? 0 : -1, !nuke_inner_whitespace)
398: @dont_indent_next_line = nuke_outer_whitespace
399: end
Concatenate text to @buffer without tabulation.
# File lib/haml/precompiler.rb, line 295
295: def concat_merged_text(text)
296: @merged_text << text
297: end
# File lib/haml/precompiler.rb, line 730
730: def contains_interpolation?(str)
731: str.include?('#{')
732: end
Counts the tabulation of a line.
# File lib/haml/precompiler.rb, line 767
767: def count_soft_tabs(line)
768: spaces = line.index(/([^ ]|$)/)
769: if line[spaces] == ?\t
770: return 0, 0 if line.strip.empty?
771: raise SyntaxError.new("A tab character was used for indentation. Haml must be indented using two spaces.\nAre you sure you have soft tabs enabled in your editor?\n".strip, @next_line.index)
772: end
773: [spaces, spaces/2]
774: end
# File lib/haml/precompiler.rb, line 303
303: def flush_merged_text
304: return if @merged_text.empty?
305:
306: @precompiled << "_hamlout.push_text(#{@merged_text.dump}"
307: @precompiled << ", #{@dont_tab_up_next_text.inspect}" if @dont_tab_up_next_text || @tab_change != 0
308: @precompiled << ", #{@tab_change}" if @tab_change != 0
309: @precompiled << ");"
310: @merged_text = ''
311: @dont_tab_up_next_text = false
312: @tab_change = 0
313: end
Deals with all the logic of figuring out whether a given line is the beginning, continuation, or end of a multiline sequence.
This returns whether or not the line should be rendered normally.
# File lib/haml/precompiler.rb, line 248
248: def handle_multiline(line)
249: text = line.text
250:
251: # A multiline string is active, and is being continued
252: if is_multiline?(text) && @multiline
253: @multiline.text << text[0...-1]
254: return true
255: end
256:
257: # A multiline string has just been activated, start adding the lines
258: if is_multiline?(text) && (MULTILINE_STARTERS.include? text[0])
259: @multiline = Line.new text[0...-1], nil, line.index, nil, line.tabs
260: process_indent(line)
261: return true
262: end
263:
264: # A multiline string has just ended, make line into the result
265: if @multiline && !line.text.empty?
266: process_line(@multiline.text, @multiline.index, line.tabs > @multiline.tabs)
267: @multiline = nil
268: end
269:
270: return false
271: end
Checks whether or not line is in a multiline sequence.
# File lib/haml/precompiler.rb, line 274
274: def is_multiline?(text)
275: text && text.length > 1 && text[-1] == MULTILINE_CHAR_VALUE && text[-2] == ?\s
276: end
# File lib/haml/precompiler.rb, line 107
107: def locals_code(names)
108: names = names.keys if Hash == names
109:
110: names.map do |name|
111: # Can't use || because someone might explicitly pass in false with a symbol
112: sym_local = "_haml_locals[#{name.to_sym.inspect}]"
113: str_local = "_haml_locals[#{name.to_s.inspect}]"
114: "#{name} = #{sym_local}.nil? ? #{str_local} : #{sym_local}"
115: end.join(';') + ';'
116: end
Returns whether or not the text is a silent script text with one of Ruby‘s mid-block keywords.
# File lib/haml/precompiler.rb, line 239
239: def mid_block_keyword?(text)
240: text.length > 2 && text[0] == SILENT_SCRIPT && MID_BLOCK_KEYWORDS.include?(text[1..-1].split[0])
241: end
# File lib/haml/precompiler.rb, line 795
795: def newline_now
796: @precompiled << "\n"
797: @newlines -= 1
798: end
# File lib/haml/precompiler.rb, line 540
540: def parse_attributes(line)
541: scanner = StringScanner.new(line)
542: attributes_hash, rest = balance(scanner, ?{, ?})
543: attributes_hash = attributes_hash[1...-1] if attributes_hash
544: return attributes_hash, rest
545: end
Iterates through the classes and ids supplied through . and # syntax, and returns a hash with them as attributes, that can then be merged with another attributes hash.
# File lib/haml/precompiler.rb, line 442
442: def parse_class_and_id(list)
443: attributes = {}
444: list.scan(/([#.])([-_a-zA-Z0-9]+)/) do |type, property|
445: case type
446: when '.'
447: if attributes['class']
448: attributes['class'] += " "
449: else
450: attributes['class'] = ""
451: end
452: attributes['class'] += property
453: when '#'; attributes['id'] = property
454: end
455: end
456: attributes
457: end
# File lib/haml/precompiler.rb, line 459
459: def parse_literal_value(text)
460: return nil unless text
461: text.match(LITERAL_VALUE_REGEX)
462:
463: # $2 holds the value matched by a symbol, but is nil for a string match
464: # $5 holds the value matched by a string
465: $2 || $5
466: end
# File lib/haml/precompiler.rb, line 468
468: def parse_static_hash(text)
469: return {} unless text
470:
471: attributes = {}
472: text.split(',').each do |attrib|
473: key, value, more = attrib.split('=>')
474:
475: # Make sure the key and value and only the key and value exist
476: # Otherwise, it's too complicated or dynamic and we'll defer it to the actual Ruby parser
477: key = parse_literal_value key
478: value = parse_literal_value value
479: return nil if more || key.nil? || value.nil?
480:
481: attributes[key] = value
482: end
483: attributes
484: end
Parses a line into tag_name, attributes, attributes_hash, object_ref, action, value
# File lib/haml/precompiler.rb, line 523
523: def parse_tag(line)
524: raise SyntaxError.new("Invalid tag: \"#{line}\".") unless match = line.scan(/%([-:\w]+)([-\w\.\#]*)(.*)/)[0]
525: tag_name, attributes, rest = match
526: attributes_hash, rest = parse_attributes(rest) if rest[0] == ?{
527: if rest
528: object_ref, rest = balance(rest, ?[, ?]) if rest[0] == ?[
529: attributes_hash, rest = parse_attributes(rest) if rest[0] == ?{ && attributes_hash.nil?
530: nuke_whitespace, action, value = rest.scan(/(<>|><|[><])?([=\/\~&!])?(.*)?/)[0]
531: nuke_whitespace ||= ''
532: nuke_outer_whitespace = nuke_whitespace.include? '>'
533: nuke_inner_whitespace = nuke_whitespace.include? '<'
534: end
535: value = value.to_s.strip
536: [tag_name, attributes, attributes_hash, object_ref, nuke_outer_whitespace,
537: nuke_inner_whitespace, action, value]
538: end
# File lib/haml/precompiler.rb, line 120
120: def precompile
121: @haml_comment = @dont_indent_next_line = @dont_tab_up_next_text = false
122: @indentation = nil
123: old_line = Line.new
124: @template.split(/\r\n|\r|\n/).each_with_index do |text, index|
125: @next_line = line = Line.new(text.strip, text.lstrip.chomp, index)
126: line.spaces, line.tabs = count_soft_tabs(text)
127:
128: suppress_render = handle_multiline(old_line) unless flat?
129:
130: if old_line.text.nil? || suppress_render
131: old_line = line
132: resolve_newlines
133: newline
134: next
135: end
136:
137: process_indent(old_line) unless old_line.text.empty?
138:
139: if line.text.empty? && !flat?
140: newline
141: next
142: end
143:
144: if flat?
145: push_flat(old_line)
146: old_line = line
147: newline
148: next
149: end
150:
151: if old_line.spaces != old_line.tabs * 2
152: raise SyntaxError.new("\#{old_line.spaces} space\#{old_line.spaces == 1 ? ' was' : 's were'} used for indentation. Haml must be indented using two spaces.\n".strip, old_line.index)
153: end
154:
155: unless old_line.text.empty? || @haml_comment
156: process_line(old_line.text, old_line.index, line.tabs > old_line.tabs && !line.text.empty?)
157: end
158: resolve_newlines
159:
160: if !flat? && line.tabs - old_line.tabs > 1
161: raise SyntaxError.new("\#{line.spaces} spaces were used for indentation. Haml must be indented using two spaces.\n".strip, line.index)
162: end
163: old_line = line
164: newline
165: end
166:
167: # Close all the open tags
168: close until @to_close_stack.empty?
169: flush_merged_text
170: end
Returns the precompiled string with the preamble and postamble
# File lib/haml/precompiler.rb, line 91
91: def precompiled_with_ambles(local_names)
92: preamble = "extend Haml::Helpers\n_hamlout = @haml_buffer = Haml::Buffer.new(@haml_buffer, \#{options_for_buffer.inspect})\n_erbout = _hamlout.buffer\n__in_erb_template = true\n".gsub("\n", ";")
93: postamble = "@haml_buffer = @haml_buffer.upper\n_erbout\n".gsub("\n", ";")
94: preamble + locals_code(local_names) + @precompiled + postamble
95: end
# File lib/haml/precompiler.rb, line 517
517: def prerender_tag(name, self_close, attributes)
518: attributes_string = Precompiler.build_attributes(html?, @options[:attr_wrapper], attributes)
519: "<#{name}#{attributes_string}#{self_close && xhtml? ? ' /' : ''}>"
520: end
Processes and deals with lowering indentation.
# File lib/haml/precompiler.rb, line 179
179: def process_indent(line)
180: return unless line.tabs <= @template_tabs && @template_tabs > 0
181:
182: to_close = @template_tabs - line.tabs
183: to_close.times { |i| close unless to_close - 1 - i == 0 && mid_block_keyword?(line.text) }
184: end
Processes a single line of Haml.
This method doesn‘t return anything; it simply processes the line and adds the appropriate code to @precompiled.
# File lib/haml/precompiler.rb, line 190
190: def process_line(text, index, block_opened)
191: @block_opened = block_opened
192: @index = index + 1
193:
194: case text[0]
195: when DIV_CLASS, DIV_ID; render_div(text)
196: when ELEMENT; render_tag(text)
197: when COMMENT; render_comment(text[1..-1].strip)
198: when SANITIZE
199: return push_script(unescape_interpolation(text[3..-1].strip), false, false, false, true) if text[1..2] == "=="
200: return push_script(text[2..-1].strip, false, false, false, true) if text[1] == SCRIPT
201: push_plain text
202: when SCRIPT
203: return push_script(unescape_interpolation(text[2..-1].strip), false) if text[1] == SCRIPT
204: return push_script(text[1..-1], false, false, false, true) if options[:escape_html]
205: push_script(text[1..-1], false)
206: when FLAT_SCRIPT; push_flat_script(text[1..-1])
207: when SILENT_SCRIPT
208: return start_haml_comment if text[1] == SILENT_COMMENT
209:
210: raise SyntaxError.new("You don't need to use \"- end\" in Haml. Use indentation instead:\n- if foo?\n %strong Foo!\n- else\n Not foo.\n".rstrip, index) if text[1..-1].strip == "end"
211:
212: push_silent(text[1..-1], true)
213: newline_now
214:
215: case_stmt = text[1..-1].split(' ', 2)[0] == "case"
216: block = @block_opened && !mid_block_keyword?(text)
217: push_and_tabulate([:script]) if block || case_stmt
218: push_and_tabulate(nil) if block && case_stmt
219: when FILTER; start_filtered(text[1..-1].downcase)
220: when DOCTYPE
221: return render_doctype(text) if text[0...3] == '!!!'
222: return push_script(unescape_interpolation(text[3..-1].strip), false) if text[1..2] == "=="
223: return push_script(text[2..-1].strip, false) if text[1] == SCRIPT
224: push_plain text
225: when ESCAPE; push_plain text[1..-1]
226: else push_plain text
227: end
228: end
Pushes value onto @to_close_stack and increases @template_tabs.
# File lib/haml/precompiler.rb, line 782
782: def push_and_tabulate(value)
783: @to_close_stack.push(value)
784: @template_tabs += 1
785: end
Adds text to @buffer while flattening text.
# File lib/haml/precompiler.rb, line 326
326: def push_flat(line)
327: tabulation = line.spaces - @flat_spaces
328: tabulation = tabulation > -1 ? tabulation : 0
329: @filter_buffer << "#{' ' * tabulation}#{line.unstripped}\n"
330: end
Causes text to be evaluated, and Haml::Helpers#find_and_flatten to be run on it afterwards.
# File lib/haml/precompiler.rb, line 361
361: def push_flat_script(text)
362: flush_merged_text
363:
364: raise SyntaxError.new("There's no Ruby code for ~ to evaluate.") if text.empty?
365: push_script(text, true)
366: end
Adds text to @buffer with appropriate tabulation without parsing it.
# File lib/haml/precompiler.rb, line 288
288: def push_merged_text(text, tab_change = 0, indent = true)
289: @merged_text << (!indent || @dont_indent_next_line || @options[:ugly] ? text : "#{' ' * @output_tabs}#{text}")
290: @dont_indent_next_line = false
291: @tab_change += tab_change
292: end
Renders a block of text as plain text. Also checks for an illegally opened block.
# File lib/haml/precompiler.rb, line 317
317: def push_plain(text)
318: if @block_opened
319: raise SyntaxError.new("Illegal nesting: nesting within plain text is illegal.", @next_line.index)
320: end
321:
322: push_text text
323: end
Causes text to be evaluated in the context of the scope object and the result to be added to @buffer.
If preserve_script is true, Haml::Helpers#find_and_flatten is run on the result before it is added to @buffer
# File lib/haml/precompiler.rb, line 337
337: def push_script(text, preserve_script, in_tag = false, preserve_tag = false,
338: escape_html = false, nuke_inner_whitespace = false)
339: # Prerender tabulation unless we're in a tag
340: push_merged_text '' unless in_tag
341:
342: flush_merged_text
343: return if options[:suppress_eval]
344:
345: raise SyntaxError.new("There's no Ruby code for = to evaluate.") if text.empty?
346:
347: push_silent "haml_temp = #{text}"
348: newline_now
349: args = [preserve_script, in_tag, preserve_tag,
350: escape_html, nuke_inner_whitespace].map { |a| a.inspect }.join(', ')
351: out = "haml_temp = _hamlout.push_script(haml_temp, #{args});"
352: if @block_opened
353: push_and_tabulate([:loud, out])
354: else
355: @precompiled << out
356: end
357: end
Evaluates text in the context of the scope object, but does not output the result.
# File lib/haml/precompiler.rb, line 280
280: def push_silent(text, can_suppress = false)
281: flush_merged_text
282: return if can_suppress && options[:suppress_eval]
283: @precompiled << "#{text};"
284: end
# File lib/haml/precompiler.rb, line 299
299: def push_text(text, tab_change = 0)
300: push_merged_text("#{text}\n", tab_change)
301: end
Renders an XHTML comment.
# File lib/haml/precompiler.rb, line 651
651: def render_comment(line)
652: conditional, line = balance(line, ?[, ?]) if line[0] == ?[
653: line.strip!
654: conditional << ">" if conditional
655:
656: if @block_opened && !line.empty?
657: raise SyntaxError.new('Illegal nesting: nesting within a tag that already has content is illegal.', @next_line.index)
658: end
659:
660: open = "<!--#{conditional} "
661:
662: # Render it statically if possible
663: unless line.empty?
664: return push_text("#{open}#{line} #{conditional ? "<![endif]-->" : "-->"}")
665: end
666:
667: push_text(open, 1)
668: @output_tabs += 1
669: push_and_tabulate([:comment, !conditional.nil?])
670: unless line.empty?
671: push_text(line)
672: close
673: end
674: end
Renders a line that creates an XHTML tag and has an implicit div because of . or #.
# File lib/haml/precompiler.rb, line 646
646: def render_div(line)
647: render_tag('%div' + line)
648: end
Renders an XHTML doctype or XML shebang.
# File lib/haml/precompiler.rb, line 677
677: def render_doctype(line)
678: raise SyntaxError.new("Illegal nesting: nesting within a header command is illegal.", @next_line.index) if @block_opened
679: doctype = text_for_doctype(line)
680: push_text doctype if doctype
681: end
Parses a line that will render as an XHTML tag, and adds the code that will render that tag to @precompiled.
# File lib/haml/precompiler.rb, line 549
549: def render_tag(line)
550: tag_name, attributes, attributes_hash, object_ref, nuke_outer_whitespace,
551: nuke_inner_whitespace, action, value = parse_tag(line)
552:
553: raise SyntaxError.new("Illegal element: classes and ids must have values.") if attributes =~ /[\.#](\.|#|\z)/
554:
555: # Get rid of whitespace outside of the tag if we need to
556: rstrip_buffer! if nuke_outer_whitespace
557:
558: preserve_tag = options[:preserve].include?(tag_name)
559: nuke_inner_whitespace ||= preserve_tag
560: preserve_tag &&= !options[:ugly]
561:
562: case action
563: when '/'; self_closing = true
564: when '~'; parse = preserve_script = true
565: when '='
566: parse = true
567: value = unescape_interpolation(value[1..-1].strip) if value[0] == ?=
568: when '&', '!'
569: if value[0] == ?=
570: parse = true
571: value = (value[1] == ?= ? unescape_interpolation(value[2..-1].strip) : value[1..-1].strip)
572: end
573: end
574:
575: if parse && @options[:suppress_eval]
576: parse = false
577: value = ''
578: end
579:
580: escape_html = (action == '&' || (action != '!' && @options[:escape_html]))
581:
582: object_ref = "nil" if object_ref.nil? || @options[:suppress_eval]
583:
584: static_attributes = parse_static_hash(attributes_hash) # Try pre-compiling a static attributes hash
585: attributes_hash = nil if static_attributes || @options[:suppress_eval]
586: attributes = parse_class_and_id(attributes)
587: Buffer.merge_attrs(attributes, static_attributes) if static_attributes
588:
589: raise SyntaxError.new("Illegal nesting: nesting within a self-closing tag is illegal.", @next_line.index) if @block_opened && self_closing
590: raise SyntaxError.new("Illegal nesting: content can't be both given on the same line as %#{tag_name} and nested within it.", @next_line.index) if @block_opened && !value.empty?
591: raise SyntaxError.new("There's no Ruby code for #{action} to evaluate.") if parse && value.empty?
592: raise SyntaxError.new("Self-closing tags can't have content.") if self_closing && !value.empty?
593:
594: self_closing ||= !!( !@block_opened && value.empty? && @options[:autoclose].include?(tag_name) )
595:
596: dont_indent_next_line =
597: (nuke_outer_whitespace && !@block_opened) ||
598: (nuke_inner_whitespace && @block_opened)
599:
600: # Check if we can render the tag directly to text and not process it in the buffer
601: if object_ref == "nil" && attributes_hash.nil? && !preserve_script
602: tag_closed = !@block_opened && !self_closing && !parse
603:
604: open_tag = prerender_tag(tag_name, self_closing, attributes)
605: if tag_closed
606: open_tag << "#{value}</#{tag_name}>"
607: open_tag << "\n" unless nuke_outer_whitespace
608: else
609: open_tag << "\n" unless parse || nuke_inner_whitespace || (self_closing && nuke_outer_whitespace)
610: end
611:
612: push_merged_text(open_tag, tag_closed || self_closing || nuke_inner_whitespace ? 0 : 1,
613: !nuke_outer_whitespace)
614:
615: @dont_indent_next_line = dont_indent_next_line
616: return if tag_closed
617: else
618: flush_merged_text
619: content = value.empty? || parse ? 'nil' : value.dump
620: attributes_hash = ', ' + attributes_hash if attributes_hash
621: args = [tag_name, self_closing, !@block_opened, preserve_tag, escape_html,
622: attributes, nuke_outer_whitespace, nuke_inner_whitespace
623: ].map { |v| v.inspect }.join(', ')
624: push_silent "_hamlout.open_tag(#{args}, #{object_ref}, #{content}#{attributes_hash})"
625: @dont_tab_up_next_text = @dont_indent_next_line = dont_indent_next_line
626: end
627:
628: return if self_closing
629:
630: if value.empty?
631: push_and_tabulate([:element, [tag_name, nuke_outer_whitespace, nuke_inner_whitespace]])
632: @output_tabs += 1 unless nuke_inner_whitespace
633: return
634: end
635:
636: if parse
637: flush_merged_text
638: push_script(value, preserve_script, true, preserve_tag, escape_html, nuke_inner_whitespace)
639: @dont_tab_up_next_text = true
640: concat_merged_text("</#{tag_name}>" + (nuke_outer_whitespace ? "" : "\n"))
641: end
642: end
# File lib/haml/precompiler.rb, line 800
800: def resolve_newlines
801: return unless @newlines > 0
802: @precompiled << "\n" * @newlines
803: @newlines = 0
804: end
Get rid of and whitespace at the end of the buffer or the merged text
# File lib/haml/precompiler.rb, line 808
808: def rstrip_buffer!
809: unless @merged_text.empty?
810: @merged_text.rstrip!
811: else
812: push_silent("_erbout.rstrip!", false)
813: @dont_tab_up_next_text = true
814: end
815: end
Starts a filtered block.
# File lib/haml/precompiler.rb, line 720
720: def start_filtered(name)
721: raise Error.new("Invalid filter name \":#{name}\".") unless name =~ /^\w+$/
722: raise Error.new("Filter \"#{name}\" is not defined.") unless filter = Filters.defined[name]
723:
724: push_and_tabulate([:filtered, filter])
725: @flat_spaces = @template_tabs * 2
726: @filter_buffer = String.new
727: @block_opened = false
728: end
# File lib/haml/precompiler.rb, line 368
368: def start_haml_comment
369: return unless @block_opened
370:
371: @haml_comment = true
372: push_and_tabulate([:haml_comment])
373: end
# File lib/haml/precompiler.rb, line 683
683: def text_for_doctype(text)
684: text = text[3..-1].lstrip.downcase
685: if text.index("xml") == 0
686: return nil if html?
687: wrapper = @options[:attr_wrapper]
688: return "<?xml version=#{wrapper}1.0#{wrapper} encoding=#{wrapper}#{text.split(' ')[1] || "utf-8"}#{wrapper} ?>"
689: end
690:
691: if html5?
692: '<!DOCTYPE html>'
693: else
694: version, type = text.scan(DOCTYPE_REGEX)[0]
695:
696: if xhtml?
697: if version == "1.1"
698: '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
699: else
700: case type
701: when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
702: when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
703: when "mobile"; '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
704: when "basic"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
705: else '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
706: end
707: end
708:
709: elsif html4?
710: case type
711: when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'
712: when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
713: else '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
714: end
715: end
716: end
717: end
# File lib/haml/precompiler.rb, line 734
734: def unescape_interpolation(str)
735: scan = StringScanner.new(str.dump)
736: str = ''
737:
738: while scan.scan(/(.*?)(\\+)\#\{/)
739: escapes = (scan[2].size - 1) / 2
740: str << scan.matched[0...-3 - escapes]
741: if escapes % 2 == 1
742: str << '#{'
743: else
744: # Use eval to get rid of string escapes
745: str << '#{' + eval('"' + balance(scan, ?{, ?}, 1)[0][0...-1] + '"') + "}"
746: end
747: end
748:
749: str + scan.rest
750: end