Browse Source
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>maint
![pclouds@gmail.com](/assets/img/avatar_default.png)
![Junio C Hamano](/assets/img/avatar_default.png)
12 changed files with 391 additions and 0 deletions
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
git-column(1) |
||||
============= |
||||
|
||||
NAME |
||||
---- |
||||
git-column - Display data in columns |
||||
|
||||
SYNOPSIS |
||||
-------- |
||||
[verse] |
||||
'git column' [--command=<name>] [--[raw-]mode=<mode>] [--width=<width>] |
||||
[--indent=<string>] [--nl=<string>] [--pading=<n>] |
||||
|
||||
DESCRIPTION |
||||
----------- |
||||
This command formats its input into multiple columns. |
||||
|
||||
OPTIONS |
||||
------- |
||||
--command=<name>:: |
||||
Look up layout mode using configuration variable column.<name> and |
||||
column.ui. |
||||
|
||||
--mode=<mode>:: |
||||
Specify layout mode. See configuration variable column.ui for option |
||||
syntax. |
||||
|
||||
--raw-mode=<n>:: |
||||
Same as --mode but take mode encoded as a number. This is mainly used |
||||
by other commands that have already parsed layout mode. |
||||
|
||||
--width=<width>:: |
||||
Specify the terminal width. By default 'git column' will detect the |
||||
terminal width, or fall back to 80 if it is unable to do so. |
||||
|
||||
--indent=<string>:: |
||||
String to be printed at the beginning of each line. |
||||
|
||||
--nl=<N>:: |
||||
String to be printed at the end of each line, |
||||
including newline character. |
||||
|
||||
--padding=<N>:: |
||||
The number of spaces between columns. One space by default. |
||||
|
||||
|
||||
Author |
||||
------ |
||||
Written by Nguyen Thai Ngoc Duy <pclouds@gmail.com> |
||||
|
||||
GIT |
||||
--- |
||||
Part of the linkgit:git[1] suite |
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
#include "builtin.h" |
||||
#include "cache.h" |
||||
#include "strbuf.h" |
||||
#include "parse-options.h" |
||||
#include "string-list.h" |
||||
#include "column.h" |
||||
|
||||
static const char * const builtin_column_usage[] = { |
||||
"git column [options]", |
||||
NULL |
||||
}; |
||||
static unsigned int colopts; |
||||
|
||||
static int column_config(const char *var, const char *value, void *cb) |
||||
{ |
||||
return git_column_config(var, value, cb, &colopts); |
||||
} |
||||
|
||||
int cmd_column(int argc, const char **argv, const char *prefix) |
||||
{ |
||||
struct string_list list = STRING_LIST_INIT_DUP; |
||||
struct strbuf sb = STRBUF_INIT; |
||||
struct column_options copts; |
||||
const char *command = NULL, *real_command = NULL; |
||||
struct option options[] = { |
||||
OPT_STRING(0, "command", &real_command, "name", "lookup config vars"), |
||||
OPT_COLUMN(0, "mode", &colopts, "layout to use"), |
||||
OPT_INTEGER(0, "raw-mode", &colopts, "layout to use"), |
||||
OPT_INTEGER(0, "width", &copts.width, "Maximum width"), |
||||
OPT_STRING(0, "indent", &copts.indent, "string", "Padding space on left border"), |
||||
OPT_INTEGER(0, "nl", &copts.nl, "Padding space on right border"), |
||||
OPT_INTEGER(0, "padding", &copts.padding, "Padding space between columns"), |
||||
OPT_END() |
||||
}; |
||||
|
||||
/* This one is special and must be the first one */ |
||||
if (argc > 1 && !prefixcmp(argv[1], "--command=")) { |
||||
command = argv[1] + 10; |
||||
git_config(column_config, (void *)command); |
||||
} else |
||||
git_config(column_config, NULL); |
||||
|
||||
memset(&copts, 0, sizeof(copts)); |
||||
copts.width = term_columns(); |
||||
copts.padding = 1; |
||||
argc = parse_options(argc, argv, "", options, builtin_column_usage, 0); |
||||
if (argc) |
||||
usage_with_options(builtin_column_usage, options); |
||||
if (real_command || command) { |
||||
if (!real_command || !command || strcmp(real_command, command)) |
||||
die(_("--command must be the first argument")); |
||||
} |
||||
finalize_colopts(&colopts, -1); |
||||
while (!strbuf_getline(&sb, stdin, '\n')) |
||||
string_list_append(&list, sb.buf); |
||||
|
||||
print_columns(&list, colopts, &copts); |
||||
return 0; |
||||
} |
@ -0,0 +1,169 @@
@@ -0,0 +1,169 @@
|
||||
#include "cache.h" |
||||
#include "column.h" |
||||
#include "string-list.h" |
||||
#include "parse-options.h" |
||||
|
||||
/* Display without layout when not enabled */ |
||||
static void display_plain(const struct string_list *list, |
||||
const char *indent, const char *nl) |
||||
{ |
||||
int i; |
||||
|
||||
for (i = 0; i < list->nr; i++) |
||||
printf("%s%s%s", indent, list->items[i].string, nl); |
||||
} |
||||
|
||||
void print_columns(const struct string_list *list, unsigned int colopts, |
||||
const struct column_options *opts) |
||||
{ |
||||
struct column_options nopts; |
||||
|
||||
if (!list->nr) |
||||
return; |
||||
assert((colopts & COL_ENABLE_MASK) != COL_AUTO); |
||||
|
||||
memset(&nopts, 0, sizeof(nopts)); |
||||
nopts.indent = opts && opts->indent ? opts->indent : ""; |
||||
nopts.nl = opts && opts->nl ? opts->nl : "\n"; |
||||
nopts.padding = opts ? opts->padding : 1; |
||||
nopts.width = opts && opts->width ? opts->width : term_columns() - 1; |
||||
if (!column_active(colopts)) { |
||||
display_plain(list, "", "\n"); |
||||
return; |
||||
} |
||||
switch (COL_LAYOUT(colopts)) { |
||||
case COL_PLAIN: |
||||
display_plain(list, nopts.indent, nopts.nl); |
||||
break; |
||||
default: |
||||
die("BUG: invalid layout mode %d", COL_LAYOUT(colopts)); |
||||
} |
||||
} |
||||
|
||||
int finalize_colopts(unsigned int *colopts, int stdout_is_tty) |
||||
{ |
||||
if ((*colopts & COL_ENABLE_MASK) == COL_AUTO) { |
||||
if (stdout_is_tty < 0) |
||||
stdout_is_tty = isatty(1); |
||||
*colopts &= ~COL_ENABLE_MASK; |
||||
if (stdout_is_tty) |
||||
*colopts |= COL_ENABLED; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
struct colopt { |
||||
const char *name; |
||||
unsigned int value; |
||||
unsigned int mask; |
||||
}; |
||||
|
||||
#define LAYOUT_SET 1 |
||||
#define ENABLE_SET 2 |
||||
|
||||
static int parse_option(const char *arg, int len, unsigned int *colopts, |
||||
int *group_set) |
||||
{ |
||||
struct colopt opts[] = { |
||||
{ "always", COL_ENABLED, COL_ENABLE_MASK }, |
||||
{ "never", COL_DISABLED, COL_ENABLE_MASK }, |
||||
{ "auto", COL_AUTO, COL_ENABLE_MASK }, |
||||
{ "plain", COL_PLAIN, COL_LAYOUT_MASK }, |
||||
}; |
||||
int i; |
||||
|
||||
for (i = 0; i < ARRAY_SIZE(opts); i++) { |
||||
int arg_len = len, name_len; |
||||
const char *arg_str = arg; |
||||
|
||||
name_len = strlen(opts[i].name); |
||||
if (arg_len != name_len || |
||||
strncmp(arg_str, opts[i].name, name_len)) |
||||
continue; |
||||
|
||||
switch (opts[i].mask) { |
||||
case COL_ENABLE_MASK: |
||||
*group_set |= ENABLE_SET; |
||||
break; |
||||
case COL_LAYOUT_MASK: |
||||
*group_set |= LAYOUT_SET; |
||||
break; |
||||
} |
||||
|
||||
if (opts[i].mask) |
||||
*colopts = (*colopts & ~opts[i].mask) | opts[i].value; |
||||
return 0; |
||||
} |
||||
|
||||
return error("unsupported option '%s'", arg); |
||||
} |
||||
|
||||
static int parse_config(unsigned int *colopts, const char *value) |
||||
{ |
||||
const char *sep = " ,"; |
||||
int group_set = 0; |
||||
|
||||
while (*value) { |
||||
int len = strcspn(value, sep); |
||||
if (len) { |
||||
if (parse_option(value, len, colopts, &group_set)) |
||||
return -1; |
||||
|
||||
value += len; |
||||
} |
||||
value += strspn(value, sep); |
||||
} |
||||
/* |
||||
* Setting layout implies "always" if neither always, never |
||||
* nor auto is specified. |
||||
* |
||||
* Current value in COL_ENABLE_MASK is disregarded. This means if |
||||
* you set column.ui = auto and pass --column=row, then "auto" |
||||
* will become "always". |
||||
*/ |
||||
if ((group_set & LAYOUT_SET) && !(group_set & ENABLE_SET)) |
||||
*colopts = (*colopts & ~COL_ENABLE_MASK) | COL_ENABLED; |
||||
return 0; |
||||
} |
||||
|
||||
static int column_config(const char *var, const char *value, |
||||
const char *key, unsigned int *colopts) |
||||
{ |
||||
if (!value) |
||||
return config_error_nonbool(var); |
||||
if (parse_config(colopts, value)) |
||||
return error("invalid column.%s mode %s", key, value); |
||||
return 0; |
||||
} |
||||
|
||||
int git_column_config(const char *var, const char *value, |
||||
const char *command, unsigned int *colopts) |
||||
{ |
||||
const char *it = skip_prefix(var, "column."); |
||||
if (!it) |
||||
return 0; |
||||
|
||||
if (!strcmp(it, "ui")) |
||||
return column_config(var, value, "ui", colopts); |
||||
|
||||
if (command && !strcmp(it, command)) |
||||
return column_config(var, value, it, colopts); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int parseopt_column_callback(const struct option *opt, |
||||
const char *arg, int unset) |
||||
{ |
||||
unsigned int *colopts = opt->value; |
||||
*colopts |= COL_PARSEOPT; |
||||
*colopts &= ~COL_ENABLE_MASK; |
||||
if (unset) /* --no-column == never */ |
||||
return 0; |
||||
/* --column == always unless "arg" states otherwise */ |
||||
*colopts |= COL_ENABLED; |
||||
if (arg) |
||||
return parse_config(colopts, arg); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
#ifndef COLUMN_H |
||||
#define COLUMN_H |
||||
|
||||
#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_DISABLED 0x0000 /* must be zero */ |
||||
#define COL_ENABLED 0x0010 |
||||
#define COL_AUTO 0x0020 |
||||
|
||||
#define COL_LAYOUT(c) ((c) & COL_LAYOUT_MASK) |
||||
#define COL_PLAIN 15 /* one column */ |
||||
|
||||
#define explicitly_enable_column(c) \ |
||||
(((c) & COL_PARSEOPT) && column_active(c)) |
||||
|
||||
struct column_options { |
||||
int width; |
||||
int padding; |
||||
const char *indent; |
||||
const char *nl; |
||||
}; |
||||
|
||||
struct option; |
||||
extern int parseopt_column_callback(const struct option *, const char *, int); |
||||
extern int git_column_config(const char *var, const char *value, |
||||
const char *command, unsigned int *colopts); |
||||
extern int finalize_colopts(unsigned int *colopts, int stdout_is_tty); |
||||
static inline int column_active(unsigned int colopts) |
||||
{ |
||||
return (colopts & COL_ENABLE_MASK) == COL_ENABLED; |
||||
} |
||||
|
||||
extern void print_columns(const struct string_list *list, unsigned int colopts, |
||||
const struct column_options *opts); |
||||
|
||||
#endif |
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
#!/bin/sh |
||||
|
||||
test_description='git column' |
||||
. ./test-lib.sh |
||||
|
||||
test_expect_success 'setup' ' |
||||
cat >lista <<\EOF |
||||
one |
||||
two |
||||
three |
||||
four |
||||
five |
||||
six |
||||
seven |
||||
eight |
||||
nine |
||||
ten |
||||
eleven |
||||
EOF |
||||
' |
||||
|
||||
test_expect_success 'never' ' |
||||
git column --indent=Z --mode=never <lista >actual && |
||||
test_cmp lista actual |
||||
' |
||||
|
||||
test_expect_success 'always' ' |
||||
cat >expected <<\EOF && |
||||
Zone |
||||
Ztwo |
||||
Zthree |
||||
Zfour |
||||
Zfive |
||||
Zsix |
||||
Zseven |
||||
Zeight |
||||
Znine |
||||
Zten |
||||
Zeleven |
||||
EOF |
||||
git column --indent=Z --mode=plain <lista >actual && |
||||
test_cmp expected actual |
||||
' |
||||
|
||||
test_done |
Loading…
Reference in new issue