haml-edge 2.3.93 → 2.3.94

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,16 +8,16 @@ module Sass
8
8
  class Lexer
9
9
  # A struct containing information about an individual token.
10
10
  #
11
- # `type`: [{Symbol}]
11
+ # `type`: \[{Symbol}\]
12
12
  # : The type of token.
13
13
  #
14
- # `value`: [{Object}]
14
+ # `value`: \[{Object}\]
15
15
  # : The Ruby object corresponding to the value of the token.
16
16
  #
17
- # `line`: [{Fixnum}]
17
+ # `line`: \[{Fixnum}\]
18
18
  # : The line of the source file on which the token appears.
19
19
  #
20
- # `offset`: [{Fixnum}]
20
+ # `offset`: \[{Fixnum}\]
21
21
  # : The number of bytes into the line the SassScript token appeared.
22
22
  Token = Struct.new(:type, :value, :line, :offset)
23
23
 
@@ -55,7 +55,7 @@ module Sass
55
55
  :variable => /!([\w-]+)/,
56
56
  :ident => /(\\.|[^\s\\+*\/%(),=!])+/,
57
57
  :number => /(-)?(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
58
- :color => /\##{"([0-9a-fA-F]{1,2})" * 3}|(#{Color::HTML4_COLORS.keys.join("|")})(?!\()/,
58
+ :color => /\##{"([0-9a-fA-F]{1,2})" * 3}|(#{Color::HTML4_COLORS.keys.join("|")})(?![^\s+*\/%),=!])/,
59
59
  :bool => /(true|false)\b/,
60
60
  :op => %r{(#{Regexp.union(*OP_NAMES.map{|s| Regexp.new(Regexp.escape(s) + (s =~ /\w$/ ? '(?:\b|$)' : ''))})})}
61
61
  }
@@ -46,9 +46,9 @@ module Sass::Tree
46
46
  if was_prop
47
47
  result[-1] = "\n"
48
48
  end
49
- rendered = child.to_s(tabs + 1)
50
- rendered.lstrip! if first
51
- result << rendered
49
+ rendered = child.to_s(tabs + 1).dup
50
+ rendered = rendered.lstrip if first
51
+ result << rendered.rstrip + "\n"
52
52
  end
53
53
  was_prop = child.is_a?(PropNode)
54
54
  first = false
@@ -14,24 +14,35 @@ module Sass
14
14
 
15
15
  protected
16
16
 
17
- # Computes the CSS for the imported file.
18
- #
19
- # @param args [Array] Ignored
20
- def _to_s(*args)
21
- @to_s ||= (style == :compressed ? super.strip : super)
17
+ # @see Node#_cssize
18
+ def _cssize(*args)
19
+ super.children
22
20
  rescue Sass::SyntaxError => e
23
21
  e.modify_backtrace(:filename => children.first.filename)
24
22
  e.add_backtrace(:filename => @filename, :line => @line)
25
23
  raise e
26
24
  end
27
25
 
28
- # Parses the imported file
29
- # and runs the dynamic Sass for it.
26
+ # Returns a static DirectiveNode if this is importing a CSS file,
27
+ # or parses and includes the imported Sass file.
30
28
  #
31
29
  # @param environment [Sass::Environment] The lexical environment containing
32
30
  # variable and mixin values
33
- def perform!(environment)
34
- return unless full_filename = import
31
+ def _perform(environment)
32
+ full_filename = import
33
+ return DirectiveNode.new("@import url(#{full_filename})") if full_filename =~ /\.css$/
34
+
35
+ node = dup
36
+ node.perform!(environment, full_filename)
37
+ node
38
+ end
39
+
40
+ # Parses the imported file and runs the dynamic Sass for it.
41
+ #
42
+ # @param environment [Sass::Environment] The lexical environment containing
43
+ # variable and mixin values
44
+ # @param full_filename [String] The full path to the Sass file to import
45
+ def perform!(environment, full_filename)
35
46
  root = Sass::Files.tree_for(full_filename, @options)
36
47
  @template = root.template
37
48
  self.children = root.children
@@ -51,18 +62,9 @@ module Sass
51
62
  end
52
63
 
53
64
  def import
54
- begin
55
- full_filename = Sass::Files.find_file_to_import(@imported_filename, import_paths)
56
- rescue Exception => e
57
- raise SyntaxError.new(e.message, :line => self.line, :filename => @filename)
58
- end
59
-
60
- if full_filename =~ /\.css$/
61
- @to_s = "@import url(#{full_filename});"
62
- return false
63
- end
64
-
65
- return full_filename
65
+ Sass::Files.find_file_to_import(@imported_filename, import_paths)
66
+ rescue Exception => e
67
+ raise SyntaxError.new(e.message, :line => self.line, :filename => @filename)
66
68
  end
67
69
  end
68
70
  end
@@ -1,7 +1,9 @@
1
1
  require 'sass/tree/node'
2
2
 
3
3
  module Sass::Tree
4
- # A dynamic node representing a mixin include.
4
+ # A static node representing a mixin include.
5
+ # When in a static tree, the sole purpose is to wrap exceptions
6
+ # to add the mixin to the backtrace.
5
7
  #
6
8
  # @see Sass::Tree
7
9
  class MixinNode < Node
@@ -13,17 +15,30 @@ module Sass::Tree
13
15
  super()
14
16
  end
15
17
 
18
+ # @see Node#cssize
19
+ def cssize(parent = nil)
20
+ _cssize(parent) # Pass on the parent even if it's not a MixinNode
21
+ end
22
+
16
23
  protected
17
24
 
25
+ # @see Node#_cssize
26
+ def _cssize(parent)
27
+ children.map {|c| c.cssize(parent)}.flatten
28
+ rescue Sass::SyntaxError => e
29
+ e.modify_backtrace(:mixin => @name, :line => line)
30
+ e.add_backtrace(:filename => filename, :line => line)
31
+ raise e
32
+ end
33
+
18
34
  # Runs the mixin.
19
35
  #
20
36
  # @param environment [Sass::Environment] The lexical environment containing
21
37
  # variable and mixin values
22
- # @return [Array<Tree::Node>] The resulting static nodes
23
38
  # @raise [Sass::SyntaxError] if there is no mixin with the given name
24
39
  # @raise [Sass::SyntaxError] if an incorrect number of arguments was passed
25
40
  # @see Sass::Tree
26
- def _perform(environment)
41
+ def perform!(environment)
27
42
  raise Sass::SyntaxError.new("Undefined mixin '#{@name}'.") unless mixin = environment.mixin(@name)
28
43
 
29
44
  raise Sass::SyntaxError.new(<<END.gsub("\n", "")) if mixin.args.size < @args.size
@@ -42,7 +57,12 @@ END
42
57
  raise Sass::SyntaxError.new("Mixin #{@name} is missing parameter #{var.inspect}.") unless env.var(var.name)
43
58
  env
44
59
  end
45
- mixin.tree.map {|c| c.perform(environment)}.flatten
60
+
61
+ self.children = mixin.tree.map {|c| c.perform(environment)}.flatten
62
+ rescue Sass::SyntaxError => e
63
+ e.modify_backtrace(:mixin => @name, :line => @line)
64
+ e.add_backtrace(:line => @line)
65
+ raise e
46
66
  end
47
67
  end
48
68
  end
@@ -1,19 +1,28 @@
1
1
  module Sass
2
2
  # A namespace for nodes in the Sass parse tree.
3
3
  #
4
- # The Sass parse tree has two states.
5
- # When it's first parsed, it has nodes for mixin definitions
6
- # and for loops and so forth,
4
+ # The Sass parse tree has three states: dynamic, static Sass, and static CSS.
5
+ #
6
+ # When it's first parsed, a Sass document is in the dynamic state.
7
+ # It has nodes for mixin definitions and `@for` loops and so forth,
7
8
  # in addition to nodes for CSS rules and properties.
9
+ # Nodes that only appear in this state are called **dynamic nodes**.
8
10
  #
9
- # However, {Tree::Node#perform} returns a different sort of tree.
10
- # This tree maps more closely to the resulting CSS document
11
- # than it does to the original Sass document.
12
- # It still has nodes for CSS rules and properties,
11
+ # {Tree::Node#perform} returns a static Sass tree, which is different.
12
+ # It still has nodes for CSS rules and properties
13
13
  # but it doesn't have any dynamic-generation-related nodes.
14
+ # The nodes in this state are in the same structure as the Sass document:
15
+ # rules and properties are nested beneath one another.
16
+ # Nodes that can be in this state or in the dynamic state
17
+ # are called **static nodes**.
18
+ #
19
+ # {Tree::Node#cssize} then returns a static CSS tree.
20
+ # This is like a static Sass tree,
21
+ # but the structure exactly mirrors that of the generated CSS.
22
+ # Rules and properties can't be nested beneath one another in this state.
14
23
  #
15
- # Nodes that only appear in the pre-perform state are called **dynamic nodes**;
16
- # those that appear in both states are called **static nodes**.
24
+ # Finally, {Tree::Node#to_s} can be called on a static CSS tree
25
+ # to get the actual CSS code as a string.
17
26
  module Tree
18
27
  # The abstract superclass of all parse-tree nodes.
19
28
  class Node
@@ -99,7 +108,7 @@ module Sass
99
108
  # @see #perform
100
109
  # @see #to_s
101
110
  def render
102
- perform(Environment.new).to_s
111
+ perform(Environment.new).cssize.to_s
103
112
  end
104
113
 
105
114
  # True if \{#to\_s} will return `nil`;
@@ -116,15 +125,15 @@ module Sass
116
125
  @options[:style]
117
126
  end
118
127
 
119
- # Computes the CSS corresponding to this Sass tree.
128
+ # Computes the CSS corresponding to this static CSS tree.
120
129
  #
130
+ # \{#to_s} shouldn't be overridden directly; instead, override \{#\_to\_s}.
121
131
  # Only static-node subclasses need to implement \{#to\_s}.
122
132
  #
123
133
  # This may return `nil`, but it will only do so if \{#invisible?} is true.
124
134
  #
125
135
  # @param args [Array] Passed on to \{#\_to\_s}
126
136
  # @return [String, nil] The resulting CSS
127
- # @raise [Sass::SyntaxError] if some element of the tree is invalid
128
137
  # @see Sass::Tree
129
138
  def to_s(*args)
130
139
  _to_s(*args)
@@ -133,15 +142,31 @@ module Sass
133
142
  raise e
134
143
  end
135
144
 
136
- # Runs the dynamic Sass code:
145
+ # Converts a static Sass tree (e.g. the output of \{#perform})
146
+ # into a static CSS tree.
147
+ #
148
+ # \{#cssize} shouldn't be overridden directly;
149
+ # instead, override \{#\_cssize} or \{#cssize!}.
150
+ #
151
+ # @param parent [Node, nil] The parent node of this node.
152
+ # This should only be non-nil if the parent is the same class as this node
153
+ # @return [Tree::Node] The resulting tree of static nodes
154
+ # @raise [Sass::SyntaxError] if some element of the tree is invalid
155
+ # @see Sass::Tree
156
+ def cssize(parent = nil)
157
+ _cssize((parent if parent.class == self.class))
158
+ rescue Sass::SyntaxError => e
159
+ e.modify_backtrace(:filename => filename, :line => line)
160
+ raise e
161
+ end
162
+
163
+ # Converts a dynamic tree into a static Sass tree.
164
+ # That is, runs the dynamic Sass code:
137
165
  # mixins, variables, control directives, and so forth.
138
166
  # This doesn't modify this node or any of its children.
139
167
  #
140
168
  # \{#perform} shouldn't be overridden directly;
141
- # if you want to return a new node (or list of nodes),
142
- # override \{#\_perform};
143
- # if you want to destructively modify this node,
144
- # override \{#perform!}.
169
+ # instead, override \{#\_perform} or \{#perform!}.
145
170
  #
146
171
  # @param environment [Sass::Environment] The lexical environment containing
147
172
  # variable and mixin values
@@ -159,15 +184,46 @@ module Sass
159
184
 
160
185
  # Computes the CSS corresponding to this particular Sass node.
161
186
  #
187
+ # This method should never raise {Sass::SyntaxError}s.
188
+ # Such errors will not be properly annotated with Sass backtrace information.
189
+ # All error conditions should be checked in earlier transformations,
190
+ # such as \{#cssize} and \{#perform}.
191
+ #
162
192
  # @param args [Array] ignored
163
193
  # @return [String, nil] The resulting CSS
164
- # @raise [Sass::SyntaxError] if some element of the tree is invalid
165
194
  # @see #to_s
166
195
  # @see Sass::Tree
167
196
  def _to_s
168
197
  raise NotImplementedError.new("All static-node subclasses of Sass::Tree::Node must override #_to_s or #to_s.")
169
198
  end
170
199
 
200
+ # Converts this static Sass node into a static CSS node,
201
+ # returning the new node.
202
+ # This doesn't modify this node or any of its children.
203
+ #
204
+ # @param parent [Node, nil] The parent node of this node.
205
+ # This should only be non-nil if the parent is the same class as this node
206
+ # @return [Tree::Node, Array<Tree::Node>] The resulting static CSS nodes
207
+ # @raise [Sass::SyntaxError] if some element of the tree is invalid
208
+ # @see #cssize
209
+ # @see Sass::Tree
210
+ def _cssize(parent)
211
+ node = dup
212
+ node.cssize!(parent)
213
+ node
214
+ end
215
+
216
+ # Destructively converts this static Sass node into a static CSS node.
217
+ # This *does* modify this node,
218
+ # but will be run non-destructively by \{#\_cssize\}.
219
+ #
220
+ # @param parent [Node, nil] The parent node of this node.
221
+ # This should only be non-nil if the parent is the same class as this node
222
+ # @see #cssize
223
+ def cssize!(parent)
224
+ self.children = children.map {|c| c.cssize(self)}.flatten
225
+ end
226
+
171
227
  # Runs any dynamic Sass code in this particular node.
172
228
  # This doesn't modify this node or any of its children.
173
229
  #
@@ -18,13 +18,13 @@ module Sass::Tree
18
18
  # relative to a normal property.
19
19
  # This is only greater than 0 in the case that:
20
20
  #
21
- # * This node is in a static tree
21
+ # * This node is in a CSS tree
22
22
  # * The style is :nested
23
23
  # * This is a child property of another property
24
24
  # * The parent property has a value, and thus will be rendered
25
25
  #
26
26
  # @return [Fixnum]
27
- attr_accessor :indentation
27
+ attr_accessor :tabs
28
28
 
29
29
  # @param name [String] See \{#name}
30
30
  # @param value [String] See \{#value}
@@ -33,7 +33,7 @@ module Sass::Tree
33
33
  def initialize(name, value, prop_syntax)
34
34
  @name = name
35
35
  @value = value
36
- @indentation = 0
36
+ @tabs = 0
37
37
  @prop_syntax = prop_syntax
38
38
  super()
39
39
  end
@@ -63,34 +63,37 @@ module Sass::Tree
63
63
  #
64
64
  # @param tabs [Fixnum] The level of indentation for the CSS
65
65
  # @return [String] The resulting CSS
66
- # @raise [Sass::SyntaxError] if the property uses invalid syntax
67
66
  def _to_s(tabs)
68
- if @options[:property_syntax] == :old && @prop_syntax == :new
69
- raise Sass::SyntaxError.new("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.")
70
- elsif @options[:property_syntax] == :new && @prop_syntax == :old
71
- raise Sass::SyntaxError.new("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.")
72
- elsif value[-1] == ?;
73
- raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no \";\" required at end-of-line).")
74
- elsif value.empty?
75
- raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no value)." +
76
- pseudo_class_selector_message)
77
- end
78
-
79
- to_return = ' ' * (tabs - 1 + indentation) + name + ":" +
67
+ to_return = ' ' * (tabs - 1 + self.tabs) + name + ":" +
80
68
  (style == :compressed ? '' : ' ') + value + (style == :compressed ? "" : ";")
