sass 3.1.0 → 3.3.0

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 (260) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING +1 -1
  3. data/MIT-LICENSE +2 -2
  4. data/README.md +29 -17
  5. data/Rakefile +43 -9
  6. data/VERSION +1 -1
  7. data/VERSION_DATE +1 -0
  8. data/VERSION_NAME +1 -1
  9. data/bin/sass +6 -1
  10. data/bin/sass-convert +6 -1
  11. data/bin/scss +6 -1
  12. data/ext/mkrf_conf.rb +27 -0
  13. data/lib/sass/cache_stores/base.rb +7 -3
  14. data/lib/sass/cache_stores/chain.rb +3 -2
  15. data/lib/sass/cache_stores/filesystem.rb +5 -7
  16. data/lib/sass/cache_stores/memory.rb +1 -1
  17. data/lib/sass/cache_stores/null.rb +2 -2
  18. data/lib/sass/callbacks.rb +2 -1
  19. data/lib/sass/css.rb +168 -53
  20. data/lib/sass/engine.rb +502 -174
  21. data/lib/sass/environment.rb +151 -111
  22. data/lib/sass/error.rb +7 -7
  23. data/lib/sass/exec.rb +176 -60
  24. data/lib/sass/features.rb +40 -0
  25. data/lib/sass/importers/base.rb +46 -7
  26. data/lib/sass/importers/deprecated_path.rb +51 -0
  27. data/lib/sass/importers/filesystem.rb +113 -30
  28. data/lib/sass/importers.rb +1 -0
  29. data/lib/sass/logger/base.rb +30 -0
  30. data/lib/sass/logger/log_level.rb +45 -0
  31. data/lib/sass/logger.rb +12 -0
  32. data/lib/sass/media.rb +213 -0
  33. data/lib/sass/plugin/compiler.rb +194 -104
  34. data/lib/sass/plugin/configuration.rb +18 -25
  35. data/lib/sass/plugin/merb.rb +1 -1
  36. data/lib/sass/plugin/staleness_checker.rb +37 -11
  37. data/lib/sass/plugin.rb +10 -13
  38. data/lib/sass/railtie.rb +2 -1
  39. data/lib/sass/repl.rb +5 -6
  40. data/lib/sass/script/css_lexer.rb +8 -4
  41. data/lib/sass/script/css_parser.rb +5 -2
  42. data/lib/sass/script/functions.rb +1547 -618
  43. data/lib/sass/script/lexer.rb +122 -72
  44. data/lib/sass/script/parser.rb +304 -135
  45. data/lib/sass/script/tree/funcall.rb +306 -0
  46. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +43 -13
  47. data/lib/sass/script/tree/list_literal.rb +77 -0
  48. data/lib/sass/script/tree/literal.rb +45 -0
  49. data/lib/sass/script/tree/map_literal.rb +64 -0
  50. data/lib/sass/script/{node.rb → tree/node.rb} +30 -12
  51. data/lib/sass/script/{operation.rb → tree/operation.rb} +33 -21
  52. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +14 -4
  53. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +21 -9
  54. data/lib/sass/script/tree/variable.rb +57 -0
  55. data/lib/sass/script/tree.rb +15 -0
  56. data/lib/sass/script/value/arg_list.rb +36 -0
  57. data/lib/sass/script/value/base.rb +238 -0
  58. data/lib/sass/script/value/bool.rb +40 -0
  59. data/lib/sass/script/{color.rb → value/color.rb} +256 -74
  60. data/lib/sass/script/value/deprecated_false.rb +55 -0
  61. data/lib/sass/script/value/helpers.rb +155 -0
  62. data/lib/sass/script/value/list.rb +128 -0
  63. data/lib/sass/script/value/map.rb +70 -0
  64. data/lib/sass/script/value/null.rb +49 -0
  65. data/lib/sass/script/{number.rb → value/number.rb} +115 -62
  66. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  67. data/lib/sass/script/value.rb +12 -0
  68. data/lib/sass/script.rb +35 -9
  69. data/lib/sass/scss/css_parser.rb +2 -12
  70. data/lib/sass/scss/parser.rb +657 -230
  71. data/lib/sass/scss/rx.rb +17 -12
  72. data/lib/sass/scss/static_parser.rb +37 -6
  73. data/lib/sass/scss.rb +0 -1
  74. data/lib/sass/selector/abstract_sequence.rb +35 -3
  75. data/lib/sass/selector/comma_sequence.rb +29 -14
  76. data/lib/sass/selector/sequence.rb +371 -74
  77. data/lib/sass/selector/simple.rb +28 -13
  78. data/lib/sass/selector/simple_sequence.rb +163 -36
  79. data/lib/sass/selector.rb +138 -36
  80. data/lib/sass/shared.rb +3 -5
  81. data/lib/sass/source/map.rb +196 -0
  82. data/lib/sass/source/position.rb +39 -0
  83. data/lib/sass/source/range.rb +41 -0
  84. data/lib/sass/stack.rb +126 -0
  85. data/lib/sass/supports.rb +228 -0
  86. data/lib/sass/tree/at_root_node.rb +82 -0
  87. data/lib/sass/tree/comment_node.rb +34 -29
  88. data/lib/sass/tree/content_node.rb +9 -0
  89. data/lib/sass/tree/css_import_node.rb +60 -0
  90. data/lib/sass/tree/debug_node.rb +3 -3
  91. data/lib/sass/tree/directive_node.rb +33 -3
  92. data/lib/sass/tree/each_node.rb +9 -9
  93. data/lib/sass/tree/extend_node.rb +20 -6
  94. data/lib/sass/tree/for_node.rb +6 -6
  95. data/lib/sass/tree/function_node.rb +12 -4
  96. data/lib/sass/tree/if_node.rb +2 -15
  97. data/lib/sass/tree/import_node.rb +11 -5
  98. data/lib/sass/tree/media_node.rb +27 -11
  99. data/lib/sass/tree/mixin_def_node.rb +15 -4
  100. data/lib/sass/tree/mixin_node.rb +27 -7
  101. data/lib/sass/tree/node.rb +69 -35
  102. data/lib/sass/tree/prop_node.rb +47 -31
  103. data/lib/sass/tree/return_node.rb +4 -3
  104. data/lib/sass/tree/root_node.rb +20 -4
  105. data/lib/sass/tree/rule_node.rb +37 -26
  106. data/lib/sass/tree/supports_node.rb +38 -0
  107. data/lib/sass/tree/trace_node.rb +33 -0
  108. data/lib/sass/tree/variable_node.rb +10 -4
  109. data/lib/sass/tree/visitors/base.rb +5 -8
  110. data/lib/sass/tree/visitors/check_nesting.rb +67 -52
  111. data/lib/sass/tree/visitors/convert.rb +134 -53
  112. data/lib/sass/tree/visitors/cssize.rb +245 -51
  113. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  114. data/lib/sass/tree/visitors/extend.rb +68 -0
  115. data/lib/sass/tree/visitors/perform.rb +331 -105
  116. data/lib/sass/tree/visitors/set_options.rb +125 -0
  117. data/lib/sass/tree/visitors/to_css.rb +259 -95
  118. data/lib/sass/tree/warn_node.rb +3 -3
  119. data/lib/sass/tree/while_node.rb +3 -3
  120. data/lib/sass/util/cross_platform_random.rb +19 -0
  121. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  122. data/lib/sass/util/normalized_map.rb +130 -0
  123. data/lib/sass/util/ordered_hash.rb +192 -0
  124. data/lib/sass/util/subset_map.rb +11 -2
  125. data/lib/sass/util/test.rb +9 -0
  126. data/lib/sass/util.rb +565 -39
  127. data/lib/sass/version.rb +27 -15
  128. data/lib/sass.rb +39 -4
  129. data/test/sass/cache_test.rb +15 -0
  130. data/test/sass/compiler_test.rb +223 -0
  131. data/test/sass/conversion_test.rb +901 -107
  132. data/test/sass/css2sass_test.rb +94 -0
  133. data/test/sass/engine_test.rb +1059 -164
  134. data/test/sass/exec_test.rb +86 -0
  135. data/test/sass/extend_test.rb +933 -837
  136. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  137. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  138. data/test/sass/functions_test.rb +995 -136
  139. data/test/sass/importer_test.rb +338 -18
  140. data/test/sass/logger_test.rb +58 -0
  141. data/test/sass/more_results/more_import.css +2 -2
  142. data/test/sass/plugin_test.rb +114 -30
  143. data/test/sass/results/cached_import_option.css +3 -0
  144. data/test/sass/results/filename_fn.css +3 -0
  145. data/test/sass/results/import.css +2 -2
  146. data/test/sass/results/import_charset.css +1 -0
  147. data/test/sass/results/import_charset_1_8.css +1 -0
  148. data/test/sass/results/import_charset_ibm866.css +1 -0
  149. data/test/sass/results/import_content.css +1 -0
  150. data/test/sass/results/script.css +1 -1
  151. data/test/sass/results/scss_import.css +2 -2
  152. data/test/sass/results/units.css +2 -2
  153. data/test/sass/script_conversion_test.rb +43 -1
  154. data/test/sass/script_test.rb +380 -36
  155. data/test/sass/scss/css_test.rb +257 -75
  156. data/test/sass/scss/scss_test.rb +2322 -110
  157. data/test/sass/source_map_test.rb +887 -0
  158. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  159. data/test/sass/templates/_double_import_loop2.sass +1 -0
  160. data/test/sass/templates/_filename_fn_import.scss +11 -0
  161. data/test/sass/templates/_imported_content.sass +3 -0
  162. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  163. data/test/sass/templates/bork5.sass +3 -0
  164. data/test/sass/templates/cached_import_option.scss +3 -0
  165. data/test/sass/templates/double_import_loop1.sass +1 -0
  166. data/test/sass/templates/filename_fn.scss +18 -0
  167. data/test/sass/templates/import_charset.sass +2 -0
  168. data/test/sass/templates/import_charset_1_8.sass +2 -0
  169. data/test/sass/templates/import_charset_ibm866.sass +2 -0
  170. data/test/sass/templates/import_content.sass +4 -0
  171. data/test/sass/templates/same_name_different_ext.sass +2 -0
  172. data/test/sass/templates/same_name_different_ext.scss +1 -0
  173. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  174. data/test/sass/templates/single_import_loop.sass +1 -0
  175. data/test/sass/templates/subdir/import_up1.scss +1 -0
  176. data/test/sass/templates/subdir/import_up2.scss +1 -0
  177. data/test/sass/test_helper.rb +1 -1
  178. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  179. data/test/sass/util/normalized_map_test.rb +51 -0
  180. data/test/sass/util_test.rb +183 -0
  181. data/test/sass/value_helpers_test.rb +181 -0
  182. data/test/test_helper.rb +45 -5
  183. data/vendor/listen/CHANGELOG.md +228 -0
  184. data/vendor/listen/CONTRIBUTING.md +38 -0
  185. data/vendor/listen/Gemfile +30 -0
  186. data/vendor/listen/Guardfile +8 -0
  187. data/vendor/{fssm → listen}/LICENSE +1 -1
  188. data/vendor/listen/README.md +315 -0
  189. data/vendor/listen/Rakefile +47 -0
  190. data/vendor/listen/Vagrantfile +96 -0
  191. data/vendor/listen/lib/listen/adapter.rb +214 -0
  192. data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
  193. data/vendor/listen/lib/listen/adapters/darwin.rb +85 -0
  194. data/vendor/listen/lib/listen/adapters/linux.rb +113 -0
  195. data/vendor/listen/lib/listen/adapters/polling.rb +67 -0
  196. data/vendor/listen/lib/listen/adapters/windows.rb +87 -0
  197. data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
  198. data/vendor/listen/lib/listen/directory_record.rb +371 -0
  199. data/vendor/listen/lib/listen/listener.rb +225 -0
  200. data/vendor/listen/lib/listen/multi_listener.rb +143 -0
  201. data/vendor/listen/lib/listen/turnstile.rb +28 -0
  202. data/vendor/listen/lib/listen/version.rb +3 -0
  203. data/vendor/listen/lib/listen.rb +40 -0
  204. data/vendor/listen/listen.gemspec +22 -0
  205. data/vendor/listen/spec/listen/adapter_spec.rb +183 -0
  206. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  207. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  208. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  209. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  210. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  211. data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
  212. data/vendor/listen/spec/listen/directory_record_spec.rb +1225 -0
  213. data/vendor/listen/spec/listen/listener_spec.rb +169 -0
  214. data/vendor/listen/spec/listen/multi_listener_spec.rb +174 -0
  215. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  216. data/vendor/listen/spec/listen_spec.rb +73 -0
  217. data/vendor/listen/spec/spec_helper.rb +21 -0
  218. data/vendor/listen/spec/support/adapter_helper.rb +629 -0
  219. data/vendor/listen/spec/support/directory_record_helper.rb +55 -0
  220. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  221. data/vendor/listen/spec/support/listeners_helper.rb +156 -0
  222. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  223. metadata +344 -271
  224. data/lib/sass/less.rb +0 -382
  225. data/lib/sass/script/bool.rb +0 -18
  226. data/lib/sass/script/funcall.rb +0 -162
  227. data/lib/sass/script/list.rb +0 -76
  228. data/lib/sass/script/literal.rb +0 -245
  229. data/lib/sass/script/variable.rb +0 -54
  230. data/lib/sass/scss/sass_parser.rb +0 -11
  231. data/test/sass/less_conversion_test.rb +0 -653
  232. data/vendor/fssm/README.markdown +0 -55
  233. data/vendor/fssm/Rakefile +0 -59
  234. data/vendor/fssm/VERSION.yml +0 -5
  235. data/vendor/fssm/example.rb +0 -9
  236. data/vendor/fssm/fssm.gemspec +0 -77
  237. data/vendor/fssm/lib/fssm/backends/fsevents.rb +0 -36
  238. data/vendor/fssm/lib/fssm/backends/inotify.rb +0 -26
  239. data/vendor/fssm/lib/fssm/backends/polling.rb +0 -25
  240. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +0 -131
  241. data/vendor/fssm/lib/fssm/monitor.rb +0 -26
  242. data/vendor/fssm/lib/fssm/path.rb +0 -91
  243. data/vendor/fssm/lib/fssm/pathname.rb +0 -502
  244. data/vendor/fssm/lib/fssm/state/directory.rb +0 -57
  245. data/vendor/fssm/lib/fssm/state/file.rb +0 -24
  246. data/vendor/fssm/lib/fssm/support.rb +0 -63
  247. data/vendor/fssm/lib/fssm/tree.rb +0 -176
  248. data/vendor/fssm/lib/fssm.rb +0 -33
  249. data/vendor/fssm/profile/prof-cache.rb +0 -40
  250. data/vendor/fssm/profile/prof-fssm-pathname.html +0 -1231
  251. data/vendor/fssm/profile/prof-pathname.rb +0 -68
  252. data/vendor/fssm/profile/prof-plain-pathname.html +0 -988
  253. data/vendor/fssm/profile/prof.html +0 -2379
  254. data/vendor/fssm/spec/path_spec.rb +0 -75
  255. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  256. data/vendor/fssm/spec/root/file.css +0 -0
  257. data/vendor/fssm/spec/root/file.rb +0 -0
  258. data/vendor/fssm/spec/root/file.yml +0 -0
  259. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  260. data/vendor/fssm/spec/spec_helper.rb +0 -14
