sass 3.1.0.alpha.23 → 3.1.0.alpha.24

Sign up to get free protection for your applications and to get access to all the features.
@@ -1 +1 @@
1
- 3.1.0.alpha.23
1
+ 3.1.0.alpha.24
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.1.0.alpha.23
1
+ 3.1.0.alpha.24
@@ -14,6 +14,7 @@ require 'sass/tree/extend_node'
14
14
  require 'sass/tree/if_node'
15
15
  require 'sass/tree/while_node'
16
16
  require 'sass/tree/for_node'
17
+ require 'sass/tree/each_node'
17
18
  require 'sass/tree/debug_node'
18
19
  require 'sass/tree/warn_node'
19
20
  require 'sass/tree/import_node'
@@ -610,6 +611,8 @@ WARNING
610
611
  parse_mixin_include(line, root)
611
612
  elsif directive == "for"
612
613
  parse_for(line, root, value)
614
+ elsif directive == "each"
615
+ parse_each(line, root, value)
613
616
  elsif directive == "else"
614
617
  parse_else(parent, line, value)
615
618
  elsif directive == "while"
@@ -671,6 +674,27 @@ WARNING
671
674
  Tree::ForNode.new(var, parsed_from, parsed_to, to_name == 'to')
672
675
  end
673
676
 
677
+ def parse_each(line, root, text)
678
+ var, list_expr = text.scan(/^([^\s]+)\s+in\s+(.+)$/).first
679
+
680
+ if var.nil? # scan failed, try to figure out why for error message
681
+ if text !~ /^[^\s]+/
682
+ expected = "variable name"
683
+ elsif text !~ /^[^\s]+\s+from\s+.+/
684
+ expected = "'in <expr>'"
685
+ end
686
+ raise SyntaxError.new("Invalid for directive '@each #{text}': expected #{expected}.")
687
+ end
688
+ raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
689
+ if var.slice!(0) == ?!
690
+ offset = line.offset + line.text.index("!" + var) + 1
691
+ Script.var_warning(var, @line, offset, @options[:filename])
692
+ end
693
+
694
+ parsed_list = parse_script(list_expr, :offset => line.offset + line.text.index(list_expr))
695
+ Tree::EachNode.new(var, parsed_list)
696
+ end
697
+
674
698
  def parse_else(parent, line, text)
675
699
  previous = parent.children.last
676
700
  raise SyntaxError.new("@else must come after @if.") unless previous.is_a?(Tree::IfNode)
@@ -251,7 +251,7 @@ WARNING
251
251
  e, rest = _to_sass_tree_plus_minus_eq(arr)
252
252
  until rest.empty?
253
253
  e2, rest = _to_sass_tree_plus_minus_eq(rest)
254
- e = Sass::Script::Operation.new(e, e2, :concat)
254
+ e = Sass::Script::Operation.new(e, e2, :space)
255
255
  end
256
256
  return e
257
257
  end
@@ -22,7 +22,7 @@ module Sass
22
22
  end
23
23
 
24
24
  # Short-circuit all the SassScript-only productions
25
- alias_method :interpolation, :concat
25
+ alias_method :interpolation, :space
26
26
  alias_method :or_expr, :div
27
27
  alias_method :unary_div, :ident
28
28
  alias_method :paren, :string
@@ -113,6 +113,17 @@ module Sass::Script
113
113
  # \{#abs abs($value)}
114
114
  # : Returns the absolute value of a number.
115
115
  #
116
+ # ## List Functions {#list-functions}
117
+ #
118
+ # \{#length length($list)}
119
+ # : Returns the length of a list.
120
+ #
121
+ # \{#nth nth($list, $n)}
122
+ # : Returns a specific item in a list.
123
+ #
124
+ # \{#join join($list1, $list2, \[$separator\])}
125
+ # : Joins together two lists into one.
126
+ #
116
127
  # ## Introspection Functions
117
128
  #
118
129
  # \{#type_of type-of($value)}
@@ -705,7 +716,7 @@ module Sass::Script
705
716
  # mix(#f00, #00f) => #7f007f
706
717
  # mix(#f00, #00f, 25%) => #3f00bf
707
718
  # mix(rgba(255, 0, 0, 0.5), #00f) => rgba(63, 0, 191, 0.75)
708
- # @overload mix(color1, color2, weight = 50%)
719
+ # @overload mix(color1, color2, weight: 50%)
709
720
  # @param color1 [Color]
710
721
  # @param color2 [Color]
711
722
  # @param weight [Number] between 0% and 100%
@@ -912,7 +923,7 @@ module Sass::Script
912
923
  # round(10.6px) => 11px
913
924
  # @param value [Number] The number
914
925
  # @return [Number] The rounded number
915
- # @raise [Sass::SyntaxError] if `value` isn't a number
926
+ # @raise [ArgumentError] if `value` isn't a number
916
927
  def round(value)
917
928
  numeric_transformation(value) {|n| n.round}
918
929
  end
@@ -925,7 +936,7 @@ module Sass::Script
925
936
  # ciel(10.6px) => 11px
926
937
  # @param value [Number] The number
927
938
  # @return [Number] The rounded number
928
- # @raise [Sass::SyntaxError] if `value` isn't a number
939
+ # @raise [ArgumentError] if `value` isn't a number
929
940
  def ceil(value)
930
941
  numeric_transformation(value) {|n| n.ceil}
931
942
  end
@@ -938,7 +949,7 @@ module Sass::Script
938
949
  # floor(10.6px) => 10px
939
950
  # @param value [Number] The number
940
951
  # @return [Number] The rounded number
941
- # @raise [Sass::SyntaxError] if `value` isn't a number
952
+ # @raise [ArgumentError] if `value` isn't a number
942
953
  def floor(value)
943
954
  numeric_transformation(value) {|n| n.floor}
944
955
  end
@@ -951,12 +962,122 @@ module Sass::Script
951
962
  # abs(-10px) => 10px
952
963
  # @param value [Number] The number
953
964
  # @return [Number] The absolute value
954
- # @raise [Sass::SyntaxError] if `value` isn't a number
965
+ # @raise [ArgumentError] if `value` isn't a number
955
966
  def abs(value)
956
967
  numeric_transformation(value) {|n| n.abs}
957
968
  end
958
969
  declare :abs, [:value]
959
970
 
971
+ # Return the length of a list.
972
+ #
973
+ # @example
974
+ # length(10px) => 1
975
+ # length(10px 20px 30px) => 3
976
+ # @param list [Literal] The list
977
+ # @return [Number] The length
978
+ def length(list)
979
+ Sass::Script::Number.new(list.to_a.size)
980
+ end
981
+ declare :length, [:list]
982
+
983
+ # Gets the nth item in a list.
984
+ #
985
+ # Note that unlike some languages, the first item in a Sass list is number 1,
986
+ # the second number 2, and so forth.
987
+ #
988
+ # @example
989
+ # nth(10px 20px 30px, 1) => 10px
990
+ # nth((Helvetica, Arial, sans-serif), 3) => sans-serif
991
+ # @param list [Literal] The list
992
+ # @param n [Number] The index into the list
993
+ # @return [Literal] The nth item in the list
994
+ # @raise [ArgumentError] If `n` isn't an integer between 1 and the list's length.
995
+ def nth(list, n)
996
+ assert_type n, :Number
997
+ if !n.int?
998
+ raise ArgumentError.new("List index #{n} must be an integer")
999
+ elsif n.to_i < 1
1000
+ raise ArgumentError.new("List index #{n} must be greater than or equal to 1")
1001
+ elsif n.to_i > (size = list.to_a.size)
1002
+ raise ArgumentError.new("List index is #{n} but list is only #{size} item#{'s' if size != 1} long")
1003
+ end
1004
+
1005
+ list.to_a[n.to_i - 1]
1006
+ end
1007
+ declare :nth, [:list, :n]
1008
+
1009
+ # Joins together two lists into a new list.
1010
+ #
1011
+ # Unless the `$separator` argument is passed,
1012
+ # if one list is comma-separated and one is space-separated,
1013
+ # the first parameter's separator is used for the resulting list.
1014
+ # If the lists have only one item each, spaces are used for the resulting list.
1015
+ #
1016
+ # @example
1017
+ # join(10px 20px, 30px 40px) => 10px 20px 30px 40px
1018
+ # join((blue, red), (#abc, #def)) => blue, red, #abc, #def
1019
+ # join(10px, 20px) => 10px 20px
1020
+ # join(10px, 20px, comma) => 10px, 20px
1021
+ # join((blue, red), (#abc, #def), space) => blue red #abc #def
1022
+ # @overload join(list1, list2, separator: auto)
1023
+ # @param list1 [Literal] The first list to join
1024
+ # @param list2 [Literal] The second list to join
1025
+ # @param separator [String] How the list separator (comma or space) should be determined.
1026
+ # If this is `comma` or `space`, that is always the separator;
1027
+ # if this is `auto` (the default), the separator is determined as explained above.
1028
+ def join(list1, list2, separator = Sass::Script::String.new("auto"))
1029
+ assert_type separator, :String
1030
+ unless %w[auto space comma].include?(separator.value)
1031
+ raise ArgumentError.new("Separator name must be space, comma, or auto")
1032
+ end
1033
+ sep1 = list1.separator if list1.is_a?(Sass::Script::List)
1034
+ sep2 = list2.separator if list2.is_a?(Sass::Script::List)
1035
+ Sass::Script::List.new(
1036
+ list1.to_a + list2.to_a,
1037
+ if separator.value == 'auto'
1038
+ sep1 || sep2 || :space
1039
+ else
1040
+ separator.value.to_sym
1041
+ end)
1042
+ end
1043
+ declare :join, [:list1, :list2]
1044
+ declare :join, [:list1, :list2, :separator]
1045
+
1046
+ # Appends a single value onto the end of a list.
1047
+ #
1048
+ # Unless the `$separator` argument is passed,
1049
+ # if the list has only one item,
1050
+ # the resulting list will be space-separated.
1051
+ #
1052
+ # @example
1053
+ # append(10px 20px, 30px) => 10px 20px 30px
1054
+ # append((blue, red), green) => blue, red, green
1055
+ # append(10px 20px, 30px 40px) => 10px 20px (30px 40px)
1056
+ # join(10px, 20px, comma) => 10px, 20px
1057
+ # join((blue, red), green, space) => blue red green
1058
+ # @overload join(list, val, separator: auto)
1059
+ # @param list1 [Literal] The first list to join
1060
+ # @param list2 [Literal] The second list to join
1061
+ # @param separator [String] How the list separator (comma or space) should be determined.
1062
+ # If this is `comma` or `space`, that is always the separator;
1063
+ # if this is `auto` (the default), the separator is determined as explained above.
1064
+ def append(list, val, separator = Sass::Script::String.new("auto"))
1065
+ assert_type separator, :String
1066
+ unless %w[auto space comma].include?(separator.value)
1067
+ raise ArgumentError.new("Separator name must be space, comma, or auto")
1068
+ end
1069
+ sep = list.separator if list.is_a?(Sass::Script::List)
1070
+ Sass::Script::List.new(
1071
+ list.to_a + [val],
1072
+ if separator.value == 'auto'
1073
+ sep || :space
1074
+ else
1075
+ separator.value.to_sym
1076
+ end)
1077
+ end
1078
+ declare :append, [:list, :val]
1079
+ declare :append, [:list, :val, :separator]
1080
+
960
1081
  private
