From 065561e460d624d3b4487dfc5b15fc0f171d343c Mon Sep 17 00:00:00 2001 From: Steve Hancock Date: Mon, 31 Aug 2020 17:52:45 -0700 Subject: [PATCH] added option --non-indenting-braces --- CHANGES.md | 6 ++ bin/perltidy | 39 +++++++++ lib/Perl/Tidy.pm | 3 + lib/Perl/Tidy/Formatter.pm | 133 ++++++++++++++++++++++++++++--- lib/Perl/Tidy/VerticalAligner.pm | 3 +- t/snippets/expect/nib.def | 16 ++++ t/snippets/expect/nib.nib1 | 16 ++++ t/snippets/expect/nib.nib2 | 16 ++++ t/snippets/nib.in | 16 ++++ t/snippets/nib1.par | 1 + t/snippets/nib2.par | 1 + t/snippets/packing_list.txt | 3 + t/snippets21.t | 99 ++++++++++++++++++++++- 13 files changed, 340 insertions(+), 12 deletions(-) create mode 100644 t/snippets/expect/nib.def create mode 100644 t/snippets/expect/nib.nib1 create mode 100644 t/snippets/expect/nib.nib2 create mode 100644 t/snippets/nib.in create mode 100644 t/snippets/nib1.par create mode 100644 t/snippets/nib2.par diff --git a/CHANGES.md b/CHANGES.md index d7c49749..2d49007c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Perltidy Change Log + - Added parameter --non-indenting-braces, or -nib, which prevents + code following an opening brace marked with a special side comment + from indenting one level. This is off by default and turned on + with -nib. It might be useful for preventing code from shifting + when adding or testing closures, for example. + - Side comment locations reset at a line ending in a level 0 open block, such as when a new multi-line sub begins. This is intended to help keep side comments from drifting to far to the right. diff --git a/bin/perltidy b/bin/perltidy index 8d345f49..33383d8b 100755 --- a/bin/perltidy +++ b/bin/perltidy @@ -847,6 +847,45 @@ terminates a code block . For example, The default is not to do this, indicated by B<-nicb>. + +=item B<-nib>, B<--non-indenting-braces> + +If this flag is set, perltidy will look for opening block braces which are +followed by a special side comment, which is B<#<<<> by default. If found, the +code between this opening brace and its corresponding closing brace will not be +given the normal extra indentation level. As a simple example, + + perltidy -nib: + + { #<<< + print "hello world\n"; + } + +This might be useful, for example, to keep large sections of code from +moving to the right when placed in a closure. + +Only the opening brace needs to be marked, and nested braces can be marked. +The special side comment can be changed with the next parameter. + + +=item B<-nibp=s>, B<--non-indenting-brace-prefix=s> + +The B<-nibp=string> parameter may be used to change the marker for +non-indenting braces. The default is equivalent to -nibp='#<<<'. The string +that you enter must begin with a # and should be in quotes as necessary to get +past the command shell of your system. This string is the leading text of a +regex pattern that is constructed by appending pre-pending a '^', so you must +also include backslashes for characters to be taken literally rather than as +patterns. + +For example, to match the side comment '#++', the parameter would be + + -nibp='#\+\+' + +Note that the default string is the same as the string for starting a +B section, but it is for a side-comment rather than a block +comment. + =item B<-olq>, B<--outdent-long-quotes> When B<-olq> is set, lines which is a quoted string longer than the diff --git a/lib/Perl/Tidy.pm b/lib/Perl/Tidy.pm index f090b69e..2f52af0a 100644 --- a/lib/Perl/Tidy.pm +++ b/lib/Perl/Tidy.pm @@ -2189,6 +2189,8 @@ sub generate_options { $add_option->( 'indent-spaced-block-comments', 'isbc', '!' ); $add_option->( 'fixed-position-side-comment', 'fpsc', '=i' ); $add_option->( 'minimum-space-to-comment', 'msc', '=i' ); + $add_option->( 'non-indenting-braces', 'nib', '!' ); + $add_option->( 'non-indenting-brace-prefix', 'nibp', '=s' ); $add_option->( 'outdent-long-comments', 'olc', '!' ); $add_option->( 'outdent-static-block-comments', 'osbc', '!' ); $add_option->( 'static-block-comment-prefix', 'sbcp', '=s' ); @@ -2463,6 +2465,7 @@ sub generate_options { noquiet noshow-options nostatic-side-comments + nonon-indenting-braces notabs nowarning-output one-line-block-semicolons=1 diff --git a/lib/Perl/Tidy/Formatter.pm b/lib/Perl/Tidy/Formatter.pm index 268b39b4..01dd4616 100644 --- a/lib/Perl/Tidy/Formatter.pm +++ b/lib/Perl/Tidy/Formatter.pm @@ -190,6 +190,8 @@ use vars qw{ $format_skipping_pattern_begin $format_skipping_pattern_end + $non_indenting_brace_pattern + $bli_pattern $block_brace_vertical_tightness_pattern @@ -205,10 +207,9 @@ use vars qw{ }; -################################################################### -# Section 2: Global variables which relate to an individual script. -# These are work arrays for the current batch of tokens. -################################################################### +######################################################### +# Section 2: Work arrays for the current batch of tokens. +######################################################### use vars qw{ $max_index_to_go @@ -4574,21 +4575,96 @@ sub weld_len_right_to_go { return $weld_len; } +sub adjust_indentation_levels { + + my ($self) = @_; + + # Set adjusted levels for any non-indenting braces + $self->non_indenting_braces(); + + # Set adjusted levels for the whitespace cycle option + $self->whitespace_cycle_adjustment(); + +} + +sub non_indenting_braces { + + # remove indentation within marked braces if requested + my ($self) = @_; + return unless ( $rOpts->{'non-indenting-braces'} ); + + my $rLL = $self->[_rLL_]; + return unless ( defined($rLL) && @{$rLL} ); + + my $radjusted_levels; + my $Kmax = @{$rLL} - 1; + my @seqno_stack; + + my $is_non_indenting_brace = sub { + my ($KK) = @_; + my $token = $rLL->[$KK]->[_TOKEN_]; + my $block_type = $rLL->[$KK]->[_BLOCK_TYPE_]; + return unless ( $token eq '{' && $block_type ); + my $line_index = $rLL->[$KK]->[_LINE_INDEX_]; + my $K_sc = $self->K_next_nonblank($KK); + return unless defined($K_sc); + my $line_index_sc = $rLL->[$K_sc]->[_LINE_INDEX_]; + return unless ( $line_index_sc == $line_index ); + my $type_sc = $rLL->[$K_sc]->[_TYPE_]; + return unless ( $type_sc eq '#' ); + my $token_sc = $rLL->[$K_sc]->[_TOKEN_]; + return ( $token_sc =~ /$non_indenting_brace_pattern/ ); + }; + + foreach my $KK ( 0 .. $Kmax ) { + my $seqno = $rLL->[$KK]->[_TYPE_SEQUENCE_]; + my $level_abs = $rLL->[$KK]->[_LEVEL_]; + my $level = $level_abs; + my $num = @seqno_stack; + if ($seqno) { + my $token = $rLL->[$KK]->[_TOKEN_]; + if ( $token eq '{' && $is_non_indenting_brace->($KK) ) { + push @seqno_stack, $seqno; + } + if ( $token eq '}' && @seqno_stack && $seqno_stack[-1] == $seqno ) { + pop @seqno_stack; + $num -= 1; + } + } + if ($num) { $level -= $num } + $radjusted_levels->[$KK] = $level; + } + $self->[_radjusted_levels_] = $radjusted_levels; + return; +} + sub whitespace_cycle_adjustment { my $self = shift; my $rLL = $self->[_rLL_]; return unless ( defined($rLL) && @{$rLL} ); - my $radjusted_levels; + my $radjusted_levels = $self->[_radjusted_levels_]; + my $rOpts_whitespace_cycle = $rOpts->{'whitespace-cycle'}; if ( $rOpts_whitespace_cycle && $rOpts_whitespace_cycle > 0 ) { + + my $Kmax = @{$rLL} - 1; + + # We have to start with any existing adjustments + my $adjusted_levels_defined = + defined($radjusted_levels) && @{$radjusted_levels} == @{$rLL}; + if ( !$adjusted_levels_defined ) { + foreach my $KK ( 0 .. $Kmax ) { + $radjusted_levels->[$KK] = $rLL->[$KK]->[_LEVEL_]; + } + } + my $whitespace_last_level = -1; my @whitespace_level_stack = (); my $last_nonblank_type = 'b'; my $last_nonblank_token = ''; - my $Kmax = @{$rLL} - 1; foreach my $KK ( 0 .. $Kmax ) { - my $level_abs = $rLL->[$KK]->[_LEVEL_]; + my $level_abs = $radjusted_levels->[$KK]; ##$rLL->[$KK]->[_LEVEL_]; my $level = $level_abs; if ( $level_abs < $whitespace_last_level ) { pop(@whitespace_level_stack); @@ -4890,8 +4966,7 @@ sub finish_formatting { # Locate small nested blocks which should not be broken $self->mark_short_nested_blocks(); - # Set adjusted levels for the whitespace cycle option - $self->whitespace_cycle_adjustment(); + $self->adjust_indentation_levels(); # Adjust continuation indentation if -bli is set $self->bli_adjustment(); @@ -5078,8 +5153,13 @@ sub get_available_spaces_to_go { # Adjust levels if necessary to recycle whitespace: my $level = $level_abs; my $radjusted_levels = $self->[_radjusted_levels_]; + my $nK = @{$rLL}; + my $nws = @{$radjusted_levels}; if ( defined($radjusted_levels) && @{$radjusted_levels} == @{$rLL} ) { $level = $radjusted_levels->[$Kj]; + + # negative levels can occure in bad files + if ( $level < 0 ) { $level = 0 } } # The continued_quote flag means that this is the first token of a @@ -5810,6 +5890,7 @@ sub check_options { make_format_skipping_pattern( 'format-skipping-begin', '#<<<' ); $format_skipping_pattern_end = make_format_skipping_pattern( 'format-skipping-end', '#>>>' ); + make_non_indenting_brace_pattern(); # If closing side comments ARE selected, then we can safely # delete old closing side comments unless closing side comment @@ -6455,6 +6536,29 @@ sub make_format_skipping_pattern { return $pattern; } +sub make_non_indenting_brace_pattern { + + # create the pattern used to identify static side comments + $non_indenting_brace_pattern = '^#<<<'; + + # allow the user to change it + if ( $rOpts->{'non-indenting-brace-prefix'} ) { + my $prefix = $rOpts->{'non-indenting-brace-prefix'}; + $prefix =~ s/^\s*//; + if ( $prefix !~ /^#/ ) { + Die("ERROR: the -nibp parameter '$prefix' must begin with '#'\n"); + } + my $pattern = '^' . $prefix; + if ( bad_pattern($pattern) ) { + Die( +"ERROR: the -nibp prefix '$prefix' causes the invalid regex '$pattern'\n" + ); + } + $non_indenting_brace_pattern = $pattern; + } + return; +} + sub make_closing_side_comment_list_pattern { # turn any input list into a regex for recognizing selected block types @@ -7403,6 +7507,9 @@ sub copy_token_as_type { my $radjusted_levels = $self->[_radjusted_levels_]; if ( defined($radjusted_levels) && @{$radjusted_levels} == @{$rLL} ) { $level_wc = $radjusted_levels->[$Ktoken_vars]; + + # negative levels can occure in bad files + if ( $level_wc < 0 ) { $level_wc = 0 } } my $space_count = $ci_level * $rOpts_continuation_indentation + @@ -10728,6 +10835,13 @@ sub send_lines_to_vertical_aligner { } } + my $level_adj = $lev; + my $radjusted_levels = $self->[_radjusted_levels_]; + if ( defined($radjusted_levels) && @{$radjusted_levels} == @{$rLL} ) { + $level_adj = $radjusted_levels->[$Kbeg]; + if ( $level_adj < 0 ) { $level_adj = 0 } + } + # add any new closing side comment to the last line if ( $closing_side_comment && $n == $n_last_line && @{$rfields} ) { $rfields->[-1] .= " $closing_side_comment"; @@ -10741,6 +10855,7 @@ sub send_lines_to_vertical_aligner { my $rvalign_hash = {}; $rvalign_hash->{level} = $lev; $rvalign_hash->{level_end} = $level_end; + $rvalign_hash->{level_adj} = $level_adj; $rvalign_hash->{indentation} = $indentation; $rvalign_hash->{is_forced_break} = $forced_breakpoint || $in_comma_list; $rvalign_hash->{outdent_long_lines} = $outdent_long_lines; diff --git a/lib/Perl/Tidy/VerticalAligner.pm b/lib/Perl/Tidy/VerticalAligner.pm index 1073eff6..7a87109f 100644 --- a/lib/Perl/Tidy/VerticalAligner.pm +++ b/lib/Perl/Tidy/VerticalAligner.pm @@ -342,6 +342,7 @@ sub valign_input { my $level = $rline_hash->{level}; my $level_end = $rline_hash->{level_end}; + my $level_adj = $rline_hash->{level_adj}; my $indentation = $rline_hash->{indentation}; my $is_forced_break = $rline_hash->{is_forced_break}; my $outdent_long_lines = $rline_hash->{outdent_long_lines}; @@ -397,7 +398,7 @@ sub valign_input { # Reset side comment location if we are entering a new block from level 0. # This is intended to keep them from drifting too far to the right. - if ( $terminal_block_type && $level == 0 && $level_end > $level ) { + if ( $terminal_block_type && $level_adj == 0 && $level_end > $level ) { $self->forget_side_comment(); } diff --git a/t/snippets/expect/nib.def b/t/snippets/expect/nib.def new file mode 100644 index 00000000..dd67a28d --- /dev/null +++ b/t/snippets/expect/nib.def @@ -0,0 +1,16 @@ +{ #<<< + { #<<< + { #++ + print "hello world\n"; + } + } +} + +{ #++ + { #++ + { #<<< + print "hello world\n"; + } + } +} + diff --git a/t/snippets/expect/nib.nib1 b/t/snippets/expect/nib.nib1 new file mode 100644 index 00000000..4499f503 --- /dev/null +++ b/t/snippets/expect/nib.nib1 @@ -0,0 +1,16 @@ +{ #<<< +{ #<<< +{ #++ + print "hello world\n"; +} +} +} + +{ #++ + { #++ + { #<<< + print "hello world\n"; + } + } +} + diff --git a/t/snippets/expect/nib.nib2 b/t/snippets/expect/nib.nib2 new file mode 100644 index 00000000..4b36c954 --- /dev/null +++ b/t/snippets/expect/nib.nib2 @@ -0,0 +1,16 @@ +{ #<<< + { #<<< + { #++ + print "hello world\n"; + } + } +} + +{ #++ +{ #++ +{ #<<< + print "hello world\n"; +} +} +} + diff --git a/t/snippets/nib.in b/t/snippets/nib.in new file mode 100644 index 00000000..4499f503 --- /dev/null +++ b/t/snippets/nib.in @@ -0,0 +1,16 @@ +{ #<<< +{ #<<< +{ #++ + print "hello world\n"; +} +} +} + +{ #++ + { #++ + { #<<< + print "hello world\n"; + } + } +} + diff --git a/t/snippets/nib1.par b/t/snippets/nib1.par new file mode 100644 index 00000000..b79e8ce6 --- /dev/null +++ b/t/snippets/nib1.par @@ -0,0 +1 @@ +-nib diff --git a/t/snippets/nib2.par b/t/snippets/nib2.par new file mode 100644 index 00000000..98452327 --- /dev/null +++ b/t/snippets/nib2.par @@ -0,0 +1 @@ +-nib -nibp='#\+\+' diff --git a/t/snippets/packing_list.txt b/t/snippets/packing_list.txt index 42cf2869..38992371 100644 --- a/t/snippets/packing_list.txt +++ b/t/snippets/packing_list.txt @@ -404,3 +404,6 @@ ../snippets9.t rt98902.def ../snippets9.t rt98902.rt98902 ../snippets9.t rt99961.def +../snippets21.t nib.def +../snippets21.t nib.nib1 +../snippets21.t nib.nib2 diff --git a/t/snippets21.t b/t/snippets21.t index a41b6967..b9436b9a 100644 --- a/t/snippets21.t +++ b/t/snippets21.t @@ -14,6 +14,9 @@ #11 git33.git33 #12 rt133130.def #13 rt133130.rt133130 +#14 nib.def +#15 nib.nib1 +#16 nib.nib2 # To locate test #13 you can search for its name or the string '#13' @@ -36,8 +39,12 @@ BEGIN { -wls='->' -wrs='->' ---------- - 'gnu' => "-gnu", - 'lop' => "-nlop", + 'gnu' => "-gnu", + 'lop' => "-nlop", + 'nib1' => "-nib", + 'nib2' => <<'----------', +-nib -nibp='#\+\+' +---------- 'rt133130' => <<'----------', # only the method should get a csc: -csc -cscl=sub -sal=method @@ -105,6 +112,25 @@ $bits = lc( $self->mime_attr('content-type') || $self->{MIH_DefaultType} || 'text/plain' ); +---------- + + 'nib' => <<'----------', +{ #<<< +{ #<<< +{ #++ + print "hello world\n"; +} +} +} + +{ #++ + { #++ + { #<<< + print "hello world\n"; + } + } +} + ---------- 'prune' => <<'----------', @@ -559,6 +585,75 @@ method sum_radlinks { } ## end sub sum_radlinks #13........... }, + + 'nib.def' => { + source => "nib", + params => "def", + expect => <<'#14...........', +{ #<<< + { #<<< + { #++ + print "hello world\n"; + } + } +} + +{ #++ + { #++ + { #<<< + print "hello world\n"; + } + } +} + +#14........... + }, + + 'nib.nib1' => { + source => "nib", + params => "nib1", + expect => <<'#15...........', +{ #<<< +{ #<<< +{ #++ + print "hello world\n"; +} +} +} + +{ #++ + { #++ + { #<<< + print "hello world\n"; + } + } +} + +#15........... + }, + + 'nib.nib2' => { + source => "nib", + params => "nib2", + expect => <<'#16...........', +{ #<<< + { #<<< + { #++ + print "hello world\n"; + } + } +} + +{ #++ +{ #++ +{ #<<< + print "hello world\n"; +} +} +} + +#16........... + }, }; my $ntests = 0 + keys %{$rtests}; -- 2.39.5