From cb3e71b1d0c1d7080b16ac2f3036e41240d05be4 Mon Sep 17 00:00:00 2001 From: Steve Hancock Date: Mon, 22 Aug 2022 18:02:05 -0700 Subject: [PATCH] restructure sub get_final_indentation --- lib/Perl/Tidy/Formatter.pm | 1183 +++++++++++++++++++----------------- 1 file changed, 639 insertions(+), 544 deletions(-) diff --git a/lib/Perl/Tidy/Formatter.pm b/lib/Perl/Tidy/Formatter.pm index 63ea9556..5f60f748 100644 --- a/lib/Perl/Tidy/Formatter.pm +++ b/lib/Perl/Tidy/Formatter.pm @@ -25197,7 +25197,7 @@ sub make_paren_name { $rpatterns, $ri_first, $ri_last, $rindentation_list, $level_jump, $starting_in_quote, - $is_static_block_comment, + $is_static_block_comment ) = @_; # Find the last code token of this line @@ -25222,9 +25222,6 @@ sub make_paren_name { my $seqno_beg = $type_sequence_to_go[$ibeg]; my $is_closing_type_beg = $is_closing_type{$type_beg}; - my $ris_bli_container = $self->[_ris_bli_container_]; - my $is_bli_beg = $seqno_beg ? $ris_bli_container->{$seqno_beg} : 0; - # QW INDENTATION PATCH 3: my $seqno_qw_closing; if ( $type_beg eq 'q' && $ibeg == 0 ) { @@ -25275,663 +25272,761 @@ sub make_paren_name { # 2 - vertically align with opening token # 3 - indent #--------------------------------------------------------- + my $adjust_indentation = 0; - my $default_adjust_indentation = $adjust_indentation; + my $default_adjust_indentation = 0; + # Parameters needed for option 2, aligning with opening token: my ( $opening_indentation, $opening_offset, $is_leading, $opening_exists ); - # Honor any flag to reduce -ci set by the -bbxi=n option - if ( $seqno_beg && $self->[_rwant_reduced_ci_]->{$seqno_beg} ) { + #------------------------------------- + # Section 1A: + # if line starts with a sequenced item + #------------------------------------- + if ( $seqno_beg || $seqno_qw_closing ) { - # if this is an opening, it must be alone on the line ... - if ( $is_closing_type{$type_beg} || $ibeg == $i_terminal ) { - $adjust_indentation = 1; - } + # This can be tedious so we let a sub do it + ( + $adjust_indentation, $default_adjust_indentation, + $opening_indentation, $opening_offset, + $is_leading, $opening_exists + ) + = $self->get_closing_token_indentation( + + $ibeg, + $iend, + $ri_first, + $ri_last, + $rindentation_list, + $level_jump, + $i_terminal, + $is_semicolon_terminated, + $seqno_qw_closing, - # ... or a single welded unit (fix for b1173) - elsif ($total_weld_count) { - my $K_beg = $K_to_go[$ibeg]; - my $Kterm = $K_to_go[$i_terminal]; - my $Kterm_test = $self->[_rK_weld_left_]->{$Kterm}; - if ( defined($Kterm_test) && $Kterm_test >= $K_beg ) { - $Kterm = $Kterm_test; - } - if ( $Kterm == $K_beg ) { $adjust_indentation = 1 } - } + ); } - # Update the $is_bli flag as we go. It is initially 1. - # We note seeing a leading opening brace by setting it to 2. - # If we get to the closing brace without seeing the opening then we - # turn it off. This occurs if the opening brace did not get output - # at the start of a line, so we will then indent the closing brace - # in the default way. - if ( $is_bli_beg && $is_bli_beg == 1 ) { - my $K_opening_container = $self->[_K_opening_container_]; - my $K_opening = $K_opening_container->{$seqno_beg}; - my $K_beg = $K_to_go[$ibeg]; - if ( $K_beg eq $K_opening ) { - $ris_bli_container->{$seqno_beg} = $is_bli_beg = 2; + #-------------------------------------------------------- + # Section 1B: + # if at ');', '};', '>;', and '];' of a terminal qw quote + #-------------------------------------------------------- + elsif ( + substr( $rpatterns->[0], 0, 2 ) eq 'qb' + && substr( $rfields->[0], -1, 1 ) eq ';' + ## $rpatterns->[0] =~ /^qb*;$/ + && $rfields->[0] =~ /^([\)\}\]\>]);$/ + ) + { + if ( $closing_token_indentation{$1} == 0 ) { + $adjust_indentation = 1; } - else { $is_bli_beg = 0 } - } - - # QW PATCH for the combination -lp -wn - # For -lp formatting use $ibeg_weld_fix to get around the problem - # that with -lp type formatting the opening and closing tokens to not - # have sequence numbers. - my $ibeg_weld_fix = $ibeg; - if ( $seqno_qw_closing && $total_weld_count ) { - my $i_plus = $inext_to_go[$ibeg]; - if ( $i_plus <= $max_index_to_go ) { - my $K_plus = $K_to_go[$i_plus]; - if ( defined( $self->[_rK_weld_left_]->{$K_plus} ) ) { - $ibeg_weld_fix = $i_plus; - } + else { + $adjust_indentation = 3; } } - # if we are at a closing token of some type.. - if ( $is_closing_type_beg || $seqno_qw_closing ) { - - my $K_beg = $K_to_go[$ibeg]; - - # get the indentation of the line containing the corresponding - # opening token - ( - $opening_indentation, $opening_offset, - $is_leading, $opening_exists - ) - = $self->get_opening_indentation( $ibeg_weld_fix, $ri_first, - $ri_last, $rindentation_list, $seqno_qw_closing ); - - my $terminal_is_in_list = $self->is_in_list_by_i($i_terminal); - - # First set the default behavior: - if ( + #--------------------------------------------------------- + # Section 2: set indentation according to flag set above + # + # Select the indentation object to define leading + # whitespace. If we are outdenting something like '} } );' + # then we want to use one level below the last token + # ($i_terminal) in order to get it to fully outdent through + # all levels. + #--------------------------------------------------------- + my $indentation; + my $lev; + my $level_end = $levels_to_go[$iend]; - # default behavior is to outdent closing lines - # of the form: "); }; ]; )->xxx;" - $is_semicolon_terminated + #------------------------------------ + # Section 2A: adjust_indentation == 0 + # No change in indentation + #------------------------------------ + if ( $adjust_indentation == 0 ) { + $indentation = $leading_spaces_beg; + $lev = $level_beg; + } - # and 'cuddled parens' of the form: ")->pack(" - # Bug fix for RT #123749]: the types here were - # incorrectly '(' and ')'. Corrected to be '{' and '}' - || ( - $terminal_type eq '{' - && $type_beg eq '}' - && ( $nesting_depth_to_go[$iend] + 1 == - $nesting_depth_to_go[$ibeg] ) - ) + #------------------------------------------------------------------- + # Secton 2B: adjust_indentation == 1 + # Change the indentation to be that of a different token on the line + #------------------------------------------------------------------- + elsif ( $adjust_indentation == 1 ) { - # remove continuation indentation for any line like - # } ... { - # or without ending '{' and unbalanced, such as - # such as '}->{$operator}' - || ( - $type_beg eq '}' + # Previously, the indentation of the terminal token was used: + # OLD CODING: + # $indentation = $reduced_spaces_to_go[$i_terminal]; + # $lev = $levels_to_go[$i_terminal]; - && ( $types_to_go[$iend] eq '{' - || $levels_to_go[$iend] < $level_beg ) - ) + # Generalization for MOJO patch: + # Use the lowest level indentation of the tokens on the line. + # For example, here we can use the indentation of the ending ';': + # } until ($selection > 0 and $selection < 10); # ok to use ';' + # But this will not outdent if we use the terminal indentation: + # )->then( sub { # use indentation of the ->, not the { + # Warning: reduced_spaces_to_go[] may be a reference, do not + # do numerical checks with it - # and when the next line is at a lower indentation level... + my $i_ind = $ibeg; + $indentation = $reduced_spaces_to_go[$i_ind]; + $lev = $levels_to_go[$i_ind]; + while ( $i_ind < $i_terminal ) { + $i_ind++; + if ( $levels_to_go[$i_ind] < $lev ) { + $indentation = $reduced_spaces_to_go[$i_ind]; + $lev = $levels_to_go[$i_ind]; + } + } + } - # PATCH #1: and only if the style allows undoing continuation - # for all closing token types. We should really wait until - # the indentation of the next line is known and then make - # a decision, but that would require another pass. + #-------------------------------------------------------------- + # Secton 2C: adjust_indentation == 2 + # Handle indented closing token which aligns with opening token + #-------------------------------------------------------------- + elsif ( $adjust_indentation == 2 ) { - # PATCH #2: and not if this token is under -xci control - || ( $level_jump < 0 - && !$some_closing_token_indentation - && !$self->[_rseqno_controlling_my_ci_]->{$K_beg} ) + # handle option to align closing token with opening token + $lev = $level_beg; - # Patch for -wn=2, multiple welded closing tokens - || ( $i_terminal > $ibeg - && $is_closing_type{ $types_to_go[$iend] } ) + # calculate spaces needed to align with opening token + my $space_count = + get_spaces($opening_indentation) + $opening_offset; - # Alternate Patch for git #51, isolated closing qw token not - # outdented if no-delete-old-newlines is set. This works, but - # a more general patch elsewhere fixes the real problem: ljump. - # || ( $seqno_qw_closing && $ibeg == $i_terminal ) + # Indent less than the previous line. + # + # Problem: For -lp we don't exactly know what it was if there + # were recoverable spaces sent to the aligner. A good solution + # would be to force a flush of the vertical alignment buffer, so + # that we would know. For now, this rule is used for -lp: + # + # When the last line did not start with a closing token we will + # be optimistic that the aligner will recover everything wanted. + # + # This rule will prevent us from breaking a hierarchy of closing + # tokens, and in a worst case will leave a closing paren too far + # indented, but this is better than frequently leaving it not + # indented enough. + my $last_spaces = get_spaces($last_indentation_written); - ) + if ( ref($last_indentation_written) + && !$is_closing_token{$last_leading_token} ) { - $adjust_indentation = 1; + $last_spaces += + get_recoverable_spaces($last_indentation_written); } - # outdent something like '),' - if ( - $terminal_type eq ',' - - # Removed this constraint for -wn - # OLD: allow just one character before the comma - # && $i_terminal == $ibeg + 1 + # reset the indentation to the new space count if it works + # only options are all or none: nothing in-between looks good + $lev = $level_beg; - # require LIST environment; otherwise, we may outdent too much - - # this can happen in calls without parentheses (overload.t); - && $terminal_is_in_list - ) - { - $adjust_indentation = 1; + my $diff = $last_spaces - $space_count; + if ( $diff > 0 ) { + $indentation = $space_count; } + else { - # undo continuation indentation of a terminal closing token if - # it is the last token before a level decrease. This will allow - # a closing token to line up with its opening counterpart, and - # avoids an indentation jump larger than 1 level. - my $rLL = $self->[_rLL_]; - my $Klimit = $self->[_Klimit_]; - if ( $i_terminal == $ibeg - && $is_closing_type_beg - && defined($K_beg) - && $K_beg < $Klimit ) - { - my $K_plus = $K_beg + 1; - my $type_plus = $rLL->[$K_plus]->[_TYPE_]; + # We need to fix things ... but there is no good way to do it. + # The best solution is for the user to use a longer maximum + # line length. We could get a smooth variation if we just move + # the paren in using + # $space_count -= ( 1 - $diff ); + # But unfortunately this can give a rather unbalanced look. - if ( $type_plus eq 'b' && $K_plus < $Klimit ) { - $type_plus = $rLL->[ ++$K_plus ]->[_TYPE_]; + # For -xlp we currently allow a tolerance of one indentation + # level and then revert to a simpler default. This will jump + # suddenly but keeps a balanced look. + if ( $rOpts_extended_line_up_parentheses + && $diff >= -$rOpts_indent_columns + && $space_count > $leading_spaces_beg ) + { + $indentation = $space_count; } - if ( $type_plus eq '#' && $K_plus < $Klimit ) { - $type_plus = $rLL->[ ++$K_plus ]->[_TYPE_]; - if ( $type_plus eq 'b' && $K_plus < $Klimit ) { - $type_plus = $rLL->[ ++$K_plus ]->[_TYPE_]; - } - - # Note: we have skipped past just one comment (perhaps a - # side comment). There could be more, and we could easily - # skip past all the rest with the following code, or with a - # while loop. It would be rare to have to do this, and - # those block comments would still be indented, so it would - # to leave them indented. So it seems best to just stop at - # a maximum of one comment. - ##if ($type_plus eq '#') { - ## $K_plus = $self->K_next_code($K_plus); - ##} + # Otherwise revert to defaults + elsif ( $default_adjust_indentation == 0 ) { + $indentation = $leading_spaces_beg; } + elsif ( $default_adjust_indentation == 1 ) { + $indentation = $reduced_spaces_to_go[$i_terminal]; + $lev = $levels_to_go[$i_terminal]; + } + } + } - if ( !$is_bli_beg && defined($K_plus) ) { - my $lev = $level_beg; - my $level_next = $rLL->[$K_plus]->[_LEVEL_]; + #------------------------------------------------------------- + # Secton 2D: adjust_indentation == 3 + # Full indentation of closing tokens (-icb and -icp or -cti=2) + #------------------------------------------------------------- + else { - # and do not undo ci if it was set by the -xci option - $adjust_indentation = 1 - if ( $level_next < $lev - && !$self->[_rseqno_controlling_my_ci_]->{$K_beg} ); - } + # handle -icb (indented closing code block braces) + # Updated method for indented block braces: indent one full level if + # there is no continuation indentation. This will occur for major + # structures such as sub, if, else, but not for things like map + # blocks. + # + # Note: only code blocks without continuation indentation are + # handled here (if, else, unless, ..). In the following snippet, + # the terminal brace of the sort block will have continuation + # indentation as shown so it will not be handled by the coding + # here. We would have to undo the continuation indentation to do + # this, but it probably looks ok as is. This is a possible future + # update for semicolon terminated lines. + # + # if ($sortby eq 'date' or $sortby eq 'size') { + # @files = sort { + # $file_data{$a}{$sortby} <=> $file_data{$b}{$sortby} + # or $a cmp $b + # } @files; + # } + # + if ( $block_type_beg + && $ci_levels_to_go[$i_terminal] == 0 ) + { + my $spaces = get_spaces( $leading_spaces_to_go[$i_terminal] ); + $indentation = $spaces + $rOpts_indent_columns; - # Patch for RT #96101, in which closing brace of anonymous subs - # was not outdented. We should look ahead and see if there is - # a level decrease at the next token (i.e., a closing token), - # but right now we do not have that information. For now - # we see if we are in a list, and this works well. - # See test files 'sub*.t' for good test cases. - if ( $terminal_is_in_list - && !$rOpts_indent_closing_brace - && $block_type_beg - && $block_type_beg =~ /$ASUB_PATTERN/ ) + # NOTE: for -lp we could create a new indentation object, but + # there is probably no need to do it + } + + # handle -icp and any -icb block braces which fall through above + # test such as the 'sort' block mentioned above. + else { + + # There are currently two ways to handle -icp... + # One way is to use the indentation of the previous line: + # $indentation = $last_indentation_written; + + # The other way is to use the indentation that the previous line + # would have had if it hadn't been adjusted: + $indentation = $last_unadjusted_indentation; + + # Current method: use the minimum of the two. This avoids + # inconsistent indentation. + if ( get_spaces($last_indentation_written) < + get_spaces($indentation) ) { - ( - $opening_indentation, $opening_offset, - $is_leading, $opening_exists - ) - = $self->get_opening_indentation( $ibeg, $ri_first, - $ri_last, $rindentation_list ); - my $indentation = $leading_spaces_beg; - if ( defined($opening_indentation) - && get_spaces($indentation) > - get_spaces($opening_indentation) ) - { - $adjust_indentation = 1; - } + $indentation = $last_indentation_written; } } - # YVES patch 1 of 2: - # Undo ci of line with leading closing eval brace, - # but not beyond the indentation of the line with - # the opening brace. - if ( $block_type_beg eq 'eval' - && !ref($leading_spaces_beg) - && !$rOpts_indent_closing_brace ) + # use previous indentation but use own level + # to cause list to be flushed properly + $lev = $level_beg; + } + + #------------------------------------------------------------- + # Remember indentation except for multi-line quotes, which get + # no indentation + #------------------------------------------------------------- + if ( !( $ibeg == 0 && $starting_in_quote ) ) { + $last_indentation_written = $indentation; + $last_unadjusted_indentation = $leading_spaces_beg; + $last_leading_token = $token_beg; + + # Patch to make a line which is the end of a qw quote work with the + # -lp option. Make $token_beg look like a closing token as some + # type even if it is not. This variable will become + # $last_leading_token at the end of this loop. Then, if the -lp + # style is selected, and the next line is also a + # closing token, it will not get more indentation than this line. + # We need to do this because qw quotes (at present) only get + # continuation indentation, not one level of indentation, so we + # need to turn off the -lp indentation. + + # ... a picture is worth a thousand words: + + # perltidy -wn -gnu (Without this patch): + # ok(defined( + # $seqio = $gb->get_Stream_by_batch([qw(J00522 AF303112 + # 2981014)]) + # )); + + # perltidy -wn -gnu (With this patch): + # ok(defined( + # $seqio = $gb->get_Stream_by_batch([qw(J00522 AF303112 + # 2981014)]) + # )); + if ( $seqno_qw_closing + && ( length($token_beg) > 1 || $token_beg eq '>' ) ) { - ( - $opening_indentation, $opening_offset, - $is_leading, $opening_exists - ) - = $self->get_opening_indentation( $ibeg, $ri_first, $ri_last, - $rindentation_list ); - my $indentation = $leading_spaces_beg; - if ( defined($opening_indentation) - && get_spaces($indentation) > - get_spaces($opening_indentation) ) - { - $adjust_indentation = 1; - } + $last_leading_token = ')'; } + } - # patch for issue git #40: -bli setting has priority - $adjust_indentation = 0 if ($is_bli_beg); + #--------------------------------------------------------------------- + # Rule: lines with leading closing tokens should not be outdented more + # than the line which contained the corresponding opening token. + #--------------------------------------------------------------------- - $default_adjust_indentation = $adjust_indentation; + # Updated per bug report in alex_bug.pl: we must not + # mess with the indentation of closing logical braces, so + # we must treat something like '} else {' as if it were + # an isolated brace + my $is_isolated_block_brace = $block_type_beg + && ( $i_terminal == $ibeg + || $is_if_elsif_else_unless_while_until_for_foreach{$block_type_beg} + ); - # Now modify default behavior according to user request: - # handle option to indent non-blocks of the form ); }; ]; - # But don't do special indentation to something like ')->pack(' - if ( !$block_type_beg ) { + # only do this for a ':; which is aligned with its leading '?' + my $is_unaligned_colon = $type_beg eq ':' && !$is_leading; - # Note that logical padding has already been applied, so we may - # need to remove some spaces to get a valid hash key. - my $tok = $token_beg; - my $cti = $closing_token_indentation{$tok}; + if ( + defined($opening_indentation) + && !$leading_paren_arrow # MOJO patch + && !$is_isolated_block_brace + && !$is_unaligned_colon + ) + { + if ( get_spaces($opening_indentation) > get_spaces($indentation) ) { + $indentation = $opening_indentation; + } + } - # Fix the value of 'cti' for an isolated non-welded closing qw - # delimiter. - if ( $seqno_qw_closing && $ibeg_weld_fix == $ibeg ) { + #---------------------------------------------------- + # remember the indentation of each line of this batch + #---------------------------------------------------- + push @{$rindentation_list}, $indentation; - # A quote delimiter which is not a container will not have - # a cti value defined. In this case use the style of a - # paren. For example - # my @fars = ( - # qw< - # far - # farfar - # farfars-far - # >, - # ); - if ( !defined($cti) && length($tok) == 1 ) { + #--------------------------------------------- + # outdent lines with certain leading tokens... + #--------------------------------------------- + if ( - # something other than ')', '}', ']' ; use flag for ')' - $cti = $closing_token_indentation{')'}; + # must be first word of this batch + $ibeg == 0 - # But for now, do not outdent non-container qw - # delimiters because it would would change existing - # formatting. - if ( $tok ne '>' ) { $cti = 3 } - } + # and ... + && ( - # A non-welded closing qw cannot currently use -cti=1 - # because that option requires a sequence number to find - # the opening indentation, and qw quote delimiters are not - # sequenced items. - if ( defined($cti) && $cti == 1 ) { $cti = 0 } - } + # certain leading keywords if requested + $rOpts_outdent_keywords + && $type_beg eq 'k' + && $outdent_keyword{$token_beg} - if ( !defined($cti) ) { + # or labels if requested + || $rOpts_outdent_labels && $type_beg eq 'J' - # $cti may not be defined for several reasons. - # -padding may have been applied so the character - # has a length > 1 - # - we may have welded to a closing quote token. - # Here is an example (perltidy -wn): - # __PACKAGE__->load_components( qw( - # > Core - # > - # > ) ); - $adjust_indentation = 0; + # or static block comments if requested + || $is_static_block_comment + && $rOpts_outdent_static_block_comments + ) + ) + { + my $space_count = leading_spaces_to_go($ibeg); + if ( $space_count > 0 ) { + $space_count -= $rOpts_continuation_indentation; + $is_outdented_line = 1; + if ( $space_count < 0 ) { $space_count = 0 } + # do not promote a spaced static block comment to non-spaced; + # this is not normally necessary but could be for some + # unusual user inputs (such as -ci = -i) + if ( $type_beg eq '#' && $space_count == 0 ) { + $space_count = 1; } - elsif ( $cti == 1 ) { - if ( $i_terminal <= $ibeg + 1 - || $is_semicolon_terminated ) - { - $adjust_indentation = 2; - } - else { - $adjust_indentation = 0; - } - } - elsif ( $cti == 2 ) { - if ($is_semicolon_terminated) { - $adjust_indentation = 3; - } - else { - $adjust_indentation = 0; - } - } - elsif ( $cti == 3 ) { - $adjust_indentation = 3; - } + + $indentation = $space_count; } + } - # handle option to indent blocks - else { - if ( - $rOpts_indent_closing_brace - && ( - $i_terminal == $ibeg # isolated terminal '}' - || $is_semicolon_terminated - ) - ) # } xxxx ; - { - $adjust_indentation = 3; + return ( $indentation, $lev, $level_end, $i_terminal, + $is_outdented_line ); + } ## end sub get_final_indentation + + sub get_closing_token_indentation { + + # Determine indentation adjustment for a line with a leading closing + # token - i.e. one of these: ) ] } : + + my ( + $self, + + $ibeg, + $iend, + $ri_first, + $ri_last, + $rindentation_list, + $level_jump, + $i_terminal, + $is_semicolon_terminated, + $seqno_qw_closing, + + ) = @_; + + my $adjust_indentation = 0; + my $default_adjust_indentation = $adjust_indentation; + my $terminal_type = $types_to_go[$i_terminal]; + + my $type_beg = $types_to_go[$ibeg]; + my $token_beg = $tokens_to_go[$ibeg]; + my $level_beg = $levels_to_go[$ibeg]; + my $block_type_beg = $block_type_to_go[$ibeg]; + my $leading_spaces_beg = $leading_spaces_to_go[$ibeg]; + my $seqno_beg = $type_sequence_to_go[$ibeg]; + my $is_closing_type_beg = $is_closing_type{$type_beg}; + + my ( + $opening_indentation, $opening_offset, + $is_leading, $opening_exists + ); + + # Honor any flag to reduce -ci set by the -bbxi=n option + if ( $seqno_beg && $self->[_rwant_reduced_ci_]->{$seqno_beg} ) { + + # if this is an opening, it must be alone on the line ... + if ( $is_closing_type{$type_beg} || $ibeg == $i_terminal ) { + $adjust_indentation = 1; + } + + # ... or a single welded unit (fix for b1173) + elsif ($total_weld_count) { + my $K_beg = $K_to_go[$ibeg]; + my $Kterm = $K_to_go[$i_terminal]; + my $Kterm_test = $self->[_rK_weld_left_]->{$Kterm}; + if ( defined($Kterm_test) && $Kterm_test >= $K_beg ) { + $Kterm = $Kterm_test; } + if ( $Kterm == $K_beg ) { $adjust_indentation = 1 } } - } ## end if ( $is_closing_type_beg || $seqno_qw_closing ) + } - # if at ');', '};', '>;', and '];' of a terminal qw quote - elsif ( - substr( $rpatterns->[0], 0, 2 ) eq 'qb' - && substr( $rfields->[0], -1, 1 ) eq ';' - ##&& $rpatterns->[0] =~ /^qb*;$/ - && $rfields->[0] =~ /^([\)\}\]\>]);$/ - ) - { - if ( $closing_token_indentation{$1} == 0 ) { - $adjust_indentation = 1; + my $ris_bli_container = $self->[_ris_bli_container_]; + my $is_bli_beg = $seqno_beg ? $ris_bli_container->{$seqno_beg} : 0; + + # Update the $is_bli flag as we go. It is initially 1. + # We note seeing a leading opening brace by setting it to 2. + # If we get to the closing brace without seeing the opening then we + # turn it off. This occurs if the opening brace did not get output + # at the start of a line, so we will then indent the closing brace + # in the default way. + if ( $is_bli_beg && $is_bli_beg == 1 ) { + my $K_opening_container = $self->[_K_opening_container_]; + my $K_opening = $K_opening_container->{$seqno_beg}; + my $K_beg = $K_to_go[$ibeg]; + if ( $K_beg eq $K_opening ) { + $ris_bli_container->{$seqno_beg} = $is_bli_beg = 2; } - else { - $adjust_indentation = 3; + else { $is_bli_beg = 0 } + } + + # QW PATCH for the combination -lp -wn + # For -lp formatting use $ibeg_weld_fix to get around the problem + # that with -lp type formatting the opening and closing tokens to not + # have sequence numbers. + my $ibeg_weld_fix = $ibeg; + if ( $seqno_qw_closing && $total_weld_count ) { + my $i_plus = $inext_to_go[$ibeg]; + if ( $i_plus <= $max_index_to_go ) { + my $K_plus = $K_to_go[$i_plus]; + if ( defined( $self->[_rK_weld_left_]->{$K_plus} ) ) { + $ibeg_weld_fix = $i_plus; + } } } - # if line begins with a ':', align it with any - # previous line leading with corresponding ? - elsif ( $type_beg eq ':' ) { + # if we are at a closing token of some type.. + if ( $is_closing_type_beg || $seqno_qw_closing ) { + + my $K_beg = $K_to_go[$ibeg]; + + # get the indentation of the line containing the corresponding + # opening token ( $opening_indentation, $opening_offset, $is_leading, $opening_exists ) - = $self->get_opening_indentation( $ibeg, $ri_first, $ri_last, - $rindentation_list ); - if ($is_leading) { $adjust_indentation = 2; } - } + = $self->get_opening_indentation( $ibeg_weld_fix, $ri_first, + $ri_last, $rindentation_list, $seqno_qw_closing ); - #--------------------------------------------------------- - # Section 2: set indentation according to flag set above - # - # Select the indentation object to define leading - # whitespace. If we are outdenting something like '} } );' - # then we want to use one level below the last token - # ($i_terminal) in order to get it to fully outdent through - # all levels. - #--------------------------------------------------------- - my $indentation; - my $lev; - my $level_end = $levels_to_go[$iend]; + my $terminal_is_in_list = $self->is_in_list_by_i($i_terminal); - if ( $adjust_indentation == 0 ) { - $indentation = $leading_spaces_beg; - $lev = $level_beg; - } - elsif ( $adjust_indentation == 1 ) { + # First set the default behavior: + if ( - # Change the indentation to be that of a different token on the line - # Previously, the indentation of the terminal token was used: - # OLD CODING: - # $indentation = $reduced_spaces_to_go[$i_terminal]; - # $lev = $levels_to_go[$i_terminal]; + # default behavior is to outdent closing lines + # of the form: "); }; ]; )->xxx;" + $is_semicolon_terminated - # Generalization for MOJO patch: - # Use the lowest level indentation of the tokens on the line. - # For example, here we can use the indentation of the ending ';': - # } until ($selection > 0 and $selection < 10); # ok to use ';' - # But this will not outdent if we use the terminal indentation: - # )->then( sub { # use indentation of the ->, not the { - # Warning: reduced_spaces_to_go[] may be a reference, do not - # do numerical checks with it + # and 'cuddled parens' of the form: ")->pack(" + # Bug fix for RT #123749]: the types here were + # incorrectly '(' and ')'. Corrected to be '{' and '}' + || ( + $terminal_type eq '{' + && $type_beg eq '}' + && ( $nesting_depth_to_go[$iend] + 1 == + $nesting_depth_to_go[$ibeg] ) + ) - my $i_ind = $ibeg; - $indentation = $reduced_spaces_to_go[$i_ind]; - $lev = $levels_to_go[$i_ind]; - while ( $i_ind < $i_terminal ) { - $i_ind++; - if ( $levels_to_go[$i_ind] < $lev ) { - $indentation = $reduced_spaces_to_go[$i_ind]; - $lev = $levels_to_go[$i_ind]; - } - } - } + # remove continuation indentation for any line like + # } ... { + # or without ending '{' and unbalanced, such as + # such as '}->{$operator}' + || ( + $type_beg eq '}' - # handle indented closing token which aligns with opening token - elsif ( $adjust_indentation == 2 ) { + && ( $types_to_go[$iend] eq '{' + || $levels_to_go[$iend] < $level_beg ) + ) - # handle option to align closing token with opening token - $lev = $level_beg; + # and when the next line is at a lower indentation level... - # calculate spaces needed to align with opening token - my $space_count = - get_spaces($opening_indentation) + $opening_offset; + # PATCH #1: and only if the style allows undoing continuation + # for all closing token types. We should really wait until + # the indentation of the next line is known and then make + # a decision, but that would require another pass. - # Indent less than the previous line. - # - # Problem: For -lp we don't exactly know what it was if there - # were recoverable spaces sent to the aligner. A good solution - # would be to force a flush of the vertical alignment buffer, so - # that we would know. For now, this rule is used for -lp: - # - # When the last line did not start with a closing token we will - # be optimistic that the aligner will recover everything wanted. - # - # This rule will prevent us from breaking a hierarchy of closing - # tokens, and in a worst case will leave a closing paren too far - # indented, but this is better than frequently leaving it not - # indented enough. - my $last_spaces = get_spaces($last_indentation_written); + # PATCH #2: and not if this token is under -xci control + || ( $level_jump < 0 + && !$some_closing_token_indentation + && !$self->[_rseqno_controlling_my_ci_]->{$K_beg} ) - if ( ref($last_indentation_written) - && !$is_closing_token{$last_leading_token} ) - { - $last_spaces += - get_recoverable_spaces($last_indentation_written); - } + # Patch for -wn=2, multiple welded closing tokens + || ( $i_terminal > $ibeg + && $is_closing_type{ $types_to_go[$iend] } ) - # reset the indentation to the new space count if it works - # only options are all or none: nothing in-between looks good - $lev = $level_beg; + # Alternate Patch for git #51, isolated closing qw token not + # outdented if no-delete-old-newlines is set. This works, but + # a more general patch elsewhere fixes the real problem: ljump. + # || ( $seqno_qw_closing && $ibeg == $i_terminal ) - my $diff = $last_spaces - $space_count; - if ( $diff > 0 ) { - $indentation = $space_count; + ) + { + $adjust_indentation = 1; } - else { - # We need to fix things ... but there is no good way to do it. - # The best solution is for the user to use a longer maximum - # line length. We could get a smooth variation if we just move - # the paren in using - # $space_count -= ( 1 - $diff ); - # But unfortunately this can give a rather unbalanced look. + # outdent something like '),' + if ( + $terminal_type eq ',' - # For -xlp we currently allow a tolerance of one indentation - # level and then revert to a simpler default. This will jump - # suddenly but keeps a balanced look. - if ( $rOpts_extended_line_up_parentheses - && $diff >= -$rOpts_indent_columns - && $space_count > $leading_spaces_beg ) - { - $indentation = $space_count; - } + # Removed this constraint for -wn + # OLD: allow just one character before the comma + # && $i_terminal == $ibeg + 1 - # Otherwise revert to defaults - elsif ( $default_adjust_indentation == 0 ) { - $indentation = $leading_spaces_beg; - } - elsif ( $default_adjust_indentation == 1 ) { - $indentation = $reduced_spaces_to_go[$i_terminal]; - $lev = $levels_to_go[$i_terminal]; - } + # require LIST environment; otherwise, we may outdent too much - + # this can happen in calls without parentheses (overload.t); + && $terminal_is_in_list + ) + { + $adjust_indentation = 1; } - } - - # Full indentation of closing tokens (-icb and -icp or -cti=2) - else { - # handle -icb (indented closing code block braces) - # Updated method for indented block braces: indent one full level if - # there is no continuation indentation. This will occur for major - # structures such as sub, if, else, but not for things like map - # blocks. - # - # Note: only code blocks without continuation indentation are - # handled here (if, else, unless, ..). In the following snippet, - # the terminal brace of the sort block will have continuation - # indentation as shown so it will not be handled by the coding - # here. We would have to undo the continuation indentation to do - # this, but it probably looks ok as is. This is a possible future - # update for semicolon terminated lines. - # - # if ($sortby eq 'date' or $sortby eq 'size') { - # @files = sort { - # $file_data{$a}{$sortby} <=> $file_data{$b}{$sortby} - # or $a cmp $b - # } @files; - # } - # - if ( $block_type_beg - && $ci_levels_to_go[$i_terminal] == 0 ) + # undo continuation indentation of a terminal closing token if + # it is the last token before a level decrease. This will allow + # a closing token to line up with its opening counterpart, and + # avoids an indentation jump larger than 1 level. + my $rLL = $self->[_rLL_]; + my $Klimit = $self->[_Klimit_]; + if ( $i_terminal == $ibeg + && $is_closing_type_beg + && defined($K_beg) + && $K_beg < $Klimit ) { - my $spaces = get_spaces( $leading_spaces_to_go[$i_terminal] ); - $indentation = $spaces + $rOpts_indent_columns; + my $K_plus = $K_beg + 1; + my $type_plus = $rLL->[$K_plus]->[_TYPE_]; - # NOTE: for -lp we could create a new indentation object, but - # there is probably no need to do it - } + if ( $type_plus eq 'b' && $K_plus < $Klimit ) { + $type_plus = $rLL->[ ++$K_plus ]->[_TYPE_]; + } - # handle -icp and any -icb block braces which fall through above - # test such as the 'sort' block mentioned above. - else { + if ( $type_plus eq '#' && $K_plus < $Klimit ) { + $type_plus = $rLL->[ ++$K_plus ]->[_TYPE_]; + if ( $type_plus eq 'b' && $K_plus < $Klimit ) { + $type_plus = $rLL->[ ++$K_plus ]->[_TYPE_]; + } + + # Note: we have skipped past just one comment (perhaps a + # side comment). There could be more, and we could easily + # skip past all the rest with the following code, or with a + # while loop. It would be rare to have to do this, and + # those block comments would still be indented, so it would + # to leave them indented. So it seems best to just stop at + # a maximum of one comment. + ##if ($type_plus eq '#') { + ## $K_plus = $self->K_next_code($K_plus); + ##} + } - # There are currently two ways to handle -icp... - # One way is to use the indentation of the previous line: - # $indentation = $last_indentation_written; + if ( !$is_bli_beg && defined($K_plus) ) { + my $lev = $level_beg; + my $level_next = $rLL->[$K_plus]->[_LEVEL_]; - # The other way is to use the indentation that the previous line - # would have had if it hadn't been adjusted: - $indentation = $last_unadjusted_indentation; + # and do not undo ci if it was set by the -xci option + $adjust_indentation = 1 + if ( $level_next < $lev + && !$self->[_rseqno_controlling_my_ci_]->{$K_beg} ); + } - # Current method: use the minimum of the two. This avoids - # inconsistent indentation. - if ( get_spaces($last_indentation_written) < - get_spaces($indentation) ) + # Patch for RT #96101, in which closing brace of anonymous subs + # was not outdented. We should look ahead and see if there is + # a level decrease at the next token (i.e., a closing token), + # but right now we do not have that information. For now + # we see if we are in a list, and this works well. + # See test files 'sub*.t' for good test cases. + if ( $terminal_is_in_list + && !$rOpts_indent_closing_brace + && $block_type_beg + && $block_type_beg =~ /$ASUB_PATTERN/ ) { - $indentation = $last_indentation_written; + ( + $opening_indentation, $opening_offset, + $is_leading, $opening_exists + ) + = $self->get_opening_indentation( $ibeg, $ri_first, + $ri_last, $rindentation_list ); + my $indentation = $leading_spaces_beg; + if ( defined($opening_indentation) + && get_spaces($indentation) > + get_spaces($opening_indentation) ) + { + $adjust_indentation = 1; + } } } - # use previous indentation but use own level - # to cause list to be flushed properly - $lev = $level_beg; - } - - # remember indentation except for multi-line quotes, which get - # no indentation - unless ( $ibeg == 0 && $starting_in_quote ) { - $last_indentation_written = $indentation; - $last_unadjusted_indentation = $leading_spaces_beg; - $last_leading_token = $token_beg; - - # Patch to make a line which is the end of a qw quote work with the - # -lp option. Make $token_beg look like a closing token as some - # type even if it is not. This variable will become - # $last_leading_token at the end of this loop. Then, if the -lp - # style is selected, and the next line is also a - # closing token, it will not get more indentation than this line. - # We need to do this because qw quotes (at present) only get - # continuation indentation, not one level of indentation, so we - # need to turn off the -lp indentation. - - # ... a picture is worth a thousand words: - - # perltidy -wn -gnu (Without this patch): - # ok(defined( - # $seqio = $gb->get_Stream_by_batch([qw(J00522 AF303112 - # 2981014)]) - # )); - - # perltidy -wn -gnu (With this patch): - # ok(defined( - # $seqio = $gb->get_Stream_by_batch([qw(J00522 AF303112 - # 2981014)]) - # )); - if ( $seqno_qw_closing - && ( length($token_beg) > 1 || $token_beg eq '>' ) ) + # YVES patch 1 of 2: + # Undo ci of line with leading closing eval brace, + # but not beyond the indentation of the line with + # the opening brace. + if ( $block_type_beg eq 'eval' + && !ref($leading_spaces_beg) + && !$rOpts_indent_closing_brace ) { - $last_leading_token = ')'; + ( + $opening_indentation, $opening_offset, + $is_leading, $opening_exists + ) + = $self->get_opening_indentation( $ibeg, $ri_first, $ri_last, + $rindentation_list ); + my $indentation = $leading_spaces_beg; + if ( defined($opening_indentation) + && get_spaces($indentation) > + get_spaces($opening_indentation) ) + { + $adjust_indentation = 1; + } } - } - # be sure lines with leading closing tokens are not outdented more - # than the line which contained the corresponding opening token. + # patch for issue git #40: -bli setting has priority + $adjust_indentation = 0 if ($is_bli_beg); - #-------------------------------------------------------- - # updated per bug report in alex_bug.pl: we must not - # mess with the indentation of closing logical braces so - # we must treat something like '} else {' as if it were - # an isolated brace - #-------------------------------------------------------- - my $is_isolated_block_brace = $block_type_beg - && ( $i_terminal == $ibeg - || $is_if_elsif_else_unless_while_until_for_foreach{$block_type_beg} - ); + $default_adjust_indentation = $adjust_indentation; - # only do this for a ':; which is aligned with its leading '?' - my $is_unaligned_colon = $type_beg eq ':' && !$is_leading; + # Now modify default behavior according to user request: + # handle option to indent non-blocks of the form ); }; ]; + # But don't do special indentation to something like ')->pack(' + if ( !$block_type_beg ) { - if ( - defined($opening_indentation) - && !$leading_paren_arrow # MOJO patch - && !$is_isolated_block_brace - && !$is_unaligned_colon - ) - { - if ( get_spaces($opening_indentation) > get_spaces($indentation) ) { - $indentation = $opening_indentation; - } - } + # Note that logical padding has already been applied, so we may + # need to remove some spaces to get a valid hash key. + my $tok = $token_beg; + my $cti = $closing_token_indentation{$tok}; - # remember the indentation of each line of this batch - push @{$rindentation_list}, $indentation; + # Fix the value of 'cti' for an isolated non-welded closing qw + # delimiter. + if ( $seqno_qw_closing && $ibeg_weld_fix == $ibeg ) { - # outdent lines with certain leading tokens... - if ( + # A quote delimiter which is not a container will not have + # a cti value defined. In this case use the style of a + # paren. For example + # my @fars = ( + # qw< + # far + # farfar + # farfars-far + # >, + # ); + if ( !defined($cti) && length($tok) == 1 ) { - # must be first word of this batch - $ibeg == 0 + # something other than ')', '}', ']' ; use flag for ')' + $cti = $closing_token_indentation{')'}; - # and ... - && ( + # But for now, do not outdent non-container qw + # delimiters because it would would change existing + # formatting. + if ( $tok ne '>' ) { $cti = 3 } + } - # certain leading keywords if requested - $rOpts_outdent_keywords - && $type_beg eq 'k' - && $outdent_keyword{$token_beg} + # A non-welded closing qw cannot currently use -cti=1 + # because that option requires a sequence number to find + # the opening indentation, and qw quote delimiters are not + # sequenced items. + if ( defined($cti) && $cti == 1 ) { $cti = 0 } + } - # or labels if requested - || $rOpts_outdent_labels && $type_beg eq 'J' + if ( !defined($cti) ) { - # or static block comments if requested - || $is_static_block_comment - && $rOpts_outdent_static_block_comments - ) - ) - { - my $space_count = leading_spaces_to_go($ibeg); - if ( $space_count > 0 ) { - $space_count -= $rOpts_continuation_indentation; - $is_outdented_line = 1; - if ( $space_count < 0 ) { $space_count = 0 } + # $cti may not be defined for several reasons. + # -padding may have been applied so the character + # has a length > 1 + # - we may have welded to a closing quote token. + # Here is an example (perltidy -wn): + # __PACKAGE__->load_components( qw( + # > Core + # > + # > ) ); + $adjust_indentation = 0; - # do not promote a spaced static block comment to non-spaced; - # this is not normally necessary but could be for some - # unusual user inputs (such as -ci = -i) - if ( $type_beg eq '#' && $space_count == 0 ) { - $space_count = 1; } + elsif ( $cti == 1 ) { + if ( $i_terminal <= $ibeg + 1 + || $is_semicolon_terminated ) + { + $adjust_indentation = 2; + } + else { + $adjust_indentation = 0; + } + } + elsif ( $cti == 2 ) { + if ($is_semicolon_terminated) { + $adjust_indentation = 3; + } + else { + $adjust_indentation = 0; + } + } + elsif ( $cti == 3 ) { + $adjust_indentation = 3; + } + } - $indentation = $space_count; + # handle option to indent blocks + else { + if ( + $rOpts_indent_closing_brace + && ( + $i_terminal == $ibeg # isolated terminal '}' + || $is_semicolon_terminated + ) + ) # } xxxx ; + { + $adjust_indentation = 3; + } } + } ## end if ( $is_closing_type_beg || $seqno_qw_closing ) + + # if line begins with a ':', align it with any + # previous line leading with corresponding ? + elsif ( $type_beg eq ':' ) { + ( + $opening_indentation, $opening_offset, + $is_leading, $opening_exists + ) + = $self->get_opening_indentation( $ibeg, $ri_first, $ri_last, + $rindentation_list ); + if ($is_leading) { $adjust_indentation = 2; } } - return ( $indentation, $lev, $level_end, $i_terminal, - $is_outdented_line ); - } ## end sub get_final_indentation + return ( $adjust_indentation, $default_adjust_indentation, + $opening_indentation, $opening_offset, + $is_leading, $opening_exists ); + } + } ## end closure get_final_indentation sub get_opening_indentation { -- 2.39.5