author | Lars Hjemli <hjemli@gmail.com> | 2009-12-08 18:32:48 (UTC) |
---|---|---|
committer | Lars Hjemli <hjemli@gmail.com> | 2009-12-08 18:32:48 (UTC) |
commit | f4f6210b4f9c214dc247567bbd06b1135c9ecd06 (patch) (side-by-side diff) | |
tree | 432b3dc327e05220c6ac0aff9ca81cf38a47524c | |
parent | 57bb4fdb8419d7c250d4c6146910df3263943951 (diff) | |
parent | 735e15e38a484bf0daa98776fa7cde270a271cda (diff) | |
download | cgit-f4f6210b4f9c214dc247567bbd06b1135c9ecd06.zip cgit-f4f6210b4f9c214dc247567bbd06b1135c9ecd06.tar.gz cgit-f4f6210b4f9c214dc247567bbd06b1135c9ecd06.tar.bz2 |
Merge branch 'ro/ssdiff'
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | cgit.c | 5 | ||||
-rw-r--r-- | cgit.css | 99 | ||||
-rw-r--r-- | cgit.h | 2 | ||||
-rw-r--r-- | cgitrc.5.txt | 4 | ||||
-rw-r--r-- | ui-commit.c | 11 | ||||
-rw-r--r-- | ui-diff.c | 52 | ||||
-rw-r--r-- | ui-log.c | 4 | ||||
-rw-r--r-- | ui-refs.c | 2 | ||||
-rw-r--r-- | ui-shared.c | 34 | ||||
-rw-r--r-- | ui-shared.h | 5 | ||||
-rw-r--r-- | ui-ssdiff.c | 369 | ||||
-rw-r--r-- | ui-ssdiff.h | 13 |
13 files changed, 581 insertions, 20 deletions
@@ -92,8 +92,9 @@ OBJECTS += ui-plain.o OBJECTS += ui-refs.o OBJECTS += ui-repolist.o OBJECTS += ui-shared.o OBJECTS += ui-snapshot.o +OBJECTS += ui-ssdiff.o OBJECTS += ui-stats.o OBJECTS += ui-summary.o OBJECTS += ui-tag.o OBJECTS += ui-tree.o @@ -183,8 +183,10 @@ void config_cb(const char *name, const char *value) else if (!strcmp(name, "summary-branches")) ctx.cfg.summary_branches = atoi(value); else if (!strcmp(name, "summary-tags")) ctx.cfg.summary_tags = atoi(value); + else if (!strcmp(name, "side-by-side-diffs")) + ctx.cfg.ssdiff = atoi(value); else if (!strcmp(name, "agefile")) ctx.cfg.agefile = xstrdup(value); else if (!strcmp(name, "renamelimit")) ctx.cfg.renamelimit = atoi(value); @@ -241,8 +243,10 @@ static void querystring_cb(const char *name, const char *value) } else if (!strcmp(name, "showmsg")) { ctx.qry.showmsg = atoi(value); } else if (!strcmp(name, "period")) { ctx.qry.period = xstrdup(value); + } else if (!strcmp(name, "ss")) { + ctx.qry.ssdiff = atoi(value); } } char *xstrdupn(const char *str) @@ -283,8 +287,9 @@ static void prepare_context(struct cgit_context *ctx) ctx->cfg.section = ""; ctx->cfg.summary_branches = 10; ctx->cfg.summary_log = 10; ctx->cfg.summary_tags = 10; + ctx->cfg.ssdiff = 0; ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); ctx->env.https = xstrdupn(getenv("HTTPS")); ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); @@ -605,4 +605,103 @@ table.hgraph td { table.hgraph div.bar { background-color: #eee; 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: #cfc; + min-width: 50%; +} + +table.ssdiff td.add_dark { + color: black; + background: #aca; + min-width: 50%; +} + +table.ssdiff span.add { + background: #cfc; + font-weight: bold; +} + +table.ssdiff td.del { + color: black; + background: #fcc; + min-width: 50%; +} + +table.ssdiff td.del_dark { + color: black; + background: #caa; + min-width: 50%; +} + +table.ssdiff span.del { + background: #fcc; + font-weight: bold; +} + +table.ssdiff td.changed { + color: black; + background: #ffc; + min-width: 50%; +} + +table.ssdiff td.changed_dark { + color: black; + 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 @@ -142,8 +142,9 @@ struct cgit_query { int ofs; int nohead; char *sort; int showmsg; + int ssdiff; }; struct cgit_config { char *agefile; @@ -194,8 +195,9 @@ struct cgit_config { int snapshots; int summary_branches; int summary_log; int summary_tags; + int ssdiff; struct string_list mimetypes; struct cgit_filter *about_filter; struct cgit_filter *commit_filter; struct cgit_filter *source_filter; diff --git a/cgitrc.5.txt b/cgitrc.5.txt index e69140b..70e4c78 100644 --- a/cgitrc.5.txt +++ b/cgitrc.5.txt @@ -244,8 +244,12 @@ section:: The name of the current repository section - all repositories defined after this option will inherit the current section name. Default value: none. +side-by-side-diffs:: + If set to "1" shows side-by-side diffs instead of unidiffs per + default. Default value: "0". + snapshots:: Text which specifies the default set of snapshot formats generated by cgit. The value is a space-separated list of zero or more of the values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. diff --git a/ui-commit.c b/ui-commit.c index f5b0ae5..b5e3c01 100644 --- a/ui-commit.c +++ b/ui-commit.c @@ -57,11 +57,16 @@ void cgit_print_commit(char *hex) cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time); html("</td></tr>\n"); html("<tr><th>commit</th><td colspan='2' class='sha1'>"); tmp = sha1_to_hex(commit->object.sha1); - cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp); + cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp, 0); html(" ("); cgit_patch_link("patch", NULL, NULL, NULL, tmp); + html(") ("); + if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff)) + cgit_commit_link("unidiff", NULL, NULL, ctx.qry.head, tmp, 1); + else + cgit_commit_link("side-by-side diff", NULL, NULL, ctx.qry.head, tmp, 1); html(")</td></tr>\n"); html("<tr><th>tree</th><td colspan='2' class='sha1'>"); tmp = xstrdup(hex); cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL, @@ -77,12 +82,12 @@ void cgit_print_commit(char *hex) } html("<tr><th>parent</th>" "<td colspan='2' class='sha1'>"); cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL, - ctx.qry.head, sha1_to_hex(p->item->object.sha1)); + ctx.qry.head, sha1_to_hex(p->item->object.sha1), 0); html(" ("); cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex, - sha1_to_hex(p->item->object.sha1), NULL); + sha1_to_hex(p->item->object.sha1), NULL, 0); html(")</td></tr>"); parents++; } if (ctx.repo->snapshots) { @@ -8,8 +8,9 @@ #include "cgit.h" #include "html.h" #include "ui-shared.h" +#include "ui-ssdiff.h" unsigned char old_rev_sha1[20]; unsigned char new_rev_sha1[20]; @@ -31,8 +32,9 @@ static struct fileinfo { unsigned long new_size; int binary:1; } *items; +static int use_ssdiff = 0; static void print_fileinfo(struct fileinfo *info) { char *class; @@ -82,9 +84,9 @@ static void print_fileinfo(struct fileinfo *info) html("]</span>"); } htmlf("</td><td class='%s'>", class); cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, ctx.qry.sha1, - ctx.qry.sha2, info->new_path); + ctx.qry.sha2, info->new_path, 0); if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) htmlf(" (%s from %s)", info->status == DIFF_STATUS_COPIED ? "copied" : "renamed", info->old_path); @@ -157,9 +159,9 @@ void cgit_print_diffstat(const unsigned char *old_sha1, int i; html("<div class='diffstat-header'>"); cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.sha1, - ctx.qry.sha2, NULL); + ctx.qry.sha2, NULL, 0); html("</div>"); html("<table summary='diffstat' class='diffstat'>"); max_changes = 0; cgit_diff_tree(old_sha1, new_sha1, inspect_filepair, NULL); @@ -245,29 +247,57 @@ static void header(unsigned char *sha1, char *path1, int mode1, } html("</div>"); } +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, ctx.qry.path, 1); + else + cgit_diff_link("Side-by-side diff", NULL, NULL, + ctx.qry.head, ctx.qry.sha1, + ctx.qry.sha2, ctx.qry.path, 1); + } +} + static void filepair_cb(struct diff_filepair *pair) { unsigned long old_size = 0; unsigned long new_size = 0; int binary = 0; + linediff_fn print_line_fn = print_line; + if (use_ssdiff) { + 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); + if (use_ssdiff) + cgit_ssdiff_footer(); return; } if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, - &new_size, &binary, print_line)) + &new_size, &binary, print_line_fn)) cgit_print_error("Error running diff"); - if (binary) + if (binary) { + if (use_ssdiff) + html("<tr><td colspan='4'>Binary files differ</td></tr>"); + else html("Binary files differ"); } + if (use_ssdiff) + cgit_ssdiff_footer(); +} void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix) { enum object_type type; @@ -302,12 +332,22 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefi commit2 = lookup_commit_reference(old_rev_sha1); if (!commit2 || parse_commit(commit2)) cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(old_rev_sha1))); } + + if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff)) + use_ssdiff = 1; + + print_ssdiff_link(); cgit_print_diffstat(old_rev_sha1, new_rev_sha1); + if (use_ssdiff) { + html("<table summary='ssdiff' class='ssdiff'>"); + } else { html("<table summary='diff' class='diff'>"); html("<tr><td>"); + } cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix); + if (!use_ssdiff) html("</td></tr>"); html("</table>"); } @@ -65,9 +65,9 @@ void show_commit_decorations(struct commit *commit) } else { strncpy(buf, deco->name, sizeof(buf) - 1); cgit_commit_link(buf, NULL, "deco", ctx.qry.head, - sha1_to_hex(commit->object.sha1)); + sha1_to_hex(commit->object.sha1), 0); } deco = deco->next; } } @@ -88,9 +88,9 @@ void print_commit(struct commit *commit) html_link_close(); htmlf("</td><td%s>", ctx.qry.showmsg ? " class='logsubject'" : ""); cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head, - sha1_to_hex(commit->object.sha1)); + sha1_to_hex(commit->object.sha1), 0); show_commit_decorations(commit); html("</td><td>"); html_txt(info->author); if (ctx.repo->enable_log_filecount) { @@ -73,9 +73,9 @@ static int print_branch(struct refinfo *ref) ctx.qry.showmsg); html("</td><td>"); if (ref->object->type == OBJ_COMMIT) { - cgit_commit_link(info->subject, NULL, NULL, name, NULL); + cgit_commit_link(info->subject, NULL, NULL, name, NULL, 0); html("</td><td>"); html_txt(info->author); html("</td><td colspan='2'>"); cgit_print_age(info->commit->date, -1, NULL); diff --git a/ui-shared.c b/ui-shared.c index 3a9e67b..08ea003 100644 --- a/ui-shared.c +++ b/ui-shared.c @@ -316,17 +316,33 @@ void cgit_log_link(char *name, char *title, char *class, char *head, html("</a>"); } void cgit_commit_link(char *name, char *title, char *class, char *head, - char *rev) + char *rev, int toggle_ssdiff) { if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { name[ctx.cfg.max_msg_len] = '\0'; name[ctx.cfg.max_msg_len - 1] = '.'; name[ctx.cfg.max_msg_len - 2] = '.'; name[ctx.cfg.max_msg_len - 3] = '.'; } - reporevlink("commit", name, title, class, head, rev, NULL); + + char *delim; + + delim = repolink(title, class, "commit", head, NULL); + if (rev && strcmp(rev, ctx.qry.head)) { + html(delim); + html("id="); + html_url_arg(rev); + delim = "&"; + } + if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) { + html(delim); + html("ss=1"); + } + html("'>"); + html_txt(name); + html("</a>"); } void cgit_refs_link(char *name, char *title, char *class, char *head, char *rev, char *path) @@ -340,9 +356,10 @@ void cgit_snapshot_link(char *name, char *title, char *class, char *head, reporevlink("snapshot", name, title, class, head, rev, archivename); } void cgit_diff_link(char *name, char *title, char *class, char *head, - char *new_rev, char *old_rev, char *path) + char *new_rev, char *old_rev, char *path, + int toggle_ssdiff) { char *delim; delim = repolink(title, class, "diff", head, path); @@ -355,8 +372,13 @@ void cgit_diff_link(char *name, char *title, char *class, char *head, if (old_rev) { html(delim); html("id2="); html_url_arg(old_rev); + delim = "&"; + } + if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) { + html(delim); + html("ss=1"); } html("'>"); html_txt(name); html("</a>"); @@ -382,9 +404,9 @@ void cgit_object_link(struct object *obj) shortrev = xstrdup(fullrev); shortrev[10] = '\0'; if (obj->type == OBJ_COMMIT) { cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL, - ctx.qry.head, fullrev); + ctx.qry.head, fullrev, 0); return; } else if (obj->type == OBJ_TREE) page = "tree"; else if (obj->type == OBJ_TAG) @@ -694,11 +716,11 @@ void cgit_print_pageheader(struct cgit_context *ctx) NULL, NULL, 0, NULL, NULL, ctx->qry.showmsg); cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head, ctx->qry.sha1, NULL); cgit_commit_link("commit", NULL, hc(cmd, "commit"), - ctx->qry.head, ctx->qry.sha1); + ctx->qry.head, ctx->qry.sha1, 0); cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head, - ctx->qry.sha1, ctx->qry.sha2, NULL); + ctx->qry.sha1, ctx->qry.sha2, NULL, 0); if (ctx->repo->max_stats) cgit_stats_link("stats", NULL, hc(cmd, "stats"), ctx->qry.head, NULL); if (ctx->repo->readme) diff --git a/ui-shared.h b/ui-shared.h index b12aa89..9ebc1f9 100644 --- a/ui-shared.h +++ b/ui-shared.h @@ -22,17 +22,18 @@ extern void cgit_plain_link(char *name, char *title, char *class, char *head, extern void cgit_log_link(char *name, char *title, char *class, char *head, char *rev, char *path, int ofs, char *grep, char *pattern, int showmsg); extern void cgit_commit_link(char *name, char *title, char *class, char *head, - char *rev); + char *rev, int toggle_ssdiff); extern void cgit_patch_link(char *name, char *title, char *class, char *head, char *rev); extern void cgit_refs_link(char *name, char *title, char *class, char *head, char *rev, char *path); extern void cgit_snapshot_link(char *name, char *title, char *class, char *head, char *rev, char *archivename); extern void cgit_diff_link(char *name, char *title, char *class, char *head, - char *new_rev, char *old_rev, char *path); + char *new_rev, char *old_rev, char *path, + int toggle_ssdiff); extern void cgit_stats_link(char *name, char *title, char *class, char *head, char *path); extern void cgit_object_link(struct object *obj); diff --git a/ui-ssdiff.c b/ui-ssdiff.c new file mode 100644 index 0000000..408e620 --- a/dev/null +++ b/ui-ssdiff.c @@ -0,0 +1,369 @@ +#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 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; + 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 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)); + 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_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("<span class='%s'>", class); + } + } else if (line[i] == lcs[j]) { + same = 1; + htmlf("</span>"); + 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("<tr>"); + if (old_line_no > 0) + htmlf("<td class='lineno'>%d</td><td class='%s'>", + old_line_no, class); + else if (old_line) + htmlf("<td class='lineno'></td><td class='%s'>", class); + else + htmlf("<td class='lineno'></td><td class='%s_dark'>", class); + if (old_line) { + if (lcs) + print_part_with_lcs("del", old_line, lcs); + else + html_txt(old_line); + } + + html("</td>"); + if (new_line_no > 0) + htmlf("<td class='lineno'>%d</td><td class='%s'>", + new_line_no, class); + else if (new_line) + htmlf("<td class='lineno'></td><td class='%s'>", class); + else + htmlf("<td class='lineno'></td><td class='%s_dark'>", class); + if (new_line) { + if (lcs) + print_part_with_lcs("add", new_line, lcs); + else + html_txt(new_line); + } + + html("</td></tr>"); + 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, 0); + 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, 0); + tmp = iter_new->next; + free(iter_new); + iter_new = tmp; + } +} + +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; + 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, + individual_chars); + else if (iter_old) + print_ssdiff_line("changed", iter_old->line_no, + iter_old->line, -1, NULL, 0); + else if (iter_new) + print_ssdiff_line("changed", -1, NULL, + iter_new->line_no, iter_new->line, 0); + 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, 0); + 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("<tr><td colspan='4' class='hunk'>"); + html_txt(line); + html("</td></tr>"); + } else { + html("<tr><td colspan='4' class='ctx'>"); + html_txt(line); + html("</td></tr>"); + } + line[len - 1] = c; +} + +void cgit_ssdiff_header_begin() +{ + current_old_line = -1; + current_new_line = -1; + html("<tr><td class='space' colspan='4'><div></div></td></tr>"); + html("<tr><td class='head' colspan='4'>"); +} + +void cgit_ssdiff_header_end() +{ + html("</td><tr>"); +} + +void cgit_ssdiff_footer() +{ + if (deferred_old || deferred_new) + cgit_ssdiff_print_deferred_lines(); + html("<tr><td class='foot' colspan='4'></td></tr>"); +} diff --git a/ui-ssdiff.h b/ui-ssdiff.h new file mode 100644 index 0000000..64b4b12 --- a/dev/null +++ b/ui-ssdiff.h @@ -0,0 +1,13 @@ +#ifndef UI_SSDIFF_H +#define UI_SSDIFF_H + +extern void cgit_ssdiff_print_deferred_lines(); + +extern void cgit_ssdiff_line_cb(char *line, int len); + +extern void cgit_ssdiff_header_begin(); +extern void cgit_ssdiff_header_end(); + +extern void cgit_ssdiff_footer(); + +#endif /* UI_SSDIFF_H */ |