Merge with master
authorJonas Fonseca <fonseca@diku.dk>
Sat, 16 Sep 2006 01:14:29 +0000 (03:14 +0200)
committerJonas Fonseca <fonseca@antimatter.localdomain>
Sat, 16 Sep 2006 01:14:29 +0000 (03:14 +0200)
1  2 
tig.c

diff --combined tig.c
--- 1/tig.c
--- 2/tig.c
+++ b/tig.c
@@@ -60,15 -60,6 +60,15 @@@ static size_t utf8_length(const char *s
  #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. */
  
  /* This color name can be used to refer to the default term colors. */
@@@ -1274,6 -1265,7 +1274,7 @@@ draw_view_line(struct view *view, unsig
  {
        struct line *line;
        bool selected = (view->offset + lineno == view->lineno);
+       bool draw_ok;
  
        assert(view_is_displayed(view));
  
                wclrtoeol(view->win);
        }
  
-       return view->ops->draw(view, line, lineno, selected);
+       scrollok(view->win, FALSE);
+       draw_ok = view->ops->draw(view, line, lineno, selected);
+       scrollok(view->win, TRUE);
+       return draw_ok;
  }
  
  static void
@@@ -1319,20 -1315,11 +1324,11 @@@ 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
-               wbkgdset(view->title, get_line_attr(LINE_TITLE_BLUR));
-       werase(view->title);
-       wmove(view->title, 0, 0);
+       char buf[SIZEOF_STR];
+       char state[SIZEOF_STR];
+       size_t bufpos = 0, statelen = 0;
  
-       if (*view->ref)
-               wprintw(view->title, "[%s] %s", view->name, view->ref);
-       else
-               wprintw(view->title, "[%s]", view->name);
+       assert(view_is_displayed(view));
  
        if (view->lines || view->pipe) {
                unsigned int view_lines = view->offset + view->height;
                                   ? MIN(view_lines, view->lines) * 100 / view->lines
                                   : 0;
  
-               wprintw(view->title, " - %s %d of %d (%d%%)",
-                       view->ops->type,
-                       view->lineno + 1,
-                       view->lines,
-                       lines);
+               string_format_from(state, &statelen, "- %s %d of %d (%d%%)",
+                                  view->ops->type,
+                                  view->lineno + 1,
+                                  view->lines,
+                                  lines);
+               if (view->pipe) {
+                       time_t secs = time(NULL) - view->start_time;
+                       /* Three git seconds are a long time ... */
+                       if (secs > 2)
+                               string_format_from(state, &statelen, " %lds", secs);
+               }
        }
  
-       if (view->pipe) {
-               time_t secs = time(NULL) - view->start_time;
+       string_format_from(buf, &bufpos, "[%s]", view->name);
+       if (*view->ref && bufpos < view->width) {
+               size_t refsize = strlen(view->ref);
+               size_t minsize = bufpos + 1 + /* abbrev= */ 7 + 1 + statelen;
+               if (minsize < view->width)
+                       refsize = view->width - minsize + 7;
+               string_format_from(buf, &bufpos, " %.*s", refsize, view->ref);
+       }
  
-               /* Three git seconds are a long time ... */
-               if (secs > 2)
-                       wprintw(view->title, " %lds", secs);
+       if (statelen && bufpos < view->width) {
+               string_format_from(buf, &bufpos, " %s", state);
        }
  
+       if (view == display[current_view])
+               wbkgdset(view->title, get_line_attr(LINE_TITLE_FOCUS));
+       else
+               wbkgdset(view->title, get_line_attr(LINE_TITLE_BLUR));
+       werase(view->title);
+       mvwaddnstr(view->title, 0, 0, buf, bufpos);
        wmove(view->title, 0, view->width - 1);
        wrefresh(view->title);
  }
@@@ -1423,10 -1431,8 +1440,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
@@@ -1928,7 -1934,6 +1943,7 @@@ alloc_error
        report("Allocation failure");
  
  end:
 +      view->ops->read(view, NULL);
        end_update(view);
        return FALSE;
  }
