oreorenasass 3.4.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 (268) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +11 -0
  3. data/CONTRIBUTING +3 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +221 -0
  6. data/Rakefile +370 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/sass +13 -0
  10. data/bin/sass-convert +12 -0
  11. data/bin/scss +13 -0
  12. data/extra/update_watch.rb +13 -0
  13. data/init.rb +18 -0
  14. data/lib/sass/cache_stores/base.rb +88 -0
  15. data/lib/sass/cache_stores/chain.rb +34 -0
  16. data/lib/sass/cache_stores/filesystem.rb +60 -0
  17. data/lib/sass/cache_stores/memory.rb +47 -0
  18. data/lib/sass/cache_stores/null.rb +25 -0
  19. data/lib/sass/cache_stores.rb +15 -0
  20. data/lib/sass/callbacks.rb +67 -0
  21. data/lib/sass/css.rb +407 -0
  22. data/lib/sass/engine.rb +1181 -0
  23. data/lib/sass/environment.rb +191 -0
  24. data/lib/sass/error.rb +198 -0
  25. data/lib/sass/exec/base.rb +187 -0
  26. data/lib/sass/exec/sass_convert.rb +264 -0
  27. data/lib/sass/exec/sass_scss.rb +424 -0
  28. data/lib/sass/exec.rb +9 -0
  29. data/lib/sass/features.rb +47 -0
  30. data/lib/sass/importers/base.rb +182 -0
  31. data/lib/sass/importers/filesystem.rb +211 -0
  32. data/lib/sass/importers.rb +22 -0
  33. data/lib/sass/logger/base.rb +30 -0
  34. data/lib/sass/logger/log_level.rb +45 -0
  35. data/lib/sass/logger.rb +12 -0
  36. data/lib/sass/media.rb +210 -0
  37. data/lib/sass/plugin/compiler.rb +565 -0
  38. data/lib/sass/plugin/configuration.rb +118 -0
  39. data/lib/sass/plugin/generic.rb +15 -0
  40. data/lib/sass/plugin/merb.rb +48 -0
  41. data/lib/sass/plugin/rack.rb +60 -0
  42. data/lib/sass/plugin/rails.rb +47 -0
  43. data/lib/sass/plugin/staleness_checker.rb +199 -0
  44. data/lib/sass/plugin.rb +133 -0
  45. data/lib/sass/railtie.rb +10 -0
  46. data/lib/sass/repl.rb +57 -0
  47. data/lib/sass/root.rb +7 -0
  48. data/lib/sass/script/css_lexer.rb +33 -0
  49. data/lib/sass/script/css_parser.rb +34 -0
  50. data/lib/sass/script/functions.rb +2626 -0
  51. data/lib/sass/script/lexer.rb +449 -0
  52. data/lib/sass/script/parser.rb +637 -0
  53. data/lib/sass/script/tree/funcall.rb +306 -0
  54. data/lib/sass/script/tree/interpolation.rb +118 -0
  55. data/lib/sass/script/tree/list_literal.rb +77 -0
  56. data/lib/sass/script/tree/literal.rb +45 -0
  57. data/lib/sass/script/tree/map_literal.rb +64 -0
  58. data/lib/sass/script/tree/node.rb +109 -0
  59. data/lib/sass/script/tree/operation.rb +103 -0
  60. data/lib/sass/script/tree/selector.rb +26 -0
  61. data/lib/sass/script/tree/string_interpolation.rb +104 -0
  62. data/lib/sass/script/tree/unary_operation.rb +69 -0
  63. data/lib/sass/script/tree/variable.rb +57 -0
  64. data/lib/sass/script/tree.rb +16 -0
  65. data/lib/sass/script/value/arg_list.rb +36 -0
  66. data/lib/sass/script/value/base.rb +240 -0
  67. data/lib/sass/script/value/bool.rb +35 -0
  68. data/lib/sass/script/value/color.rb +680 -0
  69. data/lib/sass/script/value/helpers.rb +262 -0
  70. data/lib/sass/script/value/list.rb +113 -0
  71. data/lib/sass/script/value/map.rb +70 -0
  72. data/lib/sass/script/value/null.rb +44 -0
  73. data/lib/sass/script/value/number.rb +530 -0
  74. data/lib/sass/script/value/string.rb +97 -0
  75. data/lib/sass/script/value.rb +11 -0
  76. data/lib/sass/script.rb +66 -0
  77. data/lib/sass/scss/css_parser.rb +42 -0
  78. data/lib/sass/scss/parser.rb +1209 -0
  79. data/lib/sass/scss/rx.rb +141 -0
  80. data/lib/sass/scss/script_lexer.rb +15 -0
  81. data/lib/sass/scss/script_parser.rb +25 -0
  82. data/lib/sass/scss/static_parser.rb +368 -0
  83. data/lib/sass/scss.rb +16 -0
  84. data/lib/sass/selector/abstract_sequence.rb +109 -0
  85. data/lib/sass/selector/comma_sequence.rb +175 -0
  86. data/lib/sass/selector/pseudo.rb +256 -0
  87. data/lib/sass/selector/sequence.rb +600 -0
  88. data/lib/sass/selector/simple.rb +117 -0
  89. data/lib/sass/selector/simple_sequence.rb +325 -0
  90. data/lib/sass/selector.rb +326 -0
  91. data/lib/sass/shared.rb +76 -0
  92. data/lib/sass/source/map.rb +210 -0
  93. data/lib/sass/source/position.rb +39 -0
  94. data/lib/sass/source/range.rb +41 -0
  95. data/lib/sass/stack.rb +120 -0
  96. data/lib/sass/supports.rb +227 -0
  97. data/lib/sass/tree/at_root_node.rb +83 -0
  98. data/lib/sass/tree/charset_node.rb +22 -0
  99. data/lib/sass/tree/comment_node.rb +82 -0
  100. data/lib/sass/tree/content_node.rb +9 -0
  101. data/lib/sass/tree/css_import_node.rb +60 -0
  102. data/lib/sass/tree/debug_node.rb +18 -0
  103. data/lib/sass/tree/directive_node.rb +59 -0
  104. data/lib/sass/tree/each_node.rb +24 -0
  105. data/lib/sass/tree/error_node.rb +18 -0
  106. data/lib/sass/tree/extend_node.rb +43 -0
  107. data/lib/sass/tree/for_node.rb +36 -0
  108. data/lib/sass/tree/function_node.rb +39 -0
  109. data/lib/sass/tree/if_node.rb +52 -0
  110. data/lib/sass/tree/import_node.rb +74 -0
  111. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  112. data/lib/sass/tree/media_node.rb +48 -0
  113. data/lib/sass/tree/mixin_def_node.rb +38 -0
  114. data/lib/sass/tree/mixin_node.rb +52 -0
  115. data/lib/sass/tree/node.rb +238 -0
  116. data/lib/sass/tree/prop_node.rb +171 -0
  117. data/lib/sass/tree/return_node.rb +19 -0
  118. data/lib/sass/tree/root_node.rb +44 -0
  119. data/lib/sass/tree/rule_node.rb +145 -0
  120. data/lib/sass/tree/supports_node.rb +38 -0
  121. data/lib/sass/tree/trace_node.rb +33 -0
  122. data/lib/sass/tree/variable_node.rb +36 -0
  123. data/lib/sass/tree/visitors/base.rb +72 -0
  124. data/lib/sass/tree/visitors/check_nesting.rb +177 -0
  125. data/lib/sass/tree/visitors/convert.rb +334 -0
  126. data/lib/sass/tree/visitors/cssize.rb +369 -0
  127. data/lib/sass/tree/visitors/deep_copy.rb +107 -0
  128. data/lib/sass/tree/visitors/extend.rb +68 -0
  129. data/lib/sass/tree/visitors/perform.rb +539 -0
  130. data/lib/sass/tree/visitors/set_options.rb +139 -0
  131. data/lib/sass/tree/visitors/to_css.rb +381 -0
  132. data/lib/sass/tree/warn_node.rb +18 -0
  133. data/lib/sass/tree/while_node.rb +18 -0
  134. data/lib/sass/util/cross_platform_random.rb +19 -0
  135. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  136. data/lib/sass/util/normalized_map.rb +130 -0
  137. data/lib/sass/util/ordered_hash.rb +192 -0
  138. data/lib/sass/util/subset_map.rb +110 -0
  139. data/lib/sass/util/test.rb +9 -0
  140. data/lib/sass/util.rb +1318 -0
  141. data/lib/sass/version.rb +124 -0
  142. data/lib/sass.rb +102 -0
  143. data/rails/init.rb +1 -0
  144. data/test/sass/cache_test.rb +131 -0
  145. data/test/sass/callbacks_test.rb +61 -0
  146. data/test/sass/compiler_test.rb +232 -0
  147. data/test/sass/conversion_test.rb +2054 -0
  148. data/test/sass/css2sass_test.rb +477 -0
  149. data/test/sass/data/hsl-rgb.txt +319 -0
  150. data/test/sass/encoding_test.rb +219 -0
  151. data/test/sass/engine_test.rb +3301 -0
  152. data/test/sass/exec_test.rb +86 -0
  153. data/test/sass/extend_test.rb +1661 -0
  154. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  155. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  156. data/test/sass/functions_test.rb +1926 -0
  157. data/test/sass/importer_test.rb +412 -0
  158. data/test/sass/logger_test.rb +58 -0
  159. data/test/sass/mock_importer.rb +49 -0
  160. data/test/sass/more_results/more1.css +9 -0
  161. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  162. data/test/sass/more_results/more_import.css +29 -0
  163. data/test/sass/more_templates/_more_partial.sass +2 -0
  164. data/test/sass/more_templates/more1.sass +23 -0
  165. data/test/sass/more_templates/more_import.sass +11 -0
  166. data/test/sass/plugin_test.rb +554 -0
  167. data/test/sass/results/alt.css +4 -0
  168. data/test/sass/results/basic.css +9 -0
  169. data/test/sass/results/cached_import_option.css +3 -0
  170. data/test/sass/results/compact.css +5 -0
  171. data/test/sass/results/complex.css +86 -0
  172. data/test/sass/results/compressed.css +1 -0
  173. data/test/sass/results/expanded.css +19 -0
  174. data/test/sass/results/filename_fn.css +3 -0
  175. data/test/sass/results/if.css +3 -0
  176. data/test/sass/results/import.css +31 -0
  177. data/test/sass/results/import_charset.css +5 -0
  178. data/test/sass/results/import_charset_1_8.css +5 -0
  179. data/test/sass/results/import_charset_ibm866.css +5 -0
  180. data/test/sass/results/import_content.css +1 -0
  181. data/test/sass/results/line_numbers.css +49 -0
  182. data/test/sass/results/mixins.css +95 -0
  183. data/test/sass/results/multiline.css +24 -0
  184. data/test/sass/results/nested.css +22 -0
  185. data/test/sass/results/options.css +1 -0
  186. data/test/sass/results/parent_ref.css +13 -0
  187. data/test/sass/results/script.css +16 -0
  188. data/test/sass/results/scss_import.css +31 -0
  189. data/test/sass/results/scss_importee.css +2 -0
  190. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  191. data/test/sass/results/subdir/subdir.css +3 -0
  192. data/test/sass/results/units.css +11 -0
  193. data/test/sass/results/warn.css +0 -0
  194. data/test/sass/results/warn_imported.css +0 -0
  195. data/test/sass/script_conversion_test.rb +328 -0
  196. data/test/sass/script_test.rb +1054 -0
  197. data/test/sass/scss/css_test.rb +1215 -0
  198. data/test/sass/scss/rx_test.rb +156 -0
  199. data/test/sass/scss/scss_test.rb +3900 -0
  200. data/test/sass/scss/test_helper.rb +37 -0
  201. data/test/sass/source_map_test.rb +977 -0
  202. data/test/sass/superselector_test.rb +191 -0
  203. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  204. data/test/sass/templates/_double_import_loop2.sass +1 -0
  205. data/test/sass/templates/_filename_fn_import.scss +11 -0
  206. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  207. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  208. data/test/sass/templates/_imported_content.sass +3 -0
  209. data/test/sass/templates/_partial.sass +2 -0
  210. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  211. data/test/sass/templates/alt.sass +16 -0
  212. data/test/sass/templates/basic.sass +23 -0
  213. data/test/sass/templates/bork1.sass +2 -0
  214. data/test/sass/templates/bork2.sass +2 -0
  215. data/test/sass/templates/bork3.sass +2 -0
  216. data/test/sass/templates/bork4.sass +2 -0
  217. data/test/sass/templates/bork5.sass +3 -0
  218. data/test/sass/templates/cached_import_option.scss +3 -0
  219. data/test/sass/templates/compact.sass +17 -0
  220. data/test/sass/templates/complex.sass +305 -0
  221. data/test/sass/templates/compressed.sass +15 -0
  222. data/test/sass/templates/double_import_loop1.sass +1 -0
  223. data/test/sass/templates/expanded.sass +17 -0
  224. data/test/sass/templates/filename_fn.scss +18 -0
  225. data/test/sass/templates/if.sass +11 -0
  226. data/test/sass/templates/import.sass +12 -0
  227. data/test/sass/templates/import_charset.sass +9 -0
  228. data/test/sass/templates/import_charset_1_8.sass +6 -0
  229. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  230. data/test/sass/templates/import_content.sass +4 -0
  231. data/test/sass/templates/importee.less +2 -0
  232. data/test/sass/templates/importee.sass +19 -0
  233. data/test/sass/templates/line_numbers.sass +13 -0
  234. data/test/sass/templates/mixin_bork.sass +5 -0
  235. data/test/sass/templates/mixins.sass +76 -0
  236. data/test/sass/templates/multiline.sass +20 -0
  237. data/test/sass/templates/nested.sass +25 -0
  238. data/test/sass/templates/nested_bork1.sass +2 -0
  239. data/test/sass/templates/nested_bork2.sass +2 -0
  240. data/test/sass/templates/nested_bork3.sass +2 -0
  241. data/test/sass/templates/nested_bork4.sass +2 -0
  242. data/test/sass/templates/nested_import.sass +2 -0
  243. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  244. data/test/sass/templates/options.sass +2 -0
  245. data/test/sass/templates/parent_ref.sass +25 -0
  246. data/test/sass/templates/same_name_different_ext.sass +2 -0
  247. data/test/sass/templates/same_name_different_ext.scss +1 -0
  248. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  249. data/test/sass/templates/script.sass +101 -0
  250. data/test/sass/templates/scss_import.scss +12 -0
  251. data/test/sass/templates/scss_importee.scss +1 -0
  252. data/test/sass/templates/single_import_loop.sass +1 -0
  253. data/test/sass/templates/subdir/import_up1.scss +1 -0
  254. data/test/sass/templates/subdir/import_up2.scss +1 -0
  255. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  256. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  257. data/test/sass/templates/subdir/subdir.sass +6 -0
  258. data/test/sass/templates/units.sass +11 -0
  259. data/test/sass/templates/warn.sass +3 -0
  260. data/test/sass/templates/warn_imported.sass +4 -0
  261. data/test/sass/test_helper.rb +8 -0
  262. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  263. data/test/sass/util/normalized_map_test.rb +51 -0
  264. data/test/sass/util/subset_map_test.rb +91 -0
  265. data/test/sass/util_test.rb +467 -0
  266. data/test/sass/value_helpers_test.rb +179 -0
  267. data/test/test_helper.rb +109 -0
  268. metadata +386 -0
