xass 0.1.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 (242) 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 +201 -0
  6. data/Rakefile +349 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/push +13 -0
  10. data/bin/sass +13 -0
  11. data/bin/sass-convert +12 -0
  12. data/bin/scss +13 -0
  13. data/extra/update_watch.rb +13 -0
  14. data/init.rb +18 -0
  15. data/lib/sass/cache_stores/base.rb +88 -0
  16. data/lib/sass/cache_stores/chain.rb +33 -0
  17. data/lib/sass/cache_stores/filesystem.rb +64 -0
  18. data/lib/sass/cache_stores/memory.rb +47 -0
  19. data/lib/sass/cache_stores/null.rb +25 -0
  20. data/lib/sass/cache_stores.rb +15 -0
  21. data/lib/sass/callbacks.rb +66 -0
  22. data/lib/sass/css.rb +409 -0
  23. data/lib/sass/engine.rb +930 -0
  24. data/lib/sass/environment.rb +101 -0
  25. data/lib/sass/error.rb +201 -0
  26. data/lib/sass/exec.rb +707 -0
  27. data/lib/sass/importers/base.rb +139 -0
  28. data/lib/sass/importers/filesystem.rb +186 -0
  29. data/lib/sass/importers.rb +22 -0
  30. data/lib/sass/logger/base.rb +32 -0
  31. data/lib/sass/logger/log_level.rb +49 -0
  32. data/lib/sass/logger.rb +15 -0
  33. data/lib/sass/media.rb +213 -0
  34. data/lib/sass/plugin/compiler.rb +406 -0
  35. data/lib/sass/plugin/configuration.rb +123 -0
  36. data/lib/sass/plugin/generic.rb +15 -0
  37. data/lib/sass/plugin/merb.rb +48 -0
  38. data/lib/sass/plugin/rack.rb +60 -0
  39. data/lib/sass/plugin/rails.rb +47 -0
  40. data/lib/sass/plugin/staleness_checker.rb +199 -0
  41. data/lib/sass/plugin.rb +133 -0
  42. data/lib/sass/railtie.rb +10 -0
  43. data/lib/sass/repl.rb +57 -0
  44. data/lib/sass/root.rb +7 -0
  45. data/lib/sass/script/arg_list.rb +52 -0
  46. data/lib/sass/script/bool.rb +18 -0
  47. data/lib/sass/script/color.rb +606 -0
  48. data/lib/sass/script/css_lexer.rb +29 -0
  49. data/lib/sass/script/css_parser.rb +31 -0
  50. data/lib/sass/script/funcall.rb +245 -0
  51. data/lib/sass/script/functions.rb +1543 -0
  52. data/lib/sass/script/interpolation.rb +79 -0
  53. data/lib/sass/script/lexer.rb +345 -0
  54. data/lib/sass/script/list.rb +85 -0
  55. data/lib/sass/script/literal.rb +221 -0
  56. data/lib/sass/script/node.rb +99 -0
  57. data/lib/sass/script/null.rb +37 -0
  58. data/lib/sass/script/number.rb +453 -0
  59. data/lib/sass/script/operation.rb +110 -0
  60. data/lib/sass/script/parser.rb +502 -0
  61. data/lib/sass/script/string.rb +51 -0
  62. data/lib/sass/script/string_interpolation.rb +103 -0
  63. data/lib/sass/script/unary_operation.rb +69 -0
  64. data/lib/sass/script/variable.rb +58 -0
  65. data/lib/sass/script.rb +39 -0
  66. data/lib/sass/scss/css_parser.rb +36 -0
  67. data/lib/sass/scss/parser.rb +1180 -0
  68. data/lib/sass/scss/rx.rb +133 -0
  69. data/lib/sass/scss/script_lexer.rb +15 -0
  70. data/lib/sass/scss/script_parser.rb +25 -0
  71. data/lib/sass/scss/static_parser.rb +54 -0
  72. data/lib/sass/scss.rb +16 -0
  73. data/lib/sass/selector/abstract_sequence.rb +94 -0
  74. data/lib/sass/selector/comma_sequence.rb +92 -0
  75. data/lib/sass/selector/sequence.rb +507 -0
  76. data/lib/sass/selector/simple.rb +119 -0
  77. data/lib/sass/selector/simple_sequence.rb +215 -0
  78. data/lib/sass/selector.rb +452 -0
  79. data/lib/sass/shared.rb +76 -0
  80. data/lib/sass/supports.rb +229 -0
  81. data/lib/sass/tree/charset_node.rb +22 -0
  82. data/lib/sass/tree/comment_node.rb +82 -0
  83. data/lib/sass/tree/content_node.rb +9 -0
  84. data/lib/sass/tree/css_import_node.rb +60 -0
  85. data/lib/sass/tree/debug_node.rb +18 -0
  86. data/lib/sass/tree/directive_node.rb +42 -0
  87. data/lib/sass/tree/each_node.rb +24 -0
  88. data/lib/sass/tree/extend_node.rb +36 -0
  89. data/lib/sass/tree/for_node.rb +36 -0
  90. data/lib/sass/tree/function_node.rb +34 -0
  91. data/lib/sass/tree/if_node.rb +52 -0
  92. data/lib/sass/tree/import_node.rb +75 -0
  93. data/lib/sass/tree/media_node.rb +58 -0
  94. data/lib/sass/tree/mixin_def_node.rb +38 -0
  95. data/lib/sass/tree/mixin_node.rb +39 -0
  96. data/lib/sass/tree/node.rb +196 -0
  97. data/lib/sass/tree/prop_node.rb +152 -0
  98. data/lib/sass/tree/return_node.rb +18 -0
  99. data/lib/sass/tree/root_node.rb +78 -0
  100. data/lib/sass/tree/rule_node.rb +132 -0
  101. data/lib/sass/tree/supports_node.rb +51 -0
  102. data/lib/sass/tree/trace_node.rb +32 -0
  103. data/lib/sass/tree/variable_node.rb +30 -0
  104. data/lib/sass/tree/visitors/base.rb +75 -0
  105. data/lib/sass/tree/visitors/check_nesting.rb +147 -0
  106. data/lib/sass/tree/visitors/convert.rb +316 -0
  107. data/lib/sass/tree/visitors/cssize.rb +241 -0
  108. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  109. data/lib/sass/tree/visitors/extend.rb +68 -0
  110. data/lib/sass/tree/visitors/perform.rb +446 -0
  111. data/lib/sass/tree/visitors/set_options.rb +125 -0
  112. data/lib/sass/tree/visitors/to_css.rb +228 -0
  113. data/lib/sass/tree/warn_node.rb +18 -0
  114. data/lib/sass/tree/while_node.rb +18 -0
  115. data/lib/sass/util/multibyte_string_scanner.rb +155 -0
  116. data/lib/sass/util/subset_map.rb +109 -0
  117. data/lib/sass/util/test.rb +10 -0
  118. data/lib/sass/util.rb +948 -0
  119. data/lib/sass/version.rb +126 -0
  120. data/lib/sass.rb +95 -0
  121. data/rails/init.rb +1 -0
  122. data/test/Gemfile +3 -0
  123. data/test/Gemfile.lock +10 -0
  124. data/test/sass/cache_test.rb +89 -0
  125. data/test/sass/callbacks_test.rb +61 -0
  126. data/test/sass/conversion_test.rb +1760 -0
  127. data/test/sass/css2sass_test.rb +458 -0
  128. data/test/sass/data/hsl-rgb.txt +319 -0
  129. data/test/sass/engine_test.rb +3244 -0
  130. data/test/sass/exec_test.rb +86 -0
  131. data/test/sass/extend_test.rb +1482 -0
  132. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  133. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  134. data/test/sass/functions_test.rb +1139 -0
  135. data/test/sass/importer_test.rb +192 -0
  136. data/test/sass/logger_test.rb +58 -0
  137. data/test/sass/mock_importer.rb +49 -0
  138. data/test/sass/more_results/more1.css +9 -0
  139. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  140. data/test/sass/more_results/more_import.css +29 -0
  141. data/test/sass/more_templates/_more_partial.sass +2 -0
  142. data/test/sass/more_templates/more1.sass +23 -0
  143. data/test/sass/more_templates/more_import.sass +11 -0
  144. data/test/sass/plugin_test.rb +564 -0
  145. data/test/sass/results/alt.css +4 -0
  146. data/test/sass/results/basic.css +9 -0
  147. data/test/sass/results/cached_import_option.css +3 -0
  148. data/test/sass/results/compact.css +5 -0
  149. data/test/sass/results/complex.css +86 -0
  150. data/test/sass/results/compressed.css +1 -0
  151. data/test/sass/results/expanded.css +19 -0
  152. data/test/sass/results/filename_fn.css +3 -0
  153. data/test/sass/results/if.css +3 -0
  154. data/test/sass/results/import.css +31 -0
  155. data/test/sass/results/import_charset.css +5 -0
  156. data/test/sass/results/import_charset_1_8.css +5 -0
  157. data/test/sass/results/import_charset_ibm866.css +5 -0
  158. data/test/sass/results/import_content.css +1 -0
  159. data/test/sass/results/line_numbers.css +49 -0
  160. data/test/sass/results/mixins.css +95 -0
  161. data/test/sass/results/multiline.css +24 -0
  162. data/test/sass/results/nested.css +22 -0
  163. data/test/sass/results/options.css +1 -0
  164. data/test/sass/results/parent_ref.css +13 -0
  165. data/test/sass/results/script.css +16 -0
  166. data/test/sass/results/scss_import.css +31 -0
  167. data/test/sass/results/scss_importee.css +2 -0
  168. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  169. data/test/sass/results/subdir/subdir.css +3 -0
  170. data/test/sass/results/units.css +11 -0
  171. data/test/sass/results/warn.css +0 -0
  172. data/test/sass/results/warn_imported.css +0 -0
  173. data/test/sass/script_conversion_test.rb +299 -0
  174. data/test/sass/script_test.rb +622 -0
  175. data/test/sass/scss/css_test.rb +1100 -0
  176. data/test/sass/scss/rx_test.rb +156 -0
  177. data/test/sass/scss/scss_test.rb +2106 -0
  178. data/test/sass/scss/test_helper.rb +37 -0
  179. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  180. data/test/sass/templates/_double_import_loop2.sass +1 -0
  181. data/test/sass/templates/_filename_fn_import.scss +11 -0
  182. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  183. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  184. data/test/sass/templates/_imported_content.sass +3 -0
  185. data/test/sass/templates/_partial.sass +2 -0
  186. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  187. data/test/sass/templates/alt.sass +16 -0
  188. data/test/sass/templates/basic.sass +23 -0
  189. data/test/sass/templates/bork1.sass +2 -0
  190. data/test/sass/templates/bork2.sass +2 -0
  191. data/test/sass/templates/bork3.sass +2 -0
  192. data/test/sass/templates/bork4.sass +2 -0
  193. data/test/sass/templates/bork5.sass +3 -0
  194. data/test/sass/templates/cached_import_option.scss +3 -0
  195. data/test/sass/templates/compact.sass +17 -0
  196. data/test/sass/templates/complex.sass +305 -0
  197. data/test/sass/templates/compressed.sass +15 -0
  198. data/test/sass/templates/double_import_loop1.sass +1 -0
  199. data/test/sass/templates/expanded.sass +17 -0
  200. data/test/sass/templates/filename_fn.scss +18 -0
  201. data/test/sass/templates/if.sass +11 -0
  202. data/test/sass/templates/import.sass +12 -0
  203. data/test/sass/templates/import_charset.sass +9 -0
  204. data/test/sass/templates/import_charset_1_8.sass +6 -0
  205. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  206. data/test/sass/templates/import_content.sass +4 -0
  207. data/test/sass/templates/importee.less +2 -0
  208. data/test/sass/templates/importee.sass +19 -0
  209. data/test/sass/templates/line_numbers.sass +13 -0
  210. data/test/sass/templates/mixin_bork.sass +5 -0
  211. data/test/sass/templates/mixins.sass +76 -0
  212. data/test/sass/templates/multiline.sass +20 -0
  213. data/test/sass/templates/nested.sass +25 -0
  214. data/test/sass/templates/nested_bork1.sass +2 -0
  215. data/test/sass/templates/nested_bork2.sass +2 -0
  216. data/test/sass/templates/nested_bork3.sass +2 -0
  217. data/test/sass/templates/nested_bork4.sass +2 -0
  218. data/test/sass/templates/nested_import.sass +2 -0
  219. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  220. data/test/sass/templates/options.sass +2 -0
  221. data/test/sass/templates/parent_ref.sass +25 -0
  222. data/test/sass/templates/same_name_different_ext.sass +2 -0
  223. data/test/sass/templates/same_name_different_ext.scss +1 -0
  224. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  225. data/test/sass/templates/script.sass +101 -0
  226. data/test/sass/templates/scss_import.scss +11 -0
  227. data/test/sass/templates/scss_importee.scss +1 -0
  228. data/test/sass/templates/single_import_loop.sass +1 -0
  229. data/test/sass/templates/subdir/import_up1.scss +1 -0
  230. data/test/sass/templates/subdir/import_up2.scss +1 -0
  231. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  232. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  233. data/test/sass/templates/subdir/subdir.sass +6 -0
  234. data/test/sass/templates/units.sass +11 -0
  235. data/test/sass/templates/warn.sass +3 -0
  236. data/test/sass/templates/warn_imported.sass +4 -0
  237. data/test/sass/test_helper.rb +8 -0
  238. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  239. data/test/sass/util/subset_map_test.rb +91 -0
  240. data/test/sass/util_test.rb +382 -0
  241. data/test/test_helper.rb +80 -0
  242. metadata +354 -0
