sass 3.3.0 → 3.4.25

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.
Files changed (208) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -1
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/CONTRIBUTING.md +148 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +76 -62
  7. data/Rakefile +104 -24
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/VERSION_NAME +1 -1
  11. data/bin/sass +1 -1
  12. data/bin/scss +1 -1
  13. data/extra/sass-spec-ref.sh +32 -0
  14. data/extra/update_watch.rb +1 -1
  15. data/lib/sass/cache_stores/filesystem.rb +9 -5
  16. data/lib/sass/cache_stores/memory.rb +4 -5
  17. data/lib/sass/callbacks.rb +2 -2
  18. data/lib/sass/css.rb +12 -13
  19. data/lib/sass/deprecation.rb +55 -0
  20. data/lib/sass/engine.rb +106 -70
  21. data/lib/sass/environment.rb +39 -19
  22. data/lib/sass/error.rb +17 -20
  23. data/lib/sass/exec/base.rb +199 -0
  24. data/lib/sass/exec/sass_convert.rb +283 -0
  25. data/lib/sass/exec/sass_scss.rb +440 -0
  26. data/lib/sass/exec.rb +5 -771
  27. data/lib/sass/features.rb +9 -2
  28. data/lib/sass/importers/base.rb +8 -3
  29. data/lib/sass/importers/filesystem.rb +30 -38
  30. data/lib/sass/logger/base.rb +8 -2
  31. data/lib/sass/logger/delayed.rb +50 -0
  32. data/lib/sass/logger.rb +8 -3
  33. data/lib/sass/media.rb +1 -4
  34. data/lib/sass/plugin/compiler.rb +224 -90
  35. data/lib/sass/plugin/configuration.rb +38 -22
  36. data/lib/sass/plugin/merb.rb +2 -2
  37. data/lib/sass/plugin/rack.rb +3 -3
  38. data/lib/sass/plugin/rails.rb +1 -1
  39. data/lib/sass/plugin/staleness_checker.rb +4 -4
  40. data/lib/sass/plugin.rb +6 -5
  41. data/lib/sass/script/css_lexer.rb +1 -1
  42. data/lib/sass/script/css_parser.rb +2 -3
  43. data/lib/sass/script/css_variable_warning.rb +52 -0
  44. data/lib/sass/script/functions.rb +739 -318
  45. data/lib/sass/script/lexer.rb +134 -54
  46. data/lib/sass/script/parser.rb +252 -56
  47. data/lib/sass/script/tree/funcall.rb +13 -6
  48. data/lib/sass/script/tree/interpolation.rb +127 -4
  49. data/lib/sass/script/tree/list_literal.rb +31 -4
  50. data/lib/sass/script/tree/literal.rb +4 -0
  51. data/lib/sass/script/tree/node.rb +21 -3
  52. data/lib/sass/script/tree/operation.rb +54 -1
  53. data/lib/sass/script/tree/selector.rb +26 -0
  54. data/lib/sass/script/tree/string_interpolation.rb +59 -38
  55. data/lib/sass/script/tree/variable.rb +1 -1
  56. data/lib/sass/script/tree.rb +1 -0
  57. data/lib/sass/script/value/base.rb +17 -14
  58. data/lib/sass/script/value/bool.rb +0 -5
  59. data/lib/sass/script/value/color.rb +78 -42
  60. data/lib/sass/script/value/helpers.rb +119 -2
  61. data/lib/sass/script/value/list.rb +0 -15
  62. data/lib/sass/script/value/map.rb +1 -1
  63. data/lib/sass/script/value/null.rb +0 -5
  64. data/lib/sass/script/value/number.rb +112 -31
  65. data/lib/sass/script/value/string.rb +102 -13
  66. data/lib/sass/script/value.rb +0 -1
  67. data/lib/sass/script.rb +3 -3
  68. data/lib/sass/scss/css_parser.rb +24 -4
  69. data/lib/sass/scss/parser.rb +290 -383
  70. data/lib/sass/scss/rx.rb +17 -9
  71. data/lib/sass/scss/static_parser.rb +306 -4
  72. data/lib/sass/scss.rb +0 -2
  73. data/lib/sass/selector/abstract_sequence.rb +35 -18
  74. data/lib/sass/selector/comma_sequence.rb +114 -19
  75. data/lib/sass/selector/pseudo.rb +266 -0
  76. data/lib/sass/selector/sequence.rb +146 -40
  77. data/lib/sass/selector/simple.rb +22 -33
  78. data/lib/sass/selector/simple_sequence.rb +122 -39
  79. data/lib/sass/selector.rb +57 -197
  80. data/lib/sass/shared.rb +2 -2
  81. data/lib/sass/source/map.rb +31 -14
  82. data/lib/sass/source/position.rb +4 -4
  83. data/lib/sass/stack.rb +2 -8
  84. data/lib/sass/supports.rb +10 -13
  85. data/lib/sass/tree/at_root_node.rb +1 -0
  86. data/lib/sass/tree/charset_node.rb +1 -1
  87. data/lib/sass/tree/comment_node.rb +1 -1
  88. data/lib/sass/tree/css_import_node.rb +9 -1
  89. data/lib/sass/tree/directive_node.rb +8 -2
  90. data/lib/sass/tree/error_node.rb +18 -0
  91. data/lib/sass/tree/extend_node.rb +1 -1
  92. data/lib/sass/tree/function_node.rb +9 -0
  93. data/lib/sass/tree/import_node.rb +6 -5
  94. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  95. data/lib/sass/tree/node.rb +5 -3
  96. data/lib/sass/tree/prop_node.rb +6 -7
  97. data/lib/sass/tree/rule_node.rb +26 -11
  98. data/lib/sass/tree/visitors/check_nesting.rb +56 -32
  99. data/lib/sass/tree/visitors/convert.rb +59 -44
  100. data/lib/sass/tree/visitors/cssize.rb +34 -30
  101. data/lib/sass/tree/visitors/deep_copy.rb +6 -1
  102. data/lib/sass/tree/visitors/extend.rb +15 -13
  103. data/lib/sass/tree/visitors/perform.rb +87 -50
  104. data/lib/sass/tree/visitors/set_options.rb +15 -1
  105. data/lib/sass/tree/visitors/to_css.rb +72 -43
  106. data/lib/sass/util/multibyte_string_scanner.rb +0 -2
  107. data/lib/sass/util/normalized_map.rb +0 -1
  108. data/lib/sass/util/subset_map.rb +2 -3
  109. data/lib/sass/util.rb +334 -154
  110. data/lib/sass/version.rb +7 -7
  111. data/lib/sass.rb +10 -8
  112. data/test/sass/cache_test.rb +62 -20
  113. data/test/sass/callbacks_test.rb +1 -1
  114. data/test/sass/compiler_test.rb +24 -11
  115. data/test/sass/conversion_test.rb +241 -50
  116. data/test/sass/css2sass_test.rb +73 -5
  117. data/test/sass/css_variable_test.rb +132 -0
  118. data/test/sass/encoding_test.rb +219 -0
  119. data/test/sass/engine_test.rb +343 -260
  120. data/test/sass/exec_test.rb +12 -2
  121. data/test/sass/extend_test.rb +333 -44
  122. data/test/sass/functions_test.rb +353 -260
  123. data/test/sass/importer_test.rb +40 -21
  124. data/test/sass/logger_test.rb +1 -1
  125. data/test/sass/more_results/more_import.css +1 -1
  126. data/test/sass/more_templates/more1.sass +10 -10
  127. data/test/sass/more_templates/more_import.sass +2 -2
  128. data/test/sass/plugin_test.rb +24 -21
  129. data/test/sass/results/compact.css +1 -1
  130. data/test/sass/results/complex.css +4 -4
  131. data/test/sass/results/expanded.css +1 -1
  132. data/test/sass/results/import.css +1 -1
  133. data/test/sass/results/import_charset_ibm866.css +2 -2
  134. data/test/sass/results/mixins.css +17 -17
  135. data/test/sass/results/nested.css +1 -1
  136. data/test/sass/results/parent_ref.css +2 -2
  137. data/test/sass/results/script.css +5 -5
  138. data/test/sass/results/scss_import.css +1 -1
  139. data/test/sass/script_conversion_test.rb +71 -39
  140. data/test/sass/script_test.rb +714 -123
  141. data/test/sass/scss/css_test.rb +213 -30
  142. data/test/sass/scss/rx_test.rb +8 -4
  143. data/test/sass/scss/scss_test.rb +766 -22
  144. data/test/sass/source_map_test.rb +263 -95
  145. data/test/sass/superselector_test.rb +210 -0
  146. data/test/sass/templates/_partial.sass +1 -1
  147. data/test/sass/templates/basic.sass +10 -10
  148. data/test/sass/templates/bork1.sass +1 -1
  149. data/test/sass/templates/bork5.sass +1 -1
  150. data/test/sass/templates/compact.sass +10 -10
  151. data/test/sass/templates/complex.sass +187 -187
  152. data/test/sass/templates/compressed.sass +10 -10
  153. data/test/sass/templates/expanded.sass +10 -10
  154. data/test/sass/templates/import.sass +2 -2
  155. data/test/sass/templates/importee.sass +3 -3
  156. data/test/sass/templates/mixins.sass +22 -22
  157. data/test/sass/templates/multiline.sass +4 -4
  158. data/test/sass/templates/nested.sass +13 -13
  159. data/test/sass/templates/parent_ref.sass +12 -12
  160. data/test/sass/templates/script.sass +70 -70
  161. data/test/sass/templates/scss_import.scss +2 -1
  162. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
  163. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
  164. data/test/sass/templates/subdir/subdir.sass +3 -3
  165. data/test/sass/templates/units.sass +10 -10
  166. data/test/sass/test_helper.rb +1 -1
  167. data/test/sass/util/multibyte_string_scanner_test.rb +11 -3
  168. data/test/sass/util/normalized_map_test.rb +1 -1
  169. data/test/sass/util/subset_map_test.rb +2 -2
  170. data/test/sass/util_test.rb +46 -45
  171. data/test/sass/value_helpers_test.rb +5 -7
  172. data/test/sass-spec.yml +3 -0
  173. data/test/test_helper.rb +7 -6
  174. data/vendor/listen/CHANGELOG.md +1 -228
  175. data/vendor/listen/Gemfile +5 -15
  176. data/vendor/listen/README.md +111 -77
  177. data/vendor/listen/Rakefile +0 -42
  178. data/vendor/listen/lib/listen/adapter.rb +195 -82
  179. data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
  180. data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
  181. data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
  182. data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
  183. data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
  184. data/vendor/listen/lib/listen/directory_record.rb +96 -61
  185. data/vendor/listen/lib/listen/listener.rb +135 -37
  186. data/vendor/listen/lib/listen/turnstile.rb +9 -5
  187. data/vendor/listen/lib/listen/version.rb +1 -1
  188. data/vendor/listen/lib/listen.rb +33 -19
  189. data/vendor/listen/listen.gemspec +6 -0
  190. data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
  191. data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
  192. data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
  193. data/vendor/listen/spec/listen/listener_spec.rb +128 -39
  194. data/vendor/listen/spec/listen_spec.rb +15 -21
  195. data/vendor/listen/spec/spec_helper.rb +4 -0
  196. data/vendor/listen/spec/support/adapter_helper.rb +52 -15
  197. data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
  198. data/vendor/listen/spec/support/listeners_helper.rb +30 -7
  199. metadata +310 -300
  200. data/CONTRIBUTING +0 -3
  201. data/ext/mkrf_conf.rb +0 -27
  202. data/lib/sass/script/value/deprecated_false.rb +0 -55
  203. data/lib/sass/scss/script_lexer.rb +0 -15
  204. data/lib/sass/scss/script_parser.rb +0 -25
  205. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  206. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  207. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  208. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
