Hack 6. Use the Best Emacs Mode for Perl


Hack 6. Use the Best Emacs Mode for Perl

Configure Emacs for easy Perl coding.

While perl-mode is the classic Perl-editing mode that Emacs uses for Perl files by default, most Perl programmers prefer the newer cperl-mode. (The "c" in the name is because its early versions borrowed code from c-mode. It's not actually written in C, nor meant for C.) Enabling it is easy.

The Hack

cperl-mode is probably already included in your version of Emacs, but you can get an up-to-date version from http://math.berkeley.edu/~ilya/software/emacs/. Save it to an Emacs library directory. Then enable it for .pl and .pm files by adding nine lines to your ~/.emacs file:

(load-library "cperl-mode")
  (add-to-list 'auto-mode-alist '("\\\\.[Pp][LlMm][Cc]?$" . cperl-mode))
  (while (let ((orig (rassoc 'perl-mode auto-mode-alist)))
              (if orig (setcdr orig 'cperl-mode))))
  (while (let ((orig (rassoc 'perl-mode interpreter-mode-alist)))
           (if orig (setcdr orig 'cperl-mode))))
  (dolist (interpreter '("perl" "perl5" "miniperl" "pugs"))
    (unless (assoc interpreter interpreter-mode-alist)
      (add-to-list 'interpreter-mode-alist (cons interpreter 'cperl-mode))))

What can you do with it?

Put Perldoc at your fingertips

cperl-mode provides a handy function for calling perldoc, but does not associate it with any key by default. To put it at your fingertips, add one line to your .emacs file:

(global-set-key "\\M-p" 'cperl-perldoc) ; alt-p

If you want to use Pod::Webserver [Hack #3], use one of the various in-Emacs web browsers:

(global-set-key "\\M-p" '(lambda ( ) (interactive)
  (require 'w3m)
  (w3m-goto-url "http://localhost:8020/")
))

If you prefer your normal web browser, just set some particular key to start it up on the Pod::Webserver page:

(global-set-key "\\M-p"
  '(lambda ( ) (interactive) (start-process "" nil
  "firefox" "http://localhost:8020/"
  ; Or however you launch your favorite browser, like:
  ;   "gnome-terminal" "-e" "lynx http://localhost:8020/"
  ;   "xterm" "-e" "elinks http://localhost:8020/"
)))

Use a special mode just for Pod

One problem with both cperl-mode and perl-mode is that they both treat Pod the same: they just ignore it. To get better syntax highlighting for Pod, switch to the pod-mode. It probably isn't part of your Emacs distribution, so you download the latest version from http://www.cpan.org/authors/id/S/SC/SCHWIGON/pod-mode/.

Once installed, enable it in your .emacs file with:

(require 'pod-mode)
(add-to-list 'auto-mode-alist
  '("\\\\.pod$" . pod-mode))

; You might appreciate turning on these
;   features by default for Pod:

(add-hook 'pod-mode-hook '(lambda ( ) (progn
 (font-lock-mode)   ; =syntax highlighting
 (auto-fill-mode 1) ; =wordwrap
 (flyspell-mode 1)  ; =spellchecking
                  
)))


Hack 7. Enforce Local Style

Keep your code clean without editing it by hand.

One of the first barriers to understanding code written by others is that their formatting style may not match yours. This is especially true if you find yourself maintaining code that, at best, has grown with little direction over the years. Whether you work with other developers and want to maintain a consistent set of coding guidelines, or you want to find some structure in a big ball of mud, perltidy can help untangle and bring consistency to even the scariest code.

The Hack

Install the CPAN module Perl::Tidy. This will also install the perltidy utility. Now you can use it!

From the command line

Run perltidy on a Perl program or module and it will write out a tidied version of that file with a .tdy suffix. For example, given poorly_written_script.pl, perltidy will, if possible, reformat the code and write the new version to poorly_written_script.pl.tdy. You can then run tests against the new code to verify that it performs just as did the previous version (even if it is much easier to read).

This command reformats the contents of some_ugly_code.pl so that it's no longer, well, ugly. How effective is it? The Perltidy docs offer an example. Before:

$_= <<'EOL';
   $url = URI::URL->new( "http://www/" );   die if $url eq "xXx";
EOL
LOOP:{print(" digits"),redo LOOP if/\\G\\d+\\b[,.;]?\\s*/gc;print(" lowercase"),
redo LOOP if/\\G[a-z]+\\b[,.;]?\\s*/gc;print(" UPPERCASE"),redo LOOP
if/\\G[A-Z]+\\b[,.;]?\\s*/gc;print(" Capitalized"),
redo LOOP if/\\G[A-Z][a-z]+\\b[,.;]?\\s*/gc;
print(" MiXeD"),redo LOOP if/\\G[A-Za-z]+\\b[,.;]?\\s*/gc;print(
" alphanumeric"),redo LOOP if/\\G[A-Za-z0-9]+\\b[,.;]?\\s*/gc;print(" line-noise"
),redo LOOP if/\\G[^A-Za-z0-9]+/gc;print". That's all!\\n";}

After:

$_ = <<'EOL';
   $url = URI::URL->new( "http://www/" );   die if $url eq "xXx";
EOL
LOOP: {
    print(" digits"),       redo LOOP if /\\G\\d+\\b[,.;]?\\s*/gc;
    print(" lowercase"),    redo LOOP if /\\G[a-z]+\\b[,.;]?\\s*/gc;
    print(" UPPERCASE"),    redo LOOP if /\\G[A-Z]+\\b[,.;]?\\s*/gc;
    print(" Capitalized"),  redo LOOP if /\\G[A-Z][a-z]+\\b[,.;]?\\s*/gc;
    print(" MiXeD"),        redo LOOP if /\\G[A-Za-z]+\\b[,.;]?\\s*/gc;
    print(" alphanumeric"), redo LOOP if /\\G[A-Za-z0-9]+\\b[,.;]?\\s*/gc;
    print(" line-noise"),   redo LOOP if /\\G[^A-Za-z0-9]+/gc;
    print ". That's all!\\n";
}

Big difference!

Perltidy is of course great for enforcing a particular coding style as you work, but it's also a lifesaver when the task of maintaining someone else's spaghetti code suddenly falls on you.

The default is good for the paranoid. For the adventurous, use the -b flag, which modifies the files in place and writes the originals to backup files. For example running perltidy -b scary_script.pl will produce a tidied scary_script.pl, if possible, and a scary_script.pl.bak.

This operation is not idempotentperltidy will overwrite an existing backup file of the same name, if it exists.


The default formatting options may be inappropriate for your use. Perl::Tidy looks for a .perltidyrc file, first in your current directory, next in your home directory, and then in system-wide directories. The contents of this file are simple; they're the same command line switches that perltidy uses. For example, the author's preferred .perltidyrc file contains:

-ci=4 # indent 4 spaces when breaking a long line
-et=4 # replace 4 leading spaces with a tab
-bl   # place opening braces on newlines

See man perltidy for a complete list of formatting options.

Within Vim

The perltidy program is also useful from within text editors that can call external programs. This makes it possible to tidy code within an editor, without saving and opening external filesit's great for figuring out what poorly indented code does. From Vim, run it on the entirety of the current buffer with the ex command %! perltidy. It also makes a great Vim mapadd to your .vimrc file something like:

map ,pt  <Esc>:%! perltidy<CR>
map ,ptv <Esc>:'<,'>! perltidy<CR>

Then in edit mode, type ,pt and perltidy will reformat the contents of the current buffer. Select a region and ,ptv will format its contents.

If you have a coding style that differs from the default values, add the command-line options to the maps.


Within Emacs

If you use Emacs to edit your Perl code, you can be virtuously lazy when it comes to reformatting your code. Just drop a bit of code into your ~/.emacs file and restart Emacs:

(defmacro mark-active ()
    "Xemacs/emacs compatibility macro"
    (if (boundp 'mark-active)
        'mark-active
      '(mark)))
(defun perltidy ( )
  "Run perltidy on the current region or buffer."
  (interactive)
  ; Inexplicably, save-excursion doesn't work here.
  (let ((orig-point (point)))
    (unless (mark-active) (mark-defun))
    (shell-command-on-region (point) (mark) "perltidy -q" nil t)
    (goto-char orig-point)))
(global-set-key "\\C-ct" 'perltidy)

Then the next time you open up a file full of spaghetti Perl, just hit C-c t and watch as the "paragraph" of nearby code magically becomes legible! Better yet, if you want to reformat the entire file, hit M-x mark-whole-buffer and then C-c t.

To make Emacs tidy your code automatically when you save it, add this snippet of code:

(defvar perltidy-mode nil
    "Automatically 'perltidy' when saving.")
  (make-variable-buffer-local 'perltidy-mode)
  (defun perltidy-write-hook ()
    "Perltidys a buffer during 'write-file-hooks' for 'perltidy-mode'"
    (if perltidy-mode
        (save-excursion
          (widen)
          (mark-whole-buffer)
          (not (perltidy)))
      nil))
  (defun perltidy-mode (&optional arg)
    "Perltidy minor mode."
    (interactive "P")
    (setq perltidy-mode
          (if (null arg)
              (not perltidy-mode)
            (> (prefix-numeric-value arg) 0)))
    (make-local-hook 'write-file-hooks)
    (if perltidy-mode
        (add-hook 'write-file-hooks 'perltidy-write-hook)
      (remove-hook 'write-file-hooks 'perltidy-write-hook)))
  (if (not (assq 'perltidy-mode minor-mode-alist))
      (setq minor-mode-alist
            (cons '(perltidy-mode " Perltidy")
                  minor-mode-alist)))
  (eval-after-load "cperl-mode"
    '(add-hook 'cperl-mode-hook 'perltidy-mode))

Run M-x perltidy-mode to disable or re-enable the automatic code tidying.