diff options
-rw-r--r-- | Documentation/config.txt | 4 | ||||
-rw-r--r-- | column.c | 114 | ||||
-rw-r--r-- | column.h | 2 | ||||
-rwxr-xr-x | t/t9002-column.sh | 86 |
4 files changed, 206 insertions, 0 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt index 9aabef124c..ab6ae3da72 100644 --- a/Documentation/config.txt +++ b/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 -- @@ -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; @@ -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) \ diff --git a/t/t9002-column.sh b/t/t9002-column.sh index a7f3cd9285..ec288aeb95 100755 --- a/t/t9002-column.sh +++ b/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 |