How to properly write the default value for a non-trivial defcustom form for something like I have below: a list of 3-element lists, with choices of the types?

The defcustom form is: defcustom option standard doc [keyword value]...

The following defcustom works fine as long as:

  • it's default is nil
  • it's a list of one or several elements of the first type

It fails with a "mismatch" warning in the customize buffer as soon as the default is a list of heterogeneous types (e.g. one element is from the second or third choice).

#1: My defcustom form: (with a nil default, it works fine)

(defcustom pel-key-chords nil
:group 'pel-pkg-for-key-chord
  :type '(repeat
            :tag "expansion-keys"
            (symbol       :tag "mode           " :value global)
            (string       :tag "the 2 keys     ")
             (key-sequence :tag "key")))
            :tag "command"
            (symbol       :tag "mode           " :value global)
            (string       :tag "the 2 keys     ")
            (function     :tag "command        "))
            :tag "lambda"
            (symbol       :tag "mode           " :value global)
            (string       :tag "the 2 keys     ")
            (function     :tag "elisp code     "
                          :value (lambda () (interactive) <YOUR CODE HERE>))))))

If, instead of nil as the default I write a list with several items of different types, the customization menu fails with a mismatch and does not to properly show the widgets to enter new entries. If the list contains elements of the first type, it's fine.

#2: My defcustom form: (same code, but with an explicit default that works fine: a list of elements of the first choice:

(defcustom pel-key-chords
'((global   "<>"  ("<>\C-b"))
  (global   "[]"  ("[]\C-b"))
  (c++-mode "{}"  ("{\n\n}\C-p")))

#3: My defcustom form: (same code, but with an explicit default that fails with a mismatch: a list of 3 elements of the first choice, then 1 element of the second choice:

(defcustom pel-key-chords
'((global   "<>"  ("<>\C-b"))
  (global   "[]"  ("[]\C-b"))
  (c++-mode "{}"  ("{\n\n}\C-p"))
  (global   ".;"  pel-search-word-from-top))

Interesting note: Additions of second and/or third type elements via the customize buffer while the default has accepted data (as in #2 above), are accepted, saved to the custom-set-variables form properly and restored properly back in the customize buffer!

This is a copy/paste of the content of the relevant portion of my custom-set-variables form after manually adding the last list element (global ".;" pel-search-word-from-top) via the customize buffer and then saved it gives:

    ((global "<>"
     (global "[]"
     (c-mode "{}"

     (c++-mode "{}"

     (global ".;" pel-search-word-from-top))))

I can then read it back without any mismatch. If I try to put this inside the default I get the mismatch warning...

I read Emacs Lisp Customization Settings section and some code like the dump-jump.el code that defines some complex data with initialization. I assume I must identify some keywords in my declaration of the default, but for some reason I can't find the proper syntax for initializing the data I defined above.

Note: this question evolved from my previous question How to properly specify defcustom default value for non-trivial data type?

  • It sounds like a bug. I see you submitted a bug report - bug #41831. Thx. – Drew Jun 13 at 1:42

Although I did report a bug for the problem I ran into and described above in bug #41831 I found a work-around by declaring a similar data structure differently. The new layout is handled properly by *customize* buffer UI and I can identify a default with heterogeneous types in it.

Work-around defcustom declaration:

(defcustom pel-key-chords2
  '((global ",." "<<<<<>>>>>\\C-b\\C-b\\C-p\\C-b\\C-b")
    (global "<>" "<>\\C-b")
    (global "  q" pel-indent-rigidly)
    (global "yu"
            (lambda nil
              (message "Hello You"))))
  "Another way to implement the data structure, placing the choice
deeper inside the leaf data element.  
This is handled properly by customize UI."
  :group 'pel-pkg-for-key-chord
     (symbol   :tag "mode" :value global)
     (string   :tag "2-keys")
     (choice (string   :tag "expansion")
             (function :tag "function")
             (function :tag "lambda" :value (lambda () (interactive) <YOUR CODE HERE>))))))

In this new defcustom declaration, this is a list of 3 elements where the 3rd list element is a choice of 3 types, as opposed to a top-level choice of 3 lists of different 'sub-types' as I did originally and described in the question.

With the new declaration I can identify defaults, save the values and restore them as one would expect with Emacs customization mechanism.

So, at least, this works around the problem described in my original question, and it might help looking into the cause of the original Emacs defcustom UI implementation issue.

Important Note: another cause of the problem: unbound symbols at edit time

I also noticed that the customize UI will reject the data structure if a symbol identified in the data structure is not bound at the time the customize UI buffer is opened to edit the data structure.

So if something like pel-indent-rigidly is not bound when the customize buffer is opened and that symbol is part of the default quoted data structure, then the UI will fail and show the mismatch message again.

So in the end, it is important to ensure that the symbols will be bound when the defcustom default form will be shown for editing. That is what the defcustom :load keyword is for: to ensure that all symbols are known when the form is loaded for editing.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.