Migrating to use no-littering

Table of Contents

1. Introduction

This document describes migration towards using no-littering for path theming.

2. Steps

2.1. Prepare code

You must not install no-littering with a package manager because it might automatically get loaded. Make sure installing it won't load it. If you are not sure, clone the repository somewhere but do not load or require the package. We need this to preserve the existing values set to themed variables.

Before you start, tangle this file by running M-x org-babel-tangle and load no-littering-migration.el, for example by opening dired and hitting L on the file.

2.2. Save current values of your settings

First, we need to save the current values of the themed variables to be able to move the files from their existing locations. Execute the code below:

(setq no-littering-current-values
      (no-littering-get-current-variables))

2.3. Load no-littering package

Now you can install the package with your package manager of choice. After installation, run:

(require 'no-littering)

2.4. Save new values

Now we save the new themed values set by no-littering.

(setq no-littering-new-values (no-littering-get-current-variables))

2.5. Generate migrations

We now have the old list and a new list and we can compare the existing values to new values. The code below generates a migration script which you have to review and execute manually. In order to generate the script, place the cursor inside the code block and press C-c C-c.

(no-littering-generate-migration no-littering-current-values
                                 no-littering-new-values)

The result is a list of progn forms which can be evaluated by placing the point at the end of the progn form and calling M-x eval-last-sexp (usually bound to C-x C-e).

The form looks like this:

(progn
  "abbrev-file-name" ; name of the variable
  (make-directory
   ;; create a parent dir for target
   "/home/matus/.config/emacs/etc/" t)
  (rename-file
   ;; old (current) file
   "/home/matus/.config/emacs/abbrev_defs"
   ;; new file
   "/home/matus/.config/emacs/etc/abbrev.el"
   1))

If you are satisfied, execute it and move to the next one.

Some of the forms might error out when the source file does not exist. In that case there's nothing to do and you can move to the next one.

Occasionally, the generated code is not valid and you need to do the migration manually.

2.6. Remove customized values

If you were using customize-variable before, you need to remove customized settings or they will keep overwriting the values managed by no-littering. Simply run the following to remove all customizations.

Make sure to backup your custom file before running this so you can compare the results.

(no-littering-custom-reset)
(custom-save-all)

Likewise, remove any other customization of the paths managed by no-littering.

3. Code

(defun no-littering-get-themed-variables ()
  "Return all variables recognized by no-littering."
  (with-temp-buffer
    (insert-file-contents "no-littering.el")
    (goto-char (point-min))
    (re-search-forward "^(cl-letf")
    (beginning-of-line)
    (down-list)
    (forward-sexp 4)
    (let* ((data (cdr (read (current-buffer))))
           (vars (cl-remove-if-not
                  (lambda (form) (eq (car form) 'setq)) data))
           (current-values
            (mapcar
             (lambda (form)
               (let* ((var (cadr form))
                      (value (and (boundp var)
                                  (symbol-value var))))
                 (when (stringp value)
                   (setq value (file-truename value)))
                 (cons var value)))
             vars)))
      current-values)))

(defun no-littering-get-current-variables ()
  "Return all variables recognized by no-littering currently used."
  (cl-remove-if
   (lambda (var) (not (cdr var)))
   (no-littering-get-themed-variables)))

(defun no-littering-generate-migration (old-values new-values)
  (let* ((migrations
          (mapcar (lambda (var)
                    (list (car var)
                          (cdr var)
                          (cdr (assq (car var) new-values))))
                  old-values))
         (commands
          (mapcar (lambda (var)
                    (if (stringp (nth 2 var))
                        (when (not (equal (nth 1 var) (nth 2 var)))
                          `(progn
                             ,(format "%s" (car var))
                             (make-directory
                              ,(file-name-directory (nth 2 var))
                              t)
                             (rename-file ,(nth 1 var) ,(nth 2 var) 1)))
                      `(progn ,(car var) "Value is not a directory")))
                  migrations)))
    commands))

(defun no-littering--custom-reset-symbol (symbol)
  (put symbol 'variable-comment nil)
  (put symbol 'standard-value nil)
  (put symbol 'customized-value nil)
  (put symbol 'customized-variable-comment nil)
  (when (or (get symbol 'saved-value)
            (get symbol 'saved-variable-comment))
    (put symbol 'saved-value nil)
    (put symbol 'saved-variable-comment nil)))

(defun no-littering-custom-reset ()
  (dolist (var (no-littering-get-themed-variables))
    (no-littering--custom-reset-symbol (car var))))

Created: 2024-10-29 Tue 19:11

Validate