diff --git a/ChangeLog b/ChangeLog index a4a2ad65..7cfb5b7e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18,6 +18,7 @@ master - threadpools size dynamically with load - operations can hint threadpool size - support for N-colour ICC profiles +- add bash completions for "vips" - fits load and allows many more bands - fits write doesn't duplicate header fields - add @wrap to vips_text() diff --git a/README.md b/README.md index b66c4950..2812294a 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,8 @@ libvips must have `build-essential`, `pkg-config`, `libglib2.0-dev`, `libexpat1-dev`. See the **Dependencies** section below for a full list of the libvips optional dependencies. +There are basic bash completions in `completions/`, see the README in there. + ## Cheatsheet ``` diff --git a/completions/README.md b/completions/README.md new file mode 100644 index 00000000..8b90a03f --- /dev/null +++ b/completions/README.md @@ -0,0 +1,28 @@ +# Shell completions for vips + +Basic shell completions for the `vips` program. Internally, these use the +`-c` argument to `vips` to list argument options. + +## Example + +``` +$ vips relational +relational relational_const +$ vips relational_const ~/pics/k2. +~/pics/k2.avif ~/pics/k2.hdr ~/pics/k2.pdf ~/pics/k2.tif +~/pics/k2.bmp ~/pics/k2.heic ~/pics/k2.pfm ~/pics/k2.v +~/pics/k2.csv ~/pics/k2.jp2 ~/pics/k2.pgm ~/pics/k2.vips +~/pics/k2.fits ~/pics/k2.jpg ~/pics/k2.png ~/pics/k2.webp +~/pics/k2.flif ~/pics/k2.jxl ~/pics/k2.ppm +~/pics/k2.gif ~/pics/k2.pbm ~/pics/k2.ppt +$ vips relational_const ~/pics/k2.jpg x.v less +less lesseq +$ vips relational_const ~/pics/k2.jpg x.v lesseq 12 +``` + +## Install + +### `vips-completion.bash` + +Usually copy to `/etc/bash_completion.d` to install, but it depends on your +system. diff --git a/completions/vips-completion.bash b/completions/vips-completion.bash new file mode 100644 index 00000000..c92f3684 --- /dev/null +++ b/completions/vips-completion.bash @@ -0,0 +1,43 @@ +#/usr/bin/env bash + +# bash completions for the "vips" command + +# copy to /etc/bash_completion.d to install + +_vips_compgen_f() +{ + COMPREPLY=($(compgen -f -- "${COMP_WORDS[-1]}")) + + if [ ${#COMPREPLY[@]} = 1 ]; then + local LASTCHAR=' ' + if [ -d "$COMPREPLY" ]; then + LASTCHAR=/ + fi + + COMPREPLY=$(printf %q%s "$COMPREPLY" "$LASTCHAR") + else + for ((i=0; i < ${#COMPREPLY[@]}; i++)); do + if [ -d "${COMPREPLY[$i]}" ]; then + COMPREPLY[$i]=${COMPREPLY[$i]}/ + fi + done + fi +} + +_vips_completions() +{ + if [ ${#COMP_WORDS[@]} == "2" ]; then + COMPREPLY=($(compgen -W "$(vips -c)" "${COMP_WORDS[1]}")) + else + local args=($(vips -c ${COMP_WORDS[1]})) + local arg_type=${args[${#COMP_WORDS[@]}-3]} + if [ $arg_type == "file" ]; then + _vips_compgen_f + elif [[ $arg_type = word:* ]]; then + local options=$(echo $arg_type | sed 's/word://' | sed 's/|/ /g') + COMPREPLY=($(compgen -W "${options[@]}" "${COMP_WORDS[-1]}")) + fi + fi +} + +complete -F _vips_completions vips diff --git a/libvips/iofuncs/operation.c b/libvips/iofuncs/operation.c index cffbae94..a8af713a 100644 --- a/libvips/iofuncs/operation.c +++ b/libvips/iofuncs/operation.c @@ -360,7 +360,6 @@ vips_operation_pspec_usage( VipsBuf *buf, GParamSpec *pspec ) vips_buf_appendf( buf, "%s", _( "max" ) ); vips_buf_appendf( buf, ": %d\n", pspec_int->maximum ); } - } static void * diff --git a/man/vips.1 b/man/vips.1 index 495a47ab..b8423aed 100644 --- a/man/vips.1 +++ b/man/vips.1 @@ -33,6 +33,11 @@ loaded automatically. .B -v, --version Show VIPS version. +.TP +.B -c NAME, --completion NAME +Print completions for +.B NAME + .SH COMMANDS .TP @@ -41,7 +46,7 @@ Execute a named operation, for example add. .SH EXAMPLES -Run a vips8 operation. Operation options must follow the operation name. +Run a vips operation. Operation options must follow the operation name. $ vips insert lena.v lena2.v out.v 0 0 --background "128 0 0" diff --git a/tools/vips.c b/tools/vips.c index 05ca901c..62fadcb9 100644 --- a/tools/vips.c +++ b/tools/vips.c @@ -45,6 +45,8 @@ * 18/6/20 kleisauke * - avoid using vips7 symbols * - remove deprecated vips7 C++ generator + * 1/11/22 + * - add "-c" flag */ /* @@ -170,6 +172,119 @@ parse_main_option_list( const gchar *option_name, const gchar *value, exit( 0 ); } +static void * +list_operation( GType type, void *user_data ) +{ + VipsObjectClass *class = VIPS_OBJECT_CLASS( g_type_class_ref( type ) ); + + if( G_TYPE_IS_ABSTRACT( type ) ) + return( NULL ); + if( class->deprecated ) + return( NULL ); + if( VIPS_OPERATION_CLASS( class )->flags & VIPS_OPERATION_DEPRECATED ) + return( NULL ); + + printf( "%s\n", class->nickname ); + + return( NULL ); +} + +static void * +list_operation_arg( VipsObjectClass *object_class, + GParamSpec *pspec, VipsArgumentClass *argument_class, + void *_data1, void *_data2 ) +{ + GType type = G_PARAM_SPEC_VALUE_TYPE( pspec ); + + if( !(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) || + (argument_class->flags & VIPS_ARGUMENT_DEPRECATED) ) + return( NULL ); + + /* We don't try to complete options, though maybe we should. + */ + if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) ) + return( NULL ); + + /* These are the pspecs that vips uses that have interesting values. + */ + if( G_IS_PARAM_SPEC_ENUM( pspec ) ) { + GTypeClass *class = g_type_class_ref( type ); + + GEnumClass *genum; + int i; + + /* Should be impossible, no need to warn. + */ + if( !class ) + return( NULL ); + + genum = G_ENUM_CLASS( class ); + + printf( "word:" ); + + /* -1 since we always have a "last" member. + */ + for( i = 0; i < genum->n_values - 1; i++ ) { + if( i > 0 ) + printf( "|" ); + printf( "%s", genum->values[i].value_nick ); + } + + printf( "\n" ); + } + else if( G_IS_PARAM_SPEC_BOOLEAN( pspec ) ) + printf( "word:true|false\n" ); + else if( G_IS_PARAM_SPEC_DOUBLE( pspec ) ) { + GParamSpecDouble *pspec_double = (GParamSpecDouble *) pspec; + + printf( "word:%g\n", pspec_double->default_value ); + } + else if( G_IS_PARAM_SPEC_INT( pspec ) ) { + GParamSpecInt *pspec_int = (GParamSpecInt *) pspec; + + printf( "word:%d\n", pspec_int->default_value ); + } + else if( G_IS_PARAM_SPEC_OBJECT( pspec ) ) + /* Eg. an image input or output. + */ + printf( "file\n" ); + else + /* We can offer no useful suggestion for eg. array_int etc. + */ + printf( "none\n" ); + + return( NULL ); +} + +static gboolean +parse_main_option_completion( const gchar *option_name, const gchar *value, + gpointer data, GError **error ) +{ + VipsObjectClass *class; + + if( value && + (class = (VipsObjectClass *) vips_type_map_all( + g_type_from_name( "VipsOperation" ), + test_nickname, (void *) value )) ) + vips_argument_class_map( class, + (VipsArgumentClassMapFn) list_operation_arg, + NULL, NULL ); + else if( value ) { + vips_error( g_get_prgname(), + _( "'%s' is not the name of a vips operation" ), + value ); + vips_error_g( error ); + + return( FALSE ); + } + else { + vips_type_map_all( g_type_from_name( "VipsOperation" ), + list_operation, NULL ); + } + + exit( 0 ); +} + static GOptionEntry main_option[] = { { "list", 'l', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, (GOptionArgFunc) parse_main_option_list, @@ -180,6 +295,10 @@ static GOptionEntry main_option[] = { N_( "PLUGIN" ) }, { "version", 'v', 0, G_OPTION_ARG_NONE, &main_option_version, N_( "print version" ), NULL }, + { "completion", 'c', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, + (GOptionArgFunc) parse_main_option_completion, + N_( "print completions" ), + N_( "BASE-NAME" ) }, { NULL } };