Skip to content

Commit

Permalink
column: add dense layout support
Browse files Browse the repository at this point in the history
Normally all cells (and in turn columns) share the same width. This
layout mode can waste space because one long item can stretch our all
columns.

With COL_DENSE enabled, column width is calculated indepdendently. All
columns are shrunk to minimum, then it attempts to push cells of the
last row over to the next column with hope that everything still fits
even there's one row less. The process is repeated until the new layout
cannot fit in given width any more, or there's only one row left
(perfect!).

Apparently, this mode consumes more cpu than the old one, but it makes
better use of terminal space. For layouting one or two screens, cpu
usage should not be detectable.

This patch introduces option handling code besides layout modes and
enable/disable to expose this feature as "dense". The feature can be
turned off by specifying "nodense".

Thanks-to: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
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
pclouds authored and gitster committed Apr 27, 2012
1 parent f78b1c5 commit 3f8eccb
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 1 deletion.
4 changes: 4 additions & 0 deletions Documentation/config.txt
Expand Up @@ -854,6 +854,10 @@ column.ui::
fill rows before columns
`plain`;;
show in one column
`dense`;;
make unequal size columns to utilize more space
`nodense`;;
make equal size columns
--
+
This option defaults to 'never'.
Expand Down
84 changes: 83 additions & 1 deletion column.c
Expand Up @@ -15,6 +15,7 @@ struct column_data {

int rows, cols;
int *len; /* cell length */
int *width; /* index to the longest row in column */
};

/* return length of 's' in letters, ANSI escapes stripped */
Expand Down Expand Up @@ -56,6 +57,57 @@ static void layout(struct column_data *data, int *width)
data->rows = DIV_ROUND_UP(data->list->nr, data->cols);
}

static void compute_column_width(struct column_data *data)
{
int i, x, y;
for (x = 0; x < data->cols; x++) {
data->width[x] = XY2LINEAR(data, x, 0);
for (y = 0; y < data->rows; y++) {
i = XY2LINEAR(data, x, y);
if (i < data->list->nr &&
data->len[data->width[x]] < data->len[i])
data->width[x] = i;
}
}
}

/*
* Shrink all columns by shortening them one row each time (and adding
* more columns along the way). Hopefully the longest cell will be
* moved to the next column, column is shrunk so we have more space
* for new columns. The process ends when the whole thing no longer
* fits in data->total_width.
*/
static void shrink_columns(struct column_data *data)
{
data->width = xrealloc(data->width,
sizeof(*data->width) * data->cols);
while (data->rows > 1) {
int x, total_width, cols, rows;
rows = data->rows;
cols = data->cols;

data->rows--;
data->cols = DIV_ROUND_UP(data->list->nr, data->rows);
if (data->cols != cols)
data->width = xrealloc(data->width,
sizeof(*data->width) * data->cols);
compute_column_width(data);

total_width = strlen(data->opts.indent);
for (x = 0; x < data->cols; x++) {
total_width += data->len[data->width[x]];
total_width += data->opts.padding;
}
if (total_width > data->opts.width) {
data->rows = rows;
data->cols = cols;
break;
}
}
compute_column_width(data);
}

/* Display without layout when not enabled */
static void display_plain(const struct string_list *list,
const char *indent, const char *nl)
Expand All @@ -75,7 +127,18 @@ static int display_cell(struct column_data *data, int initial_width,
i = XY2LINEAR(data, x, y);
if (i >= data->list->nr)
return -1;

len = data->len[i];
if (data->width && data->len[data->width[x]] < initial_width) {
/*
* empty_cell has initial_width chars, if real column
* is narrower, increase len a bit so we fill less
* space.
*/
len += initial_width - data->len[data->width[x]];
len -= data->opts.padding;
}

if (COL_LAYOUT(data->colopts) == COL_COLUMN)
newline = i + data->rows >= data->list->nr;
else
Expand Down Expand Up @@ -108,6 +171,9 @@ static void display_table(const struct string_list *list,

layout(&data, &initial_width);

if (colopts & COL_DENSE)
shrink_columns(&data);

empty_cell = xmalloc(initial_width + 1);
memset(empty_cell, ' ', initial_width);
empty_cell[initial_width] = '\0';
Expand All @@ -118,6 +184,7 @@ static void display_table(const struct string_list *list,
}

free(data.len);
free(data.width);
free(empty_cell);
}

Expand Down Expand Up @@ -183,13 +250,22 @@ static int parse_option(const char *arg, int len, unsigned int *colopts,
{ "plain", COL_PLAIN, COL_LAYOUT_MASK },
{ "column", COL_COLUMN, COL_LAYOUT_MASK },
{ "row", COL_ROW, COL_LAYOUT_MASK },
{ "dense", COL_DENSE, 0 },
};
int i;

for (i = 0; i < ARRAY_SIZE(opts); i++) {
int arg_len = len, name_len;
int set = 1, arg_len = len, name_len;
const char *arg_str = arg;

if (!opts[i].mask) {
if (arg_len > 2 && !strncmp(arg_str, "no", 2)) {
arg_str += 2;
arg_len -= 2;
set = 0;
}
}

name_len = strlen(opts[i].name);
if (arg_len != name_len ||
strncmp(arg_str, opts[i].name, name_len))
Expand All @@ -206,6 +282,12 @@ static int parse_option(const char *arg, int len, unsigned int *colopts,

if (opts[i].mask)
*colopts = (*colopts & ~opts[i].mask) | opts[i].value;
else {
if (set)
*colopts |= opts[i].value;
else
*colopts &= ~opts[i].value;
}
return 0;
}

Expand Down
2 changes: 2 additions & 0 deletions column.h
Expand Up @@ -4,6 +4,8 @@
#define COL_LAYOUT_MASK 0x000F
#define COL_ENABLE_MASK 0x0030 /* always, never or auto */
#define COL_PARSEOPT 0x0040 /* --column is given from cmdline */
#define COL_DENSE 0x0080 /* Shrink columns when possible,
making space for more columns */

#define COL_DISABLED 0x0000 /* must be zero */
#define COL_ENABLED 0x0010
Expand Down
48 changes: 48 additions & 0 deletions t/t9002-column.sh
Expand Up @@ -90,6 +90,30 @@ EOF
test_cmp expected actual
'

test_expect_success '20 columns, nodense' '
cat >expected <<\EOF &&
one seven
two eight
three nine
four ten
five eleven
six
EOF
git column --mode=column,nodense < lista > actual &&
test_cmp expected actual
'

test_expect_success '20 columns, dense' '
cat >expected <<\EOF &&
one five nine
two six ten
three seven eleven
four eight
EOF
git column --mode=column,dense < lista > actual &&
test_cmp expected actual
'

test_expect_success '20 columns, padding 2' '
cat >expected <<\EOF &&
one seven
Expand Down Expand Up @@ -129,4 +153,28 @@ EOF
test_cmp expected actual
'

test_expect_success '20 columns, row first, nodense' '
cat >expected <<\EOF &&
one two
three four
five six
seven eight
nine ten
eleven
EOF
git column --mode=row,nodense <lista >actual &&
test_cmp expected actual
'

test_expect_success '20 columns, row first, dense' '
cat >expected <<\EOF &&
one two three
four five six
seven eight nine
ten eleven
EOF
git column --mode=row,dense <lista >actual &&
test_cmp expected actual
'

test_done

0 comments on commit 3f8eccb

Please sign in to comment.