@@ -8,7 +8,31 @@ module Sass
8
8
  # The array of individual selectors.
9
9
  #
10
10
  # @return [Array<Simple>]
11
- attr_reader :members
11
+ attr_accessor :members
12
+
13
+ # The extending selectors that caused this selector sequence to be
14
+ # generated. For example:
15
+ #
16
+ # a.foo { ... }
17
+ # b.bar {@extend a}
18
+ # c.baz {@extend b}
19
+ #
20
+ # The generated selector `b.foo.bar` has `{b.bar}` as its `sources` set,
21
+ # and the generated selector `c.foo.bar.baz` has `{b.bar, c.baz}` as its
22
+ # `sources` set.
23
+ #
24
+ # This is populated during the {Sequence#do_extend} process.
25
+ #
26
+ # @return {Set<Sequence>}
27
+ attr_accessor :sources
28
+
29
+ # This sequence source range.
30
+ #
31
+ # @return [Sass::Source::Range]
32
+ attr_accessor :source_range
33
+
34
+ # @see \{#subject?}
35
+ attr_writer :subject
12
36
 
13
37
  # Returns the element or universal selector in this sequence,
14
38
  # if it exists.
@@ -18,67 +42,136 @@ module Sass
18
42
  @base ||= (members.first if members.first.is_a?(Element) || members.first.is_a?(Universal))
