X-Git-Url: https://git.distorted.org.uk/~mdw/tig/blobdiff_plain/8855ada4ae068bc5d8746f125747a678975180d9..cfc6fa71c81e66c653fe83763d262c948b87731c:/tig.c diff --git a/tig.c b/tig.c index c210699..cbfa854 100644 --- a/tig.c +++ b/tig.c @@ -46,7 +46,7 @@ static void die(const char *err, ...); static void report(const char *msg, ...); -static void set_nonblocking_input(int boolean); +static void set_nonblocking_input(bool loading); #define ABS(x) ((x) >= 0 ? (x) : -(x)) #define MIN(x, y) ((x) < (y) ? (x) : (y)) @@ -90,6 +90,7 @@ enum request { REQ_QUIT, REQ_PROMPT, REQ_SCREEN_REDRAW, + REQ_SCREEN_RESIZE, REQ_SCREEN_UPDATE, REQ_SHOW_VERSION, REQ_STOP_LOADING, @@ -195,7 +196,7 @@ parse_options(int argc, char *argv[]) /** * -l:: - * Start up in log view. + * Start up in log view using the internal log command. **/ if (!strcmp(opt, "-l")) { opt_request = REQ_VIEW_LOG; @@ -204,7 +205,7 @@ parse_options(int argc, char *argv[]) /** * -d:: - * Start up in diff view. + * Start up in diff view using the internal diff command. **/ if (!strcmp(opt, "-d")) { opt_request = REQ_VIEW_DIFF; @@ -306,13 +307,12 @@ parse_options(int argc, char *argv[]) /** * Git command options * ~~~~~~~~~~~~~~~~~~~ - * * All git command options specified on the command line will * be passed to the given command and all will be shell quoted * before used. * * NOTE: It is possible to specify options even for the main - * view. If doing this you should not touch the --pretty + * view. If doing this you should not touch the `--pretty` * option. * * Example on how to open the log view and show both author and @@ -458,6 +458,9 @@ struct keymap keymap[] = { /* wgetch() with nodelay() enabled returns ERR when there's no input. */ { ERR, REQ_SCREEN_UPDATE }, + + /* Use the ncurses SIGWINCH handler. */ + { KEY_RESIZE, REQ_SCREEN_RESIZE }, }; static enum request @@ -585,13 +588,59 @@ init_colors(void) } +/** + * ENVIRONMENT VARIABLES + * --------------------- + * It is possible to alter which commands are used for the different views. + * If for example you prefer commits in the main to be sorted by date and + * only show 500 commits, use: + * + * $ TIG_MAIN_CMD="git log --date-order -n500 --pretty=raw %s" tig + * + * Or set the variable permanently in your environment. + * + * Notice, how `%s` is used to specify the commit reference. There can + * be a maximum of 5 `%s` ref specifications. + * + * TIG_DIFF_CMD:: + * The command used for the diff view. By default, git show is used + * as a backend. + * + * TIG_LOG_CMD:: + * The command used for the log view. + * + * TIG_MAIN_CMD:: + * The command used for the main view. Note, you must always specify + * the option: `--pretty=raw` since the main view parser expects to + * read that format. + **/ + +#define TIG_DIFF_CMD \ + "git show --patch-with-stat --find-copies-harder -B -C %s" + +#define TIG_LOG_CMD \ + "git log --cc --stat -n100 %s" + +#define TIG_MAIN_CMD \ + "git log --topo-order --stat --pretty=raw %s" + +/* We silently ignore that the following are also exported. */ + +#define TIG_HELP_CMD \ + "man tig 2> /dev/null" + +#define TIG_PAGER_CMD \ + "" + + /* * Viewer */ struct view { const char *name; /* View name */ - const char *cmdfmt; /* Default command line format */ + char *cmd_fmt; /* Default command line format */ + char *cmd_env; /* Command line set via environment */ char *id; /* Points to either of ref_{head,commit} */ size_t objsize; /* Size of objects in the line index */ @@ -629,27 +678,21 @@ struct view { static struct view_ops pager_ops; static struct view_ops main_ops; -#define DIFF_CMD \ - "git show --patch-with-stat --find-copies-harder -B -C %s" - -#define LOG_CMD \ - "git log --cc --stat -n100 %s" - -#define MAIN_CMD \ - "git log --stat --pretty=raw %s" - -#define HELP_CMD \ - "man tig 2> /dev/null" - char ref_head[SIZEOF_REF] = "HEAD"; char ref_commit[SIZEOF_REF] = "HEAD"; +#define VIEW_STR(name, cmd, env, ref, objsize, ops) \ + { name, cmd, #env, ref, objsize, ops } + +#define VIEW_(id, name, ops, ref, objsize) \ + VIEW_STR(name, TIG_##id##_CMD, TIG_##id##_CMD, ref, objsize, ops) + static struct view views[] = { - { "main", MAIN_CMD, ref_head, sizeof(struct commit), &main_ops }, - { "diff", DIFF_CMD, ref_commit, sizeof(char), &pager_ops }, - { "log", LOG_CMD, ref_head, sizeof(char), &pager_ops }, - { "help", HELP_CMD, ref_head, sizeof(char), &pager_ops }, - { "pager", "", "static", sizeof(char), &pager_ops }, + VIEW_(MAIN, "main", &main_ops, ref_head, sizeof(struct commit)), + VIEW_(DIFF, "diff", &pager_ops, ref_commit, sizeof(char)), + VIEW_(LOG, "log", &pager_ops, ref_head, sizeof(char)), + VIEW_(HELP, "help", &pager_ops, ref_head, sizeof(char)), + VIEW_(PAGER, "pager", &pager_ops, "static", sizeof(char)), }; #define VIEW(req) (&views[(req) - REQ_OFFSET - 1]) @@ -827,7 +870,7 @@ scroll_view(struct view *view, enum request request) lines = view->lines - view->offset; if (lines == 0 || view->offset + view->height >= view->lines) { - report("Already on last line"); + report("Cannot scroll beyond the last line"); return; } break; @@ -839,7 +882,7 @@ scroll_view(struct view *view, enum request request) lines = view->offset; if (lines == 0) { - report("Already on first line"); + report("Cannot scroll beyond the first line"); return; } @@ -891,11 +934,11 @@ move_view(struct view *view, enum request request) } if (steps <= 0 && view->lineno == 0) { - report("Already on first line"); + report("Cannot move beyond the first line"); return; } else if (steps >= 0 && view->lineno + 1 >= view->lines) { - report("Already on last line"); + report("Cannot move beyond the last line"); return; } @@ -956,8 +999,10 @@ begin_update(struct view *view) * invalid so clear it. */ view->ref[0] = 0; } else { - if (snprintf(view->cmd, sizeof(view->cmd), view->cmdfmt, - id, id, id) >= sizeof(view->cmd)) + char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt; + + if (snprintf(view->cmd, sizeof(view->cmd), format, + id, id, id, id, id) >= sizeof(view->cmd)) return FALSE; } @@ -1001,7 +1046,10 @@ end_update(struct view *view) if (!view->pipe) return; set_nonblocking_input(FALSE); - pclose(view->pipe); + if (view->pipe == stdin) + fclose(view->pipe); + else + pclose(view->pipe); view->pipe = NULL; } @@ -1058,9 +1106,6 @@ update_view(struct view *view) } } - /* CPU hogilicious! */ - update_view_title(view); - if (redraw_from >= 0) { /* If this is an incremental update, redraw the previous line * since for commits some members could have changed when @@ -1072,6 +1117,10 @@ update_view(struct view *view) redraw_view_from(view, redraw_from); } + /* 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); + if (ferror(view->pipe)) { report("Failed to read: %s", strerror(errno)); goto end; @@ -1153,9 +1202,9 @@ open_view(struct view *prev, enum request request, enum open_flags flags) resize_display(); - if (split && prev->lineno - prev->offset > prev->height) { + if (split && prev->lineno - prev->offset >= prev->height) { /* Take the title line into account. */ - int lines = prev->lineno - prev->height + 1; + int lines = prev->lineno - prev->offset - prev->height + 1; /* Scroll the view that was split if the current line is * outside the new limited view. */ @@ -1262,8 +1311,14 @@ view_driver(struct view *view, enum request request) report("Version: %s", VERSION); return TRUE; + case REQ_SCREEN_RESIZE: + resize_display(); + /* Fall-through */ case REQ_SCREEN_REDRAW: - redraw_view(view); + foreach_view (view, i) { + redraw_view(view); + update_view_title(view); + } break; case REQ_SCREEN_UPDATE: @@ -1370,14 +1425,6 @@ pager_draw(struct view *view, unsigned int lineno) static bool pager_read(struct view *view, char *line) { - /* For some reason the piped man page has many empty lines - * so skip successive emptys lines to work around it. */ - if (view == VIEW(REQ_VIEW_HELP) && - !*line && - view->lines && - !*((char *) view->line[view->lines - 1])) - return TRUE; - view->line[view->lines] = strdup(line); if (!view->line[view->lines]) return FALSE; @@ -1398,13 +1445,13 @@ pager_enter(struct view *view) return TRUE; } - static struct view_ops pager_ops = { pager_draw, pager_read, pager_enter, }; + static bool main_draw(struct view *view, unsigned int lineno) { @@ -1533,8 +1580,10 @@ main_read(struct view *view, char *line) /* 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[5])) + isspace(line[4])) break; string_copy(commit->title, line + 4); @@ -1572,15 +1621,16 @@ report(const char *msg, ...) { va_list args; - va_start(args, msg); - /* Update the title window first, so the cursor ends up in the status * window. */ update_view_title(display[current_view]); + va_start(args, msg); + werase(status_win); wmove(status_win, 0, 0); - vwprintw(status_win, msg, args); + if (*msg) + vwprintw(status_win, msg, args); wrefresh(status_win); va_end(args); @@ -1588,19 +1638,14 @@ report(const char *msg, ...) /* Controls when nodelay should be in effect when polling user input. */ static void -set_nonblocking_input(int loading) +set_nonblocking_input(bool loading) { /* The number of loading views. */ static unsigned int nloading; - if (loading == TRUE) { - if (nloading++ == 0) - nodelay(status_win, TRUE); - return; - } - - if (nloading-- == 1) - nodelay(status_win, FALSE); + if ((loading == FALSE && nloading-- == 1) || + (loading == TRUE && nloading++ == 0)) + nodelay(status_win, loading); } static void @@ -1670,19 +1715,23 @@ static void die(const char *err, ...) int main(int argc, char *argv[]) { + struct view *view; enum request request; + size_t i; signal(SIGINT, quit); if (!parse_options(argc, argv)) return 0; + for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++) + view->cmd_env = getenv(view->cmd_env); + request = opt_request; init_display(); while (view_driver(display[current_view], request)) { - struct view *view; int key; int i; @@ -1693,7 +1742,10 @@ main(int argc, char *argv[]) key = wgetch(status_win); request = get_request(key); - if (request == REQ_PROMPT) { + /* Some low-level request handling. This keeps handling of + * status_win restricted. */ + switch (request) { + case REQ_PROMPT: report(":"); /* Temporarily switch to line-oriented and echoed * input. */ @@ -1709,6 +1761,23 @@ main(int argc, char *argv[]) noecho(); cbreak(); + break; + + case REQ_SCREEN_RESIZE: + { + int height, width; + + getmaxyx(stdscr, height, width); + + /* Resize the status view and let the view driver take + * care of resizing the displayed views. */ + wresize(status_win, 1, width); + mvwin(status_win, height - 1, 0); + wrefresh(status_win); + break; + } + default: + break; } } @@ -1722,8 +1791,7 @@ main(int argc, char *argv[]) * ---- * Features that should be explored. * - * - Terminal resizing support. I am yet to figure out whether catching - * SIGWINCH is preferred over using ncurses' built-in support for resizing. + * - Searching. * * - Locale support. *