diff-lcs 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,59 +1,58 @@
1
- #! /usr/env/bin ruby
2
- #--
3
- # Copyright 2004 Austin Ziegler <diff-lcs@halostatue.ca>
4
- # adapted from:
5
- # Algorithm::Diff (Perl) by Ned Konz <perl@bike-nomad.com>
6
- # Smalltalk by Mario I. Wolczko <mario@wolczko.com>
7
- # implements McIlroy-Hunt diff algorithm
8
- #
9
- # This program is free software. It may be redistributed and/or modified under
10
- # the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
11
- # Ruby licence.
12
- #
13
- # $Id$
14
- #++
15
- # Provides Diff::LCS::Change and Diff::LCS::ContextChange.
16
-
17
- # Centralises the change test code in Diff::LCS::Change and
18
- # Diff::LCS::ContextChange, since it's the same for both classes.
19
- module Diff::LCS::ChangeTypeTests
20
- def deleting?
21
- @action == '-'
22
- end
1
+ # -*- ruby encoding: utf-8 -*-
23
2
 
24
- def adding?
25
- @action == '+'
26
- end
3
+ # Represents a simplistic (non-contextual) change. Represents the removal or
4
+ # addition of an element from either the old or the new sequenced
5
+ # enumerable.
6
+ class Diff::LCS::Change
7
+ # The only actions valid for changes are '+' (add), '-' (delete), '='
8
+ # (no change), '!' (changed), '<' (tail changes from first sequence), or
9
+ # '>' (tail changes from second sequence). The last two ('<>') are only
10
+ # found with Diff::LCS::diff and Diff::LCS::sdiff.
11
+ VALID_ACTIONS = %W(+ - = ! > <)
27
12
 
28
- def unchanged?
29
- @action == '='
13
+ def self.valid_action?(action)
14
+ VALID_ACTIONS.include? action
30
15
  end
31
16
 
32
- def changed?
33
- @changed == '!'
17
+ # Returns the action this Change represents.
18
+ attr_reader :action
19
+
20
+ # Returns the position of the Change.
21
+ attr_reader :position
22
+ # Returns the sequence element of the Change.
23
+ attr_reader :element
24
+
25
+ def initialize(*args)
26
+ @action, @position, @element = *args
27
+
28
+ unless Diff::LCS::Change.valid_action?(@action)
29
+ raise "Invalid Change Action '#{@action}'"
30
+ end
31
+ raise "Invalid Position Type" unless @position.kind_of? Fixnum
34
32
  end
35
33
 
36
- def finished_a?
37
- @changed == '>'
34
+ def inspect
35
+ to_a.inspect
38
36
  end
39
37
 
40
- def finished_b?
41
- @changed == '<'
38
+ def to_a
39
+ [ @action, @position, @element ]
42
40
  end
43
- end
44
41
 
45
- # Represents a simplistic (non-contextual) change. Represents the removal or
46
- # addition of an element from either the old or the new sequenced enumerable.
47
- class Diff::LCS::Change
48
- # Returns the action this Change represents. Can be '+' (#adding?), '-'
49
- # (#deleting?), '=' (#unchanged?), # or '!' (#changed?). When created by
50
- # Diff::LCS#diff or Diff::LCS#sdiff, it may also be '>' (#finished_a?) or
51
- # '<' (#finished_b?).
52
- attr_reader :action
53
- attr_reader :position
54
- attr_reader :element
42
+ def self.from_a(arr)
43
+ arr = arr.flatten
44
+ case arr.size
45
+ when 5
46
+ Diff::LCS::ContextChange.new(*(arr[0...5]))
47
+ when 3
48
+ Diff::LCS::Change.new(*(arr[0...3]))
49
+ else
50
+ raise "Invalid change array format provided."
51
+ end
52
+ end
55
53
 
56
54
  include Comparable
55
+
57
56
  def ==(other)
58
57
  (self.action == other.action) and
59
58
  (self.position == other.position) and
@@ -67,85 +66,79 @@ class Diff::LCS::Change
67
66
  r
