X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;ds=sidebyside;f=tig.c;h=68465192d31d87bab36ca44ed434abeccaf74a86;hb=60e8ea56880fc2e42008075d516c356ef605bc60;hp=ef8f6caccb4faaaf6dd17430f2652b7e61cdc0f2;hpb=6362c217c5356b6df6e74fbec066fe756ed479bd;p=tig.git diff --git a/tig.c b/tig.c index ef8f6ca..6846519 100644 --- a/tig.c +++ b/tig.c @@ -45,7 +45,15 @@ /* ncurses(3): Must be defined to have extended wide-character functions. */ #define _XOPEN_SOURCE_EXTENDED -#include +#ifdef HAVE_NCURSESW_NCURSES_H +#include +#else +#ifdef HAVE_NCURSES_NCURSES_H +#include +#else +#include +#endif +#endif #if __GNUC__ >= 3 #define __NORETURN __attribute__((__noreturn__)) @@ -58,7 +66,7 @@ static void warn(const char *msg, ...); static void report(const char *msg, ...); static int read_properties(FILE *pipe, const char *separators, int (*read)(char *, size_t, char *, size_t)); static void set_nonblocking_input(bool loading); -static size_t utf8_length(const char *string, size_t max_width, int *trimmed, bool reserve); +static size_t utf8_length(const char *string, int *width, size_t max_width, int *trimmed, bool reserve); #define ABS(x) ((x) >= 0 ? (x) : -(x)) #define MIN(x, y) ((x) < (y) ? (x) : (y)) @@ -109,7 +117,7 @@ static size_t utf8_length(const char *string, size_t max_width, int *trimmed, bo #endif #define TIG_LS_REMOTE \ - "git ls-remote $(git rev-parse --git-dir) 2>/dev/null" + "git ls-remote . 2>/dev/null" #define TIG_DIFF_CMD \ "git show --pretty=fuller --no-color --root --patch-with-stat --find-copies-harder -C %s 2>/dev/null" @@ -368,6 +376,7 @@ sq_quote(char buf[SIZEOF_STR], size_t bufsize, const char *src) REQ_(TOGGLE_REFS, "Toggle reference display (tags/branches)"), \ REQ_(STATUS_UPDATE, "Update file status"), \ 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_(EDIT, "Open in editor"), \ REQ_(NONE, "Do nothing") @@ -442,7 +451,7 @@ static bool opt_rev_graph = FALSE; static bool opt_show_refs = TRUE; static int opt_num_interval = NUMBER_INTERVAL; static int opt_tab_size = TAB_SIZE; -static enum request opt_request = REQ_VIEW_MAIN; +static int opt_author_cols = AUTHOR_COLS-1; static char opt_cmd[SIZEOF_STR] = ""; static char opt_path[SIZEOF_STR] = ""; static char opt_file[SIZEOF_STR] = ""; @@ -461,34 +470,32 @@ 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 bool +static enum request parse_options(int argc, char *argv[]) { + enum request request = REQ_VIEW_MAIN; size_t buf_size; char *subcommand; bool seen_dashdash = FALSE; int i; if (!isatty(STDIN_FILENO)) { - opt_request = REQ_VIEW_PAGER; opt_pipe = stdin; - return TRUE; + return REQ_VIEW_PAGER; } if (argc <= 1) - return TRUE; + return REQ_VIEW_MAIN; subcommand = argv[1]; if (!strcmp(subcommand, "status") || !strcmp(subcommand, "-S")) { - opt_request = REQ_VIEW_STATUS; if (!strcmp(subcommand, "-S")) warn("`-S' has been deprecated; use `tig status' instead"); if (argc > 2) warn("ignoring arguments after `%s'", subcommand); - return TRUE; + return REQ_VIEW_STATUS; } else if (!strcmp(subcommand, "blame")) { - opt_request = REQ_VIEW_BLAME; if (argc <= 2 || argc > 4) die("invalid number of options to blame\n\n%s", usage); @@ -499,14 +506,13 @@ parse_options(int argc, char *argv[]) } string_ncopy(opt_file, argv[i], strlen(argv[i])); - return TRUE; + return REQ_VIEW_BLAME; } else if (!strcmp(subcommand, "show")) { - opt_request = REQ_VIEW_DIFF; + request = REQ_VIEW_DIFF; } else if (!strcmp(subcommand, "log") || !strcmp(subcommand, "diff")) { - opt_request = subcommand[0] == 'l' - ? REQ_VIEW_LOG : REQ_VIEW_DIFF; + request = subcommand[0] == 'l' ? REQ_VIEW_LOG : REQ_VIEW_DIFF; warn("`tig %s' has been deprecated", subcommand); } else { @@ -530,11 +536,11 @@ parse_options(int argc, char *argv[]) } else if (!strcmp(opt, "-v") || !strcmp(opt, "--version")) { printf("tig version %s\n", TIG_VERSION); - return FALSE; + return REQ_NONE; } else if (!strcmp(opt, "-h") || !strcmp(opt, "--help")) { printf("%s\n", usage); - return FALSE; + return REQ_NONE; } opt_cmd[buf_size++] = ' '; @@ -545,7 +551,7 @@ parse_options(int argc, char *argv[]) opt_cmd[buf_size] = 0; - return TRUE; + return request; } @@ -612,7 +618,8 @@ LINE(BLAME_ID, "", COLOR_MAGENTA, COLOR_DEFAULT, 0) enum line_type { #define LINE(type, line, fg, bg, attr) \ LINE_##type - LINE_INFO + LINE_INFO, + LINE_NONE #undef LINE }; @@ -770,6 +777,7 @@ static struct keybinding default_keybindings[] = { { ':', REQ_PROMPT }, { 'u', REQ_STATUS_UPDATE }, { 'M', REQ_STATUS_MERGE }, + { '@', REQ_STAGE_NEXT }, { ',', REQ_TREE_PARENT }, { 'e', REQ_EDIT }, @@ -955,22 +963,23 @@ static size_t run_requests; static enum request add_run_request(enum keymap keymap, int key, int argc, char **argv) { - struct run_request *tmp; - struct run_request req = { keymap, key }; + struct run_request *req; + char cmd[SIZEOF_STR]; size_t bufpos; for (bufpos = 0; argc > 0; argc--, argv++) - if (!string_format_from(req.cmd, &bufpos, "%s ", *argv)) + if (!string_format_from(cmd, &bufpos, "%s ", *argv)) return REQ_NONE; - req.cmd[bufpos - 1] = 0; - - tmp = realloc(run_request, (run_requests + 1) * sizeof(*run_request)); - if (!tmp) + req = realloc(run_request, (run_requests + 1) * sizeof(*run_request)); + if (!req) return REQ_NONE; - run_request = tmp; - run_request[run_requests++] = req; + run_request = req; + req = &run_request[run_requests++]; + string_copy(req->cmd, cmd); + req->keymap = keymap; + req->key = key; return REQ_NONE + run_requests; } @@ -1088,6 +1097,14 @@ static bool parse_bool(const char *s) !strcmp(s, "yes")) ? TRUE : FALSE; } +static int +parse_int(const char *s, int default_value, int min, int max) +{ + int value = atoi(s); + + return (value < min || value > max) ? default_value : value; +} + /* Wants: name = value */ static int option_set_command(int argc, char *argv[]) @@ -1133,12 +1150,17 @@ option_set_command(int argc, char *argv[]) } if (!strcmp(argv[0], "line-number-interval")) { - opt_num_interval = atoi(argv[2]); + opt_num_interval = parse_int(argv[2], opt_num_interval, 1, 1024); + return OK; + } + + if (!strcmp(argv[0], "author-width")) { + opt_author_cols = parse_int(argv[2], opt_author_cols, 0, 1024); return OK; } if (!strcmp(argv[0], "tab-size")) { - opt_tab_size = atoi(argv[2]); + opt_tab_size = parse_int(argv[2], opt_tab_size, 1, 1024); return OK; } @@ -1397,6 +1419,11 @@ struct view { size_t line_size; /* Total number of used lines */ unsigned int digits; /* Number of digits in the lines member. */ + /* Drawing */ + struct line *curline; /* Line currently being drawn. */ + enum line_type curtype; /* Attribute currently used for drawing. */ + unsigned long col; /* Column when drawing. */ + /* Loading */ FILE *pipe; time_t start_time; @@ -1410,7 +1437,7 @@ struct view_ops { /* Read one line; updates view->line. */ 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); + bool (*draw)(struct view *view, struct line *line, unsigned int lineno); /* 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. */ @@ -1459,114 +1486,173 @@ static struct view views[] = { enum line_graphic { - LINE_GRAPHIC_VLINE, + LINE_GRAPHIC_VLINE }; static int line_graphics[] = { /* LINE_GRAPHIC_VLINE: */ '|' }; +static inline void +set_view_attr(struct view *view, enum line_type type) +{ + if (!view->curline->selected && view->curtype != type) { + wattrset(view->win, get_line_attr(type)); + wchgat(view->win, -1, 0, type, NULL); + view->curtype = type; + } +} + static int -draw_text(struct view *view, const char *string, int max_len, - bool use_tilde, bool selected) +draw_chars(struct view *view, enum line_type type, const char *string, + int max_len, bool use_tilde) { int len = 0; + int col = 0; int trimmed = FALSE; if (max_len <= 0) return 0; if (opt_utf8) { - len = utf8_length(string, max_len, &trimmed, use_tilde); + len = utf8_length(string, &col, max_len, &trimmed, use_tilde); } else { - len = strlen(string); + col = len = strlen(string); if (len > max_len) { if (use_tilde) { max_len -= 1; } - len = max_len; + col = len = max_len; trimmed = TRUE; } } + set_view_attr(view, type); waddnstr(view->win, string, len); if (trimmed && use_tilde) { - if (!selected) - wattrset(view->win, get_line_attr(LINE_DELIMITER)); + set_view_attr(view, LINE_DELIMITER); waddch(view->win, '~'); - len++; + col++; } - return len; + return col; } static int -draw_lineno(struct view *view, unsigned int lineno, int max, bool selected) +draw_space(struct view *view, enum line_type type, int max, int spaces) +{ + static char space[] = " "; + int col = 0; + + spaces = MIN(max, spaces); + + while (spaces > 0) { + int len = MIN(spaces, sizeof(space) - 1); + + col += draw_chars(view, type, space, spaces, FALSE); + spaces -= len; + } + + return col; +} + +static bool +draw_lineno(struct view *view, unsigned int lineno) { - static char fmt[] = "%1ld"; - char number[10] = " "; + char number[10]; int digits3 = view->digits < 3 ? 3 : view->digits; int max_number = MIN(digits3, STRING_SIZE(number)); - bool showtrimmed = FALSE; + int max = view->width - view->col; int col; + if (max < max_number) + max_number = max; + lineno += view->offset + 1; if (lineno == 1 || (lineno % opt_num_interval) == 0) { + static char fmt[] = "%1ld"; + if (view->digits <= 9) fmt[1] = '0' + digits3; if (!string_format(number, fmt, lineno)) number[0] = 0; - showtrimmed = TRUE; + col = draw_chars(view, LINE_LINE_NUMBER, number, max_number, TRUE); + } else { + col = draw_space(view, LINE_LINE_NUMBER, max_number, max_number); } - if (max < max_number) - max_number = max; - - if (!selected) - wattrset(view->win, get_line_attr(LINE_LINE_NUMBER)); - col = draw_text(view, number, max_number, showtrimmed, selected); if (col < max) { - if (!selected) - wattrset(view->win, A_NORMAL); + set_view_attr(view, LINE_DEFAULT); waddch(view->win, line_graphics[LINE_GRAPHIC_VLINE]); col++; } - if (col < max) { + + if (col < max) + col += draw_space(view, LINE_DEFAULT, max - col, 1); + view->col += col; + + return view->width - view->col <= 0; +} + +static bool +draw_text(struct view *view, enum line_type type, const char *string, bool trim) +{ + view->col += draw_chars(view, type, string, view->width - view->col, trim); + return view->width - view->col <= 0; +} + +static bool +draw_graphic(struct view *view, enum line_type type, chtype graphic[], size_t size) +{ + int max = view->width - view->col; + int i; + + if (max < size) + size = max; + + set_view_attr(view, type); + /* Using waddch() instead of waddnstr() ensures that + * they'll be rendered correctly for the cursor line. */ + for (i = 0; i < size; i++) + waddch(view->win, graphic[i]); + + view->col += size; + if (size < max) { waddch(view->win, ' '); - col++; + view->col++; } - return col; + return view->width - view->col <= 0; } -static int -draw_date(struct view *view, struct tm *time, int max, bool selected) +static bool +draw_field(struct view *view, enum line_type type, char *text, int len, bool trim) { - char buf[DATE_COLS]; + int max = MIN(view->width - view->col, len); int col; + + if (text) + col = draw_chars(view, type, text, max - 1, trim); + else + col = draw_space(view, type, max - 1, max - 1); + + view->col += col + draw_space(view, LINE_DEFAULT, max - col, max - col); + return view->width - view->col <= 0; +} + +static bool +draw_date(struct view *view, struct tm *time) +{ + char buf[DATE_COLS]; + char *date; int timelen = 0; - if (max > DATE_COLS) - max = DATE_COLS; if (time) timelen = strftime(buf, sizeof(buf), DATE_FORMAT, time); - if (!timelen) { - memset(buf, ' ', sizeof(buf) - 1); - buf[sizeof(buf) - 1] = 0; - } - - if (!selected) - wattrset(view->win, get_line_attr(LINE_DATE)); - col = draw_text(view, buf, max, FALSE, selected); - if (col < max) { - if (!selected) - wattrset(view->win, get_line_attr(LINE_DEFAULT)); - waddch(view->win, ' '); - col++; - } + date = timelen ? buf : NULL; - return col; + return draw_field(view, LINE_DATE, date, DATE_COLS, FALSE); } static bool @@ -1584,19 +1670,21 @@ draw_view_line(struct view *view, unsigned int lineno) line = &view->line[view->offset + lineno]; wmove(view->win, lineno, 0); + view->col = 0; + view->curline = line; + view->curtype = LINE_NONE; + line->selected = FALSE; if (selected) { + set_view_attr(view, LINE_CURSOR); line->selected = TRUE; view->ops->select(view, line); - wchgat(view->win, -1, 0, LINE_CURSOR, NULL); - wattrset(view->win, get_line_attr(LINE_CURSOR)); } else if (line->selected) { - line->selected = FALSE; wclrtoeol(view->win); } scrollok(view->win, FALSE); - draw_ok = view->ops->draw(view, line, lineno, selected); + draw_ok = view->ops->draw(view, line, lineno); scrollok(view->win, TRUE); return draw_ok; @@ -2085,10 +2173,13 @@ search_view(struct view *view, enum request request) */ static void -end_update(struct view *view) +end_update(struct view *view, bool force) { if (!view->pipe) return; + while (!view->ops->read(view, NULL)) + if (!force) + return; set_nonblocking_input(FALSE); if (view->pipe == stdin) fclose(view->pipe); @@ -2100,9 +2191,6 @@ end_update(struct view *view) static bool begin_update(struct view *view) { - if (view->pipe) - end_update(view); - if (opt_cmd[0]) { string_copy(view->cmd, opt_cmd); opt_cmd[0] = 0; @@ -2306,21 +2394,18 @@ update_view(struct view *view) check_pipe: if (ferror(view->pipe)) { report("Failed to read: %s", strerror(errno)); - goto end; + end_update(view, TRUE); } else if (feof(view->pipe)) { report(""); - goto end; + end_update(view, FALSE); } return TRUE; alloc_error: report("Allocation failure"); - -end: - if (view->ops->read(view, NULL)) - end_update(view); + end_update(view, TRUE); return FALSE; } @@ -2355,7 +2440,7 @@ enum open_flags { OPEN_SPLIT = 1, /* Split current view. */ OPEN_BACKGROUNDED = 2, /* Backgrounded. */ OPEN_RELOAD = 4, /* Reload view even if it is the current. */ - OPEN_NOMAXIMIZE = 8, /* Do not maximize the current view. */ + OPEN_NOMAXIMIZE = 8 /* Do not maximize the current view. */ }; static void @@ -2396,6 +2481,9 @@ open_view(struct view *prev, enum request request, enum open_flags flags) (nviews == 1 && base_view != display[0])) resize_display(); + if (view->pipe) + end_update(view, TRUE); + if (view->ops->open) { if (!view->ops->open(view)) { report("Failed to load %s view", view->name); @@ -2717,11 +2805,6 @@ view_driver(struct view *view, enum request request) redraw_display(); break; - case REQ_PROMPT: - /* Always reload^Wrerun commands from the prompt. */ - open_view(view, opt_request, OPEN_RELOAD); - break; - case REQ_SEARCH: case REQ_SEARCH_BACK: search_view(view, request); @@ -2737,7 +2820,7 @@ view_driver(struct view *view, enum request request) view = &views[i]; if (view->pipe) report("Stopped loading the %s view", view->name), - end_update(view); + end_update(view, TRUE); } break; @@ -2756,12 +2839,10 @@ view_driver(struct view *view, enum request request) report("Nothing to edit"); break; - case REQ_ENTER: report("Nothing to enter"); break; - case REQ_VIEW_CLOSE: /* XXX: Mark closed views by letting view->parent point to the * view itself. Parents to closed view should never be @@ -2795,21 +2876,14 @@ view_driver(struct view *view, enum request request) */ static bool -pager_draw(struct view *view, struct line *line, unsigned int lineno, bool selected) +pager_draw(struct view *view, struct line *line, unsigned int lineno) { char *text = line->data; - int col = 0; - if (opt_line_number) { - col += draw_lineno(view, lineno, view->width, selected); - if (col >= view->width) - return TRUE; - } - - if (!selected) - wattrset(view->win, get_line_attr(line->type)); + if (opt_line_number && draw_lineno(view, lineno)) + return TRUE; - draw_text(view, text, view->width - col, TRUE, selected); + draw_text(view, line->type, text, TRUE); return TRUE; } @@ -3512,9 +3586,6 @@ blame_read_file(struct view *view, char *line) size_t linelen = strlen(line); struct blame *blame = malloc(sizeof(*blame) + linelen); - if (!line) - return FALSE; - blame->commit = NULL; strncpy(blame->text, line, linelen); blame->text[linelen] = 0; @@ -3593,51 +3664,32 @@ blame_read(struct view *view, char *line) } static bool -blame_draw(struct view *view, struct line *line, unsigned int lineno, bool selected) +blame_draw(struct view *view, struct line *line, unsigned int lineno) { struct blame *blame = line->data; - int col = 0; - - if (opt_date) { - struct tm *time = blame->commit && *blame->commit->filename - ? &blame->commit->time : NULL; + struct tm *time = NULL; + char *id = NULL, *author = NULL; - col += draw_date(view, time, view->width, selected); - if (col >= view->width) - return TRUE; + if (blame->commit && *blame->commit->filename) { + id = blame->commit->id; + author = blame->commit->author; + time = &blame->commit->time; } - if (opt_author) { - int max = MIN(AUTHOR_COLS - 1, view->width - col); + if (opt_date && draw_date(view, time)) + return TRUE; - if (!selected) - wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR)); - if (blame->commit) - draw_text(view, blame->commit->author, max, TRUE, selected); - col += AUTHOR_COLS; - if (col >= view->width) - return TRUE; - wmove(view->win, lineno, col); - } + if (opt_author && + draw_field(view, LINE_MAIN_AUTHOR, author, opt_author_cols, TRUE)) + return TRUE; - { - int max = MIN(ID_COLS - 1, view->width - col); - - if (!selected) - wattrset(view->win, get_line_attr(LINE_BLAME_ID)); - if (blame->commit) - draw_text(view, blame->commit->id, max, FALSE, -1); - col += ID_COLS; - if (col >= view->width) - return TRUE; - wmove(view->win, lineno, col); - } + if (draw_field(view, LINE_BLAME_ID, id, ID_COLS, FALSE)) + return TRUE; - col += draw_lineno(view, lineno, view->width - col, selected); - if (col >= view->width) + if (draw_lineno(view, lineno)) return TRUE; - col += draw_text(view, blame->text, view->width - col, TRUE, selected); + draw_text(view, LINE_DEFAULT, blame->text, TRUE); return TRUE; } @@ -3746,6 +3798,8 @@ struct status { static char status_onbranch[SIZEOF_STR]; static struct status stage_status; static enum line_type stage_line_type; +static size_t stage_chunks; +static int *stage_chunk; /* Get fields from the diff line: * :100644 100644 06a5d6ae9eca55be2e0e585a152e6b1336f2b20e 0000000000000000000000000000000000000000 M @@ -3996,46 +4050,36 @@ status_open(struct view *view) } static bool -status_draw(struct view *view, struct line *line, unsigned int lineno, bool selected) +status_draw(struct view *view, struct line *line, unsigned int lineno) { struct status *status = line->data; + enum line_type type; char *text; - int col = 0; - - if (selected) { - /* No attributes. */ - - } else if (line->type == LINE_STAT_HEAD) { - wattrset(view->win, get_line_attr(LINE_STAT_HEAD)); - wchgat(view->win, -1, 0, LINE_STAT_HEAD, NULL); - - } else if (!status && line->type != LINE_STAT_NONE) { - wattrset(view->win, get_line_attr(LINE_STAT_SECTION)); - wchgat(view->win, -1, 0, LINE_STAT_SECTION, NULL); - - } else { - wattrset(view->win, get_line_attr(line->type)); - } if (!status) { switch (line->type) { case LINE_STAT_STAGED: + type = LINE_STAT_SECTION; text = "Changes to be committed:"; break; case LINE_STAT_UNSTAGED: + type = LINE_STAT_SECTION; text = "Changed but not updated:"; break; case LINE_STAT_UNTRACKED: + type = LINE_STAT_SECTION; text = "Untracked files:"; break; case LINE_STAT_NONE: + type = LINE_DEFAULT; text = " (no files)"; break; case LINE_STAT_HEAD: + type = LINE_STAT_HEAD; text = status_onbranch; break; @@ -4043,15 +4087,16 @@ status_draw(struct view *view, struct line *line, unsigned int lineno, bool sele return FALSE; } } else { - char buf[] = { status->status, ' ', ' ', ' ', 0 }; + static char buf[] = { '?', ' ', ' ', ' ', 0 }; - col += draw_text(view, buf, view->width, TRUE, selected); - if (!selected) - wattrset(view->win, A_NORMAL); + buf[0] = status->status; + if (draw_text(view, line->type, buf, TRUE)) + return TRUE; + type = LINE_DEFAULT; text = status->new.name; } - draw_text(view, text, view->width - col, TRUE, selected); + draw_text(view, type, text, TRUE); return TRUE; } @@ -4146,6 +4191,7 @@ status_enter(struct view *view, struct line *line) } stage_line_type = line->type; + stage_chunks = 0; string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.new.name); } @@ -4555,6 +4601,42 @@ stage_update(struct view *view, struct line *line) return TRUE; } +static void +stage_next(struct view *view, struct line *line) +{ + int i; + + if (!stage_chunks) { + static size_t alloc = 0; + int *tmp; + + for (line = view->line; line < view->line + view->lines; line++) { + if (line->type != LINE_DIFF_CHUNK) + continue; + + tmp = realloc_items(stage_chunk, &alloc, + stage_chunks, sizeof(*tmp)); + if (!tmp) { + report("Allocation failure"); + return; + } + + stage_chunk = tmp; + stage_chunk[stage_chunks++] = line - view->line; + } + } + + for (i = 0; i < stage_chunks; i++) { + if (stage_chunk[i] > view->lineno) { + do_scroll_view(view, stage_chunk[i] - view->lineno); + report("Chunk %d of %d", i + 1, stage_chunks); + return; + } + } + + report("No next chunk found"); +} + static enum request stage_request(struct view *view, enum request request, struct line *line) { @@ -4564,6 +4646,15 @@ stage_request(struct view *view, enum request request, struct line *line) return REQ_NONE; break; + case REQ_STAGE_NEXT: + if (stage_line_type == LINE_STAT_UNTRACKED) { + report("File is untracked; press %s to add", + get_key(REQ_STATUS_UPDATE)); + return REQ_NONE; + } + stage_next(view, line); + return REQ_NONE; + case REQ_EDIT: if (!stage_status.new.name[0]) return request; @@ -4830,100 +4921,54 @@ update_rev_graph(struct rev_graph *graph) */ static bool -main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected) +main_draw(struct view *view, struct line *line, unsigned int lineno) { struct commit *commit = line->data; - enum line_type type; - int col = 0; if (!*commit->author) return FALSE; - if (selected) { - type = LINE_CURSOR; - } else { - type = LINE_MAIN_COMMIT; - } - - if (opt_date) { - col += draw_date(view, &commit->time, view->width, selected); - if (col >= view->width) - return TRUE; - } - if (type != LINE_CURSOR) - wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR)); - - if (opt_author) { - int max_len; - - max_len = view->width - col; - if (max_len > AUTHOR_COLS - 1) - max_len = AUTHOR_COLS - 1; - draw_text(view, commit->author, max_len, TRUE, selected); - col += AUTHOR_COLS; - if (col >= view->width) - return TRUE; - } - - if (opt_rev_graph && commit->graph_size) { - size_t graph_size = view->width - col; - size_t i; - - if (type != LINE_CURSOR) - wattrset(view->win, get_line_attr(LINE_MAIN_REVGRAPH)); - wmove(view->win, lineno, col); - if (graph_size > commit->graph_size) - graph_size = commit->graph_size; - /* Using waddch() instead of waddnstr() ensures that - * they'll be rendered correctly for the cursor line. */ - for (i = 0; i < graph_size; i++) - waddch(view->win, commit->graph[i]); + if (opt_date && draw_date(view, &commit->time)) + return TRUE; - col += commit->graph_size + 1; - if (col >= view->width) - return TRUE; - waddch(view->win, ' '); - } - if (type != LINE_CURSOR) - wattrset(view->win, A_NORMAL); + if (opt_author && + draw_field(view, LINE_MAIN_AUTHOR, commit->author, opt_author_cols, TRUE)) + return TRUE; - wmove(view->win, lineno, col); + if (opt_rev_graph && commit->graph_size && + draw_graphic(view, LINE_MAIN_REVGRAPH, commit->graph, commit->graph_size)) + return TRUE; if (opt_show_refs && commit->refs) { size_t i = 0; do { - if (type == LINE_CURSOR) - ; - else if (commit->refs[i]->head) - wattrset(view->win, get_line_attr(LINE_MAIN_HEAD)); + enum line_type type; + + if (commit->refs[i]->head) + type = LINE_MAIN_HEAD; else if (commit->refs[i]->ltag) - wattrset(view->win, get_line_attr(LINE_MAIN_LOCAL_TAG)); + type = LINE_MAIN_LOCAL_TAG; else if (commit->refs[i]->tag) - wattrset(view->win, get_line_attr(LINE_MAIN_TAG)); + type = LINE_MAIN_TAG; else if (commit->refs[i]->tracked) - wattrset(view->win, get_line_attr(LINE_MAIN_TRACKED)); + type = LINE_MAIN_TRACKED; else if (commit->refs[i]->remote) - wattrset(view->win, get_line_attr(LINE_MAIN_REMOTE)); + type = LINE_MAIN_REMOTE; else - wattrset(view->win, get_line_attr(LINE_MAIN_REF)); - - col += draw_text(view, "[", view->width - col, TRUE, selected); - col += draw_text(view, commit->refs[i]->name, view->width - col, - TRUE, selected); - col += draw_text(view, "]", view->width - col, TRUE, selected); - if (type != LINE_CURSOR) - wattrset(view->win, A_NORMAL); - col += draw_text(view, " ", view->width - col, TRUE, selected); - if (col >= view->width) + type = LINE_MAIN_REF; + + if (draw_text(view, type, "[", TRUE) || + draw_text(view, type, commit->refs[i]->name, TRUE) || + draw_text(view, type, "]", TRUE)) + return TRUE; + + if (draw_text(view, LINE_DEFAULT, " ", TRUE)) return TRUE; } while (commit->refs[i++]->next); } - if (type != LINE_CURSOR) - wattrset(view->win, get_line_attr(type)); - - draw_text(view, commit->title, view->width - col, TRUE, selected); + draw_text(view, LINE_DEFAULT, commit->title, TRUE); return TRUE; } @@ -5249,13 +5294,14 @@ utf8_to_unicode(const char *string, size_t length) * * Returns the number of bytes to output from string to satisfy max_width. */ static size_t -utf8_length(const char *string, size_t max_width, int *trimmed, bool reserve) +utf8_length(const char *string, int *width, size_t max_width, int *trimmed, bool reserve) { const char *start = string; const char *end = strchr(string, '\0'); unsigned char last_bytes = 0; - size_t width = 0; + size_t last_ucwidth = 0; + *width = 0; *trimmed = 0; while (string < end) { @@ -5276,17 +5322,20 @@ utf8_length(const char *string, size_t max_width, int *trimmed, bool reserve) break; ucwidth = unicode_width(unicode); - width += ucwidth; - if (width > max_width) { + *width += ucwidth; + if (*width > max_width) { *trimmed = 1; - if (reserve && width - ucwidth == max_width) { + *width -= ucwidth; + if (reserve && *width == max_width) { string -= last_bytes; + *width -= last_ucwidth; } break; } string += bytes; last_bytes = bytes; + last_ucwidth = ucwidth; } return string - start; @@ -5811,11 +5860,12 @@ main(int argc, char *argv[]) if (load_git_config() == ERR) die("Failed to load repo config."); - if (!parse_options(argc, argv)) + request = parse_options(argc, argv); + if (request == REQ_NONE) return 0; /* Require a git repository unless when running in pager mode. */ - if (!opt_git_dir[0] && opt_request != REQ_VIEW_PAGER) + if (!opt_git_dir[0] && request != REQ_VIEW_PAGER) die("Not a git repository"); if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8")) @@ -5833,8 +5883,6 @@ main(int argc, char *argv[]) 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)) { @@ -5865,11 +5913,13 @@ main(int argc, char *argv[]) if (cmd && string_format(opt_cmd, "git %s", cmd)) { if (strncmp(cmd, "show", 4) && isspace(cmd[4])) { - opt_request = REQ_VIEW_DIFF; + request = REQ_VIEW_DIFF; } else { - opt_request = REQ_VIEW_PAGER; + request = REQ_VIEW_PAGER; } - break; + + /* Always reload^Wrerun commands from the prompt. */ + open_view(view, request, OPEN_RELOAD); } request = REQ_NONE;