2

I am trying to get the perlcritic minor mode (v 1.10) to work with the cperl mode. What happens is that the minor mode is activated for the buffer but is then deactivated. I've used edebug to try to figure out what's going on by instrumenting the perlcritic-mode function inside perlcritic.el. The section of code in that function that toggles the mode is cycled through twice for some reason I haven't been able to figure out. If I invoke the minor mode from the minibuffer (M-x perlcritic-mode) you can actually see Critic (the label for the minor mode) appear on the mode line the first time through that section and then disappear as the code is cycled through again and the minor mode is toggled off.

The method is autoloaded in my .emacs using the code I copied out of perlcritic.el. I don't know if its somehow been loaded twice but during debugging I see two entries under the Emacs-Lisp menu. I have no idea if that's significant or not.

Here are the relevant sections of my .emacs:

(add-to-list 'load-path "~/.emacs.d/elpa/perlcritic-1.10")

(add-to-list 'auto-mode-alist '("\\.pl\\'" . cperl-mode))
(add-to-list 'auto-mode-alist '("\\.pm\\'" . cperl-mode))
(add-to-list 'interpreter-mode-alist '("perl" . cperl-mode))

(autoload 'perlcritic        "perlcritic" "" t)
(autoload 'perlcritic-region "perlcritic" "" t)
(autoload 'perlcritic-mode   "perlcritic" "" t)

(eval-after-load "cperl-mode"
  '(add-hook 'cperl-mode-hook 'perlcritic-mode))

Note that I need to use both the auto-mode-alist and the interpreter-mode-alist to get the cperl mode for existing scripts and modules as well as new scripts and modules. The reason has to do with pre-defined values in those lists that map Perl stuff to perl-mode. I don't know if this is what's causing the problem I'm seeing but I did some testing and it did not seem to be the case.

Here's the function in perlcritic.el that's behaving strangely:

(defun perlcritic-mode (&optional arg)
  "Perl::Critic checking minor mode."
  (interactive "P")

  ;; Enable/disable perlcritic-mode
  (setq perlcritic-mode (if (null arg)
                ;; Nothing! Just toggle it.
                (not perlcritic-mode)
              ;; Set it.
              (> (prefix-numeric-value arg) 0)))

;;  (make-local-hook 'write-file-hooks)
  (if perlcritic-mode
      (add-hook 'write-file-hooks 'perlcritic-write-hook)
    (remove-hook 'write-file-hooks 'perlcritic-write-hook)))


;; Make a nice name for perl critic mode. This string will appear at
;; the bottom of the screen.
(if (not (assq 'perlcritic-mode minor-mode-alist))
    (setq minor-mode-alist
          (cons '(perlcritic-mode " Critic")
                minor-mode-alist)))

The part that it cycles through twice is the section under the comment Enable/disable perlcritic-mode.

Update: I used the d command in edebug each time the debugger hit the line stat begins (setq perlcritic-mode.

Both traces showed the same information:

  perlcritic-mode()
  run-hooks(change-major-mode-after-body-hook prog-mode-hook cperl-mode-hook cperl-mode-hook)
  apply(run-hooks (change-major-mode-after-body-hook prog-mode-hook cperl-mode-hook cperl-mode-hook))
  run-mode-hooks(cperl-mode-hook)
  cperl-mode()
  set-auto-mode-0(cperl-mode nil)
  set-auto-mode()
  normal-mode(t)
  after-find-file(t t)
  find-file-noselect-1(#<buffer junk.pl> "~/junk.pl" nil nil "~/junk.pl" nil)
  find-file-noselect("~/junk.pl" nil nil t)
  find-file("~/junk.pl" t)
  funcall-interactively(find-file "~/junk.pl" t)
  call-interactively(find-file nil nil)
  command-execute(find-file)

I don't know if the fact that cperl-mode-hook appears twice in the lines beginning with run-hooks and apply is significant or what would cause that.

3
  • Thanks. I tried that and this is what I found. Both times I hit the section that starts with setq perlcritic_mode I see: perlcritic-mode() run-hooks(change-major-mode-after-body-hook prog-mode-hook cperl-mode-hook cperl-mode-hook) apply(run-hooks (change-major-mode-after-body-hook prog-mode-hook cperl-mode-hook cperl-mode-hook)) run-mode-hooks(cperl-mode-hook) cperl-mode()
    – roadrider
    May 26, 2017 at 14:12
  • I timed out on editing the comment but what I noticed is that cperl-mode-hook appears twice in run-hooks and apply. I don't know enough about the internals of emacs to know if that's significant or not or what might be causing it.
    – roadrider
    May 26, 2017 at 14:19
  • I have updated the question with the backtrace information.
    – roadrider
    May 26, 2017 at 14:53

1 Answer 1

1
  ;; Enable/disable perlcritic-mode
  (setq perlcritic-mode (if (null arg)
                ;; Nothing! Just toggle it.
                (not perlcritic-mode)

perlcritic-mode toggles itself when you pass it no arguments, but you really just want to enable, so you could do something like this in your .emacs:

(defun turn-on-perlcritic-mode ()
  (perlcritic-mode +1))

(eval-after-load "cperl-mode"
  '(add-hook 'cperl-mode-hook 'turn-on-perlcritic-mode))

Note that define-minor-mode was updated in Emacs 24.1 so that minor modes using this macro would already have this behaviour by default; therefore you usually don't need to go through the trouble of using a wrapper function like that. From etc/NEWS.24:

* Incompatible Lisp Changes in Emacs 24.1

** Passing a nil argument to a minor mode function call now ENABLES
the minor mode unconditionally.  This is so that you can write e.g.

 (add-hook 'text-mode-hook 'foo-mode)

to enable foo-mode in Text mode buffers, removing the need for
`turn-on-foo-mode' style functions.  This affects all mode commands
defined by `define-minor-mode'.  If called interactively, the mode
command still toggles the minor mode.

I don't know if the fact that cperl-mode-hook appears twice in the lines beginning with run-hooks and apply is significant or what would cause that.

I think this is a bug in cperl-mode, it has:

(define-derived-mode cperl-mode prog-mode "CPerl"
  ...
  (run-mode-hooks 'cperl-mode-hook)

But define-derived-mode already produces code to run the hooks, so the macroexpansion gives:

(progn
  ...
  (defun cperl-mode () ...
         ...
         (delay-mode-hooks
           ...
           (run-mode-hooks 'cperl-mode-hook)
           ...)
         (run-mode-hooks 'cperl-mode-hook)))
3
  • Thanks for that. I tried the run-on function and it seems to work although since I'm running emacs 25.1 it sounds like it shouldn't be necessary. I may try altering the cperl-mode.el when I get a chance and will update if it leads to anything interesting.
    – roadrider
    May 26, 2017 at 17:58
  • Emacs Lisp looks pretty interesting. I've never delved into it that much but this has kind of whetted my appetite.
    – roadrider
    May 26, 2017 at 18:04
  • @roadrider turn-on-perlcritic-mode wouldn't have been necessary if perlcritic-mode was defined using define-minor-mode, but unfortunately, it's just a plain defun which doesn't follow the newer minor mode convention.
    – npostavs
    May 26, 2017 at 18:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.