Class Sass::Engine
In: lib/sass/engine.rb
Parent: Object
Haml::Util Engine Color SyntaxError UnitConversionError StandardError AbstractSequence CommaSequence Sequence SimpleSequence Simple Parent Universal Class Negation Id Pseudo Attribute Interpolation Element Node Operation Literal UnaryOperation StringInterpolation Funcall Variable Interpolation Lexer CssLexer Number String Bool Parser Parser CssParser EvaluationContext StaticParser SassParser CssParser Node DebugNode IfNode CommentNode ForNode PropNode MixinNode DirectiveNode VariableNode RootNode WarnNode ExtendNode RuleNode MixinDefNode WhileNode Enumerable ImportNode Merb::BootLoader MerbBootLoader Repl CSS Environment Rack StalenessChecker lib/sass/repl.rb lib/sass/css.rb lib/sass/environment.rb lib/sass/error.rb lib/sass/engine.rb lib/sass/selector/simple_sequence.rb lib/sass/selector/abstract_sequence.rb lib/sass/selector/sequence.rb lib/sass/selector/comma_sequence.rb lib/sass/selector/simple.rb lib/sass/selector.rb Selector lib/sass/script/css_parser.rb lib/sass/script/lexer.rb lib/sass/script/color.rb lib/sass/script/string.rb lib/sass/script/unary_operation.rb lib/sass/script/variable.rb lib/sass/script/funcall.rb lib/sass/script/string_interpolation.rb lib/sass/script/operation.rb lib/sass/script/bool.rb lib/sass/script/parser.rb lib/sass/script/node.rb lib/sass/script/literal.rb lib/sass/script/interpolation.rb lib/sass/script/css_lexer.rb lib/sass/script/number.rb lib/sass/script/functions.rb Functions Script lib/sass/scss/sass_parser.rb lib/sass/scss/static_parser.rb lib/sass/scss/parser.rb lib/sass/scss/css_parser.rb ScriptParser ScriptLexer RX SCSS Files Callbacks lib/sass/tree/while_node.rb lib/sass/tree/if_node.rb lib/sass/tree/mixin_def_node.rb lib/sass/tree/debug_node.rb lib/sass/tree/root_node.rb lib/sass/tree/for_node.rb lib/sass/tree/import_node.rb lib/sass/tree/prop_node.rb lib/sass/tree/node.rb lib/sass/tree/comment_node.rb lib/sass/tree/extend_node.rb lib/sass/tree/mixin_node.rb lib/sass/tree/warn_node.rb lib/sass/tree/directive_node.rb lib/sass/tree/rule_node.rb lib/sass/tree/variable_node.rb Tree lib/sass/plugin/rack.rb lib/sass/plugin/staleness_checker.rb lib/sass/plugin/merb.rb Plugin Sass dot/m_61_0.png

This class handles the parsing and compilation of the Sass template. Example usage:

    template = File.load('stylesheets/sassy.sass')
    sass_engine = Sass::Engine.new(template)
    output = sass_engine.render
    puts output

Methods

Included Modules

Haml::Util

Classes and Modules

Class Sass::Engine::Line

Constants

