## 2021 10 29.03
+ - A new flag -gal=s, --grep-alias-list=s, has been added as suggested in
+ git #77. This allows code blocks passed to list operator functions to
+ be formatted in the same way as a code block passed to grep, map, or sort.
+
- A new flag -xlp has been added which can be set to avoid most of the
limitations of the -lp flag regarding side comments, blank lines, and
code blocks. This is off by default to avoid changing existing coding,
Note that several other parameters accept a list of keywords, including 'sub' (see L<Specifying Block Types>).
You do not need to include any sub aliases in these lists. Just include keyword 'sub' if you wish, and all aliases are automatically included.
+=item B<-gal=s>, B<--grep-alias-list=s>
+
+This flag causes a code block following a specified word to be formatted as if it followed the keyword 'grep'. The string B<s> contains one or more such alias words, separated by spaces or commas.
+
+This allows code block arguments to external list operator functions to be formatted in the same way as code blocks which follow the perl builtin keywords 'grep', 'map', and 'sort'. Perltidy tries to keep code blocks for these functions intact, and does not automatically break after the closing brace since a list may follow.
+
+For example, the functions 'any' and 'all' in module List::Util can be given formatting like 'grep' with
+
+ perltidy -gal='any all'
+
=back
=head1 FORMATTING OPTIONS
$add_option->( 'assert-tidy', 'ast', '!' );
$add_option->( 'assert-untidy', 'asu', '!' );
$add_option->( 'sub-alias-list', 'sal', '=s' );
+ $add_option->( 'grep-alias-list', 'gal', '=s' );
########################################
$category = 2; # Code indentation control
$rOpts->{'sub-alias-list'} = join ' ', @filtered_word_list;
}
+ if ( $rOpts->{'grep-alias-list'} ) {
+ my $grep_alias_string = $rOpts->{'grep-alias-list'};
+ $grep_alias_string =~ s/,/ /g; # allow commas
+ $grep_alias_string =~ s/^\s+//;
+ $grep_alias_string =~ s/\s+$//;
+ my @grep_alias_list = split /\s+/, $grep_alias_string;
+ my @filtered_word_list;
+ my %seen;
+
+ foreach my $word (@grep_alias_list) {
+ if ($word) {
+ if ( $word !~ /^\w[\w\d]*$/ ) {
+ Warn("unexpected grep alias '$word' - ignoring\n");
+ }
+ if ( !$seen{$word} ) {
+ $seen{$word}++;
+ push @filtered_word_list, $word;
+ }
+ }
+ }
+ my $joined_words = join ' ', @filtered_word_list;
+ $rOpts->{'grep-alias-list'} = join ' ', @filtered_word_list;
+ }
+
# Turn on fuzzy-line-length unless this is an extrude run, as determined
# by the -i and -ci settings. Otherwise blinkers can form (case b935)
if ( !$rOpts->{'fuzzy-line-length'} ) {
%is_last_next_redo_return,
%is_sort_map_grep,
%is_sort_map_grep_eval,
+ %is_sort_map_grep_eval_do,
+ %block_type_map,
%is_if_unless,
%is_and_or,
%is_chain_operator,
@q = qw(sort map grep eval);
@is_sort_map_grep_eval{@q} = (1) x scalar(@q);
+ @q = qw(sort map grep eval do);
+ @is_sort_map_grep_eval_do{@q} = (1) x scalar(@q);
+
+ # Map related block names into a common name to allow vertical alignment
+ # used by sub make_alignment_patterns
+ %block_type_map = (
+ 'unless' => 'if',
+ 'else' => 'if',
+ 'elsif' => 'if',
+ 'when' => 'if',
+ 'default' => 'if',
+ 'case' => 'if',
+ 'sort' => 'map',
+ 'grep' => 'map',
+ );
+
@q = qw(if unless);
@is_if_unless{@q} = (1) x scalar(@q);
initialize_whitespace_hashes();
initialize_bond_strength_hashes();
+ install_grep_alias_list( $rOpts->{'grep-alias-list'} );
# Make needed regex patterns for matching text.
# NOTE: sub_matching_patterns must be made first because later patterns use
return;
}
+sub install_grep_alias_list {
+ my ($str) = @_;
+ return unless ($str);
+
+ # Note that any 'grep-alias-list' string has been preprocessed to be a
+ # trimmed, space-separated list.
+ my @q = split /\s+/, $str;
+ @{is_sort_map_grep}{@q} = (1) x scalar(@q);
+ @{is_sort_map_grep_eval}{@q} = (1) x scalar(@q);
+ @{is_sort_map_grep_eval_do}{@q} = (1) x scalar(@q);
+ @{is_block_with_ci}{@q} = (1) x scalar(@q);
+ @{is_keyword_returning_list}{@q} = (1) x scalar(@q);
+ foreach (@q) {
+ $block_type_map{$_} = 'map' unless ( $_ eq 'map' );
+ }
+}
+
sub initialize_weld_nested_exclusion_rules {
my ($rOpts) = @_;
%weld_nested_exclusion_rules = ();
BEGIN {
my @q;
- @q = qw(sort grep map);
- @is_sort_grep_map{@q} = (1) x scalar(@q);
@q = qw(for foreach);
@is_for_foreach{@q} = (1) x scalar(@q);
## || $typel eq 'Y'
# must have space between grep and left paren; "grep(" will fail
- || $is_sort_grep_map{$tokenl}
+ || $is_sort_map_grep{$tokenl}
# don't stick numbers next to left parens, as in:
#use Mail::Internet 1.28 (); (see Entity.pm, Head.pm, Test.pm)
return ( $rindentation_list->[ $nline + 1 ], $offset, $is_leading );
}
-{ ## begin closure terminal_type_i
+sub terminal_type_i {
- my %is_sort_map_grep_eval_do;
+ # returns type of last token on this line (terminal token), as follows:
+ # returns # for a full-line comment
+ # returns ' ' for a blank line
+ # otherwise returns final token type
- BEGIN {
- my @q = qw(sort map grep eval do);
- @is_sort_map_grep_eval_do{@q} = (1) x scalar(@q);
- }
-
- sub terminal_type_i {
-
- # returns type of last token on this line (terminal token), as follows:
- # returns # for a full-line comment
- # returns ' ' for a blank line
- # otherwise returns final token type
-
- my ( $ibeg, $iend ) = @_;
+ my ( $ibeg, $iend ) = @_;
- # Start at the end and work backwards
- my $i = $iend;
- my $type_i = $types_to_go[$i];
+ # Start at the end and work backwards
+ my $i = $iend;
+ my $type_i = $types_to_go[$i];
- # Check for side comment
- if ( $type_i eq '#' ) {
- $i--;
- if ( $i < $ibeg ) {
- return wantarray ? ( $type_i, $ibeg ) : $type_i;
- }
- $type_i = $types_to_go[$i];
- }
-
- # Skip past a blank
- if ( $type_i eq 'b' ) {
- $i--;
- if ( $i < $ibeg ) {
- return wantarray ? ( $type_i, $ibeg ) : $type_i;
- }
- $type_i = $types_to_go[$i];
+ # Check for side comment
+ if ( $type_i eq '#' ) {
+ $i--;
+ if ( $i < $ibeg ) {
+ return wantarray ? ( $type_i, $ibeg ) : $type_i;
}
+ $type_i = $types_to_go[$i];
+ }
- # Found it..make sure it is a BLOCK termination,
- # but hide a terminal } after sort/grep/map because it is not
- # necessarily the end of the line. (terminal.t)
- my $block_type = $block_type_to_go[$i];
- if (
- $type_i eq '}'
- && ( !$block_type
- || ( $is_sort_map_grep_eval_do{$block_type} ) )
- )
- {
- $type_i = 'b';
+ # Skip past a blank
+ if ( $type_i eq 'b' ) {
+ $i--;
+ if ( $i < $ibeg ) {
+ return wantarray ? ( $type_i, $ibeg ) : $type_i;
}
- return wantarray ? ( $type_i, $i ) : $type_i;
+ $type_i = $types_to_go[$i];
}
-} ## end closure terminal_type_i
+ # Found it..make sure it is a BLOCK termination,
+ # but hide a terminal } after sort/map/grep/eval/do because it is not
+ # necessarily the end of the line. (terminal.t)
+ my $block_type = $block_type_to_go[$i];
+ if (
+ $type_i eq '}'
+ && ( !$block_type
+ || ( $is_sort_map_grep_eval_do{$block_type} ) )
+ )
+ {
+ $type_i = 'b';
+ }
+ return wantarray ? ( $type_i, $i ) : $type_i;
+}
sub pad_array_to_go {
{ ## begin closure make_alignment_patterns
- my %block_type_map;
my %keyword_map;
my %operator_map;
my %is_w_n_C;
BEGIN {
- # map related block names into a common name to
- # allow alignment
- %block_type_map = (
- 'unless' => 'if',
- 'else' => 'if',
- 'elsif' => 'if',
- 'when' => 'if',
- 'default' => 'if',
- 'case' => 'if',
- 'sort' => 'map',
- 'grep' => 'map',
- );
+ # Note: %block_type_map is now global to enable the -gal=s option
# map certain keywords to the same 'if' class to align
# long if/elsif sequences. [elsif.pl]
%is_valid_token_type
%is_keyword
%is_code_block_token
+ %is_sort_map_grep_eval_do
+ %is_grep_alias
%really_want_term
@opening_brace_names
@closing_brace_names
}
}
+ # Install any aliases to 'grep'
+ if ( $rOpts->{'grep-alias-list'} ) {
+
+ # Note that 'grep-alias-list' has been preprocessed to be a trimmed,
+ # space-separated list
+ my @q = split /\s+/, $rOpts->{'grep-alias-list'};
+ @{is_grep_alias}{@q} = (1) x scalar(@q);
+ @{is_code_block_token}{@q} = (1) x scalar(@q);
+ @{is_sort_map_grep_eval_do}{@q} = (1) x scalar(@q);
+ }
+
$rOpts_code_skipping = $rOpts->{'code-skipping'};
$code_skipping_pattern_begin =
make_code_skipping_pattern( $rOpts, 'code-skipping-begin', '#<<V' );
&& $last_nonblank_i >= 0 )
{
if ( $routput_token_type->[$last_nonblank_i] eq 'w' ) {
- $routput_token_type->[$last_nonblank_i] = 'G';
+ $routput_token_type->[$last_nonblank_i] =
+ $is_grep_alias{$block_type} ? 'k' : 'G';
}
}
if elsif else unless while until for foreach switch case given when);
@is_zero_continuation_block_type{@_} = (1) x scalar(@_);
- my %is_not_zero_continuation_block_type;
- @_ = qw(sort grep map do eval);
- @is_not_zero_continuation_block_type{@_} = (1) x scalar(@_);
-
my %is_logical_container;
@_ = qw(if elsif unless while and or err not && ! || for foreach);
@is_logical_container{@_} = (1) x scalar(@_);
# ..but these are not terminal types:
# /^(sort|grep|map|do|eval)$/ )
elsif (
- $is_not_zero_continuation_block_type{
+ $is_sort_map_grep_eval_do{
$routput_block_type->[$i]
}
)
switch case given when default catch try finally);
@is_code_block_token{@q} = (1) x scalar(@q);
+ # Note: this hash was formerly named '%is_not_zero_continuation_block_type'
+ # to contrast it with the block types in '%is_zero_continuation_block_type'
+ @q = qw( sort map grep eval do );
+ @is_sort_map_grep_eval_do{@q} = (1) x scalar(@q);
+
+ %is_grep_alias = ();
+
# I'll build the list of keywords incrementally
my @Keywords = ();
@is_keyword{@Keywords} = (1) x scalar(@Keywords);
}
+1;
--- /dev/null
+ # These should format the same with -gal='Map Grep'
+ return +{
+ Map {
+ $_->init_arg => $_->get_value($instance)
+ } Grep { $_->has_value($instance) }
+ Grep {
+ defined( $_->init_arg )
+ }
+ $class->get_all_attributes
+ };
+
+ return +{
+ map { $_->init_arg => $_->get_value($instance) }
+ grep { $_->has_value($instance) }
+ grep { defined( $_->init_arg ) } $class->get_all_attributes
+ };
--- /dev/null
+ # These should format the same with -gal='Map Grep'
+ return +{
+ Map { $_->init_arg => $_->get_value($instance) }
+ Grep { $_->has_value($instance) }
+ Grep { defined( $_->init_arg ) } $class->get_all_attributes
+ };
+
+ return +{
+ map { $_->init_arg => $_->get_value($instance) }
+ grep { $_->has_value($instance) }
+ grep { defined( $_->init_arg ) } $class->get_all_attributes
+ };
--- /dev/null
+# These should format the same with -gal='Map Grep'
+ return +{
+ Map {
+$_->init_arg => $_->get_value($instance) }
+ Grep { $_->has_value($instance) }
+ Grep {
+defined( $_->init_arg ) }
+$class->get_all_attributes
+ };
+
+ return +{
+ map {
+$_->init_arg => $_->get_value($instance) }
+ grep { $_->has_value($instance) }
+ grep {
+defined( $_->init_arg ) }
+$class->get_all_attributes
+ };
--- /dev/null
+-gal='Grep Map'
../snippets9.t rt98902.def
../snippets9.t rt98902.rt98902
../snippets9.t rt99961.def
+../snippets25.t git77.def
+../snippets25.t git77.git77
#11 xlp1.xlp1
#12 git74.def
#13 git74.git74
+#14 git77.def
+#15 git77.git77
# To locate test #13 you can search for its name or the string '#13'
--brace-left-and-indent
--brace-left-and-indent-list="*"
--break-before-hash-brace=3
+----------
+ 'git77' => <<'----------',
+-gal='Grep Map'
----------
'lp' => "-lp",
'novalign1' => "-novalign",
);
+----------
+
+ 'git77' => <<'----------',
+# These should format the same with -gal='Map Grep'
+ return +{
+ Map {
+$_->init_arg => $_->get_value($instance) }
+ Grep { $_->has_value($instance) }
+ Grep {
+defined( $_->init_arg ) }
+$class->get_all_attributes
+ };
+
+ return +{
+ map {
+$_->init_arg => $_->get_value($instance) }
+ grep { $_->has_value($instance) }
+ grep {
+defined( $_->init_arg ) }
+$class->get_all_attributes
+ };
----------
'lp2' => <<'----------',
#13...........
},
+
+ 'git77.def' => {
+ source => "git77",
+ params => "def",
+ expect => <<'#14...........',
+ # These should format the same with -gal='Map Grep'
+ return +{
+ Map {
+ $_->init_arg => $_->get_value($instance)
+ } Grep { $_->has_value($instance) }
+ Grep {
+ defined( $_->init_arg )
+ }
+ $class->get_all_attributes
+ };
+
+ return +{
+ map { $_->init_arg => $_->get_value($instance) }
+ grep { $_->has_value($instance) }
+ grep { defined( $_->init_arg ) } $class->get_all_attributes
+ };
+#14...........
+ },
+
+ 'git77.git77' => {
+ source => "git77",
+ params => "git77",
+ expect => <<'#15...........',
+ # These should format the same with -gal='Map Grep'
+ return +{
+ Map { $_->init_arg => $_->get_value($instance) }
+ Grep { $_->has_value($instance) }
+ Grep { defined( $_->init_arg ) } $class->get_all_attributes
+ };
+
+ return +{
+ map { $_->init_arg => $_->get_value($instance) }
+ grep { $_->has_value($instance) }
+ grep { defined( $_->init_arg ) } $class->get_all_attributes
+ };
+#15...........
+ },
};
my $ntests = 0 + keys %{$rtests};