Code

Fix drawing loading views that are not displayed.
[tig.git] / tig.c
diff --git a/tig.c b/tig.c
index f436571c1f4dfb4e5b4294c3200aeb8d62b4896e..d2bc2dd46555008a03b6aa903fea78ea304ecfbf 100644 (file)
--- a/tig.c
+++ b/tig.c
@@ -280,6 +280,15 @@ string_enum_compare(const char *str1, const char *str2, int len)
 #define prefixcmp(str1, str2) \
        strncmp(str1, str2, STRING_SIZE(str2))
 
+static inline int
+suffixcmp(const char *str, int slen, const char *suffix)
+{
+       size_t len = slen >= 0 ? slen : strlen(str);
+       size_t suffixlen = strlen(suffix);
+
+       return suffixlen < len ? strcmp(str + len - suffixlen, suffix) : -1;
+}
+
 /* Shell quoting
  *
  * NOTE: The following is a slightly modified copy of the git project's shell
@@ -471,8 +480,8 @@ static char opt_path[SIZEOF_STR]    = "";
 static char opt_file[SIZEOF_STR]       = "";
 static char opt_ref[SIZEOF_REF]                = "";
 static char opt_head[SIZEOF_REF]       = "";
+static char opt_head_rev[SIZEOF_REV]   = "";
 static char opt_remote[SIZEOF_REF]     = "";
-static bool opt_no_head                        = TRUE;
 static FILE *opt_pipe                  = NULL;
 static char opt_encoding[20]           = "UTF-8";
 static bool opt_utf8                   = TRUE;
@@ -483,6 +492,10 @@ static char opt_cdup[SIZEOF_STR]   = "";
 static char opt_git_dir[SIZEOF_STR]    = "";
 static signed char opt_is_inside_work_tree     = -1; /* set to TRUE or FALSE */
 static char opt_editor[SIZEOF_STR]     = "";
+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)))
 
 static enum request
 parse_options(int argc, const char *argv[])
@@ -2378,8 +2391,17 @@ update_view(struct view *view)
                }
        }
 
+       if (ferror(view->pipe) && errno != 0) {
+               report("Failed to read: %s", strerror(errno));
+               end_update(view, TRUE);
+
+       } else if (feof(view->pipe)) {
+               report("");
+               end_update(view, FALSE);
+       }
+
        if (!view_is_displayed(view))
-               goto check_pipe;
+               return TRUE;
 
        if (view == VIEW(REQ_VIEW_TREE)) {
                /* Clear the view and redraw everything since the tree sorting
@@ -2409,17 +2431,6 @@ update_view(struct view *view)
        /* Update the title _after_ the redraw so that if the redraw picks up a
         * commit reference in view->ref it'll be available here. */
        update_view_title(view);
-
-check_pipe:
-       if (ferror(view->pipe) && errno != 0) {
-               report("Failed to read: %s", strerror(errno));
-               end_update(view, TRUE);
-
-       } else if (feof(view->pipe)) {
-               report("");
-               end_update(view, FALSE);
-       }
-
        return TRUE;
 
 alloc_error:
@@ -2567,7 +2578,7 @@ open_external_viewer(const char *cmd)
        endwin();                  /* restore original tty modes */
        system(cmd);
        fprintf(stderr, "Press Enter to continue");
-       getc(stdin);
+       getc(opt_tty);
        reset_prog_mode();
        redraw_display();
 }