@@ -23,6 +23,21 @@ module Sass::Script::Tree
23
23
  # SassScript.
24
24
  attr_reader :originally_text
25
25
 
26
+ # @return [Boolean] Whether a color value passed to the interpolation should
27
+ # generate a warning.
28
+ attr_reader :warn_for_color
29
+
30
+ # The type of interpolation deprecation for this node.
31
+ #
32
+ # This can be `:none`, indicating that the node doesn't use deprecated
33
+ # interpolation; `:immediate`, indicating that a deprecation warning should
34
+ # be emitted as soon as possible; or `:potential`, indicating that a
35
+ # deprecation warning should be emitted if the resulting string is used in a
36
+ # way that would distinguish it from a list.
37
+ #
38
+ # @return [Symbol]
39
+ attr_reader :deprecation
40
+
26
41
  # Interpolation in a property is of the form `before #{mid} after`.
27
42
  #
28
43
  # @param before [Node] See {Interpolation#before}
@@ -31,16 +46,19 @@ module Sass::Script::Tree
31
46
  # @param wb [Boolean] See {Interpolation#whitespace_before}
32
47
  # @param wa [Boolean] See {Interpolation#whitespace_after}
33
48
  # @param originally_text [Boolean] See {Interpolation#originally_text}
49
+ # @param warn_for_color [Boolean] See {Interpolation#warn_for_color}
34
50
  # @comment
