X-Git-Url: https://git.distorted.org.uk/~mdw/tig/blobdiff_plain/8eb627705bcfb8aa2079d3c553621aa024d79819..5f7b975a9f65386462b97de55f519fa3e4640cbf:/tig.c diff --git a/tig.c b/tig.c index 859681c..1f36682 100644 --- a/tig.c +++ b/tig.c @@ -47,6 +47,7 @@ static void load_help_page(void); #define SIZEOF_REF 256 /* Size of symbolic or SHA1 ID. */ #define SIZEOF_CMD 1024 /* Size of command buffer. */ +#define SIZEOF_REVGRAPH 19 /* Size of revision ancestry graphics. */ /* This color name can be used to refer to the default term colors. */ #define COLOR_DEFAULT (-1) @@ -255,7 +256,8 @@ sq_quote(char buf[SIZEOF_CMD], size_t bufsize, const char *src) REQ_(SCREEN_RESIZE, "Resize the screen"), \ REQ_(SHOW_VERSION, "Show version information"), \ REQ_(STOP_LOADING, "Stop all loading views"), \ - REQ_(TOGGLE_LINENO, "Toggle line numbers"), + REQ_(TOGGLE_LINENO, "Toggle line numbers"), \ + REQ_(TOGGLE_REV_GRAPH, "Toggle revision graph visualization"), /* User action requests. */ @@ -309,6 +311,7 @@ VERSION " (" __DATE__ ")\n" /* Option and state variables. */ static bool opt_line_number = FALSE; +static bool opt_rev_graph = TRUE; static int opt_num_interval = NUMBER_INTERVAL; static int opt_tab_size = TABSIZE; static enum request opt_request = REQ_VIEW_MAIN; @@ -317,6 +320,49 @@ static char opt_encoding[20] = ""; static bool opt_utf8 = TRUE; static FILE *opt_pipe = NULL; +enum option_type { + OPT_NONE, + OPT_INT, +}; + +static bool +check_option(char *opt, char short_name, char *name, enum option_type type, ...) +{ + va_list args; + char *value = ""; + int *number; + + if (opt[0] != '-') + return FALSE; + + if (opt[1] == '-') { + int namelen = strlen(name); + + opt += 2; + + if (strncmp(opt, name, namelen)) + return FALSE; + + if (opt[namelen] == '=') + value = opt + namelen + 1; + + } else { + if (!short_name || opt[1] != short_name) + return FALSE; + value = opt + 2; + } + + va_start(args, type); + if (type == OPT_INT) { + number = va_arg(args, int *); + if (isdigit(*value)) + *number = atoi(value); + } + va_end(args); + + return TRUE; +} + /* Returns the index of log or diff command or -1 to exit. */ static bool parse_options(int argc, char *argv[]) @@ -336,48 +382,22 @@ parse_options(int argc, char *argv[]) continue; } - if (!strncmp(opt, "-n", 2) || - !strncmp(opt, "--line-number", 13)) { - char *num = opt; - - if (opt[1] == 'n') { - num = opt + 2; - - } else if (opt[STRING_SIZE("--line-number")] == '=') { - num = opt + STRING_SIZE("--line-number="); - } - - if (isdigit(*num)) - opt_num_interval = atoi(num); - + if (check_option(opt, 'n', "line-number", OPT_INT, &opt_num_interval)) { opt_line_number = TRUE; continue; } - if (!strncmp(opt, "-b", 2) || - !strncmp(opt, "--tab-size", 10)) { - char *num = opt; - - if (opt[1] == 'b') { - num = opt + 2; - - } else if (opt[STRING_SIZE("--tab-size")] == '=') { - num = opt + STRING_SIZE("--tab-size="); - } - - if (isdigit(*num)) - opt_tab_size = MIN(atoi(num), TABSIZE); + if (check_option(opt, 'b', "tab-size", OPT_INT, &opt_tab_size)) { + opt_tab_size = MIN(opt_tab_size, TABSIZE); continue; } - if (!strcmp(opt, "-v") || - !strcmp(opt, "--version")) { + if (check_option(opt, 'v', "version", OPT_NONE)) { printf("tig version %s\n", VERSION); return FALSE; } - if (!strcmp(opt, "-h") || - !strcmp(opt, "--help")) { + if (check_option(opt, 'h', "help", OPT_NONE)) { printf(usage); return FALSE; } @@ -435,29 +455,9 @@ parse_options(int argc, char *argv[]) } -static struct int_map color_map[] = { -#define COLOR_MAP(name) { #name, STRING_SIZE(#name), COLOR_##name } - COLOR_MAP(DEFAULT), - COLOR_MAP(BLACK), - COLOR_MAP(BLUE), - COLOR_MAP(CYAN), - COLOR_MAP(GREEN), - COLOR_MAP(MAGENTA), - COLOR_MAP(RED), - COLOR_MAP(WHITE), - COLOR_MAP(YELLOW), -}; - -static struct int_map attr_map[] = { -#define ATTR_MAP(name) { #name, STRING_SIZE(#name), A_##name } - ATTR_MAP(NORMAL), - ATTR_MAP(BLINK), - ATTR_MAP(BOLD), - ATTR_MAP(DIM), - ATTR_MAP(REVERSE), - ATTR_MAP(STANDOUT), - ATTR_MAP(UNDERLINE), -}; +/* + * Line-oriented content detection. + */ #define LINE_INFO \ LINE(DIFF_HEADER, "diff --git ", COLOR_YELLOW, COLOR_DEFAULT, 0), \ @@ -499,11 +499,6 @@ LINE(MAIN_DELIM, "", COLOR_MAGENTA, COLOR_DEFAULT, 0), \ LINE(MAIN_TAG, "", COLOR_MAGENTA, COLOR_DEFAULT, A_BOLD), \ LINE(MAIN_REF, "", COLOR_CYAN, COLOR_DEFAULT, A_BOLD), \ - -/* - * Line-oriented content detection. - */ - enum line_type { #define LINE(type, line, fg, bg, attr) \ LINE_##type @@ -603,9 +598,33 @@ struct line { * User config file handling. */ +static struct int_map color_map[] = { +#define COLOR_MAP(name) { #name, STRING_SIZE(#name), COLOR_##name } + COLOR_MAP(DEFAULT), + COLOR_MAP(BLACK), + COLOR_MAP(BLUE), + COLOR_MAP(CYAN), + COLOR_MAP(GREEN), + COLOR_MAP(MAGENTA), + COLOR_MAP(RED), + COLOR_MAP(WHITE), + COLOR_MAP(YELLOW), +}; + #define set_color(color, name, namelen) \ set_from_int_map(color_map, ARRAY_SIZE(color_map), color, name, namelen) +static struct int_map attr_map[] = { +#define ATTR_MAP(name) { #name, STRING_SIZE(#name), A_##name } + ATTR_MAP(NORMAL), + ATTR_MAP(BLINK), + ATTR_MAP(BOLD), + ATTR_MAP(DIM), + ATTR_MAP(REVERSE), + ATTR_MAP(STANDOUT), + ATTR_MAP(UNDERLINE), +}; + #define set_attribute(attr, name, namelen) \ set_from_int_map(attr_map, ARRAY_SIZE(attr_map), attr, name, namelen) @@ -848,8 +867,9 @@ update_view_title(struct view *view) wprintw(view->title, "[%s]", view->name); if (view->lines || view->pipe) { + unsigned int view_lines = view->offset + view->height; unsigned int lines = view->lines - ? (view->lineno + 1) * 100 / view->lines + ? MIN(view_lines, view->lines) * 100 / view->lines : 0; wprintw(view->title, " - %s %d of %d (%d%%)", @@ -1324,8 +1344,11 @@ open_view(struct view *prev, enum request request, enum open_flags flags) return; } - if ((reload || strcmp(view->vid, view->id)) && - !begin_update(view)) { + if (view == VIEW(REQ_VIEW_HELP)) { + load_help_page(); + + } else if ((reload || strcmp(view->vid, view->id)) && + !begin_update(view)) { report("Failed to load %s view", view->name); return; } @@ -1365,9 +1388,6 @@ open_view(struct view *prev, enum request request, enum open_flags flags) view->parent = prev; } - if (view == VIEW(REQ_VIEW_HELP)) - load_help_page(); - if (view->pipe && view->lines == 0) { /* Clear the old view and let the incremental updating refill * the screen. */ @@ -1465,6 +1485,11 @@ view_driver(struct view *view, enum request request) redraw_display(); break; + case REQ_TOGGLE_REV_GRAPH: + opt_rev_graph = !opt_rev_graph; + redraw_display(); + break; + case REQ_PROMPT: /* Always reload^Wrerun commands from the prompt. */ open_view(view, opt_request, OPEN_RELOAD); @@ -1696,11 +1721,13 @@ static struct view_ops pager_ops = { */ struct commit { - char id[41]; /* SHA1 ID. */ - char title[75]; /* The first line of the commit message. */ - char author[75]; /* The author of the commit. */ - struct tm time; /* Date from the author ident. */ - struct ref **refs; /* Repository references; tags & branch heads. */ + char id[41]; /* SHA1 ID. */ + char title[75]; /* First line of the commit message. */ + char author[75]; /* Author of the commit. */ + struct tm time; /* Date from the author ident. */ + struct ref **refs; /* Repository references. */ + chtype graph[SIZEOF_REVGRAPH]; /* Ancestry chain graphics. */ + size_t graph_size; /* The width of the graph array. */ }; static bool @@ -1763,9 +1790,19 @@ main_draw(struct view *view, struct line *line, unsigned int lineno) if (type != LINE_CURSOR) wattrset(view->win, A_NORMAL); - mvwaddch(view->win, lineno, col, ACS_LTEE); - wmove(view->win, lineno, col + 2); - col += 2; + if (opt_rev_graph && commit->graph_size) { + size_t i; + + wmove(view->win, lineno, col); + /* Using waddch() instead of waddnstr() ensures that + * they'll be rendered correctly for the cursor line. */ + for (i = 0; i < commit->graph_size; i++) + waddch(view->win, commit->graph[i]); + + col += commit->graph_size + 1; + } + + wmove(view->win, lineno, col); if (commit->refs) { size_t i = 0; @@ -1821,6 +1858,7 @@ main_read(struct view *view, char *line) view->line[view->lines++].data = commit; string_copy(commit->id, line); commit->refs = get_refs(commit->id); + commit->graph[commit->graph_size++] = ACS_LTEE; break; case LINE_AUTHOR: @@ -1955,6 +1993,7 @@ static struct keymap keymap[] = { { 'v', REQ_SHOW_VERSION }, { 'r', REQ_SCREEN_REDRAW }, { 'n', REQ_TOGGLE_LINENO }, + { 'g', REQ_TOGGLE_REV_GRAPH}, { ':', REQ_PROMPT }, /* wgetch() with nodelay() enabled returns ERR when there's no input. */ @@ -2403,25 +2442,22 @@ read_ref(char *id, int idlen, char *name, int namelen) { struct ref *ref; bool tag = FALSE; - bool tag_commit = FALSE; - /* Commits referenced by tags has "^{}" appended. */ - if (name[namelen - 1] == '}') { + if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) { + /* Commits referenced by tags has "^{}" appended. */ + if (name[namelen - 1] != '}') + return OK; + while (namelen > 0 && name[namelen] != '^') namelen--; - if (namelen > 0) - tag_commit = TRUE; - name[namelen] = 0; - } - if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) { - if (!tag_commit) - return OK; - name += STRING_SIZE("refs/tags/"); tag = TRUE; + namelen -= STRING_SIZE("refs/tags/"); + name += STRING_SIZE("refs/tags/"); } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) { - name += STRING_SIZE("refs/heads/"); + namelen -= STRING_SIZE("refs/heads/"); + name += STRING_SIZE("refs/heads/"); } else if (!strcmp(name, "HEAD")) { return OK; @@ -2432,10 +2468,12 @@ read_ref(char *id, int idlen, char *name, int namelen) return ERR; ref = &refs[refs_size++]; - ref->name = strdup(name); + ref->name = malloc(namelen + 1); if (!ref->name) return ERR; + strncpy(ref->name, name, namelen); + ref->name[namelen] = 0; ref->tag = tag; string_copy(ref->id, id); @@ -2454,9 +2492,8 @@ load_refs(void) static int read_repo_config_option(char *name, int namelen, char *value, int valuelen) { - if (!strcmp(name, "i18n.commitencoding")) { + if (!strcmp(name, "i18n.commitencoding")) string_copy(opt_encoding, value); - } return OK; }