@@ -3357,30 +3368,41 @@ tree_request(struct view *view, enum request request, struct line *line)
 {
        enum open_flags flags;
 
-       if (request == REQ_VIEW_BLAME) {
-               const char *filename = tree_path(line);
-
-               if (line->type == LINE_TREE_DIR) {
-                       report("Cannot show blame for directory %s", opt_path);
+       switch (request) {
+       case REQ_VIEW_BLAME:
+               if (line->type != LINE_TREE_FILE) {
+                       report("Blame only supported for files");
                        return REQ_NONE;
                }
 
                string_copy(opt_ref, view->vid);
-               string_format(opt_file, "%s%s", opt_path, filename);
                return request;
-       }
-       if (request == REQ_TREE_PARENT) {
-               if (*opt_path) {
-                       /* fake 'cd  ..' */
-                       request = REQ_ENTER;
-                       line = &view->line[1];
+
+       case REQ_EDIT:
+               if (line->type != LINE_TREE_FILE) {
+                       report("Edit only supported for files");
+               } else if (!is_head_commit(view->vid)) {
+                       report("Edit only supported for files in the current work tree");
                } else {
+                       open_editor(TRUE, opt_file);
+               }
+               return REQ_NONE;
+
+       case REQ_TREE_PARENT:
+               if (!*opt_path) {
                        /* quit view if at top of tree */
                        return REQ_VIEW_CLOSE;
                }
-       }
-       if (request != REQ_ENTER)
+               /* fake 'cd  ..' */
+               line = &view->line[1];
+               break;
+
+       case REQ_ENTER:
+               break;
+
+       default:
                return request;
+       }
 
        /* Cleanup the stack if the tree view is at a different tree. */
        while (!*opt_path && tree_stack)
@@ -3429,6 +3451,7 @@ tree_select(struct view *view, struct line *line)
 
        if (line->type == LINE_TREE_FILE) {
                string_copy_rev(ref_blob, text);
+               string_format(opt_file, "%s%s", opt_path, tree_path(line));
 
        } else if (line->type != LINE_TREE_DIR) {
                return;
@@ -4030,7 +4053,7 @@ status_open(struct view *view)
                return FALSE;
 
        add_line_data(view, NULL, LINE_STAT_HEAD);
-       if (opt_no_head)
+       if (is_initial_commit())
                string_copy(status_onbranch, "Initial commit");
        else if (!*opt_head)
                string_copy(status_onbranch, "Not currently on any branch");
@@ -4039,7 +4062,7 @@ status_open(struct view *view)
 
        system("git update-index -q --refresh >/dev/null 2>/dev/null");
 
-       if (opt_no_head) {
+       if (is_initial_commit()) {
                if (!status_run(view, STATUS_LIST_NO_HEAD_CMD, 'A', LINE_STAT_STAGED))
                        return FALSE;
        } else if (!status_run(view, STATUS_DIFF_INDEX_CMD, 0, LINE_STAT_STAGED)) {
@@ -4158,7 +4181,7 @@ status_enter(struct view *view, struct line *line)
 
        switch (line->type) {
        case LINE_STAT_STAGED:
-               if (opt_no_head) {
+               if (is_initial_commit()) {
                        if (!string_format_from(opt_cmd, &cmdsize,
                                                STATUS_DIFF_NO_HEAD_SHOW_CMD,
                                                newpath))
@@ -4195,6 +4218,11 @@ status_enter(struct view *view, struct line *line)
                        return REQ_NONE;
                }
 
+               if (!suffixcmp(status->new.name, -1, "/")) {
+                       report("Cannot display a directory");
+                       return REQ_NONE;
+               }
+
                opt_pipe = fopen(status->new.name, "r");
                info = "Untracked file %s";
                break;
@@ -4428,6 +4456,10 @@ status_request(struct view *view, enum request request, struct line *line)
        case REQ_EDIT:
                if (!status)
                        return request;
+               if (status->status == 'D') {
+                       report("File has been deleted.");
+                       return REQ_NONE;
+               }
 
                open_editor(status->status != '?', status->new.name);
                break;
@@ -4630,7 +4662,7 @@ stage_update(struct view *view, struct line *line)
 {
        struct line *chunk = NULL;
 
-       if (!opt_no_head && stage_line_type != LINE_STAT_UNTRACKED)
+       if (!is_initial_commit() && stage_line_type != LINE_STAT_UNTRACKED)
                chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
 
        if (chunk) {
@@ -4664,7 +4696,7 @@ stage_revert(struct view *view, struct line *line)
 {
        struct line *chunk = NULL;
 
-       if (!opt_no_head && stage_line_type == LINE_STAT_UNSTAGED)
+       if (!is_initial_commit() && stage_line_type == LINE_STAT_UNSTAGED)
                chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
 
        if (chunk) {
@@ -4746,6 +4778,10 @@ stage_request(struct view *view, enum request request, struct line *line)
        case REQ_EDIT:
                if (!stage_status.new.name[0])
                        return request;
+               if (stage_status.status == 'D') {
+                       report("File has been deleted.");
+                       return REQ_NONE;
+               }
 
                open_editor(stage_status.status != '?', stage_status.new.name);
                break;
@@ -4775,8 +4811,14 @@ stage_request(struct view *view, enum request request, struct line *line)
        if (!status_exists(&stage_status, stage_line_type))
                return REQ_VIEW_CLOSE;
 
-       if (stage_line_type == LINE_STAT_UNTRACKED)
+       if (stage_line_type == LINE_STAT_UNTRACKED) {
+               if (!suffixcmp(stage_status.new.name, -1, "/")) {
+                       report("Cannot display a directory");
+                       return REQ_NONE;
+               }
+
                opt_pipe = fopen(stage_status.new.name, "r");
+       }
        open_view(view, REQ_VIEW_STAGE, OPEN_REFRESH);
 
        return REQ_NONE;
@@ -5532,13 +5574,13 @@ init_display(void)
        /* Initialize the curses library */
        if (isatty(STDIN_FILENO)) {
                cursed = !!initscr();
+               opt_tty = stdin;
        } else {
                /* Leave stdin and stdout alone when acting as a pager. */
-               FILE *io = fopen("/dev/tty", "r+");
-
-               if (!io)
+               opt_tty = fopen("/dev/tty", "r+");
+               if (!opt_tty)
                        die("Failed to open /dev/tty");
-               cursed = !!newterm(NULL, io, io);
+               cursed = !!newterm(NULL, opt_tty, opt_tty);
        }
 
        if (!cursed)
@@ -5783,7 +5825,7 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen)
        bool head = FALSE;
 
        if (!prefixcmp(name, "refs/tags/")) {
-               if (!strcmp(name + namelen - 3, "^{}")) {
+               if (!suffixcmp(name, namelen, "^{}")) {
                        namelen -= 3;
                        name[namelen] = 0;
                        if (refs_size > 0 && refs[refs_size - 1].ltag == TRUE)
@@ -5808,7 +5850,7 @@ read_ref(char *id, size_t idlen, char *name, size_t namelen)
                head     = !strncmp(opt_head, name, namelen);
 
        } else if (!strcmp(name, "HEAD")) {
-               opt_no_head = FALSE;
+               string_ncopy(opt_head_rev, id, idlen);
                return OK;
        }