19
43
  end
20
44
 
21
- # Returns the non-base selectors in this sequence.
45
+ def pseudo_elements
46
+ @pseudo_elements ||= (members - [base]).
47
+ select {|sel| sel.is_a?(Pseudo) && sel.type == :element}
48
+ end
49
+
50
+ # Returns the non-base, non-pseudo-class selectors in this sequence.
22
51
  #
23
52
  # @return [Set<Simple>]
24
53
  def rest
25
- @rest ||= Set.new(base ? members[1..-1] : members)
54
+ @rest ||= Set.new(members - [base] - pseudo_elements)
55
+ end
56
+
57
+ # Whether or not this compound selector is the subject of the parent
58
+ # selector; that is, whether it is prepended with `$` and represents the
59
+ # actual element that will be selected.
60
+ #
61
+ # @return [Boolean]
62
+ def subject?
63
+ @subject
26
64
  end
27
65
 
28
66
  # @param selectors [Array<Simple>] See \{#members}
29
- def initialize(selectors)
67
+ # @param subject [Boolean] See \{#subject?}
68
+ # @param source_range [Sass::Source::Range]
69
+ def initialize(selectors, subject, source_range = nil)
30
70
  @members = selectors
71
+ @subject = subject
72
+ @sources = Set.new
73
+ @source_range = source_range
31
74
  end