data/lib/sass/scss.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'sass/scss/rx'
2
+ require 'sass/scss/script_lexer'
3
+ require 'sass/scss/script_parser'
4
+ require 'sass/scss/parser'
5
+ require 'sass/scss/static_parser'
6
+ require 'sass/scss/css_parser'
7
+
8
+ module Sass
9
+ # SCSS is the CSS syntax for Sass.
10
+ # It parses into the same syntax tree as Sass,
11
+ # and generates the same sort of output CSS.
12
+ #
13
+ # This module contains code for the parsing of SCSS.
14
+ # The evaluation is handled by the broader {Sass} module.
15
+ module SCSS; end
16
+ end
@@ -0,0 +1,109 @@
1
+ module Sass
2
+ module Selector
3
+ # The abstract parent class of the various selector sequence classes.
4
+ #
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_s`
7
+ # method that returns the string representation of the selector.
8
+ class AbstractSequence
9
+ # The line of the Sass template on which this selector was declared.
10
+ #
11
+ # @return [Fixnum]
12
+ attr_reader :line
13
+
14
+ # The name of the file in which this selector was declared.
15
+ #
16
+ # @return [String, nil]
17
+ attr_reader :filename
18
+
19
+ # Sets the line of the Sass template on which this selector was declared.
20
+ # This also sets the line for all child selectors.
21
+ #
22
+ # @param line [Fixnum]
23
+ # @return [Fixnum]
24
+ def line=(line)
25
+ members.each {|m| m.line = line}
26
+ @line = line
27
+ end
28
+
29
+ # Sets the name of the file in which this selector was declared,
30
+ # or `nil` if it was not declared in a file (e.g. on stdin).
31
+ # This also sets the filename for all child selectors.
32
+ #
33
+ # @param filename [String, nil]
34
+ # @return [String, nil]
35
+ def filename=(filename)
36
+ members.each {|m| m.filename = filename}
37
+ @filename = filename
38
+ end
39
+
40
+ # Returns a hash code for this sequence.
41
+ #
42
+ # Subclasses should define `#_hash` rather than overriding this method,
43
+ # which automatically handles memoizing the result.
44
+ #
45
+ # @return [Fixnum]
46
+ def hash
47
+ @_hash ||= _hash
48
+ end
49
+
50
+ # Checks equality between this and another object.
51
+ #
52
+ # Subclasses should define `#_eql?` rather than overriding this method,
53
+ # which handles checking class equality and hash equality.
54
+ #
55
+ # @param other [Object] The object to test equality against
56
+ # @return [Boolean] Whether or not this is equal to `other`
57
+ def eql?(other)
58
+ other.class == self.class && other.hash == hash && _eql?(other)
59
+ end
60
+ alias_method :==, :eql?
61
+
62
+ # Whether or not this selector sequence contains a placeholder selector.
63
+ # Checks recursively.
64
+ def has_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
70
+ end
71
+
72
+ # Returns the selector string.
73
+ #
74
+ # @return [String]
75
+ def to_s
76
+ Sass::Util.abstract(self)
77
+ end
78
+
79
+ # Returns the specificity of the selector.
80
+ #
81
+ # The base is given by {Sass::Selector::SPECIFICITY_BASE}. This can be a
82
+ # number or a range representing possible specificities.
83
+ #
84
+ # @return [Fixnum, Range]
85
+ def specificity
86
+ _specificity(members)
87
+ end
88
+
89
+ protected
90
+
91
+ def _specificity(arr)
92
+ min = 0
93
+ max = 0
94
+ arr.each do |m|
95
+ next if m.is_a?(String)
96
+ spec = m.specificity
97
+ if spec.is_a?(Range)
98
+ min += spec.begin
99
+ max += spec.end
100
+ else
101
+ min += spec
102
+ max += spec
103
+ end
104
+ end
105
+ min == max ? min : (min..max)
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,175 @@
1
+ module Sass
2
+ module Selector
3
+ # A comma-separated sequence of selectors.
4
+ class CommaSequence < AbstractSequence
5
+ # The comma-separated selector sequences
6
+ # represented by this class.
7
+ #
8
+ # @return [Array<Sequence>]
9
+ attr_reader :members
10
+
11
+ # @param seqs [Array<Sequence>] See \{#members}
12
+ def initialize(seqs)
13
+ @members = seqs
14
+ end
15
+
16
+ # Resolves the {Parent} selectors within this selector
17
+ # by replacing them with the given parent selector,
18
+ # handling commas appropriately.
19
+ #
20
+ # @param super_cseq [CommaSequence] The parent selector
21
+ # @param implicit_parent [Boolean] Whether the the parent
22
+ # selector should automatically be prepended to the resolved
23
+ # selector if it contains no parent refs.
24
+ # @return [CommaSequence] This selector, with parent references resolved
25
+ # @raise [Sass::SyntaxError] If a parent selector is invalid
26
+ def resolve_parent_refs(super_cseq, implicit_parent = true)
27
+ 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
34
+ raise Sass::SyntaxError.new(
35
+ "Base-level rules cannot contain the parent-selector-referencing character '&'.")
36
+ end
37
+ return self
38
+ end
39
+
40
+ CommaSequence.new(Sass::Util.flatten_vertically(@members.map do |seq|
41
+ seq.resolve_parent_refs(super_cseq, implicit_parent).members
42
+ end))
43
+ end
44
+
45
+ # Non-destrucively extends this selector with the extensions specified in a hash
46
+ # (which should come from {Sass::Tree::Visitors::Cssize}).
47
+ #
48
+ # @todo Link this to the reference documentation on `@extend`
49
+ # when such a thing exists.
50
+ #
51
+ # @param extends [Sass::Util::SubsetMap{Selector::Simple =>
52
+ # Sass::Tree::Visitors::Cssize::Extend}]
53
+ # The extensions to perform on this selector
54
+ # @param parent_directives [Array<Sass::Tree::DirectiveNode>]
55
+ # The directives containing this selector.
56
+ # @param replace [Boolean]
57
+ # Whether to replace the original selector entirely or include
58
+ # it in the result.
59
+ # @param seen [Set<Array<Selector::Simple>>]
60
+ # The set of simple sequences that are currently being replaced.
61
+ # @param original [Boolean]
62
+ # Whether this is the original selector being extended, as opposed to
63
+ # the result of a previous extension that's being re-extended.
64
+ # @return [CommaSequence] A copy of this selector,
65
+ # with extensions made according to `extends`
66
+ def do_extend(extends, parent_directives = [], replace = false, seen = Set.new,
67
+ original = true)
68
+ CommaSequence.new(members.map do |seq|
69
+ seq.do_extend(extends, parent_directives, replace, seen, original)
70
+ end.flatten)
71
+ end
72
+
73
+ # Returns whether or not this selector matches all elements
74
+ # that the given selector matches (as well as possibly more).
75
+ #
76
+ # @example
77
+ # (.foo).superselector?(.foo.bar) #=> true
78
+ # (.foo).superselector?(.bar) #=> false
79
+ # @param cseq [CommaSequence]
80
+ # @return [Boolean]
81
+ def superselector?(cseq)
82
+ cseq.members.all? {|seq1| members.any? {|seq2| seq2.superselector?(seq1)}}
83
+ end
84
+
85
+ # Populates a subset map that can then be used to extend
86
+ # selectors. This registers an extension with this selector as
87
+ # the extender and `extendee` as the extendee.
88
+ #
89
+ # @param extends [Sass::Util::SubsetMap{Selector::Simple =>
90
+ # Sass::Tree::Visitors::Cssize::Extend}]
91
+ # The subset map representing the extensions to perform.
92
+ # @param extendee [CommaSequence] The selector being extended.
93
+ # @param extend_node [Sass::Tree::ExtendNode]
94
+ # The node that caused this extension.
95
+ # @param parent_directives [Array<Sass::Tree::DirectiveNode>]
96
+ # The parent directives containing `extend_node`.
97
+ # @raise [Sass::SyntaxError] if this extension is invalid.
98
+ def populate_extends(extends, extendee, extend_node = nil, parent_directives = [])
99
+ extendee.members.each do |seq|
100
+ if seq.members.size > 1
101
+ raise Sass::SyntaxError.new("Can't extend #{seq}: can't extend nested selectors")
102
+ end
103
+
104
+ sseq = seq.members.first
105
+ if !sseq.is_a?(Sass::Selector::SimpleSequence)
106
+ raise Sass::SyntaxError.new("Can't extend #{seq}: invalid selector")
107
+ elsif sseq.members.any? {|ss| ss.is_a?(Sass::Selector::Parent)}
108
+ raise Sass::SyntaxError.new("Can't extend #{seq}: can't extend parent selectors")
109
+ end
110
+
111
+ sel = sseq.members
112
+ members.each do |member|
113
+ unless member.members.last.is_a?(Sass::Selector::SimpleSequence)
114
+ raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
115
+ end
116
+
117
+ extends[sel] = Sass::Tree::Visitors::Cssize::Extend.new(
118
+ member, sel, extend_node, parent_directives, :not_found)
119
+ end
120
+ end
121
+ end
122
+
123
+ # Unifies this with another comma selector to produce a selector
124
+ # that matches (a subset of) the intersection of the two inputs.
125
+ #
126
+ # @param other [CommaSequence]
127
+ # @return [CommaSequence, nil] The unified selector, or nil if unification failed.
128
+ # @raise [Sass::SyntaxError] If this selector cannot be unified.
129
+ # This will only ever occur when a dynamic selector,
130
+ # such as {Parent} or {Interpolation}, is used in unification.
131
+ # Since these selectors should be resolved
132
+ # by the time extension and unification happen,
133
+ # this exception will only ever be raised as a result of programmer error
134
+ def unify(other)
135
+ results = members.map {|seq1| other.members.map {|seq2| seq1.unify(seq2)}}.flatten.compact
136
+ results.empty? ? nil : CommaSequence.new(results.map {|cseq| cseq.members}.flatten)
137
+ end
138
+
139
+ # Returns a SassScript representation of this selector.
140
+ #
141
+ # @return [Sass::Script::Value::List]
142
+ def to_sass_script
143
+ Sass::Script::Value::List.new(members.map do |seq|
144
+ Sass::Script::Value::List.new(seq.members.map do |component|
145
+ next if component == "\n"
146
+ Sass::Script::Value::String.new(component.to_s)
147
+ end.compact, :space)
148
+ end, :comma)
149
+ end
150
+
151
+ # Returns a string representation of the sequence.
152
+ # This is basically the selector string.
153
+ #
154
+ # @return [String]
155
+ def inspect
156
+ members.map {|m| m.inspect}.join(", ")
157
+ end
158
+
159
+ # @see AbstractSequence#to_s
160
+ def to_s
161
+ @members.join(", ").gsub(", \n", ",\n")
162
+ end
163
+
164
+ private
165
+
166
+ def _hash
167
+ members.hash
168
+ end
169
+
170
+ def _eql?(other)
171
+ other.class == self.class && other.members.eql?(members)
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,256 @@
1
+ module Sass
2
+ module Selector
3
+ # A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`)
4
+ # selector. It can have arguments (e.g. `:nth-child(2n+1)`) which can
5
+ # contain selectors (e.g. `:nth-child(2n+1 of .foo)`).
6
+ class Pseudo < Simple
7
+ # Some pseudo-class-syntax selectors are actually considered
8
+ # pseudo-elements and must be treated differently. This is a list of such
9
+ # selectors.
10
+ #
11
+ # @return [Set<String>]
12
+ ACTUALLY_ELEMENTS = %w[after before first-line first-letter].to_set
13
+
14
+ # Like \{#type}, but returns the type of selector this looks like, rather
15
+ # than the type it is semantically. This only differs from type for
16
+ # selectors in \{ACTUALLY\_ELEMENTS}.
17
+ #
18
+ # @return [Symbol]
19
+ attr_reader :syntactic_type
20
+
21
+ # The name of the selector.
22
+ #
23
+ # @return [String]
24
+ attr_reader :name
25
+
26
+ # The argument to the selector,
27
+ # or `nil` if no argument was given.
28
+ #
29
+ # @return [String, nil]
30
+ attr_reader :arg
31
+
32
+ # The selector argument, or `nil` if no selector exists.
33
+ #
34
+ # If this and \{#arg\} are both set, \{#arg\} is considered a non-selector
35
+ # prefix.
36
+ #
37
+ # @return [CommaSequence]
38
+ attr_reader :selector
39
+
40
+ # @param syntactic_type [Symbol] See \{#syntactic_type}
41
+ # @param name [String] See \{#name}
42
+ # @param arg [nil, String] See \{#arg}
43
+ # @param selector [nil, CommaSequence] See \{#selector}
44
+ def initialize(syntactic_type, name, arg, selector)
45
+ @syntactic_type = syntactic_type
46
+ @name = name
47
+ @arg = arg
48
+ @selector = selector
49
+ end
50
+
51
+ # Returns a copy of this with \{#selector} set to \{#new\_selector}.
52
+ #
53
+ # @param new_selector [CommaSequence]
54
+ # @return [CommaSequence]
55
+ def with_selector(new_selector)
56
+ Pseudo.new(syntactic_type, name, arg, CommaSequence.new(new_selector.members.map do |seq|
57
+ next seq unless seq.members.length == 1
58
+ sseq = seq.members.first
59
+ next seq unless sseq.is_a?(SimpleSequence) && sseq.members.length == 1
60
+ sel = sseq.members.first
61
+ next seq unless sel.is_a?(Pseudo) && sel.selector
62
+
63
+ case normalized_name
64
+ when 'not'
65
+ # In theory, if there's a nested :not its contents should be
66
+ # unified with the return value. For example, if :not(.foo)
67
+ # extends .bar, :not(.bar) should become .foo:not(.bar). However,
68
+ # this is a narrow edge case and supporting it properly would make
69
+ # this code and the code calling it a lot more complicated, so
70
+ # it's not supported for now.
71
+ next [] unless sel.normalized_name == 'matches'
72
+ sel.selector.members
73
+ when 'matches', 'any', 'current', 'nth-child', 'nth-last-child'
74
+ # As above, we could theoretically support :not within :matches, but
75
+ # doing so would require this method and its callers to handle much
76
+ # more complex cases that likely aren't worth the pain.
77
+ next [] unless sel.name == name && sel.arg == arg
78
+ sel.selector.members
79
+ when 'has', 'host', 'host-context'
80
+ # We can't expand nested selectors here, because each layer adds an
81
+ # additional layer of semantics. For example, `:has(:has(img))`
82
+ # doesn't match `<div><img></div>` but `:has(img)` does.
83
+ sel
84
+ else
85
+ []
86
+ end
87
+ end.flatten))
88
+ end
89
+
90
+ # The type of the selector. `:class` if this is a pseudoclass selector,
91
+ # `:element` if it's a pseudoelement.
92
+ #
93
+ # @return [Symbol]
94
+ def type
95
+ ACTUALLY_ELEMENTS.include?(normalized_name) ? :element : syntactic_type
96
+ end
97
+
98
+ # Like \{#name\}, but without any vendor prefix.
99
+ #
100
+ # @return [String]
101
+ def normalized_name
102
+ @normalized_name ||= name.gsub(/^-[a-zA-Z0-9]+-/, '')
103
+ end
104
+
105
+ # @see Selector#to_s
106
+ def to_s
107
+ res = (syntactic_type == :class ? ":" : "::") + @name
108
+ if @arg || @selector
109
+ res << "("
110
+ res << @arg.strip if @arg
111
+ res << " " if @arg && @selector
112
+ res << @selector.to_s if @selector
113
+ res << ")"
114
+ end
115
+ res
116
+ end
117
+
118
+ # Returns `nil` if this is a pseudoelement selector
119
+ # and `sels` contains a pseudoelement selector different than this one.
120
+ #
121
+ # @see SimpleSequence#unify
122
+ def unify(sels)
123
+ return if type == :element && sels.any? do |sel|
124
+ sel.is_a?(Pseudo) && sel.type == :element &&
125
+ (sel.name != name || sel.arg != arg || sel.selector != selector)
126
+ end
127
+ super
128
+ end
129
+
130
+ # Returns whether or not this selector matches all elements
131
+ # that the given selector matches (as well as possibly more).
132
+ #
133
+ # @example
134
+ # (.foo).superselector?(.foo.bar) #=> true
135
+ # (.foo).superselector?(.bar) #=> false
136
+ # @param their_sseq [SimpleSequence]
137
+ # @param parents [Array<SimpleSequence, String>] The parent selectors of `their_sseq`, if any.
138
+ # @return [Boolean]
139
+ def superselector?(their_sseq, parents = [])
140
+ case normalized_name
141
+ when 'matches', 'any'
142
+ # :matches can be a superselector of another selector in one of two
143
+ # ways. Either its constituent selectors can be a superset of those of
144
+ # another :matches in the other selector, or any of its constituent
145
+ # selectors can individually be a superselector of the other selector.
146
+ (their_sseq.selector_pseudo_classes[normalized_name] || []).any? do |their_sel|
147
+ next false unless their_sel.is_a?(Pseudo)
148
+ next false unless their_sel.name == name
149
+ selector.superselector?(their_sel.selector)
150
+ end || selector.members.any? do |our_seq|
151
+ their_seq = Sequence.new(parents + [their_sseq])
152
+ our_seq.superselector?(their_seq)
153
+ end
154
+ when 'has', 'host', 'host-context'
155
+ # Like :matches, :has (et al) can be a superselector of another
156
+ # selector if its constituent selectors are a superset of those of
157
+ # another :has in the other selector. However, the :matches other case
158
+ # doesn't work, because :has refers to nested elements.
159
+ (their_sseq.selector_pseudo_classes[normalized_name] || []).any? do |their_sel|
160
+ next false unless their_sel.is_a?(Pseudo)
161
+ next false unless their_sel.name == name
162
+ selector.superselector?(their_sel.selector)
163
+ end
164
+ when 'not'
165
+ selector.members.all? do |our_seq|
166
+ their_sseq.members.any? do |their_sel|
167
+ if their_sel.is_a?(Element) || their_sel.is_a?(Id)
168
+ # `:not(a)` is a superselector of `h1` and `:not(#foo)` is a
169
+ # superselector of `#bar`.
170
+ our_sseq = our_seq.members.last
171
+ next false unless our_sseq.is_a?(SimpleSequence)
172
+ our_sseq.members.any? do |our_sel|
173
+ our_sel.class == their_sel.class && our_sel != their_sel
174
+ end
175
+ else
176
+ next false unless their_sel.is_a?(Pseudo)
177
+ next false unless their_sel.name == name
178
+ # :not(X) is a superselector of :not(Y) exactly when Y is a
179
+ # superselector of X.
180
+ their_sel.selector.superselector?(CommaSequence.new([our_seq]))
181
+ end
182
+ end
183
+ end
184
+ when 'current'
185
+ (their_sseq.selector_pseudo_classes['current'] || []).any? do |their_current|
186
+ next false if their_current.name != name
187
+ # Explicitly don't check for nested superselector relationships
188
+ # here. :current(.foo) isn't always a superselector of
189
+ # :current(.foo.bar), since it matches the *innermost* ancestor of
190
+ # the current element that matches the selector. For example:
191
+ #
192
+ # <div class="foo bar">
193
+ # <p class="foo">
194
+ # <span>current element</span>
195
+ # </p>
196
+ # </div>
197
+ #
198
+ # Here :current(.foo) would match the p element and *not* the div
199
+ # element, whereas :current(.foo.bar) would match the div and not
200
+ # the p.
201
+ selector == their_current.selector
202
+ end
203
+ when 'nth-child', 'nth-last-child'
204
+ their_sseq.members.any? do |their_sel|
205
+ # This misses a few edge cases. For example, `:nth-child(n of X)`
206
+ # is a superselector of `X`, and `:nth-child(2n of X)` is a
207
+ # superselector of `:nth-child(4n of X)`. These seem rare enough
208
+ # not to be worth worrying about, though.
209
+ next false unless their_sel.is_a?(Pseudo)
210
+ next false unless their_sel.name == name
211
+ next false unless their_sel.arg == arg
212
+ selector.superselector?(their_sel.selector)
213
+ end
214
+ else
215
+ throw "[BUG] Unknown selector pseudo class #{name}"
216
+ end
217
+ end
218
+
219
+ # @see AbstractSequence#specificity
220
+ def specificity
221
+ return 1 if type == :element
222
+ return SPECIFICITY_BASE unless selector
223
+ @specificity ||=
224
+ if normalized_name == 'not'
225
+ min = 0
226
+ max = 0
227
+ selector.members.each do |seq|
228
+ spec = seq.specificity
229
+ if spec.is_a?(Range)
230
+ min = Sass::Util.max(spec.begin, min)
231
+ max = Sass::Util.max(spec.end, max)
232
+ else
233
+ min = Sass::Util.max(spec, min)
234
+ max = Sass::Util.max(spec, max)
235
+ end
236
+ end
237
+ min == max ? max : (min..max)
238
+ else
239
+ min = 0
240
+ max = 0
241
+ selector.members.each do |seq|
242
+ spec = seq.specificity
243
+ if spec.is_a?(Range)
244
+ min = Sass::Util.min(spec.begin, min)
245
+ max = Sass::Util.max(spec.end, max)
246
+ else
247
+ min = Sass::Util.min(spec, min)
248
+ max = Sass::Util.max(spec, max)
249
+ end
250
+ end
251
+ min == max ? max : (min..max)
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end