Code

Fix whitespace
[tig.git] / tig.c
diff --git a/tig.c b/tig.c
index 87fc98656c6e95e1dbbecb31a031fcd0279ce093..a442024168dbf6d692e62437db4fab308f3173ac 100644 (file)
--- a/tig.c
+++ b/tig.c
@@ -36,6 +36,7 @@
 #include <sys/stat.h>
 #include <sys/select.h>
 #include <unistd.h>
+#include <sys/time.h>
 #include <time.h>
 #include <fcntl.h>
 
@@ -72,6 +73,7 @@ static size_t utf8_length(const char **string, size_t col, int *width, size_t ma
 
 #define ABS(x)         ((x) >= 0  ? (x) : -(x))
 #define MIN(x, y)      ((x) < (y) ? (x) :  (y))
+#define MAX(x, y)      ((x) > (y) ? (x) :  (y))
 
 #define ARRAY_SIZE(x)  (sizeof(x) / sizeof(x[0]))
 #define STRING_SIZE(x) (sizeof(x) - 1)
@@ -102,16 +104,11 @@ static size_t utf8_length(const char **string, size_t col, int *width, size_t ma
 /* The format and size of the date column in the main view. */
 #define DATE_FORMAT    "%Y-%m-%d %H:%M"
 #define DATE_COLS      STRING_SIZE("2006-04-29 14:21 ")
+#define DATE_SHORT_COLS        STRING_SIZE("2006-04-29 ")
 
-#define AUTHOR_COLS    20
 #define ID_COLS                8
 
-/* The default interval between line numbers. */
-#define NUMBER_INTERVAL        5
-
-#define TAB_SIZE       8
-
-#define        SCALE_SPLIT_VIEW(height)        ((height) * 2 / 3)
+#define MIN_VIEW_HEIGHT 4
 
 #define NULL_ID                "0000000000000000000000000000000000000000"
 
@@ -344,12 +341,75 @@ suffixcmp(const char *str, int slen, const char *suffix)
 }
 
 
-static const char *
-mkdate(const time_t *time)
+/*
+ * What value of "tz" was in effect back then at "time" in the
+ * local timezone?
+ */
+static int local_tzoffset(time_t time)
+{
+       time_t t, t_local;
+       struct tm tm;
+       int offset, eastwest; 
+
+       t = time;
+       localtime_r(&t, &tm);
+       t_local = mktime(&tm);
+
+       if (t_local < t) {
+               eastwest = -1;
+               offset = t - t_local;
+       } else {
+               eastwest = 1;
+               offset = t_local - t;
+       }
+       offset /= 60; /* in minutes */
+       offset = (offset % 60) + ((offset / 60) * 100);
+       return offset * eastwest;
+}
+
+enum date {
+       DATE_NONE = 0,
+       DATE_DEFAULT,
+       DATE_RELATIVE,
+       DATE_SHORT
+};
+
+static char *
+string_date(const time_t *time, enum date date)
 {
        static char buf[DATE_COLS + 1];
+       static const struct enum_map reldate[] = {
+               { "second", 1,                  60 * 2 },
+               { "minute", 60,                 60 * 60 * 2 },
+               { "hour",   60 * 60,            60 * 60 * 24 * 2 },
+               { "day",    60 * 60 * 24,       60 * 60 * 24 * 7 * 2 },
+               { "week",   60 * 60 * 24 * 7,   60 * 60 * 24 * 7 * 5 },
+               { "month",  60 * 60 * 24 * 30,  60 * 60 * 24 * 30 * 12 },
+       };
        struct tm tm;
 
+       if (date == DATE_RELATIVE) {
+               struct timeval now;
+               time_t date = *time + local_tzoffset(*time);
+               time_t seconds;
+               int i;
+
+               gettimeofday(&now, NULL);
+               seconds = now.tv_sec < date ? date - now.tv_sec : now.tv_sec - date;
+               for (i = 0; i < ARRAY_SIZE(reldate); i++) {
+                       if (seconds >= reldate[i].value)
+                               continue;
+
+                       seconds /= reldate[i].namelen;
+                       if (!string_format(buf, "%ld %s%s %s",
+                                          seconds, reldate[i].name,
+                                          seconds > 1 ? "s" : "",
+                                          now.tv_sec >= date ? "ago" : "ahead"))
+                               break;
+                       return buf;
+               }
+       }
+
        gmtime_r(time, &tm);
        return strftime(buf, sizeof(buf), DATE_FORMAT, &tm) ? buf : NULL;
 }
@@ -842,6 +902,7 @@ run_io_load(const char **argv, const char *separators,
        REQ_(OPTIONS,           "Open option menu"), \
        REQ_(TOGGLE_LINENO,     "Toggle line numbers"), \
        REQ_(TOGGLE_DATE,       "Toggle date display"), \
+       REQ_(TOGGLE_DATE_SHORT, "Toggle short (date-only) dates"), \
        REQ_(TOGGLE_AUTHOR,     "Toggle author display"), \
        REQ_(TOGGLE_REV_GRAPH,  "Toggle revision graph visualization"), \
        REQ_(TOGGLE_REFS,       "Toggle reference display (tags/branches)"), \
@@ -905,16 +966,17 @@ get_request(const char *name)
  */
 
 /* Option and state variables. */
-static bool opt_date                   = TRUE;
+static enum date opt_date              = DATE_DEFAULT;
 static bool opt_author                 = TRUE;
 static bool opt_line_number            = FALSE;
 static bool opt_line_graphics          = TRUE;
 static bool opt_rev_graph              = FALSE;
 static bool opt_show_refs              = TRUE;
-static int opt_num_interval            = NUMBER_INTERVAL;
+static int opt_num_interval            = 5;
 static double opt_hscroll              = 0.50;
-static int opt_tab_size                        = TAB_SIZE;
-static int opt_author_cols             = AUTHOR_COLS-1;
+static double opt_scale_split_view     = 2.0 / 3.0;
+static int opt_tab_size                        = 8;
+static int opt_author_cols             = 19;
 static char opt_path[SIZEOF_STR]       = "";
 static char opt_file[SIZEOF_STR]       = "";
 static char opt_ref[SIZEOF_REF]                = "";
@@ -935,6 +997,7 @@ static FILE *opt_tty                        = NULL;
 
 #define is_initial_commit()    (!*opt_head_rev)
 #define is_head_commit(rev)    (!strcmp((rev), "HEAD") || !strcmp(opt_head_rev, (rev)))
+#define mkdate(time)           string_date(time, opt_date)
 
 
 /*
@@ -996,6 +1059,8 @@ LINE(STAT_NONE,    "",                     COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
 LINE(STAT_STAGED,  "",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
 LINE(STAT_UNSTAGED,"",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
 LINE(STAT_UNTRACKED,"",                        COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
+LINE(HELP_KEYMAP,  "",                 COLOR_CYAN,     COLOR_DEFAULT,  0), \
+LINE(HELP_GROUP,   "",                 COLOR_BLUE,     COLOR_DEFAULT,  0), \
 LINE(BLAME_ID,     "",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0)
 
 enum line_type {
@@ -1087,6 +1152,7 @@ struct line {
        unsigned int selected:1;
        unsigned int dirty:1;
        unsigned int cleareol:1;
+       unsigned int other:16;
 
        void *data;             /* User data */
 };
@@ -1314,26 +1380,68 @@ get_key_name(int key_value)
        return seq ? seq : "(no key)";
 }
 