35
51
  # rubocop:disable ParameterLists
36
- def initialize(before, mid, after, wb, wa, originally_text = false)
52
+ def initialize(before, mid, after, wb, wa, opts = {})
37
53
  # rubocop:enable ParameterLists
38
54
  @before = before
39
55
  @mid = mid
40
56
  @after = after
41
57
  @whitespace_before = wb
42
58
  @whitespace_after = wa
43
- @originally_text = originally_text
59
+ @originally_text = opts[:originally_text] || false
60
+ @warn_for_color = opts[:warn_for_color] || false
61
+ @deprecation = opts[:deprecation] || :none
44
62
  end
45
63
 
46
64
  # @return [String] A human-readable s-expression representation of the interpolation
@@ -50,6 +68,8 @@ module Sass::Script::Tree
50
68
 
51
69
  # @see Node#to_sass
52
70
  def to_sass(opts = {})
71
+ return to_quoted_equivalent.to_sass if deprecation == :immediate
72
+
53
73
  res = ""
54
74
  res << @before.to_sass(opts) if @before
55
75
  res << ' ' if @before && @whitespace_before
@@ -61,6 +81,19 @@ module Sass::Script::Tree
61
81
  res
62
82
  end
63
83
 
84
+ # Returns an `unquote()` expression that will evaluate to the same value as
85
+ # this interpolation.
86
+ #
87
+ # @return [Sass::Script::Tree::Node]
88
+ def to_quoted_equivalent
89
+ Funcall.new(
90
+ "unquote",
91
+ [to_string_interpolation(self)],
92
+ Sass::Util::NormalizedMap.new,
93
+ nil,
94
+ nil)
95
+ end
96
+
64
97
  # Returns the three components of the interpolation, `before`, `mid`, and `after`.
65
98
  #
66
99
  # @return [Array<Node>]
@@ -81,6 +114,49 @@ module Sass::Script::Tree
81
114
 
82
115
  protected
83
116
 