@@ -0,0 +1,215 @@
1
+ module Sass
2
+ module Selector
3
+ # A unseparated sequence of selectors
4
+ # that all apply to a single element.
5
+ # For example, `.foo#bar[attr=baz]` is a simple sequence
6
+ # of the selectors `.foo`, `#bar`, and `[attr=baz]`.
7
+ class SimpleSequence < AbstractSequence
8
+ # The array of individual selectors.
9
+ #
10
+ # @return [Array<Simple>]
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 {#do_extend} process.
25
+ #
26
+ # @return {Set<Sequence>}
27
+ attr_accessor :sources
28
+
29
+ # @see \{#subject?}
30
+ attr_writer :subject
31
+
32
+ # Returns the element or universal selector in this sequence,
33
+ # if it exists.
34
+ #
35
+ # @return [Element, Universal, nil]
36
+ def base
37
+ @base ||= (members.first if members.first.is_a?(Element) || members.first.is_a?(Universal))
38
+ end
39
+
40
+ def pseudo_elements
41
+ @pseudo_elements ||= (members - [base]).
42
+ select {|sel| sel.is_a?(Pseudo) && sel.type == :element}
43
+ end
44
+
45
+ # Returns the non-base, non-pseudo-class selectors in this sequence.
46
+ #
47
+ # @return [Set<Simple>]
48
+ def rest
49
+ @rest ||= Set.new(members - [base] - pseudo_elements)
50
+ end
51
+
52
+ # Whether or not this compound selector is the subject of the parent
53
+ # selector; that is, whether it is prepended with `$` and represents the
54
+ # actual element that will be selected.
55
+ #
56
+ # @return [Boolean]
57
+ def subject?
58
+ @subject
59
+ end
60
+
61
+ # @param selectors [Array<Simple>] See \{#members}
62
+ # @param subject [Boolean] See \{#subject?}
63
+ # @param sources [Set<Sequence>]
64
+ def initialize(selectors, subject, sources = Set.new)
65
+ @members = selectors
66
+ @subject = subject
67
+ @sources = sources
68
+ end
69
+
70
+ # Resolves the {Parent} selectors within this selector
71
+ # by replacing them with the given parent selector,
72
+ # handling commas appropriately.
73
+ #
74
+ # @param super_seq [Sequence] The parent selector sequence
75
+ # @return [Array<SimpleSequence>] This selector, with parent references resolved.
76
+ # This is an array because the parent selector is itself a {Sequence}
77
+ # @raise [Sass::SyntaxError] If a parent selector is invalid
78
+ def resolve_parent_refs(super_seq)
79
+ # Parent selector only appears as the first selector in the sequence
80
+ return [self] unless @members.first.is_a?(Parent)
81
+
82
+ members = super_seq.members.dup
83
+ newline = members.pop if members.last == "\n"
84
+ return members if @members.size == 1
85
+ unless members.last.is_a?(SimpleSequence)
86
+ raise Sass::SyntaxError.new("Invalid parent selector: " + super_seq.to_a.join)
87
+ end
88
+
89
+ members[0...-1] +
90
+ [SimpleSequence.new(members.last.members + @members[1..-1], subject?)] +
91
+ [newline].compact
92
+ end
93
+
94
+ # Non-destrucively extends this selector with the extensions specified in a hash
95
+ # (which should come from {Sass::Tree::Visitors::Cssize}).
96
+ #
97
+ # @overload def do_extend(extends, parent_directives)
98
+ # @param extends [{Selector::Simple =>
99
+ # Sass::Tree::Visitors::Cssize::Extend}]
100
+ # The extensions to perform on this selector
101
+ # @param parent_directives [Array<Sass::Tree::DirectiveNode>]
102
+ # The directives containing this selector.
103
+ # @return [Array<Sequence>] A list of selectors generated
104
+ # by extending this selector with `extends`.
105
+ # @see CommaSequence#do_extend
106
+ def do_extend(extends, parent_directives, seen = Set.new)
107
+ Sass::Util.group_by_to_a(extends.get(members.to_set)) {|ex, _| ex.extender}.map do |seq, group|
108
+ sels = group.map {|_, s| s}.flatten
109
+ # If A {@extend B} and C {...},
110
+ # seq is A, sels is B, and self is C
111
+
112
+ self_without_sel = Sass::Util.array_minus(self.members, sels)
113
+ group.each {|e, _| e.result = :failed_to_unify unless e.result == :succeeded}
114
+ next unless unified = seq.members.last.unify(self_without_sel, subject?)
115
+ group.each {|e, _| e.result = :succeeded}
116
+ next if group.map {|e, _| check_directives_match!(e, parent_directives)}.none?
117
+ new_seq = Sequence.new(seq.members[0...-1] + [unified])
118
+ new_seq.add_sources!(sources + [seq])
119
+ [sels, new_seq]
120
+ end.compact.map do |sels, seq|
121
+ seen.include?(sels) ? [] : seq.do_extend(extends, parent_directives, seen + [sels])
122
+ end.flatten.uniq
123
+ end
124
+
125
+ # Unifies this selector with another {SimpleSequence}'s {SimpleSequence#members members array},
126
+ # returning another `SimpleSequence`
127
+ # that matches both this selector and the input selector.
128
+ #
129
+ # @param sels [Array<Simple>] A {SimpleSequence}'s {SimpleSequence#members members array}
130
+ # @param subject [Boolean] Whether the {SimpleSequence} being merged is a subject.
131
+ # @return [SimpleSequence, nil] A {SimpleSequence} matching both `sels` and this selector,
132
+ # or `nil` if this is impossible (e.g. unifying `#foo` and `#bar`)
133
+ # @raise [Sass::SyntaxError] If this selector cannot be unified.
134
+ # This will only ever occur when a dynamic selector,
135
+ # such as {Parent} or {Interpolation}, is used in unification.
136
+ # Since these selectors should be resolved
137
+ # by the time extension and unification happen,
138
+ # this exception will only ever be raised as a result of programmer error
139
+ def unify(sels, other_subject)
140
+ return unless sseq = members.inject(sels) do |member, sel|
141
+ return unless member
142
+ sel.unify(member)
143
+ end
144
+ SimpleSequence.new(sseq, other_subject || subject?)
145
+ end
146
+
147
+ # Returns whether or not this selector matches all elements
148
+ # that the given selector matches (as well as possibly more).
149
+ #
150
+ # @example
151
+ # (.foo).superselector?(.foo.bar) #=> true
152
+ # (.foo).superselector?(.bar) #=> false
153
+ # @param sseq [SimpleSequence]
154
+ # @return [Boolean]
155
+ def superselector?(sseq)
156
+ (base.nil? || base.eql?(sseq.base)) &&
157
+ pseudo_elements.eql?(sseq.pseudo_elements) &&
158
+ rest.subset?(sseq.rest)
159
+ end
160
+
161
+ # @see Simple#to_a
162
+ def to_a
163
+ res = @members.map {|sel| sel.to_a}.flatten
164
+ res << '!' if subject?
165
+ res
166
+ end
167
+
168
+ # Returns a string representation of the sequence.
169
+ # This is basically the selector string.
170
+ #
171
+ # @return [String]
172
+ def inspect
173
+ members.map {|m| m.inspect}.join
174
+ end
175
+
176
+ # Return a copy of this simple sequence with `sources` merged into the
177
+ # {#sources} set.
178
+ #
179
+ # @param sources [Set<Sequence>]
180
+ # @return [SimpleSequence]
181
+ def with_more_sources(sources)
182
+ sseq = dup
183
+ sseq.members = members.dup
184
+ sseq.sources = self.sources | sources
185
+ sseq
186
+ end
187
+
188
+ private
189
+
190
+ def check_directives_match!(extend, parent_directives)
191
+ dirs1 = extend.directives.map {|d| d.resolved_value}
192
+ dirs2 = parent_directives.map {|d| d.resolved_value}
193
+ return true if Sass::Util.subsequence?(dirs1, dirs2)
194
+
195
+ Sass::Util.sass_warn <<WARNING
196
+ DEPRECATION WARNING on line #{extend.node.line}#{" of #{extend.node.filename}" if extend.node.filename}:
197
+ @extending an outer selector from within #{extend.directives.last.name} is deprecated.
198
+ You may only @extend selectors within the same directive.
199
+ This will be an error in Sass 3.3.
200
+ It can only work once @extend is supported natively in the browser.
201
+ WARNING
202
+ return false
203
+ end
204
+
205
+ def _hash
206
+ [base, Sass::Util.set_hash(rest)].hash
207
+ end
208
+
209
+ def _eql?(other)
210
+ other.base.eql?(self.base) && other.pseudo_elements == pseudo_elements &&
211
+ Sass::Util.set_eql?(other.rest, self.rest) && other.subject? == self.subject?
212
+ end
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,452 @@
1
+ require 'sass/selector/simple'
2
+ require 'sass/selector/abstract_sequence'
3
+ require 'sass/selector/comma_sequence'
4
+ require 'sass/selector/sequence'
5
+ require 'sass/selector/simple_sequence'
6
+
7
+ module Sass
8
+ # A namespace for nodes in the parse tree for selectors.
9
+ #
10
+ # {CommaSequence} is the toplevel seelctor,
11
+ # representing a comma-separated sequence of {Sequence}s,
12
+ # such as `foo bar, baz bang`.
13
+ # {Sequence} is the next level,
14
+ # representing {SimpleSequence}s separated by combinators (e.g. descendant or child),
15
+ # such as `foo bar` or `foo > bar baz`.
16
+ # {SimpleSequence} is a sequence of selectors that all apply to a single element,
17
+ # such as `foo.bar[attr=val]`.
18
+ # Finally, {Simple} is the superclass of the simplest selectors,
19
+ # such as `.foo` or `#bar`.
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
+ #
25
+ # @type [Fixnum]
26
+ SPECIFICITY_BASE = 1_000
27
+
28
+ # A parent-referencing selector (`&` in Sass).
29
+ # The function of this is to be replaced by the parent selector
30
+ # in the nested hierarchy.
31
+ class Parent < Simple
32
+ # @see Selector#to_a
33
+ def to_a
34
+ ["&"]
35
+ end
36
+
37
+ # Always raises an exception.
38
+ #
39
+ # @raise [Sass::SyntaxError] Parent selectors should be resolved before unification
40
+ # @see Selector#unify
41
+ def unify(sels)
42
+ raise Sass::SyntaxError.new("[BUG] Cannot unify parent selectors.")
43
+ end
44
+ end
45
+
46
+ # A class selector (e.g. `.foo`).
47
+ class Class < Simple
48
+ # The class name.
49
+ #
50
+ # @return [Array<String, Sass::Script::Node>]
51
+ attr_reader :name
52
+
53
+ # @param name [Array<String, Sass::Script::Node>] The class name
54
+ def initialize(name)
55
+ @name = name
56
+ end
57
+
58
+ # @see Selector#to_a
59
+ def to_a
60
+ [".", *@name]
61
+ end
62
+
63
+ # @see AbstractSequence#specificity
64
+ def specificity
65
+ SPECIFICITY_BASE
66
+ end
67
+ end
68
+
69
+ # An id selector (e.g. `#foo`).
70
+ class Id < Simple
71
+ # The id name.
72
+ #
73
+ # @return [Array<String, Sass::Script::Node>]
74
+ attr_reader :name
75
+
76
+ # @param name [Array<String, Sass::Script::Node>] The id name
77
+ def initialize(name)
78
+ @name = name
79
+ end
80
+
81
+ # @see Selector#to_a
82
+ def to_a
83
+ ["#", *@name]
84
+ end
85
+
86
+ # Returns `nil` if `sels` contains an {Id} selector
87
+ # with a different name than this one.
88
+ #
89
+ # @see Selector#unify
90
+ def unify(sels)
91
+ return if sels.any? {|sel2| sel2.is_a?(Id) && self.name != sel2.name}
92
+ super
93
+ end
94
+
95
+ # @see AbstractSequence#specificity
96
+ def specificity
97
+ SPECIFICITY_BASE**2
98
+ end
99
+ end
100
+
101
+ # A placeholder selector (e.g. `%foo`).
102
+ # This exists to be replaced via `@extend`.
103
+ # Rulesets using this selector will not be printed, but can be extended.
104
+ # Otherwise, this acts just like a class selector.
105
+ class Placeholder < Simple
106
+ # The placeholder name.
107
+ #
108
+ # @return [Array<String, Sass::Script::Node>]
109
+ attr_reader :name
110
+
111
+ # @param name [Array<String, Sass::Script::Node>] The placeholder name
112
+ def initialize(name)
113
+ @name = name
114
+ end
115
+
116
+ # @see Selector#to_a
117
+ def to_a
118
+ ["%", *@name]
119
+ end
120
+
121
+ # @see AbstractSequence#specificity
122
+ def specificity
123
+ SPECIFICITY_BASE
124
+ end
125
+ end
126
+
127
+ # A universal selector (`*` in CSS).
128
+ class Universal < Simple
129
+ # The selector namespace.
130
+ # `nil` means the default namespace,
131
+ # `[""]` means no namespace,
132
+ # `["*"]` means any namespace.
133
+ #
134
+ # @return [Array<String, Sass::Script::Node>, nil]
135
+ attr_reader :namespace
136
+
137
+ # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
138
+ def initialize(namespace)
139
+ @namespace = namespace
140
+ end
141
+
142
+ # @see Selector#to_a
143
+ def to_a
144
+ @namespace ? @namespace + ["|*"] : ["*"]
145
+ end
146
+
147
+ # Unification of a universal selector is somewhat complicated,
148
+ # especially when a namespace is specified.
149
+ # If there is no namespace specified
150
+ # or any namespace is specified (namespace `"*"`),
151
+ # then `sel` is returned without change
152
+ # (unless it's empty, in which case `"*"` is required).
153
+ #
154
+ # If a namespace is specified
155
+ # but `sel` does not specify a namespace,
156
+ # then the given namespace is applied to `sel`,
157
+ # either by adding this {Universal} selector
158
+ # or applying this namespace to an existing {Element} selector.
159
+ #
160
+ # If both this selector *and* `sel` specify namespaces,
161
+ # those namespaces are unified via {Simple#unify_namespaces}
162
+ # and the unified namespace is used, if possible.
163
+ #
164
+ # @todo There are lots of cases that this documentation specifies;
165
+ # make sure we thoroughly test **all of them**.
166
+ # @todo Keep track of whether a default namespace has been declared
167
+ # and handle namespace-unspecified selectors accordingly.
168
+ # @todo If any branch of a CommaSequence ends up being just `"*"`,
169
+ # then all other branches should be eliminated
170
+ #
171
+ # @see Selector#unify
172
+ def unify(sels)
173
+ name =
174
+ case sels.first
175
+ when Universal; :universal
176
+ when Element; sels.first.name
177
+ else
178
+ return [self] + sels unless namespace.nil? || namespace == ['*']
179
+ return sels unless sels.empty?
180
+ return [self]
181
+ end
182
+
183
+ ns, accept = unify_namespaces(namespace, sels.first.namespace)
184
+ return unless accept
185
+ [name == :universal ? Universal.new(ns) : Element.new(name, ns)] + sels[1..-1]
186
+ end
187
+
188
+ # @see AbstractSequence#specificity
189
+ def specificity
190
+ 0
191
+ end
192
+ end
193
+
194
+ # An element selector (e.g. `h1`).
195
+ class Element < Simple
196
+ # The element name.
197
+ #
198
+ # @return [Array<String, Sass::Script::Node>]
199
+ attr_reader :name
200
+
201
+ # The selector namespace.
202
+ # `nil` means the default namespace,
203
+ # `[""]` means no namespace,
204
+ # `["*"]` means any namespace.
205
+ #
206
+ # @return [Array<String, Sass::Script::Node>, nil]
207
+ attr_reader :namespace
208
+
209
+ # @param name [Array<String, Sass::Script::Node>] The element name
210
+ # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
211
+ def initialize(name, namespace)
212
+ @name = name
213
+ @namespace = namespace
214
+ end
215
+
216
+ # @see Selector#to_a
217
+ def to_a
218
+ @namespace ? @namespace + ["|"] + @name : @name
219
+ end
220
+
221
+ # Unification of an element selector is somewhat complicated,
222
+ # especially when a namespace is specified.
223
+ # First, if `sel` contains another {Element} with a different \{#name},
224
+ # then the selectors can't be unified and `nil` is returned.
225
+ #
226
+ # Otherwise, if `sel` doesn't specify a namespace,
227
+ # or it specifies any namespace (via `"*"`),
228
+ # then it's returned with this element selector
229
+ # (e.g. `.foo` becomes `a.foo` or `svg|a.foo`).
230
+ # Similarly, if this selector doesn't specify a namespace,
231
+ # the namespace from `sel` is used.
232
+ #
233
+ # If both this selector *and* `sel` specify namespaces,
234
+ # those namespaces are unified via {Simple#unify_namespaces}
235
+ # and the unified namespace is used, if possible.
236
+ #
237
+ # @todo There are lots of cases that this documentation specifies;
238
+ # make sure we thoroughly test **all of them**.
239
+ # @todo Keep track of whether a default namespace has been declared
240
+ # and handle namespace-unspecified selectors accordingly.
241
+ #
242
+ # @see Selector#unify
243
+ def unify(sels)
244
+ case sels.first
245
+ when Universal;
246
+ when Element; return unless name == sels.first.name
247
+ else return [self] + sels
248
+ end
249
+
250
+ ns, accept = unify_namespaces(namespace, sels.first.namespace)
251
+ return unless accept
252
+ [Element.new(name, ns)] + sels[1..-1]
253
+ end
254
+
255
+ # @see AbstractSequence#specificity
256
+ def specificity
257
+ 1
258
+ end
259
+ end
260
+
261
+ # Selector interpolation (`#{}` in Sass).
262
+ class Interpolation < Simple
263
+ # The script to run.
264
+ #
265
+ # @return [Sass::Script::Node]
266
+ attr_reader :script
267
+
268
+ # @param script [Sass::Script::Node] The script to run
269
+ def initialize(script)
270
+ @script = script
271
+ end
272
+
273
+ # @see Selector#to_a
274
+ def to_a
275
+ [@script]
276
+ end
277
+
278
+ # Always raises an exception.
279
+ #
280
+ # @raise [Sass::SyntaxError] Interpolation selectors should be resolved before unification
281
+ # @see Selector#unify
282
+ def unify(sels)
283
+ raise Sass::SyntaxError.new("[BUG] Cannot unify interpolation selectors.")
284
+ end
285
+ end
286
+
287
+ # An attribute selector (e.g. `[href^="http://"]`).
288
+ class Attribute < Simple
289
+ # The attribute name.
290
+ #
291
+ # @return [Array<String, Sass::Script::Node>]
292
+ attr_reader :name
293
+
294
+ # The attribute namespace.
295
+ # `nil` means the default namespace,
296
+ # `[""]` means no namespace,
297
+ # `["*"]` means any namespace.
298
+ #
299
+ # @return [Array<String, Sass::Script::Node>, nil]
300
+ attr_reader :namespace
301
+
302
+ # The matching operator, e.g. `"="` or `"^="`.
303
+ #
304
+ # @return [String]
305
+ attr_reader :operator
306
+
307
+ # The right-hand side of the operator.
308
+ #
309
+ # @return [Array<String, Sass::Script::Node>]
310
+ attr_reader :value
311
+
312
+ # Flags for the attribute selector (e.g. `i`).
313
+ #
314
+ # @return [Array<String, Sass::Script::Node>]
315
+ attr_reader :flags
316
+
317
+ # @param name [Array<String, Sass::Script::Node>] The attribute name
318
+ # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
319
+ # @param operator [String] The matching operator, e.g. `"="` or `"^="`
320
+ # @param value [Array<String, Sass::Script::Node>] See \{#value}
321
+ # @param value [Array<String, Sass::Script::Node>] See \{#flags}
322
+ def initialize(name, namespace, operator, value, flags)
323
+ @name = name
324
+ @namespace = namespace
325
+ @operator = operator
326
+ @value = value
327
+ @flags = flags
328
+ end
329
+
330
+ # @see Selector#to_a
331
+ def to_a
332
+ res = ["["]
333
+ res.concat(@namespace) << "|" if @namespace
334
+ res.concat @name
335
+ (res << @operator).concat @value if @value
336
+ (res << " ").concat @flags if @flags
337
+ res << "]"
338
+ end
339
+
340
+ # @see AbstractSequence#specificity
341
+ def specificity
342
+ SPECIFICITY_BASE
343
+ end
344
+ end
345
+
346
+ # A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`) selector.
347
+ # It can have arguments (e.g. `:nth-child(2n+1)`).
348
+ class Pseudo < Simple
349
+ # Some psuedo-class-syntax selectors are actually considered
350
+ # pseudo-elements and must be treated differently. This is a list of such
351
+ # selectors
352
+ #
353
+ # @return [Array<String>]
354
+ ACTUALLY_ELEMENTS = %w[after before first-line first-letter]
355
+
356
+ # Like \{#type}, but returns the type of selector this looks like, rather
357
+ # than the type it is semantically. This only differs from type for
358
+ # selectors in \{ACTUALLY\_ELEMENTS}.
359
+ #
360
+ # @return [Symbol]
361
+ attr_reader :syntactic_type
362
+
363
+ # The name of the selector.
364
+ #
365
+ # @return [Array<String, Sass::Script::Node>]
366
+ attr_reader :name
367
+
368
+ # The argument to the selector,
369
+ # or `nil` if no argument was given.
370
+ #
371
+ # This may include SassScript nodes that will be run during resolution.
372
+ # Note that this should not include SassScript nodes
373
+ # after resolution has taken place.
374
+ #
375
+ # @return [Array<String, Sass::Script::Node>, nil]
376
+ attr_reader :arg
377
+
378
+ # @param type [Symbol] See \{#type}
379
+ # @param name [Array<String, Sass::Script::Node>] The name of the selector
380
+ # @param arg [nil, Array<String, Sass::Script::Node>] The argument to the selector,
381
+ # or nil if no argument was given
382
+ def initialize(type, name, arg)
383
+ @syntactic_type = type
384
+ @name = name
385
+ @arg = arg
386
+ end
387
+
388
+ # The type of the selector. `:class` if this is a pseudoclass selector,
389
+ # `:element` if it's a pseudoelement.
390
+ #
391
+ # @return [Symbol]
392
+ def type
393
+ ACTUALLY_ELEMENTS.include?(name.first) ? :element : syntactic_type
394
+ end
395
+
396
+ # @see Selector#to_a
397
+ def to_a
398
+ res = [syntactic_type == :class ? ":" : "::"] + @name
399
+ (res << "(").concat(Sass::Util.strip_string_array(@arg)) << ")" if @arg
400
+ res
401
+ end
402
+
403
+ # Returns `nil` if this is a pseudoelement selector
404
+ # and `sels` contains a pseudoelement selector different than this one.
405
+ #
406
+ # @see Selector#unify
407
+ def unify(sels)
408
+ return if type == :element && sels.any? do |sel|
409
+ sel.is_a?(Pseudo) && sel.type == :element &&
410
+ (sel.name != self.name || sel.arg != self.arg)
411
+ end
412
+ super
413
+ end
414
+
415
+ # @see AbstractSequence#specificity
416
+ def specificity
417
+ type == :class ? SPECIFICITY_BASE : 1
418
+ end
419
+ end
420
+
421
+ # A pseudoclass selector whose argument is itself a selector
422
+ # (e.g. `:not(.foo)` or `:-moz-all(.foo, .bar)`).
423
+ class SelectorPseudoClass < Simple
424
+ # The name of the pseudoclass.
425
+ #
426
+ # @return [String]
427
+ attr_reader :name
428
+
429
+ # The selector argument.
430
+ #
431
+ # @return [Selector::Sequence]
432
+ attr_reader :selector
433
+
434
+ # @param [String] The name of the pseudoclass
435
+ # @param [Selector::CommaSequence] The selector argument
436
+ def initialize(name, selector)
437
+ @name = name
438
+ @selector = selector
439
+ end
440
+
441
+ # @see Selector#to_a
442
+ def to_a
443
+ [":", @name, "("] + @selector.to_a + [")"]
444
+ end
445
+
446
+ # @see AbstractSequence#specificity
447
+ def specificity
448
+ SPECIFICITY_BASE
449
+ end
450
+ end
451
+ end
452
+ end