Name | Total Lines | Lines of Code | Total Coverage | Code Coverage |
---|---|---|---|---|
rcov/ruby/1.8/gems/diff-lcs-1.1.2/lib/diff/lcs/hunk.rb | 257 | 170 | 25.29%
|
11.76%
|
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
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: hunk.rb,v 1.2 2004/08/08 20:33:09 austin Exp $ |
14 #++ |
15 # Contains Diff::LCS::Hunk for bin/ldiff. |
16 |
17 require 'diff/lcs/block' |
18 |
19 # A Hunk is a group of Blocks which overlap because of the context |
20 # surrounding each block. (So if we're not using context, every hunk will |
21 # contain one block.) Used in the diff program (bin/diff). |
22 class Diff::LCS::Hunk |
23 # Create a hunk using references to both the old and new data, as well as |
24 # the piece of data |
25 def initialize(data_old, data_new, piece, context, file_length_difference) |
26 # At first, a hunk will have just one Block in it |
27 @blocks = [ Diff::LCS::Block.new(piece) ] |
28 @data_old = data_old |
29 @data_new = data_new |
30 |
31 before = after = file_length_difference |
32 after += @blocks[0].diff_size |
33 @file_length_difference = after # The caller must get this manually |
34 |
35 # Save the start & end of each array. If the array doesn't exist |
36 # (e.g., we're only adding items in this block), then figure out the |
37 # line number based on the line number of the other file and the |
38 # current difference in file lengths. |
39 if @blocks[0].remove.empty? |
40 a1 = a2 = nil |
41 else |
42 a1 = @blocks[0].remove[0].position |
43 a2 = @blocks[0].remove[-1].position |
44 end |
45 |
46 if @blocks[0].insert.empty? |
47 b1 = b2 = nil |
48 else |
49 b1 = @blocks[0].insert[0].position |
50 b2 = @blocks[0].insert[-1].position |
51 end |
52 |
53 @start_old = a1 || (b1 - before) |
54 @start_new = b1 || (a1 + before) |
55 @end_old = a2 || (b2 - after) |
56 @end_new = b2 || (a2 + after) |
57 |
58 self.flag_context = context |
59 end |
60 |
61 attr_reader :blocks |
62 attr_reader :start_old, :start_new |
63 attr_reader :end_old, :end_new |
64 attr_reader :file_length_difference |
65 |
66 # Change the "start" and "end" fields to note that context should be added |
67 # to this hunk |
68 attr_accessor :flag_context |
69 def flag_context=(context) #:nodoc: |
70 return if context.nil? or context.zero? |
71 |
72 add_start = (context > @start_old) ? @start_old : context |
73 @start_old -= add_start |
74 @start_new -= add_start |
75 |
76 if (@end_old + context) > @data_old.size |
77 add_end = @data_old.size - @end_old |
78 else |
79 add_end = context |
80 end |
81 @end_old += add_end |
82 @end_new += add_end |
83 end |
84 |
85 def unshift(hunk) |
86 @start_old = hunk.start_old |
87 @start_new = hunk.start_new |
88 blocks.unshift(*hunk.blocks) |
89 end |
90 |
91 # Is there an overlap between hunk arg0 and old hunk arg1? Note: if end |
92 # of old hunk is one less than beginning of second, they overlap |
93 def overlaps?(hunk = nil) |
94 return nil if hunk.nil? |
95 |
96 a = (@start_old - hunk.end_old) <= 1 |
97 b = (@start_new - hunk.end_new) <= 1 |
98 return (a or b) |
99 end |
100 |
101 def diff(format) |
102 case format |
103 when :old |
104 old_diff |
105 when :unified |
106 unified_diff |
107 when :context |
108 context_diff |
109 when :ed |
110 self |
111 when :reverse_ed, :ed_finish |
112 ed_diff(format) |
113 else |
114 raise "Unknown diff format #{format}." |
115 end |
116 end |
117 |
118 def each_old(block) |
119 @data_old[@start_old .. @end_old].each { |e| yield e } |
120 end |
121 |
122 private |
123 # Note that an old diff can't have any context. Therefore, we know that |
124 # there's only one block in the hunk. |
125 def old_diff |
126 warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1 |
127 op_act = { "+" => 'a', "-" => 'd', "!" => "c" } |
128 |
129 block = @blocks[0] |
130 |
131 # Calculate item number range. Old diff range is just like a context |
132 # diff range, except the ranges are on one line with the action between |
133 # them. |
134 s = "#{context_range(:old)}#{op_act[block.op]}#{context_range(:new)}\n" |
135 # If removing anything, just print out all the remove lines in the hunk |
136 # which is just all the remove lines in the block. |
137 @data_old[@start_old .. @end_old].each { |e| s << "< #{e}\n" } unless block.remove.empty? |
138 s << "---\n" if block.op == "!" |
139 @data_new[@start_new .. @end_new].each { |e| s << "> #{e}\n" } unless block.insert.empty? |
140 s |
141 end |
142 |
143 def unified_diff |
144 # Calculate item number range. |
145 s = "@@ -#{unified_range(:old)} +#{unified_range(:new)} @@\n" |
146 |
147 # Outlist starts containing the hunk of the old file. Removing an item |
148 # just means putting a '-' in front of it. Inserting an item requires |
149 # getting it from the new file and splicing it in. We splice in |
150 # +num_added+ items. Remove blocks use +num_added+ because splicing |
151 # changed the length of outlist. |
152 # |
153 # We remove +num_removed+ items. Insert blocks use +num_removed+ |
154 # because their item numbers -- corresponding to positions in the NEW |
155 # file -- don't take removed items into account. |
156 lo, hi, num_added, num_removed = @start_old, @end_old, 0, 0 |
157 |
158 outlist = @data_old[lo .. hi].collect { |e| e.gsub(/^/, ' ') } |
159 |
160 @blocks.each do |block| |
161 block.remove.each do |item| |
162 op = item.action.to_s # - |
163 offset = item.position - lo + num_added |
164 outlist[offset].gsub!(/^ /, op.to_s) |
165 num_removed += 1 |
166 end |
167 block.insert.each do |item| |
168 op = item.action.to_s # + |
169 offset = item.position - @start_new + num_removed |
170 outlist[offset, 0] = "#{op}#{@data_new[item.position]}" |
171 num_added += 1 |
172 end |
173 end |
174 |
175 s << outlist.join("\n") |
176 end |
177 |
178 def context_diff |
179 s = "***************\n" |
180 s << "*** #{context_range(:old)} ****\n" |
181 r = context_range(:new) |
182 |
183 # Print out file 1 part for each block in context diff format if there |
184 # are any blocks that remove items |
185 lo, hi = @start_old, @end_old |
186 removes = @blocks.select { |e| not e.remove.empty? } |
187 if removes |
188 outlist = @data_old[lo .. hi].collect { |e| e.gsub(/^/, ' ') } |
189 removes.each do |block| |
190 block.remove.each do |item| |
191 outlist[item.position - lo].gsub!(/^ /) { block.op } # - or ! |
192 end |
193 end |
194 s << outlist.join("\n") |
195 end |
196 |
197 s << "\n--- #{r} ----\n" |
198 lo, hi = @start_new, @end_new |
199 inserts = @blocks.select { |e| not e.insert.empty? } |
200 if inserts |
201 outlist = @data_new[lo .. hi].collect { |e| e.gsub(/^/, ' ') } |
202 inserts.each do |block| |
203 block.insert.each do |item| |
204 outlist[item.position - lo].gsub!(/^ /) { block.op } # + or ! |
205 end |
206 end |
207 s << outlist.join("\n") |
208 end |
209 s |
210 end |
211 |
212 def ed_diff(format) |
213 op_act = { "+" => 'a', "-" => 'd', "!" => "c" } |
214 warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1 |
215 |
216 if format == :reverse_ed |
217 s = "#{op_act[@blocks[0].op]}#{context_range(:old)}\n" |
218 else |
219 s = "#{context_range(:old).gsub(/,/, ' ')}#{op_act[@blocks[0].op]}\n" |
220 end |
221 |
222 unless @blocks[0].insert.empty? |
223 @data_new[@start_new .. @end_new].each { |e| s << "#{e}\n" } |
224 s << ".\n" |
225 end |
226 s |
227 end |
228 |
229 # Generate a range of item numbers to print. Only print 1 number if the |
230 # range has only one item in it. Otherwise, it's 'start,end' |
231 def context_range(mode) |
232 case mode |
233 when :old |
234 s, e = (@start_old + 1), (@end_old + 1) |
235 when :new |
236 s, e = (@start_new + 1), (@end_new + 1) |
237 end |
238 |
239 (s < e) ? "#{s},#{e}" : "#{e}" |
240 end |
241 |
242 # Generate a range of item numbers to print for unified diff. Print |
243 # number where block starts, followed by number of lines in the block |
244 # (don't print number of lines if it's 1) |
245 def unified_range(mode) |
246 case mode |
247 when :old |
248 s, e = (@start_old + 1), (@end_old + 1) |
249 when :new |
250 s, e = (@start_new + 1), (@end_new + 1) |
251 end |
252 |
253 length = e - s + 1 |
254 first = (length < 2) ? e : s # "strange, but correct" |
255 (length == 1) ? "#{first}" : "#{first},#{length}" |
256 end |
257 end |
Generated on Fri Apr 22 17:22:41 -0700 2011 with rcov 0.9.8