From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH 4/9] display_columns: add COL_MODE_{COLUMN,ROW} mode
Date: Sun, 20 Mar 2011 19:57:48 +0700 [thread overview]
Message-ID: <1300625873-18435-5-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1300625873-18435-1-git-send-email-pclouds@gmail.com>
COL_MODE_COLUMN and COL_MODE_ROW fill column by column (or row by row
respectively), given the terminal width and how many space between
columns.
Strings are supposed to be in UTF-8. If strings contain ANSI escape strings,
COL_ANSI must be specified for correct length calculation.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
column.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
column.h | 3 +
t/t9002-column.sh | 86 ++++++++++++++++++++++++++++++++++++
3 files changed, 215 insertions(+), 1 deletions(-)
diff --git a/column.c b/column.c
index b32b9f9..15f74a5 100644
--- a/column.c
+++ b/column.c
@@ -2,8 +2,65 @@
#include "column.h"
#include "string-list.h"
#include "parse-options.h"
+#include "utf8.h"
#define MODE(mode) ((mode) & COL_MODE)
+#define XY2LINEAR(d,x,y) (MODE((d)->mode) == COL_MODE_COLUMN ? \
+ (x) * (d)->rows + (y) : \
+ (y) * (d)->cols + (x))
+
+struct column_data {
+ const struct string_list *list; /* list of all cells */
+ int mode; /* COL_MODE */
+ int total_width; /* terminal width */
+ int padding; /* cell padding */
+ const char *indent; /* left most column indentation */
+
+ int rows, cols;
+ int *len; /* cell length */
+};
+
+/* return length of 's' in letters, ANSI escapes stripped */
+static int item_length(int mode, const char *s)
+{
+ int len, i = 0;
+ struct strbuf str = STRBUF_INIT;
+
+ if (!(mode & COL_ANSI))
+ return utf8_strwidth(s);
+
+ 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->padding;
+
+ data->cols = (data->total_width - strlen(data->indent)) / *width;
+ if (data->cols == 0)
+ data->cols = 1;
+
+ data->rows = DIV_ROUND_UP(data->list->nr, data->cols);
+}
struct string_list_item *add_to_columns(struct string_list *list, int mode,
const char *string)
@@ -23,6 +80,63 @@ static void display_plain(const struct string_list *list, const char *indent)
printf("%s%s\n", indent, list->items[i].string);
}
+/* 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 (MODE(data->mode) == COL_MODE_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->indent : "",
+ data->list->items[i].string,
+ newline ? "\n" : empty_cell + len);
+ return 0;
+}
+
+/* Display COL_MODE_COLUMN or COL_MODE_ROW */
+static void display_table(const struct string_list *list,
+ int mode, int total_width,
+ int padding, const char *indent)
+{
+ struct column_data data;
+ int x, y, i, initial_width;
+ char *empty_cell;
+
+ memset(&data, 0, sizeof(data));
+ data.list = list;
+ data.mode = mode;
+ data.total_width = total_width;
+ data.padding = padding;
+ data.indent = indent;
+
+ data.len = xmalloc(sizeof(*data.len) * list->nr);
+ for (i = 0; i < list->nr; i++)
+ data.len[i] = item_length(mode, 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 display_columns(const struct string_list *list, int mode,
int width, int padding, const char *indent)
{
@@ -34,7 +148,16 @@ void display_columns(const struct string_list *list, int mode,
display_plain(list, indent);
return;
}
- die("BUG: invalid mode %d", MODE(mode));
+
+ switch (MODE(mode)) {
+ case COL_MODE_ROW:
+ case COL_MODE_COLUMN:
+ display_table(list, mode, width, padding, indent);
+ break;
+
+ default:
+ die("BUG: invalid mode %d", MODE(mode));
+ }
}
struct colopt {
@@ -96,6 +219,8 @@ static int parse_option(const char *arg, int len,
{ ENABLE, "always", 1 },
{ ENABLE, "never", 0 },
{ ENABLE, "auto", -1 },
+ { MODE, "column", COL_MODE_COLUMN },
+ { MODE, "row", COL_MODE_ROW },
};
int i, set, name_len;
diff --git a/column.h b/column.h
index a8b24e8..4fb1004 100644
--- a/column.h
+++ b/column.h
@@ -2,8 +2,11 @@
#define COLUMN_H
#define COL_MODE 0x000F
+#define COL_MODE_COLUMN 0 /* Fill columns before rows */
+#define COL_MODE_ROW 1 /* Fill rows before columns */
#define COL_ENABLED (1 << 4)
#define COL_ENABLED_SET (1 << 5) /* Has COL_ENABLED been set? */
+#define COL_ANSI (1 << 6) /* Remove ANSI escapes from string length */
extern int term_columns(void);
extern struct string_list_item *add_to_columns(struct string_list *list, int mode, const char *string);
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index 099a29f..f801190 100755
--- a/t/t9002-column.sh
+++ b/t/t9002-column.sh
@@ -24,4 +24,90 @@ test_expect_success 'never' '
test_cmp lista actual
'
+test_expect_success '80 columns' '
+ cat >expected <<\EOF &&
+one two three four five six seven eight nine ten eleven
+EOF
+ COLUMNS=80 test-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 test-column --mode=column <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'width = 1' '
+ test-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
+ test-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
+ test-column --mode=column --padding 2 <lista >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '20 columns, left indented' '
+ cat >expected <<\EOF &&
+ one seven
+ two eight
+ three nine
+ four ten
+ five eleven
+ six
+EOF
+ test-column --mode=column --left=2 <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
+ test-column --mode=row <lista >actual &&
+ test_cmp expected actual
+'
+
test_done
--
1.7.4.74.g639db
next prev parent reply other threads:[~2011-03-20 12:58 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-03-20 12:57 [PATCH 0/9] column output v3 Nguyễn Thái Ngọc Duy
2011-03-20 12:57 ` [PATCH 1/9] Move term_columns() to pager.c and save terminal width before pager Nguyễn Thái Ngọc Duy
2011-03-20 12:57 ` [PATCH 2/9] Add display_columns() to display in columnar layout Nguyễn Thái Ngọc Duy
2011-03-20 12:57 ` [PATCH 3/9] column: add functions to parse column settings Nguyễn Thái Ngọc Duy
2011-03-20 12:57 ` Nguyễn Thái Ngọc Duy [this message]
2011-03-20 12:57 ` [PATCH 5/9] display_columns: add COL_DENSE to do unequal column layout Nguyễn Thái Ngọc Duy
2011-03-20 12:57 ` [PATCH 6/9] column: add column.ui for default column output settings Nguyễn Thái Ngọc Duy
2011-03-20 12:57 ` [PATCH 7/9] help: reuse display_columns() for help -a Nguyễn Thái Ngọc Duy
2011-03-20 16:04 ` Nguyễn Thái Ngọc Duy
2011-03-20 12:57 ` [PATCH 8/9] tag: add --column Nguyễn Thái Ngọc Duy
2011-03-20 12:57 ` [PATCH 9/9] branch: " Nguyễn Thái Ngọc Duy
2011-03-20 19:52 ` Teemu Likonen
2011-03-20 23:26 ` 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
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1300625873-18435-5-git-send-email-pclouds@gmail.com \
--to=pclouds@gmail.com \
--cc=git@vger.kernel.org \
/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.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.