commit - 4d8c22150d533cd0f073d3e0a8c65cabe8257e9b
commit + bcbd79e2dbba7016620ee0c8dcf3a3b207ef1c14
blob - 3f2551b5751217910c7223a184348710425b4883
blob + 35d9972ad1102971e5188ade36ee2e62b56459d0
--- tog/tog.1
+++ tog/tog.1
An arbitrary number of views may be opened simultaneously by
navigating the repository with
.Nm .
+Sometimes, if one view is opened from another view, the child view
+is linked to its parent view and both views will keep their data
+display in sync.
The supported views are:
.Bl -tag -width Ds
.It Cm log view
.Cm tog log
are as follows:
.Bl -tag -width Ds
-.It Cm Down-arrow, j, Page-down
+.It Cm Down-arrow, j, ], Page-down
Move the selection cursor down.
-.It Cm Up-arrow, k, Page-up
+.It Cm Up-arrow, k, [, Page-up
Move the selection cursor up.
.It Cm Enter
Switch to the
.Cm diff
view showing file changes made in the currently selected commit.
+The diff view is linked to the log view so either view will be
+updated when the other switches to a different commit.
.It Cm t
Switch to the
.Cm tree
Scroll down.
.It Cm Up-arrow, k, Page-up
Scroll up.
+.It [
+Switch to the previous commit in parent
+.Cm log view .
+.It ]
+Switch to the next commit in parent
+.Cm log view .
.El
.It Cm blame [ Fl c Ar commit ] [ Fl r Ar repository-path ] Ar path
Display line-by-line history of a file at the specified path.
blob - bd75a798e21e62084e6f2b3523cfcb01f3f690c1
blob + 1dc94f50c8b5a37214059da437455e8fd0e99dc2
--- tog/tog.c
+++ tog/tog.c
};
struct tog_diff_view_state {
+ struct got_object_id *id;
FILE *f;
int first_displayed_line;
int last_displayed_line;
struct got_repository *repo;
};
+TAILQ_HEAD(tog_view_list_head, tog_view);
struct tog_view {
TAILQ_ENTRY(tog_view) entry;
WINDOW *window;
int nlines, ncols, begin_y, begin_x;
int lines, cols; /* copies of LINES and COLS */
struct tog_view *parent;
+ struct tog_view *child;
/* type-specific state */
enum tog_view_type type;
const struct got_error *(*show)(struct tog_view *);
const struct got_error *(*input)(struct tog_view **,
struct tog_view **, struct tog_view *, int);
+ const struct got_error *(*set_child)(struct tog_view *,
+ struct tog_view *);
const struct got_error *(*close)(struct tog_view *);
};
-TAILQ_HEAD(tog_view_list_head, tog_view);
static const struct got_error *open_diff_view(struct tog_view *,
struct got_object *, struct got_object *, struct got_repository *);
static const struct got_error *input_log_view(struct tog_view **,
struct tog_view **, struct tog_view *, int);
static const struct got_error *close_log_view(struct tog_view *);
+static const struct got_error* set_child_log_view(struct tog_view *,
+ struct tog_view *);
static const struct got_error *open_blame_view(struct tog_view *, char *,
struct got_object_id *, struct got_repository *);
{
const struct got_error *err = NULL;
+ if (view->child)
+ view->child->parent = NULL;
+ if (view->parent)
+ view->parent->child = NULL;
if (view->close)
err = view->close(view);
if (view->panel)
begin_x = parent->ncols - 80;
view->parent = parent;
+ if (parent)
+ parent->child = view;
view->type = type;
view->lines = LINES;
view->cols = COLS;
{
const struct got_error *err;
+ if (view->parent) {
+ err = view->parent->show(view->parent);
+ if (err)
+ return err;
+ show_panel(view->parent->panel);
+ }
+
err = view->show(view);
if (err)
return err;
show_panel(view->panel);
+
+ if (view->child) {
+ err = view->child->show(view->child);
+ if (err)
+ return err;
+ show_panel(view->child->panel);
+ }
+
update_panels();
doupdate();
}
static const struct got_error *
+view_set_child(struct tog_view *view, struct tog_view *child)
+{
+ const struct got_error *err;
+
+ if (view->set_child) {
+ err = view->set_child(view, child);
+ if (err)
+ return err;
+ }
+
+ view->child = child;
+ return NULL;
+}
+
+static const struct got_error *
view_loop(struct tog_view *view)
{
const struct got_error *err = NULL;
view, &views);
if (err)
break;
- if (new_view) {
- /* TODO: de-duplicate! */
- TAILQ_INSERT_TAIL(&views, new_view, entry);
- view = new_view;
- }
if (dead_view) {
TAILQ_REMOVE(&views, dead_view, entry);
- TAILQ_FOREACH(view, &views, entry) {
- if (view->parent == dead_view)
- view->parent = NULL;
- }
if (dead_view->parent)
view = dead_view->parent;
else
view = TAILQ_LAST(&views, tog_view_list_head);
+ if (dead_view->child) {
+ TAILQ_REMOVE(&views, dead_view->child, entry);
+ err = view_close(dead_view->child);
+ if (err)
+ goto done;
+ }
err = view_close(dead_view);
if (err)
goto done;
}
+ if (new_view) {
+ /* TODO: de-duplicate! */
+ TAILQ_INSERT_TAIL(&views, new_view, entry);
+ if (new_view->parent) {
+ err = view_set_child(new_view->parent, new_view);
+ if (err)
+ goto done;
+ }
+ view = new_view;
+ }
}
done:
while (!TAILQ_EMPTY(&views)) {
else
*new_view = tree_view;
return err;
+}
+
+static const struct got_error *
+set_child_log_view(struct tog_view *view, struct tog_view *child)
+{
+ struct tog_log_view_state *s = &view->state.log;
+ struct tog_diff_view_state *ds;
+ struct commit_queue_entry *commit, *child_entry = NULL;
+ int selected_idx = 0;
+
+ if (child->type != TOG_VIEW_DIFF)
+ return NULL;
+ ds = &child->state.diff;
+
+ TAILQ_FOREACH(commit, &s->commits.head, entry) {
+ if (got_object_id_cmp(commit->id, ds->id) == 0) {
+ child_entry = commit;
+ break;
+ }
+ }
+ if (child_entry == NULL)
+ return NULL;
+
+ commit = s->first_displayed_entry;
+ while (commit) {
+ if (got_object_id_cmp(commit->id, child_entry->id) == 0) {
+ s->selected_entry = child_entry;
+ s->selected = selected_idx;
+ break;
+ }
+ if (commit == s->last_displayed_entry)
+ break;
+ selected_idx++;
+ commit = TAILQ_NEXT(commit, entry);
+ }
+
+ return show_log_view(view);
}
static const struct got_error *
view->show = show_log_view;
view->input = input_log_view;
view->close = close_log_view;
+ view->set_child = set_child_log_view;
done:
free(head_id);
return err;
}
static const struct got_error *
+update_diff_child_view(struct tog_view *parent,
+ struct commit_queue_entry *selected_entry, struct got_repository *repo)
+{
+ const struct got_error *err = NULL;
+ struct tog_diff_view_state *ds;
+ struct got_object *obj1 = NULL, *obj2 = NULL;
+ struct got_object_qid *parent_id;
+ struct tog_view *child_view = parent->child;
+
+ if (child_view == NULL)
+ return NULL;
+ if (child_view->type != TOG_VIEW_DIFF)
+ return NULL;
+ ds = &child_view->state.diff;
+ if (got_object_id_cmp(ds->id, selected_entry->id) == 0)
+ return NULL;
+
+ err = got_object_open(&obj2, repo, selected_entry->id);
+ if (err)
+ return err;
+
+ parent_id = SIMPLEQ_FIRST(&selected_entry->commit->parent_ids);
+ if (parent_id) {
+ err = got_object_open(&obj1, repo, parent_id->id);
+ if (err)
+ goto done;
+ }
+
+ err = close_diff_view(child_view);
+ if (err)
+ goto done;
+
+ err = open_diff_view(child_view, obj1, obj2, repo);
+ if (err)
+ goto done;
+done:
+ if (obj1)
+ got_object_close(obj1);
+ if (obj2)
+ got_object_close(obj2);
+ return err;
+}
+
+static const struct got_error *
show_log_view(struct tog_view *view)
{
+ const struct got_error *err = NULL;
struct tog_log_view_state *s = &view->state.log;
- return draw_commits(view, &s->last_displayed_entry,
+ err = draw_commits(view, &s->last_displayed_entry,
&s->selected_entry, s->first_displayed_entry,
&s->commits, s->selected, view->nlines, s->graph,
s->repo, s->in_repo_path);
+ if (err)
+ return err;
+
+ return update_diff_child_view(view, s->selected_entry, s->repo);
}
static const struct got_error *
switch (ch) {
case 'k':
case KEY_UP:
+ case '[':
if (s->selected > 0)
s->selected--;
if (s->selected > 0)
break;
case 'j':
case KEY_DOWN:
+ case ']':
if (s->selected < MIN(view->nlines - 2,
s->commits.ncommits - 1)) {
s->selected++;
fflush(f);
+ view->state.diff.id = got_object_get_id(obj2);
+ if (view->state.diff.id == NULL)
+ return got_error_from_errno();
view->state.diff.f = f;
view->state.diff.first_displayed_line = 1;
view->state.diff.last_displayed_line = view->nlines;
if (view->state.diff.f && fclose(view->state.diff.f) == EOF)
err = got_error_from_errno();
-
+ free(view->state.diff.id);
return err;
}
}
static const struct got_error *
-input_diff_view(struct tog_view **new, struct tog_view **dead,
+input_diff_view(struct tog_view **new_view, struct tog_view **dead_view,
struct tog_view *view, int ch)
{
+ const struct got_error *err = NULL;
struct tog_diff_view_state *s = &view->state.diff;
int i;
s->first_displayed_line++;
if (line == NULL)
break;
+ }
+ break;
+ case '[':
+ case ']': {
+ struct tog_log_view_state *ls;
+ struct commit_queue_entry *entry;
+ struct tog_view *diff_view;
+
+ if (view->parent == NULL)
+ break;
+ if (view->parent->type != TOG_VIEW_LOG)
+ break;
+ ls = &view->parent->state.log;
+
+ if (ch == '[') {
+ entry = TAILQ_PREV(ls->selected_entry,
+ commit_queue_head, entry);
+ } else {
+ entry = TAILQ_NEXT(ls->selected_entry, entry);
+ if (entry == NULL) {
+ err = fetch_next_commit(&entry,
+ ls->selected_entry,
+ &ls->commits, ls->graph,
+ ls->repo, ls->in_repo_path);
+ if (err)
+ break;
+ }
}
+ if (entry == NULL)
+ break;
+ err = show_commit(&diff_view, view->parent,
+ entry, ls->repo);
+ if (err)
+ break;
+ *new_view = diff_view;
+ *dead_view = view;
break;
+ }
default:
break;
}
- return NULL;
+ return err;
}
static const struct got_error *