961
1082
 
962
1083
  # This method implements the pattern of transforming a numeric value into
@@ -0,0 +1,76 @@
1
+ module Sass::Script
2
+ # A SassScript object representing a CSS list.
3
+ # This includes both comma-separated lists and space-separated lists.
4
+ class List < Literal
5
+ # The Ruby array containing the contents of the list.
6
+ #
7
+ # @return [Array<Literal>]
8
+ attr_reader :value
9
+ alias_method :children, :value
10
+ alias_method :to_a, :value
11
+
12
+ # The operator separating the values of the list.
13
+ # Either `:comma` or `:space`.
14
+ #
15
+ # @return [Symbol]
16
+ attr_reader :separator
17
+
18
+ # Creates a new list.
19
+ #
20
+ # @param value [Array<Literal>] See \{#value}
21
+ # @param separator [String] See \{#separator}
22
+ def initialize(value, separator)
23
+ super(value)
24
+ @separator = separator
25
+ end
26
+
27
+ # @see Node#eq
28
+ def eq(other)
29
+ Sass::Script::Bool.new(
30
+ self.class == other.class && self.value == other.value &&
31
+ self.separator == other.separator)
32
+ end
33
+
34
+ # @see Node#to_s
35
+ def to_s(opts = {})
36
+ return value.map {|e| e.to_s(opts)}.join(sep_str)
37
+ end
38
+
39
+ # @see Node#to_sass
40
+ def to_sass(opts = {})
41
+ precedence = Sass::Script::Parser.precedence_of(separator)
42
+ value.map do |v|
43
+ if v.is_a?(List) && Sass::Script::Parser.precedence_of(v.separator) <= precedence
44
+ "(#{v.to_sass(opts)})"
45
+ else
46
+ v.to_sass(opts)
47
+ end
48
+ end.join(sep_str(nil))
49
+ end
50
+
51
+ # @see Node#inspect
52
+ def inspect
53
+ "(#{to_sass})"
54
+ end
55
+
56
+ protected
57
+
58
+ # @see Node#_perform
59
+ def _perform(environment)
60
+ list = Sass::Script::List.new(
61
+ value.map {|e| e.perform(environment)},
62
+ separator)
63
+ list.options = self.options
64
+ list.context = self.context
65
+ list
66
+ end
67
+
68
+ private
69
+
70
+ def sep_str(opts = self.options)
71
+ return ' ' if separator == :space
72
+ return ',' if opts && opts[:style] == :compressed
73
+ return ', '
74
+ end
75
+ end
76
+ end
@@ -9,6 +9,7 @@ module Sass::Script
9
9
  require 'sass/script/number'
10
10
  require 'sass/script/color'
11
11
  require 'sass/script/bool'
12
+ require 'sass/script/list'
12
13
 
13
14
  # Returns the Ruby value of the literal.
14
15
  # The type of this value varies based on the subclass.
@@ -107,7 +108,7 @@ MSG
107
108
  # @param other [Literal] The right-hand side of the operator
108
109
  # @return [Script::String] A string containing both literals
109
110
  # separated by a space
110
- def concat(other)
111
+ def space(other)
111
112
  Sass::Script::String.new("#{self.to_s} #{other.to_s}")
112
113
  end
113
114
 
@@ -214,6 +215,14 @@ MSG
214
215
  # @raise [Sass::SyntaxError] if this literal isn't an integer
215
216
  def assert_int!; to_i; end
216
217
 
218
+ # Returns the value of this literal as a list.
219
+ # Single literals are considered the same as single-element lists.
220
+ #
221
+ # @return [Array<Literal>] The of this literal as a list
222
+ def to_a
223
+ [self]
224
+ end
225
+
217
226
  # Returns the string representation of this literal
218
227
  # as it would be output to the CSS document.
219
228
  #
@@ -238,7 +238,7 @@ module Sass::Script
238
238
  # @return [String] The CSS representation of this number
239
239
  # @raise [Sass::SyntaxError] if this number has units that can't be used in CSS
240
240
  # (e.g. `px*in`)
241
- def to_s
241
+ def to_s(opts = {})
242
242
  return original if original
243
243
  raise Sass::SyntaxError.new("#{inspect} isn't a valid CSS value.") unless legal_units?
244
244
  inspect
@@ -41,7 +41,7 @@ module Sass::Script
41
41
  sep =
42
42
  case @operator
43
43
  when :comma; ", "
44
- when :concat; " "
44
+ when :space; " "
45
45
  else; " #{Lexer::OPERATORS_REVERSE[@operator]} "
46
46
  end
