emacs: keep it clean

I want to keep the ~/.emacs.d folder as clean as possible. The no-littering project helps wit that.

The default paths used to store configuration files and persistent data are not consistent across Emacs packages. This isn’t just a problem with third-party packages but even with built-in packages.

Some packages put these files directly in user-emacs-directory or $HOME or in a subdirectory of either of the two or elsewhere. Furthermore sometimes file names are used that don’t provide any insight into what package might have created them.

This package sets out to fix this by changing the values of path variables to put configuration files in no-littering-etc-directory (defaulting to ~/.emacs.d/etc/) and persistent data files in no-littering-var-directory (defaulting to ~/.emacs.d/var/), and by using descriptive file names and subdirectories when appropriate. This is similar to a color-theme; a “path-theme” if you will.

Let’s configure it and make sure we load it as soon as possible (hence the config/00-clean.el).

As I am loading recentf during this cleanup part, I need to setup recentf before 😅. In a gist:

;;; 00-clean.el --- -*- lexical-binding: t; -*-
;;; Commentary:
;;; no-littering and recentf configurations
;;; Note: this file is autogenerated from an org-mode file.
;;; Code:
(use-package recentf
  :config
  (setq recentf-max-saved-items 200
        recentf-auto-cleanup 360
        recentf-show-file-shortcuts-flag nil)
  (recentf-mode 1)
  (add-to-list 'recentf-exclude "^/\\(?:ssh\\|su\\|sudo\\)?:")
  ;; Magic advice to rename entries in recentf when moving files in
  ;; dired.
  (defun rjs/recentf-rename-notify (oldname newname &rest args)
    (if (file-directory-p newname)
        (rjs/recentf-rename-directory oldname newname)
      (rjs/recentf-rename-file oldname newname)))

  (defun rjs/recentf-rename-file (oldname newname)
    (setq recentf-list
          (mapcar (lambda (name)
                    (if (string-equal name oldname)
                        newname
                      oldname))
                  recentf-list))
    recentf-cleanup)

  (defun rjs/recentf-rename-directory (oldname newname)
    ;; oldname, newname and all entries of recentf-list should already
    ;; be absolute and normalised so I think this can just test whether
    ;; oldname is a prefix of the element.
    (setq recentf-list
          (mapcar (lambda (name)
                    (if (string-prefix-p oldname name)
                        (concat newname (substring name (length oldname)))
                      name))
                  recentf-list))
    recentf-cleanup)

  (advice-add 'dired-rename-file :after #'rjs/recentf-rename-notify))

(use-package no-littering               ; Keep .emacs.d clean
  :config
  (require 'recentf)
  (add-to-list 'recentf-exclude no-littering-var-directory)
  (add-to-list 'recentf-exclude no-littering-etc-directory)

  ;; Move this in its own thing
  (setq
   create-lockfiles nil
   delete-old-versions t
   kept-new-versions 6
   kept-old-versions 2
   version-control t)

  (setq
   backup-directory-alist
   `((".*" . ,(no-littering-expand-var-file-name "backup/")))
   auto-save-file-name-transforms
   `((".*" ,(no-littering-expand-var-file-name "auto-save/") t))))

(provide '00-clean)
;;; 00-clean.el ends here