Class-Usul
view release on metacpan or search on metacpan
lib/Class/Usul/Getopt/Usage.pm view on Meta::CPAN
length $desc <= $max_length and return $desc; my @lines;
while (length $desc > $max_length) {
my $idx = rindex( substr( $desc, 0, $max_length ), $SPC );
$idx >= 0 or last;
push @lines, substr $desc, 0, $idx; substr( $desc, 0, 1 + $idx ) = $NUL;
}
push @lines, $desc;
return @lines;
};
my $_types = sub {
my $k = shift; my $option_type = $USAGE_CONF->{option_type} // 'short';
$option_type eq 'none' and return; # Old behaviour
$option_type eq 'verbose' and return uc $k; # New behaviour
my $types = $USAGE_CONF->{type_map}
// { int => 'i', key => 'k', num => 'n', str => 's', };
my $type = $types->{ $k } // $NUL; # Prefered behaviour
return $type;
};
my $_parse_assignment = sub {
my $assign_spec = shift; $assign_spec or return $NUL;
length $assign_spec < 2 and return $NUL; # Empty, ! or +
my $argument = substr $assign_spec, 1, 2;
my $result = $_types->( 'str' );
if ($argument eq 'i' or $argument eq 'o') { $result = $_types->( 'int' ) }
elsif ($argument eq 'f') { $result = $_types->( 'num' ) }
if (length $assign_spec > 2) {
my $desttype = substr $assign_spec, 2, 1;
# Imply it can be repeated
if ($desttype eq '@') { $result .= '...' }
elsif ($desttype eq '%') {
$result = $result ? $_types->( 'key' )."=${result}..." : $NUL;
}
}
substr $assign_spec, 0, 1 eq ':' and return "[=${result}]";
# With leading space so it can just blindly be appended.
return $result ? " $result" : $NUL;
};
my $_assemble_spec = sub {
my ($length, $spec) = @_;
my $stripped = [ Getopt::Long::Descriptive->_strip_assignment( $spec ) ];
my $assign = $_parse_assignment->( $stripped->[ 1 ] );
my $plain = join $SPC, reverse
map { length > 1 ? "--${_}${assign}" : "-${_}${assign}" }
split m{ [|] }mx, $stripped->[ 0 ];
my $pad = $SPC x ($length - length $plain);
my $highlight = $USAGE_CONF->{highlight} // 'bold';
$highlight eq 'none' and return $plain.$pad; # Old behaviour
$assign = color( $highlight ).$assign.color( 'reset' );
my $markedup = join $SPC, reverse
map { length > 1 ? "--${_}${assign}" : "-${_}${assign}" }
split m{ [|] }mx, $stripped->[ 0 ];
return $markedup.$pad; # Prefered behaviour works well with short types
};
my $_option_length = sub {
my $fullspec = shift;
my $number_opts = 1;
my $last_pos = 0;
my $number_shortopts = 0;
my ($spec, $assign)
= Getopt::Long::Descriptive->_strip_assignment( $fullspec );
my $length = length $spec;
my $arglen = length $_parse_assignment->( $assign );
# Spacing rules:
# For short options we want 1 space (for '-'), for long options 2
# spaces (for '--'). Then one space for separating the options,
# but we here abuse that $spec has a '|' char for that.
# For options that take arguments, we want 2 spaces for mandatory
# options ('=X') and 4 for optional arguments ('[=X]'). Note we
# consider {N,M} cases as "single argument" atm.
# Count the number of "variants" (e.g. "long|s" has two variants)
while ($spec =~ m{ [|] }gmx) {
$number_opts++;
(pos( $spec ) - $last_pos) == 2 and $number_shortopts++;
$last_pos = pos( $spec );
}
# Was the last option a "short" one?
# Getopt::Long::Descriptive has a 2 here and thats wrong
($length - $last_pos) == 1 and $number_shortopts++;
# We got $number_opts options, each with an argument length of
# $arglen. Plus each option (after the first) needs 3 a char
# spacing. $length gives us the total length of all options and 1
# char spacing per option (after the first). It does not account
# for argument length and we want (at least) one additional char
# for space before the description. So the result should be:
my $number_longopts = $number_opts - $number_shortopts;
my $total_arglen = $number_opts * $arglen;
my $total_optsep = 2 * $number_longopts + $number_shortopts;
my $total = $length + $total_optsep + $total_arglen;
return $total;
};
# Public methods
sub option_text {
my $self = shift;
my @options = @{ $self->{options} // [] };
my @specs = map { $_->{spec} } grep { $_->{desc} ne 'spacer' } @options;
my $length = max( map { $_option_length->( $_ ) } @specs ) || 0;
my $tab = $SPC x $_tabstop->(); # Originally an actual tab char
my $spec_fmt = "${tab}%-${length}s";
my $string = $NUL;
while (defined (my $opt = shift @options)) {
my $spec = $opt->{spec}; my $desc = $opt->{desc};
if ($desc eq 'spacer') { $string .= sprintf "${spec_fmt}\n", $spec; next }
( run in 0.964 second using v1.01-cache-2.11-cpan-71847e10f99 )