+static bool
+append_key(char *buf, size_t *pos, const struct keybinding *keybinding)
+{
+       const char *sep = *pos > 0 ? ", " : "";
+       const char *keyname = get_key_name(keybinding->alias);
+
+       return string_nformat(buf, BUFSIZ, pos, "%s%s", sep, keyname);
+}
+
+static bool
+append_keymap_request_keys(char *buf, size_t *pos, enum request request,
+                          enum keymap keymap, bool all)
+{
+       int i;
+
+       for (i = 0; i < keybindings[keymap].size; i++) {
+               if (keybindings[keymap].data[i].request == request) {
+                       if (!append_key(buf, pos, &keybindings[keymap].data[i]))
+                               return FALSE;
+                       if (!all)
+                               break;
+               }
+       }
+
+       return TRUE;
+}
+
+#define get_key(keymap, request) get_keys(keymap, request, FALSE)
+
 static const char *
-get_key(enum request request)
+get_keys(enum keymap keymap, enum request request, bool all)
 {
        static char buf[BUFSIZ];
        size_t pos = 0;
-       char *sep = "";
        int i;
 
        buf[pos] = 0;
 
-       for (i = 0; i < ARRAY_SIZE(default_keybindings); i++) {
-               const struct keybinding *keybinding = &default_keybindings[i];
+       if (!append_keymap_request_keys(buf, &pos, request, keymap, all))
+               return "Too many keybindings!";
+       if (pos > 0 && !all)
+               return buf;
 
-               if (keybinding->request != request)
-                       continue;
+       if (keymap != KEYMAP_GENERIC) {
+               /* Only the generic keymap includes the default keybindings when
+                * listing all keys. */
+               if (all)
+                       return buf;
 
-               if (!string_format_from(buf, &pos, "%s%s", sep,
-                                       get_key_name(keybinding->alias)))
+               if (!append_keymap_request_keys(buf, &pos, request, KEYMAP_GENERIC, all))
                        return "Too many keybindings!";
-               sep = ", ";
+               if (pos)
+                       return buf;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(default_keybindings); i++) {
+               if (default_keybindings[i].request == request) {
+                       if (!append_key(buf, &pos, &default_keybindings[i]))
+                               return "Too many keybindings!";
+                       if (!all)
+                               return buf;
+               }
        }
 
        return buf;
@@ -1492,7 +1600,7 @@ option_color_command(int argc, const char *argv[])
 {
        struct line_info *info;
 
-       if (argc != 3 && argc != 4) {
+       if (argc < 3) {
                config_msg = "Wrong number of arguments given to color command";
                return ERR;
        }
@@ -1519,9 +1627,15 @@ option_color_command(int argc, const char *argv[])
                return ERR;
        }
 
-       if (argc == 4 && !set_attribute(&info->attr, argv[3])) {
-               config_msg = "Unknown attribute";
-               return ERR;
+       info->attr = 0;
+       while (argc-- > 3) {
+               int attr;
+
+               if (!set_attribute(&attr, argv[argc])) {
+                       config_msg = "Unknown attribute";
+                       return ERR;
+               }
+               info->attr |= attr;
        }
 
        return OK;
@@ -1570,8 +1684,20 @@ option_set_command(int argc, const char *argv[])
        if (!strcmp(argv[0], "show-author"))
                return parse_bool(&opt_author, argv[2]);
 
-       if (!strcmp(argv[0], "show-date"))
-               return parse_bool(&opt_date, argv[2]);
+       if (!strcmp(argv[0], "show-date")) {
+               bool show_date;
+
+               if (!strcmp(argv[2], "relative")) {
+                       opt_date = DATE_RELATIVE;
+                       return OK;
+               } else if (!strcmp(argv[2], "short")) {
+                       opt_date = DATE_SHORT;
+                       return OK;
+               } else if (parse_bool(&show_date, argv[2])) {
+                       opt_date = show_date ? DATE_DEFAULT : DATE_NONE;
+               }
+               return ERR;
+       }
 
        if (!strcmp(argv[0], "show-rev-graph"))
                return parse_bool(&opt_rev_graph, argv[2]);
@@ -1594,6 +1720,9 @@ option_set_command(int argc, const char *argv[])
        if (!strcmp(argv[0], "horizontal-scroll"))
                return parse_step(&opt_hscroll, argv[2]);
 
+       if (!strcmp(argv[0], "split-view-height"))
+               return parse_step(&opt_scale_split_view, argv[2]);
+
        if (!strcmp(argv[0], "tab-size"))
                return parse_int(&opt_tab_size, argv[2], 1, 1024);
 
@@ -1609,7 +1738,7 @@ static int
 option_bind_command(int argc, const char *argv[])
 {
        enum request request;
-       int keymap;
+       int keymap = -1;
        int key;
 
        if (argc < 3) {
@@ -2021,8 +2150,9 @@ static bool
 draw_date(struct view *view, time_t *time)
 {
        const char *date = mkdate(time);
+       int cols = opt_date == DATE_SHORT ? DATE_SHORT_COLS : DATE_COLS;
 
-       return draw_field(view, LINE_DATE, date, DATE_COLS, FALSE);
+       return draw_field(view, LINE_DATE, date, cols, FALSE);
 }
 
 static bool
@@ -2223,6 +2353,15 @@ update_view_title(struct view *view)
        wnoutrefresh(view->title);
 }
 
+static int
+apply_step(double step, int value)
+{
+       if (step >= 1)
+               return (int) step;
+       value *= step + 0.01;
+       return value ? value : 1;
+}
+
 static void
 resize_display(void)
 {
@@ -2240,7 +2379,9 @@ resize_display(void)
        if (view != base) {
                /* Horizontal split. */
                view->width   = base->width;
-               view->height  = SCALE_SPLIT_VIEW(base->height);
+               view->height  = apply_step(opt_scale_split_view, base->height);
+               view->height  = MAX(view->height, MIN_VIEW_HEIGHT);
+               view->height  = MIN(view->height, base->height - MIN_VIEW_HEIGHT);
                base->height -= view->height;
 
                /* Make room for the title bar. */
@@ -2288,6 +2429,21 @@ redraw_display(bool clear)
        }
 }
 
+static void
+toggle_date_option(enum date *date)
+{
+       static const char *help[] = {
+               "no",
+               "default",
+               "relative",
+               "short"
+       };
+
+       opt_date = (opt_date + 1) % ARRAY_SIZE(help);
+       redraw_display(FALSE);
+       report("Displaying %s dates", help[opt_date]);
+}
+
 static void
 toggle_view_option(bool *option, const char *help)
 {
@@ -2309,8 +2465,12 @@ open_option_menu(void)
        };
        int selected = 0;
 
-       if (prompt_menu("Toggle option", menu, &selected))
-               toggle_view_option(menu[selected].data, menu[selected].text);
+       if (prompt_menu("Toggle option", menu, &selected)) {
+               if (menu[selected].data == &opt_date)
+                       toggle_date_option(menu[selected].data);
+               else
+                       toggle_view_option(menu[selected].data, menu[selected].text);
+       }
 }
 
 static void
@@ -2353,15 +2513,6 @@ goto_view_line(struct view *view, unsigned long offset, unsigned long lineno)
        return FALSE;
 }
 
-static int
-apply_step(double step, int value)
-{
-       if (step >= 1)
-               return (int) step;
-       value *= step + 0.01;
-       return value ? value : 1;
-}
-
 /* Scrolling backend */
 static void
 do_scroll_view(struct view *view, int lines)
@@ -3198,7 +3349,7 @@ view_driver(struct view *view, enum request request)
        case REQ_VIEW_BLAME:
                if (!opt_file[0]) {
                        report("No file chosen, press %s to open tree view",
-                              get_key(REQ_VIEW_TREE));
+                              get_key(view->keymap, REQ_VIEW_TREE));
                        break;
                }
                open_view(view, request, OPEN_DEFAULT);
@@ -3207,7 +3358,7 @@ view_driver(struct view *view, enum request request)
        case REQ_VIEW_BLOB:
                if (!ref_blob[0]) {
                        report("No file chosen, press %s to open tree view",
-                              get_key(REQ_VIEW_TREE));
+                              get_key(view->keymap, REQ_VIEW_TREE));
                        break;
                }
                open_view(view, request, OPEN_DEFAULT);
@@ -3216,7 +3367,7 @@ view_driver(struct view *view, enum request request)
        case REQ_VIEW_PAGER:
                if (!VIEW(REQ_VIEW_PAGER)->pipe && !VIEW(REQ_VIEW_PAGER)->lines) {
                        report("No pager content, press %s to run command from prompt",
-                              get_key(REQ_PROMPT));
+                              get_key(view->keymap, REQ_PROMPT));
                        break;
                }
                open_view(view, request, OPEN_DEFAULT);
