class ANSI::Diff

Diff produces colorized differences of two string or objects.

Constants

COLORS

Rotation of colors for diff output.

Public Class Methods

diff(object1, object2, options={}) click to toggle source

Highlights the differnce between two strings.

This class method is equivalent to calling:

ANSI::Diff.new(object1, object2).to_a
# File lib/ansi/diff.rb, line 15
def self.diff(object1, object2, options={})
  new(object1, object2, options={}).to_a
end
new(object1, object2, options={}) click to toggle source

Setup new Diff object. If the objects given are not Strings and do not have `#to_str` defined to coerce them to such, then their `#inspect` methods are used to convert them to strings for comparison.

@param [Object] object1

First object to compare.

@param [Object] object2

Second object to compare.

@param [Hash] options

Options for contoller the way difference is shown. (Not yet used.)
# File lib/ansi/diff.rb, line 33
def initialize(object1, object2, options={})
  @object1 = convert(object1)
  @object2 = convert(object2)

  @diff1, @diff2 = diff_string(@object1, @object2)
end

Public Instance Methods

diff1() click to toggle source

Returns the first object's difference string.

# File lib/ansi/diff.rb, line 41
def diff1
  @diff1
end
diff2() click to toggle source

Returns the second object's difference string.

# File lib/ansi/diff.rb, line 46
def diff2
  @diff2
end
join(separator=$/) click to toggle source

Returns both first and second difference strings separated by a the given `separator`. The default is `$/`, the record separator.

@param [String] separator

The string to use as the separtor between the difference strings.

@return [String] Joined difference strings.

# File lib/ansi/diff.rb, line 67
def join(separator=$/)
  "#{@diff1}#{separator}#{@diff2}"
end
to_a() click to toggle source

Returns the first and second difference strings in an array.

@return [Array] Both difference strings.

# File lib/ansi/diff.rb, line 74
def to_a
  [diff1, diff2]
end
to_s() click to toggle source

Returns both first and second difference strings separated by a new line character.

@todo Should we use `$/` record separator instead?

@return [String] Joined difference strings.

# File lib/ansi/diff.rb, line 56
def to_s
  "#{@diff1}\n#{@diff2}"
end

Private Instance Methods

common(x,y) click to toggle source

Oh, I should have documented this will I knew what the hell it was doing ;)

# File lib/ansi/diff.rb, line 139
def common(x,y)
  c = lcs(x, y)

  i = x.index(c)
  j = y.index(c)

  ix = i + c.size
  jx = j + c.size

  if i == 0 
    l = y[0...j]
  elsif j == 0
    l = x[0...i]
  else
    l = common(x[0...i], y[0...j])
  end

  if ix == x.size - 1
    r = y[jx..-1]
  elsif jx = y.size - 1
    r = x[ix..-1]
  else
    r = common(x[ix..-1], y[jx..-1])
  end

  [l, c, r].flatten.reject{ |s| s.empty? }
end
compare(x, y) click to toggle source

Take two plain strings and produce colorized versions of each highlighting their differences.

@param [String] x

First string to compare.

@param [String] y

Second string to compare.

@return [Array<String>] The two difference strings.

# File lib/ansi/diff.rb, line 120
def compare(x, y)
  c = common(x, y)
  a = x.dup
  b = y.dup
  oi = 0
  oj = 0
  c.each_with_index do |m, q|
    i = a.index(m, oi)
    j = b.index(m, oj)
    a[i,m.size] = ANSI.ansi(m, COLORS[q%3]) if i
    b[j,m.size] = ANSI.ansi(m, COLORS[q%3]) if j
    oi = i + m.size if i
    oj = j + m.size if j
  end
  return a, b
end
convert(object) click to toggle source

Ensure the object of comparison is a string. If object is not an instance of String then it wll be converted to one by calling either to_str, if the object responds to it, or inspect.

# File lib/ansi/diff.rb, line 97
def convert(object)
  if String === object
    object
  elsif object.respond_to?(:to_str)
    object.to_str
  else
    object.inspect
  end
end
diff_string(string1, string2) click to toggle source

Take two plain strings and produce colorized versions of each highlighting their differences.

@param [String] string1

First string to compare.

@param [String] string2

Second string to compare.

@return [Array<String>] The two difference strings.

# File lib/ansi/diff.rb, line 90
def diff_string(string1, string2)
  compare(string1, string2)
end
lcs(s1, s2) click to toggle source

Least common string.

# File lib/ansi/diff.rb, line 168
def lcs(s1, s2)
  res="" 
  num=Array.new(s1.size){Array.new(s2.size)}
  len,ans=0
  lastsub=0
  s1.scan(/./).each_with_index do |l1,i |
    s2.scan(/./).each_with_index do |l2,j |
      unless l1==l2
        num[i][j]=0
      else
        (i==0 || j==0)? num[i][j]=1 : num[i][j]=1 + num[i-1][j-1]
        if num[i][j] > len
          len = ans = num[i][j]
          thissub = i
          thissub -= num[i-1][j-1] unless num[i-1][j-1].nil?  
          if lastsub==thissub
            res+=s1[i,1]
          else
            lastsub=thissub
            res=s1[lastsub, (i+1)-lastsub]
          end
        end
      end
    end
  end
  res
end
lcs_size(s1, s2) click to toggle source

Hmm… is this even useful?

# File lib/ansi/diff.rb, line 197
def lcs_size(s1, s2)
  num=Array.new(s1.size){Array.new(s2.size)}
  len,ans=0,0
  s1.scan(/./).each_with_index do |l1,i |
    s2.scan(/./).each_with_index do |l2,j |
      unless l1==l2
        num[i][j]=0
      else
        (i==0 || j==0)? num[i][j]=1 : num[i][j]=1 + num[i-1][j-1]
        len = ans = num[i][j] if num[i][j] > len
      end
    end
  end
  ans
end