class Sass::Selector::SimpleSequence
of the selectors ‘.foo`, `#bar`, and `[attr=baz]`.
For example, `.foo#bar` is a simple sequence
that all apply to a single element.
A unseparated sequence of selectors
def _eql?(other)
def _eql?(other) other.base.eql?(base) && other.pseudo_elements == pseudo_elements && other.rest.eql?(rest) && other.subject? == subject? end
def _hash
def _hash [base, rest.hash].hash end
def base
-
(Element, Universal, nil)
-
def base @base ||= (members.first if members.first.is_a?(Element) || members.first.is_a?(Universal)) end
def check_directives_match!(extend, parent_directives)
def check_directives_match!(extend, parent_directives) dirs1 = extend.directives.map {|d| d.resolved_value} dirs2 = parent_directives.map {|d| d.resolved_value} return if Sass::Util.subsequence?(dirs1, dirs2) line = extend.node.line filename = extend.node.filename # TODO(nweiz): this should use the Sass stack trace of the extend node, # not the selector. raise Sass::SyntaxError.new(<<MESSAGE) y not @extend an outer selector from within #{extend.directives.last.name}. y only @extend selectors within the same directive. @extend #{extend.target.join(', ')}" on line #{line}#{" of #{filename}" if filename}. E end
def do_extend(extends, parent_directives, replace, seen)
- See: CommaSequence#do_extend -
Returns:
-
(Array
- A list of selectors generated)
Parameters:
-
original
(Boolean
) -- -
seen
(Set
) --> -
parent_directives
(Array
) -- -
extends
({Selector::Simple =>
) -- xtends [{Selector::Simple =>
def do_extend(extends, parent_directives, replace, seen) seen_with_pseudo_selectors = seen.dup modified_original = false members = self.members.map do |sel| next sel unless sel.is_a?(Pseudo) && sel.selector next sel if seen.include?([sel]) extended = sel.selector.do_extend(extends, parent_directives, replace, seen, false) next sel if extended == sel.selector extended.members.reject! {|seq| seq.invisible?} # For `:not()`, we usually want to get rid of any complex # selectors because that will cause the selector to fail to # parse on all browsers at time of writing. We can keep them # if either the original selector had a complex selector, or # the result of extending has only complex selectors, # because either way we aren't breaking anything that isn't # already broken. if sel.normalized_name == 'not' && (sel.selector.members.none? {|seq| seq.members.length > 1} && extended.members.any? {|seq| seq.members.length == 1}) extended.members.reject! {|seq| seq.members.length > 1} end modified_original = true result = sel.with_selector(extended) result.each {|new_sel| seen_with_pseudo_selectors << [new_sel]} result end.flatten groups = extends[members.to_set].group_by {|ex| ex.extender}.to_a groups.map! do |seq, group| sels = group.map {|e| e.target}.flatten # If A {@extend B} and C {...}, # seq is A, sels is B, and self is C self_without_sel = Sass::Util.array_minus(members, sels) group.each {|e| e.success = true} unified = seq.members.last.unify(SimpleSequence.new(self_without_sel, subject?)) next unless unified group.each {|e| check_directives_match!(e, parent_directives)} new_seq = Sequence.new(seq.members[0...-1] + [unified]) new_seq.add_sources!(sources + [seq]) [sels, new_seq] end groups.compact! groups.map! do |sels, seq| next [] if seen.include?(sels) seq.do_extend( extends, parent_directives, false, seen_with_pseudo_selectors + [sels], false) end groups.flatten! if modified_original || !replace || groups.empty? # First Law of Extend: the result of extending a selector should # (almost) always contain the base selector. # # See https://github.com/nex3/sass/issues/324. original = Sequence.new([SimpleSequence.new(members, @subject, source_range)]) original.add_sources! sources groups.unshift original end groups.uniq! groups end
def initialize(selectors, subject, source_range = nil)
-
source_range
(Sass::Source::Range
) -- -
subject
(Boolean
) -- See \{#subject?} -
selectors
(Array
) -- See \{#members}
def initialize(selectors, subject, source_range = nil) @members = selectors @subject = subject @sources = Set.new @source_range = source_range end
def inspect
-
(String)
-
def inspect res = members.map {|m| m.inspect}.join res << '!' if subject? res end
def pseudo_elements
def pseudo_elements @pseudo_elements ||= members.select {|sel| sel.is_a?(Pseudo) && sel.type == :element} end
def resolve_parent_refs(super_cseq)
-
(Sass::SyntaxError)
- If a parent selector is invalid
Returns:
-
(CommaSequence)
- This selector, with parent references resolved
Parameters:
-
super_cseq
(CommaSequence
) -- The parent selector
def resolve_parent_refs(super_cseq) resolved_members = @members.map do |sel| next sel unless sel.is_a?(Pseudo) && sel.selector sel.with_selector(sel.selector.resolve_parent_refs(super_cseq, false)) end.flatten # Parent selector only appears as the first selector in the sequence unless (parent = resolved_members.first).is_a?(Parent) return CommaSequence.new([Sequence.new([SimpleSequence.new(resolved_members, subject?)])]) end return super_cseq if @members.size == 1 && parent.suffix.nil? CommaSequence.new(super_cseq.members.map do |super_seq| members = super_seq.members.dup newline = members.pop if members.last == "\n" unless members.last.is_a?(SimpleSequence) raise Sass::SyntaxError.new("Invalid parent selector for \"#{self}\": \"" + super_seq.to_s + '"') end parent_sub = members.last.members unless parent.suffix.nil? parent_sub = parent_sub.dup parent_sub[-1] = parent_sub.last.dup case parent_sub.last when Sass::Selector::Class, Sass::Selector::Id, Sass::Selector::Placeholder parent_sub[-1] = parent_sub.last.class.new(parent_sub.last.name + parent.suffix) when Sass::Selector::Element parent_sub[-1] = parent_sub.last.class.new( parent_sub.last.name + parent.suffix, parent_sub.last.namespace) when Sass::Selector::Pseudo if parent_sub.last.arg || parent_sub.last.selector raise Sass::SyntaxError.new("Invalid parent selector for \"#{self}\": \"" + super_seq.to_s + '"') end parent_sub[-1] = Sass::Selector::Pseudo.new( parent_sub.last.type, parent_sub.last.name + parent.suffix, nil, nil) else raise Sass::SyntaxError.new("Invalid parent selector for \"#{self}\": \"" + super_seq.to_s + '"') end end Sequence.new(members[0...-1] + [SimpleSequence.new(parent_sub + resolved_members[1..-1], subject?)] + [newline].compact) end) end
def rest
-
(Set
-)
def rest @rest ||= Set.new(members - [base] - pseudo_elements) end
def selector_pseudo_classes
def selector_pseudo_classes @selector_pseudo_classes ||= members. select {|sel| sel.is_a?(Pseudo) && sel.type == :class && sel.selector}. group_by {|sel| sel.normalized_name} end
def subject?
-
(Boolean)
-
def subject? @subject end
def superselector?(their_sseq, parents = [])
-
(Boolean)
-
Parameters:
-
parents
(Array
) -- The parent selectors of `their_sseq`, if any. -
their_sseq
(SimpleSequence
) --
def superselector?(their_sseq, parents = []) return false unless base.nil? || base.eql?(their_sseq.base) return false unless pseudo_elements.eql?(their_sseq.pseudo_elements) our_spcs = selector_pseudo_classes their_spcs = their_sseq.selector_pseudo_classes # Some psuedo-selectors can be subselectors of non-pseudo selectors. # Pull those out here so we can efficiently check against them below. their_subselector_pseudos = %w(matches any nth-child nth-last-child). map {|name| their_spcs[name] || []}.flatten # If `self`'s non-pseudo simple selectors aren't a subset of `their_sseq`'s, # it's definitely not a superselector. This also considers being matched # by `:matches` or `:any`. return false unless rest.all? do |our_sel| next true if our_sel.is_a?(Pseudo) && our_sel.selector next true if their_sseq.rest.include?(our_sel) their_subselector_pseudos.any? do |their_pseudo| their_pseudo.selector.members.all? do |their_seq| next false unless their_seq.members.length == 1 their_sseq = their_seq.members.first next false unless their_sseq.is_a?(SimpleSequence) their_sseq.rest.include?(our_sel) end end end our_spcs.all? do |_name, pseudos| pseudos.all? {|pseudo| pseudo.superselector?(their_sseq, parents)} end end
def to_s(opts = {})
- See: Simple#to_s -
def to_s(opts = {}) res = @members.map {|m| m.to_s(opts)}.join # :not(%foo) may resolve to the empty string, but it should match every # selector so we replace it with "*". res = '*' if res.empty? res << '!' if subject? res end
def unify(other)
-
(Sass::SyntaxError)
- If this selector cannot be unified.
Returns:
-
(SimpleSequence, nil)
- A {SimpleSequence} matching both `sels` and this selector,
Parameters:
-
other
(SimpleSequence
) --
def unify(other) sseq = members.inject(other.members) do |member, sel| return unless member sel.unify(member) end return unless sseq SimpleSequence.new(sseq, other.subject? || subject?) end
def with_more_sources(sources)
-
(SimpleSequence)
-
Parameters:
-
sources
(Set
) --
def with_more_sources(sources) sseq = dup sseq.members = members.dup sseq.sources = self.sources | sources sseq end