haml 3.0.0.beta.1 → 3.0.0.beta.2
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/README.md +7 -7
- data/REMEMBER +2 -1
- data/Rakefile +6 -4
- data/VERSION +1 -1
- data/lib/haml/exec.rb +119 -24
- data/lib/haml/filters.rb +5 -1
- data/lib/haml/helpers.rb +4 -2
- data/lib/haml/helpers/action_view_mods.rb +2 -3
- data/lib/haml/precompiler.rb +1 -0
- data/lib/sass.rb +1 -1
- data/lib/sass/css.rb +25 -6
- data/lib/sass/engine.rb +23 -7
- data/lib/sass/environment.rb +47 -1
- data/lib/sass/files.rb +9 -10
- data/lib/sass/plugin.rb +6 -27
- data/lib/sass/plugin/merb.rb +1 -0
- data/lib/sass/plugin/rails.rb +1 -0
- data/lib/sass/plugin/staleness_checker.rb +123 -0
- data/lib/sass/script/bool.rb +1 -1
- data/lib/sass/script/color.rb +1 -1
- data/lib/sass/script/css_lexer.rb +1 -4
- data/lib/sass/script/funcall.rb +2 -2
- data/lib/sass/script/functions.rb +102 -7
- data/lib/sass/script/interpolation.rb +5 -5
- data/lib/sass/script/lexer.rb +10 -8
- data/lib/sass/script/literal.rb +11 -1
- data/lib/sass/script/node.rb +10 -1
- data/lib/sass/script/number.rb +25 -10
- data/lib/sass/script/operation.rb +7 -6
- data/lib/sass/script/parser.rb +37 -28
- data/lib/sass/script/string.rb +12 -7
- data/lib/sass/script/string_interpolation.rb +70 -0
- data/lib/sass/script/unary_operation.rb +3 -3
- data/lib/sass/script/variable.rb +2 -2
- data/lib/sass/scss/css_parser.rb +1 -0
- data/lib/sass/scss/parser.rb +58 -44
- data/lib/sass/scss/rx.rb +1 -0
- data/lib/sass/tree/comment_node.rb +3 -2
- data/lib/sass/tree/debug_node.rb +1 -1
- data/lib/sass/tree/for_node.rb +1 -1
- data/lib/sass/tree/if_node.rb +1 -1
- data/lib/sass/tree/import_node.rb +3 -0
- data/lib/sass/tree/mixin_def_node.rb +3 -3
- data/lib/sass/tree/mixin_node.rb +7 -3
- data/lib/sass/tree/node.rb +8 -0
- data/lib/sass/tree/prop_node.rb +21 -16
- data/lib/sass/tree/rule_node.rb +3 -3
- data/lib/sass/tree/variable_node.rb +1 -1
- data/lib/sass/tree/warn_node.rb +41 -0
- data/lib/sass/tree/while_node.rb +1 -1
- data/test/haml/engine_test.rb +9 -0
- data/test/sass/conversion_test.rb +127 -15
- data/test/sass/css2sass_test.rb +34 -3
- data/test/sass/engine_test.rb +82 -5
- data/test/sass/functions_test.rb +31 -0
- data/test/sass/plugin_test.rb +25 -24
- data/test/sass/results/script.css +4 -4
- data/test/sass/results/warn.css +0 -0
- data/test/sass/results/warn_imported.css +0 -0
- data/test/sass/script_conversion_test.rb +43 -1
- data/test/sass/script_test.rb +3 -3
- data/test/sass/scss/css_test.rb +46 -5
- data/test/sass/scss/scss_test.rb +72 -0
- data/test/sass/templates/import.sass +1 -1
- data/test/sass/templates/warn.sass +3 -0
- data/test/sass/templates/warn_imported.sass +4 -0
- metadata +10 -3
@@ -9,16 +9,16 @@ module Sass::Script
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def inspect
|
12
|
-
"(interpolation #{@before.inspect} #{@mid.inspect} #{after.inspect})"
|
12
|
+
"(interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
|
13
13
|
end
|
14
14
|
|
15
|
-
def to_sass
|
15
|
+
def to_sass(opts = {})
|
16
16
|
res = ""
|
17
|
-
res << @before.to_sass if @before
|
17
|
+
res << @before.to_sass(opts) if @before
|
18
18
|
res << ' ' if @before && @whitespace_before
|
19
|
-
res << '#{' << @mid.to_sass << '}'
|
19
|
+
res << '#{' << @mid.to_sass(opts) << '}'
|
20
20
|
res << ' ' if @after && @whitespace_after
|
21
|
-
res << @after.to_sass if @after
|
21
|
+
res << @after.to_sass(opts) if @after
|
22
22
|
res
|
23
23
|
end
|
24
24
|
|
data/lib/sass/script/lexer.rb
CHANGED
@@ -111,7 +111,7 @@ module Sass
|
|
111
111
|
|
112
112
|
# A hash of regular expressions that are used for tokenizing strings.
|
113
113
|
#
|
114
|
-
# The key is a [Symbol, Boolean] pair.
|
114
|
+
# The key is a `[Symbol, Boolean]` pair.
|
115
115
|
# The symbol represents which style of quotation to use,
|
116
116
|
# while the boolean represents whether or not the string
|
117
117
|
# is following an interpolated segment.
|
@@ -196,11 +196,12 @@ module Sass
|
|
196
196
|
def read_token
|
197
197
|
return if done?
|
198
198
|
return unless value = token
|
199
|
+
type, val, size = value
|
200
|
+
size ||= @scanner.matched_size
|
199
201
|
|
200
|
-
|
201
|
-
Token.new(
|
202
|
-
current_position - @scanner.
|
203
|
-
@scanner.pos - @scanner.matched_size)
|
202
|
+
val.line = @line if val.is_a?(Script::Node)
|
203
|
+
Token.new(type, val, @line,
|
204
|
+
current_position - size, @scanner.pos - size)
|
204
205
|
end
|
205
206
|
|
206
207
|
def whitespace
|
@@ -236,13 +237,13 @@ module Sass
|
|
236
237
|
|
237
238
|
def ident
|
238
239
|
return unless s = scan(REGULAR_EXPRESSIONS[:ident])
|
239
|
-
[:ident, s
|
240
|
+
[:ident, s]
|
240
241
|
end
|
241
242
|
|
242
243
|
def string(re, open)
|
243
244
|
return unless scan(STRING_REGULAR_EXPRESSIONS[[re, open]])
|
244
245
|
@interpolation_stack << re if @scanner[2].empty? # Started an interpolated section
|
245
|
-
[:string, Script::String.new(@scanner[1].gsub(/\\([
|
246
|
+
[:string, Script::String.new(@scanner[1].gsub(/\\(['"]|\#\{)/, '\1'), :string)]
|
246
247
|
end
|
247
248
|
|
248
249
|
def number
|
@@ -272,7 +273,8 @@ module Sass
|
|
272
273
|
@offset = (c == 0 ? @offset + str2.size : str2[/\n(.*)/, 1].size)
|
273
274
|
[:special_fun,
|
274
275
|
Haml::Util.merge_adjacent_strings(
|
275
|
-
[str1] + Sass::Engine.parse_interp(str2, @line, @options))
|
276
|
+
[str1] + Sass::Engine.parse_interp(str2, @line, @options)),
|
277
|
+
str1.size + str2.size]
|
276
278
|
end
|
277
279
|
|
278
280
|
def ident_op
|
data/lib/sass/script/literal.rb
CHANGED
@@ -120,6 +120,16 @@ MSG
|
|
120
120
|
Sass::Script::String.new("#{self.to_s}, #{other.to_s}")
|
121
121
|
end
|
122
122
|
|
123
|
+
# The SassScript `=` operation
|
124
|
+
# (used for proprietary MS syntax like `alpha(opacity=20)`).
|
125
|
+
#
|
126
|
+
# @param other [Literal] The right-hand side of the operator
|
127
|
+
# @return [Script::String] A string containing both literals
|
128
|
+
# separated by `"="`
|
129
|
+
def single_eq(other)
|
130
|
+
Sass::Script::String.new("#{self.to_s}=#{other.to_s}")
|
131
|
+
end
|
132
|
+
|
123
133
|
# The SassScript `+` operation.
|
124
134
|
#
|
125
135
|
# @param other [Literal] The right-hand side of the operator
|
@@ -208,7 +218,7 @@ MSG
|
|
208
218
|
# as it would be output to the CSS document.
|
209
219
|
#
|
210
220
|
# @return [String]
|
211
|
-
def to_s
|
221
|
+
def to_s(opts = {})
|
212
222
|
raise Sass::SyntaxError.new("[BUG] All subclasses of Sass::Literal must implement #to_s.")
|
213
223
|
end
|
214
224
|
alias_method :to_sass, :to_s
|
data/lib/sass/script/node.rb
CHANGED
@@ -74,12 +74,21 @@ module Sass::Script
|
|
74
74
|
# Returns the text of this SassScript expression.
|
75
75
|
#
|
76
76
|
# @return [String]
|
77
|
-
def to_sass
|
77
|
+
def to_sass(opts = {})
|
78
78
|
raise NotImplementedError.new("All subclasses of Sass::Script::Node must override #to_sass.")
|
79
79
|
end
|
80
80
|
|
81
81
|
protected
|
82
82
|
|
83
|
+
# Converts underscores to dashes if the :dasherize option is set.
|
84
|
+
def dasherize(s, opts)
|
85
|
+
if opts[:dasherize]
|
86
|
+
s.gsub(/_/,'-')
|
87
|
+
else
|
88
|
+
s
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
83
92
|
# Evaluates this node.
|
84
93
|
#
|
85
94
|
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
data/lib/sass/script/number.rb
CHANGED
@@ -238,7 +238,7 @@ module Sass::Script
|
|
238
238
|
# as long as there is only one unit.
|
239
239
|
#
|
240
240
|
# @return [String] The representation
|
241
|
-
def inspect
|
241
|
+
def inspect(opts = {})
|
242
242
|
value =
|
243
243
|
if self.value.is_a?(Float) && (self.value.infinite? || self.value.nan?)
|
244
244
|
self.value
|
@@ -299,6 +299,30 @@ module Sass::Script
|
|
299
299
|
end, num_units, den_units)
|
300
300
|
end
|
301
301
|
|
302
|
+
# @param other [Number] A number to decide if it can be compared with this number.
|
303
|
+
# @return [Boolean] Whether or not this number can be compared with the other.
|
304
|
+
def comparable_to?(other)
|
305
|
+
begin
|
306
|
+
self.operate(other, :+)
|
307
|
+
true
|
308
|
+
rescue Sass::UnitConversionError
|
309
|
+
false
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# Returns a human readable representation of the units in this number.
|
314
|
+
# For complex units this takes the form of:
|
315
|
+
# numerator_unit1 * numerator_unit2 / denominator_unit1 * denominator_unit2
|
316
|
+
# @return [String] a string that represents the units in this number
|
317
|
+
def unit_str
|
318
|
+
rv = numerator_units.sort.join("*")
|
319
|
+
if denominator_units.any?
|
320
|
+
rv << "/"
|
321
|
+
rv << denominator_units.sort.join("*")
|
322
|
+
end
|
323
|
+
rv
|
324
|
+
end
|
325
|
+
|
302
326
|
protected
|
303
327
|
|
304
328
|
def operate(other, operation)
|
@@ -343,15 +367,6 @@ module Sass::Script
|
|
343
367
|
end
|
344
368
|
end
|
345
369
|
|
346
|
-
def unit_str
|
347
|
-
rv = numerator_units.join("*")
|
348
|
-
if denominator_units.any?
|
349
|
-
rv << "/"
|
350
|
-
rv << denominator_units.join("*")
|
351
|
-
end
|
352
|
-
rv
|
353
|
-
end
|
354
|
-
|
355
370
|
def normalize!
|
356
371
|
return if unitless?
|
357
372
|
@numerator_units, @denominator_units = sans_common_units(numerator_units, denominator_units)
|
@@ -5,6 +5,7 @@ require 'sass/script/color'
|
|
5
5
|
require 'sass/script/functions'
|
6
6
|
require 'sass/script/unary_operation'
|
7
7
|
require 'sass/script/interpolation'
|
8
|
+
require 'sass/script/string_interpolation'
|
8
9
|
|
9
10
|
module Sass::Script
|
10
11
|
# A SassScript parse node representing a binary operation,
|
@@ -33,10 +34,10 @@ module Sass::Script
|
|
33
34
|
end
|
34
35
|
|
35
36
|
# @see Node#to_sass
|
36
|
-
def to_sass
|
37
|
+
def to_sass(opts = {})
|
37
38
|
pred = Sass::Script::Parser.precedence_of(@operator)
|
38
|
-
o1 = operand_to_sass pred, @operand1
|
39
|
-
o2 = operand_to_sass pred, @operand2
|
39
|
+
o1 = operand_to_sass pred, @operand1, opts
|
40
|
+
o2 = operand_to_sass pred, @operand2, opts
|
40
41
|
sep =
|
41
42
|
case @operator
|
42
43
|
when :comma; ", "
|
@@ -82,10 +83,10 @@ module Sass::Script
|
|
82
83
|
|
83
84
|
private
|
84
85
|
|
85
|
-
def operand_to_sass(pred, op)
|
86
|
-
return "(#{op.to_sass})" if op.is_a?(Operation) &&
|
86
|
+
def operand_to_sass(pred, op, opts = {})
|
87
|
+
return "(#{op.to_sass(opts)})" if op.is_a?(Operation) &&
|
87
88
|
Sass::Script::Parser.precedence_of(op.operator) < pred
|
88
|
-
op.to_sass
|
89
|
+
op.to_sass(opts)
|
89
90
|
end
|
90
91
|
end
|
91
92
|
end
|
data/lib/sass/script/parser.rb
CHANGED
@@ -97,12 +97,7 @@ module Sass
|
|
97
97
|
# @return [Array<Script::Node>] The root nodes of the arguments.
|
98
98
|
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
|
99
99
|
def parse_mixin_definition_arglist
|
100
|
-
args =
|
101
|
-
|
102
|
-
if try_tok(:lparen)
|
103
|
-
args = defn_arglist(false) || args
|
104
|
-
assert_tok(:rparen)
|
105
|
-
end
|
100
|
+
args = defn_arglist!(false)
|
106
101
|
assert_done
|
107
102
|
|
108
103
|
args.each do |k, v|
|
@@ -127,7 +122,7 @@ module Sass
|
|
127
122
|
|
128
123
|
# @private
|
129
124
|
PRECEDENCE = [
|
130
|
-
:comma, :concat, :or, :and,
|
125
|
+
:comma, :single_eq, :concat, :or, :and,
|
131
126
|
[:eq, :neq],
|
132
127
|
[:gt, :gte, :lt, :lte],
|
133
128
|
[:plus, :minus],
|
@@ -186,6 +181,7 @@ RUBY
|
|
186
181
|
def lexer_class; Lexer; end
|
187
182
|
|
188
183
|
production :expr, :interpolation, :comma
|
184
|
+
production :equals, :interpolation, :single_eq
|
189
185
|
|
190
186
|
def interpolation
|
191
187
|
e = concat
|
@@ -232,32 +228,45 @@ RUBY
|
|
232
228
|
end
|
233
229
|
node(Script::String.new(name.value, :identifier))
|
234
230
|
else
|
235
|
-
args =
|
231
|
+
args = fn_arglist || []
|
236
232
|
assert_tok(:rparen)
|
237
233
|
node(Script::Funcall.new(name.value, args))
|
238
234
|
end
|
239
235
|
end
|
240
236
|
|
241
|
-
def defn_arglist(must_have_default)
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
val
|
252
|
-
|
253
|
-
|
237
|
+
def defn_arglist!(must_have_default)
|
238
|
+
return [] unless try_tok(:lparen)
|
239
|
+
return [] if try_tok(:rparen)
|
240
|
+
res = []
|
241
|
+
loop do
|
242
|
+
line = @lexer.line
|
243
|
+
offset = @lexer.offset + 1
|
244
|
+
c = assert_tok(:const)
|
245
|
+
var = Script::Variable.new(c.value)
|
246
|
+
if tok = (try_tok(:colon) || try_tok(:single_eq))
|
247
|
+
val = assert_expr(:concat)
|
248
|
+
|
249
|
+
if tok.type == :single_eq
|
250
|
+
val.context = :equals
|
251
|
+
val.options = @options
|
252
|
+
Script.equals_warning("mixin argument defaults", "$#{c.value}",
|
253
|
+
val.to_sass, false, line, offset, @options[:filename])
|
254
|
+
end
|
255
|
+
must_have_default = true
|
256
|
+
elsif must_have_default
|
257
|
+
raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.")
|
254
258
|
end
|
255
|
-
|
256
|
-
|
259
|
+
res << [var, val]
|
260
|
+
break unless try_tok(:comma)
|
257
261
|
end
|
262
|
+
assert_tok(:rparen)
|
263
|
+
res
|
264
|
+
end
|
258
265
|
|
259
|
-
|
260
|
-
|
266
|
+
def fn_arglist
|
267
|
+
return unless e = equals
|
268
|
+
return [e] unless try_tok(:comma)
|
269
|
+
[e, *fn_arglist]
|
261
270
|
end
|
262
271
|
|
263
272
|
def arglist
|
@@ -303,9 +312,9 @@ RUBY
|
|
303
312
|
line = @lexer.line
|
304
313
|
mid = parse_interpolated
|
305
314
|
last = assert_expr(:string)
|
306
|
-
|
307
|
-
|
308
|
-
|
315
|
+
interp = StringInterpolation.new(first.value, mid, last)
|
316
|
+
interp.line = line
|
317
|
+
interp
|
309
318
|
end
|
310
319
|
|
311
320
|
def number
|
data/lib/sass/script/string.rb
CHANGED
@@ -35,23 +35,28 @@ module Sass::Script
|
|
35
35
|
end
|
36
36
|
|
37
37
|
# @see Node#to_s
|
38
|
-
def to_s
|
39
|
-
to_sass
|
38
|
+
def to_s(opts = {})
|
39
|
+
to_sass(opts)
|
40
40
|
end
|
41
41
|
|
42
|
-
# @param
|
43
|
-
#
|
42
|
+
# @param opts [{Symbol => Object}]
|
43
|
+
# `opts[:type]` -- The type of string to render this as.
|
44
|
+
# `:string`s have double quotes, `:identifier`s do not.
|
45
|
+
# Defaults to `:identifier`.
|
44
46
|
# @see Node#to_sass
|
45
|
-
def to_sass(
|
47
|
+
def to_sass(opts = {})
|
48
|
+
type = opts[:type] || self.type
|
46
49
|
if type == :identifier
|
47
50
|
if context == :equals && Sass::SCSS::RX.escape_ident(self.value).include?(?\\)
|
48
51
|
return "unquote(#{Sass::Script::String.new(self.value, :string).to_sass})"
|
52
|
+
elsif context == :equals && self.value.size == 0
|
53
|
+
return %q{""}
|
49
54
|
end
|
50
55
|
return self.value
|
51
56
|
end
|
52
57
|
|
53
|
-
#
|
54
|
-
|
58
|
+
return "\"#{value.gsub('"', "\\\"")}\"" if opts[:quote] == %q{"}
|
59
|
+
return "'#{value.gsub("'", "\\'")}'" if opts[:quote] == %q{'}
|
55
60
|
return "\"#{value}\"" unless value.include?('"')
|
56
61
|
return "'#{value}'" unless value.include?("'")
|
57
62
|
"\"#{value.gsub('"', "\\\"")}\"" #'
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Sass::Script
|
2
|
+
class StringInterpolation < Node
|
3
|
+
def initialize(before, mid, after)
|
4
|
+
@before = before
|
5
|
+
@mid = mid
|
6
|
+
@after = after
|
7
|
+
end
|
8
|
+
|
9
|
+
def inspect
|
10
|
+
"(string_interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_sass(opts = {})
|
14
|
+
# We can get rid of all of this when we remove the deprecated :equals context
|
15
|
+
before_unquote, before_quote_char, before_str = parse_str(@before.to_sass(opts))
|
16
|
+
after_unquote, after_quote_char, after_str = parse_str(@after.to_sass(opts))
|
17
|
+
unquote = before_unquote || after_unquote ||
|
18
|
+
(before_quote_char && !after_quote_char && !after_str.empty?) ||
|
19
|
+
(!before_quote_char && after_quote_char && !before_str.empty?)
|
20
|
+
quote_char =
|
21
|
+
if before_quote_char && after_quote_char && before_quote_char != after_quote_char
|
22
|
+
before_str.gsub!("\\'", "'")
|
23
|
+
before_str.gsub!('"', "\\\"")
|
24
|
+
after_str.gsub!("\\'", "'")
|
25
|
+
after_str.gsub!('"', "\\\"")
|
26
|
+
'"'
|
27
|
+
else
|
28
|
+
before_quote_char || after_quote_char
|
29
|
+
end
|
30
|
+
|
31
|
+
res = ""
|
32
|
+
res << 'unquote(' if unquote
|
33
|
+
res << quote_char if quote_char
|
34
|
+
res << before_str
|
35
|
+
res << '#{' << @mid.to_sass(opts) << '}'
|
36
|
+
res << after_str
|
37
|
+
res << quote_char if quote_char
|
38
|
+
res << ')' if unquote
|
39
|
+
res
|
40
|
+
end
|
41
|
+
|
42
|
+
def children
|
43
|
+
[@before, @mid, @after].compact
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def _perform(environment)
|
49
|
+
res = ""
|
50
|
+
res << @before.perform(environment).value
|
51
|
+
val = @mid.perform(environment)
|
52
|
+
res << (val.is_a?(Sass::Script::String) ? val.value : val.to_s)
|
53
|
+
res << @after.perform(environment).value
|
54
|
+
Sass::Script::String.new(res, :string)
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_str(str)
|
58
|
+
case str
|
59
|
+
when /^unquote\((["'])(.*)\1\)$/
|
60
|
+
return true, $1, $2
|
61
|
+
when '""'
|
62
|
+
return false, nil, ""
|
63
|
+
when /^(["'])(.*)\1$/
|
64
|
+
return false, $1, $2
|
65
|
+
else
|
66
|
+
return false, nil, str
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -19,12 +19,12 @@ module Sass::Script
|
|
19
19
|
end
|
20
20
|
|
21
21
|
# @see Node#to_sass
|
22
|
-
def to_sass
|
23
|
-
operand = @operand.to_sass
|
22
|
+
def to_sass(opts = {})
|
23
|
+
operand = @operand.to_sass(opts)
|
24
24
|
if @operand.is_a?(Operation) ||
|
25
25
|
(@operator == :minus &&
|
26
26
|
(operand =~ Sass::SCSS::RX::IDENT) == 0)
|
27
|
-
operand = "(#{@operand.to_sass})"
|
27
|
+
operand = "(#{@operand.to_sass(opts)})"
|
28
28
|
end
|
29
29
|
op = Lexer::OPERATORS_REVERSE[@operator]
|
30
30
|
op + (op =~ /[a-z]/ ? " " : "") + operand
|
data/lib/sass/script/variable.rb
CHANGED
@@ -14,9 +14,9 @@ module Sass
|
|
14
14
|
end
|
15
15
|
|
16
16
|
# @return [String] A string representation of the variable
|
17
|
-
def inspect
|
17
|
+
def inspect(opts = {})
|
18
18
|
return "!important" if name == "important"
|
19
|
-
"$#{name}"
|
19
|
+
"$#{dasherize(name, opts)}"
|
20
20
|
end
|
21
21
|
alias_method :to_sass, :inspect
|
22
22
|
|