Fix handling of REQ_NEXT/REQ_PREVIOUS
[tig] / tig.c
diff --git a/tig.c b/tig.c
index 50fbaa7..c3bc1f4 100644 (file)
--- a/tig.c
+++ b/tig.c
@@ -337,7 +337,8 @@ sq_quote(char buf[SIZEOF_STR], size_t bufsize, const char *src)
        REQ_(STOP_LOADING,      "Stop all loading views"), \
        REQ_(TOGGLE_LINENO,     "Toggle line numbers"), \
        REQ_(TOGGLE_REV_GRAPH,  "Toggle revision graph visualization"), \
-       REQ_(STATUS_UPDATE,     "Update file status") \
+       REQ_(STATUS_UPDATE,     "Update file status"), \
+       REQ_(EDIT,              "Open in editor")
 
 
 /* User action requests. */
@@ -424,6 +425,7 @@ static iconv_t opt_iconv            = ICONV_NONE;
 static char opt_search[SIZEOF_STR]     = "";
 static char opt_cdup[SIZEOF_STR]       = "";
 static char opt_git_dir[SIZEOF_STR]    = "";
+static char opt_editor[SIZEOF_STR]     = "";
 
 enum option_type {
        OPT_NONE,
@@ -500,7 +502,7 @@ parse_options(int argc, char *argv[])
 
                if (!strcmp(opt, "-S")) {
                        opt_request = REQ_VIEW_STATUS;
-                       break;
+                       continue;
                }
 
                if (check_option(opt, 'n', "line-number", OPT_INT, &opt_num_interval)) {
@@ -767,6 +769,7 @@ static struct keybinding default_keybindings[] = {
        { 'g',          REQ_TOGGLE_REV_GRAPH },
        { ':',          REQ_PROMPT },
        { 'u',          REQ_STATUS_UPDATE },
+       { 'e',          REQ_EDIT },
 
        /* Using the ncurses SIGWINCH handler. */
        { KEY_RESIZE,   REQ_SCREEN_RESIZE },
@@ -1263,8 +1266,8 @@ struct view_ops {
        bool (*read)(struct view *view, char *data);
        /* Draw one line; @lineno must be < view->height. */
        bool (*draw)(struct view *view, struct line *line, unsigned int lineno, bool selected);
-       /* Depending on view, change display based on current line. */
-       bool (*enter)(struct view *view, struct line *line);
+       /* Depending on view handle a special requests. */
+       enum request (*request)(struct view *view, enum request request, struct line *line);
        /* Search for regex in a line. */
        bool (*grep)(struct view *view, struct line *line);
        /* Select line */
@@ -1368,7 +1371,7 @@ update_view_title(struct view *view)
 
        assert(view_is_displayed(view));
 
-       if (view->lines || view->pipe) {
+       if (view != VIEW(REQ_VIEW_STATUS) && (view->lines || view->pipe)) {
                unsigned int view_lines = view->offset + view->height;
                unsigned int lines = view->lines
                                   ? MIN(view_lines, view->lines) * 100 / view->lines
@@ -2117,18 +2120,48 @@ open_view(struct view *prev, enum request request, enum open_flags flags)
                update_view_title(view);
 }
 
+static void
+open_editor(struct view *view, char *file)
+{
+       char cmd[SIZEOF_STR];
+       char file_sq[SIZEOF_STR];
+       char *editor;
+
+       editor = getenv("GIT_EDITOR");
+       if (!editor && *opt_editor)
+               editor = opt_editor;
+       if (!editor)
+               editor = getenv("VISUAL");
+       if (!editor)
+               editor = getenv("EDITOR");
+       if (!editor)
+               editor = "vi";
+
+       if (sq_quote(file_sq, 0, file) < sizeof(file_sq) &&
+           string_format(cmd, "%s %s", editor, file_sq)) {
+               def_prog_mode();           /* save current tty modes */
+               endwin();                  /* restore original tty modes */
+               system(cmd);
+               reset_prog_mode();
+               redraw_display();
+       }
+}
 
 /*
  * User request switch noodle
  */
 
-static void status_update(struct view *view);
-
 static int
 view_driver(struct view *view, enum request request)
 {
        int i;
 
+       if (view && view->lines) {
+               request = view->ops->request(view, request, &view->line[view->lineno]);
+               if (request == REQ_NONE)
+                       return TRUE;
+       }
+
        switch (request) {
        case REQ_MOVE_UP:
        case REQ_MOVE_DOWN:
@@ -2188,20 +2221,14 @@ view_driver(struct view *view, enum request request)
                        move_view(view, request);
                        if (view_is_displayed(view))
                                update_view_title(view);
-                       if (line == view->lineno)
-                               break;
+                       if (line != view->lineno)
+                               view->ops->request(view, REQ_ENTER,
+                                                  &view->line[view->lineno]);
+
                } else {
                        move_view(view, request);
-                       break;
                }
-               /* Fall-through */
-
-       case REQ_ENTER:
-               if (!view->lines) {
-                       report("Nothing to enter");
-                       break;
-               }
-               return view->ops->enter(view, &view->line[view->lineno]);
+               break;
 
        case REQ_VIEW_NEXT:
        {
@@ -2264,8 +2291,12 @@ view_driver(struct view *view, enum request request)
                redraw_display();
                break;
 
-       case REQ_STATUS_UPDATE:
-               status_update(view);
+       case REQ_EDIT:
+               report("Nothing to edit");
+               break;
+
+       case REQ_ENTER:
+               report("Nothing to enter");
                break;
 
        case REQ_NONE:
@@ -2469,11 +2500,14 @@ pager_read(struct view *view, char *data)
        return TRUE;
 }
 
-static bool
-pager_enter(struct view *view, struct line *line)
+static enum request
+pager_request(struct view *view, enum request request, struct line *line)
 {
        int split = 0;
 
+       if (request != REQ_ENTER)
+               return request;
+
        if (line->type == LINE_COMMIT &&
           (view == VIEW(REQ_VIEW_LOG) ||
            view == VIEW(REQ_VIEW_PAGER))) {
@@ -2492,7 +2526,7 @@ pager_enter(struct view *view, struct line *line)
        if (split)
                update_view_title(view);
 
-       return TRUE;
+       return REQ_NONE;
 }
 
 static bool
@@ -2527,7 +2561,7 @@ static struct view_ops pager_ops = {
        NULL,
        pager_read,
        pager_draw,
-       pager_enter,
+       pager_request,
        pager_grep,
        pager_select,
 };
@@ -2581,7 +2615,7 @@ static struct view_ops help_ops = {
        help_open,
        NULL,
        pager_draw,
-       pager_enter,
+       pager_request,
        pager_grep,
        pager_select,
 };
@@ -2694,11 +2728,13 @@ tree_read(struct view *view, char *text)
        return TRUE;
 }
 
-static bool
-tree_enter(struct view *view, struct line *line)
+static enum request
+tree_request(struct view *view, enum request request, struct line *line)
 {
        enum open_flags flags;
-       enum request request;
+
+       if (request != REQ_ENTER)
+               return request;
 
        switch (line->type) {
        case LINE_TREE_DIR:
@@ -2742,7 +2778,7 @@ tree_enter(struct view *view, struct line *line)
 
        open_view(view, request, flags);
 
-       return TRUE;
+       return REQ_NONE;
 }
 
 static void
@@ -2765,7 +2801,7 @@ static struct view_ops tree_ops = {
        NULL,
        tree_read,
        pager_draw,
-       tree_enter,
+       tree_request,
        pager_grep,
        tree_select,
 };
@@ -2781,7 +2817,7 @@ static struct view_ops blob_ops = {
        NULL,
        blob_read,
        pager_draw,
-       pager_enter,
+       pager_request,
        pager_grep,
        pager_select,
 };
@@ -3020,7 +3056,7 @@ status_draw(struct view *view, struct line *line, unsigned int lineno, bool sele
        return TRUE;
 }
 
-static bool
+static enum request
 status_enter(struct view *view, struct line *line)
 {
        struct status *status = line->data;
@@ -3031,22 +3067,22 @@ status_enter(struct view *view, struct line *line)
        if (line->type == LINE_STAT_NONE ||
            (!status && line[1].type == LINE_STAT_NONE)) {
                report("No file to diff");
-               return TRUE;
+               return REQ_NONE;
        }
 
        if (status && sq_quote(path, 0, status->name) >= sizeof(path))
-               return FALSE;
+               return REQ_QUIT;
 
        if (opt_cdup[0] &&
            line->type != LINE_STAT_UNTRACKED &&
            !string_format_from(opt_cmd, &cmdsize, "cd %s;", opt_cdup))
-               return FALSE;
+               return REQ_QUIT;
 
        switch (line->type) {
        case LINE_STAT_STAGED:
                if (!string_format_from(opt_cmd, &cmdsize, STATUS_DIFF_SHOW_CMD,
                                        "--cached", path))
-                       return FALSE;
+                       return REQ_QUIT;
                if (status)
                        info = "Staged changes to %s";
                else
@@ -3056,7 +3092,7 @@ status_enter(struct view *view, struct line *line)
        case LINE_STAT_UNSTAGED:
                if (!string_format_from(opt_cmd, &cmdsize, STATUS_DIFF_SHOW_CMD,
                                        "", path))
-                       return FALSE;
+                       return REQ_QUIT;
                if (status)
                        info = "Unstaged changes to %s";
                else
@@ -3065,11 +3101,12 @@ status_enter(struct view *view, struct line *line)
 
        case LINE_STAT_UNTRACKED:
                if (opt_pipe)
-                       return FALSE;
+                       return REQ_QUIT;
+
 
                if (!status) {
                        report("No file to show");
-                       return TRUE;
+                       return REQ_NONE;
                }
 
                opt_pipe = fopen(status->name, "r");
@@ -3085,9 +3122,10 @@ status_enter(struct view *view, struct line *line)
                string_format(VIEW(REQ_VIEW_DIFF)->ref, info, status->name);
        }
 
-       return TRUE;
+       return REQ_NONE;
 }
 
+
 static bool
 status_update_file(struct view *view, struct status *status, enum line_type type)
 {
@@ -3145,58 +3183,91 @@ status_update_file(struct view *view, struct status *status, enum line_type type
 static void
 status_update(struct view *view)
 {
-       if (view == VIEW(REQ_VIEW_STATUS)) {
-               struct line *line = &view->line[view->lineno];
-
-               assert(view->lines);
+       struct line *line = &view->line[view->lineno];
 
-               if (!line->data) {
-                       if (line[1].type == LINE_STAT_NONE) {
-                               report("Nothing to update");
-                               return;
-                       }
+       assert(view->lines);
 
-                       while (++line < view->line + view->lines && line->data) {
-                               if (!status_update_file(view, line->data, line->type))
-                                       report("Failed to update file status");
-                       }
+       if (!line->data) {
+               while (++line < view->line + view->lines && line->data) {
+                       if (!status_update_file(view, line->data, line->type))
+                               report("Failed to update file status");
+               }
 
-               } else if (!status_update_file(view, line->data, line->type)) {
-                       report("Failed to update file status");
+               if (!line[-1].data) {
+                       report("Nothing to update");
+                       return;
                }
 
-               open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
-       } else {
-               report("This action is only valid for the status view");
+       } else if (!status_update_file(view, line->data, line->type)) {
+               report("Failed to update file status");
        }
+
+       open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
+}
+
+static enum request
+status_request(struct view *view, enum request request, struct line *line)
+{
+       struct status *status = line->data;
+
+       switch (request) {
+       case REQ_STATUS_UPDATE:
+               status_update(view);
+               break;
+
+       case REQ_EDIT:
+               if (!status)
+                       return request;
+
+               open_editor(view, status->name);
+               break;
+
+       case REQ_ENTER:
+               status_enter(view, line);
+               break;
+
+       default:
+               return request;
+       }
+
+       return REQ_NONE;
 }
 
 static void
 status_select(struct view *view, struct line *line)
 {
+       struct status *status = line->data;
+       char file[SIZEOF_STR] = "all files";
        char *text;
 
+       if (status && !string_format(file, "'%s'", status->name))
+               return;
+
+       if (!status && line[1].type == LINE_STAT_NONE)
+               line++;
+
        switch (line->type) {
        case LINE_STAT_STAGED:
-               text = "Press %s to unstage file for commit";
+               text = "Press %s to unstage %s for commit";
                break;
 
        case LINE_STAT_UNSTAGED:
-               text = "Press %s to stage file for commit  ";
+               text = "Press %s to stage %s for commit";
                break;
 
        case LINE_STAT_UNTRACKED:
-               text = "Press %s to stage file for addition";
+               text = "Press %s to stage %s for addition";
                break;
 
        case LINE_STAT_NONE:
-               return;
+               text = "Nothing to update";
+               break;
 
        default:
                die("w00t");
        }
 
-       string_format(view->ref, text, get_key(REQ_STATUS_UPDATE));
+       string_format(view->ref, text, get_key(REQ_STATUS_UPDATE), file);
 }
 
 static bool
@@ -3236,7 +3307,7 @@ static struct view_ops status_ops = {
        status_open,
        NULL,
        status_draw,
-       status_enter,
+       status_request,
        status_grep,
        status_select,
 };
@@ -3668,13 +3739,17 @@ main_read(struct view *view, char *line)
        return TRUE;
 }
 
-static bool
-main_enter(struct view *view, struct line *line)
+static enum request
+main_request(struct view *view, enum request request, struct line *line)
 {
        enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
 
-       open_view(view, REQ_VIEW_DIFF, flags);
-       return TRUE;
+       if (request == REQ_ENTER)
+               open_view(view, REQ_VIEW_DIFF, flags);
+       else
+               return request;
+
+       return REQ_NONE;
 }
 
 static bool
@@ -3722,7 +3797,7 @@ static struct view_ops main_ops = {
        NULL,
        main_read,
        main_draw,
-       main_enter,
+       main_request,
        main_grep,
        main_select,
 };
@@ -4172,6 +4247,9 @@ read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen
        if (!strcmp(name, "i18n.commitencoding"))
                string_ncopy(opt_encoding, value, valuelen);
 
+       if (!strcmp(name, "core.editor"))
+               string_ncopy(opt_editor, value, valuelen);
+
        return OK;
 }