]> git.donarmstrong.com Git - perltidy.git/commitdiff
add + and - prefixes to -wtc controls
authorSteve Hancock <perltidy@users.sourceforge.net>
Wed, 25 Sep 2024 00:51:37 +0000 (17:51 -0700)
committerSteve Hancock <perltidy@users.sourceforge.net>
Wed, 25 Sep 2024 00:51:37 +0000 (17:51 -0700)
bin/perltidy
lib/Perl/Tidy.pm
lib/Perl/Tidy/Formatter.pm

index de55462653b174a67cc936de9a98afc095ed6387..0b7f0f71554dc8f59222c862c97b4f74fb782c1d 100755 (executable)
@@ -3909,7 +3909,7 @@ This option is on by default.  Use B<-ndrc> to turn it off.
 
 =item B<--want-trailing-commas=s> or B<-wtc=s>, B<--add-trailing-commas> or B<-atc>, and B<--delete-trailing-commas> or B<-dtc>
 
-A trailing comma is a comma following the last item of a list. Perl allows
+A B<trailing comma> is a comma following the last item of a list. Perl allows
 trailing commas but they are not required.  Including them can sometimes
 simplify the maintenance of large or complex lists, and help display structure.
 By default, perltidy does not add
@@ -3931,17 +3931,41 @@ B<--delete-trailing-commas, -dtc>   - gives permission to delete trailing commas
 
 The parameter B<--want-trailing-commas=s>, or B<-wtc=s>, defines a preferred style.  The string B<s> indicates which lists should get trailing commas, as follows:
 
-  s=0 : no list should have a trailing comma
   s=1 or '*' : every list should have a trailing comma
   s=m multiline list
   s=b multiline list with bare trailing comma
   s=i multiline list with bare trailing comma and about one comma per line
   s=h multiline list with bare trailing comma and about one key=>value pair per line
+  s=0 : no list should have a trailing comma
+
   s=' ' or -wtc not defined : leave trailing commas unchanged [DEFAULT].
 
-A multiline list here is a list for which the opening and closing container
-tokens are on different lines. A bare trailing comma is a comma followed
-by a newline.
+where:
+
+=over 4
+
+=item *
+
+A B<list> here is basically taken to be a container of items (parens, square
+brackets, or braces), which is not a code block, which contains one or more
+commas or fat commas.  These parameters only apply to something that fits this
+definition of a list.
+
+A paren-less list of parameters is not a list by this definition, so
+these parameters do not apply to a paren-less list.
+
+=item *
+
+A B<multiline list> is a list for which the opening and closing brackets
+on different lines.
+
+=item *
+
+A B<bare trailing comma> is a comma which is at the end of a line. That is,
+the closing container token follows on a different line.  So a list with a
+bare trailing comma is a special case of a multiline list.
+
+=back
 
 This parameter by itself only indicates where trailing commas are wanted.
 Perltidy only adds these trailing commas if the flag B<--add-trailing-commas>,
@@ -4000,7 +4024,6 @@ are currently 'k', 'K', 'f', 'F', 'w', and 'W', with these meanings for
 matching whatever precedes an opening paren:
 
  'k' matches if the previous nonblank token is a perl built-in keyword
-     (such as 'if', 'while'),
  'K' matches if 'k' does not, meaning that the previous token is not a keyword.
  'f' matches if the previous token is a function other than a keyword.
  'F' matches if 'f' does not.
@@ -4010,34 +4033,47 @@ matching whatever precedes an opening paren:
 These are the same codes used for B<--line-up-parentheses-inclusion-list>.
 For example,
 
-  -wtc = 'w(m'
+  -wtc='w(m'
 
 means that trailing commas are wanted for multiline parenthesized lists following a function call or keyword.
 
-B<Some points to note> regarding adding and deleting trailing commas:
+Finally, a leading B<+> can be placed on any term to indicate that it only
+applies when adding commas. A leading B<-> indicates that it only applies when
+deleting commas. For example,
 
-=over 4
+  -wtc='+h -b' -atc -dtc
 
-=item *
+means that missing trailing commas should be added to lists of key => values
+pairs, and trailing commas which are not bare should be removed. No other
+changes are made. When both plus and minus terms are used like this, they must
+not conflict at any trailing comma locations.  This turns out to be equivalent
+to requiring that the letter of the plus term does not occur before the letter
+of the minus term in the hierarchical order B<m>, B<b>, B<i>, B<h>.  In this
+example, the plus term B<h> follows the minus term B<b>, so there is no
+conflict.
 
