Perinci-CmdLine-Classic
view release on metacpan or search on metacpan
lib/Perinci/CmdLine/Classic/Role/Help.pm view on Meta::CPAN
sub _help_add_heading {
my ($self, $r, $heading) = @_;
$self->_help_add_row($r, [$self->_color('heading', $heading)]);
}
sub _color {
my ($self, $item_name, $text) = @_;
my $color_code = $item_name ?
$self->{color_theme_obj}->get_item_color_as_ansi($item_name) : "";
my $reset_code = $color_code ? "\e[0m" : "";
"$color_code$text$reset_code";
}
sub help_section_summary {
my ($self, $r) = @_;
my $summary = rimeta($r->{_help_meta})->langprop("summary");
return unless $summary;
my $name = $self->get_program_and_subcommand_name($r);
my $ct = join(
"",
$self->_color('program_name', $name),
($name && $summary ? ' - ' : ''),
$summary // "",
);
$self->_help_add_row($r, [$ct], {wrap=>1});
}
sub help_section_usage {
my ($self, $r) = @_;
my $co = $self->common_opts;
my @con = grep {
my $cov = $co->{$_};
my $show = $cov->{show_in_usage} // 1;
for ($show) { if (ref($_) eq 'CODE') { $_ = $_->($self, $r) } }
$show;
} sort {
($co->{$a}{order}//1) <=> ($co->{$b}{order}//1) || $a cmp $b
} keys %$co;
my $pn = $self->_color(
'program_name', $self->get_program_and_subcommand_name($r));
my $ct = "";
for my $con (@con) {
my $cov = $co->{$con};
next unless $cov->{usage};
$ct .= ($ct ? "\n" : "") . $pn . " " . __($cov->{usage});
}
if ($self->subcommands && !$r->{subcommand_name}) {
if (defined $self->default_subcommand) {
$ct .= ($ct ? "\n" : "") . $pn .
" " . __("--cmd=<other-subcommand> [options]");
} else {
$ct .= ($ct ? "\n" : "") . $pn .
" " . __("<subcommand> [options]");
}
} else {
my $usage = $r->{_help_clidocdata}{usage_line};
$usage =~ s/\[\[prog\]\]/$pn/;
$usage =~ s/\[options\]/__("[options]")/e;
$ct .= ($ct ? "\n" : "") . $usage;
}
$self->_help_add_heading($r, __("Usage"));
$self->_help_add_row($r, [$ct], {indent=>1});
}
sub help_section_options {
my ($self, $r) = @_;
my $opts = $r->{_help_clidocdata}{opts};
return unless keys %$opts;
my $verbose = $r->{_help_verbose};
my $info = $r->{_help_info};
my $meta = $r->{_help_meta};
my $args_p = $meta->{args};
my $sc = $self->subcommands;
# group options by raw category, e.g. $cats{""} (for options
# without category and common options) or $cat{"cat1"}.
my %cats; # val = [ospec, ...]
for (keys %$opts) {
push @{ $cats{$opts->{$_}{raw_category} // ''} }, $_;
}
for my $cat (sort keys %cats) {
# find the longest option
my @opts = sort {length($b)<=>length($a)} @{ $cats{$cat} };
my $len = length($opts[0]);
# sort again by name
@opts = sort {
(my $a_without_dash = $a) =~ s/^-+//;
(my $b_without_dash = $b) =~ s/^-+//;
lc($a) cmp lc($b);
} @opts;
my $cat_title;
if ($cat eq '') {
$cat_title = __("Options");
} else {
$cat_title = __x("{category} options", category=>ucfirst($cat));
}
$self->_help_add_heading($r, $cat_title);
if ($verbose) {
for my $opt_name (@opts) {
my $opt_spec = $opts->{$opt_name};
my $arg_spec = $opt_spec->{arg_spec};
my $ct = $self->_color('option_name', $opt_name);
# BEGIN DUPE1
if ($arg_spec && !$opt_spec->{main_opt} &&
defined($arg_spec->{pos})) {
if ($arg_spec->{slurpy} // $arg_spec->{greedy}) {
$ct .= " (=arg[$arg_spec->{pos}-])";
} else {
$ct .= " (=arg[$arg_spec->{pos}])";
}
}
if ($arg_spec && !$opt_spec->{main_opt} &&
defined($arg_spec->{cmdline_src})) {
$ct .= " (or from $arg_spec->{cmdline_src})";
$ct =~ s!_or_!/!;
}
# END DUPE1
$self->_help_add_row($r, [$ct], {indent=>1});
if ($opt_spec->{summary} || $opt_spec->{description}) {
my $ct = "";
$ct .= ($ct ? "\n\n":"")."$opt_spec->{summary}."
lib/Perinci/CmdLine/Classic/Role/Help.pm view on Meta::CPAN
for (@shown_scs) {
my $summary = rimeta($_)->langprop("summary");
$self->_help_add_row(
$r,
[$self->_color('program_name', $_->{name}), $summary],
{column_widths=>[-17, -40], indent=>1});
}
} else {
# for compactness, display in columns
my $tw = $self->term_width;
my $columns = int($tw/25); $columns = 1 if $columns < 1;
while (1) {
my @row;
for (1..$columns) {
last unless @shown_scs;
my $sc = shift @shown_scs;
push @row, $sc->{name};
}
last unless @row;
for (@row+1 .. $columns) { push @row, "" }
$self->_help_add_row($r, \@row, {indent=>1});
}
}
}
sub help_section_hints {
my ($self, $r) = @_;
my $verbose = $r->{_help_verbose};
my @hints;
unless ($verbose) {
push @hints, N__("For more complete help, use '--help --verbose'");
}
if ($r->{_help_hide_some_subcommands}) {
push @hints,
N__("To see all available subcommands, use '--subcommands'");
}
return unless @hints;
$self->_help_add_row(
$r, [join(" ", map { __($_)."." } @hints)], {wrap=>1});
}
sub help_section_description {
my ($self, $r) = @_;
my $desc = rimeta($r->{_help_meta})->langprop("description") //
$self->description;
return unless $desc;
$self->_help_add_heading($r, __("Description"));
$self->_help_add_row($r, [$desc], {wrap=>1, indent=>1});
}
sub help_section_examples {
my ($self, $r) = @_;
my $verbose = $r->{_help_verbose};
my $meta = $r->{_help_meta};
my $egs = $r->{_help_clidocdata}{examples};
return unless $egs && @$egs;
$self->_help_add_heading($r, __("Examples"));
my $pn = $self->_color(
'program_name', $self->get_program_and_subcommand_name($r));
for my $eg (@$egs) {
my $cmdline = $eg->{cmdline};
$cmdline =~ s/\[\[prog\]\]/$pn/;
$self->_help_add_row($r, ["% $cmdline"], {indent=>1});
if ($verbose) {
my $ct = "";
if ($eg->{summary}) { $ct .= "$eg->{summary}." }
if ($eg->{description}) { $ct .= "\n\n$eg->{description}" }
$self->_help_add_row($r, [$ct], {indent=>2}) if $ct;
}
}
}
sub help_section_result {
my ($self, $r) = @_;
my $meta = $r->{_help_meta};
my $rmeta = $meta->{result};
my $rmetao = rimeta($rmeta);
my $text;
my $summary = $rmetao->langprop('summary') // '';
my $desc = $rmetao->langprop('description') // '';
$text = $summary . ($summary ? "\n\n" : "") . $desc;
# collect handler
my %handlers;
for my $k0 (keys %$rmeta) {
my $v = $rmeta->{$k0};
my $k = $k0; $k =~ s/\..+//;
next if $k =~ /\A_/;
# check builtin result spec key
next if $k =~ /\A(
summary|description|tags|default_lang|
schema|
x
)\z/x;
# try a property module first
require "Perinci/Sub/Property/result/$k.pm";
my $meth = "help_hookmeta_result__$k";
unless ($self->can($meth)) {
die "No help handler for property result/$k0 ($meth)";
}
my $hmeta = $self->$meth;
$handlers{$k} = {
prio => $hmeta->{prio},
meth => "help_hook_result__$k",
};
}
# call all the handlers in order
for my $k (sort {$handlers{$a}{prio} <=> $handlers{$b}{prio}}
keys %handlers) {
my $h = $handlers{$k};
my $meth = $h->{meth};
my $t = $self->$meth($r);
$text .= $t if $t;
}
return unless length $text;
$self->_help_add_heading($r, __("Result"));
$self->_help_add_row($r, [$text], {wrap=>1, indent=>1});
}
sub help_section_links {
# not yet
}
sub action_help {
my ($self, $r) = @_;
$r->{_help_buf} = '';
my $verbose = $ENV{VERBOSE} // 0;
local $r->{_help_verbose} = $verbose;
# get function metadata first
unless ($r->{_help_meta}) {
my $url = $r->{subcommand_data}{url} // $self->url;
my $res = $self->riap_client->request(info => $url);
die [500, "Can't info '$url': $res->[0] - $res->[1]"]
unless $res->[0] == 200;
$r->{_help_info} = $res->[2];
$res = $self->riap_client->request(meta => $url);
die [500, "Can't meta '$url': $res->[0] - $res->[1]"]
unless $res->[0] == 200;
$r->{_help_meta} = $res->[2]; # cache here
}
# get cli opt spec
unless ($r->{_help_clidocdata}) {
require Perinci::Sub::To::CLIDocData;
my $res = Perinci::Sub::To::CLIDocData::gen_cli_doc_data_from_meta(
meta => $r->{_help_meta}, meta_is_normalized => 1,
common_opts => $self->common_opts,
per_arg_json => $self->per_arg_json,
per_arg_yaml => $self->per_arg_yaml,
);
die [500, "Can't gen_cli_doc_data_from_meta: $res->[0] - $res->[1]"]
unless $res->[0] == 200;
$r->{_help_clidocdata} = $res->[2]; # cache here
}
# ux: since --verbose will potentially show lots of paragraph text, let's
# default to 80 and not wider width, unless user specifically requests
# column width via COLUMNS.
if ($verbose && !defined($ENV{COLUMNS}) && $self->term_width > 80) {
$self->term_width(80);
}
# determine which help sections should we generate
my @hsects;
if ($verbose) {
@hsects = (
'summary',
'usage',
'subcommands',
'examples',
'description',
'options',
'result',
'links',
'hints',
);
} else {
@hsects = (
'summary',
'usage',
'subcommands',
'examples',
'options',
'hints',
);
}
for my $s (@hsects) {
my $meth = "help_section_$s";
#say "D:$meth";
#$log->tracef("=> $meth()");
$self->$meth($r);
}
$self->_help_draw_curtbl($r);
[200, "OK", $r->{_help_buf}, {"cmdline.skip_format"=>1}];
}
1;
# ABSTRACT: Help-related routines
__END__
=pod
=encoding UTF-8
=head1 NAME
Perinci::CmdLine::Classic::Role::Help - Help-related routines
=head1 VERSION
This document describes version 1.818 of Perinci::CmdLine::Classic::Role::Help (from Perl distribution Perinci-CmdLine-Classic), released on 2023-10-30.
( run in 3.457 seconds using v1.01-cache-2.11-cpan-97f6503c9c8 )