You are configuring Emacs incorrectly: use-package

ABOUT, use-package!

This is the package that literally changed everything. If before init.el filled with code in imperative style, then with the advent of use-package many Emacs users have begun to describe their settings in a declarative style.

The package has had such a big impact on Emacs settings management that, starting with Emacs 29 use-package has become builtin, meaning it no longer requires manual installation (although it can still be installed or updated from other sources).

There is a whole bunch of stuff on the GNU documentation site chapterdedicated use-packageand everything seems to be fine, but… I wouldn’t have written this article then.

Incorrect use of :init and :config

The first mistake users make use-package — they are confused :init And :config. I gave a link to the documentation above, you can read the original. Here I will tell you in my own words.

In the block :init you need to add code that is executed before downloading the package.

Let's say you have a code snippet like this:

(use-package anzu
  :init
  (global-anzu-mode 1))

This code is equivalent to the following imperative-style code:

(global-anzu-mode 1)
(require 'anzu)

As you can see, the function from the module is called before it is loaded. Needless to say, this code won't work?

The correct solution is to place package function calls inside a block :config:

(use-package anzu
  :config
  (global-anzu-mode 1))

Abuse of use-package-always-ensure

IN use-package there is such a built-in functionality as automatic installation of uninstalled packages. You can enable global setting use-package-always-ensureand then use-package will try to install all uninstalled packages automatically:

(require 'use-package)
(custom-set-variables '(use-package-always-ensure t))

It's all good if you use Emacs only on your local computer or the same typical environments: Ubuntu 20.04 LTS at home and Ubuntu 20.04 LTS on all machines at work. In this case, you will very rarely encounter the fact that use-package cannot install the package because the package author has increased the Emacs version requirements.

In this case, each execution of your init.el will fail to install packages.

Let's say a package markdown-mode.el for some time now it has started to require Emacs 27.1 or newer.

A naive and non-working solution (remember, use-package-always-ensure t) c :if, :when or :unless:

(use-package ace-window
  :when (or ((> emacs-major-version 27)
           (and (= emacs-major-version 27)
                (>= emacs-minor-version 1)))))

The problem is that all the code settings the package is executed after it installationsAnd :if does not interfere with the installation process in any way:

The :if, :whenand :unless keywords predicates the loading and initialization of packages. They all accept one argument, an Emacs Lisp form that is evaluated at run-time.

:requires won't help you either. The code in this block is also executed after installations package.

Maybe then we won't appropriate it use-package-always-ensure meaning tbut we will simply write in the block :ensure the right expression? Let's try!

Initial conditions:

  • Emacs 29.3.

  • use-package v2.4.5.

Let's add in init.el such code:

(use-package all-the-icons
  :ensure (> emacs-major-version 27))

Let's launch init.el by using eval-buffer… Oh!

⛔ Error (use-package): Failed to parse package all-the-icons: use-package: :ensure wants an optional package name (an unquoted symbol name), or (<symbol> :pin <string>)

What to do? Obviously, check the Emacs version and other requirements before call use-package. If you have a lot of packages that don't work in certain environments, it might make sense to write a function like this:

;; Возвращает t, если версия Emacs больше или равна указанной.
(defun emacs-version-not-less-than (major minor)
  "True when Emacs version is not less than MAJOR and MINOR versions."
  (or
    (> emacs-major-version major)
    (and (= emacs-major-version major)
      (>= emacs-minor-version minor))))

You need to use it like this:

(when (emacs-version-not-less-than 27 1)
  (use-package buffer-env
    :ensure t
    :pin "gnu"
    :defer t
    :after (files)
    :hook ((
             hack-local-variables
             comint-mode
             ) . buffer-env-update)))

(when (emacs-version-not-less-than 28 1)
  (use-package denote
    :pin "gnu"
    :ensure t
    :custom
    (denote-directory "~/Документы/Notes/" "Каталог для хранения заметок.")))

Plastic bag buffer-env will be installed only if the Emacs version is greater than or equal to 27.1 and the package denote — 28.1.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *