summaryrefslogtreecommitdiffabout
path: root/ui-ssdiff.c
Unidiff
Diffstat (limited to 'ui-ssdiff.c') (more/less context) (show whitespace changes)
-rw-r--r--ui-ssdiff.c32
1 files changed, 23 insertions, 9 deletions
diff --git a/ui-ssdiff.c b/ui-ssdiff.c
index 408e620..2481585 100644
--- a/ui-ssdiff.c
+++ b/ui-ssdiff.c
@@ -1,315 +1,329 @@
1#include "cgit.h" 1#include "cgit.h"
2#include "html.h" 2#include "html.h"
3#include "ui-shared.h" 3#include "ui-shared.h"
4#include "ui-diff.h"
4 5
5extern int use_ssdiff; 6extern int use_ssdiff;
6 7
7static int current_old_line, current_new_line; 8static int current_old_line, current_new_line;
8 9
9struct deferred_lines { 10struct deferred_lines {
10 int line_no; 11 int line_no;
11 char *line; 12 char *line;
12 struct deferred_lines *next; 13 struct deferred_lines *next;
13}; 14};
14 15
15static struct deferred_lines *deferred_old, *deferred_old_last; 16static struct deferred_lines *deferred_old, *deferred_old_last;
16static struct deferred_lines *deferred_new, *deferred_new_last; 17static struct deferred_lines *deferred_new, *deferred_new_last;
17 18
18static char *longest_common_subsequence(char *A, char *B) 19static char *longest_common_subsequence(char *A, char *B)
19{ 20{
20 int i, j, ri; 21 int i, j, ri;
21 int m = strlen(A); 22 int m = strlen(A);
22 int n = strlen(B); 23 int n = strlen(B);
23 int L[m + 1][n + 1]; 24 int L[m + 1][n + 1];
24 int tmp1, tmp2; 25 int tmp1, tmp2;
25 int lcs_length; 26 int lcs_length;
26 char *result; 27 char *result;
27 28
28 for (i = m; i >= 0; i--) { 29 for (i = m; i >= 0; i--) {
29 for (j = n; j >= 0; j--) { 30 for (j = n; j >= 0; j--) {
30 if (A[i] == '\0' || B[j] == '\0') { 31 if (A[i] == '\0' || B[j] == '\0') {
31 L[i][j] = 0; 32 L[i][j] = 0;
32 } else if (A[i] == B[j]) { 33 } else if (A[i] == B[j]) {
33 L[i][j] = 1 + L[i + 1][j + 1]; 34 L[i][j] = 1 + L[i + 1][j + 1];
34 } else { 35 } else {
35 tmp1 = L[i + 1][j]; 36 tmp1 = L[i + 1][j];
36 tmp2 = L[i][j + 1]; 37 tmp2 = L[i][j + 1];
37 L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2); 38 L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2);
38 } 39 }
39 } 40 }
40 } 41 }
41 42
42 lcs_length = L[0][0]; 43 lcs_length = L[0][0];
43 result = xmalloc(lcs_length + 2); 44 result = xmalloc(lcs_length + 2);
44 memset(result, 0, sizeof(*result) * (lcs_length + 2)); 45 memset(result, 0, sizeof(*result) * (lcs_length + 2));
45 46
46 ri = 0; 47 ri = 0;
47 i = 0; 48 i = 0;
48 j = 0; 49 j = 0;
49 while (i < m && j < n) { 50 while (i < m && j < n) {
50 if (A[i] == B[j]) { 51 if (A[i] == B[j]) {
51 result[ri] = A[i]; 52 result[ri] = A[i];
52 ri += 1; 53 ri += 1;
53 i += 1; 54 i += 1;
54 j += 1; 55 j += 1;
55 } else if (L[i + 1][j] >= L[i][j + 1]) { 56 } else if (L[i + 1][j] >= L[i][j + 1]) {
56 i += 1; 57 i += 1;
57 } else { 58 } else {
58 j += 1; 59 j += 1;
59 } 60 }
60 } 61 }
61 return result; 62 return result;
62} 63}
63 64
64static int line_from_hunk(char *line, char type) 65static int line_from_hunk(char *line, char type)
65{ 66{
66 char *buf1, *buf2; 67 char *buf1, *buf2;
67 int len; 68 int len;
68 69
69 buf1 = strchr(line, type); 70 buf1 = strchr(line, type);
70 if (buf1 == NULL) 71 if (buf1 == NULL)
71 return 0; 72 return 0;
72 buf1 += 1; 73 buf1 += 1;
73 buf2 = strchr(buf1, ','); 74 buf2 = strchr(buf1, ',');
74 if (buf2 == NULL) 75 if (buf2 == NULL)
75 return 0; 76 return 0;
76 len = buf2 - buf1; 77 len = buf2 - buf1;
77 buf2 = xmalloc(len + 1); 78 buf2 = xmalloc(len + 1);
78 strncpy(buf2, buf1, len); 79 strncpy(buf2, buf1, len);
79 buf2[len] = '\0'; 80 buf2[len] = '\0';
80 int res = atoi(buf2); 81 int res = atoi(buf2);
81 free(buf2); 82 free(buf2);
82 return res; 83 return res;
83} 84}
84 85
85static char *replace_tabs(char *line) 86static char *replace_tabs(char *line)
86{ 87{
87 char *prev_buf = line; 88 char *prev_buf = line;
88 char *cur_buf; 89 char *cur_buf;
89 int linelen = strlen(line); 90 int linelen = strlen(line);
90 int n_tabs = 0; 91 int n_tabs = 0;
91 int i; 92 int i;
92 char *result; 93 char *result;
93 char *spaces = " "; 94 char *spaces = " ";
94 95
95 if (linelen == 0) { 96 if (linelen == 0) {
96 result = xmalloc(1); 97 result = xmalloc(1);
97 result[0] = '\0'; 98 result[0] = '\0';
98 return result; 99 return result;
99 } 100 }
100 101
101 for (i = 0; i < linelen; i++) 102 for (i = 0; i < linelen; i++)
102 if (line[i] == '\t') 103 if (line[i] == '\t')
103 n_tabs += 1; 104 n_tabs += 1;
104 result = xmalloc(linelen + n_tabs * 8 + 1); 105 result = xmalloc(linelen + n_tabs * 8 + 1);
105 result[0] = '\0'; 106 result[0] = '\0';
106 107
107 while (1) { 108 while (1) {
108 cur_buf = strchr(prev_buf, '\t'); 109 cur_buf = strchr(prev_buf, '\t');
109 if (!cur_buf) { 110 if (!cur_buf) {
110 strcat(result, prev_buf); 111 strcat(result, prev_buf);
111 break; 112 break;
112 } else { 113 } else {
113 strcat(result, " "); 114 strcat(result, " ");
114 strncat(result, spaces, 8 - (strlen(result) % 8)); 115 strncat(result, spaces, 8 - (strlen(result) % 8));
115 strncat(result, prev_buf, cur_buf - prev_buf); 116 strncat(result, prev_buf, cur_buf - prev_buf);
116 } 117 }
117 prev_buf = cur_buf + 1; 118 prev_buf = cur_buf + 1;
118 } 119 }
119 return result; 120 return result;
120} 121}
121 122
122static int calc_deferred_lines(struct deferred_lines *start) 123static int calc_deferred_lines(struct deferred_lines *start)
123{ 124{
124 struct deferred_lines *item = start; 125 struct deferred_lines *item = start;
125 int result = 0; 126 int result = 0;
126 while (item) { 127 while (item) {
127 result += 1; 128 result += 1;
128 item = item->next; 129 item = item->next;
129 } 130 }
130 return result; 131 return result;
131} 132}
132 133
133static void deferred_old_add(char *line, int line_no) 134static void deferred_old_add(char *line, int line_no)
134{ 135{
135 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); 136 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
136 item->line = xstrdup(line); 137 item->line = xstrdup(line);
137 item->line_no = line_no; 138 item->line_no = line_no;
138 item->next = NULL; 139 item->next = NULL;
139 if (deferred_old) { 140 if (deferred_old) {
140 deferred_old_last->next = item; 141 deferred_old_last->next = item;
141 deferred_old_last = item; 142 deferred_old_last = item;
142 } else { 143 } else {
143 deferred_old = deferred_old_last = item; 144 deferred_old = deferred_old_last = item;
144 } 145 }
145} 146}
146 147
147static void deferred_new_add(char *line, int line_no) 148static void deferred_new_add(char *line, int line_no)
148{ 149{
149 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); 150 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
150 item->line = xstrdup(line); 151 item->line = xstrdup(line);
151 item->line_no = line_no; 152 item->line_no = line_no;
152 item->next = NULL; 153 item->next = NULL;
153 if (deferred_new) { 154 if (deferred_new) {
154 deferred_new_last->next = item; 155 deferred_new_last->next = item;
155 deferred_new_last = item; 156 deferred_new_last = item;
156 } else { 157 } else {
157 deferred_new = deferred_new_last = item; 158 deferred_new = deferred_new_last = item;
158 } 159 }
159} 160}
160 161
161static void print_part_with_lcs(char *class, char *line, char *lcs) 162static void print_part_with_lcs(char *class, char *line, char *lcs)
162{ 163{
163 int line_len = strlen(line); 164 int line_len = strlen(line);
164 int i, j; 165 int i, j;
165 char c[2] = " "; 166 char c[2] = " ";
166 int same = 1; 167 int same = 1;
167 168
168 j = 0; 169 j = 0;
169 for (i = 0; i < line_len; i++) { 170 for (i = 0; i < line_len; i++) {
170 c[0] = line[i]; 171 c[0] = line[i];
171 if (same) { 172 if (same) {
172 if (line[i] == lcs[j]) 173 if (line[i] == lcs[j])
173 j += 1; 174 j += 1;
174 else { 175 else {
175 same = 0; 176 same = 0;
176 htmlf("<span class='%s'>", class); 177 htmlf("<span class='%s'>", class);
177 } 178 }
178 } else if (line[i] == lcs[j]) { 179 } else if (line[i] == lcs[j]) {
179 same = 1; 180 same = 1;
180 htmlf("</span>"); 181 htmlf("</span>");
181 j += 1; 182 j += 1;
182 } 183 }
183 html_txt(c); 184 html_txt(c);
184 } 185 }
185} 186}
186 187
187static void print_ssdiff_line(char *class, 188static void print_ssdiff_line(char *class,
188 int old_line_no, 189 int old_line_no,
189 char *old_line, 190 char *old_line,
190 int new_line_no, 191 int new_line_no,
191 char *new_line, int individual_chars) 192 char *new_line, int individual_chars)
192{ 193{
193 char *lcs = NULL; 194 char *lcs = NULL;
195
194 if (old_line) 196 if (old_line)
195 old_line = replace_tabs(old_line + 1); 197 old_line = replace_tabs(old_line + 1);
196 if (new_line) 198 if (new_line)
197 new_line = replace_tabs(new_line + 1); 199 new_line = replace_tabs(new_line + 1);
198 if (individual_chars && old_line && new_line) 200 if (individual_chars && old_line && new_line)
199 lcs = longest_common_subsequence(old_line, new_line); 201 lcs = longest_common_subsequence(old_line, new_line);
200 html("<tr>"); 202 html("<tr>\n");
201 if (old_line_no > 0) 203 if (old_line_no > 0) {
202 htmlf("<td class='lineno'>%d</td><td class='%s'>", 204 struct diff_filespec *old_file = cgit_get_current_old_file();
203 old_line_no, class); 205 char *lineno_str = fmt("n%d", old_line_no);
204 else if (old_line) 206 char *id_str = fmt("%s#%s", is_null_sha1(old_file->sha1)?"HEAD":sha1_to_hex(old_rev_sha1), lineno_str);
207 html("<td class='lineno'><a class='no' href='");
208 html(cgit_fileurl(ctx.repo->url, "tree", old_file->path, id_str));
209 htmlf("' id='%s' name='%s'>%s</a>", lineno_str, lineno_str, lineno_str + 1);
210 html("</td>");
211 htmlf("<td class='%s'>", class);
212 } else if (old_line)
205 htmlf("<td class='lineno'></td><td class='%s'>", class); 213 htmlf("<td class='lineno'></td><td class='%s'>", class);
206 else 214 else
207 htmlf("<td class='lineno'></td><td class='%s_dark'>", class); 215 htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
208 if (old_line) { 216 if (old_line) {
209 if (lcs) 217 if (lcs)
210 print_part_with_lcs("del", old_line, lcs); 218 print_part_with_lcs("del", old_line, lcs);
211 else 219 else
212 html_txt(old_line); 220 html_txt(old_line);
213 } 221 }
214 222
223 html("</td>\n");
224 if (new_line_no > 0) {
225 struct diff_filespec *new_file = cgit_get_current_new_file();
226 char *lineno_str = fmt("n%d", new_line_no);
227 char *id_str = fmt("%s#%s", is_null_sha1(new_file->sha1)?"HEAD":sha1_to_hex(new_rev_sha1), lineno_str);
228 html("<td class='lineno'><a class='no' href='");
229 html(cgit_fileurl(ctx.repo->url, "tree", new_file->path, id_str));
230 htmlf("' id='%s' name='%s'>%s</a>", lineno_str, lineno_str, lineno_str + 1);
215 html("</td>"); 231 html("</td>");
216 if (new_line_no > 0) 232 htmlf("<td class='%s'>", class);
217 htmlf("<td class='lineno'>%d</td><td class='%s'>", 233 } else if (new_line)
218 new_line_no, class);
219 else if (new_line)
220 htmlf("<td class='lineno'></td><td class='%s'>", class); 234 htmlf("<td class='lineno'></td><td class='%s'>", class);
221 else 235 else
222 htmlf("<td class='lineno'></td><td class='%s_dark'>", class); 236 htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
223 if (new_line) { 237 if (new_line) {
224 if (lcs) 238 if (lcs)
225 print_part_with_lcs("add", new_line, lcs); 239 print_part_with_lcs("add", new_line, lcs);
226 else 240 else
227 html_txt(new_line); 241 html_txt(new_line);
228 } 242 }
229 243
230 html("</td></tr>"); 244 html("</td></tr>");
231 if (lcs) 245 if (lcs)
232 free(lcs); 246 free(lcs);
233 if (new_line) 247 if (new_line)
234 free(new_line); 248 free(new_line);
235 if (old_line) 249 if (old_line)
236 free(old_line); 250 free(old_line);
237} 251}
238 252
239static void print_deferred_old_lines() 253static void print_deferred_old_lines()
240{ 254{
241 struct deferred_lines *iter_old, *tmp; 255 struct deferred_lines *iter_old, *tmp;
242 iter_old = deferred_old; 256 iter_old = deferred_old;
243 while (iter_old) { 257 while (iter_old) {
244 print_ssdiff_line("del", iter_old->line_no, 258 print_ssdiff_line("del", iter_old->line_no,
245 iter_old->line, -1, NULL, 0); 259 iter_old->line, -1, NULL, 0);
246 tmp = iter_old->next; 260 tmp = iter_old->next;
247 free(iter_old); 261 free(iter_old);
248 iter_old = tmp; 262 iter_old = tmp;
249 } 263 }
250} 264}
251 265
252static void print_deferred_new_lines() 266static void print_deferred_new_lines()
253{ 267{
254 struct deferred_lines *iter_new, *tmp; 268 struct deferred_lines *iter_new, *tmp;
255 iter_new = deferred_new; 269 iter_new = deferred_new;
256 while (iter_new) { 270 while (iter_new) {
257 print_ssdiff_line("add", -1, NULL, 271 print_ssdiff_line("add", -1, NULL,
258 iter_new->line_no, iter_new->line, 0); 272 iter_new->line_no, iter_new->line, 0);
259 tmp = iter_new->next; 273 tmp = iter_new->next;
260 free(iter_new); 274 free(iter_new);
261 iter_new = tmp; 275 iter_new = tmp;
262 } 276 }
263} 277}
264 278
265static void print_deferred_changed_lines() 279static void print_deferred_changed_lines()
266{ 280{
267 struct deferred_lines *iter_old, *iter_new, *tmp; 281 struct deferred_lines *iter_old, *iter_new, *tmp;
268 int n_old_lines = calc_deferred_lines(deferred_old); 282 int n_old_lines = calc_deferred_lines(deferred_old);
269 int n_new_lines = calc_deferred_lines(deferred_new); 283 int n_new_lines = calc_deferred_lines(deferred_new);
270 int individual_chars = (n_old_lines == n_new_lines ? 1 : 0); 284 int individual_chars = (n_old_lines == n_new_lines ? 1 : 0);
271 285
272 iter_old = deferred_old; 286 iter_old = deferred_old;
273 iter_new = deferred_new; 287 iter_new = deferred_new;
274 while (iter_old || iter_new) { 288 while (iter_old || iter_new) {
275 if (iter_old && iter_new) 289 if (iter_old && iter_new)
276 print_ssdiff_line("changed", iter_old->line_no, 290 print_ssdiff_line("changed", iter_old->line_no,
277 iter_old->line, 291 iter_old->line,
278 iter_new->line_no, iter_new->line, 292 iter_new->line_no, iter_new->line,
279 individual_chars); 293 individual_chars);
280 else if (iter_old) 294 else if (iter_old)
281 print_ssdiff_line("changed", iter_old->line_no, 295 print_ssdiff_line("changed", iter_old->line_no,
282 iter_old->line, -1, NULL, 0); 296 iter_old->line, -1, NULL, 0);
283 else if (iter_new) 297 else if (iter_new)
284 print_ssdiff_line("changed", -1, NULL, 298 print_ssdiff_line("changed", -1, NULL,
285 iter_new->line_no, iter_new->line, 0); 299 iter_new->line_no, iter_new->line, 0);
286 if (iter_old) { 300 if (iter_old) {
287 tmp = iter_old->next; 301 tmp = iter_old->next;
288 free(iter_old); 302 free(iter_old);
289 iter_old = tmp; 303 iter_old = tmp;
290 } 304 }
291 305
292 if (iter_new) { 306 if (iter_new) {
293 tmp = iter_new->next; 307 tmp = iter_new->next;
294 free(iter_new); 308 free(iter_new);
295 iter_new = tmp; 309 iter_new = tmp;
296 } 310 }
297 } 311 }
298} 312}
299 313
300void cgit_ssdiff_print_deferred_lines() 314void cgit_ssdiff_print_deferred_lines()
301{ 315{
302 if (!deferred_old && !deferred_new) 316 if (!deferred_old && !deferred_new)
303 return; 317 return;
304 if (deferred_old && !deferred_new) 318 if (deferred_old && !deferred_new)
305 print_deferred_old_lines(); 319 print_deferred_old_lines();
306 else if (!deferred_old && deferred_new) 320 else if (!deferred_old && deferred_new)
307 print_deferred_new_lines(); 321 print_deferred_new_lines();
308 else 322 else
309 print_deferred_changed_lines(); 323 print_deferred_changed_lines();
310 deferred_old = deferred_old_last = NULL; 324 deferred_old = deferred_old_last = NULL;
311 deferred_new = deferred_new_last = NULL; 325 deferred_new = deferred_new_last = NULL;
312} 326}
313 327
314/* 328/*
315 * print a single line returned from xdiff 329 * print a single line returned from xdiff