Class: ANSI::Diff

Inherits:
Object
  • Object
show all
Defined in:
lib/ansi/diff.rb

Overview

Diff produces colorized differences of two string or objects.

Constant Summary collapse

COLORS =

Rotation of colors for diff output.

[:red, :yellow, :magenta]

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(object1, object2, options = {}) ⇒ Diff

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.

Parameters:

  • object1 (Object)

    First object to compare.

  • object2 (Object)

    Second object to compare.

  • options (Hash) (defaults to: {})

    Options for contoller the way difference is shown. (Not yet used.)



33
34
35
36
37
38
# 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

Class Method Details

.diff(object1, object2, options = {}) ⇒ Object

Highlights the differnce between two strings.

This class method is equivalent to calling:

ANSI::Diff.new(object1, object2).to_a


15
16
17
# File 'lib/ansi/diff.rb', line 15

def self.diff(object1, object2, options={})
  new(object1, object2, options={}).to_a
end

Instance Method Details

#common(x, y) ⇒ Object (private)

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



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# 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) ⇒ Array<String> (private)

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

Parameters:

  • x (String)

    First string to compare.

  • y (String)

    Second string to compare.

Returns:

  • (Array<String>)

    The two difference strings.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# 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) ⇒ Object (private)

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.



97
98
99
100
101
102
103
104
105
# 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

#diff1Object

Returns the first object’s difference string.



41
42
43
# File 'lib/ansi/diff.rb', line 41

def diff1
  @diff1
end

#diff2Object

Returns the second object’s difference string.



46
47
48
# File 'lib/ansi/diff.rb', line 46

def diff2
  @diff2
end

#diff_string(string1, string2) ⇒ Array<String> (private)

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

Parameters:

  • string1 (String)

    First string to compare.

  • string2 (String)

    Second string to compare.

Returns:

  • (Array<String>)

    The two difference strings.



90
91
92
# File 'lib/ansi/diff.rb', line 90

def diff_string(string1, string2)
  compare(string1, string2)
end

#join(separator = $/) ⇒ String

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

Parameters:

  • separator (String) (defaults to: $/)

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

Returns:

  • (String)

    Joined difference strings.



67
68
69
# File 'lib/ansi/diff.rb', line 67

def join(separator=$/)
  "#{@diff1}#{separator}#{@diff2}"
end

#lcs(s1, s2) ⇒ Object (private)

Least common string.



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# 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) ⇒ Object (private)

Hmm… is this even useful?



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# 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

#to_aArray

Returns the first and second difference strings in an array.

Returns:

  • (Array)

    Both difference strings.



74
75
76
# File 'lib/ansi/diff.rb', line 74

def to_a
  [diff1, diff2]
end

#to_sString

TODO:

Should we use ‘$/` record separator instead?

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

Returns:

  • (String)

    Joined difference strings.



56
57
58
# File 'lib/ansi/diff.rb', line 56

def to_s
  "#{@diff1}\n#{@diff2}"
end