$add_option->( 'maximum-consecutive-blank-lines', 'mbl', '=i' );
$add_option->( 'keep-old-blank-lines', 'kbl', '=i' );
- $add_option->( 'blanks-after-comments', 'bac', '!' );
- $add_option->( 'keyword-group-list', 'kwgl', '=s' );
- $add_option->( 'long-keyword-group-count', 'lkwgc', '=i' );
- $add_option->( 'blanks-before-keyword-group', 'bbkwg', '=i' );
- $add_option->( 'blanks-after-keyword-group', 'bakwg', '=i' );
- $add_option->( 'blanks-inside-keyword-group', 'bikwg', '=i' );
+ $add_option->( 'blanks-after-comments', 'bac', '!' );
+ $add_option->( 'keyword-group-blanks-list', 'kgbl', '=s' );
+ $add_option->( 'keyword-group-blanks-count', 'kgbc', '=i' );
+ $add_option->( 'keyword-group-blanks-before', 'kgbb', '=i' );
+ $add_option->( 'keyword-group-blanks-after', 'kgba', '=i' );
+ $add_option->( 'keyword-group-blanks-inside', 'kgbi', '=i' );
+ $add_option->( 'keyword-group-blanks-delete', 'kgbd', '!' );
$add_option->( 'blank-lines-after-opening-block', 'blao', '=i' );
$add_option->( 'blank-lines-before-closing-block', 'blbc', '=i' );
'closing-side-comment-else-flag' => [ 0, 2 ],
'comma-arrow-breakpoints' => [ 0, 5 ],
+
+ 'keyword-group-blanks-before' => [ 0, 2 ],
+ 'keyword-group-blanks-after' => [ 0, 2 ],
+ 'keyword-group-blanks-inside' => [ 0, 1 ],
);
# Note: we could actually allow negative ci if someone really wants it:
blank-lines-before-packages=1
noblanks-after-comments
- long-keyword-group-count=5
- blanks-before-keyword-group=0
- blanks-after-keyword-group=0
- blanks-inside-keyword-group=0
+ keyword-group-blanks-count=5
+ keyword-group-blanks-before=0
+ keyword-group-blanks-after=0
+ keyword-group-blanks-inside=0
block-brace-tightness=0
block-brace-vertical-tightness=0
'noblanks-before-subs' => [qw(blbs=0 blbp=0)],
'nbbs' => [qw(blbs=0 blbp=0)],
+ 'keyword-group-blanks' => [qw(kgbb=1 kgbi=1 kgba=1)],
+ 'kgb' => [qw(kgbb=1 kgbi=1 kgba=1)],
+ 'nokeyword-group-blanks' => [qw(kgbb=0 kgbi=0 kgba=0)],
+ 'nkgb' => [qw(kgbb=0 kgbi=0 kgba=0)],
+
'break-at-old-trinary-breakpoints' => [qw(bot)],
'cti=0' => [qw(cpi=0 cbi=0 csbi=0)],
sub keyword_group_scan {
my $self = shift;
+ # Handle blank lines around keyword groups (kgb* flags)
# Scan all lines looking for runs of consecutive lines beginning with
# selected keywords. Example keywords are 'my', 'our', 'local', ... but
# they may be anything. We will set flags requesting that blanks be
# they are not necessarily well formatted.
# The output of this sub is a return hash ref whose keys are the indexes of
- # lines after which we desire a blank line.
- # $rhash_of_desires->{$i} >0 means we want a blank line after the line
- # at index i
+ # lines after which we desire a blank line. For line index i,
+ # $rhash_of_desires->{$i} = 1 means we want a blank line AFTER this line
+ # $rhash_of_desires->{$i} = 2 means we want THIS blank line removed
my $rhash_of_desires = {};
- return $rhash_of_desires
- unless ( $rOpts->{'blanks-after-keyword-group'}
- || $rOpts->{'blanks-before-keyword-group'}
- || $rOpts->{'blanks-within-keyword-group'} );
+ my $Opt_blanks_before = $rOpts->{'keyword-group-blanks-before'}; # '-kgbb'
+ my $Opt_blanks_after = $rOpts->{'keyword-group-blanks-after'}; # '-kgba'
+ my $Opt_blanks_inside = $rOpts->{'keyword-group-blanks-inside'}; # '-kgbi'
+ my $Opt_blanks_delete = $rOpts->{'keyword-group-blanks-delete'}; # '-kgbd'
+ my $Opt_long_count = $rOpts->{'keyword-group-blanks-count'}; # '-kgbc'
+ my $Opt_blanks_after_comments = $rOpts->{'blanks-after-comments'}; # '-bac'
+ my $Opt_pattern =
+ $keyword_group_list_pattern; # like '^(my|local|our|use)$';
- my $Opt_blanks_before = $rOpts->{'blanks-before-keyword-group'}; # '-bbkwg'
- my $Opt_blanks_after = $rOpts->{'blanks-after-keyword-group'}; # '-bakwg'
- my $Opt_blanks_inside = $rOpts->{'blanks-inside-keyword-group'}; # '-bikwg'
- my $Opt_long_count = $rOpts->{'long-keyword-group-count'}; # '-lkwgc'
- my $Opt_blanks_after_comments = $rOpts->{'blanks-after-comments'}; # '-bac'
- my $Opt_pattern = $keyword_group_list_pattern; # '^(my|local|our|use)$';
+ return $rhash_of_desires
+ unless ( $Opt_blanks_before
+ || $Opt_blanks_after
+ || $Opt_blanks_inside
+ || $Opt_blanks_delete );
my $rlines = $self->{rlines};
my $rLL = $self->{rLL};
my $K_closing_container = $self->{K_closing_container};
- my ( $ibeg, $iend, $count, $level_beg, $K_closing, @sublist, $K_first,
- $K_last );
+ # These vars save values for the current group and subgroups:
+ my ( $ibeg, $iend, $count, $level_beg, $K_closing, @sublist, );
- my $sub_group = sub {
+ # These vars will contain values for the most recently seen line:
+ my ( $line_type, $CODE_type, $K_first, $K_last );
- # Here we place blanks around long sub-groups of keywords
+ my $split_into_sub_groups = sub {
+
+ # Here we place blanks around long sub-groups of keywords
# if requested.
return unless ($Opt_blanks_inside);
for ( my $j = 1 ; $j < @sublist ; $j++ ) {
my $ie = $sublist[$j]->[0] - 1;
my $num = $sublist[ $j - 1 ]->[2];
- if ( $num > $Opt_long_count ) {
- $rhash_of_desires->{ $ib - 1 } = $Opt_blanks_inside
- unless $ib == $ibeg;
- $rhash_of_desires->{$ie} = $Opt_blanks_inside
- unless $ie == $iend;
+ if ( $num >= $Opt_long_count ) {
+ if ($Opt_blanks_inside) {
+ $rhash_of_desires->{ $ib - 1 } = 1 unless ( $ib == $ibeg );
+ $rhash_of_desires->{$ie} = 1 unless ( $ie == $iend );
+ }
}
$ib = $ie + 1;
}
};
- my $CODE_type;
+ my $delete_if_blank = sub {
+ my ($i) = @_;
+ return unless ( $i >= 0 && $i < @{$rlines} );
+ my $line_type = $rlines->[$i]->{_line_type};
+ return if ( $line_type ne 'CODE' );
+ my $code_type = $rlines->[$i]->{_code_type};
+ if ( $code_type eq 'BL' ) { $rhash_of_desires->{$i} = 2; }
+ return;
+ };
+
+ my $delete_inner_blank_lines = sub {
+
+ # mark blank lines for deletion if requested
+ return unless $Opt_blanks_delete;
+
+ # remove trailing blank lines from the list
+ my $i_last_nonblank = $iend;
+ for ( my $i = $iend ; $i >= $ibeg ; $i-- ) {
+ my $line_type = $rlines->[$i]->{_line_type};
+ next if ( $line_type ne 'CODE' );
+ my $code_type = $rlines->[$i]->{_code_type};
+ if ( $code_type ne 'BL' ) {
+ $i_last_nonblank = $i;
+ last;
+ }
+ }
+ $iend = $i_last_nonblank;
+
+ for ( my $i = $ibeg + 1 ; $i <= $iend - 1 ; $i++ ) {
+ $delete_if_blank->($i);
+ }
+ };
my $end_group = sub {
- # end a group of keywords
+ # end a group of keywords
my ($bad_ending) = @_;
if ( defined($ibeg) && $ibeg >= 0 ) {
+
+ # first do any blank deletions regardless of the count
+ $delete_inner_blank_lines->();
+
+ # then handle sufficiently large groups
if ( $count >= $Opt_long_count ) {
if ( $ibeg > 0 ) {
my $code_type = $rlines->[ $ibeg - 1 ]->{_code_type};
+
+ # patch for hash bang line which is not currently marked as
+ # a comment
+ if ( $ibeg == 1 && !$code_type ) {
+ my $line_text = $rlines->[ $ibeg - 1 ]->{_line_text};
+ $code_type = 'BC'
+ if ( $line_text && $line_text =~ /^#/ );
+ }
+
if ( $Opt_blanks_after_comments
|| $code_type !~ /(BC|SBC|SBCX)/ )
{
- $rhash_of_desires->{ $ibeg - 1 } = $Opt_blanks_before;
+ if ( $Opt_blanks_before == 1 ) {
+ $rhash_of_desires->{ $ibeg - 1 } = 1;
+ if ( defined( $rhash_of_desires->{$ibeg} )
+ && $rhash_of_desires->{$ibeg} == 2 )
+ {
+ $rhash_of_desires->{$ibeg} = 0;
+ }
+ }
+ elsif ( $Opt_blanks_before == 2 ) {
+ $delete_if_blank->( $ibeg - 1 );
+ }
}
}
- if ( defined($K_first) ) {
+ # We will only put blanks before code lines. We could loosen
+ # this rule a little, but we have to be very careful because
+ # for example we certainly don't want to drop a blank line
+ # after a line like this:
+ # my $var = <<EOM;
+ if ( $line_type eq 'CODE' && defined($K_first) ) {
# - Do not put a blank before a line of different level
# - Do not put a blank line if we ended the search badly
&& $iend < @{$rlines}
&& $CODE_type ne 'HSC' )
{
- $rhash_of_desires->{$iend} = $Opt_blanks_after;
+ if ( $Opt_blanks_after == 1 ) {
+ $rhash_of_desires->{$iend} = 1;
+ }
+ elsif ( $Opt_blanks_after == 2 ) {
+ $delete_if_blank->( $iend + 1 );
+ }
}
}
}
- $sub_group->();
+ $split_into_sub_groups->();
}
- # reset for another group
+ # reset for another group
$ibeg = -1;
$iend = undef;
$level_beg = -1;
my $container_check = sub {
- # If the keyword lines ends with an open token, find the closing token
- # '$K_closing' so that we can easily skip past the contents of the
- # container.
+ # If the keyword lines ends with an open token, find the closing token
+ # '$K_closing' so that we can easily skip past the contents of the
+ # container.
my $KK = $K_last;
my $type_last = $rLL->[$KK]->[_TYPE_];
my $tok_last = $rLL->[$KK]->[_TOKEN_];
my $i = -1;
foreach my $line_of_tokens ( @{$rlines} ) {
$i++;
- $K_first = undef;
- $K_last = undef;
- my $line_type = $line_of_tokens->{_line_type};
+
$CODE_type = "";
+ $K_first = undef;
+ $K_last = undef;
+ $line_type = $line_of_tokens->{_line_type};
+
+ # always end a group at non-CODE
if ( $line_type ne 'CODE' ) { $end_group->(); next }
+
$CODE_type = $line_of_tokens->{_code_type};
- # end any group at a blank, verbatim, or format skipping line
- if (
- $CODE_type
- && ( $CODE_type eq 'BL'
- || $CODE_type eq 'VB'
- || $CODE_type eq 'FS' ) #|| $CODE_type eq 'HSC' )
- )
- {
+ # end any group at a format skipping line
+ if ( $CODE_type && $CODE_type eq 'FS' ) {
$end_group->();
next;
}
+ # continue in a verbatim (VB) type; it may be quoted text
+ if ( $CODE_type eq 'VB' ) {
+ if ( $ibeg >= 0 ) { $iend = $i; }
+ next;
+ }
+
+ # continue in blank (BL) types only if we are deleting blanks
+ if ( $CODE_type eq 'BL' ) {
+ if ( $ibeg >= 0 ) {
+ if ($Opt_blanks_delete) { $iend = $i }
+ else { $end_group->() }
+ }
+ next;
+ }
+
# examine the first token of this line
my $rK_range = $line_of_tokens->{_rK_range};
( $K_first, $K_last ) = @{$rK_range};
my $token = $rLL->[$K_first]->[_TOKEN_];
my $ci_level = $rLL->[$K_first]->[_CI_LEVEL_];
- # See if it is a keyword we seek, but
- # never start a group in a continuation line; the code is badly formatted
+ # See if it is a keyword we seek, but never start a group in a
+ # continuation line; the code may be badly formatted.
if ( $ci_level == 0
&& $type eq 'k'
&& $token =~ /$Opt_pattern/o )
# - continue if if we are within in a container which started with
# the line of the previous keyword.
- ##if (defined($K_closing) && $K_first<=$K_closing) {$iend=$i; next}
if ( defined($K_closing) && $K_first <= $K_closing ) {
# continue if entire line is within container
if ( $K_last <= $K_closing ) { $iend = $i; next }
# continue at ); or }; or ];
- if ( $K_first == $K_closing ) {
- my $KK = $K_first++;
- if ( $rLL->[$KK]->[_TYPE_] eq 'b' ) { $KK++ }
- if ( $KK <= $K_last && $rLL->[$KK]->[_TYPE_] eq ';' ) {
- $iend = $i;
- next;
+ my $KK = $K_closing + 1;
+ if ( $rLL->[$KK]->[_TYPE_] eq ';' ) {
+ if ( $KK < $K_last ) {
+ if ( $rLL->[ ++$KK ]->[_TYPE_] eq 'b' ) { ++$KK }
+ if ( $KK > $K_last || $rLL->[$KK]->[_TYPE_] ne '#' ) {
+ $end_group->(1);
+ next;
+ }
}
+ $iend = $i;
+ next;
}
+
$end_group->(1);
next;
}
# not in a keyword group; continue
else { next }
}
+
+ # end of loop over all lines
$end_group->();
return $rhash_of_desires;
}
$i++;
# insert blank lines requested for keyword sequences
- if ( $i > 0 && $rwant_blank_line_after->{ $i - 1 } ) {
+ if ( $i > 0
+ && defined( $rwant_blank_line_after->{ $i - 1 } )
+ && $rwant_blank_line_after->{ $i - 1 } == 1 )
+ {
$self->want_blank_line();
- }
+ }
my $last_line_type = $line_type;
$line_type = $line_of_tokens->{_line_type};
# If keep-old-blank-lines is zero, we delete all
# old blank lines and let the blank line rules generate any
# needed blanks.
- if ($rOpts_keep_old_blank_lines) {
+
+ # We also delete lines requested by the keyword-group logic
+ my $kgb_keep = !( defined( $rwant_blank_line_after->{$i} )
+ && $rwant_blank_line_after->{$i} == 2 );
+
+ if ($rOpts_keep_old_blank_lines && $kgb_keep) {
$self->flush();
$file_writer_object->write_blank_code_line(
$rOpts_keep_old_blank_lines == 2 );
sub make_keyword_group_list_pattern {
- # turn any input list into a regex for recognizing selected block types
- $keyword_group_list_pattern = '^(our|local|my|use)$';
+ # turn any input list into a regex for recognizing selected block types.
+ # Here are the defaults:
+ $keyword_group_list_pattern = '^((our|local|my|use|require|)$|sub)';
if ( defined( $rOpts->{'keyword-group-list'} )
&& $rOpts->{'keyword-group-list'} )
{
$keyword_group_list_pattern =
- make_block_pattern( '-kwgl', $rOpts->{'keyword-group-list'} );
+ make_block_pattern( '-kgbl', $rOpts->{'keyword-group-list'} );
}
return;
}