class Test::Unit::Diff::ReadableDiffer

Public Instance Methods

diff(options={}) click to toggle source
# File lib/test/unit/diff.rb, line 414
def diff(options={})
  @result = []
  operations.each do |tag, from_start, from_end, to_start, to_end|
    case tag
    when :replace
      diff_lines(from_start, from_end, to_start, to_end)
    when :delete
      tag_deleted(@from[from_start...from_end])
    when :insert
      tag_inserted(@to[to_start...to_end])
    when :equal
      tag_equal(@from[from_start...from_end])
    else
      raise "unknown tag: #{tag}"
    end
  end
  @result
end

Private Instance Methods

_diff_lines(from_start, from_end, to_start, to_end) click to toggle source
# File lib/test/unit/diff.rb, line 529
def _diff_lines(from_start, from_end, to_start, to_end)
  if from_start < from_end
    if to_start < to_end
      diff_lines(from_start, from_end, to_start, to_end)
    else
      tag_deleted(@from[from_start...from_end])
    end
  else
    tag_inserted(@to[to_start...to_end])
  end
end
compute_width(line, start, _end) click to toggle source
# File lib/test/unit/diff.rb, line 551
def compute_width(line, start, _end)
  if line.respond_to?(:encoding) and
      Encoding.compatible?(Encoding::UTF_8, line.encoding)
    utf8_line = line[start..._end].encode(Encoding::UTF_8)
    width = 0
    utf8_line.each_codepoint do |unicode_codepoint|
      if UTF8Line.wide_character?(unicode_codepoint)
        width += 2
      else
        width += 1
      end
    end
    width
  elsif line.is_a?(UTF8Line)
    line.compute_width(start, _end)
  else
    _end - start
  end
end
cut_off_ratio() click to toggle source
# File lib/test/unit/diff.rb, line 447
def cut_off_ratio
  0.75
end
default_ratio() click to toggle source
# File lib/test/unit/diff.rb, line 443
def default_ratio
  0.74
end
diff_line(from_line, to_line) click to toggle source
# File lib/test/unit/diff.rb, line 571
def diff_line(from_line, to_line)
  from_tags = ""
  to_tags = ""
  from_line, to_line, _operations = line_operations(from_line, to_line)
  _operations.each do |tag, from_start, from_end, to_start, to_end|
    from_width = compute_width(from_line, from_start, from_end)
    to_width = compute_width(to_line, to_start, to_end)
    case tag
    when :replace
      from_tags += "^" * from_width
      to_tags += "^" * to_width
    when :delete
      from_tags += "-" * from_width
    when :insert
      to_tags += "+" * to_width
    when :equal
      from_tags += " " * from_width
      to_tags += " " * to_width
    else
      raise "unknown tag: #{tag}"
    end
  end
  format_diff_point(from_line, to_line, from_tags, to_tags)
end
diff_lines(from_start, from_end, to_start, to_end) click to toggle source
# File lib/test/unit/diff.rb, line 501
def diff_lines(from_start, from_end, to_start, to_end)
  info = find_diff_line_info(from_start, from_end, to_start, to_end)
  best_ratio, from_equal_index, to_equal_index, *info = info
  from_best_index, to_best_index = info
  from_best_index ||= from_start
  to_best_index ||= to_start

  if best_ratio < cut_off_ratio
    if from_equal_index.nil?
      if to_end - to_start < from_end - from_start
        tag_inserted(@to[to_start...to_end])
        tag_deleted(@from[from_start...from_end])
      else
        tag_deleted(@from[from_start...from_end])
        tag_inserted(@to[to_start...to_end])
      end
      return
    end
    from_best_index = from_equal_index
    to_best_index = to_equal_index
    best_ratio = 1.0
  end

  _diff_lines(from_start, from_best_index, to_start, to_best_index)
  diff_line(@from[from_best_index], @to[to_best_index])
  _diff_lines(from_best_index + 1, from_end, to_best_index + 1, to_end)
end
find_diff_line_info(from_start, from_end, to_start, to_end) click to toggle source
# File lib/test/unit/diff.rb, line 473
def find_diff_line_info(from_start, from_end, to_start, to_end)
  best_ratio = default_ratio
  from_equal_index = to_equal_index = nil
  from_best_index = to_best_index = nil

  to_start.upto(to_end - 1) do |to_index|
    from_start.upto(from_end - 1) do |from_index|
      if @from[from_index] == @to[to_index]
        from_equal_index ||= from_index
        to_equal_index ||= to_index
        next
      end

      matcher = SequenceMatcher.new(@from[from_index], @to[to_index],
                                    &method(:space_character?))
      if matcher.ratio > best_ratio
        best_ratio = matcher.ratio
        from_best_index = from_index
        to_best_index = to_index
      end
    end
  end

  [best_ratio,
   from_equal_index, to_equal_index,
   from_best_index,  to_best_index]
end
format_diff_point(from_line, to_line, from_tags, to_tags) click to toggle source
# File lib/test/unit/diff.rb, line 596
def format_diff_point(from_line, to_line, from_tags, to_tags)
  common = [n_leading_characters(from_line, ?\t),
            n_leading_characters(to_line, ?\t)].min
  common = [common,
            n_leading_characters(from_tags[0, common], " "[0])].min
  from_tags = from_tags[common..-1].rstrip
  to_tags = to_tags[common..-1].rstrip

  tag_deleted([from_line])
  unless from_tags.empty?
    tag_difference(["#{"\t" * common}#{from_tags}"])
  end
  tag_inserted([to_line])
  unless to_tags.empty?
    tag_difference(["#{"\t" * common}#{to_tags}"])
  end
end
line_operations(from_line, to_line) click to toggle source
# File lib/test/unit/diff.rb, line 541
def line_operations(from_line, to_line)
  if !from_line.respond_to?(:force_encoding) and $KCODE == "UTF8"
    from_line = UTF8Line.new(from_line)
    to_line = UTF8Line.new(to_line)
  end
  matcher = SequenceMatcher.new(from_line, to_line,
                                &method(:space_character?))
  [from_line, to_line, matcher.operations]
end
n_leading_characters(string, character) click to toggle source
# File lib/test/unit/diff.rb, line 614
def n_leading_characters(string, character)
  n = 0
  while string[n] == character
    n += 1
  end
  n
end
operations() click to toggle source
# File lib/test/unit/diff.rb, line 434
def operations
  @operations ||= nil
  if @operations.nil?
    matcher = SequenceMatcher.new(@from, @to)
    @operations = matcher.operations
  end
  @operations
end
space_character?(character) click to toggle source
# File lib/test/unit/diff.rb, line 622
def space_character?(character)
  [" "[0], "\t"[0]].include?(character)
end
tag(mark, contents) click to toggle source
# File lib/test/unit/diff.rb, line 451
def tag(mark, contents)
  contents.each do |content|
    @result << (mark + content)
  end
end
tag_deleted(contents) click to toggle source
# File lib/test/unit/diff.rb, line 457
def tag_deleted(contents)
  tag("- ", contents)
end
tag_difference(contents) click to toggle source
# File lib/test/unit/diff.rb, line 469
def tag_difference(contents)
  tag("? ", contents)
end
tag_equal(contents) click to toggle source
# File lib/test/unit/diff.rb, line 465
def tag_equal(contents)
  tag("  ", contents)
end
tag_inserted(contents) click to toggle source
# File lib/test/unit/diff.rb, line 461
def tag_inserted(contents)
  tag("+ ", contents)
end