X-Git-Url: https://git.distorted.org.uk/~mdw/tig/blobdiff_plain/95d7ddcdaaff5fd6f0a05e3d515c436d9a3605e3..b3a54cba6627f5562702e4bf4d8f904e724da745:/tig.c diff --git a/tig.c b/tig.c index 0981593..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); /* @@ -699,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 */ @@ -893,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; @@ -935,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(""); @@ -977,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; @@ -1006,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; @@ -1056,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(""); @@ -1256,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); @@ -1302,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) { @@ -1313,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) { @@ -1351,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: @@ -1369,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: @@ -1431,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; @@ -1455,7 +1456,7 @@ view_driver(struct view *view, enum request request) /* - * View backend handlers + * Pager backend */ static bool @@ -1566,7 +1567,7 @@ pager_enter(struct view *view) int split = 0; if ((view == VIEW(REQ_VIEW_LOG) || - 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; @@ -1594,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) @@ -1783,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; } @@ -1820,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 @@ -1829,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:: * -:: @@ -1865,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 }, @@ -2204,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 @@ -2304,9 +2343,6 @@ load_refs(void) pclose(pipe); - if (refs_size == 0) - die("Not a git repository"); - return OK; } @@ -2360,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);