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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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