X-Git-Url: https://git.distorted.org.uk/~mdw/tig/blobdiff_plain/88757ebdbd32ba72232e22d4afe1fe882f719e3d..2c27faac1c8fcd27a863cafdfb0543d1251f8176:/tig.c diff --git a/tig.c b/tig.c index 4e0900a..cc39d7e 100644 --- a/tig.c +++ b/tig.c @@ -71,9 +71,6 @@ static size_t utf8_length(const char *string, size_t max_width, int *coloffset, #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) @@ -2668,7 +2665,7 @@ static struct view_ops blob_ops = { /* - * Main view backend + * Revision graph */ struct commit { @@ -2681,6 +2678,179 @@ 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[3]; + +/* The current stack of revisions on the graph. */ +static struct rev_graph graph_stacks[3] = { + { &graph_stacks[2], &graph_stacks[1], &graph_parents[0] }, + { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] }, + { &graph_stacks[1], &graph_stacks[0], &graph_parents[2] }, +}; + +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); + } +} + +void +update_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]); + + draw_rev_graph(graph); + done_rev_graph(graph->prev); +} + + +/* + * Main view backend + */ + static bool main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected) { @@ -2748,6 +2918,7 @@ main_draw(struct view *view, struct line *line, unsigned int lineno, bool select for (i = 0; i < commit->graph_size; i++) waddch(view->win, commit->graph[i]); + waddch(view->win, ' '); col += commit->graph_size + 1; } @@ -2788,129 +2959,11 @@ 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; - struct commit *commit; - size_t pos; -}; - -/* 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 inline void -append_to_rev_graph(struct rev_stack *stack, chtype symbol) -{ - stack->commit->graph[stack->commit->graph_size++] = symbol; -} - -static void -push_rev_stack(struct rev_stack *stack, char *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 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 (stack->pos >= stack->size) - symbol = REVGRAPH_BRANCH; - else - symbol = REVGRAPH_COMMIT; - - separator = ' '; - line = REVGRAPH_LINE; - - for (i = 0; i < stack->pos; i++) { - append_to_rev_graph(stack, line); - if (prev_parents->size > 1 && - i == graph_last_rev) { - separator = '`'; - line = '.'; - } - append_to_rev_graph(stack, separator); - } - - append_to_rev_graph(stack, symbol); - - separator = ' '; - line = REVGRAPH_LINE; - i++; - - for (; i < stack->size; i++) { - append_to_rev_graph(stack, separator); - append_to_rev_graph(stack, 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 i; - - stack->commit = commit; - - /* First traverse all lines of revisions up to the active one. */ - for (stack->pos = 0; stack->pos < stack->size; stack->pos++) { - if (!strcmp(stack->rev[stack->pos], commit->id)) - break; - - push_rev_stack(graph, stack->rev[stack->pos]); - } - - 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 = stack->pos + 1; i < stack->size; i++) - push_rev_stack(graph, stack->rev[i]); - - draw_rev_graph(stack, parents, prev_parents); - - graph_last_rev = stack->pos; - memset(stack, 0, sizeof(*stack)); - memset(prev_parents, 0, sizeof(*stack)); -} - /* Reads git log --pretty=raw output and parses it into the commit struct. */ static bool main_read(struct view *view, char *line) { + static struct rev_graph *graph = graph_stacks; enum line_type type = get_line_type(line); struct commit *commit = view->lines ? view->line[view->lines - 1].data : NULL; @@ -2926,12 +2979,13 @@ main_read(struct view *view, char *line) view->line[view->lines++].data = commit; string_copy(commit->id, line); commit->refs = get_refs(commit->id); + graph->commit = commit; break; case LINE_PARENT: if (commit) { line += STRING_SIZE("parent "); - push_rev_stack(&graph_parents[graph_stack_no & 1], line); + push_rev_graph(graph->parents, line); } break; @@ -2943,7 +2997,8 @@ main_read(struct view *view, char *line) if (!commit) break; - update_rev_graph(commit); + update_rev_graph(graph); + graph = graph->next; if (end) { char *email = end + 1;