@@ -3225,7 +3376,7 @@ view_driver(struct view *view, enum request request)
        case REQ_VIEW_STAGE:
                if (!VIEW(REQ_VIEW_STAGE)->lines) {
                        report("No stage content, press %s to open the status view and choose file",
-                              get_key(REQ_VIEW_STATUS));
+                              get_key(view->keymap, REQ_VIEW_STATUS));
                        break;
                }
                open_view(view, request, OPEN_DEFAULT);
@@ -3312,7 +3463,7 @@ view_driver(struct view *view, enum request request)
                break;
 
        case REQ_TOGGLE_DATE:
-               toggle_view_option(&opt_date, "date display");
+               toggle_date_option(&opt_date);
                break;
 
        case REQ_TOGGLE_AUTHOR:
@@ -3382,7 +3533,8 @@ view_driver(struct view *view, enum request request)
                return FALSE;
 
        default:
-               report("Unknown key, press 'h' for help");
+               report("Unknown key, press %s for help",
+                      get_key(view->keymap, REQ_VIEW_HELP));
                return TRUE;
        }
 
@@ -3517,22 +3669,39 @@ parse_author_line(char *ident, const char **author, time_t *time)
        }
 }
 
-static enum input_status
-select_commit_parent_handler(void *data, char *buf, int c)
+static bool
+open_commit_parent_menu(char buf[SIZEOF_STR], int *parents)
 {
-       size_t parents = *(size_t *) data;
-       int parent = 0;
+       char rev[SIZEOF_REV];
+       const char *revlist_argv[] = {
+               "git", "log", "--no-color", "-1", "--pretty=format:%s", rev, NULL
+       };
+       struct menu_item *items;
+       char text[SIZEOF_STR];
+       bool ok = TRUE;
+       int i;
 
-       if (!isdigit(c))
-               return INPUT_SKIP;
+       items = calloc(*parents + 1, sizeof(*items));
+       if (!items)
+               return FALSE;
 
-       if (*buf)
-               parent = atoi(buf) * 10;
-       parent += c - '0';
+       for (i = 0; i < *parents; i++) {
+               string_copy_rev(rev, &buf[SIZEOF_REV * i]);
+               if (!run_io_buf(revlist_argv, text, sizeof(text)) ||
+                   !(items[i].text = strdup(text))) {
+                       ok = FALSE;
+                       break;
+               }
+       }
 
-       if (parent > parents)
-               return INPUT_SKIP;
-       return INPUT_OK;
+       if (ok) {
+               *parents = 0;
+               ok = prompt_menu("Select parent", items, parents);
+       }
+       for (i = 0; items[i].text; i++)
+               free((char *) items[i].text);
+       free(items);
+       return ok;
 }
 
 static bool