47
47
  "#{o1}#{sep}#{o2}"
@@ -66,7 +66,7 @@ module Sass::Script
66
66
  literal1 = @operand1.perform(environment)
67
67
  literal2 = @operand2.perform(environment)
68
68
 
69
- if @operator == :concat && context == :equals
69
+ if @operator == :space && context == :equals
70
70
  literal1 = Sass::Script::String.new(literal1.value) if literal1.is_a?(Sass::Script::String)
71
71
  literal2 = Sass::Script::String.new(literal2.value) if literal2.is_a?(Sass::Script::String)
72
72
  end
@@ -82,6 +82,7 @@ module Sass::Script
82
82
  private
83
83
 
84
84
  def operand_to_sass(op, side, opts)
85
+ return "(#{op.to_sass(opts)})" if op.is_a?(List)
85
86
  return op.to_sass(opts) unless op.is_a?(Operation)
86
87
 
87
88
  pred = Sass::Script::Parser.precedence_of(@operator)
@@ -123,14 +123,14 @@ module Sass
123
123
  end
124
124
 
125
125
  PRECEDENCE = [
126
- :comma, :single_eq, :concat, :or, :and,
126
+ :comma, :single_eq, :space, :or, :and,
127
127
  [:eq, :neq],
128
128
  [:gt, :gte, :lt, :lte],
129
129
  [:plus, :minus],
130
130
  [:times, :div, :mod],
131
131
  ]
132
132
 
133
- ASSOCIATIVE = [:comma, :concat, :plus, :times]
133
+ ASSOCIATIVE = [:plus, :times]
134
134
 
135
135
  class << self
136
136
  # Returns an integer representing the precedence
@@ -193,7 +193,18 @@ RUBY
193
193
  # @private
194
194
  def lexer_class; Lexer; end
195
195
 
196
- production :expr, :interpolation, :comma
196
+ def expr
197
+ interp = try_ops_after_interp([:comma], :expr) and return interp
198
+ line = @lexer.line
199
+ return unless e = interpolation
200
+ arr = [e]
201
+ while tok = try_tok(:comma)
202
+ interp = try_op_before_interp(tok, e) and return interp
203
+ arr << assert_expr(:interpolation)
204
+ end
205
+ arr.size == 1 ? arr.first : node(List.new(arr, :comma), line)
206
+ end
207
+
197
208
  production :equals, :interpolation, :single_eq
198
209
 
199
210
  def try_op_before_interp(op, prev = nil)
@@ -219,25 +230,27 @@ RUBY
219
230
  return interp
220
231
  end
221
232
 
222
- def interpolation(first = concat)
233
+ def interpolation(first = space)
223
234
  e = first
224
235
  while interp = try_tok(:begin_interpolation)
225
236
  wb = @lexer.whitespace?(interp)
226
237
  line = @lexer.line
227
238
  mid = parse_interpolated
228
239
  wa = @lexer.whitespace?
229
- e = Script::Interpolation.new(e, mid, concat, wb, wa)
240
+ e = Script::Interpolation.new(e, mid, space, wb, wa)
230
241
  e.line = line
231
242
  end
232
243
  e
233
244
  end
234
245
 
235
- def concat
246
+ def space
247
+ line = @lexer.line
236
248
  return unless e = or_expr
237
- while sub = or_expr
238
- e = node(Operation.new(e, sub, :concat))
249
+ arr = [e]
250
+ while e = or_expr
251
+ arr << e
239
252
  end
240
- e
253
+ arr.size == 1 ? arr.first : node(List.new(arr, :space), line)
241
254
  end
242
255
 
243
256
  production :or_expr, :and_expr, :or
@@ -280,7 +293,7 @@ RUBY
280
293
  c = assert_tok(:const)
281
294
  var = Script::Variable.new(c.value)
282
295
  if tok = (try_tok(:colon) || try_tok(:single_eq))
283
- val = assert_expr(:concat)
296
+ val = assert_expr(:space)
284
297
 
285
298
  if tok.type == :single_eq
286
299
  val.context = :equals
@@ -427,8 +440,8 @@ RUBY
427
440
  @lexer.expected!(EXPR_NAMES[:default])
428
441
  end
429
442
 
430
- def node(node)
431
- node.line = @lexer.line
443
+ def node(node, line = @lexer.line)
444
+ node.line = line
432
445
  node
433
446
  end
434
447
  end
@@ -98,8 +98,8 @@ module Sass
98
98
  node << comment
99
99
  end
100
100
 
101
- DIRECTIVES = Set[:mixin, :include, :debug, :warn, :for, :while, :if, :extend, :import,
102
- :media, :charset]
101
+ DIRECTIVES = Set[:mixin, :include, :debug, :warn, :for, :each, :while, :if,
102
+ :extend, :import, :media, :charset]
103
103
 
104
104
  def directive
105
105
  return unless tok(/@/)
@@ -169,6 +169,18 @@ module Sass
169
169
  block(node(Sass::Tree::ForNode.new(var, from, to, exclusive)), :directive)
170
170
  end
171
171
 
172
+ def each_directive
173
+ tok!(/\$/)
174
+ var = tok! IDENT
175
+ ss
176
+
177
+ tok!(/in/)
178
+ list = sass_script(:parse)
179
+ ss
180
+
181
+ block(node(Sass::Tree::EachNode.new(var, list)), :directive)
182
+ end
183
+
172
184
  def while_directive