81
69
  end
82
70
 
83
- # Returns this node's fully-resolved child properties, and/or this node.
71
+ # Converts nested properties into flat properties.
84
72
  #
85
- # @param environment [Sass::Environment] The lexical environment containing
86
- # variable and mixin values
87
- def _perform(environment)
73
+ # @param parent [PropNode, nil] The parent node of this node,
74
+ # or nil if the parent isn't a {PropNode}
75
+ # @raise [Sass::SyntaxError] if the property uses invalid syntax
76
+ def _cssize(parent)
88
77
  node = super
89
78
  result = node.children.dup
90
- result.unshift(node) if !node.value.empty? || node.children.empty?
79
+ if !node.value.empty? || node.children.empty?
80
+ node.send(:check!)
81
+ result.unshift(node)
82
+ end
91
83
  result
92
84
  end
93
85
 
86
+ # Updates the name and indentation of this node based on the parent name
87
+ # and nesting level.
88
+ #
89
+ # @param parent [PropNode, nil] The parent node of this node,
90
+ # or nil if the parent isn't a {PropNode}
91
+ def cssize!(parent)
92
+ self.name = "#{parent.name}-#{name}" if parent
93
+ self.tabs = parent.tabs + (parent.value.empty? ? 0 : 1) if parent && style == :nested
94
+ super
95
+ end
96
+
94
97
  # Runs any SassScript that may be embedded in the property,