32
75
 
33
76
  # Resolves the {Parent} selectors within this selector
34
77
  # by replacing them with the given parent selector,
35
78
  # handling commas appropriately.
36
79
  #
37
- # @param super_seq [Sequence] The parent selector sequence
38
- # @return [Array<SimpleSequence>] This selector, with parent references resolved.
39
- # This is an array because the parent selector is itself a {Sequence}
80
+ # @param super_cseq [CommaSequence] The parent selector
81
+ # @return [CommaSequence] This selector, with parent references resolved
40
82
  # @raise [Sass::SyntaxError] If a parent selector is invalid
41
- def resolve_parent_refs(super_seq)
83
+ def resolve_parent_refs(super_cseq)
42
84
  # Parent selector only appears as the first selector in the sequence
43
- return [self] unless @members.first.is_a?(Parent)
44
-
45
- return super_seq.members if @members.size == 1
46
- unless super_seq.members.last.is_a?(SimpleSequence)
47
- raise Sass::SyntaxError.new("Invalid parent selector: " + super_seq.to_a.join)
85
+ unless (parent = @members.first).is_a?(Parent)
86
+ return CommaSequence.new([Sequence.new([self])])
48
87
  end
49
88
 
50
- super_seq.members[0...-1] +
51
- [SimpleSequence.new(super_seq.members.last.members + @members[1..-1])]
89
+ return super_cseq if @members.size == 1 && parent.suffix.empty?
90
+
91
+ CommaSequence.new(super_cseq.members.map do |super_seq|
92
+ members = super_seq.members.dup
93
+ newline = members.pop if members.last == "\n"
94
+ unless members.last.is_a?(SimpleSequence)
95
+ raise Sass::SyntaxError.new("Invalid parent selector for \"#{self}\": \"" +
96
+ super_seq.to_a.join + '"')
97
+ end
98
+
99
+ parent_sub = members.last.members
100
+ unless parent.suffix.empty?
101
+ parent_sub = parent_sub.dup
102
+ parent_sub[-1] = parent_sub.last.dup
103
+ case parent_sub.last
104
+ when Sass::Selector::Class, Sass::Selector::Id, Sass::Selector::Placeholder
105
+ parent_sub[-1] = parent_sub.last.class.new(parent_sub.last.name + parent.suffix)
106
+ when Sass::Selector::Element
107
+ parent_sub[-1] = parent_sub.last.class.new(
108
+ parent_sub.last.name + parent.suffix,
109
+ parent_sub.last.namespace)
110
+ when Sass::Selector::Pseudo
111
+ if parent_sub.last.arg
112
+ raise Sass::SyntaxError.new("Invalid parent selector for \"#{self}\": \"" +
113
+ super_seq.to_a.join + '"')
114
+ end
115
+ parent_sub[-1] = parent_sub.last.class.new(
116
+ parent_sub.last.type,
117
+ parent_sub.last.name + parent.suffix,
118
+ nil)
119
+ else
120
+ raise Sass::SyntaxError.new("Invalid parent selector for \"#{self}\": \"" +
121
+ super_seq.to_a.join + '"')
122
+ end
123
+ end
124
+
125
+ Sequence.new(members[0...-1] +
126
+ [SimpleSequence.new(parent_sub + @members[1..-1], subject?)] +
127
+ [newline].compact)
128
+ end)
52
129
  end
53
130
 
54
131
  # Non-destrucively extends this selector with the extensions specified in a hash
55
132
  # (which should come from {Sass::Tree::Visitors::Cssize}).
56
133
  #
57
- # @overload def do_extend(extends)
58
- # @param extends [{Selector::Simple => Selector::Sequence}]
134
+ # @overload def do_extend(extends, parent_directives)
135
+ # @param extends [{Selector::Simple =>
136
+ # Sass::Tree::Visitors::Cssize::Extend}]
59
137
  # The extensions to perform on this selector
