More cleanups
[tig] / tig.c
diff --git a/tig.c b/tig.c
index 7dba19d..7121a42 100644 (file)
--- a/tig.c
+++ b/tig.c
@@ -59,8 +59,21 @@ 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 */
+
+/* Revision graph */
+
+#define REVGRAPH_INIT  'I'
+#define REVGRAPH_MERGE 'M'
+#define REVGRAPH_BRANCH        '+'
+#define REVGRAPH_COMMIT        '*'
+#define REVGRAPH_LINE  '|'
+
 #define SIZEOF_REVGRAPH        19      /* Size of revision ancestry graphics. */
 
+/* Size of rev graph with no  "padding" columns */
+#define SIZEOF_REVITEMS        (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
+
 /* This color name can be used to refer to the default term colors. */
 #define COLOR_DEFAULT  (-1)
 
@@ -109,7 +122,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? */
 };
@@ -565,6 +578,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), \
@@ -1432,12 +1446,10 @@ 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;
 
-       assert(view_is_displayed(view));
-
        /* The rendering expects the new offset. */
        view->offset += lines;
 
@@ -1474,9 +1486,6 @@ do_scroll_view(struct view *view, int lines, bool redraw)
                        draw_view_line(view, view->lineno - view->offset);
        }
 
-       if (!redraw)
-               return;
-
        redrawwin(view->win);
        wrefresh(view->win);
        report("");
@@ -1488,6 +1497,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;
@@ -1519,13 +1530,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) {
@@ -1572,34 +1584,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)
-               draw_view_line(view, view->lineno - steps - view->offset);
-
        /* 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);
@@ -2014,7 +2032,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) {
@@ -2059,7 +2077,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:
@@ -2092,14 +2110,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 */
@@ -2444,7 +2460,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
@@ -2554,8 +2570,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) {
@@ -2574,6 +2589,7 @@ 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)) {
@@ -2589,10 +2605,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;
 
@@ -2602,29 +2614,27 @@ tree_enter(struct view *view, struct line *line)
 
        open_view(view, request, flags);
 
-       if (!VIEW(request)->pipe)
-               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);
-               string_copy(VIEW(REQ_VIEW_BLOB)->ref, ref_blob);
-               return TRUE;
-       }
-
        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;
+       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);
 
-               string_ncopy(view->ref, text + STRING_SIZE("100644 blob "), 40);
-               string_copy(ref_blob, view->ref);
+       } else if (line->type != LINE_TREE_DIR) {
+               return;
        }
+
+       string_ncopy(view->ref, text, 40);
 }
 
 static struct view_ops tree_ops = {
@@ -2662,7 +2672,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. */
@@ -2778,6 +2788,119 @@ main_draw(struct view *view, struct line *line, unsigned int lineno, bool select
        return TRUE;
 }
 
+
+struct rev_stack {
+       char rev[SIZEOF_REVITEMS][SIZEOF_REV];
+       size_t size;
+};
+
+/* The current stack of revisions on the graph. */
+static struct rev_stack graph_stacks[3];
+static size_t graph_stack_no;
+
+/* Parents of the commit being visualized. */
+static struct rev_stack graph_parents[2];
+
+static size_t graph_last_rev;
+
+static void
+push_rev_stack(struct rev_stack *stack, char *parent)
+{
+       fprintf(stderr, " (%s)", parent);
+
+       /* Combine duplicate parents lines. */
+       if (stack->size > 0 &&
+           !strncmp(stack->rev[stack->size - 1], parent, SIZEOF_REV))
+               return;
+
+       if (stack->size < SIZEOF_REVITEMS) {
+               string_ncopy(stack->rev[stack->size++], parent, SIZEOF_REV);
+       }
+}
+
+static void
+draw_rev_graph(struct commit *commit, size_t stackpos,
+              struct rev_stack *stack, struct rev_stack *parents,
+              struct rev_stack *prev_parents)
+{
+       chtype symbol, separator, line;
+       size_t i;
+
+       /* Place the symbol for this commit. */
+       if (parents->size == 0)
+               symbol = REVGRAPH_INIT;
+       else if (parents->size > 1)
+               symbol = REVGRAPH_MERGE;
+       else if (stackpos >= stack->size)
+               symbol = REVGRAPH_BRANCH;
+       else
+               symbol = REVGRAPH_COMMIT;
+
+       separator = ' ';
+       line = REVGRAPH_LINE;
+
+       for (i = 0; i < stackpos; i++) {
+               commit->graph[commit->graph_size++] = line;
+               if (prev_parents->size > 1 &&
+                   i == graph_last_rev) {
+                       separator = '`';
+                       line = '.';
+               }
+               commit->graph[commit->graph_size++] = separator;
+       }
+
+       commit->graph[commit->graph_size++] = symbol;
+
+       separator = ' ';
+       line = REVGRAPH_LINE;
+       i++;
+
+       for (; i < stack->size; i++) {
+               commit->graph[commit->graph_size++] = separator;
+               commit->graph[commit->graph_size++] = line;
+               if (prev_parents->size > 1) {
+                       if (i < graph_last_rev + prev_parents->size) {
+                               separator = '`';
+                               line = '.';
+                       }
+               }
+       }
+}
+
+void
+update_rev_graph(struct commit *commit)
+{
+       struct rev_stack *parents = &graph_parents[graph_stack_no & 1];
+       struct rev_stack *stack = &graph_stacks[graph_stack_no++ & 1];
+       struct rev_stack *prev_parents = &graph_parents[graph_stack_no & 1];
+       struct rev_stack *graph = &graph_stacks[graph_stack_no & 1];
+       size_t stackpos = 0;
+       size_t i;
+
+       fprintf(stderr, "\n%p <%s> ", graph, commit->id);
+
+       /* First traverse all lines of revisions up to the active one. */
+       for (stackpos = 0; stackpos < stack->size; stackpos++) {
+               if (!strcmp(stack->rev[stackpos], commit->id))
+                       break;
+
+               push_rev_stack(graph, stack->rev[stackpos]);
+       }
+
+       assert(commit->graph_size < ARRAY_SIZE(commit->graph));
+
+       for (i = 0; i < parents->size; i++)
+               push_rev_stack(graph, parents->rev[i]);
+
+       /* FIXME: Moving branches left and right when collapsing a branch. */
+       for (i = stackpos + 1; i < stack->size; i++)
+               push_rev_stack(graph, stack->rev[i]);
+
+       draw_rev_graph(commit, stackpos, stack, parents, prev_parents);
+       graph_last_rev = stackpos;
+       stack->size = prev_parents->size = 0;
+}
+
 /* Reads git log --pretty=raw output and parses it into the commit struct. */
 static bool
 main_read(struct view *view, char *line)
@@ -2797,7 +2920,14 @@ 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;
+               fprintf(stderr, "\n%p [%s]", &graph_stacks[graph_stack_no & 1], commit->id);
+               break;
+
+       case LINE_PARENT:
+               if (commit) {
+                       line += STRING_SIZE("parent ");
+                       push_rev_stack(&graph_parents[graph_stack_no & 1], line);
+               }
                break;
 
        case LINE_AUTHOR:
@@ -2808,6 +2938,8 @@ main_read(struct view *view, char *line)
                if (!commit)
                        break;
 
+               update_rev_graph(commit);
+
                if (end) {
                        char *email = end + 1;