haml 2.2.24 → 3.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of haml might be problematic. Click here for more details.
- data/.yardopts +0 -1
- data/README.md +91 -151
- data/REMEMBER +11 -1
- data/Rakefile +73 -55
- data/VERSION +1 -1
- data/VERSION_NAME +1 -1
- data/bin/css2sass +7 -1
- data/bin/sass-convert +7 -0
- data/extra/haml-mode.el +2 -1
- data/lib/haml/buffer.rb +22 -4
- data/lib/haml/engine.rb +5 -1
- data/lib/haml/exec.rb +231 -46
- data/lib/haml/filters.rb +19 -8
- data/lib/haml/helpers.rb +47 -20
- data/lib/haml/helpers/action_view_extensions.rb +2 -4
- data/lib/haml/helpers/action_view_mods.rb +11 -8
- data/lib/haml/helpers/xss_mods.rb +13 -2
- data/lib/haml/html.rb +179 -48
- data/lib/haml/html/erb.rb +141 -0
- data/lib/haml/precompiler.rb +40 -15
- data/lib/haml/railtie.rb +1 -5
- data/lib/haml/root.rb +3 -0
- data/lib/haml/template.rb +4 -14
- data/lib/haml/util.rb +120 -30
- data/lib/haml/version.rb +25 -2
- data/lib/sass.rb +5 -1
- data/lib/sass/callbacks.rb +50 -0
- data/lib/sass/css.rb +40 -191
- data/lib/sass/engine.rb +170 -74
- data/lib/sass/environment.rb +8 -2
- data/lib/sass/error.rb +163 -25
- data/lib/sass/files.rb +31 -28
- data/lib/sass/plugin.rb +268 -87
- data/lib/sass/plugin/rails.rb +9 -4
- data/lib/sass/repl.rb +1 -1
- data/lib/sass/script.rb +31 -29
- data/lib/sass/script/bool.rb +1 -0
- data/lib/sass/script/color.rb +290 -23
- data/lib/sass/script/css_lexer.rb +22 -0
- data/lib/sass/script/css_parser.rb +28 -0
- data/lib/sass/script/funcall.rb +22 -3
- data/lib/sass/script/functions.rb +523 -33
- data/lib/sass/script/interpolation.rb +42 -0
- data/lib/sass/script/lexer.rb +169 -52
- data/lib/sass/script/literal.rb +58 -9
- data/lib/sass/script/node.rb +79 -1
- data/lib/sass/script/number.rb +20 -5
- data/lib/sass/script/operation.rb +49 -3
- data/lib/sass/script/parser.rb +162 -28
- data/lib/sass/script/string.rb +50 -2
- data/lib/sass/script/unary_operation.rb +25 -2
- data/lib/sass/script/variable.rb +21 -4
- data/lib/sass/scss.rb +14 -0
- data/lib/sass/scss/css_parser.rb +39 -0
- data/lib/sass/scss/parser.rb +683 -0
- data/lib/sass/scss/rx.rb +112 -0
- data/lib/sass/scss/script_lexer.rb +13 -0
- data/lib/sass/scss/script_parser.rb +25 -0
- data/lib/sass/tree/comment_node.rb +69 -27
- data/lib/sass/tree/debug_node.rb +7 -2
- data/lib/sass/tree/directive_node.rb +41 -35
- data/lib/sass/tree/for_node.rb +6 -0
- data/lib/sass/tree/if_node.rb +13 -1
- data/lib/sass/tree/import_node.rb +52 -27
- data/lib/sass/tree/mixin_def_node.rb +18 -0
- data/lib/sass/tree/mixin_node.rb +41 -6
- data/lib/sass/tree/node.rb +197 -70
- data/lib/sass/tree/prop_node.rb +152 -57
- data/lib/sass/tree/root_node.rb +118 -0
- data/lib/sass/tree/rule_node.rb +193 -96
- data/lib/sass/tree/variable_node.rb +9 -5
- data/lib/sass/tree/while_node.rb +4 -0
- data/test/benchmark.rb +5 -5
- data/test/haml/engine_test.rb +147 -10
- data/test/haml/{rhtml/_av_partial_1.rhtml → erb/_av_partial_1.erb} +1 -1
- data/test/haml/{rhtml/_av_partial_2.rhtml → erb/_av_partial_2.erb} +1 -1
- data/test/haml/{rhtml/action_view.rhtml → erb/action_view.erb} +1 -1
- data/test/haml/{rhtml/standard.rhtml → erb/standard.erb} +0 -0
- data/test/haml/helper_test.rb +91 -24
- data/test/haml/html2haml/erb_tests.rb +410 -0
- data/test/haml/html2haml_test.rb +210 -66
- data/test/haml/results/filters.xhtml +1 -1
- data/test/haml/results/just_stuff.xhtml +2 -0
- data/test/haml/spec_test.rb +44 -0
- data/test/haml/template_test.rb +22 -2
- data/test/haml/templates/helpers.haml +0 -13
- data/test/haml/templates/just_stuff.haml +2 -0
- data/test/haml/util_test.rb +48 -0
- data/test/sass/callbacks_test.rb +61 -0
- data/test/sass/conversion_test.rb +884 -0
- data/test/sass/css2sass_test.rb +99 -18
- data/test/sass/data/hsl-rgb.txt +319 -0
- data/test/sass/engine_test.rb +1049 -131
- data/test/sass/functions_test.rb +398 -47
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/more_templates/more_import.sass +3 -3
- data/test/sass/plugin_test.rb +184 -10
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +5 -5
- data/test/sass/results/compressed.css +1 -1
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +3 -1
- data/test/sass/results/mixins.css +12 -12
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/options.css +1 -0
- data/test/sass/results/parent_ref.css +4 -4
- data/test/sass/results/script.css +3 -3
- data/test/sass/results/scss_import.css +15 -0
- data/test/sass/results/scss_importee.css +2 -0
- data/test/sass/script_conversion_test.rb +153 -0
- data/test/sass/script_test.rb +137 -70
- data/test/sass/scss/css_test.rb +811 -0
- data/test/sass/scss/rx_test.rb +156 -0
- data/test/sass/scss/scss_test.rb +871 -0
- data/test/sass/scss/test_helper.rb +37 -0
- data/test/sass/templates/alt.sass +2 -2
- data/test/sass/templates/bork1.sass +2 -0
- data/test/sass/templates/bork3.sass +2 -0
- data/test/sass/templates/bork4.sass +2 -0
- data/test/sass/templates/import.sass +4 -4
- data/test/sass/templates/importee.sass +3 -3
- data/test/sass/templates/line_numbers.sass +1 -1
- data/test/sass/templates/mixin_bork.sass +5 -0
- data/test/sass/templates/mixins.sass +2 -2
- data/test/sass/templates/nested_bork1.sass +2 -0
- data/test/sass/templates/nested_bork2.sass +2 -0
- data/test/sass/templates/nested_bork3.sass +2 -0
- data/test/sass/templates/nested_bork4.sass +2 -0
- data/test/sass/templates/nested_mixin_bork.sass +6 -0
- data/test/sass/templates/options.sass +2 -0
- data/test/sass/templates/parent_ref.sass +2 -2
- data/test/sass/templates/script.sass +69 -69
- data/test/sass/templates/scss_import.scss +10 -0
- data/test/sass/templates/scss_importee.scss +1 -0
- data/test/sass/templates/units.sass +10 -10
- data/test/test_helper.rb +20 -8
- data/vendor/fssm/LICENSE +20 -0
- data/vendor/fssm/README.markdown +55 -0
- data/vendor/fssm/Rakefile +59 -0
- data/vendor/fssm/VERSION.yml +5 -0
- data/vendor/fssm/example.rb +9 -0
- data/vendor/fssm/fssm.gemspec +77 -0
- data/vendor/fssm/lib/fssm.rb +33 -0
- data/vendor/fssm/lib/fssm/backends/fsevents.rb +36 -0
- data/vendor/fssm/lib/fssm/backends/inotify.rb +26 -0
- data/vendor/fssm/lib/fssm/backends/polling.rb +25 -0
- data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +131 -0
- data/vendor/fssm/lib/fssm/monitor.rb +26 -0
- data/vendor/fssm/lib/fssm/path.rb +91 -0
- data/vendor/fssm/lib/fssm/pathname.rb +502 -0
- data/vendor/fssm/lib/fssm/state/directory.rb +57 -0
- data/vendor/fssm/lib/fssm/state/file.rb +24 -0
- data/vendor/fssm/lib/fssm/support.rb +63 -0
- data/vendor/fssm/lib/fssm/tree.rb +176 -0
- data/vendor/fssm/profile/prof-cache.rb +40 -0
- data/vendor/fssm/profile/prof-fssm-pathname.html +1231 -0
- data/vendor/fssm/profile/prof-pathname.rb +68 -0
- data/vendor/fssm/profile/prof-plain-pathname.html +988 -0
- data/vendor/fssm/profile/prof.html +2379 -0
- data/vendor/fssm/spec/path_spec.rb +75 -0
- data/vendor/fssm/spec/root/duck/quack.txt +0 -0
- data/vendor/fssm/spec/root/file.css +0 -0
- data/vendor/fssm/spec/root/file.rb +0 -0
- data/vendor/fssm/spec/root/file.yml +0 -0
- data/vendor/fssm/spec/root/moo/cow.txt +0 -0
- data/vendor/fssm/spec/spec_helper.rb +14 -0
- metadata +94 -14
- data/test/sass/templates/bork.sass +0 -2
data/lib/sass/tree/prop_node.rb
CHANGED
@@ -3,24 +3,54 @@ module Sass::Tree
|
|
3
3
|
#
|
4
4
|
# @see Sass::Tree
|
5
5
|
class PropNode < Node
|
6
|
-
# The name of the property
|
6
|
+
# The name of the property,
|
7
|
+
# interspersed with {Sass::Script::Node}s
|
8
|
+
# representing `#{}`-interpolation.
|
9
|
+
# Any adjacent strings will be merged together.
|
7
10
|
#
|
8
|
-
# @return [String]
|
11
|
+
# @return [Array<String, Sass::Script::Node>]
|
9
12
|
attr_accessor :name
|
10
13
|
|
11
|
-
# The
|
12
|
-
#
|
14
|
+
# The name of the property
|
15
|
+
# after any interpolated SassScript has been resolved.
|
16
|
+
# Only set once \{Tree::Node#perform} has been called.
|
17
|
+
#
|
18
|
+
# @return [String]
|
19
|
+
attr_accessor :resolved_name
|
20
|
+
|
21
|
+
# The value of the property.
|
13
22
|
#
|
14
|
-
# @return [
|
23
|
+
# @return [Sass::Script::Node]
|
15
24
|
attr_accessor :value
|
16
25
|
|
17
|
-
#
|
18
|
-
#
|
26
|
+
# The value of the property
|
27
|
+
# after any interpolated SassScript has been resolved.
|
28
|
+
# Only set once \{Tree::Node#perform} has been called.
|
29
|
+
#
|
30
|
+
# @return [String]
|
31
|
+
attr_accessor :resolved_value
|
32
|
+
|
33
|
+
# How deep this property is indented
|
34
|
+
# relative to a normal property.
|
35
|
+
# This is only greater than 0 in the case that:
|
36
|
+
#
|
37
|
+
# * This node is in a CSS tree
|
38
|
+
# * The style is :nested
|
39
|
+
# * This is a child property of another property
|
40
|
+
# * The parent property has a value, and thus will be rendered
|
41
|
+
#
|
42
|
+
# @return [Fixnum]
|
43
|
+
attr_accessor :tabs
|
44
|
+
|
45
|
+
# @param name [Array<String, Sass::Script::Node>] See \{#name}
|
46
|
+
# @param value [Sass::Script::Node] See \{#value}
|
19
47
|
# @param prop_syntax [Symbol] `:new` if this property uses `a: b`-style syntax,
|
20
48
|
# `:old` if it uses `:a b`-style syntax
|
21
49
|
def initialize(name, value, prop_syntax)
|
22
|
-
@name =
|
50
|
+
@name = Haml::Util.strip_string_array(
|
51
|
+
Haml::Util.merge_adjacent_strings(name))
|
23
52
|
@value = value
|
53
|
+
@tabs = 0
|
24
54
|
@prop_syntax = prop_syntax
|
25
55
|
super()
|
26
56
|
end
|
@@ -34,69 +64,79 @@ module Sass::Tree
|
|
34
64
|
self.class == other.class && name == other.name && value == other.value && super
|
35
65
|
end
|
36
66
|
|
37
|
-
# Computes the CSS for the property.
|
38
|
-
#
|
39
|
-
# @param tabs [Fixnum] The level of indentation for the CSS
|
40
|
-
# @param parent_name [String] The name of the parent property (e.g. `text`) or nil
|
41
|
-
# @return [String] The resulting CSS
|
42
|
-
# @raise [Sass::SyntaxError] if the property uses invalid syntax
|
43
|
-
def to_s(tabs, parent_name = nil)
|
44
|
-
if @options[:property_syntax] == :old && @prop_syntax == :new
|
45
|
-
raise Sass::SyntaxError.new("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.", @line)
|
46
|
-
elsif @options[:property_syntax] == :new && @prop_syntax == :old
|
47
|
-
raise Sass::SyntaxError.new("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.", @line)
|
48
|
-
end
|
49
|
-
|
50
|
-
if value[-1] == ?;
|
51
|
-
raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no \";\" required at end-of-line).", @line)
|
52
|
-
end
|
53
|
-
real_name = name
|
54
|
-
real_name = "#{parent_name}-#{real_name}" if parent_name
|
55
|
-
|
56
|
-
if value.empty? && children.empty?
|
57
|
-
message = "Invalid property: #{declaration.dump} (no value)." +
|
58
|
-
pseudo_class_selector_message
|
59
|
-
raise Sass::SyntaxError.new(message, @line)
|
60
|
-
end
|
61
|
-
|
62
|
-
join_string = case style
|
63
|
-
when :compact; ' '
|
64
|
-
when :compressed; ''
|
65
|
-
else "\n"
|
66
|
-
end
|
67
|
-
spaces = ' ' * (tabs - 1)
|
68
|
-
to_return = ''
|
69
|
-
if !value.empty?
|
70
|
-
to_return << "#{spaces}#{real_name}:#{style == :compressed ? '' : ' '}#{value};#{join_string}"
|
71
|
-
end
|
72
|
-
|
73
|
-
children.each do |kid|
|
74
|
-
next if kid.invisible?
|
75
|
-
to_return << kid.to_s(tabs, real_name) << join_string
|
76
|
-
end
|
77
|
-
|
78
|
-
(style == :compressed && parent_name) ? to_return : to_return[0...-1]
|
79
|
-
end
|
80
|
-
|
81
67
|
# Returns a appropriate message indicating how to escape pseudo-class selectors.
|
82
68
|
# This only applies for old-style properties with no value,
|
83
69
|
# so returns the empty string if this is new-style.
|
84
70
|
#
|
71
|
+
# This should only be called once \{#perform} has been called.
|
72
|
+
#
|
85
73
|
# @return [String] The message
|
86
74
|
def pseudo_class_selector_message
|
87
|
-
return "" if @prop_syntax == :new || !
|
75
|
+
return "" if @prop_syntax == :new || !resolved_value.empty?
|
88
76
|
"\nIf #{declaration.dump} should be a selector, use \"\\#{declaration}\" instead."
|
89
77
|
end
|
90
78
|
|
91
79
|
protected
|
92
80
|
|
93
|
-
|
81
|
+
def to_src(tabs, opts, fmt)
|
82
|
+
name = self.name.map {|n| n.is_a?(String) ? n : "\#{#{n.to_sass}}"}.join
|
83
|
+
old = opts[:old] && fmt == :sass
|
84
|
+
initial = old ? ':' : ''
|
85
|
+
mid = old ? '' : ':'
|
86
|
+
res = "#{' ' * tabs}#{initial}#{name}#{mid} #{self.class.val_to_sass(value)}"
|
87
|
+
return res + "#{semi fmt}\n" if children.empty?
|
88
|
+
res.rstrip + children_to_src(tabs, opts, fmt)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Computes the CSS for the property.
|
92
|
+
#
|
93
|
+
# @param tabs [Fixnum] The level of indentation for the CSS
|
94
|
+
# @return [String] The resulting CSS
|
95
|
+
def _to_s(tabs)
|
96
|
+
to_return = ' ' * (tabs - 1 + self.tabs) + resolved_name + ":" +
|
97
|
+
(style == :compressed ? '' : ' ') + resolved_value + (style == :compressed ? "" : ";")
|
98
|
+
end
|
99
|
+
|
100
|
+
# Converts nested properties into flat properties.
|
101
|
+
#
|
102
|
+
# @param parent [PropNode, nil] The parent node of this node,
|
103
|
+
# or nil if the parent isn't a {PropNode}
|
104
|
+
# @raise [Sass::SyntaxError] if the property uses invalid syntax
|
105
|
+
def _cssize(parent)
|
106
|
+
node = super
|
107
|
+
result = node.children.dup
|
108
|
+
if !node.resolved_value.empty? || node.children.empty?
|
109
|
+
node.send(:check!)
|
110
|
+
result.unshift(node)
|
111
|
+
end
|
112
|
+
result
|
113
|
+
end
|
114
|
+
|
115
|
+
# Updates the name and indentation of this node based on the parent name
|
116
|
+
# and nesting level.
|
117
|
+
#
|
118
|
+
# @param parent [PropNode, nil] The parent node of this node,
|
119
|
+
# or nil if the parent isn't a {PropNode}
|
120
|
+
def cssize!(parent)
|
121
|
+
self.resolved_name = "#{parent.resolved_name}-#{resolved_name}" if parent
|
122
|
+
self.tabs = parent.tabs + (parent.resolved_value.empty? ? 0 : 1) if parent && style == :nested
|
123
|
+
super
|
124
|
+
end
|
125
|
+
|
126
|
+
# Runs any SassScript that may be embedded in the property,
|
127
|
+
# and invludes the parent property, if any.
|
94
128
|
#
|
95
129
|
# @param environment [Sass::Environment] The lexical environment containing
|
96
130
|
# variable and mixin values
|
97
131
|
def perform!(environment)
|
98
|
-
@
|
99
|
-
|
132
|
+
@resolved_name = run_interp(@name, environment)
|
133
|
+
val = @value.perform(environment)
|
134
|
+
@resolved_value =
|
135
|
+
if @value.context == :equals && val.is_a?(Sass::Script::String)
|
136
|
+
val.value
|
137
|
+
else
|
138
|
+
val.to_s
|
139
|
+
end
|
100
140
|
super
|
101
141
|
end
|
102
142
|
|
@@ -114,8 +154,63 @@ module Sass::Tree
|
|
114
154
|
|
115
155
|
private
|
116
156
|
|
157
|
+
def check!
|
158
|
+
if @options[:property_syntax] == :old && @prop_syntax == :new
|
159
|
+
raise Sass::SyntaxError.new("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.")
|
160
|
+
elsif @options[:property_syntax] == :new && @prop_syntax == :old
|
161
|
+
raise Sass::SyntaxError.new("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.")
|
162
|
+
elsif resolved_value.empty?
|
163
|
+
raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no value)." +
|
164
|
+
pseudo_class_selector_message)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
117
168
|
def declaration
|
118
|
-
|
169
|
+
if @prop_syntax == :new
|
170
|
+
"#{resolved_name}: #{resolved_value}"
|
171
|
+
else
|
172
|
+
":#{resolved_name} #{resolved_value}"
|
173
|
+
end.strip
|
174
|
+
end
|
175
|
+
|
176
|
+
class << self
|
177
|
+
# @private
|
178
|
+
def val_to_sass(value)
|
179
|
+
return value.to_sass unless value.context == :equals
|
180
|
+
val_to_sass_comma(value).to_sass
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
def val_to_sass_comma(node)
|
186
|
+
return node unless node.is_a?(Sass::Script::Operation)
|
187
|
+
return val_to_sass_concat(node) unless node.operator == :comma
|
188
|
+
|
189
|
+
Sass::Script::Operation.new(
|
190
|
+
val_to_sass_concat(node.operand1),
|
191
|
+
val_to_sass_comma(node.operand2),
|
192
|
+
node.operator)
|
193
|
+
end
|
194
|
+
|
195
|
+
def val_to_sass_concat(node)
|
196
|
+
return node unless node.is_a?(Sass::Script::Operation)
|
197
|
+
return val_to_sass_div(node) unless node.operator == :concat
|
198
|
+
|
199
|
+
Sass::Script::Operation.new(
|
200
|
+
val_to_sass_div(node.operand1),
|
201
|
+
val_to_sass_concat(node.operand2),
|
202
|
+
node.operator)
|
203
|
+
end
|
204
|
+
|
205
|
+
def val_to_sass_div(node)
|
206
|
+
unless node.is_a?(Sass::Script::Operation) && node.operator == :div &&
|
207
|
+
node.operand1.is_a?(Sass::Script::Number) &&
|
208
|
+
node.operand2.is_a?(Sass::Script::Number)
|
209
|
+
return node
|
210
|
+
end
|
211
|
+
|
212
|
+
Sass::Script::String.new("(#{node.to_sass})")
|
213
|
+
end
|
119
214
|
end
|
120
215
|
end
|
121
216
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Sass
|
2
|
+
module Tree
|
3
|
+
# A static node that is the root node of the Sass document.
|
4
|
+
class RootNode < Node
|
5
|
+
# The Sass template from which this node was created
|
6
|
+
#
|
7
|
+
# @param template [String]
|
8
|
+
attr_reader :template
|
9
|
+
|
10
|
+
# @param template [String] The Sass template from which this node was created
|
11
|
+
def initialize(template)
|
12
|
+
super()
|
13
|
+
@template = template
|
14
|
+
end
|
15
|
+
|
16
|
+
# @see Node#to_s
|
17
|
+
def to_s(*args)
|
18
|
+
super
|
19
|
+
rescue Sass::SyntaxError => e
|
20
|
+
e.sass_template = @template
|
21
|
+
raise e
|
22
|
+
end
|
23
|
+
|
24
|
+
# @see Node#perform
|
25
|
+
def perform(environment)
|
26
|
+
environment.options = @options if environment.options.nil? || environment.options.empty?
|
27
|
+
super
|
28
|
+
rescue Sass::SyntaxError => e
|
29
|
+
e.sass_template = @template
|
30
|
+
raise e
|
31
|
+
end
|
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
|
+
|
41
|
+
# @see \{Node#perform!}
|
42
|
+
def perform!(environment)
|
43
|
+
environment.options = @options if environment.options.nil? || environment.options.empty?
|
44
|
+
super
|
45
|
+
end
|
46
|
+
|
47
|
+
# Converts a node to Sass code that will generate it.
|
48
|
+
#
|
49
|
+
# @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
|
50
|
+
# @return [String] The Sass code corresponding to the node
|
51
|
+
def to_sass(opts = {})
|
52
|
+
to_src(opts, :sass)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Converts a node to SCSS code that will generate it.
|
56
|
+
#
|
57
|
+
# @param opts [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
|
58
|
+
# @return [String] The SCSS code corresponding to the node
|
59
|
+
def to_scss(opts = {})
|
60
|
+
to_src(opts, :scss)
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def to_src(opts, fmt)
|
66
|
+
Haml::Util.enum_cons(children + [nil], 2).map do |child, nxt|
|
67
|
+
child.send("to_#{fmt}", 0, opts) +
|
68
|
+
if nxt &&
|
69
|
+
(child.is_a?(CommentNode) && child.line + child.value.count("\n") + 1 == nxt.line) ||
|
70
|
+
(child.is_a?(ImportNode) && nxt.is_a?(ImportNode) && child.line + 1 == nxt.line)
|
71
|
+
""
|
72
|
+
else
|
73
|
+
"\n"
|
74
|
+
end
|
75
|
+
end.join.rstrip + "\n"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Destructively converts this static Sass node into a static CSS node,
|
79
|
+
# and checks that there are no properties at root level.
|
80
|
+
#
|
81
|
+
# @param parent [Node, nil] The parent node of this node.
|
82
|
+
# This should only be non-nil if the parent is the same class as this node
|
83
|
+
# @see Node#cssize!
|
84
|
+
def cssize!(parent)
|
85
|
+
super
|
86
|
+
return unless child = children.find {|c| c.is_a?(PropNode)}
|
87
|
+
message = "Properties aren't allowed at the root of a document." +
|
88
|
+
child.pseudo_class_selector_message
|
89
|
+
raise Sass::SyntaxError.new(message, :line => child.line)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Computes the CSS corresponding to this Sass tree.
|
93
|
+
#
|
94
|
+
# @param args [Array] ignored
|
95
|
+
# @return [String] The resulting CSS
|
96
|
+
# @see Sass::Tree
|
97
|
+
def _to_s(*args)
|
98
|
+
result = String.new
|
99
|
+
children.each do |child|
|
100
|
+
next if child.invisible?
|
101
|
+
child_str = child.to_s(1)
|
102
|
+
result << child_str + (style == :compressed ? '' : "\n")
|
103
|
+
end
|
104
|
+
result.rstrip!
|
105
|
+
return "" if result.empty?
|
106
|
+
return result + "\n"
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns false, because all nodes are allowed at the root of the document
|
110
|
+
# (properties are detected elsewhere post-mixin-resolution).
|
111
|
+
#
|
112
|
+
# @see Node#invalid_child?
|
113
|
+
def invalid_child?(child)
|
114
|
+
false
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/lib/sass/tree/rule_node.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'pathname'
|
2
|
+
require 'uri'
|
2
3
|
|
3
4
|
module Sass::Tree
|
4
5
|
# A static node reprenting a CSS rule.
|
@@ -9,30 +10,23 @@ module Sass::Tree
|
|
9
10
|
# @private
|
10
11
|
PARENT = '&'
|
11
12
|
|
12
|
-
# The CSS
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# foo, bar, baz,
|
17
|
-
# bip, bop, bup
|
18
|
-
#
|
19
|
-
# would be
|
20
|
-
#
|
21
|
-
# ["foo, bar, baz",
|
22
|
-
# "bip, bop, bup"]
|
13
|
+
# The CSS selector for this rule,
|
14
|
+
# interspersed with {Sass::Script::Node}s
|
15
|
+
# representing `#{}`-interpolation.
|
16
|
+
# Any adjacent strings will be merged together.
|
23
17
|
#
|
24
|
-
# @return [Array<String>]
|
25
|
-
attr_accessor :
|
18
|
+
# @return [Array<String, Sass::Script::Node>]
|
19
|
+
attr_accessor :rule
|
26
20
|
|
27
21
|
# The CSS selectors for this rule,
|
28
22
|
# parsed for commas and parent-references.
|
29
23
|
# It's only set once {Tree::Node#perform} has been called.
|
30
24
|
#
|
31
|
-
# It's an array of arrays
|
32
|
-
# The first level of arrays
|
33
|
-
# the second
|
34
|
-
# the third represents structure within those selectors,
|
25
|
+
# It's an array of arrays.
|
26
|
+
# The first level of arrays comma-separated selectors;
|
27
|
+
# the second represents structure within those selectors,
|
35
28
|
# currently only parent-refs (represented by `:parent`).
|
29
|
+
# Newlines are represented as literal `\n` characters in the strings.
|
36
30
|
# For example,
|
37
31
|
#
|
38
32
|
# &.foo, bar, baz,
|
@@ -40,15 +34,54 @@ module Sass::Tree
|
|
40
34
|
#
|
41
35
|
# would be
|
42
36
|
#
|
43
|
-
# [[
|
44
|
-
# [
|
37
|
+
# [[:parent, ".foo"], ["bar"], ["baz"],
|
38
|
+
# ["\nbip"], [:parent, ".bop"], ["bup"]]
|
45
39
|
#
|
46
|
-
# @return [Array<Array<
|
40
|
+
# @return [Array<Array<String, Symbol>>]
|
47
41
|
attr_accessor :parsed_rules
|
48
42
|
|
49
|
-
#
|
43
|
+
# The CSS selectors for this rule,
|
44
|
+
# with all nesting and parent references resolved.
|
45
|
+
# It's only set once {Tree::Node#cssize} has been called.
|
46
|
+
#
|
47
|
+
# Each element is a distinct selector, separated by commas.
|
48
|
+
# Newlines are represented as literal `\n` characters in the strings.
|
49
|
+
# For example,
|
50
|
+
#
|
51
|
+
# foo bar, baz,
|
52
|
+
# bang, bip bop, blip
|
53
|
+
#
|
54
|
+
# would be
|
55
|
+
#
|
56
|
+
# ["foo bar", "baz", "\nbang", "bip bop", "blip"]
|
57
|
+
#
|
58
|
+
# @return [Array<String>]
|
59
|
+
attr_accessor :resolved_rules
|
60
|
+
|
61
|
+
# How deep this rule is indented
|
62
|
+
# relative to a base-level rule.
|
63
|
+
# This is only greater than 0 in the case that:
|
64
|
+
#
|
65
|
+
# * This node is in a CSS tree
|
66
|
+
# * The style is :nested
|
67
|
+
# * This is a child rule of another rule
|
68
|
+
# * The parent rule has properties, and thus will be rendered
|
69
|
+
#
|
70
|
+
# @return [Fixnum]
|
71
|
+
attr_accessor :tabs
|
72
|
+
|
73
|
+
# Whether or not this rule is the last rule in a nested group.
|
74
|
+
# This is only set in a CSS tree.
|
75
|
+
#
|
76
|
+
# @return [Boolean]
|
77
|
+
attr_accessor :group_end
|
78
|
+
|
79
|
+
# @param rule [Array<String, Sass::Script::Node>]
|
80
|
+
# The CSS rule. See \{#rule}
|
50
81
|
def initialize(rule)
|
51
|
-
@
|
82
|
+
@rule = Haml::Util.strip_string_array(
|
83
|
+
Haml::Util.merge_adjacent_strings(rule))
|
84
|
+
@tabs = 0
|
52
85
|
super()
|
53
86
|
end
|
54
87
|
|
@@ -58,63 +91,86 @@ module Sass::Tree
|
|
58
91
|
# @return [Boolean] Whether or not this node and the other object
|
59
92
|
# are the same
|
60
93
|
def ==(other)
|
61
|
-
self.class == other.class &&
|
94
|
+
self.class == other.class && rule == other.rule && super
|
62
95
|
end
|
63
96
|
|
64
97
|
# Adds another {RuleNode}'s rules to this one's.
|
65
98
|
#
|
66
99
|
# @param node [RuleNode] The other node
|
67
100
|
def add_rules(node)
|
68
|
-
@
|
101
|
+
@rule = Haml::Util.strip_string_array(
|
102
|
+
Haml::Util.merge_adjacent_strings(@rule + ["\n"] + node.rule))
|
69
103
|
end
|
70
104
|
|
71
105
|
# @return [Boolean] Whether or not this rule is continued on the next line
|
72
106
|
def continued?
|
73
|
-
@
|
107
|
+
last = @rule.last
|
108
|
+
last.is_a?(String) && last[-1] == ?,
|
74
109
|
end
|
75
110
|
|
111
|
+
# @see Node#to_sass
|
112
|
+
def to_sass(tabs, opts = {})
|
113
|
+
name = rule.map do |r|
|
114
|
+
if r.is_a?(String)
|
115
|
+
r.gsub(/(,[ \t]*)?\n\s*/) {$1 ? $1 + "\n" : " "}
|
116
|
+
else
|
117
|
+
"\#{#{r.to_sass}}"
|
118
|
+
end
|
119
|
+
end.join
|
120
|
+
name = "\\" + name if name[0] == ?:
|
121
|
+
name.gsub(/^/, ' ' * tabs) + children_to_src(tabs, opts, :sass)
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_scss(tabs, opts = {})
|
125
|
+
name = rule.map {|r| r.is_a?(String) ? r : "\#{#{r.to_sass}}"}.
|
126
|
+
join.gsub(/^[ \t]*/, ' ' * tabs)
|
127
|
+
|
128
|
+
res = name + children_to_src(tabs, opts, :scss)
|
129
|
+
|
130
|
+
if children.last.is_a?(CommentNode) && children.last.silent
|
131
|
+
res.slice!(-3..-1)
|
132
|
+
res << "\n" << (' ' * tabs) << "}\n"
|
133
|
+
end
|
134
|
+
|
135
|
+
res
|
136
|
+
end
|
137
|
+
|
138
|
+
protected
|
139
|
+
|
76
140
|
# Computes the CSS for the rule.
|
77
141
|
#
|
78
142
|
# @param tabs [Fixnum] The level of indentation for the CSS
|
79
|
-
# @param super_rules [Array<Array<String>>] The rules for the parent node
|
80
|
-
# (see \{#rules}), or `nil` if there are no parents
|
81
143
|
# @return [String] The resulting CSS
|
82
|
-
|
83
|
-
|
84
|
-
resolved_rules = resolve_parent_refs(super_rules)
|
85
|
-
|
86
|
-
properties = []
|
87
|
-
sub_rules = []
|
144
|
+
def _to_s(tabs)
|
145
|
+
tabs = tabs + self.tabs
|
88
146
|
|
89
147
|
rule_separator = style == :compressed ? ',' : ', '
|
90
|
-
line_separator =
|
148
|
+
line_separator =
|
149
|
+
case style
|
150
|
+
when :nested, :expanded; "\n"
|
151
|
+
when :compressed; ""
|
152
|
+
else; " "
|
153
|
+
end
|
91
154
|
rule_indent = ' ' * (tabs - 1)
|
92
155
|
per_rule_indent, total_indent = [:nested, :expanded].include?(style) ? [rule_indent, ''] : ['', rule_indent]
|
93
156
|
|
94
|
-
total_rule = total_indent + resolved_rules.map do |line|
|
95
|
-
per_rule_indent + line.
|
157
|
+
total_rule = total_indent + resolved_rules.join(rule_separator).split("\n").map do |line|
|
158
|
+
per_rule_indent + line.strip
|
96
159
|
end.join(line_separator)
|
97
160
|
|
98
|
-
children.each do |child|
|
99
|
-
next if child.invisible?
|
100
|
-
if child.is_a? RuleNode
|
101
|
-
sub_rules << child
|
102
|
-
else
|
103
|
-
properties << child
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
161
|
to_return = ''
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
if @options[:
|
162
|
+
old_spaces = ' ' * (tabs - 1)
|
163
|
+
spaces = ' ' * tabs
|
164
|
+
if style != :compressed
|
165
|
+
if @options[:debug_info]
|
166
|
+
to_return << debug_info_rule.to_s(tabs) << "\n"
|
167
|
+
elsif @options[:line_comments]
|
112
168
|
to_return << "#{old_spaces}/* line #{line}"
|
113
169
|
|
114
170
|
if filename
|
115
171
|
relative_filename = if @options[:css_filename]
|
116
172
|
begin
|
117
|
-
Pathname.new(filename).relative_path_from(
|
173
|
+
Pathname.new(filename).relative_path_from(
|
118
174
|
Pathname.new(File.dirname(@options[:css_filename]))).to_s
|
119
175
|
rescue ArgumentError
|
120
176
|
nil
|
@@ -126,70 +182,101 @@ module Sass::Tree
|
|
126
182
|
|
127
183
|
to_return << " */\n"
|
128
184
|
end
|
129
|
-
|
130
|
-
if style == :compact
|
131
|
-
properties = properties.map { |a| a.to_s(1) }.select{|a| a && a.length > 0}.join(' ')
|
132
|
-
to_return << "#{total_rule} { #{properties} }\n"
|
133
|
-
elsif style == :compressed
|
134
|
-
properties = properties.map { |a| a.to_s(1) }.select{|a| a && a.length > 0}.join(';')
|
135
|
-
to_return << "#{total_rule}{#{properties}}"
|
136
|
-
else
|
137
|
-
properties = properties.map { |a| a.to_s(tabs + 1) }.select{|a| a && a.length > 0}.join("\n")
|
138
|
-
end_props = (style == :expanded ? "\n" + old_spaces : ' ')
|
139
|
-
to_return << "#{total_rule} {\n#{properties}#{end_props}}\n"
|
140
|
-
end
|
141
185
|
end
|
142
186
|
|
143
|
-
|
144
|
-
|
145
|
-
to_return <<
|
187
|
+
if style == :compact
|
188
|
+
properties = children.map { |a| a.to_s(1) }.join(' ')
|
189
|
+
to_return << "#{total_rule} { #{properties} }#{"\n" if group_end}"
|
190
|
+
elsif style == :compressed
|
191
|
+
properties = children.map { |a| a.to_s(1) }.join(';')
|
192
|
+
to_return << "#{total_rule}{#{properties}}"
|
193
|
+
else
|
194
|
+
properties = children.map { |a| a.to_s(tabs + 1) }.join("\n")
|
195
|
+
end_props = (style == :expanded ? "\n" + old_spaces : ' ')
|
196
|
+
to_return << "#{total_rule} {\n#{properties}#{end_props}}#{"\n" if group_end}"
|
146
197
|
end
|
147
198
|
|
148
199
|
to_return
|
149
200
|
end
|
150
201
|
|
151
|
-
protected
|
152
|
-
|
153
202
|
# Runs any SassScript that may be embedded in the rule,
|
154
203
|
# and parses the selectors for commas.
|
155
204
|
#
|
156
205
|
# @param environment [Sass::Environment] The lexical environment containing
|
157
206
|
# variable and mixin values
|
158
207
|
def perform!(environment)
|
159
|
-
@parsed_rules =
|
208
|
+
@parsed_rules = parse_selector(run_interp(@rule, environment))
|
209
|
+
super
|
210
|
+
end
|
211
|
+
|
212
|
+
# Converts nested rules into a flat list of rules.
|
213
|
+
#
|
214
|
+
# @param parent [RuleNode, nil] The parent node of this node,
|
215
|
+
# or nil if the parent isn't a {RuleNode}
|
216
|
+
def _cssize(parent)
|
217
|
+
node = super
|
218
|
+
rules = node.children.select {|c| c.is_a?(RuleNode)}
|
219
|
+
props = node.children.reject {|c| c.is_a?(RuleNode) || c.invisible?}
|
220
|
+
|
221
|
+
unless props.empty?
|
222
|
+
node.children = props
|
223
|
+
rules.each {|r| r.tabs += 1} if style == :nested
|
224
|
+
rules.unshift(node)
|
225
|
+
end
|
226
|
+
|
227
|
+
rules.last.group_end = true unless parent || rules.empty?
|
228
|
+
|
229
|
+
rules
|
230
|
+
end
|
231
|
+
|
232
|
+
# Resolves parent references and nested selectors,
|
233
|
+
# and updates the indentation based on the parent's indentation.
|
234
|
+
#
|
235
|
+
# @param parent [RuleNode, nil] The parent node of this node,
|
236
|
+
# or nil if the parent isn't a {RuleNode}
|
237
|
+
# @raise [Sass::SyntaxError] if the rule has no parents but uses `&`
|
238
|
+
def cssize!(parent)
|
239
|
+
self.resolved_rules = resolve_parent_refs(parent && parent.resolved_rules)
|
160
240
|
super
|
161
241
|
end
|
162
242
|
|
243
|
+
# A hash that will be associated with this rule in the CSS document
|
244
|
+
# if the {file:SASS_REFERENCE.md#debug_info-option `:debug_info` option} is enabled.
|
245
|
+
# This data is used by e.g. [the FireSass Firebug extension](https://addons.mozilla.org/en-US/firefox/addon/103988).
|
246
|
+
#
|
247
|
+
# @return [{#to_s => #to_s}]
|
248
|
+
def debug_info
|
249
|
+
{:filename => filename && ("file://" + URI.escape(File.expand_path(filename))),
|
250
|
+
:line => self.line}
|
251
|
+
end
|
252
|
+
|
163
253
|
private
|
164
254
|
|
165
255
|
def resolve_parent_refs(super_rules)
|
166
256
|
if super_rules.nil?
|
167
|
-
return @parsed_rules.map do |
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
end
|
257
|
+
return @parsed_rules.map do |rule|
|
258
|
+
if rule.include?(:parent)
|
259
|
+
raise Sass::SyntaxError.new("Base-level rules cannot contain the parent-selector-referencing character '#{PARENT}'.")
|
260
|
+
end
|
172
261
|
|
173
|
-
|
174
|
-
end.compact
|
262
|
+
rule.join
|
175
263
|
end
|
176
264
|
end
|
177
265
|
|
178
266
|
new_rules = []
|
179
|
-
super_rules.each do |
|
180
|
-
@parsed_rules.each do |
|
267
|
+
super_rules.each do |super_rule|
|
268
|
+
@parsed_rules.each do |rule|
|
181
269
|
new_rules << []
|
182
270
|
|
183
|
-
|
184
|
-
|
185
|
-
|
271
|
+
# An initial newline of the child rule
|
272
|
+
# should be moved to the beginning of the entire rule
|
273
|
+
rule.first.slice!(0) if nl = (rule.first.is_a?(String) && rule.first[0] == ?\n)
|
274
|
+
rule = [nl ? "\n" : "", :parent, " ", *rule] unless rule.include?(:parent)
|
186
275
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
end
|
192
|
-
end
|
276
|
+
new_rules.last << rule.map do |segment|
|
277
|
+
next segment unless segment == :parent
|
278
|
+
super_rule
|
279
|
+
end.join
|
193
280
|
end
|
194
281
|
end
|
195
282
|
new_rules
|
@@ -202,18 +289,13 @@ module Sass::Tree
|
|
202
289
|
while scanner.rest?
|
203
290
|
rules.last << scanner.scan(/[^",&]*/)
|
204
291
|
case scanner.scan(/./)
|
205
|
-
when '&'
|
206
|
-
warn <<END unless rules.last.empty? || rules.last.last =~ /(^|\s)$/
|
207
|
-
DEPRECATION WARNING:
|
208
|
-
On line #{@line}#{" of '#{@filename}'" if @filename}
|
209
|
-
In Sass 3, parent selectors will only be able to appear
|
210
|
-
at the beginning of simple selector sequences.
|
211
|
-
For example, ".foo &.bar" is allowed but ".bar&" is not.
|
212
|
-
END
|
213
|
-
rules.last << :parent
|
292
|
+
when '&'; rules.last << :parent
|
214
293
|
when ','
|
215
294
|
scanner.scan(/\s*/)
|
216
|
-
|
295
|
+
if scanner.rest?
|
296
|
+
rules << []
|
297
|
+
rules.last << "\n" if scanner.matched.include?("\n")
|
298
|
+
end
|
217
299
|
when '"'
|
218
300
|
rules.last << '"' << scanner.scan(/([^"\\]|\\.)*/)
|
219
301
|
# We don't want to enforce that strings are closed,
|
@@ -228,5 +310,20 @@ END
|
|
228
310
|
|
229
311
|
rules
|
230
312
|
end
|
313
|
+
|
314
|
+
def debug_info_rule
|
315
|
+
node = DirectiveNode.new("@media -sass-debug-info")
|
316
|
+
debug_info.map {|k, v| [k.to_s, v.to_s]}.sort.each do |k, v|
|
317
|
+
rule = RuleNode.new([""])
|
318
|
+
rule.resolved_rules = [[k.to_s.gsub(/[^\w-]/, "\\\\\\0")]]
|
319
|
+
prop = PropNode.new("", "", :new)
|
320
|
+
prop.resolved_name = "font-family"
|
321
|
+
prop.resolved_value = Sass::SCSS::RX.escape_ident(v.to_s)
|
322
|
+
rule << prop
|
323
|
+
node << rule
|
324
|
+
end
|
325
|
+
node.options = @options.merge(:debug_info => false, :line_comments => false, :style => :compressed)
|
326
|
+
node
|
327
|
+
end
|
231
328
|
end
|
232
329
|
end
|