Class Sass::CSS
In: lib/sass/css.rb
Parent: Object
StandardError SyntaxError ValueNode CommentNode DirectiveNode AttrNode RuleNode Node Literal Number Color Nil String CSS Engine Operation lib/sass/css.rb lib/sass/error.rb lib/sass/engine.rb lib/sass/tree/value_node.rb lib/sass/tree/node.rb lib/sass/tree/comment_node.rb lib/sass/tree/directive_node.rb lib/sass/tree/rule_node.rb lib/sass/tree/attr_node.rb Tree Plugin lib/sass/constant/number.rb lib/sass/constant/operation.rb lib/sass/constant/nil.rb lib/sass/constant/literal.rb lib/sass/constant/color.rb lib/sass/constant/string.rb Constant Sass dot/m_33_0.png

This class contains the functionality used in the +css2sass+ utility, namely converting CSS documents to Sass templates.

Methods

Public Class methods

Creates a new instance of Sass::CSS that will compile the given document to a Sass string when render is called.

[Source]

     # File lib/sass/css.rb, line 114
114:     def initialize(template, options = {})
115:       if template.is_a? IO
116:         template = template.read
117:       end
118: 
119:       @options = options
120:       @template = StringScanner.new(template)
121:     end

Public Instance methods

Processes the document and returns the result as a string containing the CSS template.

[Source]

     # File lib/sass/css.rb, line 125
125:     def render
126:       begin
127:         build_tree.to_sass(@options).strip + "\n"
128:       rescue Exception => err
129:         line = @template.string[0...@template.pos].split("\n").size
130: 
131:         err.backtrace.unshift "(css):#{line}"
132:         raise err
133:       end
134:     end

Private Instance methods

[Source]

     # File lib/sass/css.rb, line 210
210:     def assert_match(re)
211:       if !@template.scan(re)
212:         line = @template.string[0..@template.pos].count "\n"
213:         # Display basic regexps as plain old strings
214:         expected = re.source == Regexp.escape(re.source) ? "\"#{re.source}\"" : re.inspect
215:         raise Exception.new("Invalid CSS on line #{line}: expected #{expected}")
216:       end
217:       whitespace
218:     end

[Source]

     # File lib/sass/css.rb, line 179
179:     def attributes(rule)
180:       while @template.scan(/[^:\}\s]+/)
181:         name = @template[0]
182:         whitespace
183: 
184:         assert_match /:/
185: 
186:         value = ''
187:         while @template.scan(/[^;\s\}]+/)
188:           value << @template[0] << whitespace
189:         end
190: 
191:         assert_match /(;|(?=\}))/
192:         rule << Tree::AttrNode.new(name, value, nil)
193:       end
194: 
195:       assert_match /\}/
196:     end

[Source]

     # File lib/sass/css.rb, line 138
138:     def build_tree
139:       root = Tree::Node.new(nil)
140:       whitespace
141:       rules              root
142:       expand_commas      root
143:       parent_ref_rules   root
144:       remove_parent_refs root
145:       flatten_rules      root
146:       fold_commas        root
147:       root
148:     end

Transform

  foo, bar, baz
    color: blue

into

  foo
    color: blue
  bar
    color: blue
  baz
    color: blue

Yes, this expands the amount of code, but it‘s necessary to get nesting to work properly.

[Source]

     # File lib/sass/css.rb, line 236
236:     def expand_commas(root)
237:       root.children.map! do |child|
238:         next child unless Tree::RuleNode === child && child.rule.include?(',')
239:         child.rule.split(',').map do |rule|
240:           node = Tree::RuleNode.new(rule.strip, {})
241:           node.children = child.children
242:           node
243:         end
244:       end
245:       root.children.flatten!
246:     end

[Source]

     # File lib/sass/css.rb, line 347