173
185
  expr = sass_script(:parse)
174
186
  ss
@@ -0,0 +1,54 @@
1
+ require 'sass/tree/node'
2
+
3
+ module Sass::Tree
4
+ # A dynamic node representing a Sass `@each` loop.
5
+ #
6
+ # @see Sass::Tree
7
+ class EachNode < Node
8
+ # @param var [String] The name of the loop variable
9
+ # @param list [Script::Node] The parse tree for the list
10
+ def initialize(var, list)
11
+ @var = var
12
+ @list = list
13
+ super()
14
+ end
15
+
16
+ protected
17
+
18
+ # @see Node#to_src
19
+ def to_src(tabs, opts, fmt)
20
+ "#{' ' * tabs}@each $#{dasherize(@var, opts)} in #{@list.to_sass(opts)}" +
21
+ children_to_src(tabs, opts, fmt)
22
+ end
23
+
24
+ # Runs the child nodes once for each value in the list.
25
+ #
26
+ # @param environment [Sass::Environment] The lexical environment containing
27
+ # variable and mixin values
28
+ # @return [Array<Tree::Node>] The resulting static nodes
29
+ # @see Sass::Tree
30
+ def _perform(environment)
31
+ list = @list.perform(environment)
32
+
33
+ children = []
34
+ environment = Sass::Environment.new(environment)
35
+ list.to_a.each do |v|
36
+ environment.set_local_var(@var, v)
37
+ children += perform_children(environment)
38
+ end
39
+ children
40
+ end
41
+
42
+ # Returns an error message if the given child node is invalid,
43
+ # and false otherwise.
44
+ #
45
+ # {ExtendNode}s are valid within {EachNode}s.
46
+ #
47
+ # @param child [Tree::Node] A potential child node.
48
+ # @return [Boolean, String] Whether or not the child node is valid,
49
+ # as well as the error message to display if it is invalid
50
+ def invalid_child?(child)
51
+ super unless child.is_a?(ExtendNode)
52
+ end
53
+ end
54
+ end
@@ -199,7 +199,7 @@ module Sass::Tree
199
199
 
200
200
  def val_to_sass_concat(node, opts)
201
201
  return node unless node.is_a?(Sass::Script::Operation)
202
- return val_to_sass_div(node, opts) unless node.operator == :concat
202
+ return val_to_sass_div(node, opts) unless node.operator == :space
203
203
 
