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
data/lib/sass/scss/rx.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  module Sass
2
3
  module SCSS
3
4
  # A module containing regular expressions used
@@ -31,7 +32,7 @@ module Sass
31
32
  # @return [String] The escaped character
32
33
  # @private
33
34
  def self.escape_char(c)
34
- return "\\%06x" % Sass::Util.ord(c) unless c =~ /[ -\/:-~]/
35
+ return "\\%06x" % (Sass::Util.ord(c)) unless c =~ %r{[ -/:-~]}
35
36
  "\\#{c}"
36
37
  end
37
38
 
@@ -39,7 +40,7 @@ module Sass
39
40
  # escaping all significant characters.
40
41
  #
41
42
  # @param str [String] The text of the regexp
42
- # @param flags [Fixnum] Flags for the created regular expression
43
+ # @param flags [Integer] Flags for the created regular expression
43
44
  # @return [Regexp]
44
45
  # @private
45
46
  def self.quote(str, flags = 0)
@@ -63,9 +64,8 @@ module Sass
63
64
  STRING1 = /\"((?:[^\n\r\f\\"]|\\#{NL}|#{ESCAPE})*)\"/
64
65
  STRING2 = /\'((?:[^\n\r\f\\']|\\#{NL}|#{ESCAPE})*)\'/
65
66
 
66
- IDENT = /-?#{NMSTART}#{NMCHAR}*/
67
+ IDENT = /-*#{NMSTART}#{NMCHAR}*/
67
68
  NAME = /#{NMCHAR}+/
68
- NUM = /[0-9]+|[0-9]*\.[0-9]+/
69
69
  STRING = /#{STRING1}|#{STRING2}/