138
+ # @param parent_directives [Array<Sass::Tree::DirectiveNode>]
139
+ # The directives containing this selector.
60
140
  # @return [Array<Sequence>] A list of selectors generated
61
141
  # by extending this selector with `extends`.
62
142
  # @see CommaSequence#do_extend
63
- def do_extend(extends, seen = Set.new)
64
- extends.get(members.to_set).map do |seq, sels|
143
+ def do_extend(extends, parent_directives, seen = Set.new)
144
+ groups = Sass::Util.group_by_to_a(extends[members.to_set]) {|ex| ex.extender}
145
+ groups.map! do |seq, group|
146
+ sels = group.map {|e| e.target}.flatten
65
147
  # If A {@extend B} and C {...},
66
148
  # seq is A, sels is B, and self is C
67
149
 
68
- self_without_sel = self.members - sels
69
- next unless unified = seq.members.last.unify(self_without_sel)
70
- [sels, seq.members[0...-1] + [unified]]
71
- end.compact.map do |sels, seq|
72
- seq = Sequence.new(seq)
73
- seen.include?(sels) ? [] : seq.do_extend(extends, seen + [sels])
74
- end.flatten.uniq
150
+ self_without_sel = Sass::Util.array_minus(members, sels)
151
+ group.each {|e| e.result = :failed_to_unify unless e.result == :succeeded}
152
+ unified = seq.members.last.unify(self_without_sel, subject?)
153
+ next unless unified
154
+ group.each {|e| e.result = :succeeded}
155
+ group.each {|e| check_directives_match!(e, parent_directives)}
156
+ new_seq = Sequence.new(seq.members[0...-1] + [unified])
157
+ new_seq.add_sources!(sources + [seq])
158
+ [sels, new_seq]
159
+ end
160
+ groups.compact!
161
+ groups.map! do |sels, seq|
162
+ seen.include?(sels) ? [] : seq.do_extend(extends, parent_directives, seen + [sels])
163
+ end
164
+ groups.flatten!
165
+ groups.uniq!
166
+ groups
75
167
  end
76
168
 
77
- # Unifies this selector with another {SimpleSequence}'s {SimpleSequence#members members array},
78
- # returning another `SimpleSequence`
169
+ # Unifies this selector with another {SimpleSequence}'s
170
+ # {SimpleSequence#members members array}, returning another `SimpleSequence`
79
171
  # that matches both this selector and the input selector.
80
172
  #
81
173
  # @param sels [Array<Simple>] A {SimpleSequence}'s {SimpleSequence#members members array}
174
+ # @param other_subject [Boolean] Whether the other {SimpleSequence} being merged is a subject.
82
175
  # @return [SimpleSequence, nil] A {SimpleSequence} matching both `sels` and this selector,
83
176
  # or `nil` if this is impossible (e.g. unifying `#foo` and `#bar`)
84
177
  # @raise [Sass::SyntaxError] If this selector cannot be unified.
@@ -87,12 +180,13 @@ module Sass
87
180
  # Since these selectors should be resolved
88
181
  # by the time extension and unification happen,
89
182
  # this exception will only ever be raised as a result of programmer error
90
- def unify(sels)
91
- return unless sseq = members.inject(sels) do |sseq, sel|
92
- return unless sseq
93
- sel.unify(sseq)
183
+ def unify(sels, other_subject)
184
+ sseq = members.inject(sels) do |member, sel|
185
+ return unless member
186
+ sel.unify(member)
94
187
  end
95
- SimpleSequence.new(sseq)
188
+ return unless sseq
189
+ SimpleSequence.new(sseq, other_subject || subject?)
96
190
  end
97
191
 
98
192
  # Returns whether or not this selector matches all elements
@@ -104,12 +198,16 @@ module Sass
104
198
  # @param sseq [SimpleSequence]
105
199
  # @return [Boolean]
106
200
  def superselector?(sseq)
107
- (base.nil? || base.eql?(sseq.base)) && rest.subset?(sseq.rest)
201
+ (base.nil? || base.eql?(sseq.base)) &&
202
+ pseudo_elements.eql?(sseq.pseudo_elements) &&
203
+ rest.subset?(sseq.rest)
108
204
  end
109
205
 
110
206
  # @see Simple#to_a
111
207
  def to_a
112
- @members.map {|sel| sel.to_a}.flatten
208
+ res = @members.map {|sel| sel.to_a}.flatten
209
+ res << '!' if subject?
210
+ res
113
211
  end
114
212
 
115
213
  # Returns a string representation of the sequence.
@@ -120,14 +218,43 @@ module Sass
120
218
  members.map {|m| m.inspect}.join
121
219
  end
122
220
 
221
+ # Return a copy of this simple sequence with `sources` merged into the
222
+ # {SimpleSequence#sources} set.
223
+ #
224
+ # @param sources [Set<Sequence>]
225
+ # @return [SimpleSequence]
226
+ def with_more_sources(sources)
227
+ sseq = dup
228
+ sseq.members = members.dup
229
+ sseq.sources = self.sources | sources
230
+ sseq
231
+ end
232
+
123
233
  private
124
234
 
235
+ def check_directives_match!(extend, parent_directives)
236
+ dirs1 = extend.directives.map {|d| d.resolved_value}
237
+ dirs2 = parent_directives.map {|d| d.resolved_value}
238
+ return if Sass::Util.subsequence?(dirs1, dirs2)
239
+ line = extend.node.line
240
+ filename = extend.node.filename
241
+
242
+ # TODO(nweiz): this should use the Sass stack trace of the extend node,
243
+ # not the selector.
244
+ raise Sass::SyntaxError.new(<<MESSAGE)
245
+ You may not @extend an outer selector from within #{extend.directives.last.name}.
246
+ You may only @extend selectors within the same directive.
247
+ From "@extend #{extend.target.join(', ')}" on line #{line}#{" of #{filename}" if filename}.
248
+ MESSAGE
249
+ end
250
+
125
251
  def _hash