68
67
  end
69
68
 
70
- def initialize(action, position, element)
71
- @action = action
72
- @position = position
73
- @element = element
69
+ def adding?
70
+ @action == '+'
74
71
  end
75
72
 
76
- # Creates a Change from an array produced by Change#to_a.
77
- def to_a
78
- [@action, @position, @element]
73
+ def deleting?
74
+ @action == '-'
79
75
  end
80
76
 
81
- def self.from_a(arr)
82
- Diff::LCS::Change.new(arr[0], arr[1], arr[2])
77
+ def unchanged?
78
+ @action == '='
79
+ end
80
+
81
+ def changed?
82
+ @action == '!'
83
83
  end
84
84
 
85
- include Diff::LCS::ChangeTypeTests
85
+ def finished_a?
86
+ @action == '>'
87
+ end
88
+
89
+ def finished_b?
90
+ @action == '<'
91
+ end
86
92
  end
87
93
 
88
- # Represents a contextual change. Contains the position and values of the
89
- # elements in the old and the new sequenced enumerables as well as the action
90
- # taken.
91
- class Diff::LCS::ContextChange
92
- # Returns the action this Change represents. Can be '+' (#adding?), '-'
93
- # (#deleting?), '=' (#unchanged?), # or '!' (#changed?). When
94
- # created by Diff::LCS#diff or Diff::LCS#sdiff, it may also be '>'
95
- # (#finished_a?) or '<' (#finished_b?).
96
- attr_reader :action
94
+ # Represents a contextual change. Contains the position and values of the
95
+ # elements in the old and the new sequenced enumerables as well as the action
96
+ # taken.
97
+ class Diff::LCS::ContextChange < Diff::LCS::Change
98
+ # We don't need these two values.
99
+ undef :position
100
+ undef :element
101
+
102
+ # Returns the old position being changed.
97
103
  attr_reader :old_position
98
- attr_reader :old_element
104
+ # Returns the new position being changed.
99
105
  attr_reader :new_position
106
+ # Returns the old element being changed.
107
+ attr_reader :old_element
108
+ # Returns the new element being changed.
100
109
  attr_reader :new_element
101
110
 
102
- include Comparable
103
-
104
- def ==(other)
105
- (@action == other.action) and
106
- (@old_position == other.old_position) and
107
- (@new_position == other.new_position) and
108
- (@old_element == other.old_element) and
109
- (@new_element == other.new_element)
110
- end
111
+ def initialize(*args)
112
+ @action, @old_position, @old_element, @new_position, @new_element = *args
111
113
 
