Permalink
Browse files

column: add columnar layout

COL_COLUMN and COL_ROW fill column by column (or row by row
respectively), given the terminal width and how many space between
columns. All cells have equal width.

Strings are supposed to be in UTF-8. Valid ANSI escape strings are OK.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information...
1 parent 88e8f90 commit 077539d734cdc4b0a3d2ea87fc487fa5c21d0311 @pclouds pclouds committed with gitster Apr 13, 2012
Showing with 206 additions and 0 deletions.
  1. +4 −0 Documentation/config.txt
  2. +114 −0 column.c
  3. +2 −0 column.h
  4. +86 −0 t/t9002-column.sh
View
4 Documentation/config.txt
@@ -848,6 +848,10 @@ column.ui::
never show in columns
`auto`;;
show in columns if the output is to the terminal
+`column`;;
+ fill columns before rows (default)
+`row`;;
+ fill rows before columns
`plain`;;
show in one column
--
View
114 column.c
@@ -2,6 +2,59 @@
#include "column.h"
#include "string-list.h"
#include "parse-options.h"
+#include "utf8.h"
+
+#define XY2LINEAR(d, x, y) (COL_LAYOUT((d)->colopts) == COL_COLUMN ? \
+ (x) * (d)->rows + (y) : \
+ (y) * (d)->cols + (x))
+
+struct column_data {
+ const struct string_list *list;
+ unsigned int colopts;
+ struct column_options opts;
+
+ int rows, cols;
+ int *len; /* cell length */
+};
+
+/* return length of 's' in letters, ANSI escapes stripped */
+static int item_length(unsigned int colopts, const char *s)
+{
+ int len, i = 0;
+ struct strbuf str = STRBUF_INIT;
+
+ strbuf_addstr(&str, s);
+ while ((s = strstr(str.buf + i, "\033[")) != NULL) {
+ int len = strspn(s + 2, "0123456789;");
+ i = s - str.buf;
+ strbuf_remove(&str, i, len + 3); /* \033[<len><func char> */
+ }
+ len = utf8_strwidth(str.buf);
+ strbuf_release(&str);
+ return len;
+}
+
+/*
+ * Calculate cell width, rows and cols for a table of equal cells, given
+ * table width and how many spaces between cells.
+ */
+static void layout(struct column_data *data, int *width)
+{
+ int i;
+
+ *width = 0;
+ for (i = 0; i < data->list->nr; i++)
+ if (*width < data->len[i])
+ *width = data->len[i];
+
+ *width += data->opts.padding;
+
+ data->cols = (data->opts.width - strlen(data->opts.indent)) / *width;
+ if (data->cols == 0)
+ data->cols = 1;
+
+ data->rows = DIV_ROUND_UP(data->list->nr, data->cols);
+}
/* Display without layout when not enabled */
static void display_plain(const struct string_list *list,
@@ -13,6 +66,61 @@ static void display_plain(const struct string_list *list,
printf("%s%s%s", indent, list->items[i].string, nl);
}
+/* Print a cell to stdout with all necessary leading/traling space */
+static int display_cell(struct column_data *data, int initial_width,
+ const char *empty_cell, int x, int y)
+{
+ int i, len, newline;
+
+ i = XY2LINEAR(data, x, y);
+ if (i >= data->list->nr)
+ return -1;
+ len = data->len[i];
+ if (COL_LAYOUT(data->colopts) == COL_COLUMN)
+ newline = i + data->rows >= data->list->nr;
+ else
+ newline = x == data->cols - 1 || i == data->list->nr - 1;
+
+ printf("%s%s%s",
+ x == 0 ? data->opts.indent : "",
+ data->list->items[i].string,
+ newline ? data->opts.nl : empty_cell + len);
+ return 0;
+}
+
+/* Display COL_COLUMN or COL_ROW */
+static void display_table(const struct string_list *list,
+ unsigned int colopts,
+ const struct column_options *opts)
+{
+ struct column_data data;
+ int x, y, i, initial_width;
+ char *empty_cell;
+
+ memset(&data, 0, sizeof(data));
+ data.list = list;
+ data.colopts = colopts;
+ data.opts = *opts;
+
+ data.len = xmalloc(sizeof(*data.len) * list->nr);
+ for (i = 0; i < list->nr; i++)
+ data.len[i] = item_length(colopts, list->items[i].string);
+
+ layout(&data, &initial_width);
+
+ empty_cell = xmalloc(initial_width + 1);
+ memset(empty_cell, ' ', initial_width);
+ empty_cell[initial_width] = '\0';
+ for (y = 0; y < data.rows; y++) {
+ for (x = 0; x < data.cols; x++)
+ if (display_cell(&data, initial_width, empty_cell, x, y))
+ break;
+ }
+
+ free(data.len);
+ free(empty_cell);
+}
+
void print_columns(const struct string_list *list, unsigned int colopts,
const struct column_options *opts)
{
@@ -35,6 +143,10 @@ void print_columns(const struct string_list *list, unsigned int colopts,
case COL_PLAIN:
display_plain(list, nopts.indent, nopts.nl);
break;
+ case COL_ROW:
+ case COL_COLUMN:
+ display_table(list, colopts, &nopts);
+ break;
default:
die("BUG: invalid layout mode %d", COL_LAYOUT(colopts));
}
@@ -69,6 +181,8 @@ static int parse_option(const char *arg, int len, unsigned int *colopts,
{ "never", COL_DISABLED, COL_ENABLE_MASK },
{ "auto", COL_AUTO, COL_ENABLE_MASK },
{ "plain", COL_PLAIN, COL_LAYOUT_MASK },
+ { "column", COL_COLUMN, COL_LAYOUT_MASK },
+ { "row", COL_ROW, COL_LAYOUT_MASK },
};
int i;
View
2 column.h
@@ -10,6 +10,8 @@
#define COL_AUTO 0x0020
#define COL_LAYOUT(c) ((c) & COL_LAYOUT_MASK)
+#define COL_COLUMN 0 /* Fill columns before rows */
+#define COL_ROW 1 /* Fill rows before columns */
#define COL_PLAIN 15 /* one column */
#define explicitly_enable_column(c) \
View
86 t/t9002-column.sh
@@ -42,4 +42,90 @@ EOF
test_cmp expected actual
'
+test_expect_success '80 columns' '
+ cat >expected <<\EOF &&
+one two three four five six seven eight nine ten eleven
+EOF
+ COLUMNS=80 git column --mode=column <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'COLUMNS = 1' '
+ cat >expected <<\EOF &&
+one
+two
+three
+four
+five
+six
+seven
+eight
+nine
+ten
+eleven
+EOF
+ COLUMNS=1 git column --mode=column <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'width = 1' '
+ git column --mode=column --width=1 <lista >actual &&
+ test_cmp expected actual
+'
+
+COLUMNS=20
+export COLUMNS
+
+test_expect_success '20 columns' '
+ cat >expected <<\EOF &&
+one seven
+two eight
+three nine
+four ten
+five eleven
+six
+EOF
+ git column --mode=column <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, padding 2' '
+ cat >expected <<\EOF &&
+one seven
+two eight
+three nine
+four ten
+five eleven
+six
+EOF
+ git column --mode=column --padding 2 <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, indented' '
+ cat >expected <<\EOF &&
+ one seven
+ two eight
+ three nine
+ four ten
+ five eleven
+ six
+EOF
+ git column --mode=column --indent=" " <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first' '
+ cat >expected <<\EOF &&
+one two
+three four
+five six
+seven eight
+nine ten
+eleven
+EOF
+ git column --mode=row <lista >actual &&
+ test_cmp expected actual
+'
+
test_done

0 comments on commit 077539d

Please sign in to comment.