X-Git-Url: https://git.distorted.org.uk/~mdw/tig/blobdiff_plain/33c4f9ea1de94641094993578a789d12ab2d95e9..2b42e5d686833a9f45020dfeb8148832d0ad30f7:/tig.c diff --git a/tig.c b/tig.c index 6518f0d..384fe21 100644 --- a/tig.c +++ b/tig.c @@ -145,16 +145,22 @@ set_from_int_map(struct int_map *map, size_t map_size, */ static inline void -string_ncopy(char *dst, const char *src, int 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) @@ -172,10 +178,10 @@ chomp_string(char *name) } static bool -string_nformat(char *buf, size_t bufsize, int *bufpos, const char *fmt, ...) +string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...) { va_list args; - int pos = bufpos ? *bufpos : 0; + size_t pos = bufpos ? *bufpos : 0; va_start(args, fmt); pos += vsnprintf(buf + pos, bufsize - pos, fmt, args); @@ -655,6 +661,10 @@ init_colors(void) struct line { enum line_type type; + + /* State flags */ + unsigned int selected:1; + void *data; /* User data */ }; @@ -675,7 +685,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 }, @@ -714,7 +724,7 @@ static struct keybinding default_keybindings[] = { { 'z', REQ_STOP_LOADING }, { 'v', REQ_SHOW_VERSION }, { 'r', REQ_SCREEN_REDRAW }, - { 'n', REQ_TOGGLE_LINENO }, + { '.', REQ_TOGGLE_LINENO }, { 'g', REQ_TOGGLE_REV_GRAPH }, { ':', REQ_PROMPT }, @@ -847,7 +857,7 @@ get_key(enum request request) { static char buf[BUFSIZ]; static char key_char[] = "'X'"; - int pos = 0; + size_t pos = 0; char *sep = " "; int i; @@ -1186,7 +1196,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 +1217,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; @@ -1240,14 +1252,35 @@ static struct view views[] = { #define VIEW(req) (&views[(req) - REQ_OFFSET - 1]) +#define foreach_view(view, i) \ + for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++) + +#define view_is_displayed(view) \ + (view == display[0] || view == display[1]) static bool draw_view_line(struct view *view, unsigned int lineno) { + struct line *line; + bool selected = (view->offset + lineno == view->lineno); + + 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); + } + + return view->ops->draw(view, line, lineno, selected); } static void @@ -1275,6 +1308,8 @@ redraw_view(struct view *view) static void update_view_title(struct view *view) { + assert(view_is_displayed(view)); + if (view == display[current_view]) wbkgdset(view->title, get_line_attr(LINE_TITLE_FOCUS)); else @@ -1397,14 +1432,27 @@ 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) { + bool redraw_current_line = FALSE; + /* The rendering expects the new offset. */ view->offset += lines; 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); @@ -1419,29 +1467,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(""); @@ -1453,6 +1483,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; @@ -1484,13 +1516,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) { + bool scroll = FALSE; int steps; switch (request) { @@ -1537,15 +1570,6 @@ 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) { @@ -1561,15 +1585,26 @@ move_view(struct view *view, enum request request, bool redraw) } } - do_scroll_view(view, steps, redraw); + scroll = TRUE; + } + + 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) { + do_scroll_view(view, steps); return; + } + + /* Draw the current line */ + draw_view_line(view, view->lineno - view->offset); redrawwin(view->win); wrefresh(view->win); @@ -1581,11 +1616,13 @@ 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) { + assert(view_is_displayed(view)); + if (!view->ops->grep(view, line)) return FALSE; @@ -1598,9 +1635,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); @@ -1622,7 +1656,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; } @@ -1657,25 +1691,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)); - report("Search failed: %s", 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); } @@ -1718,8 +1756,7 @@ begin_update(struct view *view) if (strcmp(view->vid, view->id)) opt_path[0] = 0; - if (snprintf(view->cmd, sizeof(view->cmd), format, id, opt_path) - >= sizeof(view->cmd)) + if (!string_format(view->cmd, format, id, opt_path)) return FALSE; } else { @@ -1795,6 +1832,7 @@ update_view(struct view *view) if (view->offset + view->height >= view->lines) redraw_from = view->lines - view->offset; + /* FIXME: This is probably not perfect for backgrounded views. */ if (!realloc_lines(view, view->lines + lines)) goto alloc_error; @@ -1841,6 +1879,9 @@ update_view(struct view *view) } } + if (!view_is_displayed(view)) + goto check_pipe; + if (view == VIEW(REQ_VIEW_TREE)) { /* Clear the view and redraw everything since the tree sorting * might have rearranged things. */ @@ -1861,6 +1902,7 @@ update_view(struct view *view) * commit reference in view->ref it'll be available here. */ update_view_title(view); +check_pipe: if (ferror(view->pipe)) { report("Failed to read: %s", strerror(errno)); goto end; @@ -1977,7 +2019,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) { @@ -2022,7 +2064,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: @@ -2055,14 +2097,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 */ @@ -2107,7 +2147,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: @@ -2172,7 +2212,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; @@ -2181,17 +2221,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); } @@ -2252,7 +2282,7 @@ pager_draw(struct view *view, struct line *line, unsigned int lineno) } static bool -add_describe_ref(char *buf, int *bufpos, char *commit_id, const char *sep) +add_describe_ref(char *buf, size_t *bufpos, char *commit_id, const char *sep) { char refbuf[SIZEOF_STR]; char *ref = NULL; @@ -2285,7 +2315,7 @@ add_pager_refs(struct view *view, struct line *line) char buf[SIZEOF_STR]; char *commit_id = line->data + STRING_SIZE("commit "); struct ref **refs; - int bufpos = 0, refpos = 0; + size_t bufpos = 0, refpos = 0; const char *sep = "Refs: "; bool is_tag = FALSE; @@ -2311,10 +2341,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; @@ -2382,18 +2416,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, }; @@ -2434,6 +2480,7 @@ tree_read(struct view *view, char *text) char buf[SIZEOF_STR]; unsigned long pos; enum line_type type; + bool first_read = view->lines == 0; if (textlen <= SIZEOF_TREE_ATTR) return FALSE; @@ -2441,10 +2488,9 @@ tree_read(struct view *view, char *text) type = text[STRING_SIZE("100644 ")] == 't' ? LINE_TREE_DIR : LINE_TREE_FILE; - /* The first time around ... */ - if (!view->lines) { + if (first_read) { /* Add path info line */ - if (snprintf(buf, sizeof(buf), "Directory path /%s", opt_path) < sizeof(buf) && + if (string_format(buf, "Directory path /%s", opt_path) && realloc_lines(view, view->line_size + 1) && pager_read(view, buf)) view->line[view->lines - 1].type = LINE_DEFAULT; @@ -2453,7 +2499,7 @@ tree_read(struct view *view, char *text) /* Insert "link" to parent directory. */ if (*opt_path && - snprintf(buf, sizeof(buf), TREE_UP_FORMAT, view->ref) < sizeof(buf) && + string_format(buf, TREE_UP_FORMAT, view->ref) && realloc_lines(view, view->line_size + 1) && pager_read(view, buf)) view->line[view->lines - 1].type = LINE_TREE_DIR; @@ -2500,6 +2546,10 @@ tree_read(struct view *view, char *text) if (!pager_read(view, text)) return FALSE; + /* Move the current line to the first tree entry. */ + if (first_read) + view->lineno++; + view->line[view->lines - 1].type = type; return TRUE; } @@ -2507,7 +2557,7 @@ tree_read(struct view *view, char *text) static bool tree_enter(struct view *view, struct line *line) { - enum open_flags flags = OPEN_DEFAULT; + enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT; char *data = line->data; enum request request; @@ -2525,10 +2575,14 @@ tree_enter(struct view *view, struct line *line) dirsep[0] = 0; } else { - int pathlen = strlen(opt_path); + size_t pathlen = strlen(opt_path); + size_t origlen = pathlen; char *basename = data + SIZEOF_TREE_ATTR; - string_format_from(opt_path, &pathlen, "%s/", basename); + if (!string_format_from(opt_path, &pathlen, "%s/", basename)) { + opt_path[origlen] = 0; + return TRUE; + } } /* Trees and subtrees share the same ID, so they are not not @@ -2538,10 +2592,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; @@ -2565,12 +2615,24 @@ tree_enter(struct view *view, struct line *line) return TRUE; } +static void +tree_select(struct view *view, struct line *line) +{ + if (line->type == LINE_TREE_DIR || line->type == LINE_TREE_FILE) { + char *text = line->data; + + string_ncopy(view->ref, text + STRING_SIZE("100644 blob "), 40); + string_copy(ref_blob, view->ref); + } +} + static struct view_ops tree_ops = { "file", pager_draw, tree_read, tree_enter, pager_grep, + tree_select, }; static bool @@ -2590,6 +2652,7 @@ static struct view_ops blob_ops = { blob_read, pager_enter, pager_grep, + pager_select, }; @@ -2608,7 +2671,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; @@ -2623,9 +2686,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); @@ -2850,19 +2911,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, }; @@ -3125,7 +3196,7 @@ read_prompt(const char *prompt) struct view *view; int i, key; - foreach_displayed_view (view, i) + foreach_view (view, i) update_view(view); report("%s%.*s", prompt, pos, buf); @@ -3398,7 +3469,7 @@ main(int argc, char *argv[]) if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) { opt_iconv = iconv_open(opt_codeset, opt_encoding); - if (opt_iconv == (iconv_t) -1) + if (opt_iconv == ICONV_NONE) die("Failed to initialize character set conversion"); } @@ -3420,7 +3491,7 @@ main(int argc, char *argv[]) int key; int i; - foreach_displayed_view (view, i) + foreach_view (view, i) update_view(view); /* Refresh, accept single keystroke of input */