Mercurial > hg > soundsoftware-site
comparison .svn/pristine/d4/d40bacc3af8fb7641b23c992be523d5d162b016d.svn-base @ 1298:4f746d8966dd redmine_2.3_integration
Merge from redmine-2.3 branch to create new branch redmine-2.3-integration
author | Chris Cannam |
---|---|
date | Fri, 14 Jun 2013 09:28:30 +0100 |
parents | 622f24f53b42 |
children |
comparison
equal
deleted
inserted
replaced
1297:0a574315af3e | 1298:4f746d8966dd |
---|---|
1 # Redmine - project management software | |
2 # Copyright (C) 2006-2013 Jean-Philippe Lang | |
3 # | |
4 # This program is free software; you can redistribute it and/or | |
5 # modify it under the terms of the GNU General Public License | |
6 # as published by the Free Software Foundation; either version 2 | |
7 # of the License, or (at your option) any later version. | |
8 # | |
9 # This program is distributed in the hope that it will be useful, | |
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 # GNU General Public License for more details. | |
13 # | |
14 # You should have received a copy of the GNU General Public License | |
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. | |
17 | |
18 module Redmine | |
19 # Class used to parse unified diffs | |
20 class UnifiedDiff < Array | |
21 attr_reader :diff_type, :diff_style | |
22 | |
23 def initialize(diff, options={}) | |
24 options.assert_valid_keys(:type, :style, :max_lines) | |
25 diff = diff.split("\n") if diff.is_a?(String) | |
26 @diff_type = options[:type] || 'inline' | |
27 @diff_style = options[:style] | |
28 lines = 0 | |
29 @truncated = false | |
30 diff_table = DiffTable.new(diff_type, diff_style) | |
31 diff.each do |line_raw| | |
32 line = Redmine::CodesetUtil.to_utf8_by_setting(line_raw) | |
33 unless diff_table.add_line(line) | |
34 self << diff_table if diff_table.length > 0 | |
35 diff_table = DiffTable.new(diff_type, diff_style) | |
36 end | |
37 lines += 1 | |
38 if options[:max_lines] && lines > options[:max_lines] | |
39 @truncated = true | |
40 break | |
41 end | |
42 end | |
43 self << diff_table unless diff_table.empty? | |
44 self | |
45 end | |
46 | |
47 def truncated?; @truncated; end | |
48 end | |
49 | |
50 # Class that represents a file diff | |
51 class DiffTable < Array | |
52 attr_reader :file_name | |
53 | |
54 # Initialize with a Diff file and the type of Diff View | |
55 # The type view must be inline or sbs (side_by_side) | |
56 def initialize(type="inline", style=nil) | |
57 @parsing = false | |
58 @added = 0 | |
59 @removed = 0 | |
60 @type = type | |
61 @style = style | |
62 @file_name = nil | |
63 @git_diff = false | |
64 end | |
65 | |
66 # Function for add a line of this Diff | |
67 # Returns false when the diff ends | |
68 def add_line(line) | |
69 unless @parsing | |
70 if line =~ /^(---|\+\+\+) (.*)$/ | |
71 self.file_name = $2 | |
72 elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/ | |
73 @line_num_l = $2.to_i | |
74 @line_num_r = $5.to_i | |
75 @parsing = true | |
76 end | |
77 else | |
78 if line =~ %r{^[^\+\-\s@\\]} | |
79 @parsing = false | |
80 return false | |
81 elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/ | |
82 @line_num_l = $2.to_i | |
83 @line_num_r = $5.to_i | |
84 else | |
85 parse_line(line, @type) | |
86 end | |
87 end | |
88 return true | |
89 end | |
90 | |
91 def each_line | |
92 prev_line_left, prev_line_right = nil, nil | |
93 each do |line| | |
94 spacing = prev_line_left && prev_line_right && (line.nb_line_left != prev_line_left+1) && (line.nb_line_right != prev_line_right+1) | |
95 yield spacing, line | |
96 prev_line_left = line.nb_line_left.to_i if line.nb_line_left.to_i > 0 | |
97 prev_line_right = line.nb_line_right.to_i if line.nb_line_right.to_i > 0 | |
98 end | |
99 end | |
100 | |
101 def inspect | |
102 puts '### DIFF TABLE ###' | |
103 puts "file : #{file_name}" | |
104 self.each do |d| | |
105 d.inspect | |
106 end | |
107 end | |
108 | |
109 private | |
110 | |
111 def file_name=(arg) | |
112 both_git_diff = false | |
113 if file_name.nil? | |
114 @git_diff = true if arg =~ %r{^(a/|/dev/null)} | |
115 else | |
116 both_git_diff = (@git_diff && arg =~ %r{^(b/|/dev/null)}) | |
117 end | |
118 if both_git_diff | |
119 if file_name && arg == "/dev/null" | |
120 # keep the original file name | |
121 @file_name = file_name.sub(%r{^a/}, '') | |
122 else | |
123 # remove leading b/ | |
124 @file_name = arg.sub(%r{^b/}, '') | |
125 end | |
126 elsif @style == "Subversion" | |
127 # removing trailing "(revision nn)" | |
128 @file_name = arg.sub(%r{\t+\(.*\)$}, '') | |
129 else | |
130 @file_name = arg | |
131 end | |
132 end | |
133 | |
134 def diff_for_added_line | |
135 if @type == 'sbs' && @removed > 0 && @added < @removed | |
136 self[-(@removed - @added)] | |
137 else | |
138 diff = Diff.new | |
139 self << diff | |
140 diff | |
141 end | |
142 end | |
143 | |
144 def parse_line(line, type="inline") | |
145 if line[0, 1] == "+" | |
146 diff = diff_for_added_line | |
147 diff.line_right = line[1..-1] | |
148 diff.nb_line_right = @line_num_r | |
149 diff.type_diff_right = 'diff_in' | |
150 @line_num_r += 1 | |
151 @added += 1 | |
152 true | |
153 elsif line[0, 1] == "-" | |
154 diff = Diff.new | |
155 diff.line_left = line[1..-1] | |
156 diff.nb_line_left = @line_num_l | |
157 diff.type_diff_left = 'diff_out' | |
158 self << diff | |
159 @line_num_l += 1 | |
160 @removed += 1 | |
161 true | |
162 else | |
163 write_offsets | |
164 if line[0, 1] =~ /\s/ | |
165 diff = Diff.new | |
166 diff.line_right = line[1..-1] | |
167 diff.nb_line_right = @line_num_r | |
168 diff.line_left = line[1..-1] | |
169 diff.nb_line_left = @line_num_l | |
170 self << diff | |
171 @line_num_l += 1 | |
172 @line_num_r += 1 | |
173 true | |
174 elsif line[0, 1] = "\\" | |
175 true | |
176 else | |
177 false | |
178 end | |
179 end | |
180 end | |
181 | |
182 def write_offsets | |
183 if @added > 0 && @added == @removed | |
184 @added.times do |i| | |
185 line = self[-(1 + i)] | |
186 removed = (@type == 'sbs') ? line : self[-(1 + @added + i)] | |
187 offsets = offsets(removed.line_left, line.line_right) | |
188 removed.offsets = line.offsets = offsets | |
189 end | |
190 end | |
191 @added = 0 | |
192 @removed = 0 | |
193 end | |
194 | |
195 def offsets(line_left, line_right) | |
196 if line_left.present? && line_right.present? && line_left != line_right | |
197 max = [line_left.size, line_right.size].min | |
198 starting = 0 | |
199 while starting < max && line_left[starting] == line_right[starting] | |
200 starting += 1 | |
201 end | |
202 if (! "".respond_to?(:force_encoding)) && starting < line_left.size | |
203 while line_left[starting].ord.between?(128, 191) && starting > 0 | |
204 starting -= 1 | |
205 end | |
206 end | |
207 ending = -1 | |
208 while ending >= -(max - starting) && line_left[ending] == line_right[ending] | |
209 ending -= 1 | |
210 end | |
211 if (! "".respond_to?(:force_encoding)) && ending > (-1 * line_left.size) | |
212 while line_left[ending].ord.between?(128, 191) && ending > -1 | |
213 ending -= 1 | |
214 end | |
215 end | |
216 unless starting == 0 && ending == -1 | |
217 [starting, ending] | |
218 end | |
219 end | |
220 end | |
221 end | |
222 | |
223 # A line of diff | |
224 class Diff | |
225 attr_accessor :nb_line_left | |
226 attr_accessor :line_left | |
227 attr_accessor :nb_line_right | |
228 attr_accessor :line_right | |
229 attr_accessor :type_diff_right | |
230 attr_accessor :type_diff_left | |
231 attr_accessor :offsets | |
232 | |
233 def initialize() | |
234 self.nb_line_left = '' | |
235 self.nb_line_right = '' | |
236 self.line_left = '' | |
237 self.line_right = '' | |
238 self.type_diff_right = '' | |
239 self.type_diff_left = '' | |
240 end | |
241 | |
242 def type_diff | |
243 type_diff_right == 'diff_in' ? type_diff_right : type_diff_left | |
244 end | |
245 | |
246 def line | |
247 type_diff_right == 'diff_in' ? line_right : line_left | |
248 end | |
249 | |
250 def html_line_left | |
251 line_to_html(line_left, offsets) | |
252 end | |
253 | |
254 def html_line_right | |
255 line_to_html(line_right, offsets) | |
256 end | |
257 | |
258 def html_line | |
259 line_to_html(line, offsets) | |
260 end | |
261 | |
262 def inspect | |
263 puts '### Start Line Diff ###' | |
264 puts self.nb_line_left | |
265 puts self.line_left | |
266 puts self.nb_line_right | |
267 puts self.line_right | |
268 end | |
269 | |
270 private | |
271 | |
272 def line_to_html(line, offsets) | |
273 html = line_to_html_raw(line, offsets) | |
274 html.force_encoding('UTF-8') if html.respond_to?(:force_encoding) | |
275 html | |
276 end | |
277 | |
278 def line_to_html_raw(line, offsets) | |
279 if offsets | |
280 s = '' | |
281 unless offsets.first == 0 | |
282 s << CGI.escapeHTML(line[0..offsets.first-1]) | |
283 end | |
284 s << '<span>' + CGI.escapeHTML(line[offsets.first..offsets.last]) + '</span>' | |
285 unless offsets.last == -1 | |
286 s << CGI.escapeHTML(line[offsets.last+1..-1]) | |
287 end | |
288 s | |
289 else | |
290 CGI.escapeHTML(line) | |
291 end | |
292 end | |
293 end | |
294 end |