Class: Kronk::Diff

Inherits:
Object
  • Object
show all
Defined in:
lib/kronk/diff.rb,
lib/kronk/diff/output.rb,
lib/kronk/diff/ascii_format.rb,
lib/kronk/diff/color_format.rb

Overview

Creates simple diffs as formatted strings or arrays, from two strings or data objects.

Defined Under Namespace

Classes: AsciiFormat, ColorFormat, Output

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(str1, str2, opts = {}) ⇒ Diff

Create a new Diff instance from two strings. Options supported are those of Kronk::Diff::Output, plus:

:char

String/Regex - The char to split on for comparisons.



42
43
44
45
46
47
48
49
# File 'lib/kronk/diff.rb', line 42

def initialize str1, str2, opts={}
  @str1       = str1
  @str2       = str2
  @diff_ary   = nil
  @char       = opts[:char] || /\r?\n/
  @meta       = []
  @output     = Output.new opts
end

Instance Attribute Details

#charObject

Returns the value of attribute char.



35
36
37
# File 'lib/kronk/diff.rb', line 35

def char
  @char
end

#metaObject

Returns the value of attribute meta.



35
36
37
# File 'lib/kronk/diff.rb', line 35

def meta
  @meta
end

#outputObject

Returns the value of attribute output.



35
36
37
# File 'lib/kronk/diff.rb', line 35

def output
  @output
end

#str1Object

Returns the value of attribute str1.



35
36
37
# File 'lib/kronk/diff.rb', line 35

def str1
  @str1
end

#str2Object

Returns the value of attribute str2.



35
36
37
# File 'lib/kronk/diff.rb', line 35

def str2
  @str2
end

Class Method Details

.insert_line_nums(str, formatter = nil) ⇒ Object

Adds line numbers to each lines of a String.



21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/kronk/diff.rb', line 21

def self.insert_line_nums str, formatter=nil
  format = Diff::Output.formatter formatter || Kronk.config[:diff_format]

  out   = ""
  width = str.lines.count.to_s.length

  str.split("\n").each_with_index do |line, i|
    out << "#{format.lines(i+1, width)}#{line}\n"
  end

  out
end

.new_from_data(data1, data2, opts = {}) ⇒ Object

Creates a new diff from two data objects.



12
13
14
15
# File 'lib/kronk/diff.rb', line 12

def self.new_from_data data1, data2, opts={}
  new DataString.new(data1, opts),
      DataString.new(data2, opts), opts
end

Instance Method Details

#any?Boolean

Returns true if any diff is found.

Returns:

  • (Boolean)


210
211
212
# File 'lib/kronk/diff.rb', line 210

def any?
  !!diff_array.find{|i| Array === i }
end

#common_sequences(arr1, arr2, &block) ⇒ Object

Returns all common sequences between to arrays ordered by sequence length (including overlapping ones) according to the following format:

[[[len1, ix, iy], [len1, ix, iy]],[[len2, ix, iy]]]
# e.g.
[nil,[[1,2,3],[1,2,5]],nil,[[3,4,5],[3,6,9]]

Output is indexed by sequence size.



160
161
162
163
164
165
166
167
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/kronk/diff.rb', line 160

def common_sequences arr1, arr2, &block
  sequences = []

  arr2_map = {}
  arr2.each_with_index do |line, j|
    (arr2_map[line] ||= []) << j
  end

  arr1.each_with_index do |line, i|
    next unless arr2_map[line]

    arr2_map[line].each do |j|
      line1 = line
      line2 = arr2[j]

      k = i
      start_j = j

      while line1 && line1 == line2 && k < arr1.length
        k += 1
        j += 1

        line1 = arr1[k]
        line2 = arr2[j]
      end

      len = j - start_j
      (sequences[len] ||= []) << [len, i, start_j]
    end
  end

  yield_sequences sequences, &block if block_given?

  sequences
end

#countObject

Returns the number of diffs found.



218
219
220
# File 'lib/kronk/diff.rb', line 218

def count
  diff_array.select{|i| Array === i }.length
end

#create_diffObject

Returns a diff array with the following format:

str1 = "match1\nmatch2\nstr1 val"
str1 = "match1\nin str2\nmore str2\nmatch2\nstr2 val"

Diff.new(str1, str2).create_diff
#=> ["match 1",
#   [[], ["in str2", "more str2"]],
#   "match 2",
#   [["str1 val"], ["str2 val"]]]


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/kronk/diff.rb', line 63

def create_diff
  diff_ary = []
  return @str1.split @char if @str1 == @str2

  arr1 = @str1.split @char
  arr2 = @str2.split @char

  common_list = find_common arr1, arr2

  return [[arr1, arr2]] if common_list.empty?

  last_i1 = 0
  last_i2 = 0

  common_list.each do |c|
    next unless c

    left  = arr1[last_i1...c[1]]
    right = arr2[last_i2...c[2]]

    # add diffs
    unless left.empty? && right.empty?
      @meta << []
      @meta.last[0] = left[0].meta[0]  if left[0].respond_to?(:meta)
      @meta.last[1] = right[0].meta[0] if right[0].respond_to?(:meta)

      diff_ary << [left, right]
    end

    # add common
    arr1[c[1], c[0]].each_with_index do |str1, i|
      str2 = arr2[c[2]+i]
      @meta << []
      @meta.last[0] = str1.meta[0] if str1.respond_to?(:meta)
      @meta.last[1] = str2.meta[0] if str2.respond_to?(:meta)
    end

    diff_ary.concat arr1[c[1], c[0]]

    last_i1 = c[1] + c[0]
    last_i2 = c[2] + c[0]
  end

  left  = arr1[last_i1..-1]
  right = arr2[last_i2..-1]

  diff_ary << [left, right] unless left.empty? && right.empty?

  diff_ary
end

#diff_arrayObject Also known as: to_a

Returns the cached diff array when available, otherwise creates it.



226
227
228
# File 'lib/kronk/diff.rb', line 226

def diff_array
  @diff_ary ||= create_diff
end

#find_common(arr1, arr2) ⇒ Object

Finds non-overlapping common sequences between two arrays and returns them in the order they occur as an array of arrays:

find_common arr1, arr2
#=> [[size, arr1_index, arr2_index], [size, arr1_index, arr2_index],...]


121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/kronk/diff.rb', line 121

def find_common arr1, arr2
  used1 = []
  used2 = []

  common = []

  common_sequences(arr1, arr2) do |seq|
    next if used1[seq[1]] || used2[seq[2]]

    next if Array(used1[seq[1], seq[0]]).index(true) ||
            Array(used2[seq[2], seq[0]]).index(true)

    if seq[1] > (used1.length / 2)
      next if Array(used1[seq[1]+seq[0]..-1]).nitems !=
              Array(used2[seq[2]+seq[0]..-1]).nitems
    else
      next if Array(used1[0..seq[1]]).nitems !=
              Array(used2[0..seq[2]]).nitems
    end

    used1.fill(true, seq[1], seq[0])
    used2.fill(true, seq[2], seq[0])

    common[seq[1]] = seq
  end

  common
end

#formattedObject Also known as: to_s

Returns a formatted output as a String.



200
201
202
# File 'lib/kronk/diff.rb', line 200

def formatted
  ( @output.render diff_array, @meta if any? ).to_s
end