Login
New policy from the Hackathon:
authorChris Dolan <chris+github@chrisdolan.net>
Sat, 11 Nov 2006 21:35:23 +0000 (21:35 +0000)
committerChris Dolan <chris+github@chrisdolan.net>
Sat, 11 Nov 2006 21:35:23 +0000 (21:35 +0000)
  Editor::RequireEmacsFileVariables
This fails the self compliance tests!

Changes
MANIFEST
README.developer
TODO.pod
lib/Perl/Critic/Policy/Editor/RequireEmacsFileVariables.pm [new file with mode: 0644]
lib/Perl/Critic/PolicyFactory.pm
lib/Perl/Critic/PolicySummary.pod
t/00_modules.t
t/03_pragmas.t
t/20_policies_editor.t [new file with mode: 0644]
tools/ppidump

diff --git a/Changes b/Changes
index 0c6c32a..9823b34 100644 (file)
--- a/Changes
+++ b/Changes
@@ -5,6 +5,11 @@
 # $Revision$
 ##############################################################################
 
+Hackathon:
+
+     New Policies:
+     * Editor::RequireEmacsFileVariables
+
 [0.21]  Released on 2006-11-05
 
      New Policies:
index 372c616..c97c419 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -40,6 +40,7 @@ lib/Perl/Critic/Policy/ControlStructures/ProhibitUnreachableCode.pm
 lib/Perl/Critic/Policy/ControlStructures/ProhibitUntilBlocks.pm
 lib/Perl/Critic/Policy/Documentation/RequirePodAtEnd.pm
 lib/Perl/Critic/Policy/Documentation/RequirePodSections.pm
+lib/Perl/Critic/Policy/Editor/RequireEmacsFileVariables.pm
 lib/Perl/Critic/Policy/ErrorHandling/RequireCarping.pm
 lib/Perl/Critic/Policy/InputOutput/ProhibitBacktickOperators.pm
 lib/Perl/Critic/Policy/InputOutput/ProhibitBarewordFileHandles.pm
@@ -110,6 +111,7 @@ lib/Perl/Critic/Violation.pm
 LICENSE
 Makefile.PL
 MANIFEST                       This list of files
+META.yml
 README
 t/00_modules.t
 t/01_config.t
@@ -128,6 +130,7 @@ t/20_policies_classhierarchies.t
 t/20_policies_codelayout.t
 t/20_policies_controlstructures.t
 t/20_policies_documentation.t
+t/20_policies_editor.t
 t/20_policies_errorhandling.t
 t/20_policies_inputoutput.t
 t/20_policies_miscellanea.t
@@ -154,4 +157,3 @@ t/tlib/Perl/Critic/Policy/Test.pm
 t/tlib/ViolationTest.pm
 t/tlib/ViolationTest2.pm
 TODO.pod
-META.yml
index 40cb965..f2296c3 100644 (file)
@@ -5,6 +5,7 @@ Steps for adding a new Policy:
  * If it will be a default policy:
    - Add it to lib/Perl/Critic/Config.pm in native_policies()
    - Add 14 to the number of tests in t/00_modules.t
+ * If it applies_to() PPI::Document, perhaps add an exception in t/03_pragmas.t
  * Add it to MANIFEST (via "Build manifest")
  * Add it to the POD in lib/Perl/Critic/PolicySummary.pod
  * Mention it in Changes
index f53daca..e2f5ac9 100644 (file)
--- a/TODO.pod
+++ b/TODO.pod
@@ -256,24 +256,7 @@ Files must have something like the following in them for Emacs and Vi:
 
 =over 4
 
-=item Emacs file variables
-
-L<http://www.gnu.org/software/emacs/manual/html_node/File-Variables.html>
-
-In emacs, this is called "File Variables".  There are two syntaxes:
-C<-*- ... -*-> (single-line) and C<Local Variables:\n...\nEnd:>
-(multi-line).  Both syntaxes allow leading and trailing text on the
-line.
-
-The single-line syntax must be used on the first line of the file to
-be recognized.
-
-The multi-line syntax must be used "in the last page" at the end of
-the file.  As of Emacs21, this is hard-coded to be the last 3000 bytes
-of the file (in the hack-local-variables function in files.el).  In
-this syntax, each line must begin and end with the same prefix/suffix
-pair.  That pair is defined by the text before and after the "Local
-Variables:" string.
+=item Emacs file variables [IMPLEMENTED!]
 
 =item Vim modelines
 
@@ -281,6 +264,9 @@ In vim, this is called "modelines" and should match the following
 pattern:
 
   [text]{white}{vi:|vim:|ex:}[white]se[t] {options}:[text]
+  [text]{white}{vi:|vim:|ex:}[white]{options}
+
+Watch out for escaped colons!
 
 The vim modeline must be within N lines of the top or bottom of the
 file.  That N is user-settable, but defaults to 5.  To learn more type
