default. Use B<-nbbc> or B<--noblanks-before-comments> to prevent
such blank lines from being introduced.
-=item B<-bac>, B<--blanks-after-comments>
-
-The B<-bac> flag allows blank lines to be inserted after full line comments.
-This flag does not by itself introduce such lines, but is referenced by the B<-kgb>
-flags to see if they have permission to do this.
-The default is not to allow this, B<-nbac>.
-
=item B<-blbs=n>, B<--blank-lines-before-subs=n>
The parameter B<-blbs=n> requests that least B<n> blank lines precede a sub
B<--keyword-group-blanks-list=s>, or B<-kgbl=s>; B<s> is a quoted string of keywords
-B<--keyword-group-blanks-threshold-count=n>, or B<-kgbt=n>; B<n> is a threshold (minimum) group size
+B<--keyword-group-blanks-size=s>, or B<-kgbs=s>; B<s> gives the number of keywords required to form a group.
B<--keyword-group-blanks-before=n>, or B<-kgbb=n>; B<n> = (0, 1, or 2) controls a leading blank
space separated list of keywords. The default set is B<s="use require local
our my sub">, but any list of keywords may be used.
-B<--keyword-group-blanks-threshold-count=n>, or B<-kgbt=n>, where B<n> is the minimum
-number of consecutive keyword statements to which these controls apply. The
-default is B<n=5>. The special value B<n=0> is the same as n=infinity and
-can be used to turn off all searching for keyword groups.
+B<--keyword-group-blanks-size=s>, or B<-kgbs=s>, where B<s> is a string
+describing the number of consecutive keyword statements forming a group. If
+B<s> is an integer then it is the minimum number required for a group. A
+maximum value may also be given with the format B<s=min.max>, where B<min> is
+the minimum number and B<max> is the maximum number, and the min and max values
+are separated by one or more dots. No groups will be found if the maximum is
+less than the minimum. The maximum is unlimited if not given. The default is
+B<s=5>. Some examples:
+
+ s min max number for group
+ 3 3 unlimited 3 or more
+ 1.1 1 1 1
+ 1..3 1 3 1 to 3
+ 1.0 1 0 (no match)
+
B<--keyword-group-blanks-before=n>, or B<-kgbb=n>, specifies whether
a blank should appear before the first line of the group, as follows:
B<--keyword-group-blanks-inside>, or B<-kgbi>, controls
the insertion of blank lines between the first and last statement of the entire
group. If there is a continuous run of a single statement type with more than
-the minimum threshold number (as specified with B<-kgbt=n>) then this
+the minimum threshold number (as specified with B<-kgbs=s>) then this
switch causes a blank line be inserted between this
subgroup and the others. In the example above this happened between the
B<use> and B<my> statements.
=item *
-A blank line will not be introduced after a comment line unless the flag
-B<--blanks-after-comments>, or B<-bac>, is also set.
+A blank line will not be introduced after a comment line
=item *
<p>A blank line will be introduced before a full-line comment. This is the default. Use <b>-nbbc</b> or <b>--noblanks-before-comments</b> to prevent such blank lines from being introduced.</p>
-</dd>
-<dt id="bac---blanks-after-comments"><b>-bac</b>, <b>--blanks-after-comments</b></dt>
-<dd>
-
-<p>The <b>-bac</b> flag allows blank lines to be inserted after full line comments. This flag does not by itself introduce such lines, but is referenced by the <b>-kgb</b> flags to see if they have permission to do this. The default is not to allow this, <b>-nbac</b>.</p>
-
</dd>
<dt id="blbs-n---blank-lines-before-subs-n"><b>-blbs=n</b>, <b>--blank-lines-before-subs=n</b></dt>
<dd>
<p><b>--keyword-group-blanks-list=s</b>, or <b>-kgbl=s</b>; <b>s</b> is a quoted string of keywords</p>
-<p><b>--keyword-group-blanks-threshold-count=n</b>, or <b>-kgbt=n</b>; <b>n</b> is a threshold (minimum) group size</p>
+<p><b>--keyword-group-blanks-size=s</b>, or <b>-kgbs=s</b>; <b>s</b> gives the number of keywords required to form a group.</p>
<p><b>--keyword-group-blanks-before=n</b>, or <b>-kgbb=n</b>; <b>n</b> = (0, 1, or 2) controls a leading blank</p>
<p><b>--keyword-group-blanks-list=s</b>, or <b>-kgbl=s</b>, where <b>s</b> is a quoted string, defines the set of keywords which will be formed into groups. The string is a space separated list of keywords. The default set is <b>s="use require local our my sub"</b>, but any list of keywords may be used.</p>
-<p><b>--keyword-group-blanks-threshold-count=n</b>, or <b>-kgbt=n</b>, where <b>n</b> is the minimum number of consecutive keyword statements to which these controls apply. The default is <b>n=5</b>. The special value <b>n=0</b> is the same as n=infinity and can be used to turn off all searching for keyword groups.</p>
+<p><b>--keyword-group-blanks-size=s</b>, or <b>-kgbs=s</b>, where <b>s</b> is a string describing the number of consecutive keyword statements forming a group. If <b>s</b> is an integer then it is the minimum number required for a group. A maximum value may also be given with the format <b>s=min.max</b>, where <b>min</b> is the minimum number and <b>max</b> is the maximum number, and the min and max values are separated by one or more dots. No groups will be found if the maximum is less than the minimum. The maximum is unlimited if not given. The default is <b>s=5</b>. Some examples:</p>
+
+<pre><code> s min max number for group
+ 3 3 unlimited 3 or more
+ 1.1 1 1 1
+ 1..3 1 3 1 to 3
+ 1.0 1 0 (no match)
+ </code></pre>
<p><b>--keyword-group-blanks-before=n</b>, or <b>-kgbb=n</b>, specifies whether a blank should appear before the first line of the group, as follows:</p>
<p><b>--keyword-group-blanks-after=n</b>, or <b>-kgba=n</b>, likewise specifies whether a blank should appear after the last line of the group, using the same scheme (0=delete, 1=stable, 2=insert).</p>
-<p><b>--keyword-group-blanks-inside</b>, or <b>-kgbi</b>, controls the insertion of blank lines between the first and last statement of the entire group. If there is a continuous run of a single statement type with more than the minimum threshold number (as specified with <b>-kgbt=n</b>) then this switch causes a blank line be inserted between this subgroup and the others. In the example above this happened between the <b>use</b> and <b>my</b> statements.</p>
+<p><b>--keyword-group-blanks-inside</b>, or <b>-kgbi</b>, controls the insertion of blank lines between the first and last statement of the entire group. If there is a continuous run of a single statement type with more than the minimum threshold number (as specified with <b>-kgbs=s</b>) then this switch causes a blank line be inserted between this subgroup and the others. In the example above this happened between the <b>use</b> and <b>my</b> statements.</p>
<p><b>--keyword-group-blanks-delete</b>, or <b>-kgbd</b>, controls the deletion of any blank lines that exist in the the group when it is first scanned. When statements are initially scanned, any existing blank lines are included in the collection. Any such orignial blank lines will be deleted before any other insertions are made when the parameter <b>-kgbd</b> is set. The default is not to do this, <b>-nkgbd</b>.</p>
<li><p>A blank line will only be introduced at the end of a group if the next statement is a line of code (as opposed to an __END__ line for example).</p>
</li>
-<li><p>A blank line will not be introduced after a comment line unless the flag <b>--blanks-after-comments</b>, or <b>-bac</b>, is also set.</p>
+<li><p>A blank line will not be introduced after a comment line</p>
</li>
<li><p>The count which is used to determine the group size is not the number of lines but rather the total number of keywords which are found. Individual statements with a certain leading keyword may continue on multiple lines, but if any of these lines is nested more than one level deep then that group will be ended.</p>
$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-blanks-list', 'kgbl', '=s' );
- $add_option->( 'keyword-group-blanks-threshold-count', 'kgbt', '=i' );
- $add_option->( 'keyword-group-blanks-repeat-count', 'kgbr', '=i' );
- $add_option->( 'keyword-group-blanks-before', 'kgbb', '=i' );
- $add_option->( 'keyword-group-blanks-after', 'kgba', '=i' );
- $add_option->( 'keyword-group-blanks-inside', 'kgbi', '!' );
- $add_option->( 'keyword-group-blanks-delete', 'kgbd', '!' );
+ $add_option->( 'keyword-group-blanks-list', 'kgbl', '=s' );
+ $add_option->( 'keyword-group-blanks-size', 'kgbs', '=s' );
+ $add_option->( 'keyword-group-blanks-repeat-count', 'kgbr', '=i' );
+ $add_option->( 'keyword-group-blanks-before', 'kgbb', '=i' );
+ $add_option->( 'keyword-group-blanks-after', 'kgba', '=i' );
+ $add_option->( 'keyword-group-blanks-inside', 'kgbi', '!' );
+ $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' );
blank-lines-before-subs=1
blank-lines-before-packages=1
- noblanks-after-comments
- keyword-group-blanks-threshold-count=5
+ keyword-group-blanks-size=5
keyword-group-blanks-repeat-count=0
keyword-group-blanks-before=1
keyword-group-blanks-after=1
BEGIN {
+ # Codes for insertion and deletion of blanks
+ use constant DELETE => 0;
+ use constant STABLE => 1;
+ use constant INSERT => 2;
+
# Caution: these debug flags produce a lot of output
# They should all be 0 except when debugging small scripts
use constant FORMATTER_DEBUG_FLAG_RECOMBINE => 0;
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_threshold_count = $rOpts->{'keyword-group-blanks-threshold-count'}; # '-kgbt'
+ my $Opt_size = $rOpts->{'keyword-group-blanks-size'}; # '-kgbs'
+
+ # A range of sizes can be input with decimal notation like 'min.max' with
+ # any number of dots between the two numbers. Examples:
+ # string => min max matches
+ # 1.1 1 1 exactly 1
+ # 1.3 1 3 1,2, or 3
+ # 1..3 1 3 1,2, or 3
+ # 5 5 - 5 or more
+ # 6. 6 - 6 or more
+ # .2 - 2 up to 2
+ # 1.0 1 0 nothing
+ my ( $Opt_size_min, $Opt_size_max ) = split /\.+/, $Opt_size;
+ if ( $Opt_size_min && $Opt_size_min !~ /^\d+$/
+ || $Opt_size_max && $Opt_size_max !~ /^\d+$/ )
+ {
+ print STDERR "unexpected value for -kgbs: '$Opt_size'; ignoring\n";
+ return $rhash_of_desires
+ }
+ $Opt_size_min = 1 unless ($Opt_size_min);
+
+ if ( $Opt_size_max && $Opt_size_max < $Opt_size_min ) {
+ return $rhash_of_desires;
+ }
# codes for $Opt_blanks_before and $Opt_blanks_after:
# 0 = never (delete if exist)
# 2 = always (insert if missing)
return $rhash_of_desires
- unless $Opt_threshold_count > 0
+ unless $Opt_size_min > 0
&& ( $Opt_blanks_before != 1
|| $Opt_blanks_after != 1
|| $Opt_blanks_inside
|| $Opt_blanks_delete );
- my $Opt_blanks_after_comments = $rOpts->{'blanks-after-comments'}; # '-bac'
my $Opt_pattern = $keyword_group_list_pattern;
my $Opt_repeat_count =
$rOpts->{'keyword-group-blanks-repeat-count'}; # '-kgbr'
my $rLL = $self->{rLL};
my $K_closing_container = $self->{K_closing_container};
- # These vars save values for the current group and subgroups:
- my ( $ibeg, $iend, $count, $level_beg, $K_closing, @sublist, );
-
+ # variables for the current group and subgroups:
+ my ( $ibeg, $iend, $count, $level_beg, $K_closing, @iblanks, @group,
+ @subgroup );
+
+ # Definitions:
+ # ($ibeg, $iend) = starting and ending line indexes of this entire group
+ # $count = total number of keywords seen in this entire group
+ # $level_beg = indententation level of this group
+ # @group = [ $i, $token, $count ] =list of all keywords & blanks
+ # @subgroup = $j, index of group where token changes
+ # @iblanks = line indexes of blank lines in input stream in this group
+ # where i=starting line index
+ # token (the keyword)
+ # count = number of this token in this subgroup
+ # j = index in group where token changes
+ #
# These vars will contain values for the most recently seen line:
my ( $line_type, $CODE_type, $K_first, $K_last );
my $split_into_sub_groups = sub {
- # Here we place blanks around long sub-groups of keywords
- # if requested.
- return unless ( $Opt_blanks_inside );
-
- my $ib = $sublist[0]->[0];
- push @sublist, [ $iend + 1, "", 0 ];
- for ( my $j = 1 ; $j < @sublist ; $j++ ) {
- my $ie = $sublist[$j]->[0] - 1;
- my $num = $sublist[ $j - 1 ]->[2];
- if ( $num >= $Opt_threshold_count ) {
- $rhash_of_desires->{ $ib - 1 } = 1 unless ( $ib == $ibeg );
- $rhash_of_desires->{$ie} = 1 unless ( $ie == $iend );
+ # place blanks around long sub-groups of keywords
+ # ...if requested
+ return unless ($Opt_blanks_inside);
+
+ # loop over sub-groups, index k
+ push @subgroup, scalar @group;
+ my $kbeg=1;
+ my $kend=@subgroup-1;
+ for ( my $k = $kbeg ; $k <= $kend ; $k++ ) {
+
+ # index j runs through all keywords found
+ my $j_b = $subgroup[ $k - 1 ];
+ my $j_e = $subgroup[$k]-1;
+
+ # index i is the actual line number of a keyword
+ my ( $i_b, $tok_b, $count_b ) = @{ $group[$j_b] };
+ my ( $i_e, $tok_e, $count_e ) = @{ $group[$j_e] };
+ my $num = $count_e - $count_b+1;
+
+ # This subgroup runs from line $ib to line $ie-1, but may contain
+ # blank lines
+ if ( $num >= $Opt_size_min ) {
+
+ # if there are blank lines, we require that at least $num lines
+ # be non-blank up to the boundary with the next subgroup.
+ my $nog_b = my $nog_e = 1;
+ if ( @iblanks && !$Opt_blanks_delete ) {
+ my $j_bb = $j_b + $num - 1;
+ my ( $i_bb, $tok_bb, $count_bb ) = @{ $group[$j_bb] };
+ $nog_b = $count_bb - $count_b + 1 == $num;
+
+ my $j_ee = $j_e - ( $num - 1 );
+ my ( $i_ee, $tok_ee, $count_ee ) = @{ $group[$j_ee] };
+ $nog_e = $count_e - $count_ee + 1 == $num;
+ }
+ if ( $nog_b && $k > $kbeg ) {
+ $rhash_of_desires->{ $i_b - 1 } = 1;
+ }
+ if ( $nog_e && $k < $kend ) {
+ my ( $i_ep, $tok_ep, $count_ep ) = @{ $group[ $j_e + 1 ] };
+ $rhash_of_desires->{ $i_ep - 1 } = 1;
+ }
}
- $ib = $ie + 1;
}
};
my $delete_if_blank = sub {
my ($i) = @_;
- # delete line $i if it is blank
+ # delete line $i if it is blank
return unless ( $i >= 0 && $i < @{$rlines} );
my $line_type = $rlines->[$i]->{_line_type};
return if ( $line_type ne 'CODE' );
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;
- }
+ # always remove unwanted trailing blank lines from our list
+ return unless (@iblanks);
+ while (my $ibl = pop(@iblanks) ){
+ last if ( $ibl < $iend );
+ $iend = $ibl;
}
- $iend = $i_last_nonblank;
- for ( my $i = $ibeg + 1 ; $i <= $iend - 1 ; $i++ ) {
- $delete_if_blank->($i);
- }
+ # now mark mark interior blank lines for deletion if requested
+ return unless ($Opt_blanks_delete);
+
+ while ( my $ibl = pop(@iblanks) ) { $rhash_of_desires->{$ibl} = 2; }
+
};
my $end_group = sub {
if ( defined($ibeg) && $ibeg >= 0 ) {
# then handle sufficiently large groups
- if ( $count >= $Opt_threshold_count ) {
+ if ( $count >= $Opt_size_min ) {
- $number_of_groups_seen++;
+ $number_of_groups_seen++;
# do any blank deletions regardless of the count
$delete_inner_blank_lines->();
my $code_type = $rlines->[ $ibeg - 1 ]->{_code_type};
# patch for hash bang line which is not currently marked as
- # a comment
+ # a comment; mark it 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)/ )
- {
- if ( $Opt_blanks_before == 2 ) {
+ # Do not inseert a blank after a comment
+ # (this could be subject to a flag in the future)
+ #if ( $Opt_blanks_after_comments != 0
+ # || $code_type !~ /(BC|SBC|SBCX)/ )
+ if ( $code_type !~ /(BC|SBC|SBCX)/ ) {
+ if ( $Opt_blanks_before == INSERT ) {
$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 == 0 ) {
+ elsif ( $Opt_blanks_before == DELETE ) {
$delete_if_blank->( $ibeg - 1 );
}
}
&& $iend < @{$rlines}
&& $CODE_type ne 'HSC' )
{
- if ( $Opt_blanks_after == 2 ) {
+ if ( $Opt_blanks_after == INSERT ) {
$rhash_of_desires->{$iend} = 1;
}
- elsif ( $Opt_blanks_after == 0 ) {
+ elsif ( $Opt_blanks_after == DELETE ) {
$delete_if_blank->( $iend + 1 );
}
}
$iend = undef;
$level_beg = -1;
$K_closing = undef;
- @sublist = ();
+ @group = ();
+ @subgroup = ();
+ @iblanks = ();
};
my $container_check = sub {
}
};
+ my $add_to_group = sub {
+ my ( $i, $token, $level ) = @_;
+
+ # End the previous group if we have reached the maximum
+ # group size
+ if ( $Opt_size_max && @group >= $Opt_size_max ) {
+ $end_group->();
+ }
+
+ if ( @group == 0 ) {
+ $ibeg = $i;
+ $level_beg = $level;
+ $count = 0;
+ }
+
+ $count++;
+ $iend = $i;
+
+ # New sub-group?
+ if ( !@group || $token ne $group[-1]->[1] ) {
+ push @subgroup, scalar(@group);
+ }
+ push @group, [ $i, $token, $count ];
+
+ # remember if this line ends in an open container
+ $container_check->();
+
+ return;
+ };
+
$end_group->();
# loop over all lines of the source
}
# continue in a verbatim (VB) type; it may be quoted text
- # and continue in blank (BL) types
- if ( $CODE_type eq 'VB' || $CODE_type eq 'BL') {
+ if ( $CODE_type eq 'VB' ) {
if ( $ibeg >= 0 ) { $iend = $i; }
next;
}
+ # and continue in blank (BL) types
+ if ( $CODE_type eq 'BL' ) {
+ if ( $ibeg >= 0 ) {
+ $iend = $i;
+ push @{iblanks}, $i;
+ push @group, [ $i, "", $count ];
+
+ }
+ next;
+ }
+
# examine the first token of this line
my $rK_range = $line_of_tokens->{_rK_range};
( $K_first, $K_last ) = @{$rK_range};
# Continuing a keyword group
if ( $ibeg >= 0 && $level == $level_beg ) {
-
- $iend = $i;
- $count++;
-
- # New sub-group?
- if ( $token ne $sublist[-1]->[1] ) {
- push @sublist, [ $i, $token, 1 ];
- }
-
- # Continuing current sub-group
- else { $sublist[-1]->[2]++; }
-
- # remember if this line ends in an open container
- $container_check->();
+ $add_to_group->($i, $token, $level);
}
# Start new keyword group
# first end old group if any; we might be starting new
# keywords at different level
if ( $ibeg > 0 ) { $end_group->(); }
-
- $ibeg = $i;
- $iend = $i;
- $count = 1;
- $level_beg = $level;
- $container_check->();
- push @sublist, [ $i, $token, 1 ];
+ $add_to_group->($i, $token, $level);
}
next;
}
$end_group->();
return $rhash_of_desires;
}
-
sub break_lines {
# Loop over old lines to set new line break points
# 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)';
+ ##$keyword_group_list_pattern = '^((our|local|my|use|require|)$|sub)';
+ $keyword_group_list_pattern = '^(our|local|my|use|require|)$';
if ( defined( $rOpts->{'keyword-group-blanks-list'} )
&& $rOpts->{'keyword-group-blanks-list'} )
{