-For the implementation of these parameters, a B<list> is basically taken to be
-a container of items (parens, square brackets, or braces), which is not a code
-block, with one or more commas or fat commas.  These parameters only apply to
-something that fits this definition of a list.
+B<Some points to note> regarding adding and deleting trailing commas:
 
-Note that a paren-less list of parameters is not a list by this definition, so
-these parameters have no effect on a paren-less list.
+=over 4
 
 =item *
 
-A B<multiline> list is a list for which the opening and closing brackets
-on different lines.
+It is recommended to also use the B<--converge> parameter when adding and/or
+deleting trailing commas, especially if the formatter may be making other
+line break changes at the same time.  The reason is that the decision
+regarding whether or not a list is multiline or bare is made based on the
+B<input> stream if only one iteration is made, which is the default.
 
-=item *
+When iterations are requested with the B<--converge> parameter, any comma
+addition or deletion operations are postponed until the start of the second
+iteration, after changes in line breaks have been made.  For a discussion see
+L<https://github.com/perltidy/perltidy/issues/156>.
 
-A B<bare> trailing comma is a comma which is at the end of a line. That is,
-the closing container token follows on a different line.  So a list with a
-bare trailing comma is a special case of a multiline list.
+A parameter B<--delay-trailing-comma-operations>, or B<-dtco>, is available to
+control behavior if desired. Negating this parameter, with B<-ndtco>, tells
+perltidy to always use the starting state to make decisions regarding comma
+addition and deletion, even when iterations are requested. This should not
+normally be necessary.
 
 =item *
 
@@ -4056,34 +4092,20 @@ line length to be exceeded. This in turn will cause a different break point to
 occur for which the comma is no longer bare. So if a comma were added, it would
 get deleted on the next pass or iteration, an unstable situation.
 
-=item *
+This example illustrates a common situation of potentially instability, in
+which a list in parens is split over two lines, with all of the commas (or fat
+comma) on one line.  This indicates that the formatting is under stress from
+the line length limit.
 
-It is a good idea to turn on the B<--converge> parameter when adding and
-deleting trailing commas.  The reason is that the decision regarding whether or
-not a list is multiline or bare is made based on the B<input> stream if only
-one iteration is made, which is the default.
-
-When iterations are requested with the B<--converge> parameter, any comma
-addition or deletion operations are postponed until the start of the second
-iteration, after changes in line breaks have been made.  For a discussion see
-L<https://github.com/perltidy/perltidy/issues/156>.
-
-A parameter B<--delay-trailing-comma-operations>, or B<-dtco>, is available to
-control behavior if desired. Negating this parameter, with B<-ndtco>, tells
-perltidy to always use the starting state to make decisions regarding comma
-addition and deletion, even when iterations are requested. This should not
-normally be necessary.
+The rules used to detecting instability are not precise, so in some cases where
+perltidy will not add a comma, it may be possible to add a trailing comma with
+editing.
 
 =item *
 
 When using these parameters for the first time it is a good idea to practice
 on some test scripts and verify that the results are as expected.
 
-=item *
-
-Since the default behavior is not to add or delete commas, these parameters
-can be useful on a temporary basis for reformatting a script.
-
 =back
 
 B<Special Considerations for Lone Trailing Commas>
index ab829407fec5291fd06377ffbf2e00cb8075e3ac..e32c50b81ce84929ae76826c1ebda729c7871bd2 100644 (file)
@@ -4769,7 +4769,7 @@ EOM
         if ( $Opts{$long_name} ) {
             my $short_name = $early_exit_commands{$long_name};
             Die(<<EOM);
-Ambigiguous entry; please enter '--$long_name' or '-$short_name'
+Ambiguous entry; please enter '--$long_name' or '-$short_name'
 EOM
         }
     }