117
+ # Converts a script node into a corresponding string interpolation
118
+ # expression.
119
+ #
120
+ # @param node_or_interp [Sass::Script::Tree::Node]
121
+ # @return [Sass::Script::Tree::StringInterpolation]
122
+ def to_string_interpolation(node_or_interp)
123
+ unless node_or_interp.is_a?(Interpolation)
124
+ node = node_or_interp
125
+ return string_literal(node.value.to_s) if node.is_a?(Literal)
126
+ if node.is_a?(StringInterpolation)
127
+ return concat(string_literal(node.quote), concat(node, string_literal(node.quote)))
128
+ end
129
+ return StringInterpolation.new(string_literal(""), node, string_literal(""))
130
+ end
131
+
132
+ interp = node_or_interp
133
+ after_string_or_interp =
134
+ if interp.after
135
+ to_string_interpolation(interp.after)
136
+ else
137
+ string_literal("")
138
+ end
139
+ if interp.after && interp.whitespace_after
140
+ after_string_or_interp = concat(string_literal(' '), after_string_or_interp)
141
+ end
142
+
143
+ mid_string_or_interp = to_string_interpolation(interp.mid)
144
+
145
+ before_string_or_interp =
146
+ if interp.before
147
+ to_string_interpolation(interp.before)
148
+ else
149
+ string_literal("")
150
+ end
151
+ if interp.before && interp.whitespace_before
152
+ before_string_or_interp = concat(before_string_or_interp, string_literal(' '))
153
+ end
154
+
155
+ concat(before_string_or_interp, concat(mid_string_or_interp, after_string_or_interp))
156
+ end
157
+
158
+ private
159
+
84
160
  # Evaluates the interpolation.
85
161
  #
86
162
  # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
@@ -90,11 +166,58 @@ module Sass::Script::Tree
90
166
  res = ""
91
167
  res << @before.perform(environment).to_s if @before
92
168
  res << " " if @before && @whitespace_before
169
+
93
170
  val = @mid.perform(environment)
94
- res << (val.is_a?(Sass::Script::Value::String) ? val.value : val.to_s)
171
+ if @warn_for_color && val.is_a?(Sass::Script::Value::Color) && val.name
172
+ alternative = Operation.new(Sass::Script::Value::String.new("", :string), @mid, :plus)
173
+ Sass::Util.sass_warn <<MESSAGE
174
+ WARNING on line #{line}, column #{source_range.start_pos.offset}#{" of #{filename}" if filename}:
175
+ You probably don't mean to use the color value `#{val}' in interpolation here.
176
+ It may end up represented as #{val.inspect}, which will likely produce invalid CSS.
177
+ Always quote color names when using them as strings (for example, "#{val}").
178
+ If you really want to use the color value here, use `#{alternative.to_sass}'.
179
+ MESSAGE
180
+ end
181
+
182
+ res << val.to_s(:quote => :none)
95
183
  res << " " if @after && @whitespace_after
96
184
  res << @after.perform(environment).to_s if @after
97
- opts(Sass::Script::Value::String.new(res))
185
+ str = Sass::Script::Value::String.new(
186
+ res, :identifier,
187
+ (to_quoted_equivalent.to_sass if deprecation == :potential))
188
+ str.source_range = source_range
189
+ opts(str)
190
+ end
191
+
192
+ # Concatenates two string literals or string interpolation expressions.
193
+ #
194
+ # @param string_or_interp1 [Sass::Script::Tree::Literal|Sass::Script::Tree::StringInterpolation]
195
+ # @param string_or_interp2 [Sass::Script::Tree::Literal|Sass::Script::Tree::StringInterpolation]
196
+ # @return [Sass::Script::Tree::StringInterpolation]
197
+ def concat(string_or_interp1, string_or_interp2)
198
+ if string_or_interp1.is_a?(Literal) && string_or_interp2.is_a?(Literal)
199
+ return string_literal(string_or_interp1.value.value + string_or_interp2.value.value)
200
+ end
201
+
202
+ if string_or_interp1.is_a?(Literal)
203
+ string = string_or_interp1
204
+ interp = string_or_interp2
205
+ before = string_literal(string.value.value + interp.before.value.value)
206
+ return StringInterpolation.new(before, interp.mid, interp.after)
207
+ end
208
+
209
+ StringInterpolation.new(
210
+ string_or_interp1.before,
211
+ string_or_interp1.mid,
212
+ concat(string_or_interp1.after, string_or_interp2))
213
+ end
214
+
215
+ # Returns a string literal with the given contents.
216
+ #
217
+ # @param string [String]
218
+ # @return string [Sass::Script::Tree::Literal]
219
+ def string_literal(string)
220
+ Literal.new(Sass::Script::Value::String.new(string, :string))
98
221
  end
99
222
  end
100
223
  end
@@ -28,11 +28,8 @@ module Sass::Script::Tree
28
28
  # @see Value#to_sass
29
29
  def to_sass(opts = {})
30
30
  return "()" if elements.empty?
31
- precedence = Sass::Script::Parser.precedence_of(separator)
32
31
  members = elements.map do |v|
33
- if v.is_a?(ListLiteral) && Sass::Script::Parser.precedence_of(v.separator) <= precedence ||
34
- separator == :space && v.is_a?(UnaryOperation) &&
35
- (v.operator == :minus || v.operator == :plus)
32
+ if element_needs_parens?(v)
36
33
  "(#{v.to_sass(opts)})"
37
34
  else
38
35
  v.to_sass(opts)
@@ -55,6 +52,10 @@ module Sass::Script::Tree
55
52
  "(#{elements.map {|e| e.inspect}.join(separator == :space ? ' ' : ', ')})"
56
53
  end
57
54
 
55
+ def force_division!
56
+ # Do nothing. Lists prevent division propagation.
57
+ end
58
+
58
59
  protected
59
60
 
60
61
  def _perform(environment)
@@ -68,6 +69,32 @@ module Sass::Script::Tree
68
69
 
69
70
  private
70
71
 
72
+ # Returns whether an element in the list should be wrapped in parentheses
73
+ # when serialized to Sass.
74
+ def element_needs_parens?(element)
75
+ if element.is_a?(ListLiteral)
76
+ return Sass::Script::Parser.precedence_of(element.separator) <=
77
+ Sass::Script::Parser.precedence_of(separator)
78
+ end
79
+
80
+ return false unless separator == :space
81
+
82
+ if element.is_a?(UnaryOperation)
83
+ return element.operator == :minus || element.operator == :plus
84
+ end
85
+
86
+ return false unless element.is_a?(Operation)
87
+ return true unless element.operator == :div
88
+ !(is_literal_number?(element.operand1) && is_literal_number?(element.operand2))
89
+ end
90
+
91
+ # Returns whether a value is a number literal that shouldn't be divided.
92
+ def is_literal_number?(value)
93
+ value.is_a?(Literal) &&
94
+ value.value.is_a?((Sass::Script::Value::Number)) &&
95
+ !value.value.original.nil?
96
+ end
97
+
71
98
  def sep_str(opts = options)
72
99
  return ' ' if separator == :space
73
100
  return ',' if opts && opts[:style] == :compressed
@@ -35,6 +35,10 @@ module Sass::Script::Tree
35
35
  value.inspect
36
36
  end
37
37
 
38
+ def force_division!
39
+ value.original = nil if value.is_a?(Sass::Script::Value::Number)
40
+ end
41
+
38
42
  protected
39
43
 
40
44
  def _perform(environment)
@@ -10,7 +10,7 @@ module Sass::Script::Tree
10
10
 
11
11
  # The line of the document on which this node appeared.
12
12
  #
13
- # @return [Fixnum]
13
+ # @return [Integer]
14
14
  attr_accessor :line
15
15
 
16
16
  # The source range in the document on which this node appeared.
@@ -23,9 +23,17 @@ module Sass::Script::Tree
23
23
  # @return [String]
24
24
  attr_accessor :filename
25
25
 
26
+ # The warning that this node should emit if it executes in a way that's not
27
+ # safe for a CSS variable value.
28
+ #
29
+ # This is `nil` if this is not in a CSS variable value.
30
+ #
31
+ # @return [Sass::Script::CssVariableWarning]
32
+ attr_accessor :css_variable_warning
33
+
26
34
  # Sets the options hash for this node,
27
35
  # as well as for all child nodes.
28
- # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
36
+ # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
29
37
  #
30
38
  # @param options [{Symbol => Object}] The options
31
39
  def options=(options)
@@ -62,6 +70,10 @@ module Sass::Script::Tree
62
70
 
63
71
  # Returns the text of this SassScript expression.
64
72
  #
73
+ # @options opts :quote [String]
74
+ # The preferred quote style for quoted strings. If `:none`, strings are
75
+ # always emitted unquoted.
76
+ #
65
77
  # @return [String]
66
78
  def to_sass(opts = {})
67
79
  Sass::Util.abstract(self)
@@ -75,12 +87,18 @@ module Sass::Script::Tree
75
87
  Sass::Util.abstract(self)
76
88
  end
77
89
 
90
+ # Forces any division operations with number literals in this expression to
91
+ # do real division, rather than returning strings.
92
+ def force_division!
93
+ children.each {|c| c.force_division!}
94
+ end
95
+
78
96
  protected
79
97
 
80
98
  # Converts underscores to dashes if the :dasherize option is set.
81
99
  def dasherize(s, opts)
82
100
  if opts[:dasherize]
83
- s.gsub(/_/, '-')
101
+ s.tr('_', '-')
84
102
  else
85
103
  s
86
104
  end
@@ -2,6 +2,9 @@ module Sass::Script::Tree
2
2
  # A SassScript parse node representing a binary operation,
3
3
  # such as `$a + $b` or `"foo" + 1`.
4
4
  class Operation < Node
5
+ @@color_arithmetic_deprecation = Sass::Deprecation.new
6
+ @@unitless_equals_deprecation = Sass::Deprecation.new
7
+
5
8
  attr_reader :operand1
6
9
  attr_reader :operand2
7
10
  attr_reader :operator
@@ -78,16 +81,66 @@ module Sass::Script::Tree
78
81
  "Invalid null operation: \"#{value1.inspect} #{@operator} #{value2.inspect}\".")
79
82
  end
80
83
 
84
+ if css_variable_warning && @operator == :div &&
85
+ !(value1.is_a?(Sass::Script::Value::Number) && value1.original &&
86
+ value2.is_a?(Sass::Script::Value::Number) && value2.original) &&
87
+ !(value1.is_a?(Sass::Script::Value::String) && value2.is_a?(Sass::Script::Value::String))
88
+ css_variable_warning.warn!
89
+ end
90
+
81
91
  begin
82
- opts(value1.send(@operator, value2))
92
+ result = opts(value1.send(@operator, value2))
83
93
  rescue NoMethodError => e
84
94
  raise e unless e.name.to_s == @operator.to_s
85
95
  raise Sass::SyntaxError.new("Undefined operation: \"#{value1} #{@operator} #{value2}\".")
86
96
  end
97
+
98
+ warn_for_color_arithmetic(value1, value2)
99
+ warn_for_unitless_equals(value1, value2, result)
100
+
101
+ result
87
102
  end
88
103
 
89
104
  private
90
105
 
106
+ def warn_for_color_arithmetic(value1, value2)
107
+ return unless @operator == :plus || @operator == :times || @operator == :minus ||
108
+ @operator == :div || @operator == :mod
109
+
110
+ if value1.is_a?(Sass::Script::Value::Number)
111
+ return unless value2.is_a?(Sass::Script::Value::Color)
112
+ elsif value1.is_a?(Sass::Script::Value::Color)
113
+ return unless value2.is_a?(Sass::Script::Value::Color) || value2.is_a?(Sass::Script::Value::Number)
114
+ else
115
+ return
116
+ end
117
+
118
+ @@color_arithmetic_deprecation.warn(filename, line, <<WARNING)
119
+ The operation `#{value1} #{@operator} #{value2}` is deprecated and will be an error in future versions.
120
+ Consider using Sass's color functions instead.
121
+ http://sass-lang.com/documentation/Sass/Script/Functions.html#other_color_functions
122
+ WARNING
123
+ end
124
+
125
+ def warn_for_unitless_equals(value1, value2, result)
126
+ return unless @operator == :eq || @operator == :neq
127
+ return unless value1.is_a?(Sass::Script::Value::Number)
128
+ return unless value2.is_a?(Sass::Script::Value::Number)
129
+ return unless value1.unitless? != value2.unitless?
130
+ return unless result == (if @operator == :eq
131
+ Sass::Script::Value::Bool::TRUE
132
+ else
133
+ Sass::Script::Value::Bool::FALSE
134
+ end)
135
+
136
+ operation = "#{value1.to_sass} #{@operator == :eq ? '==' : '!='} #{value2.to_sass}"
137
+ future_value = @operator == :neq
138
+ @@unitless_equals_deprecation.warn(filename, line, <<WARNING)
139
+ The result of `#{operation}` will be `#{future_value}` in future releases of Sass.
140
+ Unitless numbers will no longer be equal to the same numbers with units.
141
+ WARNING
142
+ end
143
+
91
144
  def operand_to_sass(op, side, opts)
