Commit Diff


commit - 7532ccdaec478d70fa5e613b9cda974c132faa6d
commit + c0f61fa4e4ee942c16faa19eddc4b5362287b12c
blob - 983fa45bf1aa8f912574f9bd29fd81395a4f9837
blob + fb2fc4d627cc55c63bde40a0633365df3bfa641c
--- tog/tog.1
+++ tog/tog.1
@@ -296,13 +296,20 @@ If the
 .Cm diff
 view was opened via the
 .Cm log
-view, move to the Nth previous (younger) commit (default: 1).
+view, move to the Nth previous (younger) commit.
+If the diff was opened via the
+.Cm blame
+view, move to the Nth previous line and load the corresponding commit
+(default: 1).
 .It Cm >, Full stop
 If the
 .Cm diff
 view was opened via the
 .Cm log
-view, move to the Nth next (older) commit (default: 1).
+view, move to the Nth next (older) commit.
+If the diff was opened via the
+.Cm blame
+view, move to the Nth next line and load the corresponding commit (default: 1).
 .It Cm /
 Prompt for a search pattern and start searching for matching lines.
 The search pattern is an extended regular expression.
blob - a46cb65fd55714965d1a709355fc9f76db13c9f4
blob + 69d159dd681696985e8930c154e65d4c007197bb
--- tog/tog.c
+++ tog/tog.c
@@ -332,8 +332,8 @@ struct tog_diff_view_state {
 	int matched_line;
 	int selected_line;
 
-	/* passed from log view; may be NULL */
-	struct tog_view *log_view;
+	/* passed from log or blame view; may be NULL */
+	struct tog_view *parent_view;
 };
 
 pthread_mutex_t tog_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -429,6 +429,7 @@ struct tog_blame_view_state {
 	int first_displayed_line;
 	int last_displayed_line;
 	int selected_line;
+	int last_diffed_line;
 	int blame_complete;
 	int eof;
 	int done;
@@ -4426,7 +4427,7 @@ static const struct got_error *
 open_diff_view(struct tog_view *view, struct got_object_id *id1,
     struct got_object_id *id2, const char *label1, const char *label2,
     int diff_context, int ignore_whitespace, int force_text_diff,
-    struct tog_view *log_view, struct got_repository *repo)
+    struct tog_view *parent_view, struct got_repository *repo)
 {
 	const struct got_error *err;
 	struct tog_diff_view_state *s = &view->state.diff;
@@ -4498,7 +4499,7 @@ open_diff_view(struct tog_view *view, struct got_objec
 	s->diff_context = diff_context;
 	s->ignore_whitespace = ignore_whitespace;
 	s->force_text_diff = force_text_diff;
-	s->log_view = log_view;
+	s->parent_view = parent_view;
 	s->repo = repo;
 
 	STAILQ_INIT(&s->colors);
@@ -4540,8 +4541,9 @@ open_diff_view(struct tog_view *view, struct got_objec
 			goto done;
 	}
 
-	if (log_view && view_is_splitscreen(view))
-		show_log_view(log_view); /* draw vborder */
+	if (parent_view && parent_view->type == TOG_VIEW_LOG &&
+	    view_is_splitscreen(view))
+		show_log_view(parent_view); /* draw border */
 	diff_view_indicate_progress(view);
 
 	err = create_diff(s);
@@ -4632,6 +4634,11 @@ reset_diff_view(struct tog_view *view)
 	return create_diff(s);
 }
 
+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 *,
+    int, int);
+
 static const struct got_error *
 input_diff_view(struct tog_view **new_view, struct tog_view *view, int ch)
 {
@@ -4642,7 +4649,7 @@ input_diff_view(struct tog_view **new_view, struct tog
 	char *line = NULL;
 	size_t linesize = 0;
 	ssize_t linelen;
-	int i, nscroll = view->nlines - 1;
+	int i, nscroll = view->nlines - 1, up = 0;
 
 	switch (ch) {
 	case '0':
@@ -4769,54 +4776,61 @@ input_diff_view(struct tog_view **new_view, struct tog
 		break;
 	case '<':
 	case ',':
-		if (s->log_view == NULL) {
+		up = 1;
+		/* FALL THROUGH */
+	case '>':
+	case '.':
+		if (s->parent_view == NULL) {
 			view->count = 0;
 			break;
 		}
-		ls = &s->log_view->state.log;
-		old_selected_entry = ls->selected_entry;
+		s->parent_view->count = view->count;
 
-		/* view->count handled in input_log_view() */
-		err = input_log_view(NULL, s->log_view, KEY_UP);
-		if (err)
-			break;
+		if (s->parent_view->type == TOG_VIEW_LOG) {
+			ls = &s->parent_view->state.log;
+			old_selected_entry = ls->selected_entry;
 
-		if (old_selected_entry == ls->selected_entry)
-			break;
-
-		err = set_selected_commit(s, ls->selected_entry);
-		if (err)
-			break;
+			err = input_log_view(NULL, s->parent_view,
+			    up ? KEY_UP : KEY_DOWN);
+			if (err)
+				break;
+			view->count = s->parent_view->count;
 
-		s->first_displayed_line = 1;
-		s->last_displayed_line = view->nlines;
-		s->matched_line = 0;
-		view->x = 0;
+			if (old_selected_entry == ls->selected_entry)
+				break;
 
-		diff_view_indicate_progress(view);
-		err = create_diff(s);
-		break;
-	case '>':
-	case '.':
-		if (s->log_view == NULL) {
-			view->count = 0;
-			break;
-		}
-		ls = &s->log_view->state.log;
-		old_selected_entry = ls->selected_entry;
+			err = set_selected_commit(s, ls->selected_entry);
+			if (err)
+				break;
+		} else if (s->parent_view->type == TOG_VIEW_BLAME) {
+			struct tog_blame_view_state *bs;
+			struct got_object_id *id, *prev_id;
 
-		/* view->count handled in input_log_view() */
-		err = input_log_view(NULL, s->log_view, KEY_DOWN);
-		if (err)
-			break;
+			bs = &s->parent_view->state.blame;
+			prev_id = get_annotation_for_line(bs->blame.lines,
+			    bs->blame.nlines, bs->last_diffed_line);
+
+			err = input_blame_view(&view, s->parent_view,
+			    up ? KEY_UP : KEY_DOWN);
+			if (err)
+				break;
+			view->count = s->parent_view->count;
 
-		if (old_selected_entry == ls->selected_entry)
-			break;
+			if (prev_id == NULL)
+				break;
+			id = get_selected_commit_id(bs->blame.lines,
+			    bs->blame.nlines, bs->first_displayed_line,
+			    bs->selected_line);
+			if (id == NULL)
+				break;
 
-		err = set_selected_commit(s, ls->selected_entry);
-		if (err)
-			break;
+			if (!got_object_id_cmp(prev_id, id))
+				break;
 
+			err = input_blame_view(&view, s->parent_view, KEY_ENTER);
+			if (err)
+				break;
+		}
 		s->first_displayed_line = 1;
 		s->last_displayed_line = view->nlines;
 		s->matched_line = 0;
@@ -5077,15 +5091,14 @@ draw_blame(struct tog_view *view)
 		wline = NULL;
 		view->maxx = MAX(view->maxx, width);
 
-		if (view->focussed && nprinted == s->selected_line - 1)
+		if (nprinted == s->selected_line - 1)
 			wstandout(view->window);
 
 		if (blame->nlines > 0) {
 			blame_line = &blame->lines[lineno - 1];
 			if (blame_line->annotated && prev_id &&
 			    got_object_id_cmp(prev_id, blame_line->id) == 0 &&
-			    !(view->focussed &&
-			    nprinted == s->selected_line - 1)) {
+			    !(nprinted == s->selected_line - 1)) {
 				waddstr(view->window, "        ");
 			} else if (blame_line->annotated) {
 				char *id_str;
@@ -5114,7 +5127,7 @@ draw_blame(struct tog_view *view)
 			prev_id = NULL;
 		}
 
-		if (view->focussed && nprinted == s->selected_line - 1)
+		if (nprinted == s->selected_line - 1)
 			wstandend(view->window);
 		waddstr(view->window, " ");
 
@@ -5278,6 +5291,22 @@ get_selected_commit_id(struct tog_blame_line *lines, i
 		return NULL;
 
 	line = &lines[first_displayed_line - 1 + selected_line - 1];
+	if (!line->annotated)
+		return NULL;
+
+	return line->id;
+}
+
+static struct got_object_id *
+get_annotation_for_line(struct tog_blame_line *lines, int nlines,
+    int lineno)
+{
+	struct tog_blame_line *line;
+
+	if (nlines <= 0 || lineno >= nlines)
+		return NULL;
+
+	line = &lines[lineno - 1];
 	if (!line->annotated)
 		return NULL;
 
@@ -5845,22 +5874,35 @@ input_blame_view(struct tog_view **new_view, struct to
 		if (err)
 			break;
 		pid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
-		if (view_is_parent_view(view))
-			view_get_split(view, &begin_y, &begin_x);
+		if (*new_view) {
+			/* traversed from diff view, release diff resources  */
+			err = close_diff_view(*new_view);
+			if (err)
+				break;
+			diff_view = *new_view;
+		} else {
+			if (view_is_parent_view(view))
+				view_get_split(view, &begin_y, &begin_x);
 
-		diff_view = view_open(0, 0, begin_y, begin_x, TOG_VIEW_DIFF);
-		if (diff_view == NULL) {
-			got_object_commit_close(commit);
-			err = got_error_from_errno("view_open");
-			break;
+			diff_view = view_open(0, 0, begin_y, begin_x,
+			    TOG_VIEW_DIFF);
+			if (diff_view == NULL) {
+				got_object_commit_close(commit);
+				err = got_error_from_errno("view_open");
+				break;
+			}
 		}
 		err = open_diff_view(diff_view, pid ? &pid->id : NULL,
-		    id, NULL, NULL, 3, 0, 0, NULL, s->repo);
+		    id, NULL, NULL, 3, 0, 0, view, s->repo);
 		got_object_commit_close(commit);
 		if (err) {
 			view_close(diff_view);
 			break;
 		}
+		s->last_diffed_line = s->first_displayed_line - 1 +
+		    s->selected_line;
+		if (*new_view)
+			break;	/* still open from active diff view */
 		if (view_is_parent_view(view) &&
 		    view->mode == TOG_VIEW_SPLIT_HRZN) {
 			err = view_init_hsplit(view, begin_y);