Skip to content

Commit 7e29b82

Browse files
pcloudsgitster
authored andcommitted
Add column layout skeleton and git-column
A column option string consists of many token separated by either a space or a comma. A token belongs to one of three groups: - enabling: always, never and auto - layout mode: currently plain (which does not layout at all) - other future tuning flags git-column can be used to pipe output to from a command that wants column layout, but not to mess with its own output code. Simpler output code can be changed to use column layout code directly. 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>
1 parent b082687 commit 7e29b82

File tree

12 files changed

+391
-0
lines changed

12 files changed

+391
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
/git-cherry-pick
2727
/git-clean
2828
/git-clone
29+
/git-column
2930
/git-commit
3031
/git-commit-tree
3132
/git-config

Documentation/config.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,24 @@ color.ui::
836836
`never` if you prefer git commands not to use color unless enabled
837837
explicitly with some other configuration or the `--color` option.
838838

839+
column.ui::
840+
Specify whether supported commands should output in columns.
841+
This variable consists of a list of tokens separated by spaces
842+
or commas:
843+
+
844+
--
845+
`always`;;
846+
always show in columns
847+
`never`;;
848+
never show in columns
849+
`auto`;;
850+
show in columns if the output is to the terminal
851+
`plain`;;
852+
show in one column
853+
--
854+
+
855+
This option defaults to 'never'.
856+
839857
commit.status::
840858
A boolean to enable/disable inclusion of status information in the
841859
commit message template when using an editor to prepare the commit

Documentation/git-column.txt

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
git-column(1)
2+
=============
3+
4+
NAME
5+
----
6+
git-column - Display data in columns
7+
8+
SYNOPSIS
9+
--------
10+
[verse]
11+
'git column' [--command=<name>] [--[raw-]mode=<mode>] [--width=<width>]
12+
[--indent=<string>] [--nl=<string>] [--pading=<n>]
13+
14+
DESCRIPTION
15+
-----------
16+
This command formats its input into multiple columns.
17+
18+
OPTIONS
19+
-------
20+
--command=<name>::
21+
Look up layout mode using configuration variable column.<name> and
22+
column.ui.
23+
24+
--mode=<mode>::
25+
Specify layout mode. See configuration variable column.ui for option
26+
syntax.
27+
28+
--raw-mode=<n>::
29+
Same as --mode but take mode encoded as a number. This is mainly used
30+
by other commands that have already parsed layout mode.
31+
32+
--width=<width>::
33+
Specify the terminal width. By default 'git column' will detect the
34+
terminal width, or fall back to 80 if it is unable to do so.
35+
36+
--indent=<string>::
37+
String to be printed at the beginning of each line.
38+
39+
--nl=<N>::
40+
String to be printed at the end of each line,
41+
including newline character.
42+
43+
--padding=<N>::
44+
The number of spaces between columns. One space by default.
45+
46+
47+
Author
48+
------
49+
Written by Nguyen Thai Ngoc Duy <pclouds@gmail.com>
50+
51+
GIT
52+
---
53+
Part of the linkgit:git[1] suite

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,7 @@ LIB_OBJS += bulk-checkin.o
646646
LIB_OBJS += bundle.o
647647
LIB_OBJS += cache-tree.o
648648
LIB_OBJS += color.o
649+
LIB_OBJS += column.o
649650
LIB_OBJS += combine-diff.o
650651
LIB_OBJS += commit.o
651652
LIB_OBJS += compat/obstack.o
@@ -774,6 +775,7 @@ BUILTIN_OBJS += builtin/checkout-index.o
774775
BUILTIN_OBJS += builtin/checkout.o
775776
BUILTIN_OBJS += builtin/clean.o
776777
BUILTIN_OBJS += builtin/clone.o
778+
BUILTIN_OBJS += builtin/column.o
777779
BUILTIN_OBJS += builtin/commit-tree.o
778780
BUILTIN_OBJS += builtin/commit.o
779781
BUILTIN_OBJS += builtin/config.o
@@ -2166,6 +2168,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
21662168
builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
21672169
builtin/tar-tree.o archive-tar.o: tar.h
21682170
connect.o transport.o url.o http-backend.o: url.h
2171+
column.o help.o pager.o: column.h
21692172
http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
21702173
http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
21712174

builtin.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ extern int cmd_cherry(int argc, const char **argv, const char *prefix);
6161
extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
6262
extern int cmd_clone(int argc, const char **argv, const char *prefix);
6363
extern int cmd_clean(int argc, const char **argv, const char *prefix);
64+
extern int cmd_column(int argc, const char **argv, const char *prefix);
6465
extern int cmd_commit(int argc, const char **argv, const char *prefix);
6566
extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
6667
extern int cmd_config(int argc, const char **argv, const char *prefix);

