{"thread":{"id":"26803","subject":"[PATCH 0/9] column output v3","startedAt":"2011-03-20T12:57:44Z","lastAt":"2011-03-20T23:26:31Z","messageCount":13,"participants":["Nguyễn Thái Ngọc Duy","Teemu Likonen"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"163817","messageId":"1300625873-18435-1-git-send-email-pclouds@gmail.com","threadId":"26803","inReplyTo":null,"subject":"[PATCH 0/9] column output v3","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-03-20T12:57:44Z","receivedAt":"2011-03-20T12:57:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Compared to v2 [1]. It's quite ready for consumption:\n\n - dense mode works better (and consumes more cpu cycles, but not much)\n - there should be no impact when it's not activated (i.e. no\n   output buffering)\n - column.ui is used for default settings. Not sure if I should go for\n   column.branch and column.tag\n - --[no-]column can be used to override column.ui\n\n[1] http://mid.gmane.org/1297254284-3729-1-git-send-email-pclouds@gmail.com\n\nNguyễn Thái Ngọc Duy (9):\n  Move term_columns() to pager.c and save terminal width before pager\n  Add display_columns() to display in columnar layout\n  column: add functions to parse column settings\n  display_columns: add COL_MODE_{COLUMN,ROW} mode\n  display_columns: add COL_DENSE to do unequal column layout\n  column: add column.ui for default column output settings\n  help: reuse display_columns() for help -a\n  tag: add --column\n  branch: add --column\n\n .gitignore                   |    1 +\n Documentation/config.txt     |   24 +++\n Documentation/git-branch.txt |    9 +\n Documentation/git-tag.txt    |   11 +-\n Makefile                     |    3 +\n builtin/branch.c             |   23 ++-\n builtin/tag.c                |   21 ++-\n column.c                     |  378 ++++++++++++++++++++++++++++++++++++++++++\n column.h                     |   22 +++\n help.c                       |   53 +-----\n pager.c                      |   33 ++++\n parse-options.h              |    2 +\n t/t9002-column.sh            |  135 +++++++++++++++\n test-column.c                |   39 +++++\n 14 files changed, 701 insertions(+), 53 deletions(-)\n create mode 100644 column.c\n create mode 100644 column.h\n create mode 100755 t/t9002-column.sh\n create mode 100644 test-column.c\n\n-- \n1.7.4.74.g639db\n"},{"id":"163818","messageId":"1300625873-18435-2-git-send-email-pclouds@gmail.com","threadId":"26803","inReplyTo":"1300625873-18435-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/9] Move term_columns() to pager.c and save terminal width before pager","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-03-20T12:57:45Z","receivedAt":"2011-03-20T12:57:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"term_columns() checks for terminal width via ioctl(2). After\nredirecting, stdin is no longer terminal to get terminal width.\n\nCheck terminal width and save it before redirect stdin in setup_pager()\nand let term_columns() reuse the value.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile |    1 +\n column.h |    6 ++++++\n help.c   |   23 +----------------------\n pager.c  |   33 +++++++++++++++++++++++++++++++++\n 4 files changed, 41 insertions(+), 22 deletions(-)\n create mode 100644 column.h\n\ndiff --git a/Makefile b/Makefile\nindex 775ee83..6007f68 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1956,6 +1956,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o http-backend.o: url.h\n+help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/column.h b/column.h\nnew file mode 100644\nindex 0000000..55d8067\n--- /dev/null\n+++ b/column.h\n@@ -0,0 +1,6 @@\n+#ifndef COLUMN_H\n+#define COLUMN_H\n+\n+extern int term_columns(void);\n+\n+#endif\ndiff --git a/help.c b/help.c\nindex 7654f1b..768f64c 100644\n--- a/help.c\n+++ b/help.c\n@@ -4,28 +4,7 @@\n #include \"levenshtein.h\"\n #include \"help.h\"\n #include \"common-cmds.h\"\n-\n-/* most GUI terminals set COLUMNS (although some don't export it) */\n-static int term_columns(void)\n-{\n-\tchar *col_string = getenv(\"COLUMNS\");\n-\tint n_cols;\n-\n-\tif (col_string && (n_cols = atoi(col_string)) > 0)\n-\t\treturn n_cols;\n-\n-#ifdef TIOCGWINSZ\n-\t{\n-\t\tstruct winsize ws;\n-\t\tif (!ioctl(1, TIOCGWINSZ, &ws)) {\n-\t\t\tif (ws.ws_col)\n-\t\t\t\treturn ws.ws_col;\n-\t\t}\n-\t}\n-#endif\n-\n-\treturn 80;\n-}\n+#include \"column.h\"\n \n void add_cmdname(struct cmdnames *cmds, const char *name, int len)\n {\ndiff --git a/pager.c b/pager.c\nindex dac358f..dad6329 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -12,6 +12,19 @@\n  */\n \n static int spawned_pager;\n+static int max_columns;\n+\n+static int retrieve_terminal_width(void)\n+{\n+#ifdef TIOCGWINSZ\n+\tstruct winsize ws;\n+\tif (ioctl(1, TIOCGWINSZ, &ws))  /* e.g., ENOSYS */\n+\t\treturn 0;\n+\treturn ws.ws_col;\n+#else\n+\treturn 0;\n+#endif\n+}\n \n #ifndef WIN32\n static void pager_preexec(void)\n@@ -74,12 +87,17 @@ const char *git_pager(int stdout_is_tty)\n void setup_pager(void)\n {\n \tconst char *pager = git_pager(isatty(1));\n+\tint width;\n \n \tif (!pager)\n \t\treturn;\n \n \tspawned_pager = 1; /* means we are emitting to terminal */\n \n+\twidth = retrieve_terminal_width();\n+\tif (width)\n+\t\tmax_columns = width;\n+\n \t/* spawn the pager */\n \tpager_argv[0] = pager;\n \tpager_process.use_shell = 1;\n@@ -116,3 +134,18 @@ int pager_in_use(void)\n \tenv = getenv(\"GIT_PAGER_IN_USE\");\n \treturn env ? git_config_bool(\"GIT_PAGER_IN_USE\", env) : 0;\n }\n+\n+int term_columns()\n+{\n+\tchar *col_string = getenv(\"COLUMNS\");\n+\tint n_cols;\n+\n+\tif (col_string && (n_cols = atoi(col_string)) > 0)\n+\t\treturn n_cols;\n+\n+\tif (spawned_pager && max_columns)\n+\t\treturn max_columns;\n+\n+\tn_cols = retrieve_terminal_width();\n+\treturn n_cols ? n_cols : 80;\n+}\n-- \n1.7.4.74.g639db\n"},{"id":"163819","messageId":"1300625873-18435-3-git-send-email-pclouds@gmail.com","threadId":"26803","inReplyTo":"1300625873-18435-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/9] Add display_columns() to display in columnar layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-03-20T12:57:46Z","receivedAt":"2011-03-20T12:57:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Currently it does not do that, just provide the API. In order to\noutput in columns, COL_ENABLED bit must be set. A nice consequence is\nmode 0 is effectively no column mode.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile |    3 ++-\n column.c |   37 +++++++++++++++++++++++++++++++++++++\n column.h |    5 +++++\n 3 files changed, 44 insertions(+), 1 deletions(-)\n create mode 100644 column.c\n\ndiff --git a/Makefile b/Makefile\nindex 6007f68..94e1cf0 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -575,6 +575,7 @@ LIB_OBJS += branch.o\n LIB_OBJS += bundle.o\n LIB_OBJS += cache-tree.o\n LIB_OBJS += color.o\n+LIB_OBJS += column.o\n LIB_OBJS += combine-diff.o\n LIB_OBJS += commit.o\n LIB_OBJS += config.o\n@@ -1956,7 +1957,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o http-backend.o: url.h\n-help.o pager.o: column.h\n+column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/column.c b/column.c\nnew file mode 100644\nindex 0000000..8422c89\n--- /dev/null\n+++ b/column.c\n@@ -0,0 +1,37 @@\n+#include \"cache.h\"\n+#include \"column.h\"\n+#include \"string-list.h\"\n+\n+#define MODE(mode) ((mode) & COL_MODE)\n+\n+struct string_list_item *add_to_columns(struct string_list *list, int mode,\n+\t\t\t\t\tconst char *string)\n+{\n+\tif (mode & COL_ENABLED)\n+\t\treturn string_list_append(list, string);\n+\tprintf(\"%s\\n\", string);\n+\treturn NULL;\n+}\n+\n+/* Display without layout when COL_ENABLED is not set */\n+static void display_plain(const struct string_list *list, const char *indent)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tprintf(\"%s%s\\n\", indent, list->items[i].string);\n+}\n+\n+void display_columns(const struct string_list *list, int mode,\n+\t\t     int width, int padding, const char *indent)\n+{\n+\tif (!list->nr)\n+\t\treturn;\n+\tif (!indent)\n+\t\tindent = \"\";\n+\tif (width <= 1 || !(mode & COL_ENABLED)) {\n+\t\tdisplay_plain(list, indent);\n+\t\treturn;\n+\t}\n+\tdie(\"BUG: invalid mode %d\", MODE(mode));\n+}\ndiff --git a/column.h b/column.h\nindex 55d8067..ffae87c 100644\n--- a/column.h\n+++ b/column.h\n@@ -1,6 +1,11 @@\n #ifndef COLUMN_H\n #define COLUMN_H\n \n+#define COL_MODE          0x000F\n+#define COL_ENABLED      (1 << 4)\n+\n extern int term_columns(void);\n+extern struct string_list_item *add_to_columns(struct string_list *list, int mode, const char *string);\n+extern void display_columns(const struct string_list *list, int mode, int width, int padding, const char *indent);\n \n #endif\n-- \n1.7.4.74.g639db\n"},{"id":"163820","messageId":"1300625873-18435-4-git-send-email-pclouds@gmail.com","threadId":"26803","inReplyTo":"1300625873-18435-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/9] column: add functions to parse column settings","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-03-20T12:57:47Z","receivedAt":"2011-03-20T12:57:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"A column option string consists of many token separated by either\nspace of commas. A token belongs to one of three groups:\n\n - enabling: always, never and auto\n - layout mode: to be implemented\n - other tuning, which could be negated be prefix 'no'\n\nA command line option without argument (e.g. --column) will enable\ncolumn output and reuse existing settings (layout mode and options..).\n--no-column disables columnar output.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore        |    1 +\n Makefile          |    1 +\n column.c          |  125 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n column.h          |    4 ++\n parse-options.h   |    2 +\n t/t9002-column.sh |   27 +++++++++++\n test-column.c     |   39 ++++++++++++++++\n 7 files changed, 199 insertions(+), 0 deletions(-)\n create mode 100755 t/t9002-column.sh\n create mode 100644 test-column.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 3dd6ef7..a1a1202 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -162,6 +162,7 @@\n /gitweb/gitweb.cgi\n /gitweb/static/gitweb.min.*\n /test-chmtime\n+/test-column\n /test-ctype\n /test-date\n /test-delta\ndiff --git a/Makefile b/Makefile\nindex 94e1cf0..ba9944b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -417,6 +417,7 @@ PROGRAM_OBJS += http-backend.o\n PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))\n \n TEST_PROGRAMS_NEED_X += test-chmtime\n+TEST_PROGRAMS_NEED_X += test-column\n TEST_PROGRAMS_NEED_X += test-ctype\n TEST_PROGRAMS_NEED_X += test-date\n TEST_PROGRAMS_NEED_X += test-delta\ndiff --git a/column.c b/column.c\nindex 8422c89..b32b9f9 100644\n--- a/column.c\n+++ b/column.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"column.h\"\n #include \"string-list.h\"\n+#include \"parse-options.h\"\n \n #define MODE(mode) ((mode) & COL_MODE)\n \n@@ -35,3 +36,127 @@ void display_columns(const struct string_list *list, int mode,\n \t}\n \tdie(\"BUG: invalid mode %d\", MODE(mode));\n }\n+\n+struct colopt {\n+\tenum {\n+\t\tENABLE,\n+\t\tMODE,\n+\t\tOPTION\n+\t} type;\n+\tconst char *name;\n+\tint value;\n+};\n+\n+/*\n+ * Set COL_ENABLED and COL_ENABLED_SET. If 'set' is -1, check if\n+ * stdout is tty.\n+ */\n+static int set_enable_bit(int *mode, int set, int stdout_is_tty)\n+{\n+\tif (set < 0) {\t/* auto */\n+\t\tif (stdout_is_tty < 0)\n+\t\t\tstdout_is_tty = isatty(1);\n+\t\tset = stdout_is_tty || (pager_in_use() && pager_use_color);\n+\t}\n+\tif (set)\n+\t\t*mode = *mode | COL_ENABLED | COL_ENABLED_SET;\n+\telse\n+\t\t*mode = (*mode & ~COL_ENABLED) | COL_ENABLED_SET;\n+\treturn 0;\n+}\n+\n+/*\n+ * Set COL_MODE_*. mode is intially copied from column.ui. If\n+ * COL_ENABLED_SET is not set, then neither 'always', 'never' nor\n+ * 'auto' has been used. Default to 'always'.\n+ */\n+static int set_mode(int *mode, int value)\n+{\n+\t*mode = (*mode & ~COL_MODE) | value;\n+\tif (!(*mode & COL_ENABLED_SET))\n+\t\t*mode |= COL_ENABLED | COL_ENABLED_SET;\n+\n+\treturn 0;\n+}\n+\n+/* Set or unset other COL_* */\n+static int set_option(int *mode, int opt, int set)\n+{\n+\tif (set)\n+\t\t*mode |= opt;\n+\telse\n+\t\t*mode &= ~opt;\n+\treturn 0;\n+}\n+\n+static int parse_option(const char *arg, int len,\n+\t\t\tint *mode, int stdout_is_tty)\n+{\n+\tstruct colopt opts[] = {\n+\t\t{ ENABLE, \"always\",  1 },\n+\t\t{ ENABLE, \"never\",   0 },\n+\t\t{ ENABLE, \"auto\",   -1 },\n+\t};\n+\tint i, set, name_len;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(opts); i++) {\n+\t\tif (opts[i].type == OPTION) {\n+\t\t\tif (len > 2 && !strncmp(arg, \"no\", 2)) {\n+\t\t\t\targ += 2;\n+\t\t\t\tlen -= 2;\n+\t\t\t\tset = 0;\n+\t\t\t}\n+\t\t\telse\n+\t\t\t\tset = 1;\n+\t\t}\n+\n+\t\tname_len = strlen(opts[i].name);\n+\t\tif (len != name_len ||\n+\t\t    strncmp(arg, opts[i].name, name_len))\n+\t\t\tcontinue;\n+\n+\t\tswitch (opts[i].type) {\n+\t\tcase ENABLE: return set_enable_bit(mode, opts[i].value,\n+\t\t\t\t\t\t   stdout_is_tty);\n+\t\tcase MODE: return set_mode(mode, opts[i].value);\n+\t\tcase OPTION: return set_option(mode, opts[i].value, set);\n+\t\tdefault: die(\"BUG: Unknown option type %d\", opts[i].type);\n+\t\t}\n+\t}\n+\n+\treturn error(\"unsupported style '%s'\", arg);\n+}\n+\n+static int parse_string(int *mode, const char *value,\n+\t\t\tint stdout_is_tty)\n+{\n+\tconst char *sep = \" ,\";\n+\n+\twhile (*value) {\n+\t\tint len = strcspn(value, sep);\n+\t\tif (len) {\n+\t\t\tif (parse_option(value, len, mode, stdout_is_tty))\n+\t\t\t\treturn -1;\n+\n+\t\t\tvalue += len;\n+\t\t}\n+\t\tvalue += strspn(value, sep);\n+\t}\n+\treturn 0;\n+}\n+\n+int parseopt_column_callback(const struct option *opt,\n+\t\t\t     const char *arg, int unset)\n+{\n+\tint *mode = opt->value;\n+\tif (unset) {\n+\t\t*mode = (*mode & ~COL_ENABLED) | COL_ENABLED_SET;\n+\t\treturn 0;\n+\t}\n+\tif (arg)\n+\t\treturn parse_string(mode, arg, -1);\n+\n+\t/* no arg, turn it on */\n+\t*mode |= COL_ENABLED | COL_ENABLED_SET;\n+\treturn 0;\n+}\ndiff --git a/column.h b/column.h\nindex ffae87c..a8b24e8 100644\n--- a/column.h\n+++ b/column.h\n@@ -3,9 +3,13 @@\n \n #define COL_MODE          0x000F\n #define COL_ENABLED      (1 << 4)\n+#define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set? */\n \n extern int term_columns(void);\n extern struct string_list_item *add_to_columns(struct string_list *list, int mode, const char *string);\n extern void display_columns(const struct string_list *list, int mode, int width, int padding, const char *indent);\n \n+struct option;\n+extern int parseopt_column_callback(const struct option *opt, const char *arg, int unset);\n+\n #endif\ndiff --git a/parse-options.h b/parse-options.h\nindex 31ec5d2..14816b6 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -220,5 +220,7 @@ extern int parse_opt_tertiary(const struct option *, const char *, int);\n \t  PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }\n #define OPT__COLOR(var, h) \\\n \tOPT_COLOR_FLAG(0, \"color\", (var), (h))\n+#define OPT_COLUMN(s, l, v, h) \\\n+\t{ OPTION_CALLBACK, (s), (l), (v), \"style\", (h), PARSE_OPT_OPTARG, parseopt_column_callback }\n \n #endif\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nnew file mode 100755\nindex 0000000..099a29f\n--- /dev/null\n+++ b/t/t9002-column.sh\n@@ -0,0 +1,27 @@\n+#!/bin/sh\n+\n+test_description='git column'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\tcat >lista <<\\EOF\n+one\n+two\n+three\n+four\n+five\n+six\n+seven\n+eight\n+nine\n+ten\n+eleven\n+EOF\n+'\n+\n+test_expect_success 'never' '\n+\ttest-column --mode=never <lista >actual &&\n+\ttest_cmp lista actual\n+'\n+\n+test_done\ndiff --git a/test-column.c b/test-column.c\nnew file mode 100644\nindex 0000000..d6321a4\n--- /dev/null\n+++ b/test-column.c\n@@ -0,0 +1,39 @@\n+#include \"cache.h\"\n+#include \"strbuf.h\"\n+#include \"parse-options.h\"\n+#include \"string-list.h\"\n+#include \"column.h\"\n+\n+static const char * const builtin_column_usage[] = {\n+\t\"git column [--mode=<mode>] [--width=<width>] [--left-space=<N>] [--right-space=<N>]\",\n+\tNULL\n+};\n+\n+int main(int argc, const char **argv)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\tint mode = 0;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint left_space = 0, right_space = 0, padding = 1;\n+\tint term_width = term_columns();\n+\tstruct option options[] = {\n+\t\tOPT_INTEGER(0, \"width\", &term_width, \"Maximum width\"),\n+\t\tOPT_INTEGER(0, \"left\", &left_space, \"Padding space on left border\"),\n+\t\tOPT_INTEGER(0, \"right\", &right_space, \"Padding space on right border\"),\n+\t\tOPT_INTEGER(0, \"padding\", &padding, \"Padding space between columns\"),\n+\t\tOPT_COLUMN(0, \"mode\", &mode, \"layout to use\"),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, \"\", options, builtin_column_usage, 0);\n+\n+\twhile (!strbuf_getline(&sb, stdin, '\\n'))\n+\t\tstring_list_append(&list, sb.buf);\n+\n+\tstrbuf_setlen(&sb, left_space);\n+\tmemset(sb.buf, ' ', left_space);\n+\tdisplay_columns(&list, mode,\n+\t\t\tterm_width - right_space - left_space,\n+\t\t\tpadding, sb.buf);\n+\treturn 0;\n+}\n-- \n1.7.4.74.g639db\n"},{"id":"163821","messageId":"1300625873-18435-5-git-send-email-pclouds@gmail.com","threadId":"26803","inReplyTo":"1300625873-18435-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 4/9] display_columns: add COL_MODE_{COLUMN,ROW} mode","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-03-20T12:57:48Z","receivedAt":"2011-03-20T12:57:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"COL_MODE_COLUMN and COL_MODE_ROW fill column by column (or row by row\nrespectively), given the terminal width and how many space between\ncolumns.\n\nStrings are supposed to be in UTF-8. If strings contain ANSI escape strings,\nCOL_ANSI must be specified for correct length calculation.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n column.c          |  127 ++++++++++++++++++++++++++++++++++++++++++++++++++++-\n column.h          |    3 +\n t/t9002-column.sh |   86 ++++++++++++++++++++++++++++++++++++\n 3 files changed, 215 insertions(+), 1 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex b32b9f9..15f74a5 100644\n--- a/column.c\n+++ b/column.c\n@@ -2,8 +2,65 @@\n #include \"column.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"utf8.h\"\n \n #define MODE(mode) ((mode) & COL_MODE)\n+#define XY2LINEAR(d,x,y) (MODE((d)->mode) == COL_MODE_COLUMN ? \\\n+\t\t\t  (x) * (d)->rows + (y) : \\\n+\t\t\t  (y) * (d)->cols + (x))\n+\n+struct column_data {\n+\tconst struct string_list *list; /* list of all cells */\n+\tint mode;\t\t\t/* COL_MODE */\n+\tint total_width;\t\t/* terminal width */\n+\tint padding;\t\t\t/* cell padding */\n+\tconst char *indent;\t\t/* left most column indentation */\n+\n+\tint rows, cols;\n+\tint *len;\t\t\t/* cell length */\n+};\n+\n+/* return length of 's' in letters, ANSI escapes stripped */\n+static int item_length(int mode, const char *s)\n+{\n+\tint len, i = 0;\n+\tstruct strbuf str = STRBUF_INIT;\n+\n+\tif (!(mode & COL_ANSI))\n+\t\treturn utf8_strwidth(s);\n+\n+\tstrbuf_addstr(&str, s);\n+\twhile ((s = strstr(str.buf + i, \"\\033[\")) != NULL) {\n+\t\tint len = strspn(s + 2, \"0123456789;\");\n+\t\ti = s - str.buf;\n+\t\tstrbuf_remove(&str, i, len + 3); /* \\033[<len><func char> */\n+\t}\n+\tlen = utf8_strwidth(str.buf);\n+\tstrbuf_release(&str);\n+\treturn len;\n+}\n+\n+/*\n+ * Calculate cell width, rows and cols for a table of equal cells, given\n+ * table width and how many spaces between cells.\n+ */\n+static void layout(struct column_data *data, int *width)\n+{\n+\tint i;\n+\n+\t*width = 0;\n+\tfor (i = 0; i < data->list->nr; i++)\n+\t\tif (*width < data->len[i])\n+\t\t\t*width = data->len[i];\n+\n+\t*width += data->padding;\n+\n+\tdata->cols = (data->total_width - strlen(data->indent)) / *width;\n+\tif (data->cols == 0)\n+\t\tdata->cols = 1;\n+\n+\tdata->rows = DIV_ROUND_UP(data->list->nr, data->cols);\n+}\n \n struct string_list_item *add_to_columns(struct string_list *list, int mode,\n \t\t\t\t\tconst char *string)\n@@ -23,6 +80,63 @@ static void display_plain(const struct string_list *list, const char *indent)\n \t\tprintf(\"%s%s\\n\", indent, list->items[i].string);\n }\n \n+/* Print a cell to stdout with all necessary leading/traling space */\n+static int display_cell(struct column_data *data, int initial_width,\n+\t\t\tconst char *empty_cell, int x, int y)\n+{\n+\tint i, len, newline;\n+\n+\ti = XY2LINEAR(data, x, y);\n+\tif (i >= data->list->nr)\n+\t\treturn -1;\n+\tlen = data->len[i];\n+\tif (MODE(data->mode) == COL_MODE_COLUMN)\n+\t\tnewline = i + data->rows >= data->list->nr;\n+\telse\n+\t\tnewline = x == data->cols - 1 || i == data->list->nr - 1;\n+\n+\tprintf(\"%s%s%s\",\n+\t\t\tx == 0 ? data->indent : \"\",\n+\t\t\tdata->list->items[i].string,\n+\t\t\tnewline ? \"\\n\" : empty_cell + len);\n+\treturn 0;\n+}\n+\n+/* Display COL_MODE_COLUMN or COL_MODE_ROW */\n+static void display_table(const struct string_list *list,\n+\t\t\t  int mode, int total_width,\n+\t\t\t  int padding, const char *indent)\n+{\n+\tstruct column_data data;\n+\tint x, y, i, initial_width;\n+\tchar *empty_cell;\n+\n+\tmemset(&data, 0, sizeof(data));\n+\tdata.list = list;\n+\tdata.mode = mode;\n+\tdata.total_width = total_width;\n+\tdata.padding = padding;\n+\tdata.indent = indent;\n+\n+\tdata.len = xmalloc(sizeof(*data.len) * list->nr);\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tdata.len[i] = item_length(mode, list->items[i].string);\n+\n+\tlayout(&data, &initial_width);\n+\n+\tempty_cell = xmalloc(initial_width + 1);\n+\tmemset(empty_cell, ' ', initial_width);\n+\tempty_cell[initial_width] = '\\0';\n+\tfor (y = 0; y < data.rows; y++) {\n+\t\tfor (x = 0; x < data.cols; x++)\n+\t\t\tif (display_cell(&data, initial_width, empty_cell, x, y))\n+\t\t\t\tbreak;\n+\t}\n+\n+\tfree(data.len);\n+\tfree(empty_cell);\n+}\n+\n void display_columns(const struct string_list *list, int mode,\n \t\t     int width, int padding, const char *indent)\n {\n@@ -34,7 +148,16 @@ void display_columns(const struct string_list *list, int mode,\n \t\tdisplay_plain(list, indent);\n \t\treturn;\n \t}\n-\tdie(\"BUG: invalid mode %d\", MODE(mode));\n+\n+\tswitch (MODE(mode)) {\n+\tcase COL_MODE_ROW:\n+\tcase COL_MODE_COLUMN:\n+\t\tdisplay_table(list, mode, width, padding, indent);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\tdie(\"BUG: invalid mode %d\", MODE(mode));\n+\t}\n }\n \n struct colopt {\n@@ -96,6 +219,8 @@ static int parse_option(const char *arg, int len,\n \t\t{ ENABLE, \"always\",  1 },\n \t\t{ ENABLE, \"never\",   0 },\n \t\t{ ENABLE, \"auto\",   -1 },\n+\t\t{ MODE,   \"column\", COL_MODE_COLUMN },\n+\t\t{ MODE,   \"row\",    COL_MODE_ROW },\n \t};\n \tint i, set, name_len;\n \ndiff --git a/column.h b/column.h\nindex a8b24e8..4fb1004 100644\n--- a/column.h\n+++ b/column.h\n@@ -2,8 +2,11 @@\n #define COLUMN_H\n \n #define COL_MODE          0x000F\n+#define COL_MODE_COLUMN        0   /* Fill columns before rows */\n+#define COL_MODE_ROW           1   /* Fill rows before columns */\n #define COL_ENABLED      (1 << 4)\n #define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set? */\n+#define COL_ANSI         (1 << 6)  /* Remove ANSI escapes from string length */\n \n extern int term_columns(void);\n extern struct string_list_item *add_to_columns(struct string_list *list, int mode, const char *string);\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nindex 099a29f..f801190 100755\n--- a/t/t9002-column.sh\n+++ b/t/t9002-column.sh\n@@ -24,4 +24,90 @@ test_expect_success 'never' '\n \ttest_cmp lista actual\n '\n \n+test_expect_success '80 columns' '\n+\tcat >expected <<\\EOF &&\n+one    two    three  four   five   six    seven  eight  nine   ten    eleven\n+EOF\n+\tCOLUMNS=80 test-column --mode=column <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'COLUMNS = 1' '\n+\tcat >expected <<\\EOF &&\n+one\n+two\n+three\n+four\n+five\n+six\n+seven\n+eight\n+nine\n+ten\n+eleven\n+EOF\n+\tCOLUMNS=1 test-column --mode=column <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'width = 1' '\n+\ttest-column --mode=column --width=1 <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+COLUMNS=20\n+export COLUMNS\n+\n+test_expect_success '20 columns' '\n+\tcat >expected <<\\EOF &&\n+one    seven\n+two    eight\n+three  nine\n+four   ten\n+five   eleven\n+six\n+EOF\n+\ttest-column --mode=column <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, padding 2' '\n+\tcat >expected <<\\EOF &&\n+one     seven\n+two     eight\n+three   nine\n+four    ten\n+five    eleven\n+six\n+EOF\n+\ttest-column --mode=column --padding 2 <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, left indented' '\n+\tcat >expected <<\\EOF &&\n+  one    seven\n+  two    eight\n+  three  nine\n+  four   ten\n+  five   eleven\n+  six\n+EOF\n+\ttest-column --mode=column --left=2 <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, row first' '\n+\tcat >expected <<\\EOF &&\n+one    two\n+three  four\n+five   six\n+seven  eight\n+nine   ten\n+eleven\n+EOF\n+\ttest-column --mode=row <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.7.4.74.g639db\n"},{"id":"163822","messageId":"1300625873-18435-6-git-send-email-pclouds@gmail.com","threadId":"26803","inReplyTo":"1300625873-18435-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 5/9] display_columns: add COL_DENSE to do unequal column layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-03-20T12:57:49Z","receivedAt":"2011-03-20T12:57:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n column.c          |   80 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n column.h          |    1 +\n t/t9002-column.sh |   22 ++++++++++++++\n 3 files changed, 103 insertions(+), 0 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex 15f74a5..69d42f8 100644\n--- a/column.c\n+++ b/column.c\n@@ -18,6 +18,7 @@ struct column_data {\n \n \tint rows, cols;\n \tint *len;\t\t\t/* cell length */\n+\tint *width;\t\t\t/* index to the longest row in column */\n };\n \n /* return length of 's' in letters, ANSI escapes stripped */\n@@ -62,6 +63,69 @@ static void layout(struct column_data *data, int *width)\n \tdata->rows = DIV_ROUND_UP(data->list->nr, data->cols);\n }\n \n+static void compute_column_width(struct column_data *data)\n+{\n+\tint i, x, y;\n+\tfor (x = 0; x < data->cols; x++) {\n+\t\tdata->width[x] = XY2LINEAR(data, x, 0);\n+\t\tfor (y = 0; y < data->rows; y++) {\n+\t\t\ti = XY2LINEAR(data, x, y);\n+\t\t\tif (i >= data->list->nr)\n+\t\t\t\tcontinue;\n+\t\t\tif (data->len[data->width[x]] < data->len[i])\n+\t\t\t\tdata->width[x] = i;\n+\t\t}\n+\t}\n+}\n+\n+/*\n+ * Shrink all columns by shortening them one row each time (and adding\n+ * more columns along the way). Hopefully the longest cell will be\n+ * moved to the next column, column is shrunk so we have more space\n+ * for new columns. The process ends when the whole thing no longer\n+ * fits in data->total_width.\n+ */\n+static void shrink_columns(struct column_data *data)\n+{\n+\tint x, y, total_width, cols, rows;\n+\n+\tdata->width = xrealloc(data->width,\n+\t\t\t       sizeof(*data->width) * data->cols);\n+\tfor (x = 0; x < data->cols; x++) {\n+\t\tdata->width[x] = 0;\n+\t\tfor (y = 0; y < data->rows; y++) {\n+\t\t\tint len1 = data->len[data->width[x]];\n+\t\t\tint len2 = data->len[XY2LINEAR(data, x, y)];\n+\t\t\tif (len1 < len2)\n+\t\t\t\tdata->width[x] = y;\n+\t\t}\n+\t}\n+\n+\twhile (data->rows > 1) {\n+\t\trows = data->rows;\n+\t\tcols = data->cols;\n+\n+\t\tdata->rows--;\n+\t\tdata->cols = DIV_ROUND_UP(data->list->nr, data->rows);\n+\t\tif (data->cols != cols)\n+\t\t\tdata->width = xrealloc(data->width, sizeof(*data->width) * data->cols);\n+\n+\t\tcompute_column_width(data);\n+\n+\t\ttotal_width = strlen(data->indent);\n+\t\tfor (x = 0; x < data->cols; x++) {\n+\t\t\ttotal_width += data->len[data->width[x]];\n+\t\t\ttotal_width += data->padding;\n+\t\t}\n+\t\tif (total_width > data->total_width) {\n+\t\t\tdata->rows = rows;\n+\t\t\tdata->cols = cols;\n+\t\t\tcompute_column_width(data);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+}\n+\n struct string_list_item *add_to_columns(struct string_list *list, int mode,\n \t\t\t\t\tconst char *string)\n {\n@@ -89,7 +153,18 @@ static int display_cell(struct column_data *data, int initial_width,\n \ti = XY2LINEAR(data, x, y);\n \tif (i >= data->list->nr)\n \t\treturn -1;\n+\n \tlen = data->len[i];\n+\tif (data->width && data->len[data->width[x]] < initial_width) {\n+\t\t/*\n+\t\t * empty_cell has initial_width chars, if real column\n+\t\t * is narrower, increase len a bit so we fill less\n+\t\t * space.\n+\t\t */\n+\t\tlen += initial_width - data->len[data->width[x]];\n+\t\tlen -= data->padding;\n+\t}\n+\n \tif (MODE(data->mode) == COL_MODE_COLUMN)\n \t\tnewline = i + data->rows >= data->list->nr;\n \telse\n@@ -124,6 +199,9 @@ static void display_table(const struct string_list *list,\n \n \tlayout(&data, &initial_width);\n \n+\tif (mode & COL_DENSE)\n+\t\tshrink_columns(&data);\n+\n \tempty_cell = xmalloc(initial_width + 1);\n \tmemset(empty_cell, ' ', initial_width);\n \tempty_cell[initial_width] = '\\0';\n@@ -134,6 +212,7 @@ static void display_table(const struct string_list *list,\n \t}\n \n \tfree(data.len);\n+\tfree(data.width);\n \tfree(empty_cell);\n }\n \n@@ -221,6 +300,7 @@ static int parse_option(const char *arg, int len,\n \t\t{ ENABLE, \"auto\",   -1 },\n \t\t{ MODE,   \"column\", COL_MODE_COLUMN },\n \t\t{ MODE,   \"row\",    COL_MODE_ROW },\n+\t\t{ OPTION, \"dense\",  COL_DENSE },\n \t};\n \tint i, set, name_len;\n \ndiff --git a/column.h b/column.h\nindex 4fb1004..48c6345 100644\n--- a/column.h\n+++ b/column.h\n@@ -7,6 +7,7 @@\n #define COL_ENABLED      (1 << 4)\n #define COL_ENABLED_SET  (1 << 5)  /* Has COL_ENABLED been set? */\n #define COL_ANSI         (1 << 6)  /* Remove ANSI escapes from string length */\n+#define COL_DENSE        (1 << 7)  /* Shrink columns when possible, making space for more columns */\n \n extern int term_columns(void);\n extern struct string_list_item *add_to_columns(struct string_list *list, int mode, const char *string);\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nindex f801190..07161a9 100755\n--- a/t/t9002-column.sh\n+++ b/t/t9002-column.sh\n@@ -71,6 +71,17 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success '20 columns, dense' '\n+\tcat >expected <<\\EOF &&\n+one   five  nine\n+two   six   ten\n+three seven eleven\n+four  eight\n+EOF\n+\ttest-column --mode=column,dense < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success '20 columns, padding 2' '\n \tcat >expected <<\\EOF &&\n one     seven\n@@ -110,4 +121,15 @@ EOF\n \ttest_cmp expected actual\n '\n \n+test_expect_success '20 columns, row first, dense' '\n+\tcat >expected <<\\EOF &&\n+one   two    three\n+four  five   six\n+seven eight  nine\n+ten   eleven\n+EOF\n+\ttest-column --mode=row,dense <lista >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.7.4.74.g639db\n"},{"id":"163823","messageId":"1300625873-18435-7-git-send-email-pclouds@gmail.com","threadId":"26803","inReplyTo":"1300625873-18435-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 6/9] column: add column.ui for default column output settings","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-03-20T12:57:50Z","receivedAt":"2011-03-20T12:57:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt |   24 ++++++++++++++++++++++++\n column.c                 |   11 +++++++++++\n column.h                 |    3 +++\n 3 files changed, 38 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex c5e1835..7183712 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -805,6 +805,30 @@ color.ui::\n \tterminal. When more specific variables of color.* are set, they always\n \ttake precedence over this setting. Defaults to false.\n \n+column.ui::\n+\tSpecify whether supported commands should output in columns.\n+\tThis variable consists of a list of tokens separated by spaces\n+\tor commas:\n++\n+--\n+`always`;;\n+\talways show in columns\n+`never`;;\n+\tnever show in columns\n+`auto`;;\n+\tshow in columns if the output is to the terminal\n+`column`;;\n+\tfill columns before rows (default)\n+`row`;;\n+\tfill rows before columns\n+`dense`;;\n+\tmake unequal size columns to utilize more space\n+`nodense`;;\n+\tmake equal size columns\n+--\n++\n+\tThis option defaults to 'never'.\n+\n commit.status::\n \tA boolean to enable/disable inclusion of status information in the\n \tcommit message template when using an editor to prepare the commit\ndiff --git a/column.c b/column.c\nindex 69d42f8..7c1a6f1 100644\n--- a/column.c\n+++ b/column.c\n@@ -2,6 +2,7 @@\n #include \"column.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\n+#include \"color.h\"\n #include \"utf8.h\"\n \n #define MODE(mode) ((mode) & COL_MODE)\n@@ -21,6 +22,8 @@ struct column_data {\n \tint *width;\t\t\t/* index to the longest row in column */\n };\n \n+int git_core_column;\n+\n /* return length of 's' in letters, ANSI escapes stripped */\n static int item_length(int mode, const char *s)\n {\n@@ -350,6 +353,14 @@ static int parse_string(int *mode, const char *value,\n \treturn 0;\n }\n \n+int git_column_default_config(const char *var, const char *value, void *cb)\n+{\n+\tif (!strcmp(var, \"column.ui\"))\n+\t\treturn parse_string(&git_core_column, value, -1);\n+\n+\treturn git_color_default_config(var, value, cb);\n+}\n+\n int parseopt_column_callback(const struct option *opt,\n \t\t\t     const char *arg, int unset)\n {\ndiff --git a/column.h b/column.h\nindex 48c6345..0f4190d 100644\n--- a/column.h\n+++ b/column.h\n@@ -9,9 +9,12 @@\n #define COL_ANSI         (1 << 6)  /* Remove ANSI escapes from string length */\n #define COL_DENSE        (1 << 7)  /* Shrink columns when possible, making space for more columns */\n \n+extern int git_core_column;\n+\n extern int term_columns(void);\n extern struct string_list_item *add_to_columns(struct string_list *list, int mode, const char *string);\n extern void display_columns(const struct string_list *list, int mode, int width, int padding, const char *indent);\n+extern int git_column_default_config(const char *var, const char *value, void *cb);\n \n struct option;\n extern int parseopt_column_callback(const struct option *opt, const char *arg, int unset);\n-- \n1.7.4.74.g639db\n"},{"id":"163824","messageId":"1300625873-18435-8-git-send-email-pclouds@gmail.com","threadId":"26803","inReplyTo":"1300625873-18435-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 7/9] help: reuse display_columns() for help -a","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-03-20T12:57:51Z","receivedAt":"2011-03-20T12:57:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n help.c |   30 ++++++++----------------------\n 1 files changed, 8 insertions(+), 22 deletions(-)\n\ndiff --git a/help.c b/help.c\nindex 768f64c..dcc8b81 100644\n--- a/help.c\n+++ b/help.c\n@@ -4,6 +4,7 @@\n #include \"levenshtein.h\"\n #include \"help.h\"\n #include \"common-cmds.h\"\n+#include \"string-list.h\"\n #include \"column.h\"\n \n void add_cmdname(struct cmdnames *cmds, const char *name, int len)\n@@ -73,29 +74,14 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)\n \n static void pretty_print_string_list(struct cmdnames *cmds, int longest)\n {\n-\tint cols = 1, rows;\n-\tint space = longest + 1; /* min 1 SP between words */\n-\tint max_cols = term_columns() - 1; /* don't print *on* the edge */\n-\tint i, j;\n-\n-\tif (space < max_cols)\n-\t\tcols = max_cols / space;\n-\trows = DIV_ROUND_UP(cmds->cnt, cols);\n-\n-\tfor (i = 0; i < rows; i++) {\n-\t\tprintf(\"  \");\n+\tstruct string_list list = STRING_LIST_INIT_NODUP;\n+\tint i;\n \n-\t\tfor (j = 0; j < cols; j++) {\n-\t\t\tint n = j * rows + i;\n-\t\t\tint size = space;\n-\t\t\tif (n >= cmds->cnt)\n-\t\t\t\tbreak;\n-\t\t\tif (j == cols-1 || n + rows >= cmds->cnt)\n-\t\t\t\tsize = 1;\n-\t\t\tprintf(\"%-*s\", size, cmds->names[n]->name);\n-\t\t}\n-\t\tputchar('\\n');\n-\t}\n+\tfor (i = 0; i < cmds->cnt; i++)\n+\t\tstring_list_append(&list, cmds->names[i]->name);\n+\tdisplay_columns(&list, COL_MODE_COLUMN | COL_ENABLED,\n+\t\t\tterm_columns(), 1, \"  \");\n+\tstring_list_clear(&list, 0);\n }\n \n static int is_executable(const char *name)\n-- \n1.7.4.74.g639db\n"},{"id":"163825","messageId":"1300625873-18435-9-git-send-email-pclouds@gmail.com","threadId":"26803","inReplyTo":"1300625873-18435-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 8/9] tag: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-03-20T12:57:52Z","receivedAt":"2011-03-20T12:57:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-tag.txt |   11 ++++++++++-\n Makefile                  |    2 +-\n builtin/tag.c             |   21 +++++++++++++++++----\n 3 files changed, 28 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt\nindex 8b169e3..0260eea 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -12,7 +12,8 @@ SYNOPSIS\n 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n \t<tagname> [<commit> | <object>]\n 'git tag' -d <tagname>...\n-'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>]\n+'git tag' [-n[<num>]] -l [--column[=<options>] | --no-column]\n+\t[--contains <commit>] [<pattern>]\n 'git tag' -v <tagname>...\n \n DESCRIPTION\n@@ -71,6 +72,14 @@ OPTIONS\n \tList tags with names that match the given pattern (or all if no pattern is given).\n \tTyping \"git tag\" without arguments, also lists all tags.\n \n+--column[=<options>]::\n+--no-column::\n+\tOverride column.ui settings. See linkgit:git-config.txt[1] for\n+\tsyntax. `--column` and `--no-column` without options are\n+\tequivalent to 'always' and 'never' respectively. This option\n+\tis only applicable if `git tag` is used to list tags without\n+\tannotation lines.\n+\n --contains <commit>::\n \tOnly list tags which contain the specified commit.\n \ndiff --git a/Makefile b/Makefile\nindex ba9944b..e1823dd 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1958,7 +1958,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o http-backend.o: url.h\n-column.o help.o pager.o: column.h\n+builtin/tag.o column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex aa1f87d..cdf0f74 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -12,6 +12,8 @@\n #include \"tag.h\"\n #include \"run-command.h\"\n #include \"parse-options.h\"\n+#include \"string-list.h\"\n+#include \"column.h\"\n \n static const char * const git_tag_usage[] = {\n \t\"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]\",\n@@ -22,6 +24,8 @@ static const char * const git_tag_usage[] = {\n };\n \n static char signingkey[1000];\n+static struct string_list output = STRING_LIST_INIT_DUP;\n+static int column_mode;\n \n struct tag_filter {\n \tconst char *pattern;\n@@ -52,7 +56,7 @@ static int show_reference(const char *refname, const unsigned char *sha1,\n \t\t}\n \n \t\tif (!filter->lines) {\n-\t\t\tprintf(\"%s\\n\", refname);\n+\t\t\tadd_to_columns(&output, column_mode, refname);\n \t\t\treturn 0;\n \t\t}\n \t\tprintf(\"%-15s \", refname);\n@@ -233,7 +237,7 @@ static int git_tag_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n-\treturn git_default_config(var, value, cb);\n+\treturn git_column_default_config(var, value, cb);\n }\n \n static void write_tag_body(int fd, const unsigned char *sha1)\n@@ -383,6 +387,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING('u', NULL, &keyid, \"key-id\",\n \t\t\t\t\t\"use another key to sign the tag\"),\n \t\tOPT__FORCE(&force, \"replace the tag if exists\"),\n+\t\tOPT_COLUMN(0, \"column\", &column_mode, \"show tag list in columns\" ),\n \n \t\tOPT_GROUP(\"Tag listing options\"),\n \t\t{\n@@ -395,6 +400,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t};\n \n \tgit_config(git_tag_config, NULL);\n+\tcolumn_mode = git_core_column;\n \n \targc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);\n \n@@ -413,9 +419,16 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \n \tif (list + delete + verify > 1)\n \t\tusage_with_options(git_tag_usage, options);\n-\tif (list)\n-\t\treturn list_tags(argv[0], lines == -1 ? 0 : lines,\n+\tif (list) {\n+\t\tint ret;\n+\n+\t\tif (lines != -1)\n+\t\t\tcolumn_mode = 0;\n+\t\tret =  list_tags(argv[0], lines == -1 ? 0 : lines,\n \t\t\t\t with_commit);\n+\t\tdisplay_columns(&output, column_mode, term_columns(), 2, \"\");\n+\t\treturn ret;\n+\t}\n \tif (lines != -1)\n \t\tdie(\"-n option is only allowed with -l.\");\n \tif (with_commit)\n-- \n1.7.4.74.g639db\n"},{"id":"163826","messageId":"1300625873-18435-10-git-send-email-pclouds@gmail.com","threadId":"26803","inReplyTo":"1300625873-18435-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 9/9] branch: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-03-20T12:57:53Z","receivedAt":"2011-03-20T12:57:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-branch.txt |    9 +++++++++\n Makefile                     |    2 +-\n builtin/branch.c             |   23 +++++++++++++++++++----\n 3 files changed, 29 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 9106d38..a7bf4a8 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n 'git branch' [--color[=<when>] | --no-color] [-r | -a]\n+\t[--column[=<options>] | --no-column]\n \t[-v [--abbrev=<length> | --no-abbrev]]\n \t[(--merged | --no-merged | --contains) [<commit>]]\n 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]\n@@ -99,6 +100,14 @@ OPTIONS\n \tdefault to color output.\n \tSame as `--color=never`.\n \n+--column[=<options>]::\n+--no-column::\n+\tOverride column.ui settings. See linkgit:git-config.txt[1] for\n+\tsyntax. `--column` and `--no-column` without options are\n+\tequivalent to 'always' and 'never' respectively. This option\n+\tis only applicable if `git tag` is used to list tags without\n+\tannotation lines.\n+\n -r::\n \tList or delete (if used with -d) the remote-tracking branches.\n \ndiff --git a/Makefile b/Makefile\nindex e1823dd..92c49ac 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1958,7 +1958,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o http-backend.o: url.h\n-builtin/tag.o column.o help.o pager.o: column.h\n+builtin/branch.o builtin/tag.o column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 9e546e4..ff94192 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -15,6 +15,8 @@\n #include \"branch.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n+#include \"string-list.h\"\n+#include \"column.h\"\n \n static const char * const builtin_branch_usage[] = {\n \t\"git branch [options] [-r | -a] [--merged | --no-merged]\",\n@@ -53,6 +55,9 @@ static enum merge_filter {\n } merge_filter;\n static unsigned char merge_filter_ref[20];\n \n+static struct string_list output = STRING_LIST_INIT_DUP;\n+static int column_mode;\n+\n static int parse_branch_color_slot(const char *var, int ofs)\n {\n \tif (!strcasecmp(var+ofs, \"plain\"))\n@@ -83,7 +88,7 @@ static int git_branch_config(const char *var, const char *value, void *cb)\n \t\tcolor_parse(value, var, branch_colors[slot]);\n \t\treturn 0;\n \t}\n-\treturn git_color_default_config(var, value, cb);\n+\treturn git_column_default_config(var, value, cb);\n }\n \n static const char *branch_get_color(enum color_branch ix)\n@@ -451,7 +456,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,\n \t\tstrbuf_release(&stat);\n \t\tstrbuf_release(&subject);\n \t}\n-\tprintf(\"%s\\n\", out.buf);\n+\tadd_to_columns(&output, column_mode, out.buf);\n \tstrbuf_release(&name);\n \tstrbuf_release(&out);\n }\n@@ -660,6 +665,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tPARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,\n \t\t\topt_parse_merge_filter, (intptr_t) \"HEAD\",\n \t\t},\n+\t\tOPT_COLUMN(0, \"column\", &column_mode, \"list branches in columns\" ),\n \t\tOPT_END(),\n \t};\n \n@@ -686,6 +692,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t}\n \thashcpy(merge_filter_ref, head_sha1);\n \n+\tcolumn_mode = git_core_column | COL_ANSI;\n+\n \targc = parse_options(argc, argv, prefix, options, builtin_branch_usage,\n \t\t\t     0);\n \tif (!!delete + !!rename + !!force_create > 1)\n@@ -693,8 +701,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \n \tif (delete)\n \t\treturn delete_branches(argc, argv, delete > 1, kinds);\n-\telse if (argc == 0)\n-\t\treturn print_ref_list(kinds, detached, verbose, abbrev, with_commit);\n+\telse if (argc == 0) {\n+\t\tint ret;\n+\t\tif (verbose)\n+\t\t\tcolumn_mode = 0;\n+\n+\t\tret = print_ref_list(kinds, detached, verbose, abbrev, with_commit);\n+\t\tdisplay_columns(&output, column_mode, term_columns(), 1, \"\");\n+\t\treturn ret;\n+\t}\n \telse if (rename && (argc == 1))\n \t\trename_branch(head, argv[0], rename > 1);\n \telse if (rename && (argc == 2))\n-- \n1.7.4.74.g639db\n"},{"id":"163828","messageId":"1300637089-21440-1-git-send-email-pclouds@gmail.com","threadId":"26803","inReplyTo":"1300625873-18435-8-git-send-email-pclouds@gmail.com","subject":"[PATCH 7/9] help: reuse display_columns() for help -a","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-03-20T16:04:49Z","receivedAt":"2011-03-20T16:04:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n This one is better. Because 'longest' is no longer used, all support\n code may be removed as well.\n\n help.c |   45 +++++++++++----------------------------------\n 1 files changed, 11 insertions(+), 34 deletions(-)\n\ndiff --git a/help.c b/help.c\nindex 768f64c..92150ea 100644\n--- a/help.c\n+++ b/help.c\n@@ -4,6 +4,7 @@\n #include \"levenshtein.h\"\n #include \"help.h\"\n #include \"common-cmds.h\"\n+#include \"string-list.h\"\n #include \"column.h\"\n \n void add_cmdname(struct cmdnames *cmds, const char *name, int len)\n@@ -71,31 +72,16 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)\n \tcmds->cnt = cj;\n }\n \n-static void pretty_print_string_list(struct cmdnames *cmds, int longest)\n+static void pretty_print_string_list(struct cmdnames *cmds)\n {\n-\tint cols = 1, rows;\n-\tint space = longest + 1; /* min 1 SP between words */\n-\tint max_cols = term_columns() - 1; /* don't print *on* the edge */\n-\tint i, j;\n-\n-\tif (space < max_cols)\n-\t\tcols = max_cols / space;\n-\trows = DIV_ROUND_UP(cmds->cnt, cols);\n-\n-\tfor (i = 0; i < rows; i++) {\n-\t\tprintf(\"  \");\n+\tstruct string_list list = STRING_LIST_INIT_NODUP;\n+\tint i;\n \n-\t\tfor (j = 0; j < cols; j++) {\n-\t\t\tint n = j * rows + i;\n-\t\t\tint size = space;\n-\t\t\tif (n >= cmds->cnt)\n-\t\t\t\tbreak;\n-\t\t\tif (j == cols-1 || n + rows >= cmds->cnt)\n-\t\t\t\tsize = 1;\n-\t\t\tprintf(\"%-*s\", size, cmds->names[n]->name);\n-\t\t}\n-\t\tputchar('\\n');\n-\t}\n+\tfor (i = 0; i < cmds->cnt; i++)\n+\t\tstring_list_append(&list, cmds->names[i]->name);\n+\tdisplay_columns(&list, COL_MODE_COLUMN | COL_ENABLED,\n+\t\t\tterm_columns(), 1, \"  \");\n+\tstring_list_clear(&list, 0);\n }\n \n static int is_executable(const char *name)\n@@ -204,22 +190,13 @@ void load_command_list(const char *prefix,\n void list_commands(const char *title, struct cmdnames *main_cmds,\n \t\t   struct cmdnames *other_cmds)\n {\n-\tint i, longest = 0;\n-\n-\tfor (i = 0; i < main_cmds->cnt; i++)\n-\t\tif (longest < main_cmds->names[i]->len)\n-\t\t\tlongest = main_cmds->names[i]->len;\n-\tfor (i = 0; i < other_cmds->cnt; i++)\n-\t\tif (longest < other_cmds->names[i]->len)\n-\t\t\tlongest = other_cmds->names[i]->len;\n-\n \tif (main_cmds->cnt) {\n \t\tconst char *exec_path = git_exec_path();\n \t\tprintf(\"available %s in '%s'\\n\", title, exec_path);\n \t\tprintf(\"----------------\");\n \t\tmput_char('-', strlen(title) + strlen(exec_path));\n \t\tputchar('\\n');\n-\t\tpretty_print_string_list(main_cmds, longest);\n+\t\tpretty_print_string_list(main_cmds);\n \t\tputchar('\\n');\n \t}\n \n@@ -228,7 +205,7 @@ void list_commands(const char *title, struct cmdnames *main_cmds,\n \t\tprintf(\"---------------------------------------\");\n \t\tmput_char('-', strlen(title));\n \t\tputchar('\\n');\n-\t\tpretty_print_string_list(other_cmds, longest);\n+\t\tpretty_print_string_list(other_cmds);\n \t\tputchar('\\n');\n \t}\n }\n-- \n1.7.4.74.g639db\n"},{"id":"163835","messageId":"87oc557env.fsf@mithlond.arda","threadId":"26803","inReplyTo":"1300625873-18435-10-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 9/9] branch: add --column","fromName":"Teemu Likonen","fromEmail":"tlikonen@iki.fi","sentAt":"2011-03-20T19:52:36Z","receivedAt":"2011-03-20T19:52:36Z","isPatch":true,"sender":{"key":"tlikonen@iki.fi","avatar":null},"body":"* 2011-03-20 19:57 (+0700), Nguyễn Thái Ngọc Duy wrote:\n\n> diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\n> index 9106d38..a7bf4a8 100644\n> --- a/Documentation/git-branch.txt\n> +++ b/Documentation/git-branch.txt\n\n> +--column[=<options>]::\n> +--no-column::\n> +\tOverride column.ui settings. See linkgit:git-config.txt[1] for\n> +\tsyntax. `--column` and `--no-column` without options are\n> +\tequivalent to 'always' and 'never' respectively. This option\n> +\tis only applicable if `git tag` is used to list tags without\n> +\tannotation lines.\n> +\n                                \nI admit that I have not been following the development of this feature\nbut I'll confirm this anyway: Do you really mean to speak of \"git tag\"\nin the man page of \"git branch\"?\n"},{"id":"163847","messageId":"1300663591-28102-1-git-send-email-pclouds@gmail.com","threadId":"26803","inReplyTo":"87oc557env.fsf@mithlond.arda","subject":"[PATCH 9/9] branch: add --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-03-20T23:26:31Z","receivedAt":"2011-03-20T23:26:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n 2011/3/21 Teemu Likonen <tlikonen@iki.fi>:\n > I admit that I have not been following the development of this feature\n > but I'll confirm this anyway: Do you really mean to speak of \"git tag\"\n > in the man page of \"git branch\"?\n >\n\n Copy/paste error. Fixed\n\n Documentation/git-branch.txt |    8 ++++++++\n Makefile                     |    2 +-\n builtin/branch.c             |   23 +++++++++++++++++++----\n 3 files changed, 28 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 9106d38..f129339 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n 'git branch' [--color[=<when>] | --no-color] [-r | -a]\n+\t[--column[=<options>] | --no-column]\n \t[-v [--abbrev=<length> | --no-abbrev]]\n \t[(--merged | --no-merged | --contains) [<commit>]]\n 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]\n@@ -99,6 +100,13 @@ OPTIONS\n \tdefault to color output.\n \tSame as `--color=never`.\n \n+--column[=<options>]::\n+--no-column::\n+\tOverride column.ui settings. See linkgit:git-config.txt[1] for\n+\tsyntax. `--column` and `--no-column` without options are\n+\tequivalent to 'always' and 'never' respectively. This option\n+\tis only applicable in non-verbose mode.\n+\n -r::\n \tList or delete (if used with -d) the remote-tracking branches.\n \ndiff --git a/Makefile b/Makefile\nindex e1823dd..92c49ac 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1958,7 +1958,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o http-backend.o: url.h\n-builtin/tag.o column.o help.o pager.o: column.h\n+builtin/branch.o builtin/tag.o column.o help.o pager.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 9e546e4..ff94192 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -15,6 +15,8 @@\n #include \"branch.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n+#include \"string-list.h\"\n+#include \"column.h\"\n \n static const char * const builtin_branch_usage[] = {\n \t\"git branch [options] [-r | -a] [--merged | --no-merged]\",\n@@ -53,6 +55,9 @@ static enum merge_filter {\n } merge_filter;\n static unsigned char merge_filter_ref[20];\n \n+static struct string_list output = STRING_LIST_INIT_DUP;\n+static int column_mode;\n+\n static int parse_branch_color_slot(const char *var, int ofs)\n {\n \tif (!strcasecmp(var+ofs, \"plain\"))\n@@ -83,7 +88,7 @@ static int git_branch_config(const char *var, const char *value, void *cb)\n \t\tcolor_parse(value, var, branch_colors[slot]);\n \t\treturn 0;\n \t}\n-\treturn git_color_default_config(var, value, cb);\n+\treturn git_column_default_config(var, value, cb);\n }\n \n static const char *branch_get_color(enum color_branch ix)\n@@ -451,7 +456,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,\n \t\tstrbuf_release(&stat);\n \t\tstrbuf_release(&subject);\n \t}\n-\tprintf(\"%s\\n\", out.buf);\n+\tadd_to_columns(&output, column_mode, out.buf);\n \tstrbuf_release(&name);\n \tstrbuf_release(&out);\n }\n@@ -660,6 +665,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tPARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,\n \t\t\topt_parse_merge_filter, (intptr_t) \"HEAD\",\n \t\t},\n+\t\tOPT_COLUMN(0, \"column\", &column_mode, \"list branches in columns\" ),\n \t\tOPT_END(),\n \t};\n \n@@ -686,6 +692,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t}\n \thashcpy(merge_filter_ref, head_sha1);\n \n+\tcolumn_mode = git_core_column | COL_ANSI;\n+\n \targc = parse_options(argc, argv, prefix, options, builtin_branch_usage,\n \t\t\t     0);\n \tif (!!delete + !!rename + !!force_create > 1)\n@@ -693,8 +701,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \n \tif (delete)\n \t\treturn delete_branches(argc, argv, delete > 1, kinds);\n-\telse if (argc == 0)\n-\t\treturn print_ref_list(kinds, detached, verbose, abbrev, with_commit);\n+\telse if (argc == 0) {\n+\t\tint ret;\n+\t\tif (verbose)\n+\t\t\tcolumn_mode = 0;\n+\n+\t\tret = print_ref_list(kinds, detached, verbose, abbrev, with_commit);\n+\t\tdisplay_columns(&output, column_mode, term_columns(), 1, \"\");\n+\t\treturn ret;\n+\t}\n \telse if (rename && (argc == 1))\n \t\trename_branch(head, argv[0], rename > 1);\n \telse if (rename && (argc == 2))\n-- \n1.7.4.74.g639db\n"}]}