diff --git a/lib/Perl/Critic/Policy/Editor/RequireEmacsFileVariables.pm b/lib/Perl/Critic/Policy/Editor/RequireEmacsFileVariables.pm
new file mode 100644 (file)
index 0000000..ac6b3c5
--- /dev/null
@@ -0,0 +1,144 @@
+#######################################################################
+#      $URL$
+#     $Date$
+#   $Author$
+# $Revision$
+########################################################################
+
+package Perl::Critic::Policy::Editor::RequireEmacsFileVariables;
+
+use strict;
+use warnings;
+use Perl::Critic::Utils;
+use base 'Perl::Critic::Policy';
+
+our $VERSION = 0.21;
+
+#---------------------------------------------------------------------------
+
+my $desc = 'Use Emacs file variables to declare coding style';
+my $expl = 'Emacs can read per-file settings';
+
+
+#---------------------------------------------------------------------------
+
+sub default_severity { return $SEVERITY_LOW       }
+sub default_themes   { return qw(readability editor) }
+sub applies_to       { return 'PPI::Document'     }
+
+#---------------------------------------------------------------------------
+
+sub new {
+    my ( $class, %config ) = @_;
+    my $self = bless {}, $class;
+
+
+    return $self;
+}
+
+#---------------------------------------------------------------------------
+
+sub violates {
+    my ( $self, $elem, $doc ) = @_;
+
+    my $code = $doc->serialize();
+
+    ## Look for first line file vars.  Example:
+    #! /usr/bin/perl -w -*- mode: cperl; cperl-indent-level: 4 -*-
+
+    my $one_line_local_var   = qr/-[*]- .* -[*]-/xms;
+
+    # Note: If PPI changes away from native newlines, this may break
+    my ($first_line, $second_line) = $code =~ m/\A ([^\n]*) (?: \n ([^\n]*) )? /xms;
+    return if $first_line =~ m/$one_line_local_var/xms;
+    return if ($second_line && $first_line =~ m/\A #!/xms
+               && $second_line =~ m/$one_line_local_var/xms);
+
+
+    ## Look for end of doc file vars  Example:
+    #  Local Variables:
+    #   mode: cperl-mode
+    #  End:
+    my $last_page = substr $code, -3000;
+    # Look only at the last page (as delimited by "^L", aka "\f" aka formfeed)
+    $last_page =~ s/.*\f//xms;
+
+    # This regex is transliterated from emacs22 files.el
+    # Note that the [ \t]* before "End:" appears to be wrong, but is
+    # added for compatibility
+    return if $last_page =~ m/
+                          ^ ([^\n]*) Local [ ] Variables: [ \t]* ([^\n]*) $
+                            .*?
+                          ^ \1 [ \t]* End: [ \t]* \2 $
+                            /ixms;
+    
+
+    return $self->violation( $desc, $expl, $doc );
+}
+
+1;
+
+__END__
+
+#---------------------------------------------------------------------------
+
+=pod
+
+=for stopwords elisp syntaxes files.el
+
+=head1 NAME
+
+Perl::Critic::Policy::Editor::RequireEmacsFileVariables;
+
+=head1 DESCRIPTION
+
+Many text editors know how to find magic strings in files that
+indicate settings that work best for that file.  For example, the file
+can indicate that it expects four-character indentation.
+
+In emacs, this magic string is called "File Variables".  There are two
+syntaxes: C<-*- ... -*-> (single-line) and C<Local
+Variables:\n...\nEnd:> (multi-line).  Both syntaxes allow leading and
+trailing text on the line.
+
+The single-line syntax must be used on the first line of the file to
+be recognized, or on the second line if the first line is a shebang.
+The following example is explicitly allowed by Perl:
+
+   #!perl -w -*- cperl -*-
+
+The multi-line syntax must be used "in the last page" (that is, after
+the last formfeed) at the end of the file.  As of Emacs21, the "end of
+the file" is hard-coded to be the last 3000 bytes of the file (in the
+hack-local-variables function in files.el).  In this syntax, each line
+must begin and end with the same prefix/suffix pair.  That pair is
+defined by the text before and after the "Local Variables:" string.
+
+=head1 SEE ALSO
+
+L<Perl::Critic::Policy::Editor::RequireViModeline>
+
+L<http://www.gnu.org/software/emacs/manual/html_node/File-Variables.html>
+
+In Emacs, you can view the "File Variables" info node by typing:
+C<C-h>, C<i>, C<g>, C<(emacs)File Variables>
+
+Alternatively, you can execute the following elisp:
+
+  (info "(emacs)File Variables")
+
+=head1 AUTHOR
+
+Chris Dolan <cdolan@cpan.org>
+
+Michael Wolf <MichaelRWolf@att.net>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2006 Chris Dolan.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.  The full text of this license
+can be found in the LICENSE file included with this module.
+
+=cut
index 92e2656..a7b08f0 100644 (file)
@@ -202,6 +202,7 @@ sub native_policy_names {
       Perl::Critic::Policy::ControlStructures::ProhibitUntilBlocks
       Perl::Critic::Policy::Documentation::RequirePodAtEnd
       Perl::Critic::Policy::Documentation::RequirePodSections
+      Perl::Critic::Policy::Editor::RequireEmacsFileVariables
       Perl::Critic::Policy::ErrorHandling::RequireCarping
       Perl::Critic::Policy::InputOutput::ProhibitBacktickOperators
       Perl::Critic::Policy::InputOutput::ProhibitBarewordFileHandles
index 3affca6..b5458e5 100644 (file)
@@ -149,6 +149,10 @@ All POD should be after C<__END__> [Severity 1]
 
 Organize your POD into the customary sections. [Severity 2]
 
+=head2 L<Perl::Critic::Policy::Editor::RequireEmacsFileVariables>
+
+Use Emacs file variables to declare coding style. [Severity 2]
+
 =head2 L<Perl::Critic::Policy::ErrorHandling::RequireCarping>
 
 Use functions from L<Carp> instead of C<warn> or C<die>. [Severity 3]
index 2e316b7..d7aa127 100644 (file)
@@ -11,7 +11,7 @@
 use strict;
 use warnings;
 use PPI::Document;
-use Test::More tests => 1360;  # Add 14 for each new policy created
+use Test::More tests => 1374;  # Add 14 for each new policy created
 use English qw(-no_match_vars);
 
 our $VERSION = 0.21;
index a3f6893..4d08184 100644 (file)
@@ -21,6 +21,7 @@ Perl::Critic::TestUtils::block_perlcriticrc();
 my $profile = { '-CodeLayout::RequireTidyCode'          => {},
                 '-Miscellanea::RequireRcsKeywords'      => {},
                 '-Variables::ProhibitUnusedLexicalVars' => {},
+                '-Editor::RequireEmacsFileVariables'    => {},
 };
 
 my $code = undef;
diff --git a/t/20_policies_editor.t b/t/20_policies_editor.t
new file mode 100644 (file)
index 0000000..9523904
--- /dev/null
@@ -0,0 +1,119 @@
+#!perl
+
+##################################################################
+#      $URL$
+#     $Date$
+#   $Author$
+# $Revision$
+##################################################################
+
+use strict;
+use warnings;
+use Test::More tests => 9;
+
+# common P::C testing tools
+use Perl::Critic::TestUtils qw(pcritique);
+Perl::Critic::TestUtils::block_perlcriticrc();
+
+my $code ;
+my $policy;
+my %config;
+
+#----------------------------------------------------------------
+
+$code = <<'END_PERL';
+foo();
+END_PERL
+
+$policy = 'Editor::RequireEmacsFileVariables';
+is( pcritique($policy, \$code), 1, $policy.' - no vars');
+
+#----------------------------------------------------------------
+
+$code = <<'END_PERL';
+# -*- mode: cperl-mode -*-
+foo();
+END_PERL
+
+$policy = 'Editor::RequireEmacsFileVariables';
+is( pcritique($policy, \$code), 0, $policy.' - first line');
+
+#----------------------------------------------------------------
+
+
+$code = <<'END_PERL';
+#!/usr/bin/perl -w -*- mode: cperl-mode -*-
+foo();
+END_PERL
+
+$policy = 'Editor::RequireEmacsFileVariables';
+is( pcritique($policy, \$code), 0, $policy.' - first line, perl arg');
+
+#----------------------------------------------------------------
+
+$code = <<'END_PERL';
+#!/usr/bin/perl
+# -*- mode: cperl-mode -*-
+foo();
+END_PERL
+
+$policy = 'Editor::RequireEmacsFileVariables';
+is( pcritique($policy, \$code), 0, $policy.' - second line');
+
+#----------------------------------------------------------------
+
+$code = <<'END_PERL';
+# comment...
+# -*- mode: cperl-mode -*-
+foo();
+END_PERL
+
+$policy = 'Editor::RequireEmacsFileVariables';
+is( pcritique($policy, \$code), 0, $policy.' - second line, no shebang');
+
+#----------------------------------------------------------------
+
+$code = <<'END_PERL';
+foo();
+# Local Variables:
+# End:
+END_PERL
+
+$policy = 'Editor::RequireEmacsFileVariables';
+is( pcritique($policy, \$code), 0, $policy.' - multi-line');
+
+#----------------------------------------------------------------
+
+$code = <<'END_PERL';
+foo();
+\f
+# Local Variables:
+# End:
+END_PERL
+
+$policy = 'Editor::RequireEmacsFileVariables';
+is( pcritique($policy, \$code), 0, $policy.' - multi-line, after page break');
+
+#----------------------------------------------------------------
+
+$code = <<'END_PERL';
+foo();
+# Local Variables:
+# End:
+END_PERL
+$code .= 'A' x 3000;
+
+$policy = 'Editor::RequireEmacsFileVariables';
+is( pcritique($policy, \$code), 1, $policy.' - multi-line, too early');
+
+#----------------------------------------------------------------
+
+$code = <<'END_PERL';
+foo();
+# Local Variables:
+# End:
+\f
+END_PERL
+
+$policy = 'Editor::RequireEmacsFileVariables';
+is( pcritique($policy, \$code), 1, $policy.' - multi-line before page break');
index ca69189..3464e89 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 use strict;
 use warnings;
@@ -44,4 +44,4 @@ This program is free software; you can redistribute it and/or modify
 it under the same terms as Perl itself.  The full text of this license
 can be found in the LICENSE file included with this module.
 
-=cut
\ No newline at end of file
+=cut