builtin/column.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include "builtin.h"
2+
#include "cache.h"
3+
#include "strbuf.h"
4+
#include "parse-options.h"
5+
#include "string-list.h"
6+
#include "column.h"
7+
8+
static const char * const builtin_column_usage[] = {
9+
"git column [options]",
10+
NULL
11+
};
12+
static unsigned int colopts;
13+
14+
static int column_config(const char *var, const char *value, void *cb)
15+
{
16+
return git_column_config(var, value, cb, &colopts);
17+
}
18+
19+
int cmd_column(int argc, const char **argv, const char *prefix)
20+
{
21+
struct string_list list = STRING_LIST_INIT_DUP;
22+
struct strbuf sb = STRBUF_INIT;
23+
struct column_options copts;
24+
const char *command = NULL, *real_command = NULL;
25+
struct option options[] = {
26+
OPT_STRING(0, "command", &real_command, "name", "lookup config vars"),
27+
OPT_COLUMN(0, "mode", &colopts, "layout to use"),
28+
OPT_INTEGER(0, "raw-mode", &colopts, "layout to use"),
29+
OPT_INTEGER(0, "width", &copts.width, "Maximum width"),
30+
OPT_STRING(0, "indent", &copts.indent, "string", "Padding space on left border"),
31+
OPT_INTEGER(0, "nl", &copts.nl, "Padding space on right border"),
32+
OPT_INTEGER(0, "padding", &copts.padding, "Padding space between columns"),
33+
OPT_END()
34+
};
35+
36+
/* This one is special and must be the first one */
37+
if (argc > 1 && !prefixcmp(argv[1], "--command=")) {
38+
command = argv[1] + 10;
39+
git_config(column_config, (void *)command);
40+
} else
41+
git_config(column_config, NULL);
42+
43+
memset(&copts, 0, sizeof(copts));
44+
copts.width = term_columns();
45+
copts.padding = 1;
46+
argc = parse_options(argc, argv, "", options, builtin_column_usage, 0);
47+
if (argc)
48+
usage_with_options(builtin_column_usage, options);
49+
if (real_command || command) {
50+
if (!real_command || !command || strcmp(real_command, command))
51+
die(_("--command must be the first argument"));
52+
}
53+
finalize_colopts(&colopts, -1);
54+
while (!strbuf_getline(&sb, stdin, '\n'))
55+
string_list_append(&list, sb.buf);
56+
57+
print_columns(&list, colopts, &copts);
58+
return 0;
59+
}

column.c

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
#include "cache.h"
2+
#include "column.h"
3+
#include "string-list.h"
4+
#include "parse-options.h"
5+
6+
/* Display without layout when not enabled */
7+
static void display_plain(const struct string_list *list,
8+
const char *indent, const char *nl)
9+
{
10+
int i;
11+
12+
for (i = 0; i < list->nr; i++)
13+
printf("%s%s%s", indent, list->items[i].string, nl);
14+
}
15+
16+
void print_columns(const struct string_list *list, unsigned int colopts,
17+
const struct column_options *opts)
18+
{
19+
struct column_options nopts;
20+
21+
if (!list->nr)
22+
return;
23+
assert((colopts & COL_ENABLE_MASK) != COL_AUTO);
24+
25+
memset(&nopts, 0, sizeof(nopts));
26+
nopts.indent = opts && opts->indent ? opts->indent : "";
27+
nopts.nl = opts && opts->nl ? opts->nl : "\n";
28+
nopts.padding = opts ? opts->padding : 1;
29+
nopts.width = opts && opts->width ? opts->width : term_columns() - 1;
30+
if (!column_active(colopts)) {
31+
display_plain(list, "", "\n");
32+
return;
33+
}
34+
switch (COL_LAYOUT(colopts)) {
35+
case COL_PLAIN:
36+
display_plain(list, nopts.indent, nopts.nl);
37+
break;
38+
default:
39+
die("BUG: invalid layout mode %d", COL_LAYOUT(colopts));
40+
}
41+
}
42+
43+
int finalize_colopts(unsigned int *colopts, int stdout_is_tty)
44+
{
45+
if ((*colopts & COL_ENABLE_MASK) == COL_AUTO) {
46+
if (stdout_is_tty < 0)
47+
stdout_is_tty = isatty(1);
48+
*colopts &= ~COL_ENABLE_MASK;
49+
if (stdout_is_tty)
50+
*colopts |= COL_ENABLED;
51+
}
52+
return 0;
53+
}
54+
55+
struct colopt {
56+
const char *name;
57+
unsigned int value;
58+
unsigned int mask;
59+
};
60+
61+
#define LAYOUT_SET 1
62+
#define ENABLE_SET 2
63+
64+
static int parse_option(const char *arg, int len, unsigned int *colopts,
65+
int *group_set)
66+
{
67+
struct colopt opts[] = {
68+
{ "always", COL_ENABLED, COL_ENABLE_MASK },
69+
{ "never", COL_DISABLED, COL_ENABLE_MASK },
70+
{ "auto", COL_AUTO, COL_ENABLE_MASK },
71+
{ "plain", COL_PLAIN, COL_LAYOUT_MASK },
72+
};
73+
int i;
74+
75+
for (i = 0; i < ARRAY_SIZE(opts); i++) {
76+
int arg_len = len, name_len;
77+
const char *arg_str = arg;
78+
79+
name_len = strlen(opts[i].name);
80+
if (arg_len != name_len ||
81+
strncmp(arg_str, opts[i].name, name_len))
82+
continue;
83+
84+
switch (opts[i].mask) {
85+
case COL_ENABLE_MASK:
86+
*group_set |= ENABLE_SET;
87+
break;
88+
case COL_LAYOUT_MASK:
89+
*group_set |= LAYOUT_SET;
90+
break;
91+
}
92+
93+
if (opts[i].mask)
94+
*colopts = (*colopts & ~opts[i].mask) | opts[i].value;
95+
return 0;
96+
}
97+
98+
return error("unsupported option '%s'", arg);
99+
}
100+
101+
static int parse_config(unsigned int *colopts, const char *value)
102+
{
103+
const char *sep = " ,";
104+
int group_set = 0;
105+
106+
while (*value) {
107+
int len = strcspn(value, sep);
108+
if (len) {
109+
if (parse_option(value, len, colopts, &group_set))
110+
return -1;
111+
112+
value += len;
113+
}
114+
value += strspn(value, sep);
115+
}
116+
/*
117+
* Setting layout implies "always" if neither always, never
118+
* nor auto is specified.
119+
*
120+
* Current value in COL_ENABLE_MASK is disregarded. This means if
121+
* you set column.ui = auto and pass --column=row, then "auto"
122+
* will become "always".
123+
*/
124+
if ((group_set & LAYOUT_SET) && !(group_set & ENABLE_SET))
125+
*colopts = (*colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
126+
return 0;
127+
}
128+
129+
static int column_config(const char *var, const char *value,
130+
const char *key, unsigned int *colopts)
131+
{
132+
if (!value)
133+
return config_error_nonbool(var);
134+
if (parse_config(colopts, value))
135+
return error("invalid column.%s mode %s", key, value);
136+
return 0;
137+
}
138+
139+
int git_column_config(const char *var, const char *value,
140+
const char *command, unsigned int *colopts)
141+
{
142+
const char *it = skip_prefix(var, "column.");
143+
if (!it)
144+
return 0;
145+
146+
if (!strcmp(it, "ui"))
147+
return column_config(var, value, "ui", colopts);
148+
149+
if (command && !strcmp(it, command))
150+
return column_config(var, value, it, colopts);
151+
152+
return 0;
153+
}
154+
155+
int parseopt_column_callback(const struct option *opt,
156+
const char *arg, int unset)
157+
{
158+
unsigned int *colopts = opt->value;
159+
*colopts |= COL_PARSEOPT;
160+
*colopts &= ~COL_ENABLE_MASK;
161+
if (unset) /* --no-column == never */
162+
return 0;
163+
/* --column == always unless "arg" states otherwise */
164+
*colopts |= COL_ENABLED;
165+
if (arg)
166+
return parse_config(colopts, arg);
167+
168+
return 0;
169+
}

column.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#ifndef COLUMN_H
2+
#define COLUMN_H
3+
4+
#define COL_LAYOUT_MASK 0x000F
5+
#define COL_ENABLE_MASK 0x0030 /* always, never or auto */
6+
#define COL_PARSEOPT 0x0040 /* --column is given from cmdline */
7+
8+
#define COL_DISABLED 0x0000 /* must be zero */
9+
#define COL_ENABLED 0x0010
10+
#define COL_AUTO 0x0020
11+
12+
#define COL_LAYOUT(c) ((c) & COL_LAYOUT_MASK)
13+
#define COL_PLAIN 15 /* one column */
14+
15+
#define explicitly_enable_column(c) \
16+
(((c) & COL_PARSEOPT) && column_active(c))
17+
18+
struct column_options {
19+
int width;
20+
int padding;
21+
const char *indent;
22+
const char *nl;
23+
};
24+
25+
struct option;
26+
extern int parseopt_column_callback(const struct option *, const char *, int);
27+
extern int git_column_config(const char *var, const char *value,
28+
const char *command, unsigned int *colopts);
29+
extern int finalize_colopts(unsigned int *colopts, int stdout_is_tty);
30+
static inline int column_active(unsigned int colopts)
31+
{
32+
return (colopts & COL_ENABLE_MASK) == COL_ENABLED;
33+
}
34+
35+
extern void print_columns(const struct string_list *list, unsigned int colopts,
36+
const struct column_options *opts);
37+
38+
#endif

command-list.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ git-cherry-pick mainporcelain
2020
git-citool mainporcelain
2121
git-clean mainporcelain
2222
git-clone mainporcelain common
23+
git-column purehelpers
2324
git-commit mainporcelain common
2425
git-commit-tree plumbingmanipulators
2526
git-config ancillarymanipulators

0 commit comments

Comments
 (0)