70
70
  URLCHAR = /[#%&*-~]|#{NONASCII}|#{ESCAPE}/
71
71
  URL = /(#{URLCHAR}*)/
@@ -80,7 +80,7 @@ module Sass
80
80
 
81
81
  S = /[ \t\r\n\f]+/
82
82
 
83
- COMMENT = %r{/\*[^*]*\*+(?:[^/][^*]*\*+)*/}
83
+ COMMENT = %r{/\*([^*]|\*+[^/*])*\**\*/}
84
84
  SINGLE_LINE_COMMENT = %r{//.*(\n[ \t]*//.*)*}
85
85
 
86
86
  CDO = quote("<!--")
@@ -95,7 +95,14 @@ module Sass
95
95
 
96
96
  IMPORTANT = /!#{W}important/i
97
97
 
98
- NUMBER = /#{NUM}(?:#{IDENT}|%)?/
98
+ # A unit is like an IDENT, but disallows a hyphen followed by a digit.
99
+ # This allows "1px-2px" to be interpreted as subtraction rather than "1"
100
+ # with the unit "px-2px". It also allows "%".
101
+ UNIT = /-?#{NMSTART}(?:[a-zA-Z0-9_]|#{NONASCII}|#{ESCAPE}|-(?!\.?\d))*|%/
102
+
103
+ UNITLESS_NUMBER = /(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?\d+)?/
104
+ NUMBER = /#{UNITLESS_NUMBER}(?:#{UNIT})?/
105
+ PERCENTAGE = /#{UNITLESS_NUMBER}%/
99
106
 
100
107
  URI = /url\(#{W}(?:#{STRING}|#{URL})#{W}\)/i
101
108
  FUNCTION = /#{IDENT}\(/
@@ -118,14 +125,15 @@ module Sass
118
125
  INTERP_START = /#\{/
119
126
  ANY = /:(-[-\w]+-)?any\(/i
120
127
  OPTIONAL = /!#{W}optional/i
128
+ IDENT_START = /-|#{NMSTART}/
121
129
 
122
130
  IDENT_HYPHEN_INTERP = /-(#\{)/
123
- STRING1_NOINTERP = /\"((?:[^\n\r\f\\"#]|#(?!\{)|\\#{NL}|#{ESCAPE})*)\"/
124
- STRING2_NOINTERP = /\'((?:[^\n\r\f\\'#]|#(?!\{)|\\#{NL}|#{ESCAPE})*)\'/
131
+ STRING1_NOINTERP = /\"((?:[^\n\r\f\\"#]|#(?!\{)|#{ESCAPE})*)\"/
132
+ STRING2_NOINTERP = /\'((?:[^\n\r\f\\'#]|#(?!\{)|#{ESCAPE})*)\'/
125
133
  STRING_NOINTERP = /#{STRING1_NOINTERP}|#{STRING2_NOINTERP}/
126
134
 
127
135
  STATIC_COMPONENT = /#{IDENT}|#{STRING_NOINTERP}|#{HEXCOLOR}|[+-]?#{NUMBER}|\!important/i
128
- STATIC_VALUE = /#{STATIC_COMPONENT}(\s*[\s,\/]\s*#{STATIC_COMPONENT})*([;}])/i
136
+ STATIC_VALUE = %r(#{STATIC_COMPONENT}(\s*[\s,/]\s*#{STATIC_COMPONENT})*([;}]))i
129
137
  STATIC_SELECTOR = /(#{NMCHAR}|[ \t]|[,>+*]|[:#.]#{NMSTART}){1,50}([{])/i
130
138
  end
131
139
  end
@@ -41,10 +41,28 @@ module Sass
41
41
  return type, directives
42
42
  end
43
43
 
44
+ def parse_keyframes_selector
45
+ init_scanner!
46
+ sel = expr!(:keyframes_selector)
47
+ expected("keyframes selector") unless @scanner.eos?
48
+ sel
49
+ end
50
+
51
+ # @see Parser#initialize
52
+ # @param allow_parent_ref [Boolean] Whether to allow the
53
+ # parent-reference selector, `&`, when parsing the document.
54
+ # @comment
55
+ # rubocop:disable ParameterLists
56
+ def initialize(str, filename, importer, line = 1, offset = 1, allow_parent_ref = true)
57
+ # rubocop:enable ParameterLists
58
+ super(str, filename, importer, line, offset)
59
+ @allow_parent_ref = allow_parent_ref
60
+ end
61
+
44
62
  private
45
63
 
46
64
  def moz_document_function
47
- val = tok(URI) || tok(URL_PREFIX) || tok(DOMAIN) || function(!:allow_var)
65
+ val = tok(URI) || tok(URL_PREFIX) || tok(DOMAIN) || function(false)
48
66
  return unless val
49
67
  ss
50
68
  [val]
@@ -52,7 +70,7 @@ module Sass
52
70
 
53
71
  def variable; nil; end
54
72
  def script_value; nil; end
55
- def interpolation; nil; end
73
+ def interpolation(warn_for_color = false); nil; end
56
74
  def var_expr; nil; end
57
75
  def interp_string; (s = tok(STRING)) && [s]; end
58
76
  def interp_uri; (s = tok(URI)) && [s]; end
@@ -60,12 +78,296 @@ module Sass
60
78
  def use_css_import?; true; end
61
79
 
62
80
  def special_directive(name, start_pos)
63
- return unless %w[media import charset -moz-document].include?(name)
81
+ return unless %w(media import charset -moz-document).include?(name)
64
82
  super
65
83
  end
66
84
 
85
+ def selector_comma_sequence
86
+ sel = selector
87
+ return unless sel
88
+ selectors = [sel]
89
+ ws = ''
90
+ while tok(/,/)
91
+ ws << str {ss}
92
+ next unless (sel = selector)
93
+ selectors << sel
94
+ if ws.include?("\n")
95
+ selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members)
96
+ end
97
+ ws = ''
98
+ end
99
+ Selector::CommaSequence.new(selectors)
100
+ end
101
+
102
+ def selector_string
103
+ sel = selector
104
+ return unless sel
105
+ sel.to_s
106
+ end
107
+
108
+ def selector
109
+ start_pos = source_position
110
+ # The combinator here allows the "> E" hack
111
+ val = combinator || simple_selector_sequence
112
+ return unless val
113
+ nl = str {ss}.include?("\n")
114
+ res = []
115
+ res << val
116
+ res << "\n" if nl
117
+
118
+ while (val = combinator || simple_selector_sequence)
119
+ res << val
120
+ res << "\n" if str {ss}.include?("\n")
121
+ end
122
+ seq = Selector::Sequence.new(res.compact)
123
+
124
+ if seq.members.any? {|sseq| sseq.is_a?(Selector::SimpleSequence) && sseq.subject?}
125
+ location = " of #{@filename}" if @filename
126
+ Sass::Util.sass_warn <<MESSAGE
127
+ DEPRECATION WARNING on line #{start_pos.line}, column #{start_pos.offset}#{location}:
128
+ The subject selector operator "!" is deprecated and will be removed in a future release.
129
+ This operator has been replaced by ":has()" in the CSS spec.
130
+ For example: #{seq.subjectless}
131
+ MESSAGE
132
+ end
133
+
134
+ seq
135
+ end
136
+
137
+ def combinator
138
+ tok(PLUS) || tok(GREATER) || tok(TILDE) || reference_combinator
139
+ end
140
+
141
+ def reference_combinator
142
+ return unless tok(%r{/})
143
+ res = '/'
144
+ ns, name = expr!(:qualified_name)
145
+ res << ns << '|' if ns
146
+ res << name << tok!(%r{/})
147
+
148
+ location = " of #{@filename}" if @filename
149
+ Sass::Util.sass_warn <<MESSAGE
150
+ DEPRECATION WARNING on line #{@line}, column #{@offset}#{location}:
151
+ The reference combinator #{res} is deprecated and will be removed in a future release.
152
+ MESSAGE
153
+
154
+ res
155
+ end
156
+
157
+ def simple_selector_sequence
158
+ start_pos = source_position
159
+ e = element_name || id_selector || class_selector || placeholder_selector || attrib ||
160
+ pseudo || parent_selector
161
+ return unless e
162
+ res = [e]
163
+
164
+ # The tok(/\*/) allows the "E*" hack
165
+ while (v = id_selector || class_selector || placeholder_selector ||
166
+ attrib || pseudo || (tok(/\*/) && Selector::Universal.new(nil)))
167
+ res << v
168
+ end
169
+
170
+ pos = @scanner.pos
171
+ line = @line
172
+ if (sel = str? {simple_selector_sequence})
173
+ @scanner.pos = pos
174
+ @line = line
175
+ begin
176
+ # If we see "*E", don't force a throw because this could be the
177
+ # "*prop: val" hack.
178
+ expected('"{"') if res.length == 1 && res[0].is_a?(Selector::Universal)
179
+ throw_error {expected('"{"')}
180
+ rescue Sass::SyntaxError => e
181
+ e.message << "\n\n\"#{sel}\" may only be used at the beginning of a compound selector."
182
+ raise e
183
+ end
184
+ end
185
+
186
+ Selector::SimpleSequence.new(res, tok(/!/), range(start_pos))
187
+ end
188
+
189
+ def parent_selector
190
+ return unless @allow_parent_ref && tok(/&/)
191
+ Selector::Parent.new(tok(NAME))
192
+ end
193
+
194
+ def class_selector
195
+ return unless tok(/\./)
196
+ @expected = "class name"
197
+ Selector::Class.new(tok!(IDENT))
198
+ end
199
+
200
+ def id_selector
201
+ return unless tok(/#(?!\{)/)
202
+ @expected = "id name"
203
+ Selector::Id.new(tok!(NAME))
204
+ end
205
+
206
+ def placeholder_selector
207
+ return unless tok(/%/)
208
+ @expected = "placeholder name"
209
+ Selector::Placeholder.new(tok!(IDENT))
210
+ end
211
+
212
+ def element_name
213
+ ns, name = Sass::Util.destructure(qualified_name(:allow_star_name))
214
+ return unless ns || name
215
+
216
+ if name == '*'
217
+ Selector::Universal.new(ns)
218
+ else
219
+ Selector::Element.new(name, ns)
220
+ end
221
+ end
222
+
223
+ def qualified_name(allow_star_name = false)
224
+ name = tok(IDENT) || tok(/\*/) || (tok?(/\|/) && "")
225
+ return unless name
226
+ return nil, name unless tok(/\|/)
227
+
228
+ return name, tok!(IDENT) unless allow_star_name
229
+ @expected = "identifier or *"
230
+ return name, tok(IDENT) || tok!(/\*/)
231
+ end
232
+
233
+ def attrib
234
+ return unless tok(/\[/)
235
+ ss
236
+ ns, name = attrib_name!
237
+ ss
238
+
239
+ op = tok(/=/) ||
240
+ tok(INCLUDES) ||
241
+ tok(DASHMATCH) ||
242
+ tok(PREFIXMATCH) ||
243
+ tok(SUFFIXMATCH) ||
244
+ tok(SUBSTRINGMATCH)
245
+ if op
246
+ @expected = "identifier or string"
247
+ ss
248
+ val = tok(IDENT) || tok!(STRING)
249
+ ss
250
+ end
251
+ flags = tok(IDENT) || tok(STRING)
252
+ tok!(/\]/)
253
+
254
+ Selector::Attribute.new(name, ns, op, val, flags)
255
+ end
256
+
257
+ def attrib_name!
258
+ if (name_or_ns = tok(IDENT))
259
+ # E, E|E
260
+ if tok(/\|(?!=)/)
261
+ ns = name_or_ns
262
+ name = tok(IDENT)
263
+ else
264
+ name = name_or_ns
265
+ end
266
+ else
267
+ # *|E or |E
268
+ ns = tok(/\*/) || ""
269
+ tok!(/\|/)
270
+ name = tok!(IDENT)
271
+ end
272
+ return ns, name
273
+ end
274
+
275
+ SELECTOR_PSEUDO_CLASSES = %w(not matches current any has host host-context).to_set
276
+
277
+ PREFIXED_SELECTOR_PSEUDO_CLASSES = %w(nth-child nth-last-child).to_set
278
+
279
+ def pseudo
280
+ s = tok(/::?/)
281
+ return unless s
282
+ @expected = "pseudoclass or pseudoelement"
283
+ name = tok!(IDENT)
284
+ if tok(/\(/)
285
+ ss
286
+ deprefixed = deprefix(name)
287
+ if s == ':' && SELECTOR_PSEUDO_CLASSES.include?(deprefixed)
288
+ sel = selector_comma_sequence
289
+ elsif s == ':' && PREFIXED_SELECTOR_PSEUDO_CLASSES.include?(deprefixed)
290
+ arg, sel = prefixed_selector_pseudo
291
+ else
292
+ arg = expr!(:pseudo_args)
293
+ end
294
+
295
+ tok!(/\)/)
296
+ end
297
+ Selector::Pseudo.new(s == ':' ? :class : :element, name, arg, sel)
298
+ end
299
+
300
+ def pseudo_args
301
+ arg = expr!(:pseudo_expr)
302
+ while tok(/,/)
303
+ arg << ',' << str {ss}
304
+ arg.concat expr!(:pseudo_expr)
305
+ end
306
+ arg
307
+ end
308
+
309
+ def pseudo_expr
310
+ res = pseudo_expr_token
311
+ return unless res
312
+ res << str {ss}
313
+ while (e = pseudo_expr_token)
314
+ res << e << str {ss}
315
+ end
316
+ res
317
+ end
318
+
319
+ def pseudo_expr_token
320
+ tok(PLUS) || tok(/[-*]/) || tok(NUMBER) || tok(STRING) || tok(IDENT)
321
+ end
322
+
323
+ def prefixed_selector_pseudo
324
+ prefix = str do
325
+ expr = str {expr!(:a_n_plus_b)}
326
+ ss
327
+ return expr, nil unless tok(/of/)
328
+ ss
329
+ end
330
+ return prefix, expr!(:selector_comma_sequence)
331
+ end
332
+
333
+ def a_n_plus_b
334
+ if (parity = tok(/even|odd/i))
335
+ return parity
336
+ end
337
+
338
+ if tok(/[+-]?[0-9]+/)
339
+ ss
340
+ return true unless tok(/n/)
341
+ else
342
+ return unless tok(/[+-]?n/i)
343
+ end
344
+ ss
345
+
346
+ return true unless tok(/[+-]/)
347
+ ss
348
+ @expected = "number"
349
+ tok!(/[0-9]+/)
350
+ true
351
+ end
352
+
353
+ def keyframes_selector
354
+ ss
355
+ str do
356
+ return unless keyframes_selector_component
357
+ ss
358
+ while tok(/,/)
359
+ ss
360
+ expr!(:keyframes_selector_component)
361
+ ss
362
+ end
363
+ end
364
+ end
365
+
366
+ def keyframes_selector_component
367
+ tok(IDENT) || tok(PERCENTAGE)
368
+ end
369
+
67
370
  @sass_script_parser = Class.new(Sass::Script::CssParser)
68
- @sass_script_parser.send(:include, ScriptParser)
69
371
  end
70
372
  end
71
373
  end
data/lib/sass/scss.rb CHANGED
@@ -1,6 +1,4 @@
1
1
  require 'sass/scss/rx'
2
- require 'sass/scss/script_lexer'
3
- require 'sass/scss/script_parser'
4
2
  require 'sass/scss/parser'
5
3
  require 'sass/scss/static_parser'
6
4
  require 'sass/scss/css_parser'
@@ -3,12 +3,12 @@ module Sass
3
3
  # The abstract parent class of the various selector sequence classes.
4
4
  #
5
5
  # All subclasses should implement a `members` method that returns an array
6
- # of object that respond to `#line=` and `#filename=`, as well as a `to_a`
7
- # method that returns an array of strings and script nodes.
6
+ # of object that respond to `#line=` and `#filename=`, as well as a `to_s`
7
+ # method that returns the string representation of the selector.
8
8
  class AbstractSequence
9
9
  # The line of the Sass template on which this selector was declared.
10
10
  #
11
- # @return [Fixnum]
11
+ # @return [Integer]
12
12
  attr_reader :line
13
13
 
14
14
  # The name of the file in which this selector was declared.
@@ -19,8 +19,8 @@ module Sass
19
19
  # Sets the line of the Sass template on which this selector was declared.
20
20
  # This also sets the line for all child selectors.
21
21
  #
22
- # @param line [Fixnum]
23
- # @return [Fixnum]
22
+ # @param line [Integer]
23
+ # @return [Integer]
24
24
  def line=(line)
25
25
  members.each {|m| m.line = line}
26
26
  @line = line
@@ -42,7 +42,7 @@ module Sass
42
42
  # Subclasses should define `#_hash` rather than overriding this method,
43
43
  # which automatically handles memoizing the result.
44
44
  #
45
- # @return [Fixnum]
45
+ # @return [Integer]
46
46
  def hash
47
47
  @_hash ||= _hash
48
48
  end
@@ -62,22 +62,28 @@ module Sass
62
62
  # Whether or not this selector sequence contains a placeholder selector.
63
63
  # Checks recursively.
64
64
  def has_placeholder?
65
- @has_placeholder ||=
66
- members.any? {|m| m.is_a?(AbstractSequence) ? m.has_placeholder? : m.is_a?(Placeholder)}
65
+ @has_placeholder ||= members.any? do |m|
66
+ next m.has_placeholder? if m.is_a?(AbstractSequence)
67
+ next m.selector && m.selector.has_placeholder? if m.is_a?(Pseudo)
68
+ m.is_a?(Placeholder)
69
+ end
67
70
  end
68
71
 
69
- # Converts the selector into a string. This is the standard selector
70
- # string, along with any SassScript interpolation that may exist.
72
+ # Returns the selector string.
71
73
  #
74
+ # @param opts [Hash] rendering options.
75
+ # @option opts [Symbol] :style The css rendering style.
72
76
  # @return [String]
73
- def to_s
74
- to_a.map {|e| e.is_a?(Sass::Script::Tree::Node) ? "\#{#{e.to_sass}}" : e}.join
77
+ def to_s(opts = {})
78
+ Sass::Util.abstract(self)
75
79
  end
76
80
 
77
- # Returns the specificity of the selector as an integer. The base is given
78
- # by {Sass::Selector::SPECIFICITY_BASE}.
81
+ # Returns the specificity of the selector.
79
82
  #
80
- # @return [Fixnum]
83
+ # The base is given by {Sass::Selector::SPECIFICITY_BASE}. This can be a
84
+ # number or a range representing possible specificities.
85
+ #
86
+ # @return [Integer, Range]
81
87
  def specificity
82
88
  _specificity(members)
83
89
  end
@@ -85,9 +91,20 @@ module Sass
85
91
  protected
86
92
 
87
93
  def _specificity(arr)
88
- spec = 0
89
- arr.map {|m| spec += m.is_a?(String) ? 0 : m.specificity}
90
- spec
94
+ min = 0
95
+ max = 0
96
+ arr.each do |m|
97
+ next if m.is_a?(String)
98
+ spec = m.specificity
99
+ if spec.is_a?(Range)
100
+ min += spec.begin
101
+ max += spec.end
102
+ else
103
+ min += spec
104
+ max += spec
105
+ end
106
+ end
107
+ min == max ? min : (min..max)
91
108
  end
92
109
  end
93
110
  end
@@ -2,6 +2,8 @@ module Sass
2
2
  module Selector
3
3
  # A comma-separated sequence of selectors.
4
4
  class CommaSequence < AbstractSequence
5
+ @@compound_extend_deprecation = Sass::Deprecation.new
6
+
5
7
  # The comma-separated selector sequences
6
8
  # represented by this class.
7
9
  #
@@ -25,12 +27,7 @@ module Sass
25
27
  # @raise [Sass::SyntaxError] If a parent selector is invalid
26
28
  def resolve_parent_refs(super_cseq, implicit_parent = true)
27
29
  if super_cseq.nil?
28
- if @members.any? do |sel|
29
- sel.members.any? do |sel_or_op|
30
- sel_or_op.is_a?(SimpleSequence) &&
31
- sel_or_op.members.any? {|ssel| ssel.is_a?(Parent)}
32
- end
33
- end
30
+ if contains_parent_ref?
34
31
  raise Sass::SyntaxError.new(
35
32
  "Base-level rules cannot contain the parent-selector-referencing character '&'.")
36
33
  end
@@ -42,6 +39,13 @@ module Sass
42
39
  end))
43
40
  end
44
41
 
42
+ # Returns whether there's a {Parent} selector anywhere in this sequence.
43
+ #
44
+ # @return [Boolean]
45
+ def contains_parent_ref?
46
+ @members.any? {|sel| sel.contains_parent_ref?}
47
+ end
48
+
45
49
  # Non-destrucively extends this selector with the extensions specified in a hash
46
50
  # (which should come from {Sass::Tree::Visitors::Cssize}).
47
51
  #
@@ -53,20 +57,111 @@ module Sass
53
57
  # The extensions to perform on this selector
54
58
  # @param parent_directives [Array<Sass::Tree::DirectiveNode>]
55
59
  # The directives containing this selector.
60
+ # @param replace [Boolean]
61
+ # Whether to replace the original selector entirely or include
62
+ # it in the result.
63
+ # @param seen [Set<Array<Selector::Simple>>]
64
+ # The set of simple sequences that are currently being replaced.
65
+ # @param original [Boolean]
66
+ # Whether this is the original selector being extended, as opposed to
67
+ # the result of a previous extension that's being re-extended.
56
68
  # @return [CommaSequence] A copy of this selector,
57
69
  # with extensions made according to `extends`
58
- def do_extend(extends, parent_directives)
70
+ def do_extend(extends, parent_directives = [], replace = false, seen = Set.new,
71
+ original = true)
59
72
  CommaSequence.new(members.map do |seq|
60
- extended = seq.do_extend(extends, parent_directives)
61
- # First Law of Extend: the result of extending a selector should
62
- # always contain the base selector.
63
- #
64
- # See https://github.com/nex3/sass/issues/324.
65
- extended.unshift seq unless seq.has_placeholder? || extended.include?(seq)
66
- extended
73
+ seq.do_extend(extends, parent_directives, replace, seen, original)
67
74
  end.flatten)
68
75
  end
69
76
 
77
+ # Returns whether or not this selector matches all elements
78
+ # that the given selector matches (as well as possibly more).
79
+ #
80
+ # @example
81
+ # (.foo).superselector?(.foo.bar) #=> true
82
+ # (.foo).superselector?(.bar) #=> false
83
+ # @param cseq [CommaSequence]
84
+ # @return [Boolean]
85
+ def superselector?(cseq)
86
+ cseq.members.all? {|seq1| members.any? {|seq2| seq2.superselector?(seq1)}}
87
+ end
88
+
89
+ # Populates a subset map that can then be used to extend
90
+ # selectors. This registers an extension with this selector as
91
+ # the extender and `extendee` as the extendee.
92
+ #
93
+ # @param extends [Sass::Util::SubsetMap{Selector::Simple =>
94
+ # Sass::Tree::Visitors::Cssize::Extend}]
95
+ # The subset map representing the extensions to perform.
96
+ # @param extendee [CommaSequence] The selector being extended.
97
+ # @param extend_node [Sass::Tree::ExtendNode]
98
+ # The node that caused this extension.
99
+ # @param parent_directives [Array<Sass::Tree::DirectiveNode>]
100
+ # The parent directives containing `extend_node`.
101
+ # @param allow_compound_target [Boolean]
102
+ # Whether `extendee` is allowed to contain compound selectors.
103
+ # @raise [Sass::SyntaxError] if this extension is invalid.
104
+ def populate_extends(extends, extendee, extend_node = nil, parent_directives = [],
105
+ allow_compound_target = false)
106
+ extendee.members.each do |seq|
107
+ if seq.members.size > 1
108
+ raise Sass::SyntaxError.new("Can't extend #{seq}: can't extend nested selectors")
109
+ end
110
+
111
+ sseq = seq.members.first
112
+ if !sseq.is_a?(Sass::Selector::SimpleSequence)
113
+ raise Sass::SyntaxError.new("Can't extend #{seq}: invalid selector")
114
+ elsif sseq.members.any? {|ss| ss.is_a?(Sass::Selector::Parent)}
115
+ raise Sass::SyntaxError.new("Can't extend #{seq}: can't extend parent selectors")
116
+ end
117
+
118
+ sel = sseq.members
119
+ if !allow_compound_target && sel.length > 1
120
+ @@compound_extend_deprecation.warn(sseq.filename, sseq.line, <<WARNING)
121
+ Extending a compound selector, #{sseq}, is deprecated and will not be supported in a future release.
122
+ See https://github.com/sass/sass/issues/1599 for details.
123
+ WARNING
124
+ end
125
+
126
+ members.each do |member|
127
+ unless member.members.last.is_a?(Sass::Selector::SimpleSequence)
128
+ raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
129
+ end
130
+
131
+ extends[sel] = Sass::Tree::Visitors::Cssize::Extend.new(
132
+ member, sel, extend_node, parent_directives, :not_found)
133
+ end
134
+ end
135
+ end
136
+
137
+ # Unifies this with another comma selector to produce a selector
138
+ # that matches (a subset of) the intersection of the two inputs.
139
+ #
140
+ # @param other [CommaSequence]
141
+ # @return [CommaSequence, nil] The unified selector, or nil if unification failed.
142
+ # @raise [Sass::SyntaxError] If this selector cannot be unified.
143
+ # This will only ever occur when a dynamic selector,
144
+ # such as {Parent} or {Interpolation}, is used in unification.
145
+ # Since these selectors should be resolved
146
+ # by the time extension and unification happen,
147
+ # this exception will only ever be raised as a result of programmer error
148
+ def unify(other)
149
+ results = members.map {|seq1| other.members.map {|seq2| seq1.unify(seq2)}}.flatten.compact
150
+ results.empty? ? nil : CommaSequence.new(results.map {|cseq| cseq.members}.flatten)
151
+ end
152
+
153
+ # Returns a SassScript representation of this selector.
154
+ #
155
+ # @return [Sass::Script::Value::List]
156
+ def to_sass_script
157
+ Sass::Script::Value::List.new(members.map do |seq|
158
+ Sass::Script::Value::List.new(seq.members.map do |component|
159
+ next if component == "\n"
160
+ Sass::Script::Value::String.new(component.to_s)
161
+ end.compact, :space)
162
+ end, :comma)
163
+ end
164
+
70
165
  # Returns a string representation of the sequence.
71
166
  # This is basically the selector string.
72
167
  #
@@ -75,11 +170,11 @@ module Sass
75
170
  members.map {|m| m.inspect}.join(", ")
76
171
  end
77
172
 
78
- # @see Simple#to_a
79
- def to_a
80
- arr = Sass::Util.intersperse(@members.map {|m| m.to_a}, ", ").flatten
81
- arr.delete("\n")
82
- arr
173
+ # @see AbstractSequence#to_s
174
+ def to_s(opts = {})
175
+ @members.map {|m| m.to_s(opts)}.
176
+ join(opts[:style] == :compressed ? "," : ", ").
177
+ gsub(", \n", ",\n")
83
178
  end
84
179
 
85
180
  private