@@@ -2377,9 -2382,6 +2392,9 @@@ pager_read(struct view *view, char *dat
  {
        struct line *line = &view->line[view->lines];
  
 +      if (!data)
 +              return TRUE;
 +
        line->data = strdup(data);
        if (!line->data)
                return FALSE;
@@@ -2490,7 -2492,7 +2505,7 @@@ tree_compare_entry(enum line_type type1
  static bool
  tree_read(struct view *view, char *text)
  {
 -      size_t textlen = strlen(text);
 +      size_t textlen = text ? strlen(text) : 0;
        char buf[SIZEOF_STR];
        unsigned long pos;
        enum line_type type;
@@@ -2669,7 -2671,7 +2684,7 @@@ static struct view_ops blob_ops = 
  
  
  /*
 - * Main view backend
 + * Revision graph
   */
  
  struct commit {
        size_t graph_size;              /* The width of the graph array. */
  };
  
 +/* Size of rev graph with no  "padding" columns */
 +#define SIZEOF_REVITEMS       (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
 +
 +struct rev_graph {
 +      struct rev_graph *prev, *next, *parents;
 +      char rev[SIZEOF_REVITEMS][SIZEOF_REV];
 +      size_t size;
 +      struct commit *commit;
 +      size_t pos;
 +};
 +
 +/* Parents of the commit being visualized. */
 +static struct rev_graph graph_parents[4];
 +
 +/* The current stack of revisions on the graph. */
 +static struct rev_graph graph_stacks[4] = {
 +      { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
 +      { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
 +      { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
 +      { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
 +};
 +
 +static inline bool
 +graph_parent_is_merge(struct rev_graph *graph)
 +{
 +      return graph->parents->size > 1;
 +}
 +
 +static inline void
 +append_to_rev_graph(struct rev_graph *graph, chtype symbol)
 +{
 +      struct commit *commit = graph->commit;
 +
 +      if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
 +              commit->graph[commit->graph_size++] = symbol;
 +}
 +
 +static void
 +done_rev_graph(struct rev_graph *graph)
 +{
 +      if (graph_parent_is_merge(graph) &&
 +          graph->pos < graph->size - 1 &&
 +          graph->next->size == graph->size + graph->parents->size - 1) {
 +              size_t i = graph->pos + graph->parents->size - 1;
 +
 +              graph->commit->graph_size = i * 2;
 +              while (i < graph->next->size - 1) {
 +                      append_to_rev_graph(graph, ' ');
 +                      append_to_rev_graph(graph, '\\');
 +                      i++;
 +              }
 +      }
 +
 +      graph->size = graph->pos = 0;
 +      graph->commit = NULL;
 +      memset(graph->parents, 0, sizeof(*graph->parents));
 +}
 +
 +static void
 +push_rev_graph(struct rev_graph *graph, char *parent)
 +{
 +      /* Combine duplicate parents lines. */
 +      if (graph->size > 0 &&
 +          !strncmp(graph->rev[graph->size - 1], parent, SIZEOF_REV))
 +              return;
 +
 +      if (graph->size < SIZEOF_REVITEMS) {
 +              string_ncopy(graph->rev[graph->size++], parent, SIZEOF_REV);
 +      }
 +}
 +
 +static chtype
 +get_rev_graph_symbol(struct rev_graph *graph)
 +{
 +      chtype symbol;
 +
 +      if (graph->parents->size == 0)
 +              symbol = REVGRAPH_INIT;
 +      else if (graph_parent_is_merge(graph))
 +              symbol = REVGRAPH_MERGE;
 +      else if (graph->pos >= graph->size)
 +              symbol = REVGRAPH_BRANCH;
 +      else
 +              symbol = REVGRAPH_COMMIT;
 +
 +      return symbol;
 +}
 +
 +static void
 +draw_rev_graph(struct rev_graph *graph)
 +{
 +      struct rev_filler {
 +              chtype separator, line;
 +      };
 +      enum { DEFAULT, RSHARP, RDIAG, LDIAG };
 +      static struct rev_filler fillers[] = {
 +              { ' ',  REVGRAPH_LINE },
 +              { '`',  '.' },
 +              { '\'', ' ' },
 +              { '/',  ' ' },
 +      };
 +      chtype symbol = get_rev_graph_symbol(graph);
 +      struct rev_filler *filler;
 +      size_t i;
 +
 +      filler = &fillers[DEFAULT];
 +
 +      for (i = 0; i < graph->pos; i++) {
 +              append_to_rev_graph(graph, filler->line);
 +              if (graph_parent_is_merge(graph->prev) &&
 +                  graph->prev->pos == i)
 +                      filler = &fillers[RSHARP];
 +
 +              append_to_rev_graph(graph, filler->separator);
 +      }
 +
 +      /* Place the symbol for this revision. */
 +      append_to_rev_graph(graph, symbol);
 +
 +      if (graph->prev->size > graph->size)
 +              filler = &fillers[RDIAG];
 +      else
 +              filler = &fillers[DEFAULT];
 +
 +      i++;
 +
 +      for (; i < graph->size; i++) {
 +              append_to_rev_graph(graph, filler->separator);
 +              append_to_rev_graph(graph, filler->line);
 +              if (graph_parent_is_merge(graph->prev) &&
 +                  i < graph->prev->pos + graph->parents->size)
 +                      filler = &fillers[RSHARP];
 +              if (graph->prev->size > graph->size)
 +                      filler = &fillers[LDIAG];
 +      }
 +
 +      if (graph->prev->size > graph->size) {
 +              append_to_rev_graph(graph, filler->separator);
 +              if (filler->line != ' ')
 +                      append_to_rev_graph(graph, filler->line);
 +      }
 +}
 +
 +/* Prepare the next rev graph */
 +static void
 +prepare_rev_graph(struct rev_graph *graph)
 +{
 +      size_t i;
 +
 +      /* First, traverse all lines of revisions up to the active one. */
 +      for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
 +              if (!strcmp(graph->rev[graph->pos], graph->commit->id))
 +                      break;
 +
 +              push_rev_graph(graph->next, graph->rev[graph->pos]);
 +      }
 +
 +      /* Interleave the new revision parent(s). */
 +      for (i = 0; i < graph->parents->size; i++)
 +              push_rev_graph(graph->next, graph->parents->rev[i]);
 +
 +      /* Lastly, put any remaining revisions. */
 +      for (i = graph->pos + 1; i < graph->size; i++)
 +              push_rev_graph(graph->next, graph->rev[i]);
 +}
 +
 +static void
 +update_rev_graph(struct rev_graph *graph)
 +{
 +      /* If this is the finalizing update ... */
 +      if (graph->commit)
 +              prepare_rev_graph(graph);
 +
 +      /* Graph visualization needs a one rev look-ahead,
 +       * so the first update doesn't visualize anything. */
 +      if (!graph->prev->commit)
 +              return;
 +
 +      draw_rev_graph(graph->prev);
 +      done_rev_graph(graph->prev->prev);
 +}
 +
 +
 +/*
 + * Main view backend
 + */
 +
  static bool
  main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
  {
                for (i = 0; i < commit->graph_size; i++)
                        waddch(view->win, commit->graph[i]);
  
 +              waddch(view->win, ' ');
                col += commit->graph_size + 1;
        }
  
  static bool
  main_read(struct view *view, char *line)
  {
 -      enum line_type type = get_line_type(line);
 +      static struct rev_graph *graph = graph_stacks;
 +      enum line_type type;
        struct commit *commit = view->lines
                              ? view->line[view->lines - 1].data : NULL;
  
 +      if (!line) {
 +              update_rev_graph(graph);
 +              return TRUE;
 +      }
 +
 +      type = get_line_type(line);
 +
        switch (type) {
        case LINE_COMMIT:
                commit = calloc(1, sizeof(struct commit));
                view->line[view->lines++].data = commit;
                string_copy(commit->id, line);
                commit->refs = get_refs(commit->id);
 -              commit->graph[commit->graph_size++] = ACS_LTEE;
 +              graph->commit = commit;
 +              break;
 +
 +      case LINE_PARENT:
 +              if (commit) {
 +                      line += STRING_SIZE("parent ");
 +                      push_rev_graph(graph->parents, line);
 +              }
                break;
  
        case LINE_AUTHOR:
                if (!commit)
                        break;
  
 +              update_rev_graph(graph);
 +              graph = graph->next;
 +
                if (end) {
                        char *email = end + 1;
  
@@@ -3351,7 -3147,7 +3366,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. */