95
98
  # and invludes the parent property, if any.
96
99
  #
@@ -100,12 +103,6 @@ module Sass::Tree
100
103
  @name = interpolate(@name, environment)
101
104
  @value = @value.is_a?(String) ? interpolate(@value, environment) : @value.perform(environment).to_s
102
105
  super
103
- # Once we've called super, the child nodes have been dup'ed
104
- # so we can destructively modify them
105
- children.select {|c| c.is_a?(PropNode)}.each do |c|
106
- c.name = "#{name}-#{c.name}"
107
- c.indentation += 1 if style == :nested && !@value.empty?
108
- end
109
106
  end
110
107
 
111
108
  # Returns an error message if the given child node is invalid,
@@ -122,6 +119,19 @@ module Sass::Tree
122
119
 
123
120
  private
124
121
 
122
+ def check!
123
+ if @options[:property_syntax] == :old && @prop_syntax == :new
124
+ raise Sass::SyntaxError.new("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.")
125
+ elsif @options[:property_syntax] == :new && @prop_syntax == :old
126
+ raise Sass::SyntaxError.new("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.")
127
+ elsif value[-1] == ?;
128
+ raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no \";\" required at end-of-line).")
129
+ elsif value.empty?
130
+ raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no value)." +
131
+ pseudo_class_selector_message)
132
+ end
133
+ end
134
+
125
135
  def declaration
