X-Git-Url: https://git.distorted.org.uk/~mdw/tig/blobdiff_plain/4ea4ce91c88b54bcf23dfc7d5edaee25bdae7e2a..4887d44e87f6ab759e32d30511d78e3f99d0fbcc:/tig.c diff --git a/tig.c b/tig.c index 11eb06d..b0ce18f 100644 --- a/tig.c +++ b/tig.c @@ -59,6 +59,7 @@ static size_t utf8_length(const char *string, size_t max_width, int *coloffset, #define SIZEOF_STR 1024 /* Default string size. */ #define SIZEOF_REF 256 /* Size of symbolic or SHA1 ID. */ +#define SIZEOF_REV 41 /* Holds a SHA-1 and an ending NUL */ #define SIZEOF_REVGRAPH 19 /* Size of revision ancestry graphics. */ /* This color name can be used to refer to the default term colors. */ @@ -109,7 +110,7 @@ static size_t utf8_length(const char *string, size_t max_width, int *coloffset, struct ref { char *name; /* Ref name; tag or head names are shortened. */ - char id[41]; /* Commit SHA1 ID */ + char id[SIZEOF_REV]; /* Commit SHA1 ID */ unsigned int tag:1; /* Is it a tag? */ unsigned int next:1; /* For ref lists: are there more refs? */ }; @@ -145,16 +146,22 @@ set_from_int_map(struct int_map *map, size_t map_size, */ static inline void -string_ncopy(char *dst, const char *src, size_t dstlen) +string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen) { - strncpy(dst, src, dstlen - 1); - dst[dstlen - 1] = 0; + if (srclen > dstlen - 1) + srclen = dstlen - 1; + strncpy(dst, src, srclen); + dst[srclen] = 0; } -/* Shorthand for safely copying into a fixed buffer. */ +/* Shorthands for safely copying into a fixed buffer. */ + #define string_copy(dst, src) \ - string_ncopy(dst, src, sizeof(dst)) + string_ncopy_do(dst, sizeof(dst), src, sizeof(dst)) + +#define string_ncopy(dst, src, srclen) \ + string_ncopy_do(dst, sizeof(dst), src, srclen) static char * chomp_string(char *name) @@ -559,6 +566,7 @@ LINE(TREE, "tree ", COLOR_BLUE, COLOR_DEFAULT, 0), \ LINE(AUTHOR, "author ", COLOR_CYAN, COLOR_DEFAULT, 0), \ LINE(COMMITTER, "committer ", COLOR_MAGENTA, COLOR_DEFAULT, 0), \ LINE(SIGNOFF, " Signed-off-by", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(ACKED, " Acked-by", COLOR_YELLOW, COLOR_DEFAULT, 0), \ LINE(DEFAULT, "", COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL), \ LINE(CURSOR, "", COLOR_WHITE, COLOR_GREEN, A_BOLD), \ LINE(STATUS, "", COLOR_GREEN, COLOR_DEFAULT, 0), \ @@ -655,6 +663,10 @@ init_colors(void) struct line { enum line_type type; + + /* State flags */ + unsigned int selected:1; + void *data; /* User data */ }; @@ -675,7 +687,7 @@ static struct keybinding default_keybindings[] = { { 'd', REQ_VIEW_DIFF }, { 'l', REQ_VIEW_LOG }, { 't', REQ_VIEW_TREE }, - { 'b', REQ_VIEW_BLOB }, + { 'f', REQ_VIEW_BLOB }, { 'p', REQ_VIEW_PAGER }, { 'h', REQ_VIEW_HELP }, @@ -1186,7 +1198,7 @@ struct view { /* Searching */ char grep[SIZEOF_STR]; /* Search string */ - regex_t regex; /* Pre-compiled regex */ + regex_t *regex; /* Pre-compiled regex */ /* If non-NULL, points to the view that opened this view. If this view * is closed tig will switch back to the parent view. */ @@ -1207,13 +1219,15 @@ struct view_ops { /* What type of content being displayed. Used in the title bar. */ const char *type; /* Draw one line; @lineno must be < view->height. */ - bool (*draw)(struct view *view, struct line *line, unsigned int lineno); + bool (*draw)(struct view *view, struct line *line, unsigned int lineno, bool selected); /* Read one line; updates view->line. */ bool (*read)(struct view *view, char *data); /* Depending on view, change display based on current line. */ bool (*enter)(struct view *view, struct line *line); /* Search for regex in a line. */ bool (*grep)(struct view *view, struct line *line); + /* Select line */ + void (*select)(struct view *view, struct line *line); }; static struct view_ops pager_ops; @@ -1249,12 +1263,31 @@ static struct view views[] = { static bool draw_view_line(struct view *view, unsigned int lineno) { + struct line *line; + bool selected = (view->offset + lineno == view->lineno); + bool draw_ok; + assert(view_is_displayed(view)); if (view->offset + lineno >= view->lines) return FALSE; - return view->ops->draw(view, &view->line[view->offset + lineno], lineno); + line = &view->line[view->offset + lineno]; + + if (selected) { + line->selected = TRUE; + view->ops->select(view, line); + } else if (line->selected) { + line->selected = FALSE; + wmove(view->win, lineno, 0); + wclrtoeol(view->win); + } + + scrollok(view->win, FALSE); + draw_ok = view->ops->draw(view, line, lineno, selected); + scrollok(view->win, TRUE); + + return draw_ok; } static void @@ -1386,10 +1419,8 @@ redraw_display(void) } static void -update_display_cursor(void) +update_display_cursor(struct view *view) { - struct view *view = display[current_view]; - /* Move the cursor to the right-most column of the cursor line. * * XXX: This could turn out to be a bit expensive, but it ensures that @@ -1406,9 +1437,9 @@ update_display_cursor(void) /* Scrolling backend */ static void -do_scroll_view(struct view *view, int lines, bool redraw) +do_scroll_view(struct view *view, int lines) { - assert(view_is_displayed(view)); + bool redraw_current_line = FALSE; /* The rendering expects the new offset. */ view->offset += lines; @@ -1416,6 +1447,17 @@ do_scroll_view(struct view *view, int lines, bool redraw) assert(0 <= view->offset && view->offset < view->lines); assert(lines); + /* Move current line into the view. */ + if (view->lineno < view->offset) { + view->lineno = view->offset; + redraw_current_line = TRUE; + } else if (view->lineno >= view->offset + view->height) { + view->lineno = view->offset + view->height - 1; + redraw_current_line = TRUE; + } + + assert(view->offset <= view->lineno && view->lineno < view->lines); + /* Redraw the whole screen if scrolling is pointless. */ if (view->height < ABS(lines)) { redraw_view(view); @@ -1430,29 +1472,11 @@ do_scroll_view(struct view *view, int lines, bool redraw) if (!draw_view_line(view, line)) break; } - } - - /* Move current line into the view. */ - if (view->lineno < view->offset) { - view->lineno = view->offset; - draw_view_line(view, 0); - } else if (view->lineno >= view->offset + view->height) { - if (view->lineno == view->offset + view->height) { - /* Clear the hidden line so it doesn't show if the view - * is scrolled up. */ - wmove(view->win, view->height, 0); - wclrtoeol(view->win); - } - view->lineno = view->offset + view->height - 1; - draw_view_line(view, view->lineno - view->offset); + if (redraw_current_line) + draw_view_line(view, view->lineno - view->offset); } - assert(view->offset <= view->lineno && view->lineno < view->lines); - - if (!redraw) - return; - redrawwin(view->win); wrefresh(view->win); report(""); @@ -1464,6 +1488,8 @@ scroll_view(struct view *view, enum request request) { int lines = 1; + assert(view_is_displayed(view)); + switch (request) { case REQ_SCROLL_PAGE_DOWN: lines = view->height; @@ -1495,13 +1521,14 @@ scroll_view(struct view *view, enum request request) die("request %d not handled in switch", request); } - do_scroll_view(view, lines, TRUE); + do_scroll_view(view, lines); } /* Cursor moving */ static void -move_view(struct view *view, enum request request, bool redraw) +move_view(struct view *view, enum request request) { + int scroll_steps = 0; int steps; switch (request) { @@ -1548,39 +1575,40 @@ move_view(struct view *view, enum request request, bool redraw) view->lineno += steps; assert(0 <= view->lineno && view->lineno < view->lines); - /* Repaint the old "current" line if we be scrolling */ - if (ABS(steps) < view->height) { - int prev_lineno = view->lineno - steps - view->offset; - - wmove(view->win, prev_lineno, 0); - wclrtoeol(view->win); - draw_view_line(view, prev_lineno); - } - /* Check whether the view needs to be scrolled */ if (view->lineno < view->offset || view->lineno >= view->offset + view->height) { + scroll_steps = steps; if (steps < 0 && -steps > view->offset) { - steps = -view->offset; + scroll_steps = -view->offset; } else if (steps > 0) { if (view->lineno == view->lines - 1 && view->lines > view->height) { - steps = view->lines - view->offset - 1; - if (steps >= view->height) - steps -= view->height - 1; + scroll_steps = view->lines - view->offset - 1; + if (scroll_steps >= view->height) + scroll_steps -= view->height - 1; } } + } - do_scroll_view(view, steps, redraw); + if (!view_is_displayed(view)) { + view->offset += steps; + view->ops->select(view, &view->line[view->lineno]); return; } - /* Draw the current line */ - draw_view_line(view, view->lineno - view->offset); + /* Repaint the old "current" line if we be scrolling */ + if (ABS(steps) < view->height) + draw_view_line(view, view->lineno - steps - view->offset); - if (!redraw) + if (scroll_steps) { + do_scroll_view(view, scroll_steps); return; + } + + /* Draw the current line */ + draw_view_line(view, view->lineno - view->offset); redrawwin(view->win); wrefresh(view->win); @@ -1592,7 +1620,7 @@ move_view(struct view *view, enum request request, bool redraw) * Searching */ -static void search_view(struct view *view, enum request request, const char *search); +static void search_view(struct view *view, enum request request); static bool find_next_line(struct view *view, unsigned long lineno, struct line *line) @@ -1611,9 +1639,6 @@ find_next_line(struct view *view, unsigned long lineno, struct line *line) unsigned long old_lineno = view->lineno - view->offset; view->lineno = lineno; - - wmove(view->win, old_lineno, 0); - wclrtoeol(view->win); draw_view_line(view, old_lineno); draw_view_line(view, view->lineno - view->offset); @@ -1635,7 +1660,7 @@ find_next(struct view *view, enum request request) if (!*opt_search) report("No previous search"); else - search_view(view, request, opt_search); + search_view(view, request); return; } @@ -1670,25 +1695,29 @@ find_next(struct view *view, enum request request) } static void -search_view(struct view *view, enum request request, const char *search) +search_view(struct view *view, enum request request) { int regex_err; - if (*view->grep) { - regfree(&view->regex); + if (view->regex) { + regfree(view->regex); *view->grep = 0; + } else { + view->regex = calloc(1, sizeof(*view->regex)); + if (!view->regex) + return; } - regex_err = regcomp(&view->regex, search, REG_EXTENDED); + regex_err = regcomp(view->regex, opt_search, REG_EXTENDED); if (regex_err != 0) { char buf[SIZEOF_STR] = "unknown error"; - regerror(regex_err, &view->regex, buf, sizeof(buf)); + regerror(regex_err, view->regex, buf, sizeof(buf)); report("Search failed: %s", buf); return; } - string_copy(view->grep, search); + string_copy(view->grep, opt_search); find_next(view, request); } @@ -1994,7 +2023,7 @@ open_view(struct view *prev, enum request request, enum open_flags flags) /* Scroll the view that was split if the current line is * outside the new limited view. */ - do_scroll_view(prev, lines, TRUE); + do_scroll_view(prev, lines); } if (prev && view != prev) { @@ -2039,7 +2068,7 @@ view_driver(struct view *view, enum request request) case REQ_MOVE_PAGE_DOWN: case REQ_MOVE_FIRST_LINE: case REQ_MOVE_LAST_LINE: - move_view(view, request, TRUE); + move_view(view, request); break; case REQ_SCROLL_LINE_DOWN: @@ -2072,14 +2101,12 @@ view_driver(struct view *view, enum request request) view->parent == VIEW(REQ_VIEW_MAIN)) || (view == VIEW(REQ_VIEW_BLOB) && view->parent == VIEW(REQ_VIEW_TREE))) { - bool redraw = display[1] == view; - view = view->parent; - move_view(view, request, redraw); - if (redraw) + move_view(view, request); + if (view_is_displayed(view)) update_view_title(view); } else { - move_view(view, request, TRUE); + move_view(view, request); break; } /* Fall-through */ @@ -2124,7 +2151,7 @@ view_driver(struct view *view, enum request request) case REQ_SEARCH: case REQ_SEARCH_BACK: - search_view(view, request, opt_search); + search_view(view, request); break; case REQ_FIND_NEXT: @@ -2189,7 +2216,7 @@ view_driver(struct view *view, enum request request) */ static bool -pager_draw(struct view *view, struct line *line, unsigned int lineno) +pager_draw(struct view *view, struct line *line, unsigned int lineno, bool selected) { char *text = line->data; enum line_type type = line->type; @@ -2198,17 +2225,7 @@ pager_draw(struct view *view, struct line *line, unsigned int lineno) wmove(view->win, lineno, 0); - if (view->offset + lineno == view->lineno) { - if (type == LINE_COMMIT) { - string_copy(view->ref, text + 7); - string_copy(ref_commit, view->ref); - - } else if (type == LINE_TREE_DIR || type == LINE_TREE_FILE) { - strncpy(view->ref, text + STRING_SIZE("100644 blob "), 41); - view->ref[40] = 0; - string_copy(ref_blob, view->ref); - } - + if (selected) { type = LINE_CURSOR; wchgat(view->win, -1, 0, type, NULL); } @@ -2328,10 +2345,14 @@ add_pager_refs(struct view *view, struct line *line) if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) { try_add_describe_ref: + /* Add -g "fake" reference. */ if (!add_describe_ref(buf, &bufpos, commit_id, sep)) return; } + if (bufpos == 0) + return; + if (!realloc_lines(view, view->line_size + 1)) return; @@ -2399,18 +2420,30 @@ pager_grep(struct view *view, struct line *line) if (!*text) return FALSE; - if (regexec(&view->regex, text, 1, &pmatch, 0) == REG_NOMATCH) + if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH) return FALSE; return TRUE; } +static void +pager_select(struct view *view, struct line *line) +{ + if (line->type == LINE_COMMIT) { + char *text = line->data; + + string_copy(view->ref, text + STRING_SIZE("commit ")); + string_copy(ref_commit, view->ref); + } +} + static struct view_ops pager_ops = { "line", pager_draw, pager_read, pager_enter, pager_grep, + pager_select, }; @@ -2418,7 +2451,7 @@ static struct view_ops pager_ops = { * Tree backend */ -/* Parse output from git ls-tree: +/* Parse output from git-ls-tree(1): * * 100644 blob fb0e31ea6cc679b7379631188190e975f5789c26 Makefile * 100644 blob 5304ca4260aaddaee6498f9630e7d471b8591ea6 README @@ -2528,8 +2561,7 @@ tree_read(struct view *view, char *text) static bool tree_enter(struct view *view, struct line *line) { - enum open_flags flags = OPEN_DEFAULT; - char *data = line->data; + enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT; enum request request; switch (line->type) { @@ -2548,9 +2580,10 @@ tree_enter(struct view *view, struct line *line) } else { size_t pathlen = strlen(opt_path); size_t origlen = pathlen; + char *data = line->data; char *basename = data + SIZEOF_TREE_ATTR; - if (string_format_from(opt_path, &pathlen, "%s/", basename)) { + if (!string_format_from(opt_path, &pathlen, "%s/", basename)) { opt_path[origlen] = 0; return TRUE; } @@ -2563,10 +2596,6 @@ tree_enter(struct view *view, struct line *line) break; case LINE_TREE_FILE: - /* This causes the blob view to become split, and not having it - * in the tree dir case will make the blob view automatically - * disappear when moving to a different directory. */ - flags |= OPEN_SPLIT; request = REQ_VIEW_BLOB; break; @@ -2576,18 +2605,27 @@ tree_enter(struct view *view, struct line *line) open_view(view, request, flags); - if (!VIEW(request)->pipe) - return TRUE; + return TRUE; +} - /* For tree views insert the path to the parent as the first line. */ - if (request == REQ_VIEW_BLOB) { - /* Mirror what is showed in the title bar. */ - string_ncopy(ref_blob, data + STRING_SIZE("100644 blob "), 40); +static void +tree_select(struct view *view, struct line *line) +{ + char *text = line->data; + + text += STRING_SIZE("100644 blob "); + + if (line->type == LINE_TREE_FILE) { + string_ncopy(ref_blob, text, 40); + /* Also update the blob view's ref, since all there must always + * be in sync. */ string_copy(VIEW(REQ_VIEW_BLOB)->ref, ref_blob); - return TRUE; + + } else if (line->type != LINE_TREE_DIR) { + return; } - return TRUE; + string_ncopy(view->ref, text, 40); } static struct view_ops tree_ops = { @@ -2596,6 +2634,7 @@ static struct view_ops tree_ops = { tree_read, tree_enter, pager_grep, + tree_select, }; static bool @@ -2615,6 +2654,7 @@ static struct view_ops blob_ops = { blob_read, pager_enter, pager_grep, + pager_select, }; @@ -2623,7 +2663,7 @@ static struct view_ops blob_ops = { */ struct commit { - char id[41]; /* SHA1 ID. */ + char id[SIZEOF_REV]; /* 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. */ @@ -2633,7 +2673,7 @@ struct commit { }; static bool -main_draw(struct view *view, struct line *line, unsigned int lineno) +main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected) { char buf[DATE_COLS + 1]; struct commit *commit = line->data; @@ -2648,9 +2688,7 @@ main_draw(struct view *view, struct line *line, unsigned int lineno) wmove(view->win, lineno, col); - if (view->offset + lineno == view->lineno) { - string_copy(view->ref, commit->id); - string_copy(ref_commit, view->ref); + if (selected) { type = LINE_CURSOR; wattrset(view->win, get_line_attr(type)); wchgat(view->win, -1, 0, type, NULL); @@ -2875,19 +2913,29 @@ main_grep(struct view *view, struct line *line) return FALSE; } - if (regexec(&view->regex, text, 1, &pmatch, 0) != REG_NOMATCH) + if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH) return TRUE; } return FALSE; } +static void +main_select(struct view *view, struct line *line) +{ + struct commit *commit = line->data; + + string_copy(view->ref, commit->id); + string_copy(ref_commit, view->ref); +} + static struct view_ops main_ops = { "commit", main_draw, main_read, main_enter, main_grep, + main_select, }; @@ -3087,7 +3135,7 @@ report(const char *msg, ...) } update_view_title(view); - update_display_cursor(); + update_display_cursor(view); } /* Controls when nodelay should be in effect when polling user input. */