REQ_TOGGLE_LINE_NUMBERS,
REQ_VIEW_NEXT,
REQ_VIEW_CLOSE,
+ REQ_NEXT,
+ REQ_PREVIOUS,
REQ_MOVE_UP,
- REQ_MOVE_UP_ENTER,
REQ_MOVE_DOWN,
- REQ_MOVE_DOWN_ENTER,
REQ_MOVE_PAGE_UP,
REQ_MOVE_PAGE_DOWN,
REQ_MOVE_FIRST_LINE,
unsigned int next:1; /* For ref lists: are there more refs? */
};
-struct commit {
- char id[41]; /* SHA1 ID. */
- char title[75]; /* The first line of the commit message. */
- char author[75]; /* The author of the commit. */
- struct tm time; /* Date from the author ident. */
- struct ref **refs; /* Repository references; tags & branch heads. */
-};
+static struct ref **get_refs(char *id);
/*
#define foreach_view(view, i) \
for (i = 0; i < ARRAY_SIZE(display) && (view = display[i]); i++)
+#define displayed_views() (display[1] != NULL ? 2 : 1)
/**
* Current head and commit ID
/* Scrolling backend */
static void
-do_scroll_view(struct view *view, int lines)
+do_scroll_view(struct view *view, int lines, bool redraw)
{
/* The rendering expects the new offset. */
view->offset += lines;
assert(view->offset <= view->lineno && view->lineno < view->lines);
+ if (!redraw)
+ return;
+
redrawwin(view->win);
wrefresh(view->win);
report("");
die("request %d not handled in switch", request);
}
- do_scroll_view(view, lines);
+ do_scroll_view(view, lines, TRUE);
}
/* Cursor moving */
static void
-move_view(struct view *view, enum request request)
+move_view(struct view *view, enum request request, bool redraw)
{
int steps;
break;
case REQ_MOVE_UP:
- case REQ_MOVE_UP_ENTER:
steps = -1;
break;
case REQ_MOVE_DOWN:
- case REQ_MOVE_DOWN_ENTER:
steps = 1;
break;
}
}
- do_scroll_view(view, steps);
+ do_scroll_view(view, steps, redraw);
return;
}
/* Draw the current line */
view->ops->draw(view, view->lineno - view->offset);
+ if (!redraw)
+ return;
+
redrawwin(view->win);
wrefresh(view->win);
report("");
* Incremental updating
*/
+static void
+end_update(struct view *view)
+{
+ if (!view->pipe)
+ return;
+ set_nonblocking_input(FALSE);
+ if (view->pipe == stdin)
+ fclose(view->pipe);
+ else
+ pclose(view->pipe);
+ view->pipe = NULL;
+}
+
static bool
begin_update(struct view *view)
{
const char *id = view->id;
+ if (view->pipe)
+ end_update(view);
+
if (opt_cmd[0]) {
string_copy(view->cmd, opt_cmd);
opt_cmd[0] = 0;
return TRUE;
}
-static void
-end_update(struct view *view)
-{
- if (!view->pipe)
- return;
- set_nonblocking_input(FALSE);
- if (view->pipe == stdin)
- fclose(view->pipe);
- else
- pclose(view->pipe);
- view->pipe = NULL;
-}
-
static bool
update_view(struct view *view)
{
bool split = !!(flags & OPEN_SPLIT);
bool reload = !!(flags & OPEN_RELOAD);
struct view *view = VIEW(request);
- int nviews = display[1] ? 2 : 1;
+ int nviews = displayed_views();
+ struct view *base_view = display[0];
if (view == prev && nviews == 1 && !reload) {
report("Already in %s view", view->name);
display[current_view] = view;
}
- resize_display();
+ /* Resize the view when switching between split- and full-screen,
+ * or when switching between two different full-screen views. */
+ if (nviews != displayed_views() ||
+ (nviews == 1 && base_view != display[0]))
+ resize_display();
if (split && prev->lineno - prev->offset >= prev->height) {
/* Take the title line into account. */
/* Scroll the view that was split if the current line is
* outside the new limited view. */
- do_scroll_view(prev, lines);
+ do_scroll_view(prev, lines, TRUE);
}
if (prev && view != prev) {
- /* "Blur" the previous view. */
- if (!backgrounded)
- update_view_title(prev);
-
/* Continue loading split views in the background. */
if (!split)
end_update(prev);
+ else if (!backgrounded)
+ /* "Blur" the previous view. */
+ update_view_title(prev);
+
view->parent = prev;
}
case REQ_MOVE_PAGE_DOWN:
case REQ_MOVE_FIRST_LINE:
case REQ_MOVE_LAST_LINE:
- move_view(view, request);
+ move_view(view, request, TRUE);
break;
case REQ_SCROLL_LINE_DOWN:
open_view(view, request, OPEN_DEFAULT);
break;
- case REQ_MOVE_UP_ENTER:
- case REQ_MOVE_DOWN_ENTER:
- move_view(view, request);
+ case REQ_NEXT:
+ case REQ_PREVIOUS:
+ request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
+
+ if (view == VIEW(REQ_VIEW_DIFF) &&
+ view->parent == VIEW(REQ_VIEW_MAIN)) {
+ bool redraw = display[1] == view;
+
+ view = view->parent;
+ move_view(view, request, redraw);
+ if (redraw)
+ update_view_title(view);
+ } else {
+ move_view(view, request, TRUE);
+ break;
+ }
/* Fall-through */
case REQ_ENTER:
case REQ_VIEW_NEXT:
{
- int nviews = display[1] ? 2 : 1;
+ int nviews = displayed_views();
int next_view = (current_view + 1) % nviews;
if (next_view == current_view) {
/*
- * View backend handlers
+ * Pager backend
*/
static bool
int split = 0;
if ((view == VIEW(REQ_VIEW_LOG) ||
- view == VIEW(REQ_VIEW_LOG)) &&
+ view == VIEW(REQ_VIEW_PAGER)) &&
get_line_type(line) == LINE_COMMIT) {
open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
split = 1;
};
-static struct ref **get_refs(char *id);
+/*
+ * Main view backend
+ */
+
+struct commit {
+ char id[41]; /* SHA1 ID. */
+ char title[75]; /* The first line of the commit message. */
+ char author[75]; /* The author of the commit. */
+ struct tm time; /* Date from the author ident. */
+ struct ref **refs; /* Repository references; tags & branch heads. */
+};
static bool
main_draw(struct view *view, unsigned int lineno)
static bool
main_enter(struct view *view)
{
- open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
+ enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
+
+ open_view(view, REQ_VIEW_DIFF, flags);
return TRUE;
}
* Switch to pager view.
* h::
* Show man page.
+ **/
+ { 'm', REQ_VIEW_MAIN },
+ { 'd', REQ_VIEW_DIFF },
+ { 'l', REQ_VIEW_LOG },
+ { 'p', REQ_VIEW_PAGER },
+ { 'h', REQ_VIEW_HELP },
+
+ /**
+ * View manipulation
+ * ~~~~~~~~~~~~~~~~~
* q::
* Close view, if multiple views are open it will jump back to the
* previous view in the view stack. If it is the last open view it
* pressing Enter will simply scroll the view one line down.
* Tab::
* Switch to next view.
+ * Up::
+ * This key is "context sensitive" and will move the cursor one
+ * line up. However, uf you opened a diff view from the main view
+ * (split- or full-screen) it will change the cursor to point to
+ * the previous commit in the main view and update the diff view
+ * to display it.
+ * Down::
+ * Similar to 'Up' but will move down.
**/
- { 'm', REQ_VIEW_MAIN },
- { 'd', REQ_VIEW_DIFF },
- { 'l', REQ_VIEW_LOG },
- { 'p', REQ_VIEW_PAGER },
- { 'h', REQ_VIEW_HELP },
-
{ 'q', REQ_VIEW_CLOSE },
{ KEY_TAB, REQ_VIEW_NEXT },
{ KEY_RETURN, REQ_ENTER },
+ { KEY_UP, REQ_PREVIOUS },
+ { KEY_DOWN, REQ_NEXT },
/**
* Cursor navigation
* ~~~~~~~~~~~~~~~~~
- * Up::
+ * j::
* Move cursor one line up.
- * Down::
- * Move cursor one line down.
* k::
- * Move cursor one line up and enter. When used in the main view
- * this will always show the diff of the current commit in the
- * split diff view.
- * j::
- * Move cursor one line down and enter.
+ * Move cursor one line down.
* PgUp::
* b::
* -::
* End::
* Jump to last line.
**/
- { KEY_UP, REQ_MOVE_UP },
- { KEY_DOWN, REQ_MOVE_DOWN },
- { 'k', REQ_MOVE_UP_ENTER },
- { 'j', REQ_MOVE_DOWN_ENTER },
+ { 'k', REQ_MOVE_UP },
+ { 'j', REQ_MOVE_DOWN },
{ KEY_HOME, REQ_MOVE_FIRST_LINE },
{ KEY_END, REQ_MOVE_LAST_LINE },
{ KEY_NPAGE, REQ_MOVE_PAGE_DOWN },
static struct ref *refs;
static size_t refs_size;
+/* Id <-> ref store */
+static struct ref ***id_refs;
+static size_t id_refs_size;
+
static struct ref **
get_refs(char *id)
{
- struct ref **id_refs = NULL;
- size_t id_refs_size = 0;
+ struct ref ***tmp_id_refs;
+ struct ref **ref_list = NULL;
+ size_t ref_list_size = 0;
size_t i;
+ for (i = 0; i < id_refs_size; i++)
+ if (!strcmp(id, id_refs[i][0]->id))
+ return id_refs[i];
+
+ tmp_id_refs = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs));
+ if (!tmp_id_refs)
+ return NULL;
+
+ id_refs = tmp_id_refs;
+
for (i = 0; i < refs_size; i++) {
struct ref **tmp;
if (strcmp(id, refs[i].id))
continue;
- tmp = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs));
+ tmp = realloc(ref_list, (ref_list_size + 1) * sizeof(*ref_list));
if (!tmp) {
- if (id_refs)
- free(id_refs);
+ if (ref_list)
+ free(ref_list);
return NULL;
}
- id_refs = tmp;
- if (id_refs_size > 0)
- id_refs[id_refs_size - 1]->next = 1;
- id_refs[id_refs_size] = &refs[i];
+ ref_list = tmp;
+ if (ref_list_size > 0)
+ ref_list[ref_list_size - 1]->next = 1;
+ ref_list[ref_list_size] = &refs[i];
/* XXX: The properties of the commit chains ensures that we can
* safely modify the shared ref. The repo references will
* always be similar for the same id. */
- id_refs[id_refs_size]->next = 0;
- id_refs_size++;
+ ref_list[ref_list_size]->next = 0;
+ ref_list_size++;
}
- return id_refs;
+ if (ref_list)
+ id_refs[id_refs_size++] = ref_list;
+
+ return ref_list;
}
static int
pclose(pipe);
- if (refs_size == 0)
- die("Not a git repository");
-
return OK;
}
if (load_refs() == ERR)
die("Failed to load refs.");
+ /* Require a git repository unless when running in pager mode. */
+ if (refs_size == 0 && opt_request != REQ_VIEW_PAGER)
+ die("Not a git repository");
+
for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
view->cmd_env = getenv(view->cmd_env);