From 40e174d5364910750413d94b5417e57d108190ef Mon Sep 17 00:00:00 2001 From: Ragnar Ouchterlony Date: Sun, 13 Sep 2009 19:36:35 +0200 Subject: First version of side-by-side diff. This constitutes the first prototype of a side-by-side diff. It is not possible to switch between unidiff and side-by-side diff at all at this stage. Signed-off-by: Ragnar Ouchterlony Signed-off-by: Lars Hjemli --- ui-ssdiff.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 ui-ssdiff.c (limited to 'ui-ssdiff.c') diff --git a/ui-ssdiff.c b/ui-ssdiff.c new file mode 100644 index 0000000..3591ab4 --- /dev/null +++ b/ui-ssdiff.c @@ -0,0 +1,264 @@ +#include "cgit.h" +#include "html.h" +#include "ui-shared.h" + +extern int use_ssdiff; + +static int current_old_line, current_new_line; + +struct deferred_lines { + int line_no; + char *line; + struct deferred_lines *next; +}; + +static struct deferred_lines *deferred_old, *deferred_old_last; +static struct deferred_lines *deferred_new, *deferred_new_last; + +static int line_from_hunk(char *line, char type) +{ + char *buf1, *buf2; + int len; + + buf1 = strchr(line, type); + if (buf1 == NULL) + return 0; + buf1 += 1; + buf2 = strchr(buf1, ','); + if (buf2 == NULL) + return 0; + len = buf2 - buf1; + buf2 = xmalloc(len + 1); + strncpy(buf2, buf1, len); + buf2[len] = '\0'; + int res = atoi(buf2); + free(buf2); + return res; +} + +static char *replace_tabs(char *line) +{ + char *prev_buf = line; + char *cur_buf; + int linelen = strlen(line); + int n_tabs = 0; + int i; + char *result; + char *spaces = " "; + + if (linelen == 0) { + result = xmalloc(1); + result[0] = '\0'; + return result; + } + + for (i = 0; i < linelen; i++) + if (line[i] == '\t') + n_tabs += 1; + result = xmalloc(linelen + n_tabs * 8 + 1); + result[0] = '\0'; + + while (1) { + cur_buf = strchr(prev_buf, '\t'); + if (!cur_buf) { + strcat(result, prev_buf); + break; + } else { + strcat(result, " "); + strncat(result, spaces, 8 - (strlen(result) % 8)); + strncat(result, prev_buf, cur_buf - prev_buf); + } + prev_buf = cur_buf + 1; + } + return result; +} + +static void deferred_old_add(char *line, int line_no) +{ + struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); + item->line = xstrdup(line); + item->line_no = line_no; + item->next = NULL; + if (deferred_old) { + deferred_old_last->next = item; + deferred_old_last = item; + } else { + deferred_old = deferred_old_last = item; + } +} + +static void deferred_new_add(char *line, int line_no) +{ + struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); + item->line = xstrdup(line); + item->line_no = line_no; + item->next = NULL; + if (deferred_new) { + deferred_new_last->next = item; + deferred_new_last = item; + } else { + deferred_new = deferred_new_last = item; + } +} + +static void print_ssdiff_line(char *class, int old_line_no, char *old_line, + int new_line_no, char *new_line) +{ + html(""); + if (old_line_no > 0) + htmlf("%d ", class, + old_line_no, class); + else + htmlf(" ", class, class); + + if (old_line) { + old_line = replace_tabs(old_line + 1); + html_txt(old_line); + free(old_line); + } + + html(" "); + + if (new_line_no > 0) + htmlf(" %d ", class, + new_line_no, class); + else + htmlf(" ", class, class); + + if (new_line) { + new_line = replace_tabs(new_line + 1); + html_txt(new_line); + free(new_line); + } + + html(""); +} + +static void print_deferred_old_lines() +{ + struct deferred_lines *iter_old, *tmp; + + iter_old = deferred_old; + while (iter_old) { + print_ssdiff_line("del", iter_old->line_no, + iter_old->line, -1, NULL); + tmp = iter_old->next; + free(iter_old); + iter_old = tmp; + } +} + +static void print_deferred_new_lines() +{ + struct deferred_lines *iter_new, *tmp; + + iter_new = deferred_new; + while (iter_new) { + print_ssdiff_line("add", -1, NULL, iter_new->line_no, + iter_new->line); + tmp = iter_new->next; + free(iter_new); + iter_new = tmp; + } +} + +static void print_deferred_changed_lines() +{ + struct deferred_lines *iter_old, *iter_new, *tmp; + + iter_old = deferred_old; + iter_new = deferred_new; + while (iter_old || iter_new) { + if (iter_old && iter_new) + print_ssdiff_line("changed", iter_old->line_no, + iter_old->line, + iter_new->line_no, iter_new->line); + else if (iter_old) + print_ssdiff_line("changed", iter_old->line_no, + iter_old->line, -1, NULL); + else if (iter_new) + print_ssdiff_line("changed", -1, NULL, + iter_new->line_no, iter_new->line); + + if (iter_old) { + tmp = iter_old->next; + free(iter_old); + iter_old = tmp; + } + + if (iter_new) { + tmp = iter_new->next; + free(iter_new); + iter_new = tmp; + } + } +} + +void cgit_ssdiff_print_deferred_lines() +{ + if (!deferred_old && !deferred_new) + return; + + if (deferred_old && !deferred_new) + print_deferred_old_lines(); + else if (!deferred_old && deferred_new) + print_deferred_new_lines(); + else + print_deferred_changed_lines(); + + deferred_old = deferred_old_last = NULL; + deferred_new = deferred_new_last = NULL; +} + +/* + * print a single line returned from xdiff + */ +void cgit_ssdiff_line_cb(char *line, int len) +{ + char c = line[len - 1]; + + line[len - 1] = '\0'; + + if (line[0] == '@') { + current_old_line = line_from_hunk(line, '-'); + current_new_line = line_from_hunk(line, '+'); + } + + if (line[0] == ' ') { + if (deferred_old || deferred_new) + cgit_ssdiff_print_deferred_lines(); + print_ssdiff_line("ctx", current_old_line, line, + current_new_line, line); + current_old_line += 1; + current_new_line += 1; + } else if (line[0] == '+') { + deferred_new_add(line, current_new_line); + current_new_line += 1; + } else if (line[0] == '-') { + deferred_old_add(line, current_old_line); + current_old_line += 1; + } else if (line[0] == '@') { + html(""); + html_txt(line); + html(""); + } else { + html(""); + html_txt(line); + html(""); + } + line[len - 1] = c; +} + +void cgit_ssdiff_header() +{ + current_old_line = 0; + current_new_line = 0; + html(""); +} + +void cgit_ssdiff_footer() +{ + if (deferred_old || deferred_new) + cgit_ssdiff_print_deferred_lines(); + html("
"); +} -- cgit v1.2.3-70-g09d2 From 207cc34711039329b41345f716bf421a88a6fd0a Mon Sep 17 00:00:00 2001 From: Ragnar Ouchterlony Date: Tue, 15 Sep 2009 19:44:37 +0200 Subject: Polishing of how the side-by-side diff looks. Aligned all different files, so that all side-by-side tables look the same. Also made sure that the tables take up the whole browser width. Also various changes to the css to make things easier on the eye. Signed-off-by: Ragnar Ouchterlony Signed-off-by: Lars Hjemli --- cgit.css | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------ ui-diff.c | 27 ++++++++++++++----------- ui-ssdiff.c | 30 +++++++++++++++++----------- ui-ssdiff.h | 3 ++- 4 files changed, 96 insertions(+), 30 deletions(-) (limited to 'ui-ssdiff.c') diff --git a/cgit.css b/cgit.css index bf58b8a..3f37165 100644 --- a/cgit.css +++ b/cgit.css @@ -602,37 +602,91 @@ table.hgraph div.bar { height: 1em; } +table.ssdiff { + width: 100%; +} + +table.ssdiff td { + font-size: 75%; + font-family: monospace; + white-space: pre; + padding: 1px 4px 1px 4px; + border-left: solid 1px #aaa; + border-right: solid 1px #aaa; +} + table.ssdiff td.add { color: black; - background: #afa; + background: #cfc; + min-width: 50%; } table.ssdiff td.add_dark { color: black; - background: #9c9; + background: #aca; + min-width: 50%; } table.ssdiff td.del { color: black; - background: #faa; + background: #fcc; + min-width: 50%; } table.ssdiff td.del_dark { color: black; - background: #c99; + background: #caa; + min-width: 50%; } table.ssdiff td.changed { color: black; - background: #ffa; + background: #ffc; + min-width: 50%; } table.ssdiff td.changed_dark { color: black; - background: #cc9; + background: #cca; + min-width: 50%; +} + +table.ssdiff td.lineno { + color: black; + background: #eee; + text-align: right; + width: 3em; + min-width: 3em; } table.ssdiff td.hunk { color: #black; background: #ccf; + border-top: solid 1px #aaa; + border-bottom: solid 1px #aaa; } + +table.ssdiff td.head { + border-top: solid 1px #aaa; + border-bottom: solid 1px #aaa; +} + +table.ssdiff td.head div.head { + font-weight: bold; + color: black; +} + +table.ssdiff td.foot { + border-top: solid 1px #aaa; + border-left: none; + border-right: none; + border-bottom: none; +} + +table.ssdiff td.space { + border: none; +} + +table.ssdiff td.space div { + min-height: 3em; +} \ No newline at end of file diff --git a/ui-diff.c b/ui-diff.c index 42e81ac..b21c2c1 100644 --- a/ui-diff.c +++ b/ui-diff.c @@ -246,8 +246,6 @@ static void header(unsigned char *sha1, char *path1, int mode1, html_txt(path2); } html(""); - if (use_ssdiff) - cgit_ssdiff_header(); } static void print_ssdiff_link() @@ -270,24 +268,26 @@ static void filepair_cb(struct diff_filepair *pair) int binary = 0; linediff_fn print_line_fn = print_line; - header(pair->one->sha1, pair->one->path, pair->one->mode, - pair->two->sha1, pair->two->path, pair->two->mode); if (use_ssdiff) { - cgit_ssdiff_header(); + cgit_ssdiff_header_begin(); print_line_fn = cgit_ssdiff_line_cb; } + header(pair->one->sha1, pair->one->path, pair->one->mode, + pair->two->sha1, pair->two->path, pair->two->mode); + if (use_ssdiff) + cgit_ssdiff_header_end(); if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { if (S_ISGITLINK(pair->one->mode)) - print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); + print_line_fn(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); if (S_ISGITLINK(pair->two->mode)) - print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); + print_line_fn(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); return; } if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, &new_size, &binary, print_line_fn)) cgit_print_error("Error running diff"); if (binary) - html("Binary files differ"); + print_line_fn(" Binary files differ", 20); if (use_ssdiff) cgit_ssdiff_footer(); } @@ -334,9 +334,14 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefi print_ssdiff_link(); cgit_print_diffstat(old_rev_sha1, new_rev_sha1); - html(""); - html(""); if (old_line_no > 0) - htmlf(""); + html(""); if (new_line_no > 0) - htmlf(""); } diff --git a/ui-ssdiff.h b/ui-ssdiff.h index a0b1efe..64b4b12 100644 --- a/ui-ssdiff.h +++ b/ui-ssdiff.h @@ -5,7 +5,8 @@ extern void cgit_ssdiff_print_deferred_lines(); extern void cgit_ssdiff_line_cb(char *line, int len); -extern void cgit_ssdiff_header(); +extern void cgit_ssdiff_header_begin(); +extern void cgit_ssdiff_header_end(); extern void cgit_ssdiff_footer(); -- cgit v1.2.3-70-g09d2 From 4a198e4b8ee62a9a8b5156a46bfce46dc7223fe9 Mon Sep 17 00:00:00 2001 From: Ragnar Ouchterlony Date: Wed, 16 Sep 2009 18:56:26 +0200 Subject: Fixed side-by-side diff bugs related to binary diff and more. The fixed bugs: * "Binary files differ" did not show up either in unidiff or side-by-side-diff. * Subproject diffs did not work for side-by-side diffs. * The ssdiff link on diff pages did not conserve the path. Signed-off-by: Ragnar Ouchterlony Signed-off-by: Lars Hjemli --- ui-diff.c | 14 ++++++++++---- ui-ssdiff.c | 8 ++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) (limited to 'ui-ssdiff.c') diff --git a/ui-diff.c b/ui-diff.c index b21c2c1..a92a768 100644 --- a/ui-diff.c +++ b/ui-diff.c @@ -253,11 +253,11 @@ static void print_ssdiff_link() if (!strcmp(ctx.qry.page, "diff")) { if (use_ssdiff) cgit_diff_link("Unidiff", NULL, NULL, ctx.qry.head, - ctx.qry.sha1, ctx.qry.sha2, NULL, 1); + ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1); else cgit_diff_link("Side-by-side diff", NULL, NULL, ctx.qry.head, ctx.qry.sha1, - ctx.qry.sha2, NULL, 1); + ctx.qry.sha2, ctx.qry.path, 1); } } @@ -281,13 +281,19 @@ static void filepair_cb(struct diff_filepair *pair) print_line_fn(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); if (S_ISGITLINK(pair->two->mode)) print_line_fn(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); + if (use_ssdiff) + cgit_ssdiff_footer(); return; } if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, &new_size, &binary, print_line_fn)) cgit_print_error("Error running diff"); - if (binary) - print_line_fn(" Binary files differ", 20); + if (binary) { + if (use_ssdiff) + html(""); + else + html("Binary files differ"); + } if (use_ssdiff) cgit_ssdiff_footer(); } diff --git a/ui-ssdiff.c b/ui-ssdiff.c index 8215051..5673642 100644 --- a/ui-ssdiff.c +++ b/ui-ssdiff.c @@ -108,6 +108,8 @@ static void print_ssdiff_line(char *class, int old_line_no, char *old_line, if (old_line_no > 0) htmlf(""); html(""); if (old_line_no > 0) htmlf(""); - if (new_line_no > 0) htmlf(""); + if (lcs) + free(lcs); + if (new_line) + free(new_line); + if (old_line) + free(old_line); } static void print_deferred_old_lines() { struct deferred_lines *iter_old, *tmp; - iter_old = deferred_old; while (iter_old) { print_ssdiff_line("del", iter_old->line_no, - iter_old->line, -1, NULL); + iter_old->line, -1, NULL, 0); tmp = iter_old->next; free(iter_old); iter_old = tmp; @@ -155,11 +252,10 @@ static void print_deferred_old_lines() static void print_deferred_new_lines() { struct deferred_lines *iter_new, *tmp; - iter_new = deferred_new; while (iter_new) { - print_ssdiff_line("add", -1, NULL, iter_new->line_no, - iter_new->line); + print_ssdiff_line("add", -1, NULL, + iter_new->line_no, iter_new->line, 0); tmp = iter_new->next; free(iter_new); iter_new = tmp; @@ -169,6 +265,9 @@ static void print_deferred_new_lines() static void print_deferred_changed_lines() { struct deferred_lines *iter_old, *iter_new, *tmp; + int n_old_lines = calc_deferred_lines(deferred_old); + int n_new_lines = calc_deferred_lines(deferred_new); + int individual_chars = (n_old_lines == n_new_lines ? 1 : 0); iter_old = deferred_old; iter_new = deferred_new; @@ -176,14 +275,14 @@ static void print_deferred_changed_lines() if (iter_old && iter_new) print_ssdiff_line("changed", iter_old->line_no, iter_old->line, - iter_new->line_no, iter_new->line); + iter_new->line_no, iter_new->line, + individual_chars); else if (iter_old) print_ssdiff_line("changed", iter_old->line_no, - iter_old->line, -1, NULL); + iter_old->line, -1, NULL, 0); else if (iter_new) print_ssdiff_line("changed", -1, NULL, - iter_new->line_no, iter_new->line); - + iter_new->line_no, iter_new->line, 0); if (iter_old) { tmp = iter_old->next; free(iter_old); @@ -202,14 +301,12 @@ void cgit_ssdiff_print_deferred_lines() { if (!deferred_old && !deferred_new) return; - if (deferred_old && !deferred_new) print_deferred_old_lines(); else if (!deferred_old && deferred_new) print_deferred_new_lines(); else print_deferred_changed_lines(); - deferred_old = deferred_old_last = NULL; deferred_new = deferred_new_last = NULL; } @@ -220,9 +317,7 @@ void cgit_ssdiff_print_deferred_lines() void cgit_ssdiff_line_cb(char *line, int len) { char c = line[len - 1]; - line[len - 1] = '\0'; - if (line[0] == '@') { current_old_line = line_from_hunk(line, '-'); current_new_line = line_from_hunk(line, '+'); @@ -232,7 +327,7 @@ void cgit_ssdiff_line_cb(char *line, int len) if (deferred_old || deferred_new) cgit_ssdiff_print_deferred_lines(); print_ssdiff_line("ctx", current_old_line, line, - current_new_line, line); + current_new_line, line, 0); current_old_line += 1; current_new_line += 1; } else if (line[0] == '+') { -- cgit v1.2.3-70-g09d2
"); + if (use_ssdiff) { + html(""); + } else { + html("
"); + html(""); + if (!use_ssdiff) + html(""); html("
"); + } cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix); - html("
"); } diff --git a/ui-ssdiff.c b/ui-ssdiff.c index 3591ab4..8215051 100644 --- a/ui-ssdiff.c +++ b/ui-ssdiff.c @@ -40,9 +40,9 @@ static char *replace_tabs(char *line) { char *prev_buf = line; char *cur_buf; - int linelen = strlen(line); + int linelen = strlen(line); int n_tabs = 0; - int i; + int i; char *result; char *spaces = " "; @@ -52,10 +52,10 @@ static char *replace_tabs(char *line) return result; } - for (i = 0; i < linelen; i++) + for (i = 0; i < linelen; i++) if (line[i] == '\t') n_tabs += 1; - result = xmalloc(linelen + n_tabs * 8 + 1); + result = xmalloc(linelen + n_tabs * 8 + 1); result[0] = '\0'; while (1) { @@ -106,10 +106,10 @@ static void print_ssdiff_line(char *class, int old_line_no, char *old_line, { html("
%d ", class, + htmlf("%d", old_line_no, class); else - htmlf(" ", class, class); + htmlf("", class); if (old_line) { old_line = replace_tabs(old_line + 1); @@ -117,13 +117,13 @@ static void print_ssdiff_line(char *class, int old_line_no, char *old_line, free(old_line); } - html(" %d ", class, + htmlf("%d", new_line_no, class); else - htmlf(" ", class, class); + htmlf("", class); if (new_line) { new_line = replace_tabs(new_line + 1); @@ -249,16 +249,22 @@ void cgit_ssdiff_line_cb(char *line, int len) line[len - 1] = c; } -void cgit_ssdiff_header() +void cgit_ssdiff_header_begin() { current_old_line = 0; current_new_line = 0; - html(""); + html(""); + html(""); } void cgit_ssdiff_footer() { if (deferred_old || deferred_new) cgit_ssdiff_print_deferred_lines(); - html("
"); +} + +void cgit_ssdiff_header_end() +{ + html("
"); + html("
Binary files differ
%d", old_line_no, class); + else if (old_line) + htmlf("", class); else htmlf("", class); @@ -122,6 +124,8 @@ static void print_ssdiff_line(char *class, int old_line_no, char *old_line, if (new_line_no > 0) htmlf("%d", new_line_no, class); + else if (new_line) + htmlf("", class); else htmlf("", class); @@ -251,8 +255,8 @@ void cgit_ssdiff_line_cb(char *line, int len) void cgit_ssdiff_header_begin() { - current_old_line = 0; - current_new_line = 0; + current_old_line = -1; + current_new_line = -1; html("
"); } -- cgit v1.2.3-70-g09d2 From 735e15e38a484bf0daa98776fa7cde270a271cda Mon Sep 17 00:00:00 2001 From: Ragnar Ouchterlony Date: Sun, 25 Oct 2009 18:13:22 +0100 Subject: 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 --- cgit.css | 10 +++++ ui-ssdiff.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 130 insertions(+), 25 deletions(-) (limited to 'ui-ssdiff.c') diff --git a/cgit.css b/cgit.css index 3f37165..9e6d2a4 100644 --- a/cgit.css +++ b/cgit.css @@ -627,6 +627,11 @@ table.ssdiff td.add_dark { min-width: 50%; } +table.ssdiff span.add { + background: #cfc; + font-weight: bold; +} + table.ssdiff td.del { color: black; background: #fcc; @@ -639,6 +644,11 @@ table.ssdiff td.del_dark { min-width: 50%; } +table.ssdiff span.del { + background: #fcc; + font-weight: bold; +} + table.ssdiff td.changed { color: black; background: #ffc; diff --git a/ui-ssdiff.c b/ui-ssdiff.c index 5673642..408e620 100644 --- a/ui-ssdiff.c +++ b/ui-ssdiff.c @@ -15,6 +15,52 @@ struct deferred_lines { static struct deferred_lines *deferred_old, *deferred_old_last; static struct deferred_lines *deferred_new, *deferred_new_last; +static char *longest_common_subsequence(char *A, char *B) +{ + int i, j, ri; + int m = strlen(A); + int n = strlen(B); + int L[m + 1][n + 1]; + int tmp1, tmp2; + int lcs_length; + char *result; + + for (i = m; i >= 0; i--) { + for (j = n; j >= 0; j--) { + if (A[i] == '\0' || B[j] == '\0') { + L[i][j] = 0; + } else if (A[i] == B[j]) { + L[i][j] = 1 + L[i + 1][j + 1]; + } else { + tmp1 = L[i + 1][j]; + tmp2 = L[i][j + 1]; + L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2); + } + } + } + + lcs_length = L[0][0]; + result = xmalloc(lcs_length + 2); + memset(result, 0, sizeof(*result) * (lcs_length + 2)); + + ri = 0; + i = 0; + j = 0; + while (i < m && j < n) { + if (A[i] == B[j]) { + result[ri] = A[i]; + ri += 1; + i += 1; + j += 1; + } else if (L[i + 1][j] >= L[i][j + 1]) { + i += 1; + } else { + j += 1; + } + } + return result; +} + static int line_from_hunk(char *line, char type) { char *buf1, *buf2; @@ -73,6 +119,17 @@ static char *replace_tabs(char *line) return result; } +static int calc_deferred_lines(struct deferred_lines *start) +{ + struct deferred_lines *item = start; + int result = 0; + while (item) { + result += 1; + item = item->next; + } + return result; +} + static void deferred_old_add(char *line, int line_no) { struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); @@ -101,9 +158,45 @@ static void deferred_new_add(char *line, int line_no) } } -static void print_ssdiff_line(char *class, int old_line_no, char *old_line, - int new_line_no, char *new_line) +static void print_part_with_lcs(char *class, char *line, char *lcs) +{ + int line_len = strlen(line); + int i, j; + char c[2] = " "; + int same = 1; + + j = 0; + for (i = 0; i < line_len; i++) { + c[0] = line[i]; + if (same) { + if (line[i] == lcs[j]) + j += 1; + else { + same = 0; + htmlf("", class); + } + } else if (line[i] == lcs[j]) { + same = 1; + htmlf(""); + j += 1; + } + html_txt(c); + } +} + +static void print_ssdiff_line(char *class, + int old_line_no, + char *old_line, + int new_line_no, + char *new_line, int individual_chars) { + char *lcs = NULL; + if (old_line) + old_line = replace_tabs(old_line + 1); + if (new_line) + new_line = replace_tabs(new_line + 1); + if (individual_chars && old_line && new_line) + lcs = longest_common_subsequence(old_line, new_line); html("
%d", @@ -112,15 +205,14 @@ static void print_ssdiff_line(char *class, int old_line_no, char *old_line, htmlf("", class); else htmlf("", class); - if (old_line) { - old_line = replace_tabs(old_line + 1); - html_txt(old_line); - free(old_line); + if (lcs) + print_part_with_lcs("del", old_line, lcs); + else + html_txt(old_line); } html("%d", new_line_no, class); @@ -128,24 +220,29 @@ static void print_ssdiff_line(char *class, int old_line_no, char *old_line, htmlf("", class); else htmlf("", class); - if (new_line) { - new_line = replace_tabs(new_line + 1); - html_txt(new_line); - free(new_line); + if (lcs) + print_part_with_lcs("add", new_line, lcs); + else + html_txt(new_line); } html("