126
252
  [base, Sass::Util.set_hash(rest)].hash
127
253
  end
128
254
 
129
255
  def _eql?(other)
130
- other.base.eql?(self.base) && Sass::Util.set_eql?(other.rest, self.rest)
256
+ other.base.eql?(base) && other.pseudo_elements == pseudo_elements &&
257
+ Sass::Util.set_eql?(other.rest, rest) && other.subject? == subject?
131
258
  end
132
259
  end
133
260
  end
data/lib/sass/selector.rb CHANGED
@@ -18,13 +18,28 @@ module Sass
18
18
  # Finally, {Simple} is the superclass of the simplest selectors,
19
19
  # such as `.foo` or `#bar`.
20
20
  module Selector
21
+ # The base used for calculating selector specificity. The spec says this
22
+ # should be "sufficiently high"; it's extremely unlikely that any single
23
+ # selector sequence will contain 1,000 simple selectors.
24
+ SPECIFICITY_BASE = 1_000
25
+
21
26
  # A parent-referencing selector (`&` in Sass).
22
27
  # The function of this is to be replaced by the parent selector
23
28
  # in the nested hierarchy.
24
29
  class Parent < Simple
30
+ # The identifier following the `&`. Often empty.
31
+ #
32
+ # @return [Array<String, Sass::Script::Tree::Node>]
33
+ attr_reader :suffix
34
+
35
+ # @param name [Array<String, Sass::Script::Tree::Node>] See \{#suffix}
36
+ def initialize(suffix = [])
37
+ @suffix = suffix
38
+ end
39
+
25
40
  # @see Selector#to_a
26
41
  def to_a
27
- ["&"]
42
+ ["&", *@suffix]
28
43
  end
29
44
 
30
45
  # Always raises an exception.
@@ -40,10 +55,10 @@ module Sass
40
55
  class Class < Simple
41
56
  # The class name.
42
57
  #
43
- # @return [Array<String, Sass::Script::Node>]
58
+ # @return [Array<String, Sass::Script::Tree::Node>]
44
59
  attr_reader :name
45
60
 
46
- # @param name [Array<String, Sass::Script::Node>] The class name
61
+ # @param name [Array<String, Sass::Script::Tree::Node>] The class name
47
62
  def initialize(name)
48
63
  @name = name
49
64
  end
@@ -52,16 +67,21 @@ module Sass
52
67
  def to_a
53
68
  [".", *@name]
54
69
  end
70
+
71
+ # @see AbstractSequence#specificity
72
+ def specificity
73
+ SPECIFICITY_BASE
74
+ end
55
75
  end
56
76
 
57
77
  # An id selector (e.g. `#foo`).
58
78
  class Id < Simple
59
79
  # The id name.
60
80
  #
61
- # @return [Array<String, Sass::Script::Node>]
81
+ # @return [Array<String, Sass::Script::Tree::Node>]
62
82
  attr_reader :name
63
83
 
64
- # @param name [Array<String, Sass::Script::Node>] The id name
84
+ # @param name [Array<String, Sass::Script::Tree::Node>] The id name
65
85
  def initialize(name)
66
86
  @name = name
67
87
  end
@@ -76,9 +96,40 @@ module Sass
76
96
  #
77
97
  # @see Selector#unify
78
98
  def unify(sels)
79
- return if sels.any? {|sel2| sel2.is_a?(Id) && self.name != sel2.name}
99
+ return if sels.any? {|sel2| sel2.is_a?(Id) && name != sel2.name}
80
100
  super
81
101
  end
102
+
103
+ # @see AbstractSequence#specificity
104
+ def specificity
105
+ SPECIFICITY_BASE**2
106
+ end
107
+ end
108
+
109
+ # A placeholder selector (e.g. `%foo`).
110
+ # This exists to be replaced via `@extend`.
111
+ # Rulesets using this selector will not be printed, but can be extended.
112
+ # Otherwise, this acts just like a class selector.
113
+ class Placeholder < Simple
114
+ # The placeholder name.
115
+ #
116
+ # @return [Array<String, Sass::Script::Tree::Node>]
117
+ attr_reader :name
118
+
119
+ # @param name [Array<String, Sass::Script::Tree::Node>] The placeholder name
120
+ def initialize(name)
121
+ @name = name
122
+ end
123
+
124
+ # @see Selector#to_a
125
+ def to_a
126
+ ["%", *@name]
127
+ end
128
+
129
+ # @see AbstractSequence#specificity
130
+ def specificity
131
+ SPECIFICITY_BASE
132
+ end
82
133
  end
83
134
 
84
135
  # A universal selector (`*` in CSS).
@@ -88,10 +139,10 @@ module Sass
88
139
  # `[""]` means no namespace,
89
140
  # `["*"]` means any namespace.
90
141
  #
91
- # @return [Array<String, Sass::Script::Node>, nil]
142
+ # @return [Array<String, Sass::Script::Tree::Node>, nil]
92
143
  attr_reader :namespace
93
144
 
94
- # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
145
+ # @param namespace [Array<String, Sass::Script::Tree::Node>, nil] See \{#namespace}
95
146
  def initialize(namespace)
96
147
  @namespace = namespace
97
148
  end
