use
5.010001;
our
$AUTHORITY
=
'cpan:PERLANCAR'
;
our
$DATE
=
'2022-08-28'
;
our
$DIST
=
'Complete-Getopt-Long'
;
our
$VERSION
=
'0.481'
;
our
@EXPORT_OK
=
qw(
complete_cli_arg
)
;
our
%SPEC
;
our
$COMPLETE_GETOPT_LONG_TRACE
=
$ENV
{COMPLETE_GETOPT_LONG_TRACE} // 0;
our
$COMPLETE_GETOPT_LONG_DEFAULT_ENV
=
$ENV
{COMPLETE_GETOPT_LONG_DEFAULT_ENV} // 1;
our
$COMPLETE_GETOPT_LONG_DEFAULT_FILE
=
$ENV
{COMPLETE_GETOPT_LONG_DEFAULT_FILE} // 1;
sub
_default_completion {
my
%args
=
@_
;
my
$word
=
$args
{word} //
''
;
my
$fres
;
log_trace(
'[compgl] entering default completion routine'
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
if
(
$word
=~ /\A\$/ &&
$COMPLETE_GETOPT_LONG_DEFAULT_ENV
) {
log_trace(
'[compgl] completing shell variable'
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
{
my
$compres
= Complete::Env::complete_env(
word
=>
$word
);
last
unless
@$compres
;
$fres
= {
words
=>
$compres
,
esc_mode
=>
'shellvar'
};
goto
RETURN_RES;
}
}
if
(
$word
=~ m!\A~([^/]*)\z! &&
$COMPLETE_GETOPT_LONG_DEFAULT_FILE
) {
log_trace(
"[compgl] completing userdir, user=%s"
, $1)
if
$COMPLETE_GETOPT_LONG_TRACE
;
{
last
if
$@;
my
$res
= Unix::Passwd::File::list_users(
detail
=>1);
last
unless
$res
->[0] == 200;
my
$compres
= Complete::Util::complete_array_elem(
array
=>[
map
{
"~"
.
$_
->{user} . ((-d
$_
->{home}) ?
"/"
:
""
)}
@{
$res
->[2] }],
word
=>
$word
,
);
last
unless
@$compres
;
$fres
= {
words
=>
$compres
,
path_sep
=>
'/'
};
goto
RETURN_RES;
}
}
if
(
$word
=~ m!\A(~[^/]*)/! &&
$COMPLETE_GETOPT_LONG_DEFAULT_FILE
) {
log_trace(
"[compgl] completing file, path=<%s>"
,
$word
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
$fres
= Complete::Util::hashify_answer(
Complete::File::complete_file(
word
=>
$word
),
{
path_sep
=>
'/'
}
);
goto
RETURN_RES;
}
if
(String::Wildcard::Bash::contains_wildcard(
$word
) &&
$COMPLETE_GETOPT_LONG_DEFAULT_FILE
) {
log_trace(
"[compgl] completing with wildcard glob, glob=<%s>"
,
"$word*"
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
{
my
$compres
= [
glob
(
"$word*"
)];
last
unless
@$compres
;
for
(
@$compres
) {
$_
.=
"/"
if
(-d
$_
);
}
$fres
= {
words
=>
$compres
,
path_sep
=>
'/'
};
goto
RETURN_RES;
}
}
if
(
$COMPLETE_GETOPT_LONG_DEFAULT_FILE
) {
log_trace(
"[compgl] completing with file, file=<%s>"
,
$word
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
$fres
= Complete::Util::hashify_answer(
Complete::File::complete_file(
word
=>
$word
),
{
path_sep
=>
'/'
}
);
}
RETURN_RES:
log_trace(
"[compgl] leaving default completion routine, result=%s"
,
$fres
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
$fres
;
}
sub
_matching_opts {
my
(
$opt
,
$opts
) =
@_
;
my
%candidates
;
for
(
sort
{
length
(
$a
)<=>
length
(
$b
)}
keys
%$opts
) {
next
unless
index
(
$_
,
$opt
) == 0;
$candidates
{
$_
} =
$opts
->{
$_
};
last
if
$opt
eq
$_
;
}
\
%candidates
;
}
sub
_mark_seen {
my
(
$seen_opts
,
$opt
,
$opts
) =
@_
;
my
$opthash
=
$opts
->{
$opt
};
return
unless
$opthash
;
my
$ospec
=
$opthash
->{ospec};
for
(
keys
%$opts
) {
my
$v
=
$opts
->{
$_
};
$seen_opts
->{
$_
}++
if
$v
->{ospec} eq
$ospec
;
}
}
$SPEC
{complete_cli_arg} = {
v
=> 1.1,
summary
=>
'Complete command-line argument using '
.
'Getopt::Long specification'
,
description
=>
<<'_',
This routine can complete option names, where the option names are retrieved
from <pm:Getopt::Long> specification. If you provide completion routine in
`completion`, you can also complete _option values_ and _arguments_.
Note that this routine does not use <pm:Getopt::Long> (it does its own parsing)
and currently is not affected by Getopt::Long's configuration. Its behavior
mimics Getopt::Long under these configuration: `no_ignore_case`, `bundling` (or
`no_bundling` if the `bundling` option is turned off). Which I think is the
sensible default. This routine also does not currently support `auto_help` and
`auto_version`, so you'll need to add those options specifically if you want to
recognize `--help/-?` and `--version`, respectively.
_
args
=> {
getopt_spec
=> {
summary
=>
'Getopt::Long specification'
,
schema
=>
'array*'
,
req
=> 1,
},
completion
=> {
summary
=>
'Completion routine to complete option value/argument'
,
schema
=>
'code*'
,
description
=>
<<'_',
Completion code will receive a hash of arguments (`%args`) containing these
keys:
* `type` (str, what is being completed, either `optval`, or `arg`)
* `word` (str, word to be completed)
* `cword` (int, position of words in the words array, starts from 0)
* `opt` (str, option name, e.g. `--str`; undef if we're completing argument)
* `ospec` (str, Getopt::Long option spec, e.g. `str|S=s`; undef when completing
argument)
* `argpos` (int, argument position, zero-based; undef if type='optval')
* `nth` (int, the number of times this option has seen before, starts from 0
that means this is the first time this option has been seen; undef when
type='arg')
* `seen_opts` (hash, all the options seen in `words`)
* `parsed_opts` (hash, options parsed the standard/raw way)
as well as all keys from `extras` (but these won't override the above keys).
and is expected to return a completion answer structure as described in
`Complete` which is either a hash or an array. The simplest form of answer is
just to return an array of strings. The various `complete_*` function like those
in <pm:Complete::Util> or the other `Complete::*` modules are suitable to use
here.
Completion routine can also return undef to express declination, in which case
the default completion routine will then be consulted. The default routine
completes from shell environment variables (`$FOO`), Unix usernames (`~foo`),
and files/directories.
Example:
use Complete::Unix qw(complete_user);
use Complete::Util qw(complete_array_elem);
complete_cli_arg(
getopt_spec => [
'help|h' => sub{...},
'format=s' => \$format,
'user=s' => \$user,
],
completion => sub {
my %args = @_;
my $word = $args{word};
my $ospec = $args{ospec};
if ($ospec && $ospec eq 'format=s') {
complete_array_elem(array=>[qw/json text xml yaml/], word=>$word);
} else {
complete_user(word=>$word);
}
},
);
_
},
words
=> {
summary
=>
'Command line arguments, like @ARGV'
,
description
=>
<<'_',
See function `parse_cmdline` in <pm:Complete::Bash> on how to produce this (if
you're using bash).
_
schema
=>
'array*'
,
req
=> 1,
},
cword
=> {
summary
=>
"Index in words of the word we're trying to complete"
,
description
=>
<<'_',
See function `parse_cmdline` in <pm:Complete::Bash> on how to produce this (if
you're using bash).
_
schema
=>
'int*'
,
req
=> 1,
},
extras
=> {
summary
=>
'Add extra arguments to completion routine'
,
schema
=>
'hash'
,
description
=>
<<'_',
The keys from this `extras` hash will be merged into the final `%args` passed to
completion routines. Note that standard keys like `type`, `word`, and so on as
described in the function description will not be overwritten by this.
_
},
bundling
=> {
schema
=>
'bool*'
,
default
=> 1,
'summary.alt.bool.not'
=>
'Turn off bundling'
,
description
=>
<<'_',
If you turn off bundling, completion of short-letter options won't support
bundling (e.g. `-b<tab>` won't add more single-letter options), but single-dash
multiletter options can be recognized. Currently only those specified with a
single dash will be completed. For example if you have `-foo=s` in your option
specification, `-f<tab>` can complete it.
This can be used to complete old-style programs, e.g. emacs which has options
like `-nw`, `-nbc` etc (but also have double-dash options like
`--no-window-system` or `--no-blinking-cursor`).
_
},
},
result_naked
=> 1,
result
=> {
schema
=> [
'any*'
=>
of
=> [
'hash*'
,
'array*'
]],
description
=>
<<'_',
You can use `format_completion` function in <pm:Complete::Bash> module to format
the result of this function for bash.
_
},
};
sub
complete_cli_arg {
my
%args
=
@_
;
my
$fname
= __PACKAGE__ .
"::complete_cli_arg"
;
my
$fres
;
$args
{words} or
die
"Please specify words"
;
my
@words
= @{
$args
{words} };
defined
(
my
$cword
=
$args
{cword}) or
die
"Please specify cword"
;
my
$gospec
=
$args
{getopt_spec} or
die
"Please specify getopt_spec"
;
my
$comp
=
$args
{completion};
my
$extras
=
$args
{extras} // {};
my
$bundling
=
$args
{bundling} // 1;
my
%parsed_opts
;
if
(
ref
$gospec
eq
'HASH'
) {
my
$ary_gospec
= [];
for
(
keys
%$gospec
) {
push
@$ary_gospec
,
$_
;
push
@$ary_gospec
,
$gospec
->{
$_
}
if
ref
$gospec
->{
$_
};
}
$gospec
=
$ary_gospec
;
}
log_trace(
'[compgl] entering %s(), words=%s, cword=%d, word=<%s>'
,
$fname
, \
@words
,
$cword
,
$words
[
$cword
])
if
$COMPLETE_GETOPT_LONG_TRACE
;
shift
@$gospec
if
ref
$gospec
->[0] eq
'HASH'
;
my
%opts
;
my
$i
= -1;
while
(++
$i
<= $
my
$ospec
=
$gospec
->[
$i
];
my
$dest
=
$i
+1 <= $
splice
(
@$gospec
,
$i
+1, 1) :
undef
;
my
$res
= Getopt::Long::Util::parse_getopt_long_opt_spec(
$ospec
)
or
die
"Can't parse option spec '$ospec'"
;
next
if
$res
->{is_arg};
$res
->{min_vals} //=
$res
->{type} ? 1 : 0;
$res
->{max_vals} //=
$res
->{type} ||
$res
->{opttype} ? 1:0;
for
my
$o0
(@{
$res
->{opts} }) {
my
@ary
=
$res
->{is_neg} &&
length
(
$o0
) > 1 ?
([
$o0
, 0], [
"no$o0"
,1], [
"no-$o0"
,1]) : ([
$o0
,0]);
for
my
$elem
(
@ary
) {
my
$o
=
$elem
->[0];
my
$is_neg
=
$elem
->[1];
my
$k
=
length
(
$o
)==1 ||
(!
$bundling
&&
$res
->{dash_prefix} eq
'-'
) ?
"-$o"
:
"--$o"
;
$opts
{
$k
} = {
name
=>
$k
,
ospec
=>
$ospec
,
dest
=>
$dest
,
parsed
=>
$res
,
is_neg
=>
$is_neg
,
};
}
}
}
my
@optnames
=
sort
keys
%opts
;
my
$code_get_summary
=
sub
{
return
unless
$extras
;
my
$ggls_res
=
$extras
->{ggls_res};
return
unless
$ggls_res
;
my
$r
=
$extras
->{r};
return
unless
$r
;
my
$cmdline
=
$extras
->{cmdline};
my
$optname
=
shift
;
my
$ospec
=
$opts
{
$optname
}{ospec};
return
unless
$ospec
;
my
$specmeta
=
$ggls_res
->[3]{
'func.specmeta'
};
my
$ospecmeta
=
$specmeta
->{
$ospec
};
return
$ospecmeta
->{summary}
if
defined
$ospecmeta
->{summary};
if
(
$ospecmeta
->{is_alias}) {
my
$real_ospecmeta
=
$specmeta
->{
$ospecmeta
->{alias_for} };
my
$real_opt
=
$real_ospecmeta
->{parsed}{opts}[0];
$real_opt
=
length
(
$real_opt
) == 1 ?
"-$real_opt"
:
"--$real_opt"
;
return
"Alias for $real_opt"
;
}
if
(
defined
(
my
$coptname
=
$ospecmeta
->{common_opt})) {
my
$coptspec
=
$cmdline
?
$cmdline
->{common_opts}{
$coptname
} :
$r
->{common_opts} ?
$r
->{common_opts}{
$coptname
} :
undef
;
return
unless
$coptspec
;
my
$summ
;
if
(
$opts
{
$optname
}{is_neg}) {
$summ
=
$coptspec
->{
"summary.alt.bool.not"
};
return
$summ
if
defined
$summ
;
my
$pos_opt
=
$ospecmeta
->{pos_opts}[0];
$pos_opt
=
length
(
$pos_opt
) == 1 ?
"-$pos_opt"
:
"--$pos_opt"
;
return
"The opposite of $pos_opt"
;
}
else
{
$summ
=
$coptspec
->{
"summary.alt.bool.yes"
};
return
$summ
if
defined
$summ
;
$summ
=
$coptspec
->{
"summary"
};
return
$summ
if
defined
$summ
;
}
}
else
{
my
$arg
=
$ospecmeta
->{arg};
my
$argspec
=
$extras
->{r}{meta}{args}{
$arg
};
my
$summ
;
if
(
$ospecmeta
->{is_neg}) {
$summ
=
$argspec
->{
"summary.alt.bool.not"
};
return
$summ
if
defined
$summ
;
my
$pos_opt
=
$ospecmeta
->{pos_opts}[0];
$pos_opt
=
length
(
$pos_opt
) == 1 ?
"-$pos_opt"
:
"--$pos_opt"
;
return
"The opposite of $pos_opt"
;
}
else
{
$summ
=
$argspec
->{
"summary.alt.bool.yes"
};
return
$summ
if
defined
$summ
;
$summ
=
$argspec
->{
"summary"
};
return
$summ
if
defined
$summ
;
}
}
return
;
};
my
%seen_opts
;
my
@expects
;
$i
= -1;
my
$argpos
= 0;
WORD:
while
(1) {
last
WORD
if
++
$i
>=
@words
;
my
$word
=
$words
[
$i
];
if
(
$word
eq
'--'
&&
$i
!=
$cword
) {
$expects
[
$i
] = {
separator
=>1};
while
(1) {
$i
++;
last
WORD
if
$i
>=
@words
;
$expects
[
$i
] = {
arg
=>1,
argpos
=>
$argpos
++};
}
}
if
(
$word
=~ /\A-/) {
SHORT_OPTS:
{
last
unless
$opts
{
"-"
.
substr
(
$word
,1,1)};
last
unless
$bundling
;
my
$j
=
$i
;
my
$rest
=
substr
(
$word
, 1);
my
@inswords
;
my
$encounter_equal_sign
;
EXPAND:
while
(1) {
$rest
=~ s/(.)// or
last
;
my
$opt
=
"-$1"
;
my
$opthash
=
$opts
{
$opt
};
unless
(
$opthash
) {
@inswords
= ();
$expects
[
$i
]{short_only} = 0;
$rest
=
$word
;
last
EXPAND;
}
if
(
$opthash
->{parsed}{max_vals}) {
_mark_seen(\
%seen_opts
,
$opt
, \
%opts
);
if
(
$i
==
$j
) {
$words
[
$i
] =
$opt
;
}
else
{
push
@inswords
,
$opt
;
$j
++;
}
my
$expand
;
if
(
length
$rest
) {
$expand
++;
$expects
[
$j
>
$i
?
$j
+1 :
$j
+2]{do_complete_optname} = 0;
$expects
[
$j
>
$i
?
$j
+1 :
$j
+2]{optval} =
$opt
;
}
else
{
$expects
[
$j
>
$i
?
$j
-1 :
$j
]{optname} =
$opt
;
$expects
[
$j
>
$i
?
$j
-1 :
$j
]{comp_result} = [
substr
(
$word
, 0,
length
(
$word
)-
length
(
$rest
))];
}
if
(
$rest
=~ s/\A=//) {
$encounter_equal_sign
++;
}
if
(
$expand
) {
push
@inswords
,
"="
,
$rest
;
$j
+=2;
}
last
EXPAND;
}
_mark_seen(\
%seen_opts
,
$opt
, \
%opts
);
if
(
$i
==
$j
) {
$words
[
$i
] =
$opt
;
}
else
{
push
@inswords
,
$opt
;
}
$j
++;
}
my
$prefix
=
$encounter_equal_sign
?
''
:
substr
(
$word
, 0,
length
(
$word
)-
length
(
$rest
));
splice
@words
,
$i
+1, 0,
@inswords
;
for
(0..
@inswords
) {
$expects
[
$i
+
$_
]{prefix} =
$prefix
;
$expects
[
$i
+
$_
]{word} =
$rest
;
}
$cword
+=
@inswords
;
$i
+=
@inswords
;
$word
=
$words
[
$i
];
$expects
[
$i
]{short_only} //= 1;
}
SPLIT_EQUAL:
{
if
(
$word
=~ /\A(--?[^=]+)(=)(.*)/) {
splice
@words
,
$i
, 1, $1, $2, $3;
$word
= $1;
$cword
+= 2
if
$cword
>=
$i
;
}
}
my
$opt
=
$word
;
my
$matching_opts
= _matching_opts(
$opt
, \
%opts
);
if
(
keys
(
%$matching_opts
) == 1) {
my
$opthash
=
$matching_opts
->{ (
keys
%$matching_opts
)[0] };
$opt
=
$opthash
->{name};
$expects
[
$i
]{optname} =
$opt
;
my
$nth
=
$seen_opts
{
$opt
} // 0;
$expects
[
$i
]{nth} =
$nth
;
_mark_seen(\
%seen_opts
,
$opt
, \
%opts
);
my
$min_vals
=
$opthash
->{parsed}{min_vals};
my
$max_vals
=
$opthash
->{parsed}{max_vals};
if
(
$i
+1 <
@words
&&
$words
[
$i
+1] eq
'='
) {
$i
++;
$expects
[
$i
] = {
separator
=>1,
optval
=>
$opt
,
word
=>
''
,
nth
=>
$nth
};
$min_vals
= 1;
$max_vals
=
$min_vals
if
$max_vals
<
$min_vals
;
}
for
(1 ..
$min_vals
) {
$i
++;
last
WORD
if
$i
>=
@words
;
$expects
[
$i
]{optval} =
$opt
;
$expects
[
$i
]{nth} =
$nth
;
push
@{
$parsed_opts
{
$opt
} },
$words
[
$i
];
}
for
(1 ..
$max_vals
-
$min_vals
) {
last
if
$i
+
$_
>=
@words
;
last
if
$words
[
$i
+
$_
] =~ /\A-/;
$expects
[
$i
+
$_
]{optval} =
$opt
;
$expects
[
$i
]{nth} =
$nth
;
push
@{
$parsed_opts
{
$opt
} },
$words
[
$i
+
$_
];
}
}
else
{
$opt
=
undef
;
$expects
[
$i
]{optname} =
$opt
;
my
$possible_optnames
= [
sort
keys
%$matching_opts
];
$expects
[
$i
]{possible_optnames} =
$possible_optnames
;
if
(
$i
+1 <
@words
&&
$words
[
$i
+1] eq
'='
) {
$i
++;
$expects
[
$i
] = {
separator
=>1,
optval
=>
undef
,
possible_optnames
=>
$possible_optnames
,
word
=>
''
};
if
(
$i
+1 <
@words
) {
$i
++;
$expects
[
$i
]{optval} =
$opt
;
$expects
[
$i
]{possible_optnames} =
$possible_optnames
;
}
}
}
}
else
{
$expects
[
$i
]{optname} =
''
;
$expects
[
$i
]{arg} = 1;
$expects
[
$i
]{argpos} =
$argpos
++;
}
}
my
$exp
=
$expects
[
$cword
];
my
$word
=
$exp
->{word} //
$words
[
$cword
];
my
@answers
;
{
last
if
$word
=~ /\A[^-]/;
last
unless
exists
$exp
->{optname};
last
if
defined
(
$exp
->{do_complete_optname}) &&
!
$exp
->{do_complete_optname};
if
(
$exp
->{comp_result}) {
push
@answers
,
$exp
->{comp_result};
last
;
}
my
$opt
=
$exp
->{optname};
my
@o
;
my
@osumms
;
my
$o_has_summaries
;
for
my
$optname
(
@optnames
) {
my
$repeatable
= 0;
next
if
$exp
->{short_only} &&
$optname
=~ /\A--/;
if
(
$seen_opts
{
$optname
}) {
my
$opthash
=
$opts
{
$optname
};
my
$parsed
=
$opthash
->{parsed};
my
$dest
=
$opthash
->{dest};
if
(
ref
$dest
eq
'ARRAY'
) {
$repeatable
= 1;
}
elsif
(
$parsed
->{desttype} ||
$parsed
->{is_inc}) {
$repeatable
= 1;
}
}
next
if
$seen_opts
{
$optname
} && !
$repeatable
&& (
(!
$opt
||
$opt
ne
$optname
) ||
(
defined
(
$exp
->{prefix}) &&
index
(
$exp
->{prefix},
substr
(
$opt
, 1, 1)) >= 0));
if
(
defined
$exp
->{prefix}) {
my
$o
=
$optname
;
$o
=~ s/\A-//;
push
@o
,
"$exp->{prefix}$o"
;
}
else
{
push
@o
,
$optname
;
}
my
$summ
=
$code_get_summary
->(
$optname
) //
''
;
if
(
length
$summ
) {
$o_has_summaries
= 1;
push
@osumms
,
$summ
;
}
else
{
push
@osumms
,
''
;
}
}
my
$compres
= Complete::Util::complete_array_elem(
array
=> \
@o
,
word
=>
$word
,
(
summaries
=> \
@osumms
) x !!
$o_has_summaries
,
);
log_trace(
'[compgl] adding result from option names, '
.
'matching options=%s'
,
$compres
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
push
@answers
,
$compres
;
if
(!
exists
(
$exp
->{optval}) && !
exists
(
$exp
->{arg})) {
$fres
= {
words
=>
$compres
,
esc_mode
=>
'option'
};
goto
RETURN_RES;
}
}
{
last
unless
exists
(
$exp
->{optval});
my
$opt
=
$exp
->{optval};
my
$opthash
;
$opthash
=
$opts
{
$opt
}
if
$opt
;
my
%compargs
= (
%$extras
,
type
=>
'optval'
,
words
=>\
@words
,
cword
=>
$args
{cword},
word
=>
$word
,
opt
=>(
$opt
//
$exp
->{possible_optnames}),
ospec
=>
$opthash
->{ospec},
argpos
=>
undef
,
nth
=>
$exp
->{nth},
seen_opts
=>\
%seen_opts
,
parsed_opts
=>\
%parsed_opts
,
);
my
$compres
;
if
(
$comp
) {
log_trace(
"[compgl] invoking routine supplied from 'completion' argument to complete option value, option=<%s>"
,
$opt
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
$compres
=
$comp
->(
%compargs
);
Complete::Util::modify_answer(
answer
=>
$compres
,
prefix
=>
$exp
->{prefix})
if
defined
$exp
->{prefix};
log_trace(
'[compgl] adding result from routine: %s'
,
$compres
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
}
if
(!
$compres
|| !
$comp
) {
$compres
= _default_completion(
%compargs
);
Complete::Util::modify_answer(
answer
=>
$compres
,
prefix
=>
$exp
->{prefix})
if
defined
$exp
->{prefix};
log_trace(
'[compgl] adding result from default '
.
'completion routine'
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
}
push
@answers
,
$compres
;
}
{
last
unless
exists
(
$exp
->{arg});
my
%compargs
= (
%$extras
,
type
=>
'arg'
,
words
=>\
@words
,
cword
=>
$args
{cword},
word
=>
$word
,
opt
=>
undef
,
ospec
=>
undef
,
argpos
=>
$exp
->{argpos},
seen_opts
=>\
%seen_opts
,
parsed_opts
=>\
%parsed_opts
,
);
log_trace(
'[compgl] invoking \'completion\' routine '
.
'to complete argument'
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
my
$compres
;
$compres
=
$comp
->(
%compargs
)
if
$comp
;
if
(!
defined
$compres
) {
$compres
= _default_completion(
%compargs
);
log_trace(
'[compgl] adding result from default '
.
'completion routine: %s'
,
$compres
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
}
push
@answers
,
$compres
;
}
log_trace(
"[compgl] combining result from %d source(s)"
,
scalar
@answers
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
$fres
= Complete::Util::combine_answers(
@answers
) // [];
RETURN_RES:
log_trace(
"[compgl] leaving %s(), result=%s"
,
$fname
,
$fres
)
if
$COMPLETE_GETOPT_LONG_TRACE
;
$fres
;
}
1;