git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v9 3/9] column: add columnar layout

From
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Date
Apr 13, 2012, 10:54 UTC
Message-ID
<1334314481-14470-4-git-send-email-pclouds@gmail.com>
In-Reply-To
<1334314481-14470-1-git-send-email-pclouds@gmail.com>

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>
---
 Documentation/config.txt |    4 ++
 column.c                 |  114 ++++++++++++++++++++++++++++++++++++++++++++++
 column.h                 |    2 +
 t/t9002-column.sh        |   86 ++++++++++++++++++++++++++++++++++
 4 files changed, 206 insertions(+), 0 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 9aabef1..ab6ae3d 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
 --
diff --git a/column.c b/column.c
index b297037..e44e2e9 100644
--- a/column.c
+++ b/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;
 
diff --git a/column.h b/column.h
index b8719b3..ec7e1d2 100644
--- a/column.h
+++ b/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) \
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index a7f3cd9..ec288ae 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
-- 
1.7.3.1.256.g2539c.dirty
Previous: Nguyễn Thái Ngọc DuyNext: Nguyễn Thái Ngọc Duy
Message 11 of 17 in “Column display”
  1. 0/9 Column displayNguyễn Thái Ngọc Duy, Apr 13, 2012
  2. 1/9 Add column layout skeleton and git-columnNguyễn Thái Ngọc Duy, Apr 13, 2012
  3. Junio C HamanoApr 16, 2012
  4. Add column layout skeleton and git-columnNguyễn Thái Ngọc Duy, Apr 16, 2012
  5. Junio C HamanoApr 16, 2012
  6. Add column layout skeleton and git-columnNguyễn Thái Ngọc Duy, Apr 21, 2012
  7. Junio C HamanoApr 21, 2012
  8. Add column layout skeleton and git-columnNguyễn Thái Ngọc Duy, Apr 21, 2012
  9. Junio C HamanoApr 21, 2012
  10. 2/9 Stop starting pager recursivelyNguyễn Thái Ngọc Duy, Apr 13, 2012
  11. 3/9 column: add columnar layoutNguyễn Thái Ngọc Duy, Apr 13, 2012
  12. 4/9 column: add dense layout supportNguyễn Thái Ngọc Duy, Apr 13, 2012
  13. 5/9 help: reuse print_columns() for help -aNguyễn Thái Ngọc Duy, Apr 13, 2012
  14. 6/9 branch: add --columnNguyễn Thái Ngọc Duy, Apr 13, 2012
  15. 7/9 status: add --columnNguyễn Thái Ngọc Duy, Apr 13, 2012
  16. 8/9 column: support piping stdout to external git-column processNguyễn Thái Ngọc Duy, Apr 13, 2012
  17. 9/9 tag: add --columnNguyễn Thái Ngọc Duy, Apr 13, 2012

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.