92
145
  return "(#{op.to_sass(opts)})" if op.is_a?(Sass::Script::Tree::ListLiteral)
93
146
  return op.to_sass(opts) unless op.is_a?(Operation)
@@ -0,0 +1,26 @@
1
+ module Sass::Script::Tree
2
+ # A SassScript node that will resolve to the current selector.
3
+ class Selector < Node
4
+ def initialize; end
5
+
6
+ def children
7
+ []
8
+ end
9
+
10
+ def to_sass(opts = {})
11
+ '&'
12
+ end
13
+
14
+ def deep_copy
15
+ dup
16
+ end
17
+
18
+ protected
19
+
20
+ def _perform(environment)
21
+ selector = environment.selector
22
+ return opts(Sass::Script::Value::Null.new) unless selector
23
+ opts(selector.to_sass_script)
24
+ end
25
+ end
26
+ end
@@ -3,12 +3,39 @@ module Sass::Script::Tree
3
3
  #
4
4
  # @see Interpolation
5
5
  class StringInterpolation < Node
6
+ # @return [Literal] The string literal before this interpolation.
7
+ attr_reader :before
8
+
9
+ # @return [Node] The SassScript within the interpolation
10
+ attr_reader :mid
11
+
12
+ # @return [StringInterpolation, Literal]
13
+ # The string literal or string interpolation before this interpolation.
14
+ attr_reader :after
15
+
16
+ # Whether this is a CSS string or a CSS identifier. The difference is that
17
+ # strings are written with double-quotes, while identifiers aren't.
18
+ #
19
+ # String interpolations are only ever identifiers if they're quote-like
20
+ # functions such as `url()`.
21
+ #
22
+ # @return [Symbol] `:string` or `:identifier`
23
+ def type
24
+ @before.value.type
25
+ end
26
+
27
+ # Returns the quote character that should be used to wrap a Sass
28
+ # representation of this interpolation.
29
+ def quote
30
+ quote_for(self) || '"'
31
+ end
32
+
6
33
  # Interpolation in a string is of the form `"before #{mid} after"`,