112
- def inspect(*args)
113
- %Q(#<#{self.class.name}:#{__id__} @action=#{action} positions=#{old_position},#{new_position} elements=#{old_element.inspect},#{new_element.inspect}>)
114
- end
115
-
116
- def <=>(other)
117
- r = @action <=> other.action
118
- r = @old_position <=> other.old_position if r.zero?
119
- r = @new_position <=> other.new_position if r.zero?
120
- r = @old_element <=> other.old_element if r.zero?
121
- r = @new_element <=> other.new_element if r.zero?
122
- r
114
+ unless Diff::LCS::Change.valid_action?(@action)
115
+ raise "Invalid Change Action '#{@action}'"
116
+ end
117
+ unless @old_position.nil? or @old_position.kind_of? Fixnum
118
+ raise "Invalid (Old) Position Type"
119
+ end
120
+ unless @new_position.nil? or @new_position.kind_of? Fixnum
121
+ raise "Invalid (New) Position Type"
122
+ end
123
123
  end
124
124
 
125
- def initialize(action, old_position, old_element, new_position, new_element)
126
- @action = action
127
- @old_position = old_position
128
- @old_element = old_element
129
- @new_position = new_position
130
- @new_element = new_element
125
+ def to_a
126
+ [ @action,
127
+ [ @old_position, @old_element ],
128
+ [ @new_position, @new_element ]
129
+ ]
131
130
  end
132
131
 
133
- def to_a
134
- [@action, [@old_position, @old_element], [@new_position, @new_element]]
132
+ def inspect(*args)
133
+ to_a.inspect
135
134
  end
136
135
 
137
- # Creates a ContextChange from an array produced by ContextChange#to_a.
138
136
  def self.from_a(arr)
139
- if arr.size == 5
140
- Diff::LCS::ContextChange.new(arr[0], arr[1], arr[2], arr[3], arr[4])
141
- else
142
- Diff::LCS::ContextChange.new(arr[0], arr[1][0], arr[1][1], arr[2][0],
143
- arr[2][1])
144
- end
137
+ Diff::LCS::Change.from_a(arr)
145
138
  end
146
139
 
147
- # Simplifies a context change for use in some diff callbacks. '<' actions
148
- # are converted to '-' and '>' actions are converted to '+'.
140
+ # Simplifies a context change for use in some diff callbacks. '<' actions
141
+ # are converted to '-' and '>' actions are converted to '+'.
149
142
  def self.simplify(event)
150
143
  ea = event.to_a
151
144
 
@@ -165,5 +158,20 @@ class Diff::LCS::ContextChange
165
158
  Diff::LCS::ContextChange.from_a(ea)
166
159
  end
167
160
 
168
- include Diff::LCS::ChangeTypeTests
161
+ def ==(other)
162
+ (@action == other.action) and
163
+ (@old_position == other.old_position) and
164
+ (@new_position == other.new_position) and
165
+ (@old_element == other.old_element) and
166
+ (@new_element == other.new_element)
167
+ end
168
+
169
+ def <=>(other)
170
+ r = @action <=> other.action
171
+ r = @old_position <=> other.old_position if r.zero?
172
+ r = @new_position <=> other.new_position if r.zero?
173
+ r = @old_element <=> other.old_element if r.zero?
174
+ r = @new_element <=> other.new_element if r.zero?
175
+ r
176
+ end
169
177
  end
@@ -147,5 +147,3 @@ h1 { margin-left: 2em; }
147
147
  OUTPUT
148
148
  end
149
149
  end
150
-
151
- # vim: ft=ruby
@@ -1,13 +1,15 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
1
3
  require 'diff/lcs/block'
2
4
 
3
- # A Hunk is a group of Blocks which overlap because of the context
4
- # surrounding each block. (So if we're not using context, every hunk will
5
- # contain one block.) Used in the diff program (bin/diff).
5
+ # A Hunk is a group of Blocks which overlap because of the context
6
+ # surrounding each block. (So if we're not using context, every hunk will
7
+ # contain one block.) Used in the diff program (bin/diff).
6
8
  class Diff::LCS::Hunk
7
- # Create a hunk using references to both the old and new data, as well as
8
- # the piece of data
9
- def initialize(data_old, data_new, piece, context, file_length_difference)
10
- # At first, a hunk will have just one Block in it
9
+ # Create a hunk using references to both the old and new data, as well as
10
+ # the piece of data.
11
+ def initialize(data_old, data_new, piece, flag_context, file_length_difference)
12
+ # At first, a hunk will have just one Block in it
11
13
  @blocks = [ Diff::LCS::Block.new(piece) ]
12
14
  @data_old = data_old
13
15
  @data_new = data_new
@@ -16,10 +18,10 @@ class Diff::LCS::Hunk
16
18
  after += @blocks[0].diff_size
17
19
  @file_length_difference = after # The caller must get this manually
18
20
 
19
- # Save the start & end of each array. If the array doesn't exist
20
- # (e.g., we're only adding items in this block), then figure out the
21
- # line number based on the line number of the other file and the
22
- # current difference in file lengths.
21
+ # Save the start & end of each array. If the array doesn't exist (e.g.,
22
+ # we're only adding items in this block), then figure out the line
23
+ # number based on the line number of the other file and the current
24
+ # difference in file lengths.
23
25
  if @blocks[0].remove.empty?
24
26
  a1 = a2 = nil
25
27
  else
@@ -39,7 +41,7 @@ class Diff::LCS::Hunk
39
41
  @end_old = a2 || (b2 - after)
40
42
  @end_new = b2 || (a2 + after)
41
43
 
42
- self.flag_context = context
44
+ self.flag_context = flag_context
43
45
  end
44
46
 
45
47
  attr_reader :blocks
@@ -47,10 +49,10 @@ class Diff::LCS::Hunk
47
49
  attr_reader :end_old, :end_new
48
50
  attr_reader :file_length_difference
49
51
 
50
- # Change the "start" and "end" fields to note that context should be added
51
- # to this hunk
52
+ # Change the "start" and "end" fields to note that context should be added
53
+ # to this hunk.
52
54
  attr_accessor :flag_context
53
- undef :flag_context=
55
+ undef :flag_context=;
54
56
  def flag_context=(context) #:nodoc:
55
57
  return if context.nil? or context.zero?
56
58
 
@@ -67,22 +69,28 @@ class Diff::LCS::Hunk
67
69
  @end_new += add_end
68
70
  end
69
71
 
70
- def unshift(hunk)
71
- @start_old = hunk.start_old
72
- @start_new = hunk.start_new
73
- blocks.unshift(*hunk.blocks)
72
+ # Merges this hunk and the provided hunk together if they overlap. Returns
73
+ # a truthy value so that if there is no overlap, you can know the merge
74
+ # was skipped.
75
+ def merge(hunk)
76
+ if overlaps?(hunk)
77
+ @start_old = hunk.start_old
78
+ @start_new = hunk.start_new
79
+ blocks.unshift(*hunk.blocks)
80
+ else
81
+ nil
82
+ end
74
83
  end
75
84
 
76
- # Is there an overlap between hunk arg0 and old hunk arg1? Note: if end
77
- # of old hunk is one less than beginning of second, they overlap
78
- def overlaps?(hunk = nil)
79
- return nil if hunk.nil?
80
-
81
- a = (@start_old - hunk.end_old) <= 1
82
- b = (@start_new - hunk.end_new) <= 1
83
- return (a or b)
85
+ # Determines whether there is an overlap between this hunk and the
86
+ # provided hunk. This will be true if the difference between the two hunks
87
+ # start or end positions is within one position of each other.
88
+ def overlaps?(hunk)
89
+ hunk and (((@start_old - hunk.end_old) <= 1) or
90
+ ((@start_new - hunk.end_new) <= 1))
84
91
  end
85
92
 
93
+ # Returns a diff string based on a format.
86
94
  def diff(format)
87
95
  case format
88
96
  when :old
@@ -100,44 +108,40 @@ class Diff::LCS::Hunk
100
108
  end
101
109
  end
102
110
 
103
- def each_old(block)
104
- @data_old[@start_old .. @end_old].each { |e| yield e }
105
- end
106
-
107
- private
108
- # Note that an old diff can't have any context. Therefore, we know that
109
- # there's only one block in the hunk.
111
+ # Note that an old diff can't have any context. Therefore, we know that
112
+ # there's only one block in the hunk.
110
113
  def old_diff
111
114
  warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1
112
115
  op_act = { "+" => 'a', "-" => 'd', "!" => "c" }
113
116
 
114
117
  block = @blocks[0]
115
118
 
116
- # Calculate item number range. Old diff range is just like a context
117
- # diff range, except the ranges are on one line with the action between
118
- # them.
119
+ # Calculate item number range. Old diff range is just like a context
120
+ # diff range, except the ranges are on one line with the action between
121
+ # them.
119
122
  s = "#{context_range(:old)}#{op_act[block.op]}#{context_range(:new)}\n"
120
- # If removing anything, just print out all the remove lines in the hunk
121
- # which is just all the remove lines in the block.
123
+ # If removing anything, just print out all the remove lines in the hunk
124
+ # which is just all the remove lines in the block.
122
125
  @data_old[@start_old .. @end_old].each { |e| s << "< #{e}\n" } unless block.remove.empty?
123
126
  s << "---\n" if block.op == "!"
124
127
  @data_new[@start_new .. @end_new].each { |e| s << "> #{e}\n" } unless block.insert.empty?
125
128
  s
126
129
  end
130
+ private :old_diff
127
131
 
128
132
  def unified_diff
129
- # Calculate item number range.
133
+ # Calculate item number range.
130
134
  s = "@@ -#{unified_range(:old)} +#{unified_range(:new)} @@\n"
131
135
 
132
- # Outlist starts containing the hunk of the old file. Removing an item
133
- # just means putting a '-' in front of it. Inserting an item requires
134
- # getting it from the new file and splicing it in. We splice in
135
- # +num_added+ items. Remove blocks use +num_added+ because splicing
136
- # changed the length of outlist.
137
- #
138
- # We remove +num_removed+ items. Insert blocks use +num_removed+
139
- # because their item numbers -- corresponding to positions in the NEW
140
- # file -- don't take removed items into account.
136
+ # Outlist starts containing the hunk of the old file. Removing an item
137
+ # just means putting a '-' in front of it. Inserting an item requires
138
+ # getting it from the new file and splicing it in. We splice in
139
+ # +num_added+ items. Remove blocks use +num_added+ because splicing
140
+ # changed the length of outlist.
141
+ #
142
+ # We remove +num_removed+ items. Insert blocks use +num_removed+
143
+ # because their item numbers -- corresponding to positions in the NEW
144
+ # file -- don't take removed items into account.
141
145
  lo, hi, num_added, num_removed = @start_old, @end_old, 0, 0
142
146
 
143
147
  outlist = @data_old[lo .. hi].collect { |e| e.gsub(/^/, ' ') }
@@ -159,14 +163,15 @@ class Diff::LCS::Hunk
159
163
 
160
164
  s << outlist.join("\n")
161
165
  end
166
+ private :unified_diff
162
167
 
163
168
  def context_diff
164
169
  s = "***************\n"
165
170
  s << "*** #{context_range(:old)} ****\n"
166
171
  r = context_range(:new)
167
172
 
168
- # Print out file 1 part for each block in context diff format if there
169
- # are any blocks that remove items
173
+ # Print out file 1 part for each block in context diff format if there
174
+ # are any blocks that remove items
170
175
  lo, hi = @start_old, @end_old
171
176
  removes = @blocks.select { |e| not e.remove.empty? }
172
177
  if removes
@@ -193,6 +198,7 @@ class Diff::LCS::Hunk
193
198
  end
194
199
  s
195
200
  end
201
+ private :context_diff
196
202
 
197
203
  def ed_diff(format)
198
204
  op_act = { "+" => 'a', "-" => 'd', "!" => "c" }
@@ -210,9 +216,10 @@ class Diff::LCS::Hunk
210
216
  end
211
217
  s
212
218
  end
219
+ private :ed_diff
213
220
 
214
- # Generate a range of item numbers to print. Only print 1 number if the
215
- # range has only one item in it. Otherwise, it's 'start,end'
221
+ # Generate a range of item numbers to print. Only print 1 number if the
222
+ # range has only one item in it. Otherwise, it's 'start,end'
216
223
  def context_range(mode)
217
224
  case mode
218
225
  when :old
@@ -223,10 +230,11 @@ class Diff::LCS::Hunk
223
230
 
224
231
  (s < e) ? "#{s},#{e}" : "#{e}"
225
232
  end
233
+ private :context_range
226
234
 
227
- # Generate a range of item numbers to print for unified diff. Print
228
- # number where block starts, followed by number of lines in the block
229
- # (don't print number of lines if it's 1)
235
+ # Generate a range of item numbers to print for unified diff. Print number
236
+ # where block starts, followed by number of lines in the block
237
+ # (don't print number of lines if it's 1)
230
238
  def unified_range(mode)
231
239
  case mode
232
240
  when :old
@@ -239,4 +247,5 @@ class Diff::LCS::Hunk
239
247
  first = (length < 2) ? e : s # "strange, but correct"
240
248
  (length == 1) ? "#{first}" : "#{first},#{length}"
241
249
  end
250
+ private :unified_range
242
251
  end