@@ -141,13 +192,18 @@ module Sass
141
192
  return unless accept
142
193
  [name == :universal ? Universal.new(ns) : Element.new(name, ns)] + sels[1..-1]
143
194
  end
195
+
196
+ # @see AbstractSequence#specificity
197
+ def specificity
198
+ 0
199
+ end
144
200
  end
145
201
 
146
202
  # An element selector (e.g. `h1`).
147
203
  class Element < Simple
148
204
  # The element name.
149
205
  #
150
- # @return [Array<String, Sass::Script::Node>]
206
+ # @return [Array<String, Sass::Script::Tree::Node>]
151
207
  attr_reader :name
152
208
 
153
209
  # The selector namespace.
@@ -155,11 +211,11 @@ module Sass
155
211
  # `[""]` means no namespace,
156
212
  # `["*"]` means any namespace.
157
213
  #
158
- # @return [Array<String, Sass::Script::Node>, nil]
214
+ # @return [Array<String, Sass::Script::Tree::Node>, nil]
159
215
  attr_reader :namespace
160
216
 
161
- # @param name [Array<String, Sass::Script::Node>] The element name
162
- # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
217
+ # @param name [Array<String, Sass::Script::Tree::Node>] The element name
218
+ # @param namespace [Array<String, Sass::Script::Tree::Node>, nil] See \{#namespace}
163
219
  def initialize(name, namespace)
164
220
  @name = name
165
221
  @namespace = namespace
@@ -203,16 +259,21 @@ module Sass
203
259
  return unless accept
204
260
  [Element.new(name, ns)] + sels[1..-1]
205
261
  end
262
+
263
+ # @see AbstractSequence#specificity
264
+ def specificity
265
+ 1
266
+ end
206
267
  end
207
268
 
208
269
  # Selector interpolation (`#{}` in Sass).
209
270
  class Interpolation < Simple
210
271
  # The script to run.
211
272
  #
212
- # @return [Sass::Script::Node]
273
+ # @return [Sass::Script::Tree::Node]
213
274
  attr_reader :script
214
275
 
215
- # @param script [Sass::Script::Node] The script to run
276
+ # @param script [Sass::Script::Tree::Node] The script to run
216
277
  def initialize(script)
217
278
  @script = script
218
279
  end
@@ -235,7 +296,7 @@ module Sass
235
296
  class Attribute < Simple
236
297
  # The attribute name.
237
298
  #
238
- # @return [Array<String, Sass::Script::Node>]
299
+ # @return [Array<String, Sass::Script::Tree::Node>]
239
300
  attr_reader :name
240
301
 
241
302
  # The attribute namespace.
@@ -243,7 +304,7 @@ module Sass
243
304
  # `[""]` means no namespace,
244
305
  # `["*"]` means any namespace.
245
306
  #
246
- # @return [Array<String, Sass::Script::Node>, nil]
307
+ # @return [Array<String, Sass::Script::Tree::Node>, nil]
247
308
  attr_reader :namespace
248
309
 
249
310
  # The matching operator, e.g. `"="` or `"^="`.
@@ -253,18 +314,28 @@ module Sass
253
314
 
254
315
  # The right-hand side of the operator.
255
316
  #
256
- # @return [Array<String, Sass::Script::Node>]
317
+ # @return [Array<String, Sass::Script::Tree::Node>]
257
318
  attr_reader :value
258
319
 
259
- # @param name [Array<String, Sass::Script::Node>] The attribute name
260
- # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
320
+ # Flags for the attribute selector (e.g. `i`).
321
+ #
322
+ # @return [Array<String, Sass::Script::Tree::Node>]
323
+ attr_reader :flags
324
+
325
+ # @param name [Array<String, Sass::Script::Tree::Node>] The attribute name
326
+ # @param namespace [Array<String, Sass::Script::Tree::Node>, nil] See \{#namespace}
261
327
  # @param operator [String] The matching operator, e.g. `"="` or `"^="`
262
- # @param value [Array<String, Sass::Script::Node>] See \{#value}
263
- def initialize(name, namespace, operator, value)
328
+ # @param value [Array<String, Sass::Script::Tree::Node>] See \{#value}
329
+ # @param flags [Array<String, Sass::Script::Tree::Node>] See \{#flags}
330
+ # @comment
331
+ # rubocop:disable ParameterLists
332
+ def initialize(name, namespace, operator, value, flags)
333
+ # rubocop:enable ParameterLists
264
334
  @name = name
265
335
  @namespace = namespace
266
336
  @operator = operator
267
337
  @value = value
338
+ @flags = flags
268
339
  end
269
340
 
270
341
  # @see Selector#to_a
@@ -273,23 +344,36 @@ module Sass
273
344
  res.concat(@namespace) << "|" if @namespace
274
345
  res.concat @name
275
346
  (res << @operator).concat @value if @value
347
+ (res << " ").concat @flags if @flags
276
348
  res << "]"
277
349
  end
350
+
351
+ # @see AbstractSequence#specificity
352
+ def specificity
353
+ SPECIFICITY_BASE
354
+ end
278
355
  end
279
356
 
280
357
  # A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`) selector.
281
358
  # It can have arguments (e.g. `:nth-child(2n+1)`).
282
359
  class Pseudo < Simple
283
- # The type of the selector.
284
- # `:class` if this is a pseudoclass selector,
285
- # `:element` if it's a pseudoelement.
360
+ # Some psuedo-class-syntax selectors are actually considered
361
+ # pseudo-elements and must be treated differently. This is a list of such
362
+ # selectors
363
+ #
364
+ # @return [Array<String>]
365
+ ACTUALLY_ELEMENTS = %w[after before first-line first-letter]
366
+
367
+ # Like \{#type}, but returns the type of selector this looks like, rather
368
+ # than the type it is semantically. This only differs from type for
369
+ # selectors in \{ACTUALLY\_ELEMENTS}.
286
370
  #
