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