index 8c65a4753bb9d2809a7bc93b23d43eb562b8884b..4fac53286e2beeecda9b688d90fda3bac10f3eec 100644 (file)
--- a/tig.c
+++ b/tig.c
static void report(const char *msg, ...);
static void set_nonblocking_input(bool loading);
static size_t utf8_length(const char *string, int *width, size_t max_width, int *trimmed, bool reserve);
-static bool prompt_yesno(const char *prompt);
static int load_refs(void);
#define ABS(x) ((x) >= 0 ? (x) : -(x))
return ERR;
}
+enum input_status {
+ INPUT_OK,
+ INPUT_SKIP,
+ INPUT_STOP,
+ INPUT_CANCEL
+};
+
+typedef enum input_status (*input_handler)(void *data, char *buf, int c);
+
+static char *prompt_input(const char *prompt, input_handler handler, void *data);
+static bool prompt_yesno(const char *prompt);
/*
* String helpers
@@ -666,6 +676,7 @@ static int read_properties(struct io *io, const char *separators, int (*read)(ch
REQ_(ENTER, "Enter current line and scroll"), \
REQ_(NEXT, "Move to next"), \
REQ_(PREVIOUS, "Move to previous"), \
+ REQ_(PARENT, "Move to parent"), \
REQ_(VIEW_NEXT, "Move focus to next view"), \
REQ_(REFRESH, "Reload and refresh"), \
REQ_(MAXIMIZE, "Maximize the current view"), \
@@ -677,7 +688,6 @@ static int read_properties(struct io *io, const char *separators, int (*read)(ch
REQ_(STATUS_REVERT, "Revert file changes"), \
REQ_(STATUS_MERGE, "Merge file using external tool"), \
REQ_(STAGE_NEXT, "Find next chunk to stage"), \
- REQ_(TREE_PARENT, "Switch to parent directory in tree view"), \
\
REQ_GROUP("Cursor navigation") \
REQ_(MOVE_UP, "Move cursor one line up"), \
{ '!', REQ_STATUS_REVERT },
{ 'M', REQ_STATUS_MERGE },
{ '@', REQ_STAGE_NEXT },
- { ',', REQ_TREE_PARENT },
+ { ',', REQ_PARENT },
{ 'e', REQ_EDIT },
};
request = get_request(argv[2]);
if (request == REQ_NONE) {
- const char *obsolete[] = { "cherry-pick", "screen-resize" };
+ struct {
+ const char *name;
+ enum request request;
+ } obsolete[] = {
+ { "cherry-pick", REQ_NONE },
+ { "screen-resize", REQ_NONE },
+ { "tree-parent", REQ_PARENT },
+ };
size_t namelen = strlen(argv[2]);
int i;
for (i = 0; i < ARRAY_SIZE(obsolete); i++) {
- if (namelen == strlen(obsolete[i]) &&
- !string_enum_compare(obsolete[i], argv[2], namelen)) {
- config_msg = "Obsolete request name";
- return ERR;
- }
+ if (namelen != strlen(obsolete[i].name) ||
+ string_enum_compare(obsolete[i].name, argv[2], namelen))
+ continue;
+ if (obsolete[i].request != REQ_NONE)
+ add_keybinding(keymap, obsolete[i].request, key);
+ config_msg = "Obsolete request name";
+ return ERR;
}
}
if (request == REQ_NONE && *argv[2]++ == '!')
if (!dirty)
return;
- redrawwin(view->win);
if (input_mode)
wnoutrefresh(view->win);
else
break;
}
- redrawwin(view->win);
if (input_mode)
wnoutrefresh(view->win);
else
draw_view_line(view, view->lineno - view->offset);
}
- redrawwin(view->win);
wrefresh(view->win);
report("");
}
/* Draw the current line */
draw_view_line(view, view->lineno - view->offset);
- redrawwin(view->win);
wrefresh(view->win);
report("");
}
if (view_is_displayed(view)) {
draw_view_line(view, old_lineno);
draw_view_line(view, view->lineno - view->offset);
- redrawwin(view->win);
wrefresh(view->win);
} else {
view->ops->select(view, &view->line[view->lineno]);
@@ -3318,6 +3332,60 @@ parse_author_line(char *ident, char *author, size_t authorsize, struct tm *tm)
}
}
+static enum input_status
+select_commit_parent_handler(void *data, char *buf, int c)
+{
+ size_t parents = *(size_t *) data;
+ int parent = 0;
+
+ if (!isdigit(c))
+ return INPUT_SKIP;
+
+ if (*buf)
+ parent = atoi(buf) * 10;
+ parent += c - '0';
+
+ if (parent > parents)
+ return INPUT_SKIP;
+ return INPUT_OK;
+}
+
+static bool
+select_commit_parent(const char *id, char rev[SIZEOF_REV])
+{
+ char buf[SIZEOF_STR * 4];
+ const char *revlist_argv[] = {
+ "git", "rev-list", "-1", "--parents", id, NULL
+ };
+ int parents;
+
+ if (!run_io_buf(revlist_argv, buf, sizeof(buf)) ||
+ !*chomp_string(buf) ||
+ (parents = (strlen(buf) / 40) - 1) < 0) {
+ report("Failed to get parent information");
+ return FALSE;
+
+ } else if (parents == 0) {
+ report("The selected commit has no parents");
+ return FALSE;
+ }
+
+ if (parents > 1) {
+ char prompt[SIZEOF_STR];
+ char *result;
+
+ if (!string_format(prompt, "Which parent? [1..%d] ", parents))
+ return FALSE;
+ result = prompt_input(prompt, select_commit_parent_handler, &parents);
+ if (!result)
+ return FALSE;
+ parents = atoi(result);
+ }
+
+ string_copy_rev(rev, &buf[41 * parents]);
+ return TRUE;
+}
+
/*
* Pager backend
*/
static bool
help_open(struct view *view)
{
- int lines = ARRAY_SIZE(req_info) + 2;
+ char buf[SIZEOF_STR];
+ size_t bufpos;
int i;
if (view->lines > 0)
return TRUE;
- for (i = 0; i < ARRAY_SIZE(req_info); i++)
- if (!req_info[i].request)
- lines++;
-
- lines += run_requests + 1;
-
- view->line = calloc(lines, sizeof(*view->line));
- if (!view->line)
- return FALSE;
-
add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
for (i = 0; i < ARRAY_SIZE(req_info); i++) {
if (!*key)
key = "(no key defined)";
- add_line_format(view, LINE_DEFAULT, " %-25s %s",
- key, req_info[i].help);
+ for (bufpos = 0; bufpos <= req_info[i].namelen; bufpos++) {
+ buf[bufpos] = tolower(req_info[i].name[bufpos]);
+ if (buf[bufpos] == '_')
+ buf[bufpos] = '-';
+ }
+
+ add_line_format(view, LINE_DEFAULT, " %-25s %-20s %s",
+ key, buf, req_info[i].help);
}
if (run_requests) {
for (i = 0; i < run_requests; i++) {
struct run_request *req = get_run_request(REQ_NONE + i + 1);
const char *key;
- char cmd[SIZEOF_STR];
- size_t bufpos;
int argc;
if (!req)
key = "(no key defined)";
for (bufpos = 0, argc = 0; req->argv[argc]; argc++)
- if (!string_format_from(cmd, &bufpos, "%s%s",
+ if (!string_format_from(buf, &bufpos, "%s%s",
argc ? " " : "", req->argv[argc]))
return REQ_NONE;
add_line_format(view, LINE_DEFAULT, " %-10s %-14s `%s`",
- keymap_table[req->keymap].name, key, cmd);
+ keymap_table[req->keymap].name, key, buf);
}
return TRUE;
}
return REQ_NONE;
- case REQ_TREE_PARENT:
+ case REQ_PARENT:
if (!*opt_path) {
/* quit view if at top of tree */
return REQ_VIEW_CLOSE;
}
break;
+ case REQ_PARENT:
+ if (check_blame_commit(blame) &&
+ select_commit_parent(blame->commit->id, opt_ref))
+ open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH);
+ break;
+
case REQ_ENTER:
if (!blame->commit) {
report("No commit loaded yet");
}
}
-static bool
-prompt_yesno(const char *prompt)
-{
- enum { WAIT, STOP, CANCEL } status = WAIT;
- bool answer = FALSE;
-
- while (status == WAIT) {
- int key;
-
- mvwprintw(status_win, 0, 0, "%s [Yy]/[Nn]", prompt);
- wclrtoeol(status_win);
-
- key = get_input(TRUE);
- switch (key) {
- case 'y':
- case 'Y':
- answer = TRUE;
- status = STOP;
- break;
-
- case KEY_ESC:
- case KEY_RETURN:
- case KEY_ENTER:
- case KEY_BACKSPACE:
- case 'n':
- case 'N':
- case '\n':
- default:
- answer = FALSE;
- status = CANCEL;
- }
- }
-
- /* Clear the status window */
- status_empty = FALSE;
- report("");
-
- return answer;
-}
-
static char *
-read_prompt(const char *prompt)
+prompt_input(const char *prompt, input_handler handler, void *data)
{
- enum { READING, STOP, CANCEL } status = READING;
+ enum input_status status = INPUT_OK;
static char buf[SIZEOF_STR];
- int pos = 0;
+ size_t pos = 0;
+
+ buf[pos] = 0;
- while (status == READING) {
+ while (status == INPUT_OK || status == INPUT_SKIP) {
int key;
mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
case KEY_RETURN:
case KEY_ENTER:
case '\n':
- status = pos ? STOP : CANCEL;
+ status = pos ? INPUT_STOP : INPUT_CANCEL;
break;
case KEY_BACKSPACE:
if (pos > 0)
- pos--;
+ buf[--pos] = 0;
else
- status = CANCEL;
+ status = INPUT_CANCEL;
break;
case KEY_ESC:
- status = CANCEL;
+ status = INPUT_CANCEL;
break;
default:
return NULL;
}
- if (isprint(key))
+ status = handler(data, buf, key);
+ if (status == INPUT_OK)
buf[pos++] = (char) key;
}
}
status_empty = FALSE;
report("");
- if (status == CANCEL)
+ if (status == INPUT_CANCEL)
return NULL;
buf[pos++] = 0;
return buf;
}
+static enum input_status
+prompt_yesno_handler(void *data, char *buf, int c)
+{
+ if (c == 'y' || c == 'Y')
+ return INPUT_STOP;
+ if (c == 'n' || c == 'N')
+ return INPUT_CANCEL;
+ return INPUT_SKIP;
+}
+
+static bool
+prompt_yesno(const char *prompt)
+{
+ char prompt2[SIZEOF_STR];
+
+ if (!string_format(prompt2, "%s [Yy/Nn]", prompt))
+ return FALSE;
+
+ return !!prompt_input(prompt2, prompt_yesno_handler, NULL);
+}
+
+static enum input_status
+read_prompt_handler(void *data, char *buf, int c)
+{
+ return isprint(c) ? INPUT_OK : INPUT_SKIP;
+}
+
+static char *
+read_prompt(const char *prompt)
+{
+ return prompt_input(prompt, read_prompt_handler, NULL);
+}
+
/*
* Repository properties
*/