126
136
  (@prop_syntax == :new ? "#{name}: #{value}" : ":#{name} #{value}").strip
127
137
  end
@@ -13,7 +13,7 @@ module Sass
13
13
  @template = template
14
14
  end
15
15
 
16
- # @see \{Node#to\_s}
16
+ # @see Node#to_s
17
17
  def to_s(*args)
18
18
  super
19
19
  rescue Sass::SyntaxError => e
@@ -21,7 +21,7 @@ module Sass
21
21
  raise e
22
22
  end
23
23
 
24
- # @see \{Node#perform}
24
+ # @see Node#perform
25
25
  def perform(environment)
26
26
  environment.options = @options if environment.options.nil? || environment.options.empty?
27
27
  super
@@ -30,23 +30,38 @@ module Sass
30
30
  raise e
31
31
  end
32
32
 
33
+ # @see Node#cssize
34
+ def cssize(*args)
35
+ super
36
+ rescue Sass::SyntaxError => e
37
+ e.sass_template = @template
38
+ raise e
39
+ end
40
+
33
41
  protected
34
42
 
43
+ # Destructively converts this static Sass node into a static CSS node,
44
+ # and checks that there are no properties at root level.
45
+ #
46
+ # @param parent [Node, nil] The parent node of this node.
47
+ # This should only be non-nil if the parent is the same class as this node
48
+ # @see Node#cssize!
49
+ def cssize!(parent)
50
+ super
51
+ return unless child = children.find {|c| c.is_a?(PropNode)}
52
+ message = "Properties aren't allowed at the root of a document." +
53
+ child.pseudo_class_selector_message
54
+ raise Sass::SyntaxError.new(message, :line => child.line)
55
+ end
56
+
35
57
  # Computes the CSS corresponding to this Sass tree.
36
58
  #
37
59
  # @param args [Array] ignored
38
60
  # @return [String] The resulting CSS
39
- # @raise [Sass::SyntaxError] if some element of the tree is invalid
40
61
  # @see Sass::Tree
41
62
  def _to_s(*args)
42
63
  result = String.new
43
64
  children.each do |child|
44
- if child.is_a? PropNode
45
- message = "Properties aren't allowed at the root of a document." +
46
- child.pseudo_class_selector_message
47
- raise Sass::SyntaxError.new(message, :line => child.line)
48
- end
49
-
50
65
  next if child.invisible?
51
66
  child_str = child.to_s(1)
52
67
  result << child_str + (style == :compressed ? '' : "\n")