Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit 3f8eccb

Browse files
pcloudsgitster
authored andcommitted
column: add dense layout support
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 <[email protected]> Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f78b1c5 commit 3f8eccb

File tree

4 files changed

+137
-1
lines changed

4 files changed

+137
-1
lines changed

Documentation/config.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,10 @@ column.ui::
854854
fill rows before columns
855855
`plain`;;
856856
show in one column
857+
`dense`;;
858+
make unequal size columns to utilize more space
859+
`nodense`;;
860+
make equal size columns
857861
--
858862
+
859863
This option defaults to 'never'.

column.c

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ struct column_data {
1515

1616
int rows, cols;
1717
int *len; /* cell length */
18+
int *width; /* index to the longest row in column */
1819
};
1920

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

60+
static void compute_column_width(struct column_data *data)
61+
{
62+
int i, x, y;
63+
for (x = 0; x < data->cols; x++) {
64+
data->width[x] = XY2LINEAR(data, x, 0);
65+
for (y = 0; y < data->rows; y++) {
66+
i = XY2LINEAR(data, x, y);
67+
if (i < data->list->nr &&
68+
data->len[data->width[x]] < data->len[i])
69+
data->width[x] = i;
70+
}
71+
}
72+
}
73+
74+
/*
75+
* Shrink all columns by shortening them one row each time (and adding
76+
* more columns along the way). Hopefully the longest cell will be
77+
* moved to the next column, column is shrunk so we have more space
78+
* for new columns. The process ends when the whole thing no longer
79+
* fits in data->total_width.
80+
*/
81+
static void shrink_columns(struct column_data *data)
82+
{
83+
data->width = xrealloc(data->width,
84+
sizeof(*data->width) * data->cols);
85+
while (data->rows > 1) {
86+
int x, total_width, cols, rows;
87+
rows = data->rows;
88+
cols = data->cols;
89+
90+
data->rows--;
91+
data->cols = DIV_ROUND_UP(data->list->nr, data->rows);
92+
if (data->cols != cols)
93+
data->width = xrealloc(data->width,
94+
sizeof(*data->width) * data->cols);
95+
compute_column_width(data);
96+
97+
total_width = strlen(data->opts.indent);
98+
for (x = 0; x < data->cols; x++) {
99+
total_width += data->len[data->width[x]];
100+
total_width += data->opts.padding;
101+
}
102+
if (total_width > data->opts.width) {
103+
data->rows = rows;
104+
data->cols = cols;
105+
break;
106+
}
107+
}
108+
compute_column_width(data);
109+
}
110+
59111
/* Display without layout when not enabled */
60112
static void display_plain(const struct string_list *list,
61113
const char *indent, const char *nl)
@@ -75,7 +127,18 @@ static int display_cell(struct column_data *data, int initial_width,
75127
i = XY2LINEAR(data, x, y);
76128
if (i >= data->list->nr)
77129
return -1;
130+
78131
len = data->len[i];
132+
if (data->width && data->len[data->width[x]] < initial_width) {
133+
/*
134+
* empty_cell has initial_width chars, if real column
135+
* is narrower, increase len a bit so we fill less
136+
* space.
137+
*/
138+
len += initial_width - data->len[data->width[x]];
139+
len -= data->opts.padding;
140+
}
141+
79142
if (COL_LAYOUT(data->colopts) == COL_COLUMN)
80143
newline = i + data->rows >= data->list->nr;
81144
else
@@ -108,6 +171,9 @@ static void display_table(const struct string_list *list,
108171

109172
layout(&data, &initial_width);
110173

174+
if (colopts & COL_DENSE)
175+
shrink_columns(&data);
176+
111177
empty_cell = xmalloc(initial_width + 1);
112178
memset(empty_cell, ' ', initial_width);
113179
empty_cell[initial_width] = '\0';
@@ -118,6 +184,7 @@ static void display_table(const struct string_list *list,
118184
}
119185

120186
free(data.len);
187+
free(data.width);
121188
free(empty_cell);
122189
}
123190

@@ -183,13 +250,22 @@ static int parse_option(const char *arg, int len, unsigned int *colopts,
183250
{ "plain", COL_PLAIN, COL_LAYOUT_MASK },
184251
{ "column", COL_COLUMN, COL_LAYOUT_MASK },
185252
{ "row", COL_ROW, COL_LAYOUT_MASK },
253+
{ "dense", COL_DENSE, 0 },
186254
};
187255
int i;
188256

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

261+
if (!opts[i].mask) {
262+
if (arg_len > 2 && !strncmp(arg_str, "no", 2)) {
263+
arg_str += 2;
264+
arg_len -= 2;
265+
set = 0;
266+
}
267+
}
268+
193269
name_len = strlen(opts[i].name);
194270
if (arg_len != name_len ||
195271
strncmp(arg_str, opts[i].name, name_len))
@@ -206,6 +282,12 @@ static int parse_option(const char *arg, int len, unsigned int *colopts,
206282

207283
if (opts[i].mask)
208284
*colopts = (*colopts & ~opts[i].mask) | opts[i].value;
285+
else {
286+
if (set)
287+
*colopts |= opts[i].value;
288+
else
289+
*colopts &= ~opts[i].value;
290+
}
209291
return 0;
210292
}
211293

column.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#define COL_LAYOUT_MASK 0x000F
55
#define COL_ENABLE_MASK 0x0030 /* always, never or auto */
66
#define COL_PARSEOPT 0x0040 /* --column is given from cmdline */
7+
#define COL_DENSE 0x0080 /* Shrink columns when possible,
8+
making space for more columns */
79

810
#define COL_DISABLED 0x0000 /* must be zero */
911
#define COL_ENABLED 0x0010

t/t9002-column.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,30 @@ EOF
9090
test_cmp expected actual
9191
'
9292

93+
test_expect_success '20 columns, nodense' '
94+
cat >expected <<\EOF &&
95+
one seven
96+
two eight
97+
three nine
98+
four ten
99+
five eleven
100+
six
101+
EOF
102+
git column --mode=column,nodense < lista > actual &&
103+
test_cmp expected actual
104+
'
105+
106+
test_expect_success '20 columns, dense' '
107+
cat >expected <<\EOF &&
108+
one five nine
109+
two six ten
110+
three seven eleven
111+
four eight
112+
EOF
113+
git column --mode=column,dense < lista > actual &&
114+
test_cmp expected actual
115+
'
116+
93117
test_expect_success '20 columns, padding 2' '
94118
cat >expected <<\EOF &&
95119
one seven
@@ -129,4 +153,28 @@ EOF
129153
test_cmp expected actual
130154
'
131155

156+
test_expect_success '20 columns, row first, nodense' '
157+
cat >expected <<\EOF &&
158+
one two
159+
three four
160+
five six
161+
seven eight
162+
nine ten
163+
eleven
164+
EOF
165+
git column --mode=row,nodense <lista >actual &&
166+
test_cmp expected actual
167+
'
168+
169+
test_expect_success '20 columns, row first, dense' '
170+
cat >expected <<\EOF &&
171+
one two three
172+
four five six
173+
seven eight nine
174+
ten eleven
175+
EOF
176+
git column --mode=row,dense <lista >actual &&
177+
test_cmp expected actual
178+
'
179+
132180
test_done

0 commit comments

Comments
 (0)