Hi, This patch changes the semantics of the checking for the Perl code coda in Perl::Critic::Policy::CodeLayout::UseParrotCoda. At present the Perl code coda is checked at the end of the file if no __END__ or __DATA__ block is present, and is checked before the __END__ or __DATA__ block if such a block is present. Unfortunately, this makes the coda too far from the end of the file for vim to be able to read it, and therefore apply its policy. Another way of implementing this would be to put the coda at the end of the file for all files except for files with a __DATA__ block, in which case the coda should be "inverted" with respect to the ordering of the emacs and vim components and put at the start of the file just after the shebang line (or somewhere there; the vim coda needs to be within 5 lines of the start or end of the file to be able to be seen by vim). __END__ blocks need to be treated a bit more specially in that __END__ blocks are often used for pod, and if one wants the coda not to appear in the output of perldoc then a =cut line would be required before the coda. The attached patch implements testing for this (rather complicated) policy.
Is this a logical and appropriate way forward, or am I complicating the issue? I hope not. Anyway, if this patch gets accepted, then I can patch all the Perl code failing this policy and we can tick off the CAGE task. Comments definitely welcome, Regards, Paul files affected: lib/Perl/Critic/Policy/CodeLayout/UseParrotCoda.pm
Index: lib/Perl/Critic/Policy/CodeLayout/UseParrotCoda.pm =================================================================== --- lib/Perl/Critic/Policy/CodeLayout/UseParrotCoda.pm (revision 14906) +++ lib/Perl/Critic/Policy/CodeLayout/UseParrotCoda.pm (working copy) @@ -1,4 +1,4 @@ -# $Id# +# $Id$ package Perl::Critic::Policy::CodeLayout::UseParrotCoda; use strict; @@ -31,6 +31,16 @@ # vim: expandtab shiftwidth=4: END_CODA +our $DATA_CODA = <<'START_CODA'; +# vim: expandtab shiftwidth=4: +# Local Variables: +# mode: cperl +# cperl-indent-level: 4 +# fill-column: 100 +# End: +START_CODA + + #---------------------------------------------------------------------------- sub violates { @@ -38,40 +48,82 @@ my @coda_lines = split /\n/, $CODA; - my $last_node; - for ($last_node = $doc->last_element; - $last_node && @coda_lines; - $last_node = $last_node->previous_sibling) { + my $last_node = $doc->last_element; - # Skip (optional) __END__ || __DATA blocks... - next if ( $last_node->isa('PPI::Statement::End') ); - next if ( $last_node->isa('PPI::Statement::Data') ); - next if ( $last_node->isa('PPI::Token::Whitespace') ); - last if ( !$last_node->isa('PPI::Token::Comment') ); + # need to treat __END__ blocks carefully + if ( $last_node->isa('PPI::Statement::End') ) { + # look for a =cut, any amount of whitespace, and then the coda + return if ($last_node =~ m{=cut\s*\Q$CODA\E\n*\z}); + return $self->violation( $desc, $expl, $last_node ); + } + # and __DATA__ blocks... + elsif ( $last_node->isa('PPI::Statement::Data') ) { + # look for the coda at the *start* of the file, but with the vim + # part *before* the emacs part (this way vim will still see the + # settings) + my $comments = $doc->find('PPI::Token::Comment'); - my $last_coda_line = $coda_lines[-1]; - my $last_actual_line = $last_node->content; - chomp $last_actual_line; - last if ( $last_coda_line ne $last_actual_line ); - pop @coda_lines; + return if (coda_at_start($comments)); + return $self->violation( $desc, $expl, $doc ); } + # in the other cases... + else { + for ($last_node = $doc->last_element; + $last_node && @coda_lines; + $last_node = $last_node->previous_sibling) { + next if ( $last_node->isa('PPI::Token::Whitespace') ); + last if ( !$last_node->isa('PPI::Token::Comment') ); - return if ( [EMAIL PROTECTED] ); # We made it through all the coda lines - return $self->violation( $desc, $expl, $last_node || $doc ); + # compare the last line with the coda line + my $last_coda_line = $coda_lines[-1]; + my $last_actual_line = $last_node->content; + chomp $last_actual_line; + last if ( $last_coda_line ne $last_actual_line ); + pop @coda_lines; + } + + return if ( [EMAIL PROTECTED] ); # We made it through all the coda lines + return $self->violation( $desc, $expl, $last_node || $doc ); + } } +# search for the DATA_CODA at the start of a file +sub coda_at_start +{ + my $comments_ref = shift; + + # if are no comments, return failure + return if ($comments_ref eq ""); + + # search forwards through all comment lines, removing the first + # element of @data_coda_lines if it equals the comment line + my @data_coda_lines = split /\n/, $DATA_CODA; + foreach my $comment ( @$comments_ref ) { + chomp $comment; + + # if we've used up the coda, end the loop + last if ( [EMAIL PROTECTED] ); + + my $first_coda_line = $data_coda_lines[0]; + next if ($comment ne $first_coda_line); + + # if the comment matches the coda line, reduce the coda lines + if ($comment eq $first_coda_line) { + shift @data_coda_lines; + } + } + + # we've searched all comments, if @data_coda_lines has length + # zero, return success, else failure + return 1 if ([EMAIL PROTECTED]); # success + return; # failure +} + 1; # How meta! We ourselves must have this coda to be a valid perl file in the # parrot repository... -# Local Variables: -# mode: cperl -# cperl-indent-level: 4 -# fill-column: 100 -# End: -# vim: expandtab shiftwidth=4: - __END__ =head1 NAME @@ -85,3 +137,10 @@ whitespace and a C<__END__> or C<__DATA__> blocks. =cut + +# Local Variables: +# mode: cperl +# cperl-indent-level: 4 +# fill-column: 100 +# End: +# vim: expandtab shiftwidth=4: