I end up looking at source files of packages I have installed fairly often and want to avoid accidentally editing them while I am viewing them. After looking at https://emacs.stackexchange.com/a/3681/767, I put together this:

 '((nil . ((buffer-read-only . t)
           (show-trailing-whitespace . nil)
           (tab-width . 8)
           (eval . (whitespace-mode -1))))))
(dir-locals-set-directory-class (concat user-emacs-directory "elpa") 'emacs)
 (locate-dominating-file (locate-library "winner") "lisp") 'emacs)

This worked fine until I went to install a new package. At that point, the machinery kicks in to create autoloads and then fails to write them because they are considered to be read-only.

I suspect I could do something with defadvice but maybe my whole approach needs to be rethought. Has anyone else already solved this?

  • 1
    You can also use C-x C-q to change a buffer's read-only status. (And there's always undo ;-).) – Drew Dec 17 '18 at 18:27
  • 2
    Please M-x report-emacs-bug about the failure to install packages with such a config. – Stefan Dec 17 '18 at 19:13
  • The package hardhat can help here, too. – clemera Dec 21 '18 at 21:39

I worked around this before by making an exception for autoloads. From my config:

(define-derived-mode emacs-lisp-autoloads-mode emacs-lisp-mode "Autoloads"
  "Marker mode for package autoloads files.")
(add-to-list 'auto-mode-alist '("autoloads\\.el\\'" . emacs-lisp-autoloads-mode))
(add-to-list 'auto-mode-alist '("loaddefs\\.el\\'" . emacs-lisp-autoloads-mode))

 '((nil . ((buffer-read-only . t)))
   ;; Keep autoloads writeable so we can update packages
   (emacs-lisp-autoloads-mode . ((buffer-read-only . nil)))))

This defines a mode for autoloads and associates it with the typical file names. Then the dir-locals class sets the buffer to read-only, except for buffers in emacs-lisp-autoloads-mode. You can associate this 'read-only class with your elpa directory as you were doing before.


Adding following hack, which temporarily inhibits read-only-mode, is working for me:

 :around (lambda (orig-fun &rest args)
           (let ((inhibit-read-only t))
              (apply orig-fun args))))

The full relevant config looks like:

(dir-locals-set-directory-class (concat user-emacs-directory "elpa") 'emacs)
 (locate-dominating-file (locate-library "winner") "lisp") 'emacs)
 :around (lambda (orig-fun &rest args)
           (let ((inhibit-read-only t))
              (apply orig-fun args))))
  • What about just let-binding inhibit-read-only ? – phils Dec 18 '18 at 0:56
  • Sounds even better than what I came up with. I’ll give it a try tomorrow! – Gina White Dec 18 '18 at 1:43
  • inhibit-read-only worked great, I have updated the the answer with that, thank you! – Gina White Dec 18 '18 at 16:28

I think your config is sound. Too bad it trips up package.el. While waiting for this to be fixed, you can try the following instead:

(add-hook 'find-file-hook #'my-find-file-tweaks)
(defun my-find-file-tweaks ()
  (when (and (file-in-directory-p buffer-file-name "~/.emacs.d/elpa")
             (not delay-mode-hooks))
    (read-only-mode 1)))

where the (not delay-mode-hooks) test is an ugly hack that tries to detect the case where package.el generates the autoloads.

  • I don't understand how this hack is intended to work, but I gave it a try and I got the same failure (about trying to modify a read-only buffer). – Gina White Dec 17 '18 at 22:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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