1 /* Copyright (c) 2006-2014 Jonas Fonseca <jonas.fonseca@gmail.com>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
16 #include "tig/graph.h"
17 #include "compat/hashtab.h"
22 unsigned int commit
:1;
23 unsigned int boundary
:1;
24 unsigned int initial
:1;
27 unsigned int continued_down
:1;
28 unsigned int continued_up
:1;
29 unsigned int continued_right
:1;
30 unsigned int continued_left
:1;
31 unsigned int continued_up_left
:1;
33 unsigned int parent_down
:1;
34 unsigned int parent_right
:1;
36 unsigned int below_commit
:1;
37 unsigned int flanked
:1;
38 unsigned int next_right
:1;
39 unsigned int matches_commit
:1;
41 unsigned int shift_left
:1;
42 unsigned int continue_shift
:1;
43 unsigned int below_shift
:1;
45 unsigned int new_column
:1;
50 struct graph_symbol symbol
;
51 char id
[SIZEOF_REV
]; /* Parent SHA1 ID. */
56 struct graph_column
*columns
;
61 size_t count
[GRAPH_COLORS
];
67 struct graph_row parents
;
68 struct graph_row prev_row
;
69 struct graph_row next_row
;
79 DEFINE_ALLOCATOR(realloc_graph_columns
, struct graph_column
, 32)
80 DEFINE_ALLOCATOR(realloc_graph_symbols
, struct graph_symbol
, 1)
88 id_color_new(const char *id
, size_t color
)
90 struct id_color
*node
= malloc(sizeof(struct id_color
));
92 node
->id
= (char *) malloc(strlen(id
) + 1);
100 id_color_delete(struct id_color
*node
)
107 id_color_eq(const void *entry
, const void *element
)
109 return strcmp(((const struct id_color
*) entry
)->id
, ((const struct id_color
*) element
)->id
) == 0;
115 id_color_delete((struct id_color
*) key
);
119 id_color_hash(const void *node
)
121 return htab_hash_string(((const struct id_color
*) node
)->id
);
125 colors_add_id(struct colors
*colors
, const char *id
, const size_t color
)
127 struct id_color
*node
= id_color_new(id
, color
);
128 void **slot
= htab_find_slot(colors
->id_map
, node
, INSERT
);
130 if (slot
!= NULL
&& *slot
== NULL
) {
132 colors
->count
[color
]++;
134 id_color_delete(node
);
139 colors_remove_id(struct colors
*colors
, const char *id
)
141 struct id_color
*node
= id_color_new(id
, 0);
142 void **slot
= htab_find_slot(colors
->id_map
, node
, NO_INSERT
);
144 if (slot
!= NULL
&& *slot
!= NULL
) {
145 colors
->count
[((struct id_color
*) *slot
)->color
]--;
146 htab_clear_slot(colors
->id_map
, slot
);
149 id_color_delete(node
);
153 colors_get_color(struct colors
*colors
, const char *id
)
155 struct id_color
*key
= id_color_new(id
, 0);
156 struct id_color
*node
= (struct id_color
*) htab_find(colors
->id_map
, key
);
158 id_color_delete(key
);
161 return (size_t) -1; // Max value of size_t. ID not found.
167 colors_get_free_color(struct colors
*colors
)
169 size_t free_color
= 0;
170 size_t lowest
= (size_t) -1; // Max value of size_t
173 for (i
= 0; i
< ARRAY_SIZE(colors
->count
); i
++) {
174 if (colors
->count
[i
] < lowest
) {
175 lowest
= colors
->count
[i
];
183 colors_init(struct colors
*colors
)
185 if (colors
->id_map
== NULL
) {
188 colors
->id_map
= htab_create_alloc(size
, id_color_hash
, id_color_eq
, key_del
, calloc
, free
);
193 get_color(struct graph_v2
*graph
, char *new_id
)
197 colors_init(&graph
->colors
);
198 color
= colors_get_color(&graph
->colors
, new_id
);
200 if (color
< (size_t) -1) {
204 color
= colors_get_free_color(&graph
->colors
);
205 colors_add_id(&graph
->colors
, new_id
, color
);
211 done_graph_rendering(struct graph
*graph_ref
)
213 struct graph_v2
*graph
= graph_ref
->private;
215 free(graph
->prev_row
.columns
);
216 free(graph
->row
.columns
);
217 free(graph
->next_row
.columns
);
218 free(graph
->parents
.columns
);
222 done_graph(struct graph
*graph_ref
)
224 struct graph_v2
*graph
= graph_ref
->private;
229 #define graph_column_has_commit(col) ((col)->id[0])
232 graph_find_column_by_id(struct graph_row
*row
, const char *id
)
234 size_t free_column
= row
->size
;
237 for (i
= 0; i
< row
->size
; i
++) {
238 if (!graph_column_has_commit(&row
->columns
[i
]) && free_column
== row
->size
)
240 else if (!strcmp(row
->columns
[i
].id
, id
))
248 graph_find_free_column(struct graph_row
*row
)
252 for (i
= 0; i
< row
->size
; i
++) {
253 if (!graph_column_has_commit(&row
->columns
[i
]))
260 static struct graph_column
*
261 graph_insert_column(struct graph_v2
*graph
, struct graph_row
*row
, size_t pos
, const char *id
)
263 struct graph_column
*column
;
265 if (!realloc_graph_columns(&row
->columns
, row
->size
, 1))
268 column
= &row
->columns
[pos
];
269 if (pos
< row
->size
) {
270 memmove(column
+ 1, column
, sizeof(*column
) * (row
->size
- pos
));
274 memset(column
, 0, sizeof(*column
));
275 string_copy_rev(column
->id
, id
);
276 column
->symbol
.boundary
= !!graph
->is_boundary
;
282 graph_add_parent(struct graph
*graph_ref
, const char *parent
)
284 struct graph_v2
*graph
= graph_ref
->private;
286 if (graph
->has_parents
)
288 return graph_insert_column(graph
, &graph
->parents
, graph
->parents
.size
, parent
) != NULL
;
292 graph_needs_expansion(struct graph_v2
*graph
)
294 return graph
->position
+ graph
->parents
.size
> graph
->row
.size
;
298 graph_expand(struct graph_v2
*graph
)
300 while (graph_needs_expansion(graph
)) {
301 if (!graph_insert_column(graph
, &graph
->prev_row
, graph
->prev_row
.size
, ""))
304 if (!graph_insert_column(graph
, &graph
->row
, graph
->row
.size
, ""))
307 if (!graph_insert_column(graph
, &graph
->next_row
, graph
->next_row
.size
, ""))
315 graph_needs_collapsing(struct graph_v2
*graph
)
317 return graph
->row
.size
> 1
318 && !graph_column_has_commit(&graph
->row
.columns
[graph
->row
.size
- 1]);
322 graph_collapse(struct graph_v2
*graph
)
324 while (graph_needs_collapsing(graph
)) {
325 graph
->prev_row
.size
--;
327 graph
->next_row
.size
--;
334 graph_canvas_append_symbol(struct graph_v2
*graph
, struct graph_canvas
*canvas
, struct graph_symbol
*symbol
)
336 if (realloc_graph_symbols(&canvas
->symbols
, canvas
->size
, 1))
337 canvas
->symbols
[canvas
->size
++] = *symbol
;
341 graph_row_clear_commit(struct graph_row
*row
, const char *id
)
345 for (i
= 0; i
< row
->size
; i
++) {
346 if (strcmp(row
->columns
[i
].id
, id
) == 0) {
347 row
->columns
[i
].id
[0] = 0;
353 graph_insert_parents(struct graph_v2
*graph
)
355 struct graph_row
*prev_row
= &graph
->prev_row
;
356 struct graph_row
*row
= &graph
->row
;
357 struct graph_row
*next_row
= &graph
->next_row
;
358 struct graph_row
*parents
= &graph
->parents
;
361 for (i
= 0; i
< parents
->size
; i
++) {
362 struct graph_column
*new = &parents
->columns
[i
];
364 if (graph_column_has_commit(new)) {
365 size_t match
= graph_find_free_column(next_row
);
367 if (match
== next_row
->size
&& next_row
->columns
[next_row
->size
- 1].id
) {
368 graph_insert_column(graph
, next_row
, next_row
->size
, new->id
);
369 graph_insert_column(graph
, row
, row
->size
, "");
370 graph_insert_column(graph
, prev_row
, prev_row
->size
, "");
372 next_row
->columns
[match
] = *new;
379 commit_is_in_row(const char *id
, struct graph_row
*row
)
383 for (i
= 0; i
< row
->size
; i
++) {
384 if (!graph_column_has_commit(&row
->columns
[i
]))
387 if (strcmp(id
, row
->columns
[i
].id
) == 0)
394 graph_remove_collapsed_columns(struct graph_v2
*graph
)
396 struct graph_row
*row
= &graph
->next_row
;
399 for (i
= row
->size
- 1; i
> 0; i
--) {
400 if (i
== graph
->position
)
403 if (i
== graph
->position
+ 1)
406 if (strcmp(row
->columns
[i
].id
, graph
->id
) == 0)
409 if (strcmp(row
->columns
[i
].id
, row
->columns
[i
- 1].id
) != 0)
412 if (commit_is_in_row(row
->columns
[i
].id
, &graph
->parents
) && !graph_column_has_commit(&graph
->prev_row
.columns
[i
]))
415 if (strcmp(row
->columns
[i
- 1].id
, graph
->prev_row
.columns
[i
- 1].id
) != 0 || graph
->prev_row
.columns
[i
- 1].symbol
.shift_left
)
416 row
->columns
[i
] = row
->columns
[i
+ 1];
421 graph_fill_empty_columns(struct graph_v2
*graph
)
423 struct graph_row
*row
= &graph
->next_row
;
426 for (i
= row
->size
- 2; i
>= 0; i
--) {
427 if (!graph_column_has_commit(&row
->columns
[i
])) {
428 row
->columns
[i
] = row
->columns
[i
+ 1];
434 graph_generate_next_row(struct graph_v2
*graph
)
436 graph_row_clear_commit(&graph
->next_row
, graph
->id
);
437 graph_insert_parents(graph
);
438 graph_remove_collapsed_columns(graph
);
439 graph_fill_empty_columns(graph
);
443 commits_in_row(struct graph_row
*row
)
448 for (i
= 0; i
< row
->size
;i
++) {
449 if (graph_column_has_commit(&row
->columns
[i
]))
457 graph_commit_next_row(struct graph_v2
*graph
)
461 for (i
= 0; i
< graph
->row
.size
; i
++) {
462 graph
->prev_row
.columns
[i
] = graph
->row
.columns
[i
];
464 if (i
== graph
->position
&& commits_in_row(&graph
->parents
) > 0)
465 graph
->prev_row
.columns
[i
] = graph
->next_row
.columns
[i
];
467 if (!graph_column_has_commit(&graph
->prev_row
.columns
[i
]))
468 graph
->prev_row
.columns
[i
] = graph
->next_row
.columns
[i
];
470 graph
->row
.columns
[i
] = graph
->next_row
.columns
[i
];
473 graph
->prev_position
= graph
->position
;
477 continued_down(struct graph_row
*row
, struct graph_row
*next_row
, int pos
)
479 if (strcmp(row
->columns
[pos
].id
, next_row
->columns
[pos
].id
) != 0)
482 if (row
->columns
[pos
].symbol
.shift_left
)
489 shift_left(struct graph_row
*row
, struct graph_row
*prev_row
, int pos
)
493 if (!graph_column_has_commit(&row
->columns
[pos
]))
496 for (i
= pos
- 1; i
>= 0; i
--) {
497 if (!graph_column_has_commit(&row
->columns
[i
]))
500 if (strcmp(row
->columns
[i
].id
, row
->columns
[pos
].id
) != 0)
503 if (!continued_down(prev_row
, row
, i
))
513 new_column(struct graph_row
*row
, struct graph_row
*prev_row
, int pos
)
517 if (!graph_column_has_commit(&prev_row
->columns
[pos
]))
520 for (i
= pos
; i
< row
->size
; i
++) {
521 if (strcmp(row
->columns
[pos
].id
, prev_row
->columns
[i
].id
) == 0)
529 continued_right(struct graph_row
*row
, int pos
, int commit_pos
)
533 if (pos
< commit_pos
)
538 for (i
= pos
+ 1; i
< end
; i
++) {
539 if (strcmp(row
->columns
[pos
].id
, row
->columns
[i
].id
) == 0)
547 continued_left(struct graph_row
*row
, int pos
, int commit_pos
)
551 if (pos
< commit_pos
)
556 for (i
= start
; i
< pos
; i
++) {
557 if (!graph_column_has_commit(&row
->columns
[i
]))
560 if (strcmp(row
->columns
[pos
].id
, row
->columns
[i
].id
) == 0)
568 parent_down(struct graph_row
*parents
, struct graph_row
*next_row
, int pos
)
572 for (parent
= 0; parent
< parents
->size
; parent
++) {
573 if (!graph_column_has_commit(&parents
->columns
[parent
]))
576 if (strcmp(parents
->columns
[parent
].id
, next_row
->columns
[pos
].id
) == 0)
584 parent_right(struct graph_row
*parents
, struct graph_row
*row
, struct graph_row
*next_row
, int pos
)
588 for (parent
= 0; parent
< parents
->size
; parent
++) {
589 if (!graph_column_has_commit(&parents
->columns
[parent
]))
592 for (i
= pos
+ 1; i
< next_row
->size
; i
++) {
593 if (strcmp(parents
->columns
[parent
].id
, next_row
->columns
[i
].id
) != 0)
596 if (strcmp(parents
->columns
[parent
].id
, row
->columns
[i
].id
) != 0)
605 flanked(struct graph_row
*row
, int pos
, int commit_pos
, const char *commit_id
)
609 if (pos
< commit_pos
) {
617 for (i
= start
; i
< end
; i
++) {
618 if (strcmp(row
->columns
[i
].id
, commit_id
) == 0)
626 below_commit(int pos
, struct graph_v2
*graph
)
628 if (!pos
== graph
->prev_position
)
631 if (!strcmp(graph
->row
.columns
[pos
].id
, graph
->prev_row
.columns
[pos
].id
) == 0)
638 graph_generate_symbols(struct graph_v2
*graph
, struct graph_canvas
*canvas
)
640 struct graph_row
*prev_row
= &graph
->prev_row
;
641 struct graph_row
*row
= &graph
->row
;
642 struct graph_row
*next_row
= &graph
->next_row
;
643 struct graph_row
*parents
= &graph
->parents
;
646 for (pos
= 0; pos
< row
->size
; pos
++) {
647 struct graph_column
*column
= &row
->columns
[pos
];
648 struct graph_symbol
*symbol
= &column
->symbol
;
649 char *id
= next_row
->columns
[pos
].id
;
651 symbol
->commit
= (pos
== graph
->position
);
652 symbol
->boundary
= (pos
== graph
->position
&& next_row
->columns
[pos
].symbol
.boundary
);
653 symbol
->initial
= (commits_in_row(parents
) < 1);
654 symbol
->merge
= (commits_in_row(parents
) > 1);
656 symbol
->continued_down
= continued_down(row
, next_row
, pos
);
657 symbol
->continued_up
= continued_down(prev_row
, row
, pos
);
658 symbol
->continued_right
= continued_right(row
, pos
, graph
->position
);
659 symbol
->continued_left
= continued_left(row
, pos
, graph
->position
);
660 symbol
->continued_up_left
= continued_left(prev_row
, pos
, prev_row
->size
);
662 symbol
->parent_down
= parent_down(parents
, next_row
, pos
);
663 symbol
->parent_right
= (pos
> graph
->position
&& parent_right(parents
, row
, next_row
, pos
));
665 symbol
->below_commit
= below_commit(pos
, graph
);
666 symbol
->flanked
= flanked(row
, pos
, graph
->position
, graph
->id
);
667 symbol
->next_right
= continued_right(next_row
, pos
, 0);
668 symbol
->matches_commit
= (strcmp(column
->id
, graph
->id
) == 0);
670 symbol
->shift_left
= shift_left(row
, prev_row
, pos
);
671 symbol
->continue_shift
= shift_left(row
, prev_row
, pos
+ 1);
672 symbol
->below_shift
= prev_row
->columns
[pos
].symbol
.shift_left
;
674 symbol
->new_column
= new_column(row
, prev_row
, pos
);
675 symbol
->empty
= (!graph_column_has_commit(&row
->columns
[pos
]));
677 if (graph_column_has_commit(column
)) {
680 symbol
->color
= get_color(graph
, id
);
682 graph_canvas_append_symbol(graph
, canvas
, symbol
);
685 colors_remove_id(&graph
->colors
, graph
->id
);
689 graph_render_parents(struct graph
*graph_ref
, struct graph_canvas
*canvas
)
691 struct graph_v2
*graph
= graph_ref
->private;
693 if (graph
->parents
.size
== 0 &&
694 !graph_add_parent(graph_ref
, ""))
697 if (!graph_expand(graph
))
700 graph_generate_next_row(graph
);
701 graph_generate_symbols(graph
, canvas
);
702 graph_commit_next_row(graph
);
704 graph
->parents
.size
= graph
->position
= 0;
706 if (!graph_collapse(graph
))
713 graph_add_commit(struct graph
*graph_ref
, struct graph_canvas
*canvas
,
714 const char *id
, const char *parents
, bool is_boundary
)
716 struct graph_v2
*graph
= graph_ref
->private;
719 graph
->position
= graph_find_column_by_id(&graph
->row
, id
);
720 string_copy_rev(graph
->id
, id
);
721 graph
->is_boundary
= is_boundary
;
722 graph
->has_parents
= FALSE
;
724 while ((parents
= strchr(parents
, ' '))) {
726 if (!graph_add_parent(graph_ref
, parents
))
731 graph
->has_parents
= has_parents
> 0;
737 graph_symbol_forks(const struct graph_symbol
*symbol
)
739 if (!symbol
->continued_down
)
742 if (!symbol
->continued_right
)
745 if (!symbol
->continued_up
)
752 graph_symbol_cross_over(const struct graph_symbol
*symbol
)
757 if (!symbol
->continued_down
)
760 if (!symbol
->continued_up
&& !symbol
->new_column
&& !symbol
->below_commit
)
763 if (symbol
->shift_left
)
766 if (symbol
->parent_right
&& symbol
->merge
)
776 graph_symbol_turn_left(const struct graph_symbol
*symbol
)
778 if (symbol
->matches_commit
&& symbol
->continued_right
&& !symbol
->continued_down
)
781 if (symbol
->continue_shift
)
784 if (symbol
->continued_up
|| symbol
->new_column
|| symbol
->below_commit
) {
785 if (symbol
->matches_commit
)
788 if (symbol
->shift_left
)
796 graph_symbol_turn_down_cross_over(const struct graph_symbol
*symbol
)
798 if (!symbol
->continued_down
)
801 if (!symbol
->continued_right
)
814 graph_symbol_turn_down(const struct graph_symbol
*symbol
)
816 if (!symbol
->continued_down
)
819 if (!symbol
->continued_right
)
826 graph_symbol_merge(const struct graph_symbol
*symbol
)
828 if (symbol
->continued_down
)
831 if (!symbol
->parent_down
)
834 if (symbol
->parent_right
)
841 graph_symbol_multi_merge(const struct graph_symbol
*symbol
)
843 if (!symbol
->parent_down
)
846 if (!symbol
->parent_right
)
853 graph_symbol_vertical_bar(const struct graph_symbol
*symbol
)
858 if (symbol
->shift_left
)
861 if (!symbol
->continued_down
)
864 if (symbol
->continued_up
)
867 if (symbol
->parent_right
)
873 if (symbol
->continued_right
)
880 graph_symbol_horizontal_bar(const struct graph_symbol
*symbol
)
882 if (symbol
->shift_left
)
885 if (symbol
->continued_down
)
888 if (!symbol
->parent_right
&& !symbol
->continued_right
)
891 if ((symbol
->continued_up
&& !symbol
->continued_up_left
))
894 if (!symbol
->below_commit
)
901 graph_symbol_multi_branch(const struct graph_symbol
*symbol
)
903 if (symbol
->continued_down
)
906 if (!symbol
->continued_right
)
909 if (symbol
->below_shift
)
912 if (symbol
->continued_up
|| symbol
->new_column
|| symbol
->below_commit
) {
913 if (symbol
->matches_commit
)
916 if (symbol
->shift_left
)
924 graph_symbol_to_utf8(const struct graph_symbol
*symbol
)
926 if (symbol
->commit
) {
927 if (symbol
->boundary
)
929 else if (symbol
->initial
)
931 else if (symbol
->merge
)
936 if (graph_symbol_cross_over(symbol
))
939 if (graph_symbol_vertical_bar(symbol
))
942 if (graph_symbol_turn_left(symbol
))
945 if (graph_symbol_multi_branch(symbol
))
948 if (graph_symbol_horizontal_bar(symbol
))
951 if (graph_symbol_forks(symbol
))
954 if (graph_symbol_turn_down_cross_over(symbol
))
957 if (graph_symbol_turn_down(symbol
))
960 if (graph_symbol_merge(symbol
))
963 if (graph_symbol_multi_merge(symbol
))
969 static const chtype
*
970 graph_symbol_to_chtype(const struct graph_symbol
*symbol
)
972 static chtype graphics
[2];
974 if (symbol
->commit
) {
976 if (symbol
->boundary
)
978 else if (symbol
->initial
)
980 else if (symbol
->merge
)
983 graphics
[1] = 'o'; //ACS_DIAMOND; //'*';
986 } else if (graph_symbol_cross_over(symbol
)) {
987 graphics
[0] = ACS_HLINE
;
988 graphics
[1] = ACS_VLINE
;
990 } else if (graph_symbol_vertical_bar(symbol
)) {
992 graphics
[1] = ACS_VLINE
;
994 } else if (graph_symbol_turn_left(symbol
)) {
995 graphics
[0] = ACS_HLINE
;
996 graphics
[1] = ACS_LRCORNER
;
998 } else if (graph_symbol_multi_branch(symbol
)) {
999 graphics
[0] = ACS_HLINE
;
1000 graphics
[1] = ACS_BTEE
;
1002 } else if (graph_symbol_horizontal_bar(symbol
)) {
1003 graphics
[0] = graphics
[1] = ACS_HLINE
;
1005 } else if (graph_symbol_forks(symbol
)) {
1007 graphics
[1] = ACS_LTEE
;
1009 } else if (graph_symbol_turn_down_cross_over(symbol
)) {
1010 graphics
[0] = ACS_HLINE
;
1011 graphics
[1] = ACS_ULCORNER
;
1013 } else if (graph_symbol_turn_down(symbol
)) {
1015 graphics
[1] = ACS_ULCORNER
;
1017 } else if (graph_symbol_merge(symbol
)) {
1018 graphics
[0] = ACS_HLINE
;
1019 graphics
[1] = ACS_URCORNER
;
1021 } else if (graph_symbol_multi_merge(symbol
)) {
1022 graphics
[0] = ACS_HLINE
;
1023 graphics
[1] = ACS_TTEE
;
1026 graphics
[0] = graphics
[1] = ' ';
1033 graph_symbol_to_ascii(const struct graph_symbol
*symbol
)
1035 if (symbol
->commit
) {
1036 if (symbol
->boundary
)
1038 else if (symbol
->initial
)
1040 else if (symbol
->merge
)
1045 if (graph_symbol_cross_over(symbol
))
1048 if (graph_symbol_vertical_bar(symbol
))
1051 if (graph_symbol_turn_left(symbol
))
1054 if (graph_symbol_multi_branch(symbol
))
1057 if (graph_symbol_horizontal_bar(symbol
))
1060 if (graph_symbol_forks(symbol
))
1063 if (graph_symbol_turn_down_cross_over(symbol
))
1066 if (graph_symbol_turn_down(symbol
))
1069 if (graph_symbol_merge(symbol
))
1072 if (graph_symbol_multi_merge(symbol
))
1079 graph_foreach_symbol(const struct graph
*graph
, const struct graph_canvas
*canvas
,
1080 graph_symbol_iterator_fn fn
, void *data
)
1084 for (i
= 0; i
< canvas
->size
; i
++) {
1085 struct graph_symbol
*symbol
= &canvas
->symbols
[i
];
1086 int color_id
= symbol
->commit
? GRAPH_COMMIT_COLOR
: symbol
->color
;
1088 if (fn(data
, graph
, symbol
, color_id
, i
== 0))
1096 struct graph_v2
*graph
= calloc(1, sizeof(*graph
));
1103 api
->private = graph
;
1104 api
->done
= done_graph
;
1105 api
->done_rendering
= done_graph_rendering
;
1106 api
->add_commit
= graph_add_commit
;
1107 api
->add_parent
= graph_add_parent
;
1108 api
->render_parents
= graph_render_parents
;
1109 api
->foreach_symbol
= graph_foreach_symbol
;
1110 api
->symbol_to_ascii
= graph_symbol_to_ascii
;
1111 api
->symbol_to_utf8
= graph_symbol_to_utf8
;
1112 api
->symbol_to_chtype
= graph_symbol_to_chtype
;
1117 /* vim: set ts=8 sw=8 noexpandtab: */