commit c7d5c43cafada3470647a8b876d489018002f713 from: Mark Jamsek date: Thu Aug 04 11:53:10 2022 UTC tog: keymaps to jump to next/prev file/hunk in the diff Add () and {} key maps to navigate to the previous and next file or hunk in the diff. This required changing Got's diff API to collect line type metadata in addition to the line offsets already produced as prompted by stsp. ok stsp@ commit - b77ebd68348273de2b65cae433463da3da11ad6c commit + c7d5c43cafada3470647a8b876d489018002f713 blob - 52a212802c5d2d319bd43f2fa914eb2f48d10b90 blob + 15d9ab77ce628dc6b1939507cdf0e236943d6b82 --- got/got.c +++ got/got.c @@ -3701,7 +3701,7 @@ diff_trees(struct got_object_id *tree_id1, struct got_ arg.force_text_diff = force_text_diff; arg.diff_algo = GOT_DIFF_ALGORITHM_PATIENCE; arg.outfile = outfile; - arg.line_offsets = NULL; + arg.lines = NULL; arg.nlines = 0; while (path[0] == '/') path++; blob - 4243be1c5479449f6d3623e760d1979bd58682b8 blob + bc4bc871ee90bf32c4fd2bd3b2b452f29d5822a0 --- include/got_diff.h +++ include/got_diff.h @@ -20,6 +20,31 @@ enum got_diff_algorithm { }; /* + * List of all line types in a diff (including '{got,tog} log' lines). + * XXX GOT_DIFF_LINE_HUNK to GOT_DIFF_LINE_NONE inclusive must map to the + * DIFF_LINE_* macro counterparts defined in lib/diff_output.h (i.e., 60-64). + */ +enum got_diff_line_type { + GOT_DIFF_LINE_LOGMSG, + GOT_DIFF_LINE_AUTHOR, + GOT_DIFF_LINE_DATE, + GOT_DIFF_LINE_CHANGES, + GOT_DIFF_LINE_META, + GOT_DIFF_LINE_BLOB_MIN, + GOT_DIFF_LINE_BLOB_PLUS, + GOT_DIFF_LINE_HUNK = 60, + GOT_DIFF_LINE_MINUS, + GOT_DIFF_LINE_PLUS, + GOT_DIFF_LINE_CONTEXT, + GOT_DIFF_LINE_NONE +}; + +struct got_diff_line { + off_t offset; + uint8_t type; +}; + +/* * Compute the differences between two blobs and write unified diff text * to the provided output file. Two open temporary files must be provided * for internal use; these files can be obtained from got_opentemp() and @@ -34,7 +59,7 @@ enum got_diff_algorithm { * If not NULL, the two initial output arguments will be populated with an * array of line offsets for, and the number of lines in, the unidiff text. */ -const struct got_error *got_diff_blob(off_t **, size_t *, +const struct got_error *got_diff_blob(struct got_diff_line **, size_t *, struct got_blob_object *, struct got_blob_object *, FILE *, FILE *, const char *, const char *, enum got_diff_algorithm, int, int, int, FILE *); @@ -84,18 +109,18 @@ struct got_diff_blob_output_unidiff_arg { /* * The number of lines contained in produced unidiff text output, - * and an array of byte offsets to each line. May be initialized to - * zero and NULL to ignore line offsets. If not NULL, then the line - * offsets array will be populated. Optionally, the array can be - * pre-populated with line offsets, with nlines > 0 indicating - * the length of the pre-populated array. This is useful if the - * output file already contains some lines of text. - * The array will be grown as needed to accomodate additional line - * offsets, and the last offset found in a pre-populated array will - * be added to all subsequent offsets. + * and an array of got_diff_lines with byte offset and line type to + * each line. May be initialized to zero and NULL to ignore line + * metadata. If not NULL, then the array of line offsets and types will + * be populated. Optionally, the array can be pre-populated with line + * offsets and types, with nlines > 0 indicating the length of the + * pre-populated array. This is useful if the output file already + * contains some lines of text. The array will be grown as needed to + * accomodate additional offsets and types, and the last offset found + * in a pre-populated array will be added to all subsequent offsets. */ size_t nlines; - off_t *line_offsets; /* Dispose of with free(3) when done. */ + struct got_diff_line *lines; /* Dispose of with free(3) when done. */ }; const struct got_error *got_diff_blob_output_unidiff(void *, struct got_blob_object *, struct got_blob_object *, FILE *, FILE *, @@ -155,10 +180,10 @@ const struct got_error *got_diff_tree_collect_changed_ * If not NULL, the two initial output arguments will be populated with an * array of line offsets for, and the number of lines in, the unidiff text. */ -const struct got_error *got_diff_objects_as_blobs(off_t **, size_t *, - FILE *, FILE *, int, int, struct got_object_id *, struct got_object_id *, - const char *, const char *, enum got_diff_algorithm, int, int, int, - struct got_repository *, FILE *); +const struct got_error *got_diff_objects_as_blobs(struct got_diff_line **, + size_t *, FILE *, FILE *, int, int, struct got_object_id *, + struct got_object_id *, const char *, const char *, enum got_diff_algorithm, + int, int, int, struct got_repository *, FILE *); struct got_pathlist_head; @@ -177,10 +202,11 @@ struct got_pathlist_head; * If not NULL, the two initial output arguments will be populated with an * array of line offsets for, and the number of lines in, the unidiff text. */ -const struct got_error *got_diff_objects_as_trees(off_t **, size_t *, - FILE *, FILE *, int, int, struct got_object_id *, struct got_object_id *, - struct got_pathlist_head *, const char *, const char *, - enum got_diff_algorithm, int, int, int, struct got_repository *, FILE *); +const struct got_error *got_diff_objects_as_trees(struct got_diff_line **, + size_t *, FILE *, FILE *, int, int, struct got_object_id *, + struct got_object_id *, struct got_pathlist_head *, const char *, + const char *, enum got_diff_algorithm, int, int, int, + struct got_repository *, FILE *); /* * Diff two objects, assuming both objects are commits. @@ -194,9 +220,9 @@ const struct got_error *got_diff_objects_as_trees(off_ * If not NULL, the two initial output arguments will be populated with an * array of line offsets for, and the number of lines in, the unidiff text. */ -const struct got_error *got_diff_objects_as_commits(off_t **, size_t *, - FILE *, FILE *, int, int, struct got_object_id *, struct got_object_id *, - struct got_pathlist_head *, enum got_diff_algorithm, int, int, int, - struct got_repository *, FILE *); +const struct got_error *got_diff_objects_as_commits(struct got_diff_line **, + size_t *, FILE *, FILE *, int, int, struct got_object_id *, + struct got_object_id *, struct got_pathlist_head *, enum got_diff_algorithm, + int, int, int, struct got_repository *, FILE *); #define GOT_DIFF_MAX_CONTEXT 64 blob - 7268a714345f26eb33869d80941662fdd7688b13 blob + f671a4f105400e52465bc32e860ae0b0b1e71afd --- lib/diff.c +++ lib/diff.c @@ -39,21 +39,24 @@ #include "got_lib_object.h" static const struct got_error * -add_line_offset(off_t **line_offsets, size_t *nlines, off_t off) +add_line_metadata(struct got_diff_line **lines, size_t *nlines, + off_t off, uint8_t type) { - off_t *p; + struct got_diff_line *p; - p = reallocarray(*line_offsets, *nlines + 1, sizeof(off_t)); + p = reallocarray(*lines, *nlines + 1, sizeof(**lines)); if (p == NULL) return got_error_from_errno("reallocarray"); - *line_offsets = p; - (*line_offsets)[*nlines] = off; + *lines = p; + (*lines)[*nlines].offset = off; + (*lines)[*nlines].type = type; (*nlines)++; + return NULL; } static const struct got_error * -diff_blobs(off_t **line_offsets, size_t *nlines, +diff_blobs(struct got_diff_line **lines, size_t *nlines, struct got_diffreg_result **resultp, struct got_blob_object *blob1, struct got_blob_object *blob2, FILE *f1, FILE *f2, const char *label1, const char *label2, mode_t mode1, mode_t mode2, @@ -69,10 +72,10 @@ diff_blobs(off_t **line_offsets, size_t *nlines, off_t outoff = 0; int n; - if (line_offsets && *line_offsets && *nlines > 0) - outoff = (*line_offsets)[*nlines - 1]; - else if (line_offsets) { - err = add_line_offset(line_offsets, nlines, 0); + if (lines && *lines && nlines > 0) + outoff = (*lines)[*nlines - 1].offset; + else if (lines) { + err = add_line_metadata(lines, nlines, 0, GOT_DIFF_LINE_NONE); if (err) goto done; } @@ -141,8 +144,9 @@ diff_blobs(off_t **line_offsets, size_t *nlines, if (n < 0) goto done; outoff += n; - if (line_offsets) { - err = add_line_offset(line_offsets, nlines, outoff); + if (lines) { + err = add_line_metadata(lines, nlines, outoff, + GOT_DIFF_LINE_BLOB_MIN); if (err) goto done; } @@ -152,8 +156,9 @@ diff_blobs(off_t **line_offsets, size_t *nlines, if (n < 0) goto done; outoff += n; - if (line_offsets) { - err = add_line_offset(line_offsets, nlines, outoff); + if (lines) { + err = add_line_metadata(lines, nlines, outoff, + GOT_DIFF_LINE_BLOB_PLUS); if (err) goto done; } @@ -167,7 +172,7 @@ diff_blobs(off_t **line_offsets, size_t *nlines, goto done; if (outfile) { - err = got_diffreg_output(line_offsets, nlines, result, + err = got_diffreg_output(lines, nlines, result, blob1 != NULL, blob2 != NULL, label1 ? label1 : idstr1, label2 ? label2 : idstr2, @@ -196,19 +201,19 @@ got_diff_blob_output_unidiff(void *arg, struct got_blo { struct got_diff_blob_output_unidiff_arg *a = arg; - return diff_blobs(&a->line_offsets, &a->nlines, NULL, + return diff_blobs(&a->lines, &a->nlines, NULL, blob1, blob2, f1, f2, label1, label2, mode1, mode2, a->diff_context, a->ignore_whitespace, a->force_text_diff, a->outfile, a->diff_algo); } const struct got_error * -got_diff_blob(off_t **line_offsets, size_t *nlines, +got_diff_blob(struct got_diff_line **lines, size_t*nlines, struct got_blob_object *blob1, struct got_blob_object *blob2, FILE *f1, FILE *f2, const char *label1, const char *label2, enum got_diff_algorithm diff_algo, int diff_context, int ignore_whitespace, int force_text_diff, FILE *outfile) { - return diff_blobs(line_offsets, nlines, NULL, blob1, blob2, f1, f2, + return diff_blobs(lines, nlines, NULL, blob1, blob2, f1, f2, label1, label2, 0, 0, diff_context, ignore_whitespace, force_text_diff, outfile, diff_algo); } @@ -726,7 +731,7 @@ got_diff_tree(struct got_tree_object *tree1, struct go } const struct got_error * -got_diff_objects_as_blobs(off_t **line_offsets, size_t *nlines, +got_diff_objects_as_blobs(struct got_diff_line **lines, size_t *nlines, FILE *f1, FILE *f2, int fd1, int fd2, struct got_object_id *id1, struct got_object_id *id2, const char *label1, const char *label2, @@ -750,9 +755,9 @@ got_diff_objects_as_blobs(off_t **line_offsets, size_t if (err) goto done; } - err = got_diff_blob(line_offsets, nlines, blob1, blob2, f1, f2, - label1, label2, diff_algo, diff_context, ignore_whitespace, - force_text_diff, outfile); + err = got_diff_blob(lines, nlines, blob1, blob2, f1, f2, label1, label2, + diff_algo, diff_context, ignore_whitespace, force_text_diff, + outfile); done: if (blob1) got_object_blob_close(blob1); @@ -890,24 +895,26 @@ done: } static const struct got_error * -show_object_id(off_t **line_offsets, size_t *nlines, const char *obj_typestr, - int ch, const char *id_str, FILE *outfile) +show_object_id(struct got_diff_line **lines, size_t *nlines, + const char *obj_typestr, int ch, const char *id_str, FILE *outfile) { const struct got_error *err; int n; off_t outoff = 0; n = fprintf(outfile, "%s %c %s\n", obj_typestr, ch, id_str); - if (line_offsets != NULL && *line_offsets != NULL) { + if (lines != NULL && *lines != NULL) { if (*nlines == 0) { - err = add_line_offset(line_offsets, nlines, 0); + err = add_line_metadata(lines, nlines, 0, + GOT_DIFF_LINE_META); if (err) return err; } else - outoff = (*line_offsets)[*nlines - 1]; + outoff = (*lines)[*nlines - 1].offset; outoff += n; - err = add_line_offset(line_offsets, nlines, outoff); + err = add_line_metadata(lines, nlines, outoff, + GOT_DIFF_LINE_META); if (err) return err; } @@ -916,7 +923,7 @@ show_object_id(off_t **line_offsets, size_t *nlines, c } static const struct got_error * -diff_objects_as_trees(off_t **line_offsets, size_t *nlines, +diff_objects_as_trees(struct got_diff_line **lines, size_t *nlines, FILE *f1, FILE *f2, int fd1, int fd2, struct got_object_id *id1, struct got_object_id *id2, struct got_pathlist_head *paths, const char *label1, const char *label2, @@ -927,7 +934,7 @@ diff_objects_as_trees(off_t **line_offsets, size_t *nl const struct got_error *err; struct got_tree_object *tree1 = NULL, *tree2 = NULL; struct got_diff_blob_output_unidiff_arg arg; - int want_lineoffsets = (line_offsets != NULL && *line_offsets != NULL); + int want_linemeta = (lines != NULL && *lines != NULL); if (id1 == NULL && id2 == NULL) return got_error(GOT_ERR_NO_OBJ); @@ -948,11 +955,11 @@ diff_objects_as_trees(off_t **line_offsets, size_t *nl arg.ignore_whitespace = ignore_whitespace; arg.force_text_diff = force_text_diff; arg.outfile = outfile; - if (want_lineoffsets) { - arg.line_offsets = *line_offsets; + if (want_linemeta) { + arg.lines = *lines; arg.nlines = *nlines; } else { - arg.line_offsets = NULL; + arg.lines = NULL; arg.nlines = 0; } if (paths == NULL || TAILQ_EMPTY(paths)) { @@ -963,8 +970,8 @@ diff_objects_as_trees(off_t **line_offsets, size_t *nl err = diff_paths(tree1, tree2, f1, f2, fd1, fd2, paths, repo, got_diff_blob_output_unidiff, &arg); } - if (want_lineoffsets) { - *line_offsets = arg.line_offsets; /* was likely re-allocated */ + if (want_linemeta) { + *lines = arg.lines; /* was likely re-allocated */ *nlines = arg.nlines; } done: @@ -976,7 +983,7 @@ done: } const struct got_error * -got_diff_objects_as_trees(off_t **line_offsets, size_t *nlines, +got_diff_objects_as_trees(struct got_diff_line **lines, size_t *nlines, FILE *f1, FILE *f2, int fd1, int fd2, struct got_object_id *id1, struct got_object_id *id2, struct got_pathlist_head *paths, const char *label1, const char *label2, @@ -993,15 +1000,14 @@ got_diff_objects_as_trees(off_t **line_offsets, size_t err = got_object_id_str(&idstr, id1); if (err) goto done; - err = show_object_id(line_offsets, nlines, "tree", '-', - idstr, outfile); + err = show_object_id(lines, nlines, "tree", '-', idstr, outfile); if (err) goto done; free(idstr); idstr = NULL; } else { - err = show_object_id(line_offsets, nlines, "tree", '-', - "/dev/null", outfile); + err = show_object_id(lines, nlines, "tree", '-', "/dev/null", + outfile); if (err) goto done; } @@ -1010,21 +1016,20 @@ got_diff_objects_as_trees(off_t **line_offsets, size_t err = got_object_id_str(&idstr, id2); if (err) goto done; - err = show_object_id(line_offsets, nlines, "tree", '+', - idstr, outfile); + err = show_object_id(lines, nlines, "tree", '+', idstr, outfile); if (err) goto done; free(idstr); idstr = NULL; } else { - err = show_object_id(line_offsets, nlines, "tree", '+', - "/dev/null", outfile); + err = show_object_id(lines, nlines, "tree", '+', "/dev/null", + outfile); if (err) goto done; } - err = diff_objects_as_trees(line_offsets, nlines, f1, f2, fd1, fd2, - id1, id2, paths, label1, label2, diff_context, ignore_whitespace, + err = diff_objects_as_trees(lines, nlines, f1, f2, fd1, fd2, id1, id2, + paths, label1, label2, diff_context, ignore_whitespace, force_text_diff, repo, outfile, diff_algo); done: free(idstr); @@ -1032,7 +1037,7 @@ done: } const struct got_error * -got_diff_objects_as_commits(off_t **line_offsets, size_t *nlines, +got_diff_objects_as_commits(struct got_diff_line **lines, size_t *nlines, FILE *f1, FILE *f2, int fd1, int fd2, struct got_object_id *id1, struct got_object_id *id2, struct got_pathlist_head *paths, enum got_diff_algorithm diff_algo, @@ -1053,15 +1058,15 @@ got_diff_objects_as_commits(off_t **line_offsets, size err = got_object_id_str(&idstr, id1); if (err) goto done; - err = show_object_id(line_offsets, nlines, "commit", '-', - idstr, outfile); + err = show_object_id(lines, nlines, "commit", '-', idstr, + outfile); if (err) goto done; free(idstr); idstr = NULL; } else { - err = show_object_id(line_offsets, nlines, "commit", '-', - "/dev/null", outfile); + err = show_object_id(lines, nlines, "commit", '-', "/dev/null", + outfile); if (err) goto done; } @@ -1073,12 +1078,11 @@ got_diff_objects_as_commits(off_t **line_offsets, size err = got_object_id_str(&idstr, id2); if (err) goto done; - err = show_object_id(line_offsets, nlines, "commit", '+', - idstr, outfile); + err = show_object_id(lines, nlines, "commit", '+', idstr, outfile); if (err) goto done; - err = diff_objects_as_trees(line_offsets, nlines, f1, f2, fd1, fd2, + err = diff_objects_as_trees(lines, nlines, f1, f2, fd1, fd2, commit1 ? got_object_commit_get_tree_id(commit1) : NULL, got_object_commit_get_tree_id(commit2), paths, "", "", diff_context, ignore_whitespace, force_text_diff, repo, outfile, blob - 30c7388ad74a1c5e2b958cf79a9f5c1790de245d blob + ab75c9fa52c19c7f3190c186c3002f46ee627c7e --- lib/diffreg.c +++ lib/diffreg.c @@ -251,7 +251,7 @@ done: } const struct got_error * -got_diffreg_output(off_t **line_offsets, size_t *nlines, +got_diffreg_output(struct got_diff_line **lines, size_t *nlines, struct got_diffreg_result *diff_result, int f1_exists, int f2_exists, const char *path1, const char *path2, enum got_diff_output_format output_format, int context_lines, FILE *outfile) @@ -272,13 +272,13 @@ got_diffreg_output(off_t **line_offsets, size_t *nline switch (output_format) { case GOT_DIFF_OUTPUT_UNIDIFF: rc = diff_output_unidiff( - line_offsets ? &output_info : NULL, outfile, &info, + lines ? &output_info : NULL, outfile, &info, diff_result->result, context_lines); if (rc != DIFF_RC_OK) return got_error_set_errno(rc, "diff_output_unidiff"); break; case GOT_DIFF_OUTPUT_EDSCRIPT: - rc = diff_output_edscript(line_offsets ? &output_info : NULL, + rc = diff_output_edscript(lines ? &output_info : NULL, outfile, &info, diff_result->result); if (rc != DIFF_RC_OK) return got_error_set_errno(rc, "diff_output_edscript"); @@ -286,29 +286,34 @@ got_diffreg_output(off_t **line_offsets, size_t *nline } - if (line_offsets && *line_offsets) { + if (lines && *lines) { if (output_info->line_offsets.len > 0) { - off_t prev_offset = 0, *p, *o; + struct got_diff_line *p; + off_t prev_offset = 0, *o; + uint8_t *o2; int i, len; if (*nlines > 0) { - prev_offset = (*line_offsets)[*nlines - 1]; + prev_offset = (*lines)[*nlines - 1].offset; /* * First line offset is always zero. Skip it * when appending to a pre-populated array. */ o = &output_info->line_offsets.head[1]; + o2 = &output_info->line_types.head[1]; len = output_info->line_offsets.len - 1; } else { o = &output_info->line_offsets.head[0]; + o2 = &output_info->line_types.head[0]; len = output_info->line_offsets.len; } - p = reallocarray(*line_offsets, *nlines + len, - sizeof(off_t)); + p = reallocarray(*lines, *nlines + len, sizeof(**lines)); if (p == NULL) return got_error_from_errno("calloc"); - for (i = 0; i < len; i++) - p[*nlines + i] = o[i] + prev_offset; - *line_offsets = p; + for (i = 0; i < len; i++) { + p[*nlines + i].offset = o[i] + prev_offset; + p[*nlines + i].type = o2[i]; + } + *lines = p; *nlines += len; } diff_output_info_free(output_info); blob - 3ff8d96891722f9a89a5245a2fb9b5e5c8424e83 blob + 43c7096d0a3fe2bb611660d822341235f3223a17 --- lib/got_lib_diff.h +++ lib/got_lib_diff.h @@ -44,7 +44,7 @@ const struct got_error *got_diff_prepare_file(FILE *, struct diff_data *, const struct diff_config *, int, int); const struct got_error *got_diffreg(struct got_diffreg_result **, FILE *, FILE *, enum got_diff_algorithm, int, int); -const struct got_error *got_diffreg_output(off_t **, size_t *, +const struct got_error *got_diffreg_output(struct got_diff_line **, size_t *, struct got_diffreg_result *, int, int, const char *, const char *, enum got_diff_output_format, int, FILE *); const struct got_error *got_diffreg_result_free(struct got_diffreg_result *); blob - ff238905b35da47efd5533334d11290e37f51345 blob + 7675eb02809eb3f2eb24a7b6e4066ffaefbc42bc --- tog/tog.1 +++ tog/tog.1 @@ -294,6 +294,14 @@ Scroll up N half pages (default: 1). Scroll to the top of the view. .It Cm End, G Scroll to the bottom of the view. +.It Cm \&( +Navigate to the Nth previous file in the diff (default: 1). +.It Cm \&) +Navigate to the Nth next file in the diff (default: 1). +.It Cm \&{ +Navigate to the Nth previous hunk in the diff (default: 1). +.It Cm \&} +Navigate to the Nth next hunk in the diff (default: 1). .It Cm \&[ Reduce diff context by N lines (default: 1). .It Cm \&] blob - 9b5b33a0cf3e7d1f1e77a8769555a571f3356109 blob + 62046ebce62dae8540d4c2262289b5f4b62e2550 --- tog/tog.c +++ tog/tog.c @@ -312,13 +312,13 @@ get_color_value(const char *envvar) return default_color_value(envvar); } - struct tog_diff_view_state { struct got_object_id *id1, *id2; const char *label1, *label2; FILE *f, *f1, *f2; int fd1, fd2; + int lineno; int first_displayed_line; int last_displayed_line; int eof; @@ -327,8 +327,8 @@ struct tog_diff_view_state { int force_text_diff; struct got_repository *repo; struct tog_colors colors; + struct got_diff_line *lines; size_t nlines; - off_t *line_offsets; int matched_line; int selected_line; @@ -3936,7 +3936,7 @@ draw_file(struct tog_view *view, const char *header) struct tog_diff_view_state *s = &view->state.diff; regmatch_t *regmatch = &view->regmatch; const struct got_error *err; - int lineno, nprinted = 0; + int nprinted = 0; char *line; size_t linesize = 0; ssize_t linelen; @@ -3948,8 +3948,8 @@ draw_file(struct tog_view *view, const char *header) off_t line_offset; attr_t attr; - lineno = s->first_displayed_line - 1; - line_offset = s->line_offsets[s->first_displayed_line - 1]; + s->lineno = s->first_displayed_line - 1; + line_offset = s->lines[s->first_displayed_line - 1].offset; if (fseeko(s->f, line_offset, SEEK_SET) == -1) return got_error_from_errno("fseek"); @@ -3961,7 +3961,7 @@ draw_file(struct tog_view *view, const char *header) if (header) { int ln = view->gline ? view->gline <= (view->nlines - 3) / 2 ? 1 : view->gline - (view->nlines - 3) / 2 : - lineno + s->selected_line; + s->lineno + s->selected_line; if (asprintf(&line, "[%d/%d] %s", ln, nlines, header) == -1) return got_error_from_errno("asprintf"); @@ -4001,11 +4001,11 @@ draw_file(struct tog_view *view, const char *header) } attr = 0; - if (++lineno < s->first_displayed_line) + if (++s->lineno < s->first_displayed_line) continue; - if (view->gline && !gotoline(view, &lineno, &nprinted)) + if (view->gline && !gotoline(view, &s->lineno, &nprinted)) continue; - if (lineno == view->hiline) + if (s->lineno == view->hiline) attr = A_STANDOUT; /* Set view->maxx based on full line length. */ @@ -4044,7 +4044,7 @@ draw_file(struct tog_view *view, const char *header) free(wline); wline = NULL; } - if (lineno == view->hiline) { + if (s->lineno == view->hiline) { /* highlight full gline length */ while (width++ < view->ncols) waddch(view->window, ' '); @@ -4055,7 +4055,7 @@ draw_file(struct tog_view *view, const char *header) if (attr) wattroff(view->window, attr); if (++nprinted == 1) - s->first_displayed_line = lineno; + s->first_displayed_line = s->lineno; } free(line); if (nprinted >= 1) @@ -4156,21 +4156,24 @@ done: } static const struct got_error * -add_line_offset(off_t **line_offsets, size_t *nlines, off_t off) +add_line_metadata(struct got_diff_line **lines, size_t *nlines, + off_t off, uint8_t type) { - off_t *p; + struct got_diff_line *p; - p = reallocarray(*line_offsets, *nlines + 1, sizeof(off_t)); + p = reallocarray(*lines, *nlines + 1, sizeof(**lines)); if (p == NULL) return got_error_from_errno("reallocarray"); - *line_offsets = p; - (*line_offsets)[*nlines] = off; + *lines = p; + (*lines)[*nlines].offset = off; + (*lines)[*nlines].type = type; (*nlines)++; + return NULL; } static const struct got_error * -write_commit_info(off_t **line_offsets, size_t *nlines, +write_commit_info(struct got_diff_line **lines, size_t *nlines, struct got_object_id *commit_id, struct got_reflist_head *refs, struct got_repository *repo, FILE *outfile) { @@ -4204,7 +4207,7 @@ write_commit_info(off_t **line_offsets, size_t *nlines goto done; } - err = add_line_offset(line_offsets, nlines, 0); + err = add_line_metadata(lines, nlines, 0, GOT_DIFF_LINE_NONE); if (err) goto done; @@ -4215,7 +4218,7 @@ write_commit_info(off_t **line_offsets, size_t *nlines goto done; } outoff += n; - err = add_line_offset(line_offsets, nlines, outoff); + err = add_line_metadata(lines, nlines, outoff, GOT_DIFF_LINE_META); if (err) goto done; @@ -4226,7 +4229,7 @@ write_commit_info(off_t **line_offsets, size_t *nlines goto done; } outoff += n; - err = add_line_offset(line_offsets, nlines, outoff); + err = add_line_metadata(lines, nlines, outoff, GOT_DIFF_LINE_AUTHOR); if (err) goto done; @@ -4239,7 +4242,8 @@ write_commit_info(off_t **line_offsets, size_t *nlines goto done; } outoff += n; - err = add_line_offset(line_offsets, nlines, outoff); + err = add_line_metadata(lines, nlines, outoff, + GOT_DIFF_LINE_DATE); if (err) goto done; } @@ -4252,7 +4256,8 @@ write_commit_info(off_t **line_offsets, size_t *nlines goto done; } outoff += n; - err = add_line_offset(line_offsets, nlines, outoff); + err = add_line_metadata(lines, nlines, outoff, + GOT_DIFF_LINE_AUTHOR); if (err) goto done; } @@ -4271,7 +4276,8 @@ write_commit_info(off_t **line_offsets, size_t *nlines goto done; } outoff += n; - err = add_line_offset(line_offsets, nlines, outoff); + err = add_line_metadata(lines, nlines, outoff, + GOT_DIFF_LINE_META); if (err) goto done; free(id_str); @@ -4290,7 +4296,8 @@ write_commit_info(off_t **line_offsets, size_t *nlines goto done; } outoff += n; - err = add_line_offset(line_offsets, nlines, outoff); + err = add_line_metadata(lines, nlines, outoff, + GOT_DIFF_LINE_LOGMSG); if (err) goto done; } @@ -4306,7 +4313,8 @@ write_commit_info(off_t **line_offsets, size_t *nlines goto done; } outoff += n; - err = add_line_offset(line_offsets, nlines, outoff); + err = add_line_metadata(lines, nlines, outoff, + GOT_DIFF_LINE_CHANGES); if (err) goto done; free((char *)pe->path); @@ -4315,7 +4323,7 @@ write_commit_info(off_t **line_offsets, size_t *nlines fputc('\n', outfile); outoff++; - err = add_line_offset(line_offsets, nlines, outoff); + err = add_line_metadata(lines, nlines, outoff, GOT_DIFF_LINE_NONE); done: got_pathlist_free(&changed_paths); free(id_str); @@ -4323,8 +4331,8 @@ done: free(refs_str); got_object_commit_close(commit); if (err) { - free(*line_offsets); - *line_offsets = NULL; + free(*lines); + *lines = NULL; *nlines = 0; } return err; @@ -4337,9 +4345,9 @@ create_diff(struct tog_diff_view_state *s) FILE *f = NULL; int obj_type; - free(s->line_offsets); - s->line_offsets = malloc(sizeof(off_t)); - if (s->line_offsets == NULL) + free(s->lines); + s->lines = malloc(sizeof(*s->lines)); + if (s->lines == NULL) return got_error_from_errno("malloc"); s->nlines = 0; @@ -4363,13 +4371,13 @@ create_diff(struct tog_diff_view_state *s) switch (obj_type) { case GOT_OBJ_TYPE_BLOB: - err = got_diff_objects_as_blobs(&s->line_offsets, &s->nlines, + err = got_diff_objects_as_blobs(&s->lines, &s->nlines, s->f1, s->f2, s->fd1, s->fd2, s->id1, s->id2, s->label1, s->label2, tog_diff_algo, s->diff_context, s->ignore_whitespace, s->force_text_diff, s->repo, s->f); break; case GOT_OBJ_TYPE_TREE: - err = got_diff_objects_as_trees(&s->line_offsets, &s->nlines, + err = got_diff_objects_as_trees(&s->lines, &s->nlines, s->f1, s->f2, s->fd1, s->fd2, s->id1, s->id2, NULL, "", "", tog_diff_algo, s->diff_context, s->ignore_whitespace, s->force_text_diff, s->repo, s->f); @@ -4386,17 +4394,17 @@ create_diff(struct tog_diff_view_state *s) refs = got_reflist_object_id_map_lookup(tog_refs_idmap, s->id2); /* Show commit info if we're diffing to a parent/root commit. */ if (s->id1 == NULL) { - err = write_commit_info(&s->line_offsets, &s->nlines, - s->id2, refs, s->repo, s->f); + err = write_commit_info(&s->lines, &s->nlines, s->id2, + refs, s->repo, s->f); if (err) goto done; } else { parent_ids = got_object_commit_get_parent_ids(commit2); STAILQ_FOREACH(pid, parent_ids, entry) { if (got_object_id_cmp(s->id1, &pid->id) == 0) { - err = write_commit_info( - &s->line_offsets, &s->nlines, - s->id2, refs, s->repo, s->f); + err = write_commit_info(&s->lines, + &s->nlines, s->id2, refs, s->repo, + s->f); if (err) goto done; break; @@ -4405,7 +4413,7 @@ create_diff(struct tog_diff_view_state *s) } got_object_commit_close(commit2); - err = got_diff_objects_as_commits(&s->line_offsets, &s->nlines, + err = got_diff_objects_as_commits(&s->lines, &s->nlines, s->f1, s->f2, s->fd1, s->fd2, s->id1, s->id2, NULL, tog_diff_algo, s->diff_context, s->ignore_whitespace, s->force_text_diff, s->repo, s->f); @@ -4415,8 +4423,6 @@ create_diff(struct tog_diff_view_state *s) err = got_error(GOT_ERR_OBJ_TYPE); break; } - if (err) - goto done; done: if (s->f && fflush(s->f) != 0 && err == NULL) err = got_error_from_errno("fflush"); @@ -4478,7 +4484,7 @@ search_next_diff_view(struct tog_view *view) lineno = s->nlines; } - offset = s->line_offsets[lineno - 1]; + offset = s->lines[lineno - 1].offset; if (fseeko(s->f, offset, SEEK_SET) != 0) { free(line); return got_error_from_errno("fseeko"); @@ -4539,8 +4545,8 @@ close_diff_view(struct tog_view *view) err = got_error_from_errno("close"); s->fd2 = -1; free_colors(&s->colors); - free(s->line_offsets); - s->line_offsets = NULL; + free(s->lines); + s->lines = NULL; s->nlines = 0; return err; } @@ -4754,8 +4760,44 @@ reset_diff_view(struct tog_view *view) s->matched_line = 0; diff_view_indicate_progress(view); return create_diff(s); +} + +static void +diff_prev_index(struct tog_diff_view_state *s, enum got_diff_line_type type) +{ + int start, i; + + i = start = s->first_displayed_line - 1; + + while (s->lines[i].type != type) { + if (i == 0) + i = s->nlines - 1; + if (--i == start) + return; /* do nothing, requested type not in file */ + } + + s->selected_line = 1; + s->first_displayed_line = i; } +static void +diff_next_index(struct tog_diff_view_state *s, enum got_diff_line_type type) +{ + int start, i; + + i = start = s->first_displayed_line + 1; + + while (s->lines[i].type != type) { + if (i == s->nlines - 1) + i = 0; + if (++i == start) + return; /* do nothing, requested type not in file */ + } + + s->selected_line = 1; + s->first_displayed_line = i; +} + static struct got_object_id *get_selected_commit_id(struct tog_blame_line *, int, int, int); static struct got_object_id *get_annotation_for_line(struct tog_blame_line *, @@ -4773,6 +4815,8 @@ input_diff_view(struct tog_view **new_view, struct tog ssize_t linelen; int i, nscroll = view->nlines - 1, up = 0; + s->lineno = s->first_displayed_line - 1 + s->selected_line; + switch (ch) { case '0': view->x = 0; @@ -4873,6 +4917,18 @@ input_diff_view(struct tog_view **new_view, struct tog } free(line); break; + case '(': + diff_prev_index(s, GOT_DIFF_LINE_BLOB_MIN); + break; + case ')': + diff_next_index(s, GOT_DIFF_LINE_BLOB_MIN); + break; + case '{': + diff_prev_index(s, GOT_DIFF_LINE_HUNK); + break; + case '}': + diff_next_index(s, GOT_DIFF_LINE_HUNK); + break; case '[': if (s->diff_context > 0) { s->diff_context--;