author | Ragnar Ouchterlony <ragnar@lysator.liu.se> | 2009-10-25 17:13:22 (UTC) |
---|---|---|
committer | Lars Hjemli <hjemli@gmail.com> | 2009-11-07 14:37:11 (UTC) |
commit | 735e15e38a484bf0daa98776fa7cde270a271cda (patch) (unidiff) | |
tree | 588545cead471e8997adc1ae5abca164a4873481 | |
parent | 4a198e4b8ee62a9a8b5156a46bfce46dc7223fe9 (diff) | |
download | cgit-735e15e38a484bf0daa98776fa7cde270a271cda.zip cgit-735e15e38a484bf0daa98776fa7cde270a271cda.tar.gz cgit-735e15e38a484bf0daa98776fa7cde270a271cda.tar.bz2 |
In side-by-side diff, add support for marking individual characters.
Refuses to do so if the left hand side of the diff has different amount of
differing lines to the right hand side to avoid confusion.
Note that I use the naive dynamic programming approach for calculating the
longest common subsequence. We could probably be more efficient by using a
better algorithm. The LCS calculating function is O(n*m) and uses up n*m
amount of memory too (so if we we compare two strings of length 100, I use
an array of 10000 for calculating the LCS). Might want to not calculate LCS
if the length of the line is too large.
Signed-off-by: Ragnar Ouchterlony <ragnar@lysator.liu.se>
-rw-r--r-- | cgit.css | 10 | ||||
-rw-r--r-- | ui-ssdiff.c | 145 |
2 files changed, 130 insertions, 25 deletions
@@ -566,127 +566,137 @@ table.vgraph { | |||
566 | 566 | ||
567 | table.vgraph th { | 567 | table.vgraph th { |
568 | background-color: #eee; | 568 | background-color: #eee; |
569 | font-weight: bold; | 569 | font-weight: bold; |
570 | border: solid 1px white; | 570 | border: solid 1px white; |
571 | padding: 1px 0.5em; | 571 | padding: 1px 0.5em; |
572 | } | 572 | } |
573 | 573 | ||
574 | table.vgraph td { | 574 | table.vgraph td { |
575 | vertical-align: bottom; | 575 | vertical-align: bottom; |
576 | padding: 0px 10px; | 576 | padding: 0px 10px; |
577 | } | 577 | } |
578 | 578 | ||
579 | table.vgraph div.bar { | 579 | table.vgraph div.bar { |
580 | background-color: #eee; | 580 | background-color: #eee; |
581 | } | 581 | } |
582 | 582 | ||
583 | table.hgraph { | 583 | table.hgraph { |
584 | border: solid 1px black; | 584 | border: solid 1px black; |
585 | width: 800px; | 585 | width: 800px; |
586 | } | 586 | } |
587 | 587 | ||
588 | table.hgraph th { | 588 | table.hgraph th { |
589 | background-color: #eee; | 589 | background-color: #eee; |
590 | font-weight: bold; | 590 | font-weight: bold; |
591 | border: solid 1px black; | 591 | border: solid 1px black; |
592 | padding: 1px 0.5em; | 592 | padding: 1px 0.5em; |
593 | } | 593 | } |
594 | 594 | ||
595 | table.hgraph td { | 595 | table.hgraph td { |
596 | vertical-align: center; | 596 | vertical-align: center; |
597 | padding: 2px 2px; | 597 | padding: 2px 2px; |
598 | } | 598 | } |
599 | 599 | ||
600 | table.hgraph div.bar { | 600 | table.hgraph div.bar { |
601 | background-color: #eee; | 601 | background-color: #eee; |
602 | height: 1em; | 602 | height: 1em; |
603 | } | 603 | } |
604 | 604 | ||
605 | table.ssdiff { | 605 | table.ssdiff { |
606 | width: 100%; | 606 | width: 100%; |
607 | } | 607 | } |
608 | 608 | ||
609 | table.ssdiff td { | 609 | table.ssdiff td { |
610 | font-size: 75%; | 610 | font-size: 75%; |
611 | font-family: monospace; | 611 | font-family: monospace; |
612 | white-space: pre; | 612 | white-space: pre; |
613 | padding: 1px 4px 1px 4px; | 613 | padding: 1px 4px 1px 4px; |
614 | border-left: solid 1px #aaa; | 614 | border-left: solid 1px #aaa; |
615 | border-right: solid 1px #aaa; | 615 | border-right: solid 1px #aaa; |
616 | } | 616 | } |
617 | 617 | ||
618 | table.ssdiff td.add { | 618 | table.ssdiff td.add { |
619 | color: black; | 619 | color: black; |
620 | background: #cfc; | 620 | background: #cfc; |
621 | min-width: 50%; | 621 | min-width: 50%; |
622 | } | 622 | } |
623 | 623 | ||
624 | table.ssdiff td.add_dark { | 624 | table.ssdiff td.add_dark { |
625 | color: black; | 625 | color: black; |
626 | background: #aca; | 626 | background: #aca; |
627 | min-width: 50%; | 627 | min-width: 50%; |
628 | } | 628 | } |
629 | 629 | ||
630 | table.ssdiff span.add { | ||
631 | background: #cfc; | ||
632 | font-weight: bold; | ||
633 | } | ||
634 | |||
630 | table.ssdiff td.del { | 635 | table.ssdiff td.del { |
631 | color: black; | 636 | color: black; |
632 | background: #fcc; | 637 | background: #fcc; |
633 | min-width: 50%; | 638 | min-width: 50%; |
634 | } | 639 | } |
635 | 640 | ||
636 | table.ssdiff td.del_dark { | 641 | table.ssdiff td.del_dark { |
637 | color: black; | 642 | color: black; |
638 | background: #caa; | 643 | background: #caa; |
639 | min-width: 50%; | 644 | min-width: 50%; |
640 | } | 645 | } |
641 | 646 | ||
647 | table.ssdiff span.del { | ||
648 | background: #fcc; | ||
649 | font-weight: bold; | ||
650 | } | ||
651 | |||
642 | table.ssdiff td.changed { | 652 | table.ssdiff td.changed { |
643 | color: black; | 653 | color: black; |
644 | background: #ffc; | 654 | background: #ffc; |
645 | min-width: 50%; | 655 | min-width: 50%; |
646 | } | 656 | } |
647 | 657 | ||
648 | table.ssdiff td.changed_dark { | 658 | table.ssdiff td.changed_dark { |
649 | color: black; | 659 | color: black; |
650 | background: #cca; | 660 | background: #cca; |
651 | min-width: 50%; | 661 | min-width: 50%; |
652 | } | 662 | } |
653 | 663 | ||
654 | table.ssdiff td.lineno { | 664 | table.ssdiff td.lineno { |
655 | color: black; | 665 | color: black; |
656 | background: #eee; | 666 | background: #eee; |
657 | text-align: right; | 667 | text-align: right; |
658 | width: 3em; | 668 | width: 3em; |
659 | min-width: 3em; | 669 | min-width: 3em; |
660 | } | 670 | } |
661 | 671 | ||
662 | table.ssdiff td.hunk { | 672 | table.ssdiff td.hunk { |
663 | color: #black; | 673 | color: #black; |
664 | background: #ccf; | 674 | background: #ccf; |
665 | border-top: solid 1px #aaa; | 675 | border-top: solid 1px #aaa; |
666 | border-bottom: solid 1px #aaa; | 676 | border-bottom: solid 1px #aaa; |
667 | } | 677 | } |
668 | 678 | ||
669 | table.ssdiff td.head { | 679 | table.ssdiff td.head { |
670 | border-top: solid 1px #aaa; | 680 | border-top: solid 1px #aaa; |
671 | border-bottom: solid 1px #aaa; | 681 | border-bottom: solid 1px #aaa; |
672 | } | 682 | } |
673 | 683 | ||
674 | table.ssdiff td.head div.head { | 684 | table.ssdiff td.head div.head { |
675 | font-weight: bold; | 685 | font-weight: bold; |
676 | color: black; | 686 | color: black; |
677 | } | 687 | } |
678 | 688 | ||
679 | table.ssdiff td.foot { | 689 | table.ssdiff td.foot { |
680 | border-top: solid 1px #aaa; | 690 | border-top: solid 1px #aaa; |
681 | border-left: none; | 691 | border-left: none; |
682 | border-right: none; | 692 | border-right: none; |
683 | border-bottom: none; | 693 | border-bottom: none; |
684 | } | 694 | } |
685 | 695 | ||
686 | table.ssdiff td.space { | 696 | table.ssdiff td.space { |
687 | border: none; | 697 | border: none; |
688 | } | 698 | } |
689 | 699 | ||
690 | table.ssdiff td.space div { | 700 | table.ssdiff td.space div { |
691 | min-height: 3em; | 701 | min-height: 3em; |
692 | } \ No newline at end of file | 702 | } \ No newline at end of file |
diff --git a/ui-ssdiff.c b/ui-ssdiff.c index 5673642..408e620 100644 --- a/ui-ssdiff.c +++ b/ui-ssdiff.c | |||
@@ -1,274 +1,369 @@ | |||
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 | 4 | ||
5 | extern int use_ssdiff; | 5 | extern int use_ssdiff; |
6 | 6 | ||
7 | static int current_old_line, current_new_line; | 7 | static int current_old_line, current_new_line; |
8 | 8 | ||
9 | struct deferred_lines { | 9 | struct deferred_lines { |
10 | int line_no; | 10 | int line_no; |
11 | char *line; | 11 | char *line; |
12 | struct deferred_lines *next; | 12 | struct deferred_lines *next; |
13 | }; | 13 | }; |
14 | 14 | ||
15 | static struct deferred_lines *deferred_old, *deferred_old_last; | 15 | static struct deferred_lines *deferred_old, *deferred_old_last; |
16 | static struct deferred_lines *deferred_new, *deferred_new_last; | 16 | static struct deferred_lines *deferred_new, *deferred_new_last; |
17 | 17 | ||
18 | static char *longest_common_subsequence(char *A, char *B) | ||
19 | { | ||
20 | int i, j, ri; | ||
21 | int m = strlen(A); | ||
22 | int n = strlen(B); | ||
23 | int L[m + 1][n + 1]; | ||
24 | int tmp1, tmp2; | ||
25 | int lcs_length; | ||
26 | char *result; | ||
27 | |||
28 | for (i = m; i >= 0; i--) { | ||
29 | for (j = n; j >= 0; j--) { | ||
30 | if (A[i] == '\0' || B[j] == '\0') { | ||
31 | L[i][j] = 0; | ||
32 | } else if (A[i] == B[j]) { | ||
33 | L[i][j] = 1 + L[i + 1][j + 1]; | ||
34 | } else { | ||
35 | tmp1 = L[i + 1][j]; | ||
36 | tmp2 = L[i][j + 1]; | ||
37 | L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2); | ||
38 | } | ||
39 | } | ||
40 | } | ||
41 | |||
42 | lcs_length = L[0][0]; | ||
43 | result = xmalloc(lcs_length + 2); | ||
44 | memset(result, 0, sizeof(*result) * (lcs_length + 2)); | ||
45 | |||
46 | ri = 0; | ||
47 | i = 0; | ||
48 | j = 0; | ||
49 | while (i < m && j < n) { | ||
50 | if (A[i] == B[j]) { | ||
51 | result[ri] = A[i]; | ||
52 | ri += 1; | ||
53 | i += 1; | ||
54 | j += 1; | ||
55 | } else if (L[i + 1][j] >= L[i][j + 1]) { | ||
56 | i += 1; | ||
57 | } else { | ||
58 | j += 1; | ||
59 | } | ||
60 | } | ||
61 | return result; | ||
62 | } | ||
63 | |||
18 | static int line_from_hunk(char *line, char type) | 64 | static int line_from_hunk(char *line, char type) |
19 | { | 65 | { |
20 | char *buf1, *buf2; | 66 | char *buf1, *buf2; |
21 | int len; | 67 | int len; |
22 | 68 | ||
23 | buf1 = strchr(line, type); | 69 | buf1 = strchr(line, type); |
24 | if (buf1 == NULL) | 70 | if (buf1 == NULL) |
25 | return 0; | 71 | return 0; |
26 | buf1 += 1; | 72 | buf1 += 1; |
27 | buf2 = strchr(buf1, ','); | 73 | buf2 = strchr(buf1, ','); |
28 | if (buf2 == NULL) | 74 | if (buf2 == NULL) |
29 | return 0; | 75 | return 0; |
30 | len = buf2 - buf1; | 76 | len = buf2 - buf1; |
31 | buf2 = xmalloc(len + 1); | 77 | buf2 = xmalloc(len + 1); |
32 | strncpy(buf2, buf1, len); | 78 | strncpy(buf2, buf1, len); |
33 | buf2[len] = '\0'; | 79 | buf2[len] = '\0'; |
34 | int res = atoi(buf2); | 80 | int res = atoi(buf2); |
35 | free(buf2); | 81 | free(buf2); |
36 | return res; | 82 | return res; |
37 | } | 83 | } |
38 | 84 | ||
39 | static char *replace_tabs(char *line) | 85 | static char *replace_tabs(char *line) |
40 | { | 86 | { |
41 | char *prev_buf = line; | 87 | char *prev_buf = line; |
42 | char *cur_buf; | 88 | char *cur_buf; |
43 | int linelen = strlen(line); | 89 | int linelen = strlen(line); |
44 | int n_tabs = 0; | 90 | int n_tabs = 0; |
45 | int i; | 91 | int i; |
46 | char *result; | 92 | char *result; |
47 | char *spaces = " "; | 93 | char *spaces = " "; |
48 | 94 | ||
49 | if (linelen == 0) { | 95 | if (linelen == 0) { |
50 | result = xmalloc(1); | 96 | result = xmalloc(1); |
51 | result[0] = '\0'; | 97 | result[0] = '\0'; |
52 | return result; | 98 | return result; |
53 | } | 99 | } |
54 | 100 | ||
55 | for (i = 0; i < linelen; i++) | 101 | for (i = 0; i < linelen; i++) |
56 | if (line[i] == '\t') | 102 | if (line[i] == '\t') |
57 | n_tabs += 1; | 103 | n_tabs += 1; |
58 | result = xmalloc(linelen + n_tabs * 8 + 1); | 104 | result = xmalloc(linelen + n_tabs * 8 + 1); |
59 | result[0] = '\0'; | 105 | result[0] = '\0'; |
60 | 106 | ||
61 | while (1) { | 107 | while (1) { |
62 | cur_buf = strchr(prev_buf, '\t'); | 108 | cur_buf = strchr(prev_buf, '\t'); |
63 | if (!cur_buf) { | 109 | if (!cur_buf) { |
64 | strcat(result, prev_buf); | 110 | strcat(result, prev_buf); |
65 | break; | 111 | break; |
66 | } else { | 112 | } else { |
67 | strcat(result, " "); | 113 | strcat(result, " "); |
68 | strncat(result, spaces, 8 - (strlen(result) % 8)); | 114 | strncat(result, spaces, 8 - (strlen(result) % 8)); |
69 | strncat(result, prev_buf, cur_buf - prev_buf); | 115 | strncat(result, prev_buf, cur_buf - prev_buf); |
70 | } | 116 | } |
71 | prev_buf = cur_buf + 1; | 117 | prev_buf = cur_buf + 1; |
72 | } | 118 | } |
73 | return result; | 119 | return result; |
74 | } | 120 | } |
75 | 121 | ||
122 | static int calc_deferred_lines(struct deferred_lines *start) | ||
123 | { | ||
124 | struct deferred_lines *item = start; | ||
125 | int result = 0; | ||
126 | while (item) { | ||
127 | result += 1; | ||
128 | item = item->next; | ||
129 | } | ||
130 | return result; | ||
131 | } | ||
132 | |||
76 | static void deferred_old_add(char *line, int line_no) | 133 | static void deferred_old_add(char *line, int line_no) |
77 | { | 134 | { |
78 | struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); | 135 | struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); |
79 | item->line = xstrdup(line); | 136 | item->line = xstrdup(line); |
80 | item->line_no = line_no; | 137 | item->line_no = line_no; |
81 | item->next = NULL; | 138 | item->next = NULL; |
82 | if (deferred_old) { | 139 | if (deferred_old) { |
83 | deferred_old_last->next = item; | 140 | deferred_old_last->next = item; |
84 | deferred_old_last = item; | 141 | deferred_old_last = item; |
85 | } else { | 142 | } else { |
86 | deferred_old = deferred_old_last = item; | 143 | deferred_old = deferred_old_last = item; |
87 | } | 144 | } |
88 | } | 145 | } |
89 | 146 | ||
90 | static void deferred_new_add(char *line, int line_no) | 147 | static void deferred_new_add(char *line, int line_no) |
91 | { | 148 | { |
92 | struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); | 149 | struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); |
93 | item->line = xstrdup(line); | 150 | item->line = xstrdup(line); |
94 | item->line_no = line_no; | 151 | item->line_no = line_no; |
95 | item->next = NULL; | 152 | item->next = NULL; |
96 | if (deferred_new) { | 153 | if (deferred_new) { |
97 | deferred_new_last->next = item; | 154 | deferred_new_last->next = item; |
98 | deferred_new_last = item; | 155 | deferred_new_last = item; |
99 | } else { | 156 | } else { |
100 | deferred_new = deferred_new_last = item; | 157 | deferred_new = deferred_new_last = item; |
101 | } | 158 | } |
102 | } | 159 | } |
103 | 160 | ||
104 | static void print_ssdiff_line(char *class, int old_line_no, char *old_line, | 161 | static void print_part_with_lcs(char *class, char *line, char *lcs) |
105 | int new_line_no, char *new_line) | 162 | { |
163 | int line_len = strlen(line); | ||
164 | int i, j; | ||
165 | char c[2] = " "; | ||
166 | int same = 1; | ||
167 | |||
168 | j = 0; | ||
169 | for (i = 0; i < line_len; i++) { | ||
170 | c[0] = line[i]; | ||
171 | if (same) { | ||
172 | if (line[i] == lcs[j]) | ||
173 | j += 1; | ||
174 | else { | ||
175 | same = 0; | ||
176 | htmlf("<span class='%s'>", class); | ||
177 | } | ||
178 | } else if (line[i] == lcs[j]) { | ||
179 | same = 1; | ||
180 | htmlf("</span>"); | ||
181 | j += 1; | ||
182 | } | ||
183 | html_txt(c); | ||
184 | } | ||
185 | } | ||
186 | |||
187 | static void print_ssdiff_line(char *class, | ||
188 | int old_line_no, | ||
189 | char *old_line, | ||
190 | int new_line_no, | ||
191 | char *new_line, int individual_chars) | ||
106 | { | 192 | { |
193 | char *lcs = NULL; | ||
194 | if (old_line) | ||
195 | old_line = replace_tabs(old_line + 1); | ||
196 | if (new_line) | ||
197 | new_line = replace_tabs(new_line + 1); | ||
198 | if (individual_chars && old_line && new_line) | ||
199 | lcs = longest_common_subsequence(old_line, new_line); | ||
107 | html("<tr>"); | 200 | html("<tr>"); |
108 | if (old_line_no > 0) | 201 | if (old_line_no > 0) |
109 | htmlf("<td class='lineno'>%d</td><td class='%s'>", | 202 | htmlf("<td class='lineno'>%d</td><td class='%s'>", |
110 | old_line_no, class); | 203 | old_line_no, class); |
111 | else if (old_line) | 204 | else if (old_line) |
112 | htmlf("<td class='lineno'></td><td class='%s'>", class); | 205 | htmlf("<td class='lineno'></td><td class='%s'>", class); |
113 | else | 206 | else |
114 | htmlf("<td class='lineno'></td><td class='%s_dark'>", class); | 207 | htmlf("<td class='lineno'></td><td class='%s_dark'>", class); |
115 | |||
116 | if (old_line) { | 208 | if (old_line) { |
117 | old_line = replace_tabs(old_line + 1); | 209 | if (lcs) |
118 | html_txt(old_line); | 210 | print_part_with_lcs("del", old_line, lcs); |
119 | free(old_line); | 211 | else |
212 | html_txt(old_line); | ||
120 | } | 213 | } |
121 | 214 | ||
122 | html("</td>"); | 215 | html("</td>"); |
123 | |||
124 | if (new_line_no > 0) | 216 | if (new_line_no > 0) |
125 | htmlf("<td class='lineno'>%d</td><td class='%s'>", | 217 | htmlf("<td class='lineno'>%d</td><td class='%s'>", |
126 | new_line_no, class); | 218 | new_line_no, class); |
127 | else if (new_line) | 219 | else if (new_line) |
128 | htmlf("<td class='lineno'></td><td class='%s'>", class); | 220 | htmlf("<td class='lineno'></td><td class='%s'>", class); |
129 | else | 221 | else |
130 | htmlf("<td class='lineno'></td><td class='%s_dark'>", class); | 222 | htmlf("<td class='lineno'></td><td class='%s_dark'>", class); |
131 | |||
132 | if (new_line) { | 223 | if (new_line) { |
133 | new_line = replace_tabs(new_line + 1); | 224 | if (lcs) |
134 | html_txt(new_line); | 225 | print_part_with_lcs("add", new_line, lcs); |
135 | free(new_line); | 226 | else |
227 | html_txt(new_line); | ||
136 | } | 228 | } |
137 | 229 | ||
138 | html("</td></tr>"); | 230 | html("</td></tr>"); |
231 | if (lcs) | ||
232 | free(lcs); | ||
233 | if (new_line) | ||
234 | free(new_line); | ||
235 | if (old_line) | ||
236 | free(old_line); | ||
139 | } | 237 | } |
140 | 238 | ||
141 | static void print_deferred_old_lines() | 239 | static void print_deferred_old_lines() |
142 | { | 240 | { |
143 | struct deferred_lines *iter_old, *tmp; | 241 | struct deferred_lines *iter_old, *tmp; |
144 | |||
145 | iter_old = deferred_old; | 242 | iter_old = deferred_old; |
146 | while (iter_old) { | 243 | while (iter_old) { |
147 | print_ssdiff_line("del", iter_old->line_no, | 244 | print_ssdiff_line("del", iter_old->line_no, |
148 | iter_old->line, -1, NULL); | 245 | iter_old->line, -1, NULL, 0); |
149 | tmp = iter_old->next; | 246 | tmp = iter_old->next; |
150 | free(iter_old); | 247 | free(iter_old); |
151 | iter_old = tmp; | 248 | iter_old = tmp; |
152 | } | 249 | } |
153 | } | 250 | } |
154 | 251 | ||
155 | static void print_deferred_new_lines() | 252 | static void print_deferred_new_lines() |
156 | { | 253 | { |
157 | struct deferred_lines *iter_new, *tmp; | 254 | struct deferred_lines *iter_new, *tmp; |
158 | |||
159 | iter_new = deferred_new; | 255 | iter_new = deferred_new; |
160 | while (iter_new) { | 256 | while (iter_new) { |
161 | print_ssdiff_line("add", -1, NULL, iter_new->line_no, | 257 | print_ssdiff_line("add", -1, NULL, |
162 | iter_new->line); | 258 | iter_new->line_no, iter_new->line, 0); |
163 | tmp = iter_new->next; | 259 | tmp = iter_new->next; |
164 | free(iter_new); | 260 | free(iter_new); |
165 | iter_new = tmp; | 261 | iter_new = tmp; |
166 | } | 262 | } |
167 | } | 263 | } |
168 | 264 | ||
169 | static void print_deferred_changed_lines() | 265 | static void print_deferred_changed_lines() |
170 | { | 266 | { |
171 | struct deferred_lines *iter_old, *iter_new, *tmp; | 267 | struct deferred_lines *iter_old, *iter_new, *tmp; |
268 | int n_old_lines = calc_deferred_lines(deferred_old); | ||
269 | int n_new_lines = calc_deferred_lines(deferred_new); | ||
270 | int individual_chars = (n_old_lines == n_new_lines ? 1 : 0); | ||
172 | 271 | ||
173 | iter_old = deferred_old; | 272 | iter_old = deferred_old; |
174 | iter_new = deferred_new; | 273 | iter_new = deferred_new; |
175 | while (iter_old || iter_new) { | 274 | while (iter_old || iter_new) { |
176 | if (iter_old && iter_new) | 275 | if (iter_old && iter_new) |
177 | print_ssdiff_line("changed", iter_old->line_no, | 276 | print_ssdiff_line("changed", iter_old->line_no, |
178 | iter_old->line, | 277 | iter_old->line, |
179 | iter_new->line_no, iter_new->line); | 278 | iter_new->line_no, iter_new->line, |
279 | individual_chars); | ||
180 | else if (iter_old) | 280 | else if (iter_old) |
181 | print_ssdiff_line("changed", iter_old->line_no, | 281 | print_ssdiff_line("changed", iter_old->line_no, |
182 | iter_old->line, -1, NULL); | 282 | iter_old->line, -1, NULL, 0); |
183 | else if (iter_new) | 283 | else if (iter_new) |
184 | print_ssdiff_line("changed", -1, NULL, | 284 | print_ssdiff_line("changed", -1, NULL, |
185 | iter_new->line_no, iter_new->line); | 285 | iter_new->line_no, iter_new->line, 0); |
186 | |||
187 | if (iter_old) { | 286 | if (iter_old) { |
188 | tmp = iter_old->next; | 287 | tmp = iter_old->next; |
189 | free(iter_old); | 288 | free(iter_old); |
190 | iter_old = tmp; | 289 | iter_old = tmp; |
191 | } | 290 | } |
192 | 291 | ||
193 | if (iter_new) { | 292 | if (iter_new) { |
194 | tmp = iter_new->next; | 293 | tmp = iter_new->next; |
195 | free(iter_new); | 294 | free(iter_new); |
196 | iter_new = tmp; | 295 | iter_new = tmp; |
197 | } | 296 | } |
198 | } | 297 | } |
199 | } | 298 | } |
200 | 299 | ||
201 | void cgit_ssdiff_print_deferred_lines() | 300 | void cgit_ssdiff_print_deferred_lines() |
202 | { | 301 | { |
203 | if (!deferred_old && !deferred_new) | 302 | if (!deferred_old && !deferred_new) |
204 | return; | 303 | return; |
205 | |||
206 | if (deferred_old && !deferred_new) | 304 | if (deferred_old && !deferred_new) |
207 | print_deferred_old_lines(); | 305 | print_deferred_old_lines(); |
208 | else if (!deferred_old && deferred_new) | 306 | else if (!deferred_old && deferred_new) |
209 | print_deferred_new_lines(); | 307 | print_deferred_new_lines(); |
210 | else | 308 | else |
211 | print_deferred_changed_lines(); | 309 | print_deferred_changed_lines(); |
212 | |||
213 | deferred_old = deferred_old_last = NULL; | 310 | deferred_old = deferred_old_last = NULL; |
214 | deferred_new = deferred_new_last = NULL; | 311 | deferred_new = deferred_new_last = NULL; |
215 | } | 312 | } |
216 | 313 | ||
217 | /* | 314 | /* |
218 | * print a single line returned from xdiff | 315 | * print a single line returned from xdiff |
219 | */ | 316 | */ |
220 | void cgit_ssdiff_line_cb(char *line, int len) | 317 | void cgit_ssdiff_line_cb(char *line, int len) |
221 | { | 318 | { |
222 | char c = line[len - 1]; | 319 | char c = line[len - 1]; |
223 | |||
224 | line[len - 1] = '\0'; | 320 | line[len - 1] = '\0'; |
225 | |||
226 | if (line[0] == '@') { | 321 | if (line[0] == '@') { |
227 | current_old_line = line_from_hunk(line, '-'); | 322 | current_old_line = line_from_hunk(line, '-'); |
228 | current_new_line = line_from_hunk(line, '+'); | 323 | current_new_line = line_from_hunk(line, '+'); |
229 | } | 324 | } |
230 | 325 | ||
231 | if (line[0] == ' ') { | 326 | if (line[0] == ' ') { |
232 | if (deferred_old || deferred_new) | 327 | if (deferred_old || deferred_new) |
233 | cgit_ssdiff_print_deferred_lines(); | 328 | cgit_ssdiff_print_deferred_lines(); |
234 | print_ssdiff_line("ctx", current_old_line, line, | 329 | print_ssdiff_line("ctx", current_old_line, line, |
235 | current_new_line, line); | 330 | current_new_line, line, 0); |
236 | current_old_line += 1; | 331 | current_old_line += 1; |
237 | current_new_line += 1; | 332 | current_new_line += 1; |
238 | } else if (line[0] == '+') { | 333 | } else if (line[0] == '+') { |
239 | deferred_new_add(line, current_new_line); | 334 | deferred_new_add(line, current_new_line); |
240 | current_new_line += 1; | 335 | current_new_line += 1; |
241 | } else if (line[0] == '-') { | 336 | } else if (line[0] == '-') { |
242 | deferred_old_add(line, current_old_line); | 337 | deferred_old_add(line, current_old_line); |
243 | current_old_line += 1; | 338 | current_old_line += 1; |
244 | } else if (line[0] == '@') { | 339 | } else if (line[0] == '@') { |
245 | html("<tr><td colspan='4' class='hunk'>"); | 340 | html("<tr><td colspan='4' class='hunk'>"); |
246 | html_txt(line); | 341 | html_txt(line); |
247 | html("</td></tr>"); | 342 | html("</td></tr>"); |
248 | } else { | 343 | } else { |
249 | html("<tr><td colspan='4' class='ctx'>"); | 344 | html("<tr><td colspan='4' class='ctx'>"); |
250 | html_txt(line); | 345 | html_txt(line); |
251 | html("</td></tr>"); | 346 | html("</td></tr>"); |
252 | } | 347 | } |
253 | line[len - 1] = c; | 348 | line[len - 1] = c; |
254 | } | 349 | } |
255 | 350 | ||
256 | void cgit_ssdiff_header_begin() | 351 | void cgit_ssdiff_header_begin() |
257 | { | 352 | { |
258 | current_old_line = -1; | 353 | current_old_line = -1; |
259 | current_new_line = -1; | 354 | current_new_line = -1; |
260 | html("<tr><td class='space' colspan='4'><div></div></td></tr>"); | 355 | html("<tr><td class='space' colspan='4'><div></div></td></tr>"); |
261 | html("<tr><td class='head' colspan='4'>"); | 356 | html("<tr><td class='head' colspan='4'>"); |
262 | } | 357 | } |
263 | 358 | ||
264 | void cgit_ssdiff_header_end() | 359 | void cgit_ssdiff_header_end() |
265 | { | 360 | { |
266 | html("</td><tr>"); | 361 | html("</td><tr>"); |
267 | } | 362 | } |
268 | 363 | ||
269 | void cgit_ssdiff_footer() | 364 | void cgit_ssdiff_footer() |
270 | { | 365 | { |
271 | if (deferred_old || deferred_new) | 366 | if (deferred_old || deferred_new) |
272 | cgit_ssdiff_print_deferred_lines(); | 367 | cgit_ssdiff_print_deferred_lines(); |
273 | html("<tr><td class='foot' colspan='4'></td></tr>"); | 368 | html("<tr><td class='foot' colspan='4'></td></tr>"); |
274 | } | 369 | } |