7
34
  # where `before` and `after` may include more interpolation.
8
35
  #
9
- # @param before [Node] The string before the interpolation
10
- # @param mid [Node] The SassScript within the interpolation
11
- # @param after [Node] The string after the interpolation
36
+ # @param before [StringInterpolation, Literal] See {StringInterpolation#before}
37
+ # @param mid [Node] See {StringInterpolation#mid}
38
+ # @param after [Literal] See {StringInterpolation#after}
12
39
  def initialize(before, mid, after)
13
40
  @before = before
14
41
  @mid = mid
@@ -22,32 +49,15 @@ module Sass::Script::Tree
22
49
 
23
50
  # @see Node#to_sass
24
51
  def to_sass(opts = {})
25
- # We can get rid of all of this when we remove the deprecated :equals context
26
- # XXX CE: It's gone now but I'm not sure what can be removed now.
27
- before_unquote, before_quote_char, before_str = parse_str(@before.to_sass(opts))
28
- after_unquote, after_quote_char, after_str = parse_str(@after.to_sass(opts))
29
- unquote = before_unquote || after_unquote ||
30
- (before_quote_char && !after_quote_char && !after_str.empty?) ||
31
- (!before_quote_char && after_quote_char && !before_str.empty?)
32
- quote_char =
33
- if before_quote_char && after_quote_char && before_quote_char != after_quote_char
34
- before_str.gsub!("\\'", "'")
35
- before_str.gsub!('"', "\\\"")
36
- after_str.gsub!("\\'", "'")
37
- after_str.gsub!('"', "\\\"")
38
- '"'
39
- else
40
- before_quote_char || after_quote_char
41
- end
52
+ quote = type == :string ? opts[:quote] || quote_for(self) || '"' : :none
53
+ opts = opts.merge(:quote => quote)
42
54
 
43
55
  res = ""
44
- res << 'unquote(' if unquote
45
- res << quote_char if quote_char
46
- res << before_str
47
- res << '#{' << @mid.to_sass(opts) << '}'
48
- res << after_str
49
- res << quote_char if quote_char
50
- res << ')' if unquote
56
+ res << quote if quote != :none
57
+ res << _to_sass(before, opts)
58
+ res << '#{' << @mid.to_sass(opts.merge(:quote => nil)) << '}'
59
+ res << _to_sass(after, opts)
60
+ res << quote if quote != :none
51
61
  res
52
62
  end
53
63
 
@@ -81,24 +91,35 @@ module Sass::Script::Tree
81
91
  before = @before.perform(environment)
82
92
  res << before.value
83
93
  mid = @mid.perform(environment)
84
- res << (mid.is_a?(Sass::Script::Value::String) ? mid.value : mid.to_s)
94
+ res << (mid.is_a?(Sass::Script::Value::String) ? mid.value : mid.to_s(:quote => :none))
85
95
  res << @after.perform(environment).value
86
96
  opts(Sass::Script::Value::String.new(res, before.type))
87
97
  end
88
98
 
89
99
  private
90
100
 
