Download raw body.
tog: keymaps to navigate to prev/next file/hunk in the diff
On 22-07-25 02:54am, Mark Jamsek wrote:
> I got the idea from stsp the other day when he mentioned that he doesn't
> always navigate diffs linearly, and I started paying close attention to
> how I read diffs. He's right--sometimes I jump around a fair bit too,
> and sometimes I read them top to bottom. In both cases, I find these key
> maps help navigating diffs.
>
> Two new sets of key maps are proposed:
> P - jump to the previous file
> N - jump to the next file
> { - jump to the previous hunk
> } - jump to the next hunk
>
Revised diff removes the conflicting N key map!
I'm not super happy with the () for prev/next file, but I can't think of
anything better right now.
Two new sets of key maps are proposed:
( - jump to the previous file
) - jump to the next file
{ - jump to the previous hunk
} - jump to the next hunk
diff refs/heads/main refs/heads/dev/ezdiffnav
commit - 10a16316a03005a07c45b2bbf1b5644b64e846fb
commit + cfd3d4fbf3e1a90e8c6de09a35a9751fb518a0cd
blob - c09dbe11bb6e0224ed0138a35da047947c82f41d
blob + 81d1f7876e39d1a88ddf930b338afc6ec82bd39d
--- tog/tog.1
+++ tog/tog.1
@@ -290,6 +290,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 - aaca1ae9bd59788b771d3797213ce44b033f50a8
blob + b8b9d8f0f916c179a12699e08b0e67e96dde40b1
--- tog/tog.c
+++ tog/tog.c
@@ -313,12 +313,19 @@ get_color_value(const char *envvar)
return default_color_value(envvar);
}
+struct tog_index {
+ int *lineno;
+ int nalloc;
+ int idx;
+ int n;
+};
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,6 +334,7 @@ struct tog_diff_view_state {
int force_text_diff;
struct got_repository *repo;
struct tog_colors colors;
+ struct tog_index file_idx, hunk_idx;
size_t nlines;
off_t *line_offsets;
int matched_line;
@@ -4214,12 +4222,85 @@ done:
}
static const struct got_error *
+tog_alloc_index(struct tog_index *idx)
+{
+ if (idx->n == idx->nalloc) {
+ const size_t n = idx->nalloc ? idx->nalloc * 2 : 8;
+ int *p;
+
+ p = reallocarray(idx->lineno, n, sizeof(int));
+ if (p == NULL) {
+ free(idx->lineno);
+ idx->lineno = NULL;
+ return got_error_from_errno("reallocarray");
+ }
+ idx->lineno = p;
+ idx->nalloc = n;
+ }
+
+ return NULL;
+}
+
+/* Save line indexes of each file and hunk in the diff. */
+static const struct got_error *
+diff_index_file(struct tog_diff_view_state *s)
+{
+ const struct got_error *err = NULL;
+ char *line = NULL;
+ int len, lineno = 0;
+ size_t sz;
+
+ if (fseeko(s->f, 0L, SEEK_SET) == -1)
+ return got_error_from_errno("fseeko");
+
+ while (getline(&line, &sz, s->f) != -1) {
+ /* match: blob - {/dev/null,SHA1 (mode nnn)} */
+ len = strlen(line);
+ if (!strncmp(line, "blob - ", 7) &&
+ (len == SHA1_DIGEST_STRING_LENGTH + 7 ||
+ len == SHA1_DIGEST_STRING_LENGTH + 18 || len == 17)) {
+ err = tog_alloc_index(&s->file_idx);
+ if (err) {
+ free(line);
+ return err;
+ }
+ s->file_idx.lineno[s->file_idx.n++] = lineno;
+ } else if (!strncmp(line, "@@ -", 4)) {
+ err = tog_alloc_index(&s->hunk_idx);
+ if (err) {
+ free(line);
+ return err;
+ }
+ s->hunk_idx.lineno[s->hunk_idx.n++] = lineno;
+ }
+ ++lineno;
+ }
+ free(line);
+ if (!feof(s->f))
+ return got_ferror(s->f, GOT_ERR_IO);
+
+ return NULL;
+}
+
+static void
+free_index(struct tog_index *index)
+{
+ index->n = 0;
+ index->idx = 0;
+ index->nalloc = 0;
+ free(index->lineno);
+ index->lineno = NULL;
+}
+
+static const struct got_error *
create_diff(struct tog_diff_view_state *s)
{
const struct got_error *err = NULL;
FILE *f = NULL;
int obj_type;
+ free_index(&s->file_idx);
+ free_index(&s->hunk_idx);
free(s->line_offsets);
s->line_offsets = malloc(sizeof(off_t));
if (s->line_offsets == NULL)
@@ -4298,11 +4379,11 @@ 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");
+ if (err == NULL)
+ err = diff_index_file(s);
return err;
}
@@ -4421,6 +4502,8 @@ close_diff_view(struct tog_view *view)
if (s->fd2 != -1 && close(s->fd2) == -1 && err == NULL)
err = got_error_from_errno("close");
s->fd2 = -1;
+ free_index(&s->file_idx);
+ free_index(&s->hunk_idx);
free_colors(&s->colors);
free(s->line_offsets);
s->line_offsets = NULL;
@@ -4639,6 +4722,45 @@ reset_diff_view(struct tog_view *view)
return create_diff(s);
}
+static int
+diff_prev_index(struct tog_diff_view_state *s, struct tog_index *i)
+{
+ if (!i->lineno)
+ return s->first_displayed_line;
+
+ if (s->lineno - 1 <= i->lineno[i->idx]) {
+ if (i->idx == 0 || s->lineno < i->lineno[0])
+ i->idx = i->n - 1;
+ else
+ while (i->idx > 0 && s->lineno - 1 <= i->lineno[i->idx])
+ --i->idx;
+ } else
+ while (i->idx < i->n - 1 && s->lineno > i->lineno[i->idx + 1])
+ ++i->idx;
+
+ return i->lineno[i->idx] + 1;
+}
+
+static int
+diff_next_index(struct tog_diff_view_state *s, struct tog_index *i)
+{
+ if (!i->lineno)
+ return s->first_displayed_line;
+
+ if (s->lineno - 1 >= i->lineno[i->idx]) {
+ if (i->idx == i->n - 1 || s->lineno > i->lineno[i->n - 1])
+ i->idx = 0;
+ else
+ while (i->idx < i->n - 1 &&
+ s->lineno > i->lineno[i->idx])
+ ++i->idx;
+ } else
+ while (i->idx > 0 && s->lineno - 1 < i->lineno[i->idx - 1])
+ --i->idx;
+
+ return i->lineno[i->idx] + 1;
+}
+
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 *,
@@ -4656,6 +4778,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;
@@ -4756,6 +4880,22 @@ input_diff_view(struct tog_view **new_view, struct tog
}
free(line);
break;
+ case '(':
+ s->first_displayed_line = diff_prev_index(s, &s->file_idx);
+ s->selected_line = 1;
+ break;
+ case ')':
+ s->first_displayed_line = diff_next_index(s, &s->file_idx);
+ s->selected_line = 1;
+ break;
+ case '{':
+ s->first_displayed_line = diff_prev_index(s, &s->hunk_idx);
+ s->selected_line = 1;
+ break;
+ case '}':
+ s->first_displayed_line = diff_next_index(s, &s->hunk_idx);
+ s->selected_line = 1;
+ break;
case '[':
if (s->diff_context > 0) {
s->diff_context--;
--
Mark Jamsek <fnc.bsdbox.org>
GPG: F2FF 13DE 6A06 C471 CA80 E6E2 2930 DC66 86EE CF68
tog: keymaps to navigate to prev/next file/hunk in the diff