[PATCH 03/12] Add columnizer
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
.gitignore | 1 +
Makefile | 2 +
column.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
column.h | 23 ++++++++
command-list.txt | 1 +
5 files changed, 191 insertions(+), 0 deletions(-)
create mode 100644 column.c
create mode 100644 column.h
diff --git a/.gitignore b/.gitignore
index 7b3acb7..8e087e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,7 @@
/git-cherry-pick
/git-clean
/git-clone
+/git-column
/git-commit
/git-commit-tree
/git-config
diff --git a/Makefile b/Makefile
index 7358a20..337f5bf 100644
--- a/Makefile
+++ b/Makefile
@@ -454,6 +454,7 @@ LIB_H += builtin.h
LIB_H += cache.h
LIB_H += cache-tree.h
LIB_H += color.h
+LIB_H += column.h
LIB_H += commit.h
LIB_H += compat/bswap.h
LIB_H += compat/cygwin.h
@@ -526,6 +527,7 @@ LIB_OBJS += branch.o
LIB_OBJS += bundle.o
LIB_OBJS += cache-tree.o
LIB_OBJS += color.o
+LIB_OBJS += column.o
LIB_OBJS += combine-diff.o
LIB_OBJS += commit.o
LIB_OBJS += config.o
diff --git a/column.c b/column.c
new file mode 100644
index 0000000..0e88763
--- /dev/null
+++ b/column.c
@@ -0,0 +1,164 @@
+#include "cache.h"
+#include "column.h"
+
+static int ansi_length(const char *s)
+{
+ int a_len = 0;
+
+ while ((s = strstr(s, "\033[")) != NULL) {
+ int len = strspn(s+2, "0123456789;");
+ s += len+3; /* \033[<len><func char> */
+ a_len += len+3;
+ }
+ return a_len;
+}
+
+static void print_row(struct columnizer *cp, int row)
+{
+ int mode = cp->flags & COLUMNIZER_MODE_MASK;
+ int j;
+
+ if (cp->left_space)
+ printf("%-*s", cp->left_space, "");
+
+ for (j = 0; j < cp->nr_cols; j++) {
+ int n, size;
+
+ switch (mode) {
+ case COLUMNIZER_COLUMN_FIRST:
+ n = j * cp->nr_rows + row;
+ size = cp->column_width;
+ break;
+ case COLUMNIZER_ROW_FIRST:
+ n = row * cp->nr_cols + j;
+ size = cp->column_width;
+ break;
+ }
+
+ if (n >= cp->cells_nr)
+ break;
+
+ size += cp->space;
+ if (cp->flags & COLUMNIZER_HAVE_ANSI)
+ size += ansi_length(cp->cells[n]);
+ if (j == cp->nr_cols-1)
+ size = 1;
+ printf("%-*s", size, cp->cells[n]);
+ }
+ putchar('\n');
+}
+
+static void calculate_column_width(struct columnizer *cp)
+{
+ int max_cols, longest;
+
+ if (cp->flags & COLUMNIZER_READY)
+ return;
+
+ if (!cp->terminal_width)
+ cp->terminal_width = term_columns();
+
+ if (cp->column_width)
+ longest = cp->column_width;
+ else {
+ int i, len;
+ longest = 0;
+ for (i = 0; i < cp->cells_nr; i++) {
+ len = strlen(cp->cells[i]);
+ if (cp->flags & COLUMNIZER_HAVE_ANSI)
+ len -= ansi_length(cp->cells[i]);
+ if (longest < len)
+ longest = len;
+ }
+ }
+
+ max_cols = cp->terminal_width - 1; /* don't print *on* the edge */
+ max_cols -= cp->left_space + cp->right_space;
+
+ /* we don't have space after the last column */
+ max_cols += cp->space;
+
+ cp->nr_cols = (longest + cp->space) < max_cols ? max_cols / (longest + cp->space) : 1;
+ cp->nr_rows = DIV_ROUND_UP(cp->cells_nr, cp->nr_cols);
+ if (!cp->column_width)
+ cp->column_width = max_cols / cp->nr_cols - cp->space;
+ cp->flags |= COLUMNIZER_READY;
+}
+
+static int feed_cell(struct columnizer *cp, char *cell)
+{
+ int len;
+
+ /* Non-homogeneous column is not supported yet */
+ if (!(cp->flags & COLUMNIZER_HOMOGENEOUS))
+ return -1;
+
+ if (!cell) { /* EOF */
+ int i;
+
+ calculate_column_width(cp);
+ for (i = 0;i < cp->nr_rows; i++)
+ print_row(cp, i);
+ return 0;
+ }
+
+ cp->cells_nr++;
+ ALLOC_GROW(cp->cells, cp->cells_nr, cp->cells_alloc);
+ cp->cells[cp->cells_nr-1] = cell;
+
+ len = strlen(cell);
+ if (cp->flags & COLUMNIZER_HAVE_ANSI)
+ len -= ansi_length(cell);
+
+ if (!cp->terminal_width)
+ cp->terminal_width = term_columns();
+ if (cp->longest < len)
+ cp->longest = len;
+
+ /* Can't have more than one column? */
+ if (cp->terminal_width < cp->left_space+cp->longest*2+cp->space+cp->right_space) {
+ int i;
+ for (i = 0;i < cp->cells_nr;i++) {
+ if (cp->left_space)
+ printf("%-*s", cp->left_space, "");
+ printf("%s\n", cp->cells[i]);
+ if (cp->detach)
+ cp->detach(cp->cells[i]);
+ }
+ free(cp->cells);
+ cp->cells_alloc = cp->cells_nr = 0;
+
+ cp->nr_cols = 1;
+ cp->flags |= COLUMNIZER_READY;
+ }
+ return 0;
+}
+
+int feed_columnizer(struct columnizer *cp, char *cell)
+{
+ int ret;
+
+ /* Degradation case, no special layout needed */
+ if (cp->flags & COLUMNIZER_READY && cp->nr_cols == 1) {
+ if (cell) {
+ if (cp->left_space)
+ printf("%-*s", cp->left_space, "");
+ printf("%s\n", cell);
+ }
+ ret = 0;
+ }
+ else
+ ret = feed_cell(cp, cell);
+
+ if (!cell) { /* EOF, cleanup */
+ if (cp->detach) {
+ int i;
+ for (i = 0;i < cp->cells_nr;i++)
+ cp->detach(cp->cells[i]);
+ }
+ if (cp->cells_alloc)
+ free(cp->cells);
+ }
+
+ return ret;
+}
diff --git a/column.h b/column.h
new file mode 100644
index 0000000..28122e0
--- /dev/null
+++ b/column.h
@@ -0,0 +1,23 @@
+#define COLUMNIZER_MODE_MASK 0x000F
+#define COLUMNIZER_COLUMN_FIRST 0
+#define COLUMNIZER_ROW_FIRST 1
+
+#define COLUMNIZER_HOMOGENEOUS 0x0010
+#define COLUMNIZER_HAVE_ANSI 0x0020
+#define COLUMNIZER_READY 0x0040
+
+struct columnizer {
+ int flags;
+ int terminal_width, column_width;
+ int left_space, right_space, space;
+ void (*detach)(void *p);
+
+ /* private variables */
+ char **cells;
+ int cells_nr, cells_alloc;
+ int nr_cols, nr_rows;
+
+ int longest;
+};
+
+extern int feed_columnizer(struct columnizer *cp, char *cell);
diff --git a/command-list.txt b/command-list.txt
index 95bf18c..5d6bfc8 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -20,6 +20,7 @@ git-cherry-pick mainporcelain
git-citool mainporcelain
git-clean mainporcelain
git-clone mainporcelain common
+git-column purehelpers
git-commit mainporcelain common
git-commit-tree plumbingmanipulators
git-config ancillarymanipulators
--
1.7.0.1.370.gd3c5