Mercurial > hg > soundsoftware-site
comparison lib/redmine/unified_diff.rb @ 441:cbce1fd3b1b7 redmine-1.2
Update to Redmine 1.2-stable branch (Redmine SVN rev 6000)
author | Chris Cannam |
---|---|
date | Mon, 06 Jun 2011 14:24:13 +0100 |
parents | 051f544170fe |
children | cbb26bc654de |
comparison
equal
deleted
inserted
replaced
245:051f544170fe | 441:cbce1fd3b1b7 |
---|---|
1 # redMine - project management software | 1 # Redmine - project management software |
2 # Copyright (C) 2006-2008 Jean-Philippe Lang | 2 # Copyright (C) 2006-2011 Jean-Philippe Lang |
3 # | 3 # |
4 # This program is free software; you can redistribute it and/or | 4 # This program is free software; you can redistribute it and/or |
5 # modify it under the terms of the GNU General Public License | 5 # modify it under the terms of the GNU General Public License |
6 # as published by the Free Software Foundation; either version 2 | 6 # as published by the Free Software Foundation; either version 2 |
7 # of the License, or (at your option) any later version. | 7 # of the License, or (at your option) any later version. |
15 # along with this program; if not, write to the Free Software | 15 # along with this program; if not, write to the Free Software |
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | 17 |
18 module Redmine | 18 module Redmine |
19 # Class used to parse unified diffs | 19 # Class used to parse unified diffs |
20 class UnifiedDiff < Array | 20 class UnifiedDiff < Array |
21 attr_reader :diff_type | |
22 | |
21 def initialize(diff, options={}) | 23 def initialize(diff, options={}) |
22 options.assert_valid_keys(:type, :max_lines) | 24 options.assert_valid_keys(:type, :max_lines) |
23 diff = diff.split("\n") if diff.is_a?(String) | 25 diff = diff.split("\n") if diff.is_a?(String) |
24 diff_type = options[:type] || 'inline' | 26 @diff_type = options[:type] || 'inline' |
25 lines = 0 | 27 lines = 0 |
26 @truncated = false | 28 @truncated = false |
27 diff_table = DiffTable.new(diff_type) | 29 diff_table = DiffTable.new(@diff_type) |
28 diff.each do |line| | 30 diff.each do |line| |
29 line_encoding = nil | 31 line_encoding = nil |
30 if line.respond_to?(:force_encoding) | 32 if line.respond_to?(:force_encoding) |
31 line_encoding = line.encoding | 33 line_encoding = line.encoding |
32 # TODO: UTF-16 and Japanese CP932 which is imcompatible with ASCII | 34 # TODO: UTF-16 and Japanese CP932 which is imcompatible with ASCII |
51 | 53 |
52 def truncated?; @truncated; end | 54 def truncated?; @truncated; end |
53 end | 55 end |
54 | 56 |
55 # Class that represents a file diff | 57 # Class that represents a file diff |
56 class DiffTable < Hash | 58 class DiffTable < Array |
57 attr_reader :file_name, :line_num_l, :line_num_r | 59 attr_reader :file_name |
58 | 60 |
59 # Initialize with a Diff file and the type of Diff View | 61 # Initialize with a Diff file and the type of Diff View |
60 # The type view must be inline or sbs (side_by_side) | 62 # The type view must be inline or sbs (side_by_side) |
61 def initialize(type="inline") | 63 def initialize(type="inline") |
62 @parsing = false | 64 @parsing = false |
63 @nb_line = 1 | 65 @added = 0 |
64 @start = false | 66 @removed = 0 |
65 @before = 'same' | |
66 @second = true | |
67 @type = type | 67 @type = type |
68 end | 68 end |
69 | 69 |
70 # Function for add a line of this Diff | 70 # Function for add a line of this Diff |
71 # Returns false when the diff ends | 71 # Returns false when the diff ends |
84 return false | 84 return false |
85 elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/ | 85 elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/ |
86 @line_num_l = $2.to_i | 86 @line_num_l = $2.to_i |
87 @line_num_r = $5.to_i | 87 @line_num_r = $5.to_i |
88 else | 88 else |
89 @nb_line += 1 if parse_line(line, @type) | 89 parse_line(line, @type) |
90 end | 90 end |
91 end | 91 end |
92 return true | 92 return true |
93 end | |
94 | |
95 def each_line | |
96 prev_line_left, prev_line_right = nil, nil | |
97 each do |line| | |
98 spacing = prev_line_left && prev_line_right && (line.nb_line_left != prev_line_left+1) && (line.nb_line_right != prev_line_right+1) | |
99 yield spacing, line | |
100 prev_line_left = line.nb_line_left.to_i if line.nb_line_left.to_i > 0 | |
101 prev_line_right = line.nb_line_right.to_i if line.nb_line_right.to_i > 0 | |
102 end | |
93 end | 103 end |
94 | 104 |
95 def inspect | 105 def inspect |
96 puts '### DIFF TABLE ###' | 106 puts '### DIFF TABLE ###' |
97 puts "file : #{file_name}" | 107 puts "file : #{file_name}" |
98 self.each do |d| | 108 self.each do |d| |
99 d.inspect | 109 d.inspect |
100 end | 110 end |
101 end | 111 end |
102 | 112 |
103 private | 113 private |
104 # Test if is a Side By Side type | |
105 def sbs?(type, func) | |
106 if @start and type == "sbs" | |
107 if @before == func and @second | |
108 tmp_nb_line = @nb_line | |
109 self[tmp_nb_line] = Diff.new | |
110 else | |
111 @second = false | |
112 tmp_nb_line = @start | |
113 @start += 1 | |
114 @nb_line -= 1 | |
115 end | |
116 else | |
117 tmp_nb_line = @nb_line | |
118 @start = @nb_line | |
119 self[tmp_nb_line] = Diff.new | |
120 @second = true | |
121 end | |
122 unless self[tmp_nb_line] | |
123 @nb_line += 1 | |
124 self[tmp_nb_line] = Diff.new | |
125 else | |
126 self[tmp_nb_line] | |
127 end | |
128 end | |
129 | 114 |
130 # Escape the HTML for the diff | 115 # Escape the HTML for the diff |
131 def escapeHTML(line) | 116 def escapeHTML(line) |
132 CGI.escapeHTML(line) | 117 CGI.escapeHTML(line) |
133 end | 118 end |
119 | |
120 def diff_for_added_line | |
121 if @type == 'sbs' && @removed > 0 && @added < @removed | |
122 self[-(@removed - @added)] | |
123 else | |
124 diff = Diff.new | |
125 self << diff | |
126 diff | |
127 end | |
128 end | |
134 | 129 |
135 def parse_line(line, type="inline") | 130 def parse_line(line, type="inline") |
136 if line[0, 1] == "+" | 131 if line[0, 1] == "+" |
137 diff = sbs? type, 'add' | 132 diff = diff_for_added_line |
138 @before = 'add' | |
139 diff.line_right = escapeHTML line[1..-1] | 133 diff.line_right = escapeHTML line[1..-1] |
140 diff.nb_line_right = @line_num_r | 134 diff.nb_line_right = @line_num_r |
141 diff.type_diff_right = 'diff_in' | 135 diff.type_diff_right = 'diff_in' |
142 @line_num_r += 1 | 136 @line_num_r += 1 |
137 @added += 1 | |
143 true | 138 true |
144 elsif line[0, 1] == "-" | 139 elsif line[0, 1] == "-" |
145 diff = sbs? type, 'remove' | 140 diff = Diff.new |
146 @before = 'remove' | |
147 diff.line_left = escapeHTML line[1..-1] | 141 diff.line_left = escapeHTML line[1..-1] |
148 diff.nb_line_left = @line_num_l | 142 diff.nb_line_left = @line_num_l |
149 diff.type_diff_left = 'diff_out' | 143 diff.type_diff_left = 'diff_out' |
144 self << diff | |
150 @line_num_l += 1 | 145 @line_num_l += 1 |
146 @removed += 1 | |
151 true | 147 true |
152 elsif line[0, 1] =~ /\s/ | 148 else |
153 @before = 'same' | 149 write_offsets |
154 @start = false | 150 if line[0, 1] =~ /\s/ |
155 diff = Diff.new | 151 diff = Diff.new |
156 diff.line_right = escapeHTML line[1..-1] | 152 diff.line_right = escapeHTML line[1..-1] |
157 diff.nb_line_right = @line_num_r | 153 diff.nb_line_right = @line_num_r |
158 diff.line_left = escapeHTML line[1..-1] | 154 diff.line_left = escapeHTML line[1..-1] |
159 diff.nb_line_left = @line_num_l | 155 diff.nb_line_left = @line_num_l |
160 self[@nb_line] = diff | 156 self << diff |
161 @line_num_l += 1 | 157 @line_num_l += 1 |
162 @line_num_r += 1 | 158 @line_num_r += 1 |
163 true | 159 true |
164 elsif line[0, 1] = "\\" | 160 elsif line[0, 1] = "\\" |
165 true | 161 true |
166 else | 162 else |
167 false | 163 false |
168 end | 164 end |
169 end | 165 end |
170 end | 166 end |
167 | |
168 def write_offsets | |
169 if @added > 0 && @added == @removed | |
170 @added.times do |i| | |
171 line = self[-(1 + i)] | |
172 removed = (@type == 'sbs') ? line : self[-(1 + @added + i)] | |
173 offsets = offsets(removed.line_left, line.line_right) | |
174 removed.offsets = line.offsets = offsets | |
175 end | |
176 end | |
177 @added = 0 | |
178 @removed = 0 | |
179 end | |
180 | |
181 def offsets(line_left, line_right) | |
182 if line_left.present? && line_right.present? && line_left != line_right | |
183 max = [line_left.size, line_right.size].min | |
184 starting = 0 | |
185 while starting < max && line_left[starting] == line_right[starting] | |
186 starting += 1 | |
187 end | |
188 ending = -1 | |
189 while ending >= -(max - starting) && line_left[ending] == line_right[ending] | |
190 ending -= 1 | |
191 end | |
192 unless starting == 0 && ending == -1 | |
193 [starting, ending] | |
194 end | |
195 end | |
196 end | |
197 end | |
171 | 198 |
172 # A line of diff | 199 # A line of diff |
173 class Diff | 200 class Diff |
174 attr_accessor :nb_line_left | 201 attr_accessor :nb_line_left |
175 attr_accessor :line_left | 202 attr_accessor :line_left |
176 attr_accessor :nb_line_right | 203 attr_accessor :nb_line_right |
177 attr_accessor :line_right | 204 attr_accessor :line_right |
178 attr_accessor :type_diff_right | 205 attr_accessor :type_diff_right |
179 attr_accessor :type_diff_left | 206 attr_accessor :type_diff_left |
207 attr_accessor :offsets | |
180 | 208 |
181 def initialize() | 209 def initialize() |
182 self.nb_line_left = '' | 210 self.nb_line_left = '' |
183 self.nb_line_right = '' | 211 self.nb_line_right = '' |
184 self.line_left = '' | 212 self.line_left = '' |
185 self.line_right = '' | 213 self.line_right = '' |
186 self.type_diff_right = '' | 214 self.type_diff_right = '' |
187 self.type_diff_left = '' | 215 self.type_diff_left = '' |
188 end | 216 end |
217 | |
218 def type_diff | |
219 type_diff_right == 'diff_in' ? type_diff_right : type_diff_left | |
220 end | |
221 | |
222 def line | |
223 type_diff_right == 'diff_in' ? line_right : line_left | |
224 end | |
225 | |
226 def html_line_left | |
227 if offsets | |
228 line_left.dup.insert(offsets.first, '<span>').insert(offsets.last, '</span>') | |
229 else | |
230 line_left | |
231 end | |
232 end | |
233 | |
234 def html_line_right | |
235 if offsets | |
236 line_right.dup.insert(offsets.first, '<span>').insert(offsets.last, '</span>') | |
237 else | |
238 line_right | |
239 end | |
240 end | |
241 | |
242 def html_line | |
243 if offsets | |
244 line.dup.insert(offsets.first, '<span>').insert(offsets.last, '</span>') | |
245 else | |
246 line | |
247 end | |
248 end | |
189 | 249 |
190 def inspect | 250 def inspect |
191 puts '### Start Line Diff ###' | 251 puts '### Start Line Diff ###' |
192 puts self.nb_line_left | 252 puts self.nb_line_left |
193 puts self.line_left | 253 puts self.line_left |