204
204
  Sass::Script::Operation.new(
205
205
  val_to_sass_div(node.operand1, opts),
@@ -675,6 +675,26 @@ foo {
675
675
  SCSS
676
676
  end
677
677
 
678
+ def test_each
679
+ assert_renders <<SASS, <<SCSS
680
+ a
681
+ @each $number in 1px 2px 3px 4px
682
+ b: $number
683
+
684
+ c
685
+ @each $str in foo, bar, baz, bang
686
+ d: $str
687
+ SASS
688
+ a {
689
+ @each $number in 1px 2px 3px 4px {
690
+ b: $number; } }
691
+
692
+ c {
693
+ @each $str in foo, bar, baz, bang {
694
+ d: $str; } }
695
+ SCSS
696
+ end
697
+
678
698
  def test_import
679
699
  assert_renders <<SASS, <<SCSS
680
700
  @import foo
@@ -1281,6 +1281,29 @@ a
1281
1281
  SASS
1282
1282
  end
1283
1283
 
1284
+ def test_each
1285
+ assert_equal(<<CSS, render(<<SASS))
1286
+ a {
1287
+ b: 1px;
1288
+ b: 2px;
1289
+ b: 3px;
1290
+ b: 4px;
1291
+ c: foo;
1292
+ c: bar;
1293
+ c: baz;
1294
+ c: bang;
1295
+ d: blue; }
1296
+ CSS
1297
+ a
1298
+ @each $number in 1px 2px 3px 4px
1299
+ b: $number
1300
+ @each $str in foo, bar, baz, bang
1301
+ c: $str
1302
+ @each $single in blue
1303
+ d: $single
1304
+ SASS
1305
+ end
1306
+
1284
1307
  def test_variable_reassignment
1285
1308
  assert_equal(<<CSS, render(<<SASS))
1286
1309
  a {
@@ -599,6 +599,75 @@ MSG
599
599
  assert_error_message("#ff0000 is not a number for `comparable'", "comparable(1px, #f00)")
600
600
  end
601
601
 
602
+ def test_length
603
+ assert_equal("5", evaluate("length(1 2 3 4 5)"))
604
+ assert_equal("4", evaluate("length((foo, bar, baz, bip))"))
605
+ assert_equal("3", evaluate("length((foo, bar, baz bip))"))
606
+ assert_equal("3", evaluate("length((foo, bar, (baz, bip)))"))
607
+ assert_equal("1", evaluate("length(#f00)"))
608
+ end
609
+
610
+ def test_nth
611
+ assert_equal("1", evaluate("nth(1 2 3, 1)"))
612
+ assert_equal("2", evaluate("nth(1 2 3, 2)"))
613
+ assert_equal("3", evaluate("nth((1, 2, 3), 3)"))
614
+ assert_equal("foo", evaluate("nth(foo, 1)"))
615
+ assert_equal("bar baz", evaluate("nth(foo (bar baz) bang, 2)"))
616
+ assert_error_message("List index 0 must be greater than or equal to 1 for `nth'", "nth(foo, 0)")
617
+ assert_error_message("List index -10 must be greater than or equal to 1 for `nth'", "nth(foo, -10)")
618
+ assert_error_message("List index 1.5 must be an integer for `nth'", "nth(foo, 1.5)")
619
+ assert_error_message("List index is 5 but list is only 4 items long for `nth'", "nth(1 2 3 4, 5)")
620
+ assert_error_message("List index is 2 but list is only 1 item long for `nth'", "nth(foo, 2)")
621
+ end
622
+
623
+ def test_join
624
+ assert_equal("1 2 3", evaluate("join(1 2, 3)"))
625
+ assert_equal("1 2 3", evaluate("join(1, 2 3)"))
626
+ assert_equal("1 2 3 4", evaluate("join(1 2, 3 4)"))
627
+ assert_equal("true", evaluate("(1 2 3 4) == join(1 2, 3 4)"))
628
+ assert_equal("false", evaluate("(1 2 (3 4)) == join(1 2, 3 4)"))
629
+ assert_equal("1, 2, 3", evaluate("join((1, 2), 3)"))
630
+ assert_equal("1, 2, 3", evaluate("join(1, (2, 3))"))
631
+ assert_equal("1, 2, 3, 4", evaluate("join((1, 2), (3, 4))"))
632
+ assert_equal("true", evaluate("(1, 2, 3, 4) == join((1, 2), (3, 4))"))
633
+ assert_equal("false", evaluate("(1, 2, (3, 4)) == join((1, 2), (3, 4))"))
634
+
635
+ assert_equal("1 2", evaluate("join(1, 2)"))
636
+ assert_equal("1 2 3 4", evaluate("join(1 2, (3, 4))"))
637
+ assert_equal("1, 2, 3, 4", evaluate("join((1, 2), 3 4)"))
638
+
639
+ assert_equal("1 2", evaluate("join(1, 2, auto)"))
640
+ assert_equal("1, 2, 3, 4", evaluate("join(1 2, 3 4, comma)"))
641
+ assert_equal("1 2 3 4", evaluate("join((1, 2), (3, 4), space)"))
642
+ assert_equal("1, 2", evaluate("join(1, 2, comma)"))
643
+
644
+ assert_error_message("Separator name must be space, comma, or auto for `join'", "join(1, 2, baboon)")
645
+ end
646
+
647
+ def test_append
648
+ assert_equal("1 2 3", evaluate("append(1 2, 3)"))
649
+ assert_equal("1 2 3 4", evaluate("append(1 2, 3 4)"))
650
+ assert_equal("false", evaluate("(1 2 3 4) == append(1 2, 3 4)"))
651
+ assert_equal("true", evaluate("(1 2 (3 4)) == append(1 2, 3 4)"))
652
+ assert_equal("1, 2, 3", evaluate("append((1, 2), 3)"))
653
+ assert_equal("1, 2, 3, 4", evaluate("append((1, 2), (3, 4))"))
654
+ assert_equal("false", evaluate("(1, 2, 3, 4) == append((1, 2), (3, 4))"))
655
+ assert_equal("true", evaluate("(1, 2, (3, 4)) == append((1, 2), (3, 4))"))
656
+
657
+ assert_equal("1 2", evaluate("append(1, 2)"))
658
+ assert_equal("1 2 3, 4", evaluate("append(1 2, (3, 4))"))
659
+ assert_equal("true", evaluate("(1 2 (3, 4)) == append(1 2, (3, 4))"))
660
+ assert_equal("1, 2, 3 4", evaluate("append((1, 2), 3 4)"))
661
+ assert_equal("true", evaluate("(1, 2, 3 4) == append((1, 2), 3 4)"))
662
+
663
+ assert_equal("1 2", evaluate("append(1, 2, auto)"))
664
+ assert_equal("1, 2, 3 4", evaluate("append(1 2, 3 4, comma)"))
665
+ assert_equal("1 2 3, 4", evaluate("append((1, 2), (3, 4), space)"))
666
+ assert_equal("1, 2", evaluate("append(1, 2, comma)"))
667
+
668
+ assert_error_message("Separator name must be space, comma, or auto for `append'", "append(1, 2, baboon)")
669
+ end
670
+
602
671
  def test_keyword_args_rgb
603
672
  assert_equal(%Q{white}, evaluate("rgb($red: 255, $green: 255, $blue: 255)"))
604
673
  end
@@ -82,28 +82,6 @@ WARN
82
82
  assert_renders "$foo !important"
83
83
  end
84
84
 
85
- def test_comma_operator
86
- assert_renders "$foo, $bar $baz"
87
- assert_renders "$foo $bar, $baz"
88
-
89
- assert_renders "($foo, $bar) $baz"
90
- assert_renders "$foo ($bar, $baz)"
91
-
92
- assert_equal "$foo, $bar $baz", render("$foo, ($bar $baz)")
93
- assert_equal "$foo $bar, $baz", render("($foo $bar), $baz")
94
- end
95
-
96
- def test_concat_operator
97
- assert_renders "$foo $bar or $baz"
98
- assert_renders "$foo or $bar $baz"
99
-
100
- assert_renders "($foo $bar) or $baz"
101
- assert_renders "$foo or ($bar $baz)"
102
-
103
- assert_equal "$foo $bar or $baz", render("$foo ($bar or $baz)")
104
- assert_equal "$foo or $bar $baz", render("($foo or $bar) $baz")
105
- end
106
-
107
85
  def self.test_precedence(outer, inner)
108
86
  op_outer = Sass::Script::Lexer::OPERATORS_REVERSE[outer]
109
87
  op_inner = Sass::Script::Lexer::OPERATORS_REVERSE[inner]
@@ -146,7 +124,7 @@ RUBY
146
124
  def self.separator_for(op_name)
147
125
  case op_name
148
126
  when :comma; ", "
149
- when :concat; " "
127
+ when :space; " "
150
128
  else; " #{Sass::Script::Lexer::OPERATORS_REVERSE[op_name]} "
151
129
  end
152
130
  end
@@ -182,8 +160,6 @@ RUBY
182
160
  test_precedence :plus, :div
183
161
  test_precedence :plus, :mod
184
162
 
185
- assert_associative :comma, :concat
186
- assert_associative :concat, :or
187
163
  assert_associative :plus, :minus
188
164
  assert_associative :times, :div
189
165
  assert_associative :times, :mod
@@ -196,6 +172,32 @@ RUBY
196
172
  assert_non_associative :lt, :lte
197
173
  assert_non_associative :lte, :gt
198
174
 
175
+ def test_comma_precedence
176
+ assert_renders "$foo, $bar, $baz"
177
+
178
+ assert_renders "$foo ($bar, $baz)"
179
+ assert_renders "($foo, $bar) $baz"
180
+
181
+ assert_equal "$foo, $bar $baz", render("$foo, ($bar $baz)")
182
+ assert_equal "$foo $bar, $baz", render("($foo $bar), $baz")
183
+
184
+ assert_equal "$foo, ($bar, $baz)", render("$foo, ($bar, $baz)")
185
+ assert_equal "($foo, $bar), $baz", render("($foo, $bar), $baz")
186
+ end
187
+
188
+ def test_space_precedence
189
+ assert_renders "$foo $bar $baz"
190
+
191
+ assert_renders "$foo or ($bar $baz)"
192
+ assert_renders "($foo $bar) or $baz"
193
+
194
+ assert_equal "$foo $bar or $baz", render("$foo ($bar or $baz)")
195
+ assert_equal "$foo or $bar $baz", render("($foo or $bar) $baz")
196
+
197
+ assert_equal "$foo ($bar $baz)", render("$foo ($bar $baz)")
198
+ assert_equal "($foo $bar) $baz", render("($foo $bar) $baz")
199
+ end
200
+
199
201
  def test_unary_op
200
202
  assert_renders "-12px"
201
203
  assert_renders '/"foo"'
@@ -103,7 +103,6 @@ class SassScriptTest < Test::Unit::TestCase
103
103
 
104
104
  def test_implicit_strings
105
105
  assert_equal Sass::Script::String.new("foo"), eval("foo")
106
- assert_equal Sass::Script::String.new("foo bar"), eval("foo bar")
107
106
  assert_equal Sass::Script::String.new("foo/bar"), eval("foo/bar")
108
107
  end
109
108
 
@@ -235,6 +234,10 @@ SASS
235
234
  def test_ruby_equality
236
235
  assert_equal eval('"foo"'), eval('"foo"')
237
236
  assert_equal eval('1'), eval('1.0')
237
+ assert_equal eval('1 2 3.0'), eval('1 2 3')
238
+ assert_equal eval('1, 2, 3.0'), eval('1, 2, 3')
239
+ assert_equal eval('(1 2), (3, 4), (5 6)'), eval('(1 2), (3, 4), (5 6)')
240
+ assert_not_equal eval('1, 2, 3'), eval('1 2 3')
238
241
  assert_not_equal eval('1'), eval('"1"')
239
242
  end
240
243
 
@@ -313,6 +316,14 @@ SASS
313
316
  assert_equal "true", resolve("false != true")
314
317
  assert_equal "false", resolve("1em == 1px")
315
318
  assert_equal "false", resolve("12 != 12")
319
+ assert_equal "true", resolve("(foo bar baz) == (foo bar baz)")
320
+ assert_equal "true", resolve("(foo, bar, baz) == (foo, bar, baz)")
321
+ assert_equal "true", resolve('((1 2), (3, 4), (5 6)) == ((1 2), (3, 4), (5 6))')
322
+ assert_equal "true", resolve('((1 2), (3 4)) == (1 2, 3 4)')
323
+ assert_equal "false", resolve('((1 2) 3) == (1 2 3)')
324
+ assert_equal "false", resolve('(1 (2 3)) == (1 2 3)')
325
+ assert_equal "false", resolve('((1, 2) (3, 4)) == (1, 2 3, 4)')
326
+ assert_equal "false", resolve('(1 2 3) == (1, 2, 3)')
316
327
  end
317
328
 
318
329
  def test_operation_precedence
@@ -235,6 +235,33 @@ $i: 1;
235
235
  SCSS
236
236
  end
237
237
 
238
+ def test_each_directive
239
+ assert_equal <<CSS, render(<<SCSS)
240
+ a {
241
+ b: 1px;
242
+ b: 2px;
243
+ b: 3px;
244
+ b: 4px; }
245
+
246
+ c {
247
+ d: foo;
248
+ d: bar;
249
+ d: baz;
250
+ d: bang; }
251
+ CSS
252
+ a {
253
+ @each $number in 1px 2px 3px 4px {
254
+ b: $number;
255
+ }
256
+ }
257
+ c {
258
+ @each $str in foo, bar, baz, bang {
259
+ d: $str;
260
+ }
261
+ }
262
+ SCSS
263
+ end
264
+
238
265
  def test_css_import_directive
239
266
  assert_equal "@import url(foo.css);\n", render('@import "foo.css";')
240
267
  assert_equal "@import url(foo.css);\n", render("@import 'foo.css';")
@@ -9,7 +9,7 @@ $esc: 10\+12
9
9
  $str: Hello\!
10
10
  $qstr: "Quo\"ted\"!"
11
11
  $hstr: Hyph-en\!
12
- $concat: #{5 + 4} hi there
12
+ $space: #{5 + 4} hi there
13
13
  $percent: 11%
14
14
  $complex: 1px/1em
15
15
 
@@ -22,7 +22,7 @@ $complex: 1px/1em
22
22
  :color $main_text
23
23
  :short-color #123
24
24
  :named-color olive
25
- :con "foo" bar ($concat "boom")
25
+ :con "foo" bar ($space "boom")
26
26
  :con2 "noquo" quo
27
27
  #sidebar
28
28
  :background-color $color
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sass
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0.alpha.23
4
+ version: 3.1.0.alpha.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Weizenbaum
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2010-10-31 00:00:00 -04:00
14
+ date: 2010-11-10 00:00:00 -05:00
15
15
  default_executable:
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
@@ -87,6 +87,7 @@ files:
87
87
  - lib/sass/script/string_interpolation.rb
88
88
  - lib/sass/script/unary_operation.rb
89
89
  - lib/sass/script/variable.rb
90
+ - lib/sass/script/list.rb
90
91
  - lib/sass/scss.rb
91
92
  - lib/sass/scss/css_parser.rb
92
93
  - lib/sass/scss/parser.rb
@@ -102,6 +103,7 @@ files:
102
103
  - lib/sass/selector/simple.rb
103
104
  - lib/sass/selector/simple_sequence.rb
104
105
  - lib/sass/shared.rb
106
+ - lib/sass/tree/charset_node.rb
105
107
  - lib/sass/tree/comment_node.rb
106
108
  - lib/sass/tree/debug_node.rb
107
109
  - lib/sass/tree/directive_node.rb
@@ -118,7 +120,7 @@ files:
118
120
  - lib/sass/tree/variable_node.rb
119
121
  - lib/sass/tree/warn_node.rb
120
122
  - lib/sass/tree/while_node.rb
121
- - lib/sass/tree/charset_node.rb
123
+ - lib/sass/tree/each_node.rb
122
124
  - lib/sass/util.rb
123
125
  - lib/sass/util/subset_map.rb
124
126
  - lib/sass/version.rb
@@ -180,6 +182,9 @@ files:
180
182
  - test/sass/results/compressed.css
181
183
  - test/sass/results/expanded.css
182
184
  - test/sass/results/import.css
185
+ - test/sass/results/import_charset.css
186
+ - test/sass/results/import_charset_1_8.css
187
+ - test/sass/results/import_charset_ibm866.css
183
188
  - test/sass/results/line_numbers.css
184
189
  - test/sass/results/mixins.css
185
190
  - test/sass/results/multiline.css
@@ -194,15 +199,14 @@ files:
194
199
  - test/sass/results/units.css
195
200
  - test/sass/results/warn.css
196
201
  - test/sass/results/warn_imported.css
197
- - test/sass/results/import_charset.css
198
- - test/sass/results/import_charset_1_8.css
199
- - test/sass/results/import_charset_ibm866.css
200
202
  - test/sass/script_conversion_test.rb
201
203
  - test/sass/script_test.rb
202
204
  - test/sass/scss/css_test.rb
203
205
  - test/sass/scss/rx_test.rb
204
206
  - test/sass/scss/scss_test.rb
205
207
  - test/sass/scss/test_helper.rb
208
+ - test/sass/templates/_imported_charset_ibm866.sass
209
+ - test/sass/templates/_imported_charset_utf8.sass
206
210
  - test/sass/templates/_partial.sass
207
211
  - test/sass/templates/alt.sass
208
212
  - test/sass/templates/basic.sass
@@ -215,6 +219,9 @@ files:
215
219
  - test/sass/templates/compressed.sass
216
220
  - test/sass/templates/expanded.sass
217
221
  - test/sass/templates/import.sass
222
+ - test/sass/templates/import_charset.sass
223
+ - test/sass/templates/import_charset_1_8.sass
224
+ - test/sass/templates/import_charset_ibm866.sass
218
225
  - test/sass/templates/importee.less
219
226
  - test/sass/templates/importee.sass
220
227
  - test/sass/templates/line_numbers.sass
@@ -238,11 +245,6 @@ files:
238
245
  - test/sass/templates/units.sass
239
246
  - test/sass/templates/warn.sass
240
247
  - test/sass/templates/warn_imported.sass
241
- - test/sass/templates/_imported_charset_ibm866.sass
242
- - test/sass/templates/_imported_charset_utf8.sass
243
- - test/sass/templates/import_charset.sass
244
- - test/sass/templates/import_charset_1_8.sass
245
- - test/sass/templates/import_charset_ibm866.sass
246
248
  - test/sass/test_helper.rb
247
249
  - test/sass/util/subset_map_test.rb
248
250
  - test/sass/util_test.rb