*/
#ifndef VERSION
-#define VERSION "tig-0.4.git"
+#define VERSION "tig-0.5.git"
#endif
#ifndef DEBUG
#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. */
#define SCALE_SPLIT_VIEW(height) ((height) * 2 / 3)
#define TIG_LS_REMOTE \
- "git ls-remote . 2>/dev/null"
+ "git ls-remote $(git rev-parse --git-dir) 2>/dev/null"
#define TIG_DIFF_CMD \
"git show --root --patch-with-stat --find-copies-harder -B -C %s 2>/dev/null"
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? */
};
*/
static inline void
-string_ncopy(char *dst, const char *src, size_t dstlen)
+string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
{
- strncpy(dst, src, dstlen - 1);
- dst[dstlen - 1] = 0;
+ if (srclen > dstlen - 1)
+ srclen = dstlen - 1;
+ strncpy(dst, src, srclen);
+ dst[srclen] = 0;
}
-/* Shorthand for safely copying into a fixed buffer. */
+/* Shorthands for safely copying into a fixed buffer. */
+
#define string_copy(dst, src) \
- string_ncopy(dst, src, sizeof(dst))
+ string_ncopy_do(dst, sizeof(dst), src, sizeof(dst))
+
+#define string_ncopy(dst, src, srclen) \
+ string_ncopy_do(dst, sizeof(dst), src, srclen)
static char *
chomp_string(char *name)
for (i = 1; i < argc; i++) {
char *opt = argv[i];
+ if (!strcmp(opt, "log") ||
+ !strcmp(opt, "diff") ||
+ !strcmp(opt, "show")) {
+ opt_request = opt[0] == 'l'
+ ? REQ_VIEW_LOG : REQ_VIEW_DIFF;
+ break;
+ }
+
+ if (opt[0] && opt[0] != '-')
+ break;
+
if (!strcmp(opt, "-l")) {
opt_request = REQ_VIEW_LOG;
continue;
break;
}
- if (!strcmp(opt, "log") ||
- !strcmp(opt, "diff") ||
- !strcmp(opt, "show")) {
- opt_request = opt[0] == 'l'
- ? REQ_VIEW_LOG : REQ_VIEW_DIFF;
- break;
- }
-
- if (opt[0] && opt[0] != '-')
- break;
-
die("unknown option '%s'\n\n%s", opt, usage);
}
if (opt_request == REQ_VIEW_MAIN)
/* XXX: This is vulnerable to the user overriding
* options required for the main view parser. */
- string_copy(opt_cmd, "git log --stat --pretty=raw");
+ string_copy(opt_cmd, "git log --pretty=raw");
else
string_copy(opt_cmd, "git");
buf_size = strlen(opt_cmd);
die("command too long");
opt_cmd[buf_size] = 0;
-
}
if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
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 */
};
{ 'd', REQ_VIEW_DIFF },
{ 'l', REQ_VIEW_LOG },
{ 't', REQ_VIEW_TREE },
- { 'b', REQ_VIEW_BLOB },
+ { 'f', REQ_VIEW_BLOB },
{ 'p', REQ_VIEW_PAGER },
{ 'h', REQ_VIEW_HELP },
{ 'g', REQ_TOGGLE_REV_GRAPH },
{ ':', REQ_PROMPT },
- /* wgetch() with nodelay() enabled returns ERR when there's no input. */
- { ERR, REQ_NONE },
-
/* Using the ncurses SIGWINCH handler. */
{ KEY_RESIZE, REQ_SCREEN_RESIZE },
};
static struct view *display[2];
static unsigned int current_view;
+/* Reading from the prompt? */
+static bool input_mode = FALSE;
+
#define foreach_displayed_view(view, i) \
for (i = 0; i < ARRAY_SIZE(display) && (view = display[i]); i++)
/* 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
}
redrawwin(view->win);
- wrefresh(view->win);
+ if (input_mode)
+ wnoutrefresh(view->win);
+ else
+ wrefresh(view->win);
}
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));
-
- werase(view->title);
- wmove(view->title, 0, 0);
+ char buf[SIZEOF_STR];
+ char state[SIZEOF_STR];
+ size_t bufpos = 0, statelen = 0;
- if (*view->ref)
- wprintw(view->title, "[%s] %s", view->name, view->ref);
- else
- wprintw(view->title, "[%s]", view->name);
+ assert(view_is_displayed(view));
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(state, &statelen, "- %s %d of %d (%d%%)",
+ view->ops->type,
+ view->lineno + 1,
+ view->lines,
+ lines);
+
+ if (view->pipe) {
+ time_t secs = time(NULL) - view->start_time;
+
+ /* Three git seconds are a long time ... */
+ if (secs > 2)
+ string_format_from(state, &statelen, " %lds", secs);
+ }
}
- if (view->pipe) {
- time_t secs = time(NULL) - view->start_time;
+ string_format_from(buf, &bufpos, "[%s]", view->name);
+ if (*view->ref && bufpos < view->width) {
+ size_t refsize = strlen(view->ref);
+ size_t minsize = bufpos + 1 + /* abbrev= */ 7 + 1 + statelen;
- /* Three git seconds are a long time ... */
- if (secs > 2)
- wprintw(view->title, " %lds", secs);
+ if (minsize < view->width)
+ refsize = view->width - minsize + 7;
+ string_format_from(buf, &bufpos, " %.*s", refsize, view->ref);
}
+ if (statelen && bufpos < view->width) {
+ string_format_from(buf, &bufpos, " %s", state);
+ }
+
+ if (view == display[current_view])
+ wbkgdset(view->title, get_line_attr(LINE_TITLE_FOCUS));
+ else
+ wbkgdset(view->title, get_line_attr(LINE_TITLE_BLUR));
+
+ mvwaddnstr(view->title, 0, 0, buf, bufpos);
+ wclrtoeol(view->title);
wmove(view->title, 0, view->width - 1);
- wrefresh(view->title);
+
+ if (input_mode)
+ wnoutrefresh(view->title);
+ else
+ wrefresh(view->title);
}
static void
}
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) {
- strncpy(view->ref, text + STRING_SIZE("100644 blob "), 41);
- view->ref[40] = 0;
- string_copy(ref_blob, view->ref);
- }
-
+ if (selected) {
type = LINE_CURSOR;
wchgat(view->win, -1, 0, type, NULL);
}
char *ref = NULL;
FILE *pipe;
- if (!string_format(refbuf, "git describe %s", commit_id))
+ if (!string_format(refbuf, "git describe %s 2>/dev/null", commit_id))
return TRUE;
pipe = popen(refbuf, "r");
if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
try_add_describe_ref:
+ /* Add <tag>-g<commit_id> "fake" reference. */
if (!add_describe_ref(buf, &bufpos, commit_id, sep))
return;
}
+ if (bufpos == 0)
+ return;
+
if (!realloc_lines(view, view->line_size + 1))
return;
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;
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;
}
/* Trees and subtrees share the same ID, so they are not not
* unique like blobs. */
- flags |= OPEN_RELOAD;
+ flags = OPEN_RELOAD;
request = REQ_VIEW_TREE;
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;
+ flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
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 title[75]; /* First line of the commit message. */
+ char id[SIZEOF_REV]; /* SHA1 ID. */
+ char title[128]; /* 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. */
};
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);
case LINE_AUTHOR:
{
+ /* Parse author lines where the name may be empty:
+ * author <email@address.tld> 1138474660 +0100
+ */
char *ident = line + STRING_SIZE("author ");
- char *end = strchr(ident, '<');
+ char *nameend = strchr(ident, '<');
+ char *emailend = strchr(ident, '>');
- if (!commit)
+ if (!commit || !nameend || !emailend)
break;
- if (end) {
- char *email = end + 1;
-
- for (; end > ident && isspace(end[-1]); end--) ;
-
- if (end == ident && *email) {
- ident = email;
- end = strchr(ident, '>');
- for (; end > ident && isspace(end[-1]); end--) ;
- }
- *end = 0;
+ *nameend = *emailend = 0;
+ ident = chomp_string(ident);
+ if (!*ident) {
+ ident = chomp_string(nameend + 1);
+ if (!*ident)
+ ident = "Unknown";
}
- /* End is NULL or ident meaning there's no author. */
- if (end <= ident)
- ident = "Unknown";
-
string_copy(commit->author, ident);
/* Parse epoch and timezone */
- if (end) {
- char *secs = strchr(end + 1, '>');
- char *zone;
- time_t time;
-
- if (!secs || secs[1] != ' ')
- break;
+ if (emailend[1] == ' ') {
+ char *secs = emailend + 2;
+ char *zone = strchr(secs, ' ');
+ time_t time = (time_t) atol(secs);
- secs += 2;
- time = (time_t) atol(secs);
- zone = strchr(secs, ' ');
if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
long tz;
time -= tz;
}
+
gmtime_r(&time, &commit->time);
}
break;
/* Require titles to start with a non-space character at the
* offset used by git log. */
- /* FIXME: More gracefull handling of titles; append "..." to
- * shortened titles, etc. */
- if (strncmp(line, " ", 4) ||
- isspace(line[4]))
+ if (strncmp(line, " ", 4))
break;
+ line += 4;
+ /* Well, if the title starts with a whitespace character,
+ * try to be forgiving. Otherwise we end up with no title. */
+ while (isspace(*line))
+ line++;
+ if (*line == '\0')
+ break;
+ /* FIXME: More graceful handling of titles; append "..." to
+ * shortened titles, etc. */
- string_copy(commit->title, line + 4);
+ string_copy(commit->title, line);
}
return TRUE;
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,
};
/* The status window is used for polling keystrokes. */
static WINDOW *status_win;
+static bool status_empty = TRUE;
+
/* Update status and title window. */
static void
report(const char *msg, ...)
{
- static bool empty = TRUE;
struct view *view = display[current_view];
- if (!empty || *msg) {
+ if (input_mode)
+ return;
+
+ if (!status_empty || *msg) {
va_list args;
va_start(args, msg);
- werase(status_win);
wmove(status_win, 0, 0);
if (*msg) {
vwprintw(status_win, msg, args);
- empty = FALSE;
+ status_empty = FALSE;
} else {
- empty = TRUE;
+ status_empty = TRUE;
}
+ wclrtoeol(status_win);
wrefresh(status_win);
va_end(args);
}
update_view_title(view);
- update_display_cursor();
+ update_display_cursor(view);
}
/* Controls when nodelay should be in effect when polling user input. */
struct view *view;
int i, key;
+ input_mode = TRUE;
+
foreach_view (view, i)
update_view(view);
- report("%s%.*s", prompt, pos, buf);
+ input_mode = FALSE;
+
+ mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
+ wclrtoeol(status_win);
+
/* Refresh, accept single keystroke of input */
key = wgetch(status_win);
switch (key) {
}
}
- if (status == CANCEL) {
- /* Clear the status window */
- report("");
+ /* Clear the status window */
+ status_empty = FALSE;
+ report("");
+
+ if (status == CANCEL)
return NULL;
- }
buf[pos++] = 0;
/* Refresh, accept single keystroke of input */
key = wgetch(status_win);
+ /* wgetch() with nodelay() enabled returns ERR when there's no
+ * input. */
+ if (key == ERR) {
+ request = REQ_NONE;
+ continue;
+ }
+
request = get_keybinding(display[current_view]->keymap, key);
/* Some low-level request handling. This keeps access to