91
- def parse_str(str)
92
- case str
93
- when /^unquote\((["'])(.*)\1\)$/
94
- return true, $1, $2
95
- when '""'
96
- return false, nil, ""
97
- when /^(["'])(.*)\1$/
98
- return false, $1, $2
99
- else
100
- return false, nil, str
101
+ def _to_sass(string_or_interp, opts)
102
+ result = string_or_interp.to_sass(opts)
103
+ opts[:quote] == :none ? result : result.slice(1...-1)
104
+ end
105
+
106
+ def quote_for(string_or_interp)
107
+ if string_or_interp.is_a?(Sass::Script::Tree::Literal)
108
+ return nil if string_or_interp.value.value.empty?
109
+ return '"' if string_or_interp.value.value.include?("'")
110
+ return "'" if string_or_interp.value.value.include?('"')
111
+ return nil
101
112
  end
113
+
114
+ # Double-quotes take precedence over single quotes.
115
+ before_quote = quote_for(string_or_interp.before)
116
+ return '"' if before_quote == '"'
117
+ after_quote = quote_for(string_or_interp.after)
118
+ return '"' if after_quote == '"'
119
+
120
+ # Returns "'" if either or both insist on single quotes, and nil
121
+ # otherwise.
122
+ before_quote || after_quote
102
123
  end
103
124
  end
104
125
  end
@@ -14,7 +14,7 @@ module Sass::Script::Tree
14
14
  # @param name [String] See \{#name}
15
15
  def initialize(name)
16
16
  @name = name
17
- @underscored_name = name.gsub(/-/, "_")
17
+ @underscored_name = name.tr("-", "_")
18
18
  super()
19
19
  end
20
20
 
@@ -13,3 +13,4 @@ require 'sass/script/tree/string_interpolation'
13
13
  require 'sass/script/tree/literal'
14
14
  require 'sass/script/tree/list_literal'
15
15
  require 'sass/script/tree/map_literal'
16
+ require 'sass/script/tree/selector'
@@ -20,12 +20,14 @@ module Sass::Script::Value
20
20
  #
21
21
  # @param value [Object] The object for \{#value}
22
22
  def initialize(value = nil)
23
- @value = value.freeze
23
+ value.freeze unless value.nil? || value == true || value == false
24
+ @value = value
25
+ @options = nil
24
26
  end
25
27
 
26
28
  # Sets the options hash for this node,
27
29
  # as well as for all child nodes.
28
- # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
30
+ # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
29
31
  #
30
32
  # @param options [{Symbol => Object}] The options
31
33
  attr_writer :options
@@ -86,7 +88,7 @@ MSG
86
88
  # @return [Script::Value::String] A string containing both values
87
89
  # separated by `"="`
88
90
  def single_eq(other)
89
- Sass::Script::Value::String.new("#{to_s}=#{other.to_s}")
91
+ Sass::Script::Value::String.new("#{self}=#{other}")
90
92
  end
91
93
 
92
94
  # The SassScript `+` operation.
@@ -95,10 +97,8 @@ MSG
95
97
  # @return [Script::Value::String] A string containing both values
96
98
  # without any separation
97
99
  def plus(other)
98
- if other.is_a?(Sass::Script::Value::String)
99
- return Sass::Script::Value::String.new(to_s + other.value, other.type)
100
- end
101
- Sass::Script::Value::String.new(to_s + other.to_s)
100
+ type = other.is_a?(Sass::Script::Value::String) ? other.type : :identifier
101
+ Sass::Script::Value::String.new(to_s(:quote => :none) + other.to_s(:quote => :none), type)
102
102
  end
103
103
 
104
104
  # The SassScript `-` operation.
@@ -107,7 +107,7 @@ MSG
107
107
  # @return [Script::Value::String] A string containing both values
108
108
  # separated by `"-"`
109
109
  def minus(other)
110
- Sass::Script::Value::String.new("#{to_s}-#{other.to_s}")
110
+ Sass::Script::Value::String.new("#{self}-#{other}")
111
111
  end
112
112
 
113
113
  # The SassScript `/` operation.
@@ -116,7 +116,7 @@ MSG
116
116
  # @return [Script::Value::String] A string containing both values
117
117
  # separated by `"/"`
118
118
  def div(other)
119
- Sass::Script::Value::String.new("#{to_s}/#{other.to_s}")
119
+ Sass::Script::Value::String.new("#{self}/#{other}")
120
120
  end
121
121
 
122
122
  # The SassScript unary `+` operation (e.g. `+$a`).
@@ -125,7 +125,7 @@ MSG
125
125
  # @return [Script::Value::String] A string containing the value
126
126
  # preceded by `"+"`
127
127
  def unary_plus
128
- Sass::Script::Value::String.new("+#{to_s}")
128
+ Sass::Script::Value::String.new("+#{self}")
129
129
  end
130
130
 
131
131
  # The SassScript unary `-` operation (e.g. `-$a`).
@@ -134,7 +134,7 @@ MSG
134
134
  # @return [Script::Value::String] A string containing the value
135
135
  # preceded by `"-"`
136
136
  def unary_minus
137
- Sass::Script::Value::String.new("-#{to_s}")
137
+ Sass::Script::Value::String.new("-#{self}")
138
138
  end
139
139
 
140
140
  # The SassScript unary `/` operation (e.g. `/$a`).
@@ -143,13 +143,13 @@ MSG
143
143
  # @return [Script::Value::String] A string containing the value
144
144
  # preceded by `"/"`
145
145
  def unary_div
146
- Sass::Script::Value::String.new("/#{to_s}")
146
+ Sass::Script::Value::String.new("/#{self}")
147
147
  end
148
148
 
149
149
  # Returns the hash code of this value. Two objects' hash codes should be
150
150
  # equal if the objects are equal.
151
151
  #
152
- # @return [Fixnum] The hash code.
152
+ # @return [Integer for Ruby 2.4.0+, Fixnum for earlier Ruby versions] The hash code.
153
153
  def hash
154
154
  value.hash
155
155
  end
@@ -176,7 +176,7 @@ MSG
176
176
  eq(other).to_bool
177
177
  end
178
178
 
179
- # @return [Fixnum] The integer value of this value
179
+ # @return [Integer] The integer value of this value
180
180
  # @raise [Sass::SyntaxError] if this value isn't an integer
181
181
  def to_i
182
182
  raise Sass::SyntaxError.new("#{inspect} is not an integer.")
@@ -212,6 +212,9 @@ MSG
212
212
  # Returns the string representation of this value
213
213
  # as it would be output to the CSS document.
214
214
  #
215
+ # @options opts :quote [String]
216
+ # The preferred quote style for quoted strings. If `:none`, strings are
217
+ # always emitted unquoted.
215
218
  # @return [String]
216
219
  def to_s(opts = {})
217
220
  Sass::Util.abstract(self)
@@ -20,11 +20,6 @@ module Sass::Script::Value
20
20
  value ? TRUE : FALSE
21
21
  end
22
22
 
23
- def eq(other)
24
- return other.eq(self) if other.is_a?(DeprecatedFalse)
25
- super
26
- end
27
-
28
23
  # The Ruby value of the boolean.
29
24
  #
30
25
  # @return [Boolean]