use-package icon indicating copy to clipboard operation
use-package copied to clipboard

Is :custom capable of setting minor-mode variables, or is it more limited than customization menu?

Open jkroes opened this issue 5 years ago • 21 comments

I noted that which-key doesn't seem to activate at all if I include the minor mode variable (which-key-mode) in :custom. If I call which-key-mode twice in succession, only now does which-key's popup activate. This issue arises with :custom (which-key-mode t); however, it doesn't arise if I use :config (which-key-mode t).

I'm guessing that minor-mode variables can be set via customization menu but not :custom? I had assumed the keyword was able to set the values of everything the customization menu could. Is it limited to variables and options?

jkroes avatar Jul 18 '20 05:07 jkroes

What Emacs version are you using? It worked well on 26.3 for me

a13 avatar Jul 28 '20 07:07 a13

I'm using Version 27.0.91. I was able to get around the issue by simply calling the mode function from :config, but I wasn't sure if either way should work.

jkroes avatar Aug 06 '20 00:08 jkroes

it looks like it was broken in #850

a13 avatar Aug 19 '20 08:08 a13

Yep. Here is an example of use-package expansion. As you can see, custom-set property is accessed before the variable is defined and use-package always fallbacks to set-default.

;; (use-package delsel
;;   :custom (delete-selection-mode t))
(progn
  (defvar use-package--warning69
    #'(lambda
        (keyword err)
        (let
            ((msg
              (format "%s/%s: %s" 'delsel keyword
                      (error-message-string err))))
          (display-warning 'use-package msg :error))))
  (condition-case-unless-debug err
      (progn
        (funcall
         (or
          (get 'delete-selection-mode 'custom-set)
          #'set-default)
         'delete-selection-mode t)
        (if
            (not
             (require 'delsel nil t))
            (display-warning 'use-package
                             (format "Cannot load %s" 'delsel)
                             :error)))
    (error
     (funcall use-package--warning69 :catch err))))

sirikid avatar Aug 19 '20 08:08 sirikid

I'm not sure of the best way here. I've always used something like this to activate a minor mode but understand the problem is that it's not the only way:

    :config
    (ivy-mode 1))
  • We could add a global and a local override to call customize-set-variable directly when the user knows it's needed (moves the work to them)
  • We could call customize-set-variable for any variable name ending with -mode (reverting to the old behavior)
  • We could call custom-load-symbol for any variable name ending with -mode (using the native custom.el code to load dependencies)
  • We could warn the user they should use :config for variables ending with -mode (ugly)

@jwiegley any opinions?

tzz avatar Aug 22 '20 22:08 tzz

I used to have minor mode turned on in :custom, but it wasn't enabled in some packages for some reason. An easy solution to this problem is to call minor mode directly as an argument of 1, as @tzz says.

This is already solved in leaf, where the user uses the :global-minor-mode keyword instead of the :custom keyword. I think use-package should also have leaf's :global-minor-mode like keyword. (It should be noted that I kept thinking of a better name for this keyword, but in the end I couldn't come up with a better name...)

conao3 avatar Aug 22 '20 23:08 conao3

  • We could call customize-set-variable for any variable name ending with -mode (reverting to the old behavior)
  • We could call custom-load-symbol for any variable name ending with -mode (using the native custom.el code to load dependencies)

TMO both are ugly too, in theory an (abnormal?) minor mode which doesn't end with the suffix can exist as well

a13 avatar Aug 24 '20 13:08 a13

@conao3 The thing is minor modes are defcustoms, so :custom should work for them

a13 avatar Aug 30 '20 07:08 a13

@tzz How about adding a function which mimics customize-set-variable, but doesn't save customizations into custom-file?

a13 avatar Aug 30 '20 07:08 a13

@conao3 The thing is minor modes are defcustoms, so :custom should work for them

I know what define-minor-mode does. And I know what define-minor-mode actually does. But, in principle, there can be minor modes that are not defined in define-minor-mode, can't there? And since it's true that there was a minor mode that couldn't be enabled in :custom, I added a dedicated keyword in leaf.

@tzz How about adding a function which mimics customize-set-variable, but doesn't save customizations into custom-file?

This is not for me, but I want to comment. This is a good idea that essentially solves this problem. I'll send this function to ML.

conao3 avatar Aug 30 '20 07:08 conao3

But, in principle, there can be minor modes that are not defined in define-minor-mode, can't there?

They can exist but I don't think it's a good idea

a13 avatar Aug 31 '20 06:08 a13

As far as I understand this, reason minor mode is not enabled by using :custom now is the change to set-default from customize-set-variable.

define-minor-mode expansion includes defcustom macro for mode's varaible with :initialize function set to custom-initialize-default. This is the case, I checked, for which-key-mode.

custom-initialize-default is one of default custom.el initializers specifically intended for minor modes. It's designed so that :set function (which enables/disables mode) is not called for pre-set values (like those set with set-default). But whenever the varaible is set via Customize (e.g. via direct call to customize-set-variable), the setter is called and the mode is enabled. See elisp#Variable Definitions for a section about this initializer.

Enabling minor modes also works if set in a Custom theme (like in the user theme stored in custom-file). Though I haven't been able yet to trace the exact code that calls setter for minor modes in custom.el, it does work.

I have written and use my tiny evelineraine/init-custom.el package that aggregates all settings to customizable variables in init.el and then runs them through custom-theme-set-variables code as setitngs of a single Custom theme.

The way I use it is:

(use-package PACKAGE
  :defer t
  :init
  ; VAR can be a -mode variable
  (init-custom-set 'VAR VALUE))

;; at the bottom of init.el
(load-theme 'init-custom :no-confirm)

By doing customizations this way, any and all obscure Custom features work (setters, ordering, dependencies, /and/ enabling minor modes), but values can be set from anywhere in init file and nothing seems to be saved to custom-file. Have a look at my code if this approach looks interesting.

I am thinking about adding an :init-custom keyword to use-package so it can be uses just like :custom keyword.

evelineraine avatar Sep 04 '20 16:09 evelineraine

I am thinking about adding an :init-custom keyword to use-package so it can be uses just like :custom keyword.

why are you trying to invent new keywords instead of reverting to the old working behavior?

a13 avatar Sep 05 '20 22:09 a13

why are you trying to invent new keywords instead of reverting to the old working behavior?

  1. the old customize-set-variable wasn't working well enough, hence the move to set-default instead.
  2. use-package is extensible via adding support for new keywords. So far I haven't considered suggesting my approach as the replacement of :custom implementation to the upstream.

In my opinion customize-set-variable isn't the right approach because:

  1. It marks the symbol as customize-but-unsaved (by storing the value in customized-value). Any time custom-save-variables is called (and Emacs carelessly calls it in a lot of places, e. g. from package.el when installing / removing packages), all these settings are written to the custom-file. This creates clutter and redundancy, but also leads to unexpected behavior: remove customization from init.el, but it would still be installed via custom-file.
  2. Due to how underdeveloped custom.el is (IMO), there is only one fully supported way to install customizations - via themes (custom-theme-set-variables or custom-set-variables for user theme). Except custom-set-variables doesn't do type-checking, but that was intentional. customize-set-variables seems only intened for interactive use, as a setter-aware version of set-variable minus type-checking (no good reason for that). It doesn't support theme precedence, undoing customizations, ordering via :set-after, deferred evaluation, and something else maybe.

To me: custom-theme-set-variables > set-default > customize-set-variable, so reverting back would be worse.

evelineraine avatar Sep 06 '20 07:09 evelineraine

the old customize-set-variable wasn't working well enough

It had worked for me for years, any example of invalid behavior?

all these settings are written to the custom-file.

any reason to use both :custom and custom-file?

custom-theme-set-variables > set-default > customize-set-variable

(defun custom-set-variables (&rest args)
;; ...
  (apply 'custom-theme-set-variables 'user args))

a13 avatar Sep 07 '20 13:09 a13

A significant chunk of my emacs config stopped working now that I've got a version of use-package with #850 present. Are there plans to get the old behavior back (or something close to it), or should I rework my config to avoid depending on defcustom behaviors? Or is there another reasonable workaround?

benley avatar Sep 18 '20 19:09 benley

Please see #881 for an attempt to resolve this issue. Thanks to everyone who chimed in, and apologies for the late followup.

@jkroes if you want provide a test to ensure minor mode vars behave properly, that would be really terrific.

@benley I think you may be commenting on something other than this particular bug report?

A significant chunk of my emacs config stopped working now that I've got a version of use-package with #850 present. Are there plans to get the old behavior back (or something close to it), or should I rework my config to avoid depending on defcustom behaviors? Or is there another reasonable workaround?

I'll do my best to address your issues, but would appreciate more detail than "stopped working." If you're just confirming someone else's report, you can say that or react to their report.

tzz avatar Nov 08 '20 15:11 tzz

Ah, sorry - I commented on this issue before I really understood what was happening, and I've since found workarounds for my own needs. My problem was indeed about setting minor-mode variables with :custom, and I've switched to using :config (customize-set-variable ....) to handle those for now.

benley avatar Nov 09 '20 18:11 benley

#881 switches to the custom-theme-set-variables approach which should address this issue as well. Please test and update. Thank you.

tzz avatar Nov 29 '20 15:11 tzz

Also @a13 and everyone else commenting here, I think a test that verifies correct behavior would be quite welcome and provide some assurance that the code won't break user expectations in the future.

tzz avatar Nov 29 '20 15:11 tzz

#881 has been merged, I'd like to close this issue if anyone can independently confirm and ideally provide a test for it.

tzz avatar Jan 07 '21 09:01 tzz

#881 has been merged, I'd like to close this issue if anyone can independently confirm and ideally provide a test for it.

Thanks, closing.

skangas avatar Nov 13 '22 20:11 skangas