##// END OF EJS Templates
Highlight changes inside diff lines (#7139)....
Jean-Philippe Lang -
r4974:21c79827ff73
parent child
Show More
@@ -0,0 +1,46
1 --- partials.txt Wed Jan 19 12:06:17 2011
2 +++ partials.1.txt Wed Jan 19 12:06:10 2011
3 @@ -1,31 +1,31 @@
4 -Lorem ipsum dolor sit amet, consectetur adipiscing elit
5 +Lorem ipsum dolor sit amet, consectetur adipiscing xx
6 Praesent et sagittis dui. Vivamus ac diam diam
7 -Ut sed auctor justo
8 +xxx auctor justo
9 Suspendisse venenatis sollicitudin magna quis suscipit
10 -Sed blandit gravida odio ac ultrices
11 +Sed blandit gxxxxa odio ac ultrices
12 Morbi rhoncus est ut est aliquam tempus
13 -Morbi id nisi vel felis tincidunt tempus
14 +Morbi id nisi vel felis xx tempus
15 Mauris auctor sagittis ante eu luctus
16 -Fusce commodo felis sed ligula congue molestie
17 +Fusce commodo felis sed ligula congue
18 Lorem ipsum dolor sit amet, consectetur adipiscing elit
19 -Praesent et sagittis dui. Vivamus ac diam diam
20 +et sagittis dui. Vivamus ac diam diam
21 Ut sed auctor justo
22 Suspendisse venenatis sollicitudin magna quis suscipit
23 Sed blandit gravida odio ac ultrices
24
25 -Lorem ipsum dolor sit amet, consectetur adipiscing elit
26 -Praesent et sagittis dui. Vivamus ac diam diam
27 +Lorem ipsum dolor sit amet, xxxx adipiscing elit
28 Ut sed auctor justo
29 Suspendisse venenatis sollicitudin magna quis suscipit
30 Sed blandit gravida odio ac ultrices
31 -Morbi rhoncus est ut est aliquam tempus
32 +Morbi rhoncus est ut est xxxx tempus
33 +New line
34 Morbi id nisi vel felis tincidunt tempus
35 Mauris auctor sagittis ante eu luctus
36 Fusce commodo felis sed ligula congue molestie
37
38 -Lorem ipsum dolor sit amet, consectetur adipiscing elit
39 -Praesent et sagittis dui. Vivamus ac diam diam
40 -Ut sed auctor justo
41 +Lorem ipsum dolor sit amet, xxxxtetur adipiscing elit
42 +Praesent et xxxxx. Vivamus ac diam diam
43 +Ut sed auctor
44 Suspendisse venenatis sollicitudin magna quis suscipit
45 Sed blandit gravida odio ac ultrices
46 Morbi rhoncus est ut est aliquam tempus
@@ -1,66 +1,56
1 1 <% diff = Redmine::UnifiedDiff.new(diff, :type => diff_type, :max_lines => Setting.diff_max_lines_displayed.to_i) -%>
2
2 3 <% diff.each do |table_file| -%>
3 4 <div class="autoscroll">
4 <% if diff_type == 'sbs' -%>
5 <% if diff.diff_type == 'sbs' -%>
5 6 <table class="filecontent">
6 7 <thead>
7 8 <tr><th colspan="4" class="filename"><%=to_utf8 table_file.file_name %></th></tr>
8 9 </thead>
9 10 <tbody>
10 <% prev_line_left, prev_line_right = nil, nil -%>
11 <% table_file.keys.sort.each do |key| -%>
12 <% if prev_line_left && prev_line_right && (table_file[key].nb_line_left != prev_line_left+1) && (table_file[key].nb_line_right != prev_line_right+1) -%>
11 <% table_file.each_line do |spacing, line| -%>
12 <% if spacing -%>
13 13 <tr class="spacing">
14 14 <th class="line-num">...</th><td></td><th class="line-num">...</th><td></td>
15 </tr>
15 16 <% end -%>
16 17 <tr>
17 <th class="line-num"><%= table_file[key].nb_line_left %></th>
18 <td class="line-code <%= table_file[key].type_diff_left %>">
19 <pre><%=to_utf8 table_file[key].line_left %></pre>
18 <th class="line-num"><%= line.nb_line_left %></th>
19 <td class="line-code <%= line.type_diff_left %>">
20 <pre><%=to_utf8 line.html_line_left %></pre>
20 21 </td>
21 <th class="line-num"><%= table_file[key].nb_line_right %></th>
22 <td class="line-code <%= table_file[key].type_diff_right %>">
23 <pre><%=to_utf8 table_file[key].line_right %></pre>
22 <th class="line-num"><%= line.nb_line_right %></th>
23 <td class="line-code <%= line.type_diff_right %>">
24 <pre><%=to_utf8 line.html_line_right %></pre>
24 25 </td>
25 26 </tr>
26 <% prev_line_left, prev_line_right = table_file[key].nb_line_left.to_i, table_file[key].nb_line_right.to_i -%>
27 27 <% end -%>
28 28 </tbody>
29 29 </table>
30 30
31 31 <% else -%>
32 <table class="filecontent syntaxhl">
32 <table class="filecontent">
33 33 <thead>
34 34 <tr><th colspan="3" class="filename"><%=to_utf8 table_file.file_name %></th></tr>
35 35 </thead>
36 36 <tbody>
37 <% prev_line_left, prev_line_right = nil, nil -%>
38 <% table_file.keys.sort.each do |key, line| %>
39 <% if prev_line_left && prev_line_right && (table_file[key].nb_line_left != prev_line_left+1) && (table_file[key].nb_line_right != prev_line_right+1) -%>
37 <% table_file.each_line do |spacing, line| %>
38 <% if spacing -%>
40 39 <tr class="spacing">
41 40 <th class="line-num">...</th><th class="line-num">...</th><td></td>
42 41 </tr>
43 42 <% end -%>
44 43 <tr>
45 <th class="line-num"><%= table_file[key].nb_line_left %></th>
46 <th class="line-num"><%= table_file[key].nb_line_right %></th>
47 <% if table_file[key].line_left.empty? -%>
48 <td class="line-code <%= table_file[key].type_diff_right %>">
49 <pre><%=to_utf8 table_file[key].line_right %></pre>
44 <th class="line-num"><%= line.nb_line_left %></th>
45 <th class="line-num"><%= line.nb_line_right %></th>
46 <td class="line-code <%= line.type_diff %>">
47 <pre><%=to_utf8 line.html_line %></pre>
50 48 </td>
51 <% else -%>
52 <td class="line-code <%= table_file[key].type_diff_left %>">
53 <pre><%=to_utf8 table_file[key].line_left %></pre>
54 </td>
55 <% end -%>
56 49 </tr>
57 <% prev_line_left = table_file[key].nb_line_left.to_i if table_file[key].nb_line_left.to_i > 0 -%>
58 <% prev_line_right = table_file[key].nb_line_right.to_i if table_file[key].nb_line_right.to_i > 0 -%>
59 50 <% end -%>
60 51 </tbody>
61 52 </table>
62 53 <% end -%>
63
64 54 </div>
65 55 <% end -%>
66 56
@@ -1,5 +1,5
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
@@ -18,13 +18,15
18 18 module Redmine
19 19 # Class used to parse unified diffs
20 20 class UnifiedDiff < Array
21 attr_reader :diff_type
22
21 23 def initialize(diff, options={})
22 24 options.assert_valid_keys(:type, :max_lines)
23 25 diff = diff.split("\n") if diff.is_a?(String)
24 diff_type = options[:type] || 'inline'
26 @diff_type = options[:type] || 'inline'
25 27 lines = 0
26 28 @truncated = false
27 diff_table = DiffTable.new(diff_type)
29 diff_table = DiffTable.new(@diff_type)
28 30 diff.each do |line|
29 31 line_encoding = nil
30 32 if line.respond_to?(:force_encoding)
@@ -53,17 +55,15 module Redmine
53 55 end
54 56
55 57 # Class that represents a file diff
56 class DiffTable < Hash
57 attr_reader :file_name, :line_num_l, :line_num_r
58 class DiffTable < Array
59 attr_reader :file_name
58 60
59 61 # Initialize with a Diff file and the type of Diff View
60 62 # The type view must be inline or sbs (side_by_side)
61 63 def initialize(type="inline")
62 64 @parsing = false
63 @nb_line = 1
64 @start = false
65 @before = 'same'
66 @second = true
65 @added = 0
66 @removed = 0
67 67 @type = type
68 68 end
69 69
@@ -86,12 +86,22 module Redmine
86 86 @line_num_l = $2.to_i
87 87 @line_num_r = $5.to_i
88 88 else
89 @nb_line += 1 if parse_line(line, @type)
89 parse_line(line, @type)
90 90 end
91 91 end
92 92 return true
93 93 end
94 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
103 end
104
95 105 def inspect
96 106 puts '### DIFF TABLE ###'
97 107 puts "file : #{file_name}"
@@ -101,63 +111,49 module Redmine
101 111 end
102 112
103 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 115 # Escape the HTML for the diff
131 116 def escapeHTML(line)
132 117 CGI.escapeHTML(line)
133 118 end
134 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
129
135 130 def parse_line(line, type="inline")
136 131 if line[0, 1] == "+"
137 diff = sbs? type, 'add'
138 @before = 'add'
132 diff = diff_for_added_line
139 133 diff.line_right = escapeHTML line[1..-1]
140 134 diff.nb_line_right = @line_num_r
141 135 diff.type_diff_right = 'diff_in'
142 136 @line_num_r += 1
137 @added += 1
143 138 true
144 139 elsif line[0, 1] == "-"
145 diff = sbs? type, 'remove'
146 @before = 'remove'
140 diff = Diff.new
147 141 diff.line_left = escapeHTML line[1..-1]
148 142 diff.nb_line_left = @line_num_l
149 143 diff.type_diff_left = 'diff_out'
144 self << diff
150 145 @line_num_l += 1
146 @removed += 1
151 147 true
152 elsif line[0, 1] =~ /\s/
153 @before = 'same'
154 @start = false
148 else
149 write_offsets
150 if line[0, 1] =~ /\s/
155 151 diff = Diff.new
156 152 diff.line_right = escapeHTML line[1..-1]
157 153 diff.nb_line_right = @line_num_r
158 154 diff.line_left = escapeHTML line[1..-1]
159 155 diff.nb_line_left = @line_num_l
160 self[@nb_line] = diff
156 self << diff
161 157 @line_num_l += 1
162 158 @line_num_r += 1
163 159 true
@@ -169,6 +165,37 module Redmine
169 165 end
170 166 end
171 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
198
172 199 # A line of diff
173 200 class Diff
174 201 attr_accessor :nb_line_left
@@ -177,6 +204,7 module Redmine
177 204 attr_accessor :line_right
178 205 attr_accessor :type_diff_right
179 206 attr_accessor :type_diff_left
207 attr_accessor :offsets
180 208
181 209 def initialize()
182 210 self.nb_line_left = ''
@@ -187,6 +215,38 module Redmine
187 215 self.type_diff_left = ''
188 216 end
189 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
249
190 250 def inspect
191 251 puts '### Start Line Diff ###'
192 252 puts self.nb_line_left
@@ -672,7 +672,9 div.autocomplete ul li span.informal {
672 672
673 673 /***** Diff *****/
674 674 .diff_out { background: #fcc; }
675 .diff_out span { background: #faa; }
675 676 .diff_in { background: #cfc; }
677 .diff_in span { background: #afa; }
676 678
677 679 .text-diff {
678 680 padding: 1em;
@@ -1,5 +1,5
1 1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
@@ -34,6 +34,63 class Redmine::UnifiedDiffTest < ActiveSupport::TestCase
34 34 assert_equal 2, diff.size
35 35 end
36 36
37 def test_inline_partials
38 diff = Redmine::UnifiedDiff.new(read_diff_fixture('partials.diff'))
39 assert_equal 1, diff.size
40 diff = diff.first
41 assert_equal 43, diff.size
42
43 assert_equal [51, -1], diff[0].offsets
44 assert_equal [51, -1], diff[1].offsets
45 assert_equal 'Lorem ipsum dolor sit amet, consectetur adipiscing <span>elit</span>', diff[0].html_line
46 assert_equal 'Lorem ipsum dolor sit amet, consectetur adipiscing <span>xx</span>', diff[1].html_line
47
48 assert_nil diff[2].offsets
49 assert_equal 'Praesent et sagittis dui. Vivamus ac diam diam', diff[2].html_line
50
51 assert_equal [0, -14], diff[3].offsets
52 assert_equal [0, -14], diff[4].offsets
53 assert_equal '<span>Ut sed</span> auctor justo', diff[3].html_line
54 assert_equal '<span>xxx</span> auctor justo', diff[4].html_line
55
56 assert_equal [13, -19], diff[6].offsets
57 assert_equal [13, -19], diff[7].offsets
58
59 assert_equal [24, -8], diff[9].offsets
60 assert_equal [24, -8], diff[10].offsets
61
62 assert_equal [37, -1], diff[12].offsets
63 assert_equal [37, -1], diff[13].offsets
64
65 assert_equal [0, -38], diff[15].offsets
66 assert_equal [0, -38], diff[16].offsets
67 end
68
69 def test_side_by_side_partials
70 diff = Redmine::UnifiedDiff.new(read_diff_fixture('partials.diff'), :type => 'sbs')
71 assert_equal 1, diff.size
72 diff = diff.first
73 assert_equal 32, diff.size
74
75 assert_equal [51, -1], diff[0].offsets
76 assert_equal 'Lorem ipsum dolor sit amet, consectetur adipiscing <span>elit</span>', diff[0].html_line_left
77 assert_equal 'Lorem ipsum dolor sit amet, consectetur adipiscing <span>xx</span>', diff[0].html_line_right
78
79 assert_nil diff[1].offsets
80 assert_equal 'Praesent et sagittis dui. Vivamus ac diam diam', diff[1].html_line_left
81 assert_equal 'Praesent et sagittis dui. Vivamus ac diam diam', diff[1].html_line_right
82
83 assert_equal [0, -14], diff[2].offsets
84 assert_equal '<span>Ut sed</span> auctor justo', diff[2].html_line_left
85 assert_equal '<span>xxx</span> auctor justo', diff[2].html_line_right
86
87 assert_equal [13, -19], diff[4].offsets
88 assert_equal [24, -8], diff[6].offsets
89 assert_equal [37, -1], diff[8].offsets
90 assert_equal [0, -38], diff[10].offsets
91
92 end
93
37 94 def test_line_starting_with_dashes
38 95 diff = Redmine::UnifiedDiff.new(<<-DIFF
39 96 --- old.txt Wed Nov 11 14:24:58 2009
General Comments 0
You need to be logged in to leave comments. Login now