X-Git-Url: https://git.distorted.org.uk/~mdw/tig/blobdiff_plain/4b8c01a390e2118e6a1c8fc0c50a3be09787166f..b3a54cba6627f5562702e4bf4d8f904e724da745:/tig.c diff --git a/tig.c b/tig.c index 00791a7..bf53092 100644 --- a/tig.c +++ b/tig.c @@ -113,11 +113,11 @@ enum request { REQ_TOGGLE_LINE_NUMBERS, REQ_VIEW_NEXT, REQ_VIEW_CLOSE, + REQ_NEXT, + REQ_PREVIOUS, REQ_MOVE_UP, - REQ_MOVE_UP_ENTER, REQ_MOVE_DOWN, - REQ_MOVE_DOWN_ENTER, REQ_MOVE_PAGE_UP, REQ_MOVE_PAGE_DOWN, REQ_MOVE_FIRST_LINE, @@ -136,13 +136,7 @@ struct ref { unsigned int next:1; /* For ref lists: are there more refs? */ }; -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. */ -}; +static struct ref **get_refs(char *id); /* @@ -674,7 +668,6 @@ struct view { const char *cmd_fmt; /* Default command line format */ const char *cmd_env; /* Command line set via environment */ const char *id; /* Points to either of ref_{head,commit} */ - size_t objsize; /* Size of objects in the line index */ struct view_ops { /* What type of content being displayed. Used in the @@ -700,6 +693,10 @@ struct view { unsigned long offset; /* Offset of the window top */ unsigned long lineno; /* Current line number */ + /* If non-NULL, points to the view that opened this view. If this view + * is closed tig will switch back to the parent view. */ + struct view *parent; + /* Buffering */ unsigned long lines; /* Total number of lines */ void **line; /* Line index; each line contains user data */ @@ -713,11 +710,11 @@ struct view { static struct view_ops pager_ops; static struct view_ops main_ops; -#define VIEW_STR(name, cmd, env, ref, objsize, ops) \ - { name, cmd, #env, ref, objsize, ops } +#define VIEW_STR(name, cmd, env, ref, ops) \ + { name, cmd, #env, ref, ops } -#define VIEW_(id, name, ops, ref, objsize) \ - VIEW_STR(name, TIG_##id##_CMD, TIG_##id##_CMD, ref, objsize, ops) +#define VIEW_(id, name, ops, ref) \ + VIEW_STR(name, TIG_##id##_CMD, TIG_##id##_CMD, ref, ops) /** * Views @@ -750,11 +747,11 @@ static struct view_ops main_ops; **/ static struct view views[] = { - 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, "static", sizeof(char)), - VIEW_(PAGER, "pager", &pager_ops, "static", sizeof(char)), + VIEW_(MAIN, "main", &main_ops, ref_head), + VIEW_(DIFF, "diff", &pager_ops, ref_commit), + VIEW_(LOG, "log", &pager_ops, ref_head), + VIEW_(HELP, "help", &pager_ops, "static"), + VIEW_(PAGER, "pager", &pager_ops, "static"), }; #define VIEW(req) (&views[(req) - REQ_OFFSET - 1]) @@ -894,7 +891,7 @@ redraw_display(void) /* Scrolling backend */ static void -do_scroll_view(struct view *view, int lines) +do_scroll_view(struct view *view, int lines, bool redraw) { /* The rendering expects the new offset. */ view->offset += lines; @@ -936,6 +933,9 @@ do_scroll_view(struct view *view, int lines) assert(view->offset <= view->lineno && view->lineno < view->lines); + if (!redraw) + return; + redrawwin(view->win); wrefresh(view->win); report(""); @@ -978,12 +978,12 @@ scroll_view(struct view *view, enum request request) die("request %d not handled in switch", request); } - do_scroll_view(view, lines); + do_scroll_view(view, lines, TRUE); } /* Cursor moving */ static void -move_view(struct view *view, enum request request) +move_view(struct view *view, enum request request, bool redraw) { int steps; @@ -1007,12 +1007,10 @@ move_view(struct view *view, enum request request) break; case REQ_MOVE_UP: - case REQ_MOVE_UP_ENTER: steps = -1; break; case REQ_MOVE_DOWN: - case REQ_MOVE_DOWN_ENTER: steps = 1; break; @@ -1057,13 +1055,16 @@ move_view(struct view *view, enum request request) } } - do_scroll_view(view, steps); + do_scroll_view(view, steps, redraw); return; } /* Draw the current line */ view->ops->draw(view, view->lineno - view->offset); + if (!redraw) + return; + redrawwin(view->win); wrefresh(view->win); report(""); @@ -1257,21 +1258,7 @@ open_view(struct view *prev, enum request request, enum open_flags flags) bool split = !!(flags & OPEN_SPLIT); bool reload = !!(flags & OPEN_RELOAD); struct view *view = VIEW(request); - struct view *displayed; - int nviews; - - /* Cycle between displayed views and count the views. */ - foreach_view (displayed, nviews) { - if (prev != view && - view == displayed && - !strcmp(view->vid, prev->vid)) { - current_view = nviews; - /* Blur out the title of the previous view. */ - update_view_title(prev); - report(""); - return; - } - } + int nviews = display[1] ? 2 : 1; if (view == prev && nviews == 1 && !reload) { report("Already in %s view", view->name); @@ -1303,7 +1290,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); + do_scroll_view(prev, lines, TRUE); } if (prev && view != prev) { @@ -1314,6 +1301,7 @@ open_view(struct view *prev, enum request request, enum open_flags flags) /* Continue loading split views in the background. */ if (!split) end_update(prev); + view->parent = prev; } if (view->pipe) { @@ -1352,7 +1340,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); + move_view(view, request, TRUE); break; case REQ_SCROLL_LINE_DOWN: @@ -1370,9 +1358,21 @@ view_driver(struct view *view, enum request request) open_view(view, request, OPEN_DEFAULT); break; - case REQ_MOVE_UP_ENTER: - case REQ_MOVE_DOWN_ENTER: - move_view(view, request); + case REQ_NEXT: + case REQ_PREVIOUS: + request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP; + + if (view == VIEW(REQ_VIEW_DIFF) && + view->parent == VIEW(REQ_VIEW_MAIN)) { + bool redraw = display[0] == VIEW(REQ_VIEW_MAIN); + + view = view->parent; + move_view(view, request, redraw); + update_view_title(view); + } else { + move_view(view, request, TRUE); + break; + } /* Fall-through */ case REQ_ENTER: @@ -1432,11 +1432,11 @@ view_driver(struct view *view, enum request request) return TRUE; case REQ_VIEW_CLOSE: - if (display[1]) { - view = display[(current_view + 1) % ARRAY_SIZE(display)]; + if (view->parent) { memset(display, 0, sizeof(display)); current_view = 0; - display[current_view] = view; + display[current_view] = view->parent; + view->parent = NULL; resize_display(); redraw_display(); break; @@ -1456,7 +1456,7 @@ view_driver(struct view *view, enum request request) /* - * View backend handlers + * Pager backend */ static bool @@ -1564,18 +1564,25 @@ static bool pager_enter(struct view *view) { char *line = view->line[view->lineno]; + int split = 0; - if (view == VIEW(REQ_VIEW_DIFF)) { - scroll_view(view, REQ_SCROLL_LINE_DOWN); - return TRUE; + if ((view == VIEW(REQ_VIEW_LOG) || + view == VIEW(REQ_VIEW_PAGER)) && + get_line_type(line) == LINE_COMMIT) { + open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT); + split = 1; } - if (get_line_type(line) == LINE_COMMIT) { - if (view == VIEW(REQ_VIEW_LOG)) - open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT | OPEN_BACKGROUNDED); - else - open_view(view, REQ_VIEW_DIFF, OPEN_DEFAULT); - } + /* Always scroll the view even if it was split. That way + * you can use Enter to scroll through the log view and + * split open each commit diff. */ + scroll_view(view, REQ_SCROLL_LINE_DOWN); + + /* FIXME: A minor workaround. Scrolling the view will call report("") + * but if we are scolling a non-current view this won't properly update + * the view title. */ + if (split) + update_view_title(view); return TRUE; } @@ -1588,7 +1595,17 @@ static struct view_ops pager_ops = { }; -static struct ref **get_refs(char *id); +/* + * Main view backend + */ + +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. */ +}; static bool main_draw(struct view *view, unsigned int lineno) @@ -1777,7 +1794,9 @@ main_read(struct view *view, char *line) static bool main_enter(struct view *view) { - open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT); + enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT; + + open_view(view, REQ_VIEW_DIFF, flags); return TRUE; } @@ -1814,8 +1833,20 @@ static struct keymap keymap[] = { * Switch to pager view. * h:: * Show man page. + **/ + { 'm', REQ_VIEW_MAIN }, + { 'd', REQ_VIEW_DIFF }, + { 'l', REQ_VIEW_LOG }, + { 'p', REQ_VIEW_PAGER }, + { 'h', REQ_VIEW_HELP }, + + /** + * View manipulation + * ~~~~~~~~~~~~~~~~~ * q:: - * Close view if multiple views are open, else quit. + * Close view, if multiple views are open it will jump back to the + * previous view in the view stack. If it is the last open view it + * will quit. Use 'Q' to quit all views at once. * Enter:: * This key is "context sensitive" depending on what view you are * currently in. When in log view on a commit line or in the main @@ -1823,30 +1854,28 @@ static struct keymap keymap[] = { * pressing Enter will simply scroll the view one line down. * Tab:: * Switch to next view. + * Up:: + * This key is "context sensitive" and will move the cursor one + * line up. However, uf you opened a diff view from the main view + * (split- or full-screen) it will change the cursor to point to + * the previous commit in the main view and update the diff view + * to display it. + * Down:: + * Similar to 'Up' but will move down. **/ - { 'm', REQ_VIEW_MAIN }, - { 'd', REQ_VIEW_DIFF }, - { 'l', REQ_VIEW_LOG }, - { 'p', REQ_VIEW_PAGER }, - { 'h', REQ_VIEW_HELP }, - { 'q', REQ_VIEW_CLOSE }, { KEY_TAB, REQ_VIEW_NEXT }, { KEY_RETURN, REQ_ENTER }, + { KEY_UP, REQ_PREVIOUS }, + { KEY_DOWN, REQ_NEXT }, /** * Cursor navigation * ~~~~~~~~~~~~~~~~~ - * Up:: + * j:: * Move cursor one line up. - * Down:: - * Move cursor one line down. * k:: - * Move cursor one line up and enter. When used in the main view - * this will always show the diff of the current commit in the - * split diff view. - * j:: - * Move cursor one line down and enter. + * Move cursor one line down. * PgUp:: * b:: * -:: @@ -1859,10 +1888,8 @@ static struct keymap keymap[] = { * End:: * Jump to last line. **/ - { KEY_UP, REQ_MOVE_UP }, - { KEY_DOWN, REQ_MOVE_DOWN }, - { 'k', REQ_MOVE_UP_ENTER }, - { 'j', REQ_MOVE_DOWN_ENTER }, + { 'k', REQ_MOVE_UP }, + { 'j', REQ_MOVE_DOWN }, { KEY_HOME, REQ_MOVE_FIRST_LINE }, { KEY_END, REQ_MOVE_LAST_LINE }, { KEY_NPAGE, REQ_MOVE_PAGE_DOWN }, @@ -2014,7 +2041,7 @@ utf8_to_unicode(const char *string, size_t length) unicode += ((string[3] & 0x3f) << 6); unicode += (string[4] & 0x3f); break; - case 6: + case 6: unicode = (string[0] & 0x01) << 30; unicode += ((string[1] & 0x3f) << 24); unicode += ((string[2] & 0x3f) << 18); @@ -2101,7 +2128,7 @@ utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed) */ /* Whether or not the curses interface has been initialized. */ -bool cursed = FALSE; +static bool cursed = FALSE; /* The status window is used for polling keystrokes. */ static WINDOW *status_win; @@ -2198,39 +2225,57 @@ init_display(void) static struct ref *refs; static size_t refs_size; +/* Id <-> ref store */ +static struct ref ***id_refs; +static size_t id_refs_size; + static struct ref ** get_refs(char *id) { - struct ref **id_refs = NULL; - size_t id_refs_size = 0; + struct ref ***tmp_id_refs; + struct ref **ref_list = NULL; + size_t ref_list_size = 0; size_t i; + for (i = 0; i < id_refs_size; i++) + if (!strcmp(id, id_refs[i][0]->id)) + return id_refs[i]; + + tmp_id_refs = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs)); + if (!tmp_id_refs) + return NULL; + + id_refs = tmp_id_refs; + for (i = 0; i < refs_size; i++) { struct ref **tmp; if (strcmp(id, refs[i].id)) continue; - tmp = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs)); + tmp = realloc(ref_list, (ref_list_size + 1) * sizeof(*ref_list)); if (!tmp) { - if (id_refs) - free(id_refs); + if (ref_list) + free(ref_list); return NULL; } - id_refs = tmp; - if (id_refs_size > 0) - id_refs[id_refs_size - 1]->next = 1; - id_refs[id_refs_size] = &refs[i]; + ref_list = tmp; + if (ref_list_size > 0) + ref_list[ref_list_size - 1]->next = 1; + ref_list[ref_list_size] = &refs[i]; /* XXX: The properties of the commit chains ensures that we can * safely modify the shared ref. The repo references will * always be similar for the same id. */ - id_refs[id_refs_size]->next = 0; - id_refs_size++; + ref_list[ref_list_size]->next = 0; + ref_list_size++; } - return id_refs; + if (ref_list) + id_refs[id_refs_size++] = ref_list; + + return ref_list; } static int @@ -2298,9 +2343,6 @@ load_refs(void) pclose(pipe); - if (refs_size == 0) - die("Not a git repository"); - return OK; } @@ -2354,6 +2396,10 @@ main(int argc, char *argv[]) if (load_refs() == ERR) die("Failed to load refs."); + /* Require a git repository unless when running in pager mode. */ + if (refs_size == 0 && opt_request != REQ_VIEW_PAGER) + die("Not a git repository"); + for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++) view->cmd_env = getenv(view->cmd_env);