index 5584965a041fb2d43916e6ca43353702b3a9e788..df474f2599fa587b3746fb4d9b0f752754bba600 100644 (file)
@@ -3033,6 +3033,23 @@ sub initialize_trailing_comma_rules {
 
     my $rvalid_flags = [qw(0 1 * m b h i)];
 
+    # This hash shows i.e. that 'm' includes all 'b' includes all 'i' ...etc
+    # It is used to check for overlap when both + and - signs are used to
+    # cause adding and deleting of different types of trailing commas.
+    my %match_order = (
+        '1' => 0,
+        '*' => 0,
+        'm' => 1,
+        'b' => 2,
+        'i' => 3,
+        'h' => 4,
+        '0' => 5,
+    );
+
+    # Note that the hash keys are the CLOSING tokens but the input
+    # uses OPENING tokens.
+    my @all_keys = qw< ) ] } >;
+
     my $option = $rOpts->{'want-trailing-commas'};
 
     if ($option) {
@@ -3048,10 +3065,11 @@ sub initialize_trailing_comma_rules {
         my %is_valid_flag;
         @is_valid_flag{@q} = (1) x scalar(@q);
 
-        # handle single character control, such as -wtc='b'
+        # handle the common case of a single control character, like -wtc='b'
         if ( length($option) == 1 ) {
-            foreach (qw< ) ] } >) {
-                $rule_hash{$_} = [ $option, EMPTY_STRING ];
+            foreach (@all_keys) {
+                $rule_hash{add}->{$_}    = [ $option, EMPTY_STRING ];
+                $rule_hash{delete}->{$_} = [ $option, EMPTY_STRING ];
             }
         }
 
