#define SIZEOF_REF 256 /* Size of symbolic or SHA1 ID. */
#define SIZEOF_CMD 1024 /* Size of command buffer. */
+#define SIZEOF_REVGRAPH 19 /* Size of revision ancestry graphics. */
/* This color name can be used to refer to the default term colors. */
#define COLOR_DEFAULT (-1)
REQ_(SCREEN_RESIZE, "Resize the screen"), \
REQ_(SHOW_VERSION, "Show version information"), \
REQ_(STOP_LOADING, "Stop all loading views"), \
- REQ_(TOGGLE_LINENO, "Toggle line numbers"),
+ REQ_(TOGGLE_LINENO, "Toggle line numbers"), \
+ REQ_(TOGGLE_REV_GRAPH, "Toggle revision graph visualization"),
/* User action requests. */
/* Option and state variables. */
static bool opt_line_number = FALSE;
+static bool opt_rev_graph = TRUE;
static int opt_num_interval = NUMBER_INTERVAL;
static int opt_tab_size = TABSIZE;
static enum request opt_request = REQ_VIEW_MAIN;
}
-static struct int_map color_map[] = {
-#define COLOR_MAP(name) { #name, STRING_SIZE(#name), COLOR_##name }
- COLOR_MAP(DEFAULT),
- COLOR_MAP(BLACK),
- COLOR_MAP(BLUE),
- COLOR_MAP(CYAN),
- COLOR_MAP(GREEN),
- COLOR_MAP(MAGENTA),
- COLOR_MAP(RED),
- COLOR_MAP(WHITE),
- COLOR_MAP(YELLOW),
-};
-
-static struct int_map attr_map[] = {
-#define ATTR_MAP(name) { #name, STRING_SIZE(#name), A_##name }
- ATTR_MAP(NORMAL),
- ATTR_MAP(BLINK),
- ATTR_MAP(BOLD),
- ATTR_MAP(DIM),
- ATTR_MAP(REVERSE),
- ATTR_MAP(STANDOUT),
- ATTR_MAP(UNDERLINE),
-};
+/*
+ * Line-oriented content detection.
+ */
#define LINE_INFO \
LINE(DIFF_HEADER, "diff --git ", COLOR_YELLOW, COLOR_DEFAULT, 0), \
LINE(MAIN_TAG, "", COLOR_MAGENTA, COLOR_DEFAULT, A_BOLD), \
LINE(MAIN_REF, "", COLOR_CYAN, COLOR_DEFAULT, A_BOLD), \
-
-/*
- * Line-oriented content detection.
- */
-
enum line_type {
#define LINE(type, line, fg, bg, attr) \
LINE_##type
* User config file handling.
*/
+static struct int_map color_map[] = {
+#define COLOR_MAP(name) { #name, STRING_SIZE(#name), COLOR_##name }
+ COLOR_MAP(DEFAULT),
+ COLOR_MAP(BLACK),
+ COLOR_MAP(BLUE),
+ COLOR_MAP(CYAN),
+ COLOR_MAP(GREEN),
+ COLOR_MAP(MAGENTA),
+ COLOR_MAP(RED),
+ COLOR_MAP(WHITE),
+ COLOR_MAP(YELLOW),
+};
+
#define set_color(color, name, namelen) \
set_from_int_map(color_map, ARRAY_SIZE(color_map), color, name, namelen)
+static struct int_map attr_map[] = {
+#define ATTR_MAP(name) { #name, STRING_SIZE(#name), A_##name }
+ ATTR_MAP(NORMAL),
+ ATTR_MAP(BLINK),
+ ATTR_MAP(BOLD),
+ ATTR_MAP(DIM),
+ ATTR_MAP(REVERSE),
+ ATTR_MAP(STANDOUT),
+ ATTR_MAP(UNDERLINE),
+};
+
#define set_attribute(attr, name, namelen) \
set_from_int_map(attr_map, ARRAY_SIZE(attr_map), attr, name, namelen)
wprintw(view->title, "[%s]", view->name);
if (view->lines || view->pipe) {
+ unsigned int view_lines = view->offset + view->height;
unsigned int lines = view->lines
- ? (view->lineno + 1) * 100 / view->lines
+ ? MIN(view_lines, view->lines) * 100 / view->lines
: 0;
wprintw(view->title, " - %s %d of %d (%d%%)",
return;
}
- if ((reload || strcmp(view->vid, view->id)) &&
- !begin_update(view)) {
+ if (view == VIEW(REQ_VIEW_HELP)) {
+ load_help_page();
+
+ } else if ((reload || strcmp(view->vid, view->id)) &&
+ !begin_update(view)) {
report("Failed to load %s view", view->name);
return;
}
view->parent = prev;
}
- if (view == VIEW(REQ_VIEW_HELP))
- load_help_page();
-
if (view->pipe && view->lines == 0) {
/* Clear the old view and let the incremental updating refill
* the screen. */
redraw_display();
break;
+ case REQ_TOGGLE_REV_GRAPH:
+ opt_rev_graph = !opt_rev_graph;
+ redraw_display();
+ break;
+
case REQ_PROMPT:
/* Always reload^Wrerun commands from the prompt. */
open_view(view, opt_request, OPEN_RELOAD);
*/
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. */
+ char id[41]; /* SHA1 ID. */
+ char title[75]; /* First line of the commit message. */
+ char author[75]; /* Author of the commit. */
+ struct tm time; /* Date from the author ident. */
+ struct ref **refs; /* Repository references. */
+ chtype graph[SIZEOF_REVGRAPH]; /* Ancestry chain graphics. */
+ size_t graph_size; /* The width of the graph array. */
};
static bool
if (type != LINE_CURSOR)
wattrset(view->win, A_NORMAL);
- mvwaddch(view->win, lineno, col, ACS_LTEE);
- wmove(view->win, lineno, col + 2);
- col += 2;
+ if (opt_rev_graph && commit->graph_size) {
+ size_t i;
+
+ wmove(view->win, lineno, col);
+ /* Using waddch() instead of waddnstr() ensures that
+ * they'll be rendered correctly for the cursor line. */
+ for (i = 0; i < commit->graph_size; i++)
+ waddch(view->win, commit->graph[i]);
+
+ col += commit->graph_size + 1;
+ }
+
+ wmove(view->win, lineno, col);
if (commit->refs) {
size_t i = 0;
view->line[view->lines++].data = commit;
string_copy(commit->id, line);
commit->refs = get_refs(commit->id);
+ commit->graph[commit->graph_size++] = ACS_LTEE;
break;
case LINE_AUTHOR:
{ 'v', REQ_SHOW_VERSION },
{ 'r', REQ_SCREEN_REDRAW },
{ 'n', REQ_TOGGLE_LINENO },
+ { 'g', REQ_TOGGLE_REV_GRAPH},
{ ':', REQ_PROMPT },
/* wgetch() with nodelay() enabled returns ERR when there's no input. */
{
struct ref *ref;
bool tag = FALSE;
- bool tag_commit = FALSE;
- /* Commits referenced by tags has "^{}" appended. */
- if (name[namelen - 1] == '}') {
+ if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
+ /* Commits referenced by tags has "^{}" appended. */
+ if (name[namelen - 1] != '}')
+ return OK;
+
while (namelen > 0 && name[namelen] != '^')
namelen--;
- if (namelen > 0)
- tag_commit = TRUE;
- name[namelen] = 0;
- }
- if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
- if (!tag_commit)
- return OK;
- name += STRING_SIZE("refs/tags/");
tag = TRUE;
+ namelen -= STRING_SIZE("refs/tags/");
+ name += STRING_SIZE("refs/tags/");
} else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
- name += STRING_SIZE("refs/heads/");
+ namelen -= STRING_SIZE("refs/heads/");
+ name += STRING_SIZE("refs/heads/");
} else if (!strcmp(name, "HEAD")) {
return OK;
return ERR;
ref = &refs[refs_size++];
- ref->name = strdup(name);
+ ref->name = malloc(namelen + 1);
if (!ref->name)
return ERR;
+ strncpy(ref->name, name, namelen);
+ ref->name[namelen] = 0;
ref->tag = tag;
string_copy(ref->id, id);