git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: "Junio C Hamano" <gitster@pobox.com>,
	"Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH v9 4/9] column: add dense layout support
Date: Fri, 13 Apr 2012 17:54:36 +0700	[thread overview]
Message-ID: <1334314481-14470-5-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1334314481-14470-1-git-send-email-pclouds@gmail.com>

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>
---
 Documentation/config.txt |    4 ++
 column.c                 |   84 +++++++++++++++++++++++++++++++++++++++++++++-
 column.h                 |    2 +
 t/t9002-column.sh        |   48 ++++++++++++++++++++++++++
 4 files changed, 137 insertions(+), 1 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index ab6ae3d..01905a7 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -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'.
diff --git a/column.c b/column.c
index e44e2e9..0fb7582 100644
--- a/column.c
+++ b/column.c
@@ -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 */
@@ -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)
@@ -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
@@ -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';
@@ -118,6 +184,7 @@ static void display_table(const struct string_list *list,
 	}
 
 	free(data.len);
+	free(data.width);
 	free(empty_cell);
 }
 
@@ -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))
@@ -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;
 	}
 
diff --git a/column.h b/column.h
index ec7e1d2..4f178d8 100644
--- a/column.h
+++ b/column.h
@@ -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
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index ec288ae..c7d7a65 100755
--- a/t/t9002-column.sh
+++ b/t/t9002-column.sh
@@ -89,6 +89,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
@@ -128,4 +152,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
-- 
1.7.3.1.256.g2539c.dirty

  parent reply	other threads:[~2012-04-13 10:56 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-04-13 10:54 [PATCH v9 0/9] Column display Nguyễn Thái Ngọc Duy
2012-04-13 10:54 ` [PATCH v9 1/9] Add column layout skeleton and git-column Nguyễn Thái Ngọc Duy
2012-04-16  5:38   ` Junio C Hamano
2012-04-16 13:26     ` [PATCH] " Nguyễn Thái Ngọc Duy
2012-04-16 16:05       ` Junio C Hamano
2012-04-21  3:07         ` Nguyễn Thái Ngọc Duy
2012-04-21  4:18           ` Junio C Hamano
2012-04-21  4:44             ` Nguyễn Thái Ngọc Duy
2012-04-21  5:40               ` Junio C Hamano
2012-04-13 10:54 ` [PATCH v9 2/9] Stop starting pager recursively Nguyễn Thái Ngọc Duy
2012-04-13 10:54 ` [PATCH v9 3/9] column: add columnar layout Nguyễn Thái Ngọc Duy
2012-04-13 10:54 ` Nguyễn Thái Ngọc Duy [this message]
2012-04-13 10:54 ` [PATCH v9 5/9] help: reuse print_columns() for help -a Nguyễn Thái Ngọc Duy
2012-04-13 10:54 ` [PATCH v9 6/9] branch: add --column Nguyễn Thái Ngọc Duy
2012-04-13 10:54 ` [PATCH v9 7/9] status: " Nguyễn Thái Ngọc Duy
2012-04-13 10:54 ` [PATCH v9 8/9] column: support piping stdout to external git-column process Nguyễn Thái Ngọc Duy
2012-04-13 10:54 ` [PATCH v9 9/9] tag: add --column Nguyễn Thái Ngọc Duy

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1334314481-14470-5-git-send-email-pclouds@gmail.com \
    --to=pclouds@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).