@@ -3059,48 +3077,116 @@ sub initialize_trailing_comma_rules {
         else {
             my @parts = split /\s+/, $option;
             foreach my $part (@parts) {
-                if ( length($part) >= 2 && length($part) <= 3 ) {
-                    my $val   = substr( $part, -1, 1 );
-                    my $key_o = substr( $part, -2, 1 );
-                    if ( $is_opening_token{$key_o} ) {
-                        my $paren_flag = EMPTY_STRING;
-                        if ( length($part) == 3 ) {
-                            $paren_flag = substr( $part, 0, 1 );
-                        }
-                        my $key = $matching_token{$key_o};
-                        $rule_hash{$key} = [ $val, $paren_flag ];
-                    }
-                    else {
-                        $error_message .= "Unrecognized term: '$part'\n";
-                    }
-                }
-                else {
-                    $error_message .= "Unrecognized term: '$part'\n";
-                }
-            }
-        }
+                my $part_input = $part;
 
-        # check for valid control characters
-        if ( !$error_message ) {
-            foreach my $key ( keys %rule_hash ) {
-                my $item = $rule_hash{$key};
-                my ( $val, $paren_flag ) = @{$item};
+                # examples: b -b [b 0 * +f(b
+
+                # the letter value is the rightmost character
+                my $val = substr( $part, -1, 1 );
+                $part = substr( $part, 0, -1 );
                 if ( $val && !$is_valid_flag{$val} ) {
                     my $valid_str = join( SPACE, @{$rvalid_flags} );
                     $error_message .=
-                      "Unexpected value '$val'; must be one of: $valid_str\n";
-                    last;
+"In '$part_input': unexpected value '$val'; must be one of: $valid_str\n";
+                    next;
+                }
+
+                # set defaults for this item
+                my @signs      = qw( add delete );
+                my @keys       = @all_keys;
+                my $paren_flag = EMPTY_STRING;
+
+                # look for opening container bracket
+                my $is_paren;
+                if ( length($part) ) {
+                    my $token = substr( $part, -1, 1 );
+                    if ( $is_opening_token{$token} ) {
+
+                        # note that the hash key is the closing token
+                        my $key = $matching_token{$token};
+                        @keys     = ($key);
+                        $part     = substr( $part, 0, -1 );
+                        $is_paren = $token eq '(';
+                    }
                 }
-                if ($paren_flag) {
+
+                # look for a leading sign, + or -
+                if ( length($part) ) {
+                    my $sign = substr( $part, 0, 1 );
+                    if ( $sign eq '+' ) {
+                        @signs = qw(add);
+                        $part  = substr( $part, 1 );
+                    }
+                    elsif ( $sign eq '-' ) {
+                        @signs = qw(delete);
+                        $part  = substr( $part, 1 );
+                    }
+                    else {
+                        ## keep defaults
+                    }
+                }
+
+                # anything left must be a paren modifier
+                if ( length($part) ) {
+                    $paren_flag = substr( $part, -1,  1 );
+                    $part       = substr( $part,  0, -1 );
                     if ( $paren_flag !~ /^[kKfFwW]$/ ) {
                         $error_message .=
-"Unexpected paren flag '$paren_flag'; must be one of: k K f F w W\n";
-                        last;
+"In '$part_input': Unexpected paren flag '$paren_flag'; must be one of: k K f F w W\n";
+                        next;
                     }
-                    if ( $key ne ')' ) {
+                    if ( !$is_paren ) {
                         $error_message .=
-"paren flag '$paren_flag' is only allowed before a '('\n";
-                        last;
+"In '$part_input': paren flag '$paren_flag' is only allowed before a '('\n";
+                        next;
+                    }
+                }
+
+                if ( length($part) ) {
+                    $error_message .= "Unrecognized term: '$part_input'\n";
+                    next;
+                }
+
+                foreach my $sign (@signs) {
+                    foreach my $key (@keys) {
+                        $rule_hash{$sign}->{$key} = [ $val, $paren_flag ];
+                    }
+                }
+            }
+        }
+
+        # check for conflicting signed options
+        if ( !$error_message ) {
+
+            my $radd    = $rule_hash{add};
+            my $rdelete = $rule_hash{delete};
+            if ( defined($radd) && defined($rdelete) ) {
+                foreach my $key (@all_keys) {
+                    my $radd_info    = $radd->{$key};
+                    my $rdelete_info = $rdelete->{$key};
+                    if ( defined($radd_info) && defined($rdelete_info) ) {
+                        my $add_val    = $radd_info->[0];
+                        my $delete_val = $rdelete_info->[0];
+                        next if ( $add_val eq $delete_val );
+                        my $add_order    = $match_order{$add_val};
+                        my $delete_order = $match_order{$delete_val};
+                        if ( !defined($add_order) ) {
+                            ## should have been caught earlier
+                            DEVEL_MODE
+                              && Fault("unexpected + value $add_val\n");
+                            next;
+                        }
+                        if ( !defined($delete_order) ) {
+                            ## should have been caught earlier
+                            DEVEL_MODE
+                              && Fault("unexpected - value $delete_val\n");
+                            next;
+                        }
+                        if ( $add_order <= $delete_order ) {
+                            my $token = $matching_token{$key};
+                            $error_message .=
+"At token '$token': the range for '+$add_val' overlaps the range for '-$delete_val'\n";
+                        }
                     }
                 }
             }
@@ -12486,8 +12572,11 @@ sub respace_tokens_inner_loop {
                                 )
                               )
                             {
-                                $self->add_trailing_comma( $KK, $Kfirst,
-                                    $trailing_comma_rules{$token} );
+                                my $rule = $trailing_comma_rules{add};
+                                if ( $rule && $rule->{$token} ) {
+                                    $self->add_trailing_comma( $KK, $Kfirst,
+                                        $rule->{$token} );
+                                }
                             }
                         }
 
@@ -12509,9 +12598,12 @@ sub respace_tokens_inner_loop {
                                 && $last_nonblank_code_token
                               )
                             {
-                                $deleted =
-                                  $self->delete_trailing_comma( $KK, $Kfirst,
-                                    $trailing_comma_rules{$token} );
+                                my $rule = $trailing_comma_rules{delete};
+                                if ( $rule && $rule->{$token} ) {
+                                    $deleted =
+                                      $self->delete_trailing_comma( $KK,
+                                        $Kfirst, $rule->{$token} );
+                                }
                             }
 
                             # delete a weld-interfering comma if requested
@@ -15176,7 +15268,7 @@ sub package_info_maker {
 
     return {
         'rpackage_info_list'   => \@package_info_list,
-        'rpackage_lookup_list' => \@package_lookup_list
+        'rpackage_lookup_list' => \@package_lookup_list,
     };
 } ## end sub package_info_maker
 
@@ -17907,8 +17999,8 @@ sub initialize_warn_mismatched {
     #  y - want scalar but no return seen
     #  s - want scalar but only arrays with count > 1 returned
     $rwarn_mismatched_return_types =
-      initialize_warn_hash( 'warn-mismatched-return-types', 1,
-        [qw(x o u y s)] );
+      initialize_warn_hash( 'warn-mismatched-return-types',
+        1, [qw(x o u y s)] );
     $ris_warn_mismatched_return_excluded_name =
       make_excluded_name_hash('warn-mismatched-return-exclusion-list');
     return;
@@ -19260,7 +19352,10 @@ EOM
             $weld_count_this_start = 0;
             $weld_starts_in_block  = 0;
 
-            ( my $new_weld_ok, $maximum_text_length, $starting_lentot, my $msg )
+            (
+                my $new_weld_ok,
+                $maximum_text_length, $starting_lentot, my $msg
+              )
               = $self->setup_new_weld_measurements( $Kouter_opening,
                 $Kinner_opening );