My new init.el
uses require
to load various settings that I have organised into directories and files. This is an attempt to tame what was a very large and messy init.el
and custom.el
file. Also I ultimately hope to use org
files to better document each setting.
I'm trying (and largely failing) to make this setup play nicely with customize
.
My old init.el
has grown over a few years and makes liberal use of setq
.
But reading this: Advantages of setting variables with setq instead of custom.el?
And these: https://www.reddit.com/r/emacs/comments/b1jzno/need_help_making_my_initel_file_look_cleaner_its/
https://www.reddit.com/r/emacs/comments/9rrhy8/emacsers_with_beautiful_initel_files_what_about/
I decided to try replacing setq
with custom-set-variables
for custom variables.
The problem I have is that despite the fact that I've explicitly set variables using custom-set-variables
, customize
appears to take all my explicit settings and appends them to the end of my init.el
or in a separate file if custom-file
is set. This duplicates the settings needlessly, and create more, not less mess.
What's worse is that complicated expressions seems to get rendered incorrectly when trampled, the below:
(let* ((default-directory temporary-file-directory)
(emacs-temp (expand-file-name current-user "emacs-saves")))
(custom-set-variables '(backup-directory-alist (`(("." . ,emacs-temp))))
'(auto-save-file-name-transforms (`(("." ,emacs-temp t))))))
Is trampled with custom.el
containing an out of scope emacs-temp
. No such issues are seen using setq
:
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(auto-save-file-name-transforms ((\` (("." (\, emacs-temp) t)))))
'(backup-by-copying t)
'(backup-directory-alist ((\` (("." \, emacs-temp))))))
It appears I can prevent customize writing anything out by using:
(setq custom-file null-device)
But this is problematic because customize
settings made outside of my init.el
and it's require
statements, will then never be recorded. Apart from my own (very occasional) use of customize
, other packages may try to write to the custom file - eg package-selected-packages
.
The way I see it - customize
should only be writing out settings that are not already explicitly set in init.el
, etc. This will avoid trampling of any settings I've explicitly made.
Any other settings made within customize
(or elsewhere) that would not trample anything explicitly set in init.el
, could then be appended to init.el
, or directed elsewhere by custom-file
.
The closest behaviour to this seems to be the use of setq
not custom-set-variables
which seems to be ignored by customize
. However I take the points that use of defcustom
keywords means that setq
use can be problematic.
My question is - how do I correctly and safely initialize custom values in my initialization script(s), such that they are not then trampled by customize
saving these values back down to a file, and without preventing other customize
values from being saved to file?
Will use-package
help me? Can I make setq
use safe without interleaving setup before and after initialization?
EDIT
Responding to some feedback, I'm going to walk through a few simple cases step by step.
As you'll see below following through a simple example as suggested by @Drew yields the correct result. However a more complex example using require
to pull in programmatically set customizable variables reproduces the issue.
Attempt 1: Simplest Example - This works as expected
- Take a fresh install of emacs (for this I used v25.2.2)
- Inside the empty ~/.emacs.d place an
init.el
file containing a single line(setq backup-by-copying t)
.~/.emacs.d$ echo '(setq backup-by-copying t)' > init.el
. This represents a value that I do not want to set directly incustomize
as I want to organize my own setup into more readable sections over several files/directories - I've pickedbackup-by-copying
at arbitrarily. - I then run emacs and see the following in
customize
-Backup By Copying
,[ State ]: CHANGED outside Customize.
. - So far no surprises. Now let's set a completely unrelated item directly in
customize
. The logic here, is that either via automated other packages or directly by me, it's possible that something is required to be recorded directly incustomize
appended toinit.el
(by default). So in thecustomize
UI I search for andToggle
Create Lockfiles
. - This gives me
[ State ]: EDITED, shown value does not take effect until you set or save it.
- To save this (and hopefully only this) setting to the end of
init.el
I'm clicking on[ Apply and Save ]
- emacs confirms
Wrote /home/foo/.emacs.d/init.el
- The contents of my
init.el
is now:
(setq backup-by-copying t)
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(create-lockfiles nil))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)
OK - so far everything is as expected, now I'll repeat, overwriting the original init.el
, replacing setq
with custom-set-variables
in point 2:
- As above
~/.emacs.d$ echo "(custom-set-variables '(backup-by-copying t))" > init.el
- In
customize
I now seeBackup By Copying
set to[ State ]: SAVED and set.
- Same as above
- Same as above
- Same as above
- Same as above
- The contents of my init.el is now
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(backup-by-copying t)
'(create-lockfiles nil))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)
Nothing wrong with this - '(create-lockfiles nil)
has been pulled under the control of customize
. There is a single, correct, copy of the setting!
So now I will try to isolate the case I previous had seen.
Attempt 2: Using require
Example - Doesn't work as I had expected
Repeating what was done in Attempt 1 above, but with a sightly more complex init.el
structure.
Create a subdirectory under .emacs.d
called settings
, this is going to contain a single lisp file file-settings.el
which we will require
from init.el
.
echo "(add-to-list 'load-path (expand-file-name \"settings\" user-emacs-directory))" > init.el
followed byecho "(require 'file-settings)" >> init.el
echo "(setq backup-by-copying t)" > settings/file-settings.el
followed byecho "(provide 'file-settings)" >> settings/file-settings.el
- Same as
setq
for Attempt 1 -[ State ]: CHANGED outside Customize.
- Same as Attempt 1
- Same as Attempt 1
- Same as Attempt 1
- Same as Attempt 1
- The contents of my init.el is now:
(add-to-list 'load-path (expand-file-name "settings" user-emacs-directory))
(require 'file-settings)
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(create-lockfiles nil))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)
So far all good. Now, again, let's swap-out setq
for custom-set-variables
- As above
echo "(custom-set-variables '(backup-by-copying t))" > settings/file-settings.el
followed byecho "(provide 'file-settings)" >> settings/file-settings.el
- Same as Attempt 1 - In
customize
I now seeBackup By Copying
set to[ State ]: SAVED and set.
- Same as above
- Same as above
- Same as above
- Same as above
- The contents of my init.el is now
(add-to-list 'load-path (expand-file-name "settings" user-emacs-directory))
(require 'file-settings)
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(backup-by-copying t)
'(create-lockfiles nil))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)
BUT the contents of settings/file-settings.el
is still:
(custom-set-variables '(backup-by-copying t))
(provide 'file-settings)
So the setting of backup-by-copying
has been duplicated?
Is it possible to use require
(or similar) to organize my config into a modular structure of directories, without running into this issue?
(custom-set-variables 'FOO)
, hence a verbatimFOO
is set and saved.M-x customize-option
). That's the problem: you set and saved the value using Customize. And then you also set it in your init file (with code that correctly bindsemacs-temp
etc.).customize
writes out the incorrect version which tramples it? I'm retesting how it gets trampled now - I'm not (intentionally) using both methods to cause the trampling myself, but this would explain the behaviour... I'll deletecustom.el
and restart emacs. My belief is that I can showcustom.el
being recreated and trampling my settings without me setting any of the trampled values incustomize
interface.(setq FOO BAR)
into(custom-set-variables '(FOO BAR))
, without taking into account the fact that the latter form is quoted. (I can't see an original working/setq version of that code, though, so I'm only guessing.)