lib/sass/selector/sequence.rb in haml-3.0.0.rc.4 vs lib/sass/selector/sequence.rb in haml-3.0.0.rc.5
- old
+ new
@@ -116,26 +116,10 @@
# @return [String]
def inspect
members.map {|m| m.inspect}.join(" ")
end
- # Returns a hash code for this sequence.
- #
- # @return [Fixnum]
- def hash
- members.reject {|m| m == "\n"}.hash
- end
-
- # Checks equality between this and another object.
- #
- # @param other [Object] The object to test equality against
- # @return [Boolean] Whether or not this is equal to `other`
- def eql?(other)
- other.class == self.class &&
- other.members.reject {|m| m == "\n"}.eql?(self.members.reject {|m| m == "\n"})
- end
-
private
# Conceptually, this expands "parenthesized selectors".
# That is, if we have `.A .B {@extend .C}` and `.D .C {...}`,
# this conceptually expands into `.D .C, .D (.A .B)`,
@@ -174,40 +158,80 @@
# @param seq2 [Array<SimpleSequence or String>]
# @return [Array<Array<SimpleSequence or String>>]
def subweave(seq1, seq2, cache = {})
return [seq2] if seq1.empty?
return [seq1] if seq2.empty?
- cache[[seq1, seq2]] ||=
- begin
- sseq1, rest1 = seq_split(seq1)
- sseq2, rest2 = seq_split(seq2)
- if sseq1.eql?(sseq2)
- subweave(rest1, rest2, cache).map {|subseq| sseq1 + subseq}
- else
- unified = unify_heads(sseq1, sseq2) || unify_heads(sseq2, sseq1)
- res = []
- subweave(rest1, seq2, cache).each {|subseq| res << sseq1 + subseq}
- subweave(rest1, rest2, cache).each {|subseq| res << unified + subseq} if unified
- subweave(seq1, rest2, cache).each {|subseq| res << sseq2 + subseq}
- res
- end
- end
+ seq1 = group_selectors(seq1)
+ seq2 = group_selectors(seq2)
+ lcs = Haml::Util.lcs(seq2, seq1) do |s1, s2|
+ next s1 if s1 == s2
+ next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)
+ next s2 if subweave_superselector?(s1, s2)
+ next s1 if subweave_superselector?(s2, s1)
+ end
+
+ diff = []
+ until lcs.empty?
+ diff << chunks(seq1, seq2) {|s| subweave_superselector?(s.first, lcs.first)} << [lcs.shift]
+ seq1.shift
+ seq2.shift
+ end
+ diff << chunks(seq1, seq2) {|s| s.empty?}
+ diff.reject! {|c| c.empty?}
+
+ Haml::Util.paths(diff).map {|p| p.flatten}
end
- def seq_split(seq)
+ def chunks(seq1, seq2)
+ chunk1 = []
+ chunk1 << seq1.shift until yield seq1
+ chunk2 = []
+ chunk2 << seq2.shift until yield seq2
+ return [] if chunk1.empty? && chunk2.empty?
+ return [chunk2] if chunk1.empty?
+ return [chunk1] if chunk2.empty?
+ [chunk1 + chunk2, chunk2 + chunk1]
+ end
+
+ def group_selectors(seq)
+ newseq = []
tail = seq.dup
- head = []
- begin
- head << tail.shift
- end while !tail.empty? && head.last.is_a?(String) || tail.first.is_a?(String)
- return head, tail
+ until tail.empty?
+ head = []
+ begin
+ head << tail.shift
+ end while !tail.empty? && head.last.is_a?(String) || tail.first.is_a?(String)
+ newseq << head
+ end
+ return newseq
end
- def unify_heads(sseq1, sseq2)
- return unless sseq2.size == 1 # Can't unify ".foo > .bar" and ".baz > .bang"
- unified = sseq1.last.unify(sseq2.last.members) unless sseq1.last.is_a?(String) || sseq2.last.is_a?(String)
- sseq1[0...-1] << unified if unified
+ def subweave_superselector?(sseq1, sseq2)
+ if sseq1.size > 1
+ # More complex selectors are never superselectors of less complex ones
+ return unless sseq2.size > 1
+ # .foo ~ .bar is a superselector of .foo + .bar
+ return unless sseq1[1] == "~" ? sseq2[1] != ">" : sseq2[1] == sseq1[1]
+ return unless sseq1.first.superselector?(sseq2.first)
+ return true if sseq1.size == 2
+ return false if sseq2.size == 2
+ return subweave_superselector?(sseq1[2..-1], sseq2[2..-1])
+ elsif sseq2.size > 1
+ return true if sseq2[1] == ">" && sseq1.first.superselector?(sseq2.first)
+ return false if sseq2.size == 2
+ return subweave_superselector?(sseq1, sseq2[2..-1])
+ else
+ sseq1.first.superselector?(sseq2.first)
+ end
+ end
+
+ def _hash
+ members.reject {|m| m == "\n"}.hash
+ end
+
+ def _eql?(other)
+ other.members.reject {|m| m == "\n"}.eql?(self.members.reject {|m| m == "\n"})
end
end
end
end