347:     def flatten_rule(rule)
348:       while rule.children.size == 1 && rule.children.first.is_a?(Tree::RuleNode)
349:         child = rule.children.first
350: 
351:         if child.rule[0] == ?&
352:           rule.rule = child.rule.gsub /^&/, rule.rule
353:         else
354:           rule.rule = "#{rule.rule} #{child.rule}"
355:         end
356: 
357:         rule.children = child.children
358:       end
359: 
360:       flatten_rules(rule)
361:     end

Flatten rules so that

  foo
    bar
      baz
        color: red

becomes

  foo bar baz
    color: red

and

  foo
    &.bar
      color: blue

becomes

  foo.bar
    color: blue

[Source]

     # File lib/sass/css.rb, line 343
343:     def flatten_rules(root)
344:       root.children.each { |child| flatten_rule(child) if child.is_a?(Tree::RuleNode) }
345:     end

Transform

  foo
    bar
      color: blue
    baz
      color: blue

into

  foo
    bar, baz
      color: blue

[Source]

     # File lib/sass/css.rb, line 377
377:     def fold_commas(root)
378:       prev_rule = nil
379:       root.children.map! do |child|
380:         next child unless child.is_a?(Tree::RuleNode)
381: 
382:         if prev_rule && prev_rule.children == child.children
383:           prev_rule.rule << ", #{child.rule}"
384:           next nil
385:         end
386: 
387:         fold_commas(child)
388:         prev_rule = child
389:         child
390:       end
391:       root.children.compact!
392:     end

Make rules use parent refs so that

  foo
    color: green
  foo.bar
    color: blue

becomes

  foo
    color: green
    &.bar
      color: blue

This has the side effect of nesting rules, so that

  foo
    color: green
  foo bar
    color: red
  foo baz
    color: blue

becomes

  foo
    color: green
    & bar
      color: red
    & baz
      color: blue

[Source]

     # File lib/sass/css.rb, line 281
281:     def parent_ref_rules(root)
282:       rules = OrderedHash.new
283:       root.children.select { |c| Tree::RuleNode === c }.each do |child|
284:         root.children.delete child
285:         first, rest = child.rule.scan(/^(&?(?: .|[^ ])[^.#: \[]*)([.#: \[].*)?$/).first
286:         rules[first] ||= Tree::RuleNode.new(first, nil)
287:         if rest
288:           child.rule = "&" + rest
289:           rules[first] << child
290:         else
291:           rules[first].children += child.children
292:         end
293:       end
294: 
295:       rules.values.each { |v| parent_ref_rules(v) }
296:       root.children += rules.values
297:     end

Remove useless parent refs so that

  foo
    & bar
      color: blue

becomes

  foo
    bar
      color: blue

[Source]

     # File lib/sass/css.rb, line 311
311:     def remove_parent_refs(root)
312:       root.children.each do |child|
313:         if child.is_a?(Tree::RuleNode)
314:           child.rule.gsub! /^& +/, ''
315:           remove_parent_refs child
316:         end
317:       end
318:     end

[Source]

     # File lib/sass/css.rb, line 157
157:     def rule
158:       return unless rule = @template.scan(/[^\{\};]+/)
159:       rule.strip!
160:       directive = rule[0] == ?@
161: 
162:       if directive
163:         node = Tree::DirectiveNode.new(rule, nil)
164:         return node if @template.scan(/;/)
165: 
166:         assert_match /\{/
167:         whitespace
168: 
169:         rules(node)
170:         return node
171:       end
172: 
173:       assert_match /\{/
174:       node = Tree::RuleNode.new(rule, nil)
175:       attributes(node)
176:       return node
177:     end

[Source]

     # File lib/sass/css.rb, line 150
150:     def rules(root)
151:       while r = rule
152:         root << r
153:         whitespace
154:       end
155:     end

[Source]

     # File lib/sass/css.rb, line 198
198:     def whitespace
199:       space = @template.scan(/\s*/) || ''
200: 
201:       # If we've hit a comment,
202:       # go past it and look for more whitespace
203:       if @template.scan(/\/\*/)
204:         @template.scan_until(/\*\//)
205:         return space + whitespace
206:       end
207:       return space
208:     end

[Validate]