PROPERTY_CHAR = ?:   The character that begins a CSS property.
SCRIPT_CHAR = ?=   The character that designates that a property should be assigned to a SassScript expression.
COMMENT_CHAR = ?/   The character that designates the beginning of a comment, either Sass or CSS.
SASS_COMMENT_CHAR = ?/   The character that follows the general COMMENT_CHAR and designates a Sass comment, which is not output as a CSS comment.
CSS_COMMENT_CHAR = ?*   The character that follows the general COMMENT_CHAR and designates a CSS comment, which is embedded in the CSS document.
DIRECTIVE_CHAR = ?@   The character used to denote a compiler directive.
ESCAPE_CHAR = ?\\   Designates a non-parsed rule.
MIXIN_DEFINITION_CHAR = ?=   Designates block as mixin definition rather than CSS rules to output
MIXIN_INCLUDE_CHAR = ?+   Includes named mixin declared using MIXIN_DEFINITION_CHAR
PROPERTY_NEW_MATCHER = /^[^\s:"\[]+\s*[=:](\s|$)/   The regex that matches properties of the form `name: prop`.
PROPERTY_NEW = /^([^\s=:"]+)\s*(=|:)(?:\s+|$)(.*)/   The regex that matches and extracts data from properties of the form `name: prop`.
PROPERTY_OLD = /^:([^\s=:"]+)\s*(=?)(?:\s+|$)(.*)/   The regex that matches and extracts data from properties of the form `:name prop`.
DEFAULT_OPTIONS = { :style => :nested, :load_paths => ['.'], :cache => true, :cache_location => './.sass-cache', :syntax => :sass, }.freeze   The default options for Sass::Engine. @api public
MIXIN_DEF_RE = /^(?:=|@mixin)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
MIXIN_INCLUDE_RE = /^(?:\+|@include)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/

Public Class methods

@param template [String] The Sass template.

  This template can be encoded using any encoding
  that can be converted to Unicode.
  If the template contains an `@charset` declaration,
  that overrides the Ruby encoding
  (see {file:SASS_REFERENCE.md#encodings the encoding documentation})

@param options [{Symbol => Object}] An options hash;

  see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}

[Source]

     # File lib/sass/engine.rb, line 143
143:     def initialize(template, options={})
144:       @options = DEFAULT_OPTIONS.merge(options.reject {|k, v| v.nil?})
145:       @template = template
146: 
147:       # Support both, because the docs said one and the other actually worked
148:       # for quite a long time.
149:       @options[:line_comments] ||= @options[:line_numbers]
150: 
151:       # Backwards compatibility
152:       @options[:property_syntax] ||= @options[:attribute_syntax]
153:       case @options[:property_syntax]
154:       when :alternate; @options[:property_syntax] = :new
155:       when :normal; @options[:property_syntax] = :old
156:       end
157:     end

Private Class methods

It‘s important that this have strings (at least) at the beginning, the end, and between each Script::Node.

@private

[Source]

     # File lib/sass/engine.rb, line 661
661:     def self.parse_interp(text, line, offset, options)
662:       res = []
663:       rest = Haml::Shared.handle_interpolation text do |scan|
664:         escapes = scan[2].size
665:         res << scan.matched[0...-2 - escapes]
666:         if escapes % 2 == 1
667:           res << "\\" * (escapes - 1) << '#{'
668:         else
669:           res << "\\" * [0, escapes - 1].max
670:           res << Script::Parser.new(
671:             scan, line, offset + scan.pos - scan.matched_size, options).
672:             parse_interpolated
673:         end
674:       end
675:       res << rest
676:     end

Public Instance methods

Render the template to CSS.

@return [String] The CSS @raise [Sass::SyntaxError] if there‘s an error in the document @raise [Encoding::UndefinedConversionError] if the source encoding

  cannot be converted to UTF-8

@raise [ArgumentError] if the document uses an unknown encoding with `@charset`

[Source]

     # File lib/sass/engine.rb, line 166
166:     def render
167:       return _render unless @options[:quiet]
168:       Haml::Util.silence_haml_warnings {_render}
169:     end

Returns the original encoding of the document, or `nil` under Ruby 1.8.

@return [Encoding, nil] @raise [Encoding::UndefinedConversionError] if the source encoding

  cannot be converted to UTF-8

@raise [ArgumentError] if the document uses an unknown encoding with `@charset`

[Source]

     # File lib/sass/engine.rb, line 188
188:     def source_encoding
189:       check_encoding!
190:       @original_encoding
191:     end
to_css()

Alias for render

Parses the document into its parse tree.

@return [Sass::Tree::Node] The root of the parse tree. @raise [Sass::SyntaxError] if there‘s an error in the document

[Source]

     # File lib/sass/engine.rb, line 176
176:     def to_tree
177:       return _to_tree unless @options[:quiet]
178:       Haml::Util.silence_haml_warnings {_to_tree}
179:     end

Private Instance methods

[Source]

     # File lib/sass/engine.rb, line 195
195:     def _render
196:       rendered = _to_tree.render
197:       return rendered if ruby1_8?
198:       return rendered.encode(source_encoding)
199:     end

[Source]

     # File lib/sass/engine.rb, line 201
201:     def _to_tree
202:       check_encoding!
203: 
204:       if @options[:syntax] == :scss
205:         root = Sass::SCSS::Parser.new(@template).parse
206:       else
207:         root = Tree::RootNode.new(@template)
208:         append_children(root, tree(tabulate(@template)).first, true)
209:       end
210: 
211:       root.options = @options
212:       root
213:     rescue SyntaxError => e
214:       e.modify_backtrace(:filename => @options[:filename], :line => @line)
215:       e.sass_template = @template
216:       raise e
217:     end

[Source]

     # File lib/sass/engine.rb, line 337
337:     def append_children(parent, children, root)
338:       continued_rule = nil
339:       continued_comment = nil
340:       children.each do |line|
341:         child = build_tree(parent, line, root)
342: 
343:         if child.is_a?(Tree::RuleNode) && child.continued?
344:           raise SyntaxError.new("Rules can't end in commas.",
345:             :line => child.line) unless child.children.empty?
346:           if continued_rule
347:             continued_rule.add_rules child
348:           else
349:             continued_rule = child
350:           end
351:           next
352:         end
353: 
354:         if continued_rule
355:           raise SyntaxError.new("Rules can't end in commas.",
356:             :line => continued_rule.line) unless child.is_a?(Tree::RuleNode)
357:           continued_rule.add_rules child
358:           continued_rule.children = child.children
359:           continued_rule, child = nil, continued_rule
360:         end
361: 
362:         if child.is_a?(Tree::CommentNode) && child.silent
363:           if continued_comment &&
364:               child.line == continued_comment.line +
365:               continued_comment.value.count("\n") + 1
366:             continued_comment.value << "\n" << child.value
367:             next
368:           end
369: 
370:           continued_comment = child
371:         end
372: 
373:         check_for_no_children(child)
374:         validate_and_append_child(parent, child, line, root)
375:       end
376: 
377:       raise SyntaxError.new("Rules can't end in commas.",
378:         :line => continued_rule.line) if continued_rule
379: 
380:       parent
381:     end

[Source]

     # File lib/sass/engine.rb, line 320
320:     def build_tree(parent, line, root = false)
321:       @line = line.index
322:       node_or_nodes = parse_line(parent, line, root)
323: 
324:       Array(node_or_nodes).each do |node|
325:         # Node is a symbol if it's non-outputting, like a variable assignment
326:         next unless node.is_a? Tree::Node
327: 
328:         node.line = line.index
329:         node.filename = line.filename
330: 
331:         append_children(node, line.children, false)
332:       end
333: 
334:       node_or_nodes
335:     end

[Source]

     # File lib/sass/engine.rb, line 219
219:     def check_encoding!
220:       return if @checked_encoding
221:       @checked_encoding = true
222:       @template, @original_encoding = check_sass_encoding(@template) do |msg, line|
223:         raise Sass::SyntaxError.new(msg, :line => line)
224:       end
225:     end

[Source]

     # File lib/sass/engine.rb, line 392
392:     def check_for_no_children(node)
393:       return unless node.is_a?(Tree::RuleNode) && node.children.empty?
394:       Haml::Util.haml_warn("WARNING on line \#{node.line}\#{\" of \#{node.filename}\" if node.filename}:\nThis selector doesn't have any properties and will not be rendered.\n".strip)
395:     end

[Source]

     # File lib/sass/engine.rb, line 634
634:     def format_comment_text(text, silent)
635:       content = text.split("\n")
636: 
637:       if content.first && content.first.strip.empty?
638:         removed_first = true
639:         content.shift
640:       end
641: 
642:       return silent ? "//" : "/* */" if content.empty?
643:       content.map! {|l| l.gsub!(/^\*( ?)/, '\1') || (l.empty? ? "" : " ") + l}
644:       content.first.gsub!(/^ /, '') unless removed_first
645:       content.last.gsub!(%r{ ?\*/ *$}, '')
646:       if silent
647:         "//" + content.join("\n//")
648:       else
649:         "/*" + content.join("\n *") + " */"
650:       end
651:     end

[Source]

     # File lib/sass/engine.rb, line 497
497:     def parse_comment(line)
498:       if line[1] == CSS_COMMENT_CHAR || line[1] == SASS_COMMENT_CHAR
499:         silent = line[1] == SASS_COMMENT_CHAR
500:         Tree::CommentNode.new(
501:           format_comment_text(line[2..-1], silent),
502:           silent)
503:       else
504:         Tree::RuleNode.new(parse_interp(line))
505:       end
506:     end

[Source]

     # File lib/sass/engine.rb, line 508
508:     def parse_directive(parent, line, root)
509:       directive, whitespace, value = line.text[1..-1].split(/(\s+)/, 2)
510:       offset = directive.size + whitespace.size + 1 if whitespace
511: 
512:       # If value begins with url( or ",
513:       # it's a CSS @import rule and we don't want to touch it.
514:       if directive == "import"
515:         raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
516:           :line => @line + 1) unless line.children.empty?
517:         if (match = value.match(Sass::SCSS::RX::STRING) || value.match(Sass::SCSS::RX::URI)) &&
518:             !match.post_match.strip.empty? && match.post_match.strip[0] != ?,
519:           return Tree::DirectiveNode.new("@import #{value}")
520:         end
521:         value.split(/,\s*/).map do |f|
522:           f = $1 || $2 || $3 if f =~ Sass::SCSS::RX::STRING || f =~ Sass::SCSS::RX::URI
523:           Tree::ImportNode.new(f)
524:         end
525:       elsif directive == "mixin"
526:         parse_mixin_definition(line)
527:       elsif directive == "include"
528:         parse_mixin_include(line, root)
529:       elsif directive == "for"
530:         parse_for(line, root, value)
531:       elsif directive == "else"
532:         parse_else(parent, line, value)
533:       elsif directive == "while"
534:         raise SyntaxError.new("Invalid while directive '@while': expected expression.") unless value
535:         Tree::WhileNode.new(parse_script(value, :offset => offset))
536:       elsif directive == "if"
537:         raise SyntaxError.new("Invalid if directive '@if': expected expression.") unless value
538:         Tree::IfNode.new(parse_script(value, :offset => offset))
539:       elsif directive == "debug"
540:         raise SyntaxError.new("Invalid debug directive '@debug': expected expression.") unless value
541:         raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath debug directives.",
542:           :line => @line + 1) unless line.children.empty?
543:         offset = line.offset + line.text.index(value).to_i
544:         Tree::DebugNode.new(parse_script(value, :offset => offset))
545:       elsif directive == "extend"
546:         raise SyntaxError.new("Invalid extend directive '@extend': expected expression.") unless value
547:         raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath extend directives.",
548:           :line => @line + 1) unless line.children.empty?
549:         offset = line.offset + line.text.index(value).to_i
550:         Tree::ExtendNode.new(parse_interp(value, offset))
551:       elsif directive == "warn"
552:         raise SyntaxError.new("Invalid warn directive '@warn': expected expression.") unless value
553:         raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath warn directives.",
554:           :line => @line + 1) unless line.children.empty?
555:         offset = line.offset + line.text.index(value).to_i
556:         Tree::WarnNode.new(parse_script(value, :offset => offset))
557:       else
558:         Tree::DirectiveNode.new(line.text)
559:       end
560:     end

[Source]

     # File lib/sass/engine.rb, line 586
586:     def parse_else(parent, line, text)
587:       previous = parent.children.last
588:       raise SyntaxError.new("@else must come after @if.") unless previous.is_a?(Tree::IfNode)
589: 
590:       if text
591:         if text !~ /^if\s+(.+)/
592:           raise SyntaxError.new("Invalid else directive '@else #{text}': expected 'if <expr>'.")
593:         end
594:         expr = parse_script($1, :offset => line.offset + line.text.index($1))
595:       end
596: 
597:       node = Tree::IfNode.new(expr)
598:       append_children(node, line.children, false)
599:       previous.add_else node
600:       nil
601:     end

[Source]

     # File lib/sass/engine.rb, line 562
562:     def parse_for(line, root, text)
563:       var, from_expr, to_name, to_expr = text.scan(/^([^\s]+)\s+from\s+(.+)\s+(to|through)\s+(.+)$/).first
564: 
565:       if var.nil? # scan failed, try to figure out why for error message
566:         if text !~ /^[^\s]+/
567:           expected = "variable name"
568:         elsif text !~ /^[^\s]+\s+from\s+.+/
569:           expected = "'from <expr>'"
570:         else
571:           expected = "'to <expr>' or 'through <expr>'"
572:         end
573:         raise SyntaxError.new("Invalid for directive '@for #{text}': expected #{expected}.")
574:       end
575:       raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
576:       if var.slice!(0) == ?!
577:         offset = line.offset + line.text.index("!" + var) + 1
578:         Script.var_warning(var, @line, offset, @options[:filename])
579:       end
580: 
581:       parsed_from = parse_script(from_expr, :offset => line.offset + line.text.index(from_expr))
582:       parsed_to = parse_script(to_expr, :offset => line.offset + line.text.index(to_expr))
583:       Tree::ForNode.new(var, parsed_from, parsed_to, to_name == 'to')
584:     end

[Source]

     # File lib/sass/engine.rb, line 653
653:     def parse_interp(text, offset = 0)
654:       self.class.parse_interp(text, @line, offset, :filename => @filename)
655:     end

[Source]

     # File lib/sass/engine.rb, line 401
401:     def parse_line(parent, line, root)
402:       case line.text[0]
403:       when PROPERTY_CHAR
404:         if line.text[1] == PROPERTY_CHAR ||
405:             (@options[:property_syntax] == :new &&
406:              line.text =~ PROPERTY_OLD && $3.empty?)
407:           # Support CSS3-style pseudo-elements,
408:           # which begin with ::,
409:           # as well as pseudo-classes
410:           # if we're using the new property syntax
411:           Tree::RuleNode.new(parse_interp(line.text))
412:         else
413:           name, eq, value = line.text.scan(PROPERTY_OLD)[0]
414:           raise SyntaxError.new("Invalid property: \"#{line.text}\".",
415:             :line => @line) if name.nil? || value.nil?
416:           parse_property(name, parse_interp(name), eq, value, :old, line)
417:         end
418:       when ?!, ?$
419:         parse_variable(line)
420:       when COMMENT_CHAR
421:         parse_comment(line.text)
422:       when DIRECTIVE_CHAR
423:         parse_directive(parent, line, root)
424:       when ESCAPE_CHAR
425:         Tree::RuleNode.new(parse_interp(line.text[1..-1]))
426:       when MIXIN_DEFINITION_CHAR
427:         parse_mixin_definition(line)
428:       when MIXIN_INCLUDE_CHAR
429:         if line.text[1].nil? || line.text[1] == ?\s
430:           Tree::RuleNode.new(parse_interp(line.text))
431:         else
432:           parse_mixin_include(line, root)
433:         end
434:       else
435:         parse_property_or_rule(line)
436:       end
437:     end

[Source]

     # File lib/sass/engine.rb, line 604
604:     def parse_mixin_definition(line)
605:       name, arg_string = line.text.scan(MIXIN_DEF_RE).first
606:       raise SyntaxError.new("Invalid mixin \"#{line.text[1..-1]}\".") if name.nil?
607: 
608:       offset = line.offset + line.text.size - arg_string.size
609:       args = Script::Parser.new(arg_string.strip, @line, offset, @options).
610:         parse_mixin_definition_arglist
611:       default_arg_found = false
612:       Tree::MixinDefNode.new(name, args)
613:     end

[Source]

     # File lib/sass/engine.rb, line 616
616:     def parse_mixin_include(line, root)
617:       name, arg_string = line.text.scan(MIXIN_INCLUDE_RE).first
618:       raise SyntaxError.new("Invalid mixin include \"#{line.text}\".") if name.nil?
619: 
620:       offset = line.offset + line.text.size - arg_string.size
621:       args = Script::Parser.new(arg_string.strip, @line, offset, @options).
622:         parse_mixin_include_arglist
623:       raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath mixin directives.",
624:         :line => @line + 1) unless line.children.empty?
625:       Tree::MixinNode.new(name, args)
626:     end

[Source]

     # File lib/sass/engine.rb, line 461
461:     def parse_property(name, parsed_name, eq, value, prop, line)
462:       if value.strip.empty?
463:         expr = Sass::Script::String.new("")
464:       else
465:         expr = parse_script(value, :offset => line.offset + line.text.index(value))
466: 
467:         if eq.strip[0] == SCRIPT_CHAR
468:           expr.context = :equals
469:           Script.equals_warning("properties", name,
470:             Sass::Tree::PropNode.val_to_sass(expr, @options), false,
471:             @line, line.offset + 1, @options[:filename])
472:         end
473:       end
474:       Tree::PropNode.new(parse_interp(name), expr, prop)
475:     end

[Source]

     # File lib/sass/engine.rb, line 439
439:     def parse_property_or_rule(line)
440:       scanner = StringScanner.new(line.text)
441:       hack_char = scanner.scan(/[:\*\.]|\#(?!\{)/)
442:       parser = Sass::SCSS::SassParser.new(scanner, @line)
443: 
444:       unless res = parser.parse_interp_ident
445:         return Tree::RuleNode.new(parse_interp(line.text))
446:       end
447:       res.unshift(hack_char) if hack_char
448:       if comment = scanner.scan(Sass::SCSS::RX::COMMENT)
449:         res << comment
450:       end
451: 
452:       name = line.text[0...scanner.pos]
453:       if scanner.scan(/\s*([:=])(?:\s|$)/)
454:         parse_property(name, res, scanner[1], scanner.rest, :new, line)
455:       else
456:         res.pop if comment
457:         Tree::RuleNode.new(res + parse_interp(scanner.rest))
458:       end
459:     end

[Source]

     # File lib/sass/engine.rb, line 628
628:     def parse_script(script, options = {})
629:       line = options[:line] || @line
630:       offset = options[:offset] || 0
631:       Script.parse(script, line, offset, @options)
632:     end

[Source]

     # File lib/sass/engine.rb, line 477
477:     def parse_variable(line)
478:       name, op, value, default = line.text.scan(Script::MATCH)[0]
479:       guarded = op =~ /^\|\|/
480:       raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath variable declarations.",
481:         :line => @line + 1) unless line.children.empty?
482:       raise SyntaxError.new("Invalid variable: \"#{line.text}\".",
483:         :line => @line) unless name && value
484:       Script.var_warning(name, @line, line.offset + 1, @options[:filename]) if line.text[0] == ?!
485: 
486:       expr = parse_script(value, :offset => line.offset + line.text.index(value))
487:       if op =~ /=$/
488:         expr.context = :equals
489:         type = guarded ? "variable defaults" : "variables"
490:         Script.equals_warning(type, "$#{name}", expr.to_sass,
491:           guarded, @line, line.offset + 1, @options[:filename])
492:       end
493: 
494:       Tree::VariableNode.new(name, expr, default || guarded)
495:     end

[Source]

     # File lib/sass/engine.rb, line 227
227:     def tabulate(string)
228:       tab_str = nil
229:       comment_tab_str = nil
230:       first = true
231:       lines = []
232:       string.gsub(/\r|\n|\r\n|\r\n/, "\n").scan(/^.*?$/).each_with_index do |line, index|
233:         index += (@options[:line] || 1)
234:         if line.strip.empty?
235:           lines.last.text << "\n" if lines.last && lines.last.comment?
236:           next
237:         end
238: 
239:         line_tab_str = line[/^\s*/]
240:         unless line_tab_str.empty?
241:           if tab_str.nil?
242:             comment_tab_str ||= line_tab_str
243:             next if try_comment(line, lines.last, "", comment_tab_str, index)
244:             comment_tab_str = nil
245:           end
246: 
247:           tab_str ||= line_tab_str
248: 
249:           raise SyntaxError.new("Indenting at the beginning of the document is illegal.",
250:             :line => index) if first
251: 
252:           raise SyntaxError.new("Indentation can't use both tabs and spaces.",
253:             :line => index) if tab_str.include?(?\s) && tab_str.include?(?\t)
254:         end
255:         first &&= !tab_str.nil?
256:         if tab_str.nil?
257:           lines << Line.new(line.strip, 0, index, 0, @options[:filename], [])
258:           next
259:         end
260: 
261:         comment_tab_str ||= line_tab_str
262:         if try_comment(line, lines.last, tab_str * lines.last.tabs, comment_tab_str, index)
263:           next
264:         else
265:           comment_tab_str = nil
266:         end
267: 
268:         line_tabs = line_tab_str.scan(tab_str).size
269:         if tab_str * line_tabs != line_tab_str
270:           message = "Inconsistent indentation: \#{Haml::Shared.human_indentation line_tab_str, true} used for indentation,\nbut the rest of the document was indented using \#{Haml::Shared.human_indentation tab_str}.\n".strip.gsub("\n", ' ')
271:           raise SyntaxError.new(message, :line => index)
272:         end
273: 
274:         lines << Line.new(line.strip, line_tabs, index, tab_str.size, @options[:filename], [])
275:       end
276:       lines
277:     end

[Source]

     # File lib/sass/engine.rb, line 301
301:     def tree(arr, i = 0)
302:       return [], i if arr[i].nil?
303: 
304:       base = arr[i].tabs
305:       nodes = []
306:       while (line = arr[i]) && line.tabs >= base
307:         if line.tabs > base
308:           raise SyntaxError.new("The line was indented #{line.tabs - base} levels deeper than the previous line.",
309:             :line => line.index) if line.tabs > base + 1
310: 
311:           nodes.last.children, i = tree(arr, i)
312:         else
313:           nodes << line
314:           i += 1
315:         end
316:       end
317:       return nodes, i
318:     end

[Source]

     # File lib/sass/engine.rb, line 283
283:     def try_comment(line, last, tab_str, comment_tab_str, index)
284:       return unless last && last.comment?
285:       # Nested comment stuff must be at least one whitespace char deeper
286:       # than the normal indentation
287:       return unless line =~ /^#{tab_str}\s/
288:       unless line =~ /^(?:#{comment_tab_str})(.*)$/
289:         raise SyntaxError.new("Inconsistent indentation:\nprevious line was indented by \#{Haml::Shared.human_indentation comment_tab_str},\nbut this line was indented by \#{Haml::Shared.human_indentation line[/^\\s*/]}.\n".strip.gsub("\n", " "), :line => index)
290:       end
291: 
292:       last.text << "\n" << $1
293:       true
294:     end

[Source]

     # File lib/sass/engine.rb, line 383
383:     def validate_and_append_child(parent, child, line, root)
384:       case child
385:       when Array
386:         child.each {|c| validate_and_append_child(parent, c, line, root)}
387:       when Tree::Node
388:         parent << child
389:       end
390:     end

[Validate]