@@ -3540,12 +3709,13 @@ select_commit_parent(const char *id, char rev[SIZEOF_REV], const char *path)
 {
        char buf[SIZEOF_STR * 4];
        const char *revlist_argv[] = {
-               "git", "rev-list", "-1", "--parents", id, "--", path, NULL
+               "git", "log", "--no-color", "-1",
+                       "--pretty=format:%P", id, "--", path, NULL
        };
        int parents;
 
        if (!run_io_buf(revlist_argv, buf, sizeof(buf)) ||
-           (parents = (strlen(buf) / 40) - 1) < 0) {
+           (parents = strlen(buf) / 40) < 0) {
                report("Failed to get parent information");
                return FALSE;
 
@@ -3557,17 +3727,8 @@ select_commit_parent(const char *id, char rev[SIZEOF_REV], const char *path)
                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);
-       }
+       if (parents > 1 && !open_commit_parent_menu(buf, &parents))
+               return FALSE;
 
        string_copy_rev(rev, &buf[41 * parents]);
        return TRUE;
@@ -3778,80 +3939,149 @@ static struct view_ops diff_ops = {
  * Help backend
  */
 
+static bool help_keymap_hidden[ARRAY_SIZE(keymap_table)];
+
+static char *
+help_name(char buf[SIZEOF_STR], const char *name, size_t namelen)
+{
+       int bufpos;
+
+       for (bufpos = 0; bufpos <= namelen; bufpos++) {
+               buf[bufpos] = tolower(name[bufpos]);
+               if (buf[bufpos] == '_')
+                       buf[bufpos] = '-';
+       }
+
+       buf[bufpos] = 0;
+       return buf;
+}
+
+#define help_keymap_name(buf, keymap) \
+       help_name(buf, keymap_table[keymap].name, keymap_table[keymap].namelen)
+
 static bool
-help_open(struct view *view)
+help_open_keymap_title(struct view *view, enum keymap keymap)
 {
        char buf[SIZEOF_STR];
-       size_t bufpos;
-       int i;
+       struct line *line;
 
-       if (view->lines > 0)
-               return TRUE;
+       line = add_line_format(view, LINE_HELP_KEYMAP, "[%c] %s bindings",
+                              help_keymap_hidden[keymap] ? '+' : '-',
+                              help_keymap_name(buf, keymap));
+       if (line)
+               line->other = keymap;
 
-       add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
+       return help_keymap_hidden[keymap];
+}
+
+static void
+help_open_keymap(struct view *view, enum keymap keymap)
+{
+       const char *group = NULL;
+       char buf[SIZEOF_STR];
+       size_t bufpos;
+       bool add_title = TRUE;
+       int i;
 
        for (i = 0; i < ARRAY_SIZE(req_info); i++) {
-               const char *key;
+               const char *key = NULL;
 
                if (req_info[i].request == REQ_NONE)
                        continue;
 
                if (!req_info[i].request) {
-                       add_line_text(view, "", LINE_DEFAULT);
-                       add_line_text(view, req_info[i].help, LINE_DEFAULT);
+                       group = req_info[i].help;
                        continue;
                }
 
-               key = get_key(req_info[i].request);
-               if (!*key)
-                       key = "(no key defined)";
+               key = get_keys(keymap, req_info[i].request, TRUE);
+               if (!key || !*key)
+                       continue;
 
-               for (bufpos = 0; bufpos <= req_info[i].namelen; bufpos++) {
-                       buf[bufpos] = tolower(req_info[i].name[bufpos]);
-                       if (buf[bufpos] == '_')
-                               buf[bufpos] = '-';
+               if (add_title && help_open_keymap_title(view, keymap))
+                       return;
+               add_title = false;
+
+               if (group) {
+                       add_line_text(view, group, LINE_HELP_GROUP);
+                       group = NULL;
                }
 
-               add_line_format(view, LINE_DEFAULT, "    %-25s %-20s %s",
-                               key, buf, req_info[i].help);
+               add_line_format(view, LINE_DEFAULT, "    %-25s %-20s %s", key,
+                               help_name(buf, req_info[i].name, req_info[i].namelen),
+                               req_info[i].help);
        }
 
-       if (run_requests) {
-               add_line_text(view, "", LINE_DEFAULT);
-               add_line_text(view, "External commands:", LINE_DEFAULT);
-       }
+       group = "External commands:";
 
        for (i = 0; i < run_requests; i++) {
                struct run_request *req = get_run_request(REQ_NONE + i + 1);
                const char *key;
                int argc;
 
-               if (!req)
+               if (!req || req->keymap != keymap)
                        continue;
 
                key = get_key_name(req->key);
                if (!*key)
                        key = "(no key defined)";
 
+               if (add_title && help_open_keymap_title(view, keymap))
+                       return;
+               if (group) {
+                       add_line_text(view, group, LINE_HELP_GROUP);
+                       group = NULL;
+               }
+
                for (bufpos = 0, argc = 0; req->argv[argc]; argc++)
                        if (!string_format_from(buf, &bufpos, "%s%s",
                                                argc ? " " : "", req->argv[argc]))
-                               return REQ_NONE;
+                               return;
 
-               add_line_format(view, LINE_DEFAULT, "    %-10s %-14s `%s`",
-                               keymap_table[req->keymap].name, key, buf);
+               add_line_format(view, LINE_DEFAULT, "    %-25s `%s`", key, buf);
        }
+}
+
+static bool
+help_open(struct view *view)
+{
+       enum keymap keymap;
+
+       reset_view(view);
+       add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
+       add_line_text(view, "", LINE_DEFAULT);
+
+       for (keymap = 0; keymap < ARRAY_SIZE(keymap_table); keymap++)
+               help_open_keymap(view, keymap);
 
        return TRUE;
 }
 
+static enum request
+help_request(struct view *view, enum request request, struct line *line)
+{
+       switch (request) {
+       case REQ_ENTER:
+               if (line->type == LINE_HELP_KEYMAP) {
+                       help_keymap_hidden[line->other] =
+                               !help_keymap_hidden[line->other];
+                       view->p_restore = TRUE;
+                       open_view(view, REQ_VIEW_HELP, OPEN_REFRESH);
+               }
+
+               return REQ_NONE;
+       default:
+               return pager_request(view, request, line);
+       }
+}
+
 static struct view_ops help_ops = {
        "line",
        NULL,
        help_open,
        NULL,
        pager_draw,
-       pager_request,
+       help_request,
        pager_grep,
        pager_select,
 };
@@ -5469,7 +5699,7 @@ status_update_files(struct view *view, struct line *line)
        struct line *pos = view->line + view->lines;
        int files = 0;
        int file, done;
-       int cursor_y, cursor_x;
+       int cursor_y = -1, cursor_x = -1;
 
        if (!status_update_prepare(&io, line->type))
                return FALSE;
@@ -5656,10 +5886,10 @@ status_select(struct view *view, struct line *line)
 
        if (status && status->status == 'U') {
                text = "Press %s to resolve conflict in %s";
-               key = get_key(REQ_STATUS_MERGE);
+               key = get_key(KEYMAP_STATUS, REQ_STATUS_MERGE);
 
        } else {
-               key = get_key(REQ_STATUS_UPDATE);
+               key = get_key(KEYMAP_STATUS, REQ_STATUS_UPDATE);
        }
 
        string_format(view->ref, text, key, file);
@@ -5857,7 +6087,7 @@ stage_request(struct view *view, enum request request, struct line *line)
        case REQ_STAGE_NEXT:
                if (stage_line_type == LINE_STAT_UNTRACKED) {
                        report("File is untracked; press %s to add",
-                              get_key(REQ_STATUS_UPDATE));
+                              get_key(KEYMAP_STAGE, REQ_STATUS_UPDATE));
                        return REQ_NONE;
                }
                stage_next(view, line);
@@ -7357,8 +7587,8 @@ parse_options(int argc, const char *argv[])
                        die("command too long");
        }
 
-       if (!prepare_update(VIEW(request), custom_argv, NULL, FORMAT_NONE))                                                                        
-               die("Failed to format arguments"); 
+       if (!prepare_update(VIEW(request), custom_argv, NULL, FORMAT_NONE))
+               die("Failed to format arguments");
 
        return request;
 }