#define SIZEOF_STR 1024 /* Default string size. */
#define SIZEOF_REF 256 /* Size of symbolic or SHA1 ID. */
+#define SIZEOF_REV 41 /* Holds a SHA-1 and an ending NUL */
#define SIZEOF_REVGRAPH 19 /* Size of revision ancestry graphics. */
/* This color name can be used to refer to the default term colors. */
struct ref {
char *name; /* Ref name; tag or head names are shortened. */
- char id[41]; /* Commit SHA1 ID */
+ char id[SIZEOF_REV]; /* Commit SHA1 ID */
unsigned int tag:1; /* Is it a tag? */
unsigned int next:1; /* For ref lists: are there more refs? */
};
LINE(AUTHOR, "author ", COLOR_CYAN, COLOR_DEFAULT, 0), \
LINE(COMMITTER, "committer ", COLOR_MAGENTA, COLOR_DEFAULT, 0), \
LINE(SIGNOFF, " Signed-off-by", COLOR_YELLOW, COLOR_DEFAULT, 0), \
+LINE(ACKED, " Acked-by", COLOR_YELLOW, COLOR_DEFAULT, 0), \
LINE(DEFAULT, "", COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL), \
LINE(CURSOR, "", COLOR_WHITE, COLOR_GREEN, A_BOLD), \
LINE(STATUS, "", COLOR_GREEN, COLOR_DEFAULT, 0), \
struct line {
enum line_type type;
+
+ /* State flags */
+ unsigned int selected:1;
+
void *data; /* User data */
};
/* Searching */
char grep[SIZEOF_STR]; /* Search string */
- regex_t regex; /* Pre-compiled regex */
+ regex_t *regex; /* Pre-compiled regex */
/* If non-NULL, points to the view that opened this view. If this view
* is closed tig will switch back to the parent view. */
/* What type of content being displayed. Used in the title bar. */
const char *type;
/* Draw one line; @lineno must be < view->height. */
- bool (*draw)(struct view *view, struct line *line, unsigned int lineno);
+ bool (*draw)(struct view *view, struct line *line, unsigned int lineno, bool selected);
/* Read one line; updates view->line. */
bool (*read)(struct view *view, char *data);
/* Depending on view, change display based on current line. */
bool (*enter)(struct view *view, struct line *line);
/* Search for regex in a line. */
bool (*grep)(struct view *view, struct line *line);
+ /* Select line */
+ void (*select)(struct view *view, struct line *line);
};
static struct view_ops pager_ops;
static bool
draw_view_line(struct view *view, unsigned int lineno)
{
+ struct line *line;
+ bool selected = (view->offset + lineno == view->lineno);
+ bool draw_ok;
+
assert(view_is_displayed(view));
if (view->offset + lineno >= view->lines)
return FALSE;
- return view->ops->draw(view, &view->line[view->offset + lineno], lineno);
+ line = &view->line[view->offset + lineno];
+
+ if (selected) {
+ line->selected = TRUE;
+ view->ops->select(view, line);
+ } else if (line->selected) {
+ line->selected = FALSE;
+ wmove(view->win, lineno, 0);
+ wclrtoeol(view->win);
+ }
+
+ scrollok(view->win, FALSE);
+ draw_ok = view->ops->draw(view, line, lineno, selected);
+ scrollok(view->win, TRUE);
+
+ return draw_ok;
}
static void
static void
update_view_title(struct view *view)
{
- assert(view_is_displayed(view));
-
- if (view == display[current_view])
- wbkgdset(view->title, get_line_attr(LINE_TITLE_FOCUS));
- else
- wbkgdset(view->title, get_line_attr(LINE_TITLE_BLUR));
+ char buf[SIZEOF_STR];
+ size_t bufpos = 0;
- werase(view->title);
- wmove(view->title, 0, 0);
+ assert(view_is_displayed(view));
+ string_format_from(buf, &bufpos, "[%s]", view->name);
if (*view->ref)
- wprintw(view->title, "[%s] %s", view->name, view->ref);
- else
- wprintw(view->title, "[%s]", view->name);
+ string_format_from(buf, &bufpos, " %s", view->ref);
if (view->lines || view->pipe) {
unsigned int view_lines = view->offset + view->height;
? MIN(view_lines, view->lines) * 100 / view->lines
: 0;
- wprintw(view->title, " - %s %d of %d (%d%%)",
- view->ops->type,
- view->lineno + 1,
- view->lines,
- lines);
+ string_format_from(buf, &bufpos, " - %s %d of %d (%d%%)",
+ view->ops->type,
+ view->lineno + 1,
+ view->lines,
+ lines);
}
if (view->pipe) {
/* Three git seconds are a long time ... */
if (secs > 2)
- wprintw(view->title, " %lds", secs);
+ string_format_from(buf, &bufpos, " %lds", secs);
}
+ if (view == display[current_view])
+ wbkgdset(view->title, get_line_attr(LINE_TITLE_FOCUS));
+ else
+ wbkgdset(view->title, get_line_attr(LINE_TITLE_BLUR));
+
+ werase(view->title);
+ mvwaddnstr(view->title, 0, 0, buf, bufpos);
wmove(view->title, 0, view->width - 1);
wrefresh(view->title);
}
}
static void
-update_display_cursor(void)
+update_display_cursor(struct view *view)
{
- struct view *view = display[current_view];
-
/* Move the cursor to the right-most column of the cursor line.
*
* XXX: This could turn out to be a bit expensive, but it ensures that
/* Scrolling backend */
static void
-do_scroll_view(struct view *view, int lines, bool redraw)
+do_scroll_view(struct view *view, int lines)
{
- assert(view_is_displayed(view));
+ bool redraw_current_line = FALSE;
/* The rendering expects the new offset. */
view->offset += lines;
assert(0 <= view->offset && view->offset < view->lines);
assert(lines);
+ /* Move current line into the view. */
+ if (view->lineno < view->offset) {
+ view->lineno = view->offset;
+ redraw_current_line = TRUE;
+ } else if (view->lineno >= view->offset + view->height) {
+ view->lineno = view->offset + view->height - 1;
+ redraw_current_line = TRUE;
+ }
+
+ assert(view->offset <= view->lineno && view->lineno < view->lines);
+
/* Redraw the whole screen if scrolling is pointless. */
if (view->height < ABS(lines)) {
redraw_view(view);
if (!draw_view_line(view, line))
break;
}
- }
- /* Move current line into the view. */
- if (view->lineno < view->offset) {
- view->lineno = view->offset;
- draw_view_line(view, 0);
-
- } else if (view->lineno >= view->offset + view->height) {
- if (view->lineno == view->offset + view->height) {
- /* Clear the hidden line so it doesn't show if the view
- * is scrolled up. */
- wmove(view->win, view->height, 0);
- wclrtoeol(view->win);
- }
- view->lineno = view->offset + view->height - 1;
- draw_view_line(view, view->lineno - view->offset);
+ if (redraw_current_line)
+ draw_view_line(view, view->lineno - view->offset);
}
- assert(view->offset <= view->lineno && view->lineno < view->lines);
-
- if (!redraw)
- return;
-
redrawwin(view->win);
wrefresh(view->win);
report("");
{
int lines = 1;
+ assert(view_is_displayed(view));
+
switch (request) {
case REQ_SCROLL_PAGE_DOWN:
lines = view->height;
die("request %d not handled in switch", request);
}
- do_scroll_view(view, lines, TRUE);
+ do_scroll_view(view, lines);
}
/* Cursor moving */
static void
-move_view(struct view *view, enum request request, bool redraw)
+move_view(struct view *view, enum request request)
{
+ int scroll_steps = 0;
int steps;
switch (request) {
view->lineno += steps;
assert(0 <= view->lineno && view->lineno < view->lines);
- /* Repaint the old "current" line if we be scrolling */
- if (ABS(steps) < view->height) {
- int prev_lineno = view->lineno - steps - view->offset;
-
- wmove(view->win, prev_lineno, 0);
- wclrtoeol(view->win);
- draw_view_line(view, prev_lineno);
- }
-
/* Check whether the view needs to be scrolled */
if (view->lineno < view->offset ||
view->lineno >= view->offset + view->height) {
+ scroll_steps = steps;
if (steps < 0 && -steps > view->offset) {
- steps = -view->offset;
+ scroll_steps = -view->offset;
} else if (steps > 0) {
if (view->lineno == view->lines - 1 &&
view->lines > view->height) {
- steps = view->lines - view->offset - 1;
- if (steps >= view->height)
- steps -= view->height - 1;
+ scroll_steps = view->lines - view->offset - 1;
+ if (scroll_steps >= view->height)
+ scroll_steps -= view->height - 1;
}
}
+ }
- do_scroll_view(view, steps, redraw);
+ if (!view_is_displayed(view)) {
+ view->offset += steps;
+ view->ops->select(view, &view->line[view->lineno]);
return;
}
- /* Draw the current line */
- draw_view_line(view, view->lineno - view->offset);
+ /* Repaint the old "current" line if we be scrolling */
+ if (ABS(steps) < view->height)
+ draw_view_line(view, view->lineno - steps - view->offset);
- if (!redraw)
+ if (scroll_steps) {
+ do_scroll_view(view, scroll_steps);
return;
+ }
+
+ /* Draw the current line */
+ draw_view_line(view, view->lineno - view->offset);
redrawwin(view->win);
wrefresh(view->win);
* Searching
*/
-static void search_view(struct view *view, enum request request, const char *search);
+static void search_view(struct view *view, enum request request);
static bool
find_next_line(struct view *view, unsigned long lineno, struct line *line)
unsigned long old_lineno = view->lineno - view->offset;
view->lineno = lineno;
-
- wmove(view->win, old_lineno, 0);
- wclrtoeol(view->win);
draw_view_line(view, old_lineno);
draw_view_line(view, view->lineno - view->offset);
if (!*opt_search)
report("No previous search");
else
- search_view(view, request, opt_search);
+ search_view(view, request);
return;
}
}
static void
-search_view(struct view *view, enum request request, const char *search)
+search_view(struct view *view, enum request request)
{
int regex_err;
- if (*view->grep) {
- regfree(&view->regex);
+ if (view->regex) {
+ regfree(view->regex);
*view->grep = 0;
+ } else {
+ view->regex = calloc(1, sizeof(*view->regex));
+ if (!view->regex)
+ return;
}
- regex_err = regcomp(&view->regex, search, REG_EXTENDED);
+ regex_err = regcomp(view->regex, opt_search, REG_EXTENDED);
if (regex_err != 0) {
char buf[SIZEOF_STR] = "unknown error";
- regerror(regex_err, &view->regex, buf, sizeof(buf));
+ regerror(regex_err, view->regex, buf, sizeof(buf));
report("Search failed: %s", buf);
return;
}
- string_copy(view->grep, search);
+ string_copy(view->grep, opt_search);
find_next(view, request);
}
/* Scroll the view that was split if the current line is
* outside the new limited view. */
- do_scroll_view(prev, lines, TRUE);
+ do_scroll_view(prev, lines);
}
if (prev && view != prev) {
case REQ_MOVE_PAGE_DOWN:
case REQ_MOVE_FIRST_LINE:
case REQ_MOVE_LAST_LINE:
- move_view(view, request, TRUE);
+ move_view(view, request);
break;
case REQ_SCROLL_LINE_DOWN:
view->parent == VIEW(REQ_VIEW_MAIN)) ||
(view == VIEW(REQ_VIEW_BLOB) &&
view->parent == VIEW(REQ_VIEW_TREE))) {
- bool redraw = display[1] == view;
-
view = view->parent;
- move_view(view, request, redraw);
- if (redraw)
+ move_view(view, request);
+ if (view_is_displayed(view))
update_view_title(view);
} else {
- move_view(view, request, TRUE);
+ move_view(view, request);
break;
}
/* Fall-through */
case REQ_SEARCH:
case REQ_SEARCH_BACK:
- search_view(view, request, opt_search);
+ search_view(view, request);
break;
case REQ_FIND_NEXT:
*/
static bool
-pager_draw(struct view *view, struct line *line, unsigned int lineno)
+pager_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
{
char *text = line->data;
enum line_type type = line->type;
wmove(view->win, lineno, 0);
- if (view->offset + lineno == view->lineno) {
- if (type == LINE_COMMIT) {
- string_copy(view->ref, text + 7);
- string_copy(ref_commit, view->ref);
-
- } else if (type == LINE_TREE_DIR || type == LINE_TREE_FILE) {
- string_ncopy(view->ref, text + STRING_SIZE("100644 blob "), 40);
- string_copy(ref_blob, view->ref);
- }
-
+ if (selected) {
type = LINE_CURSOR;
wchgat(view->win, -1, 0, type, NULL);
}
if (!*text)
return FALSE;
- if (regexec(&view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
+ if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
return FALSE;
return TRUE;
}
+static void
+pager_select(struct view *view, struct line *line)
+{
+ if (line->type == LINE_COMMIT) {
+ char *text = line->data;
+
+ string_copy(view->ref, text + STRING_SIZE("commit "));
+ string_copy(ref_commit, view->ref);
+ }
+}
+
static struct view_ops pager_ops = {
"line",
pager_draw,
pager_read,
pager_enter,
pager_grep,
+ pager_select,
};
* Tree backend
*/
-/* Parse output from git ls-tree:
+/* Parse output from git-ls-tree(1):
*
* 100644 blob fb0e31ea6cc679b7379631188190e975f5789c26 Makefile
* 100644 blob 5304ca4260aaddaee6498f9630e7d471b8591ea6 README
static bool
tree_enter(struct view *view, struct line *line)
{
- enum open_flags flags = OPEN_DEFAULT;
- char *data = line->data;
+ enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
enum request request;
switch (line->type) {
} else {
size_t pathlen = strlen(opt_path);
size_t origlen = pathlen;
+ char *data = line->data;
char *basename = data + SIZEOF_TREE_ATTR;
- if (string_format_from(opt_path, &pathlen, "%s/", basename)) {
+ if (!string_format_from(opt_path, &pathlen, "%s/", basename)) {
opt_path[origlen] = 0;
return TRUE;
}
break;
case LINE_TREE_FILE:
- /* This causes the blob view to become split, and not having it
- * in the tree dir case will make the blob view automatically
- * disappear when moving to a different directory. */
- flags |= OPEN_SPLIT;
request = REQ_VIEW_BLOB;
break;
open_view(view, request, flags);
- if (!VIEW(request)->pipe)
- return TRUE;
+ return TRUE;
+}
+
+static void
+tree_select(struct view *view, struct line *line)
+{
+ char *text = line->data;
- /* For tree views insert the path to the parent as the first line. */
- if (request == REQ_VIEW_BLOB) {
- /* Mirror what is showed in the title bar. */
- string_ncopy(ref_blob, data + STRING_SIZE("100644 blob "), 40);
+ text += STRING_SIZE("100644 blob ");
+
+ if (line->type == LINE_TREE_FILE) {
+ string_ncopy(ref_blob, text, 40);
+ /* Also update the blob view's ref, since all there must always
+ * be in sync. */
string_copy(VIEW(REQ_VIEW_BLOB)->ref, ref_blob);
- return TRUE;
+
+ } else if (line->type != LINE_TREE_DIR) {
+ return;
}
- return TRUE;
+ string_ncopy(view->ref, text, 40);
}
static struct view_ops tree_ops = {
tree_read,
tree_enter,
pager_grep,
+ tree_select,
};
static bool
blob_read,
pager_enter,
pager_grep,
+ pager_select,
};
*/
struct commit {
- char id[41]; /* SHA1 ID. */
+ char id[SIZEOF_REV]; /* 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. */
};
static bool
-main_draw(struct view *view, struct line *line, unsigned int lineno)
+main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
{
char buf[DATE_COLS + 1];
struct commit *commit = line->data;
wmove(view->win, lineno, col);
- if (view->offset + lineno == view->lineno) {
- string_copy(view->ref, commit->id);
- string_copy(ref_commit, view->ref);
+ if (selected) {
type = LINE_CURSOR;
wattrset(view->win, get_line_attr(type));
wchgat(view->win, -1, 0, type, NULL);
return FALSE;
}
- if (regexec(&view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
+ if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
return TRUE;
}
return FALSE;
}
+static void
+main_select(struct view *view, struct line *line)
+{
+ struct commit *commit = line->data;
+
+ string_copy(view->ref, commit->id);
+ string_copy(ref_commit, view->ref);
+}
+
static struct view_ops main_ops = {
"commit",
main_draw,
main_read,
main_enter,
main_grep,
+ main_select,
};
}
update_view_title(view);
- update_display_cursor();
+ update_display_cursor(view);
}
/* Controls when nodelay should be in effect when polling user input. */