287
371
  # @return [Symbol]
288
- attr_reader :type
372
+ attr_reader :syntactic_type
289
373
 
290
374
  # The name of the selector.
291
375
  #
292
- # @return [Array<String, Sass::Script::Node>]
376
+ # @return [Array<String, Sass::Script::Tree::Node>]
293
377
  attr_reader :name
294
378
 
295
379
  # The argument to the selector,
@@ -299,37 +383,50 @@ module Sass
299
383
  # Note that this should not include SassScript nodes
300
384
  # after resolution has taken place.
301
385
  #
302
- # @return [Array<String, Sass::Script::Node>, nil]
386
+ # @return [Array<String, Sass::Script::Tree::Node>, nil]
303
387
  attr_reader :arg
304
388
 
305
389
  # @param type [Symbol] See \{#type}
306
- # @param name [Array<String, Sass::Script::Node>] The name of the selector
307
- # @param arg [nil, Array<String, Sass::Script::Node>] The argument to the selector,
390
+ # @param name [Array<String, Sass::Script::Tree::Node>] The name of the selector
391
+ # @param arg [nil, Array<String, Sass::Script::Tree::Node>] The argument to the selector,
308
392
  # or nil if no argument was given
309
393
  def initialize(type, name, arg)
310
- @type = type
394
+ @syntactic_type = type
311
395
  @name = name
312
396
  @arg = arg
313
397
  end
314
398
 
399
+ # The type of the selector. `:class` if this is a pseudoclass selector,
400
+ # `:element` if it's a pseudoelement.
401
+ #
402
+ # @return [Symbol]
403
+ def type
404
+ ACTUALLY_ELEMENTS.include?(name.first) ? :element : syntactic_type
405
+ end
406
+
315
407
  # @see Selector#to_a
316
408
  def to_a
317
- res = [@type == :class ? ":" : "::"] + @name
409
+ res = [syntactic_type == :class ? ":" : "::"] + @name
318
410
  (res << "(").concat(Sass::Util.strip_string_array(@arg)) << ")" if @arg
319
411
  res
320
412
  end
321
413
 
322
- # Returns `nil` if this is a pseudoclass selector
323
- # and `sels` contains a pseudoclass selector different than this one.
414
+ # Returns `nil` if this is a pseudoelement selector
415
+ # and `sels` contains a pseudoelement selector different than this one.
324
416
  #
325
417
  # @see Selector#unify
326
418
  def unify(sels)
327
419
  return if type == :element && sels.any? do |sel|
328
420
  sel.is_a?(Pseudo) && sel.type == :element &&
329
- (sel.name != self.name || sel.arg != self.arg)
421
+ (sel.name != name || sel.arg != arg)
330
422
  end
331
423
  super
332
424
  end
425
+
426
+ # @see AbstractSequence#specificity
427
+ def specificity
428
+ type == :class ? SPECIFICITY_BASE : 1
429
+ end
333
430
  end
334
431
 
335
432
  # A pseudoclass selector whose argument is itself a selector
@@ -345,8 +442,8 @@ module Sass
345
442
  # @return [Selector::Sequence]
346
443
  attr_reader :selector
347
444
 
348
- # @param [String] The name of the pseudoclass
349
- # @param [Selector::Sequence] The selector argument
445
+ # @param name [String] The name of the pseudoclass
446
+ # @param selector [Selector::CommaSequence] The selector argument
350
447
  def initialize(name, selector)
351
448
  @name = name
352
449
  @selector = selector
@@ -356,6 +453,11 @@ module Sass
356
453
  def to_a
357
454
  [":", @name, "("] + @selector.to_a + [")"]
358
455
  end
456
+
457
+ # @see AbstractSequence#specificity
458
+ def specificity
459
+ SPECIFICITY_BASE
460
+ end
359
461
  end
360
462
  end
361
463
  end
data/lib/sass/shared.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'strscan'
2
-
3
1
  module Sass
4
2
  # This module contains functionality that's shared between Haml and Sass.
5
3
  module Shared
@@ -16,8 +14,8 @@ module Sass
16
14
  # @yieldparam scan [StringScanner] The scanner scanning through the string
17
15
  # @return [String] The text remaining in the scanner after all `#{`s have been processed
18
16
  def handle_interpolation(str)
19
- scan = StringScanner.new(str)
20
- yield scan while scan.scan(/(.*?)(\\*)\#\{/)
17
+ scan = Sass::Util::MultibyteStringScanner.new(str)
18
+ yield scan while scan.scan(/(.*?)(\\*)\#\{/m)
21
19
  scan.rest
22
20
  end
23
21
 
@@ -40,7 +38,7 @@ module Sass
40
38
  # `["Foo (Bar (Baz bang) bop)", " (Bang (bop bip))"]` in the example above.
41
39
  def balance(scanner, start, finish, count = 0)
42
40
  str = ''
43
- scanner = StringScanner.new(scanner) unless scanner.is_a? StringScanner
41
+ scanner = Sass::Util::MultibyteStringScanner.new(scanner) unless scanner.is_a? StringScanner
44
42
  regexp = Regexp.new("(.*?)[\\#{start.chr}\\#{finish.chr}]", Regexp::MULTILINE)
45
43
  while scanner.scan(regexp)
46
44
  str << scanner.matched