2

I'd like to create a customizable variable foo-paths with certain options.

(defcustom foo-paths nil
  "Paths for foo-related things."
  :group 'foo
  :options '(foo-1 foo-2)
  :type '(alist :key-type symbol :value-type (file :must-match t)))

I'd like for the user to only be able to provide values for the specified keys in :options, not to add new options.

The manual doesn't offer a way to do this--the user can add new elements to the alist through the Customize interface.

Is there a way to achieve this? The functionality may not be impacted by the addition of new alist elements, but it seems strange that there's no way to specify a variable that has certain options and only those options.

1
  • An alist is extensible, by definition. See custom type list, which specifies a list of a prescribed length and element types.
    – Drew
    Commented Feb 10, 2017 at 14:57

1 Answer 1

1

Only a partial answer. The :must-match requirement seems broken (a bug?).

An alist is extensible by definition. And :options does not enforce using its values.

Custom type list specifies a list of prescribed length and element types. Use repeat of list. In other words,repeat lists of two elements of given types. For your permissible keys, use choice with const.

(defcustom foo-paths nil
  "Paths for foo-related things."
  :group 'foo
  :type '(repeat (list (choice (const foo-1) (const foo-2))
                       (file :must-match t))))

You can also do something like this:

(defcustom foo-paths nil
  "Paths for foo-related things."
  :group 'foo
  :options '(foo-1 foo-2)
  :type '(alist
          :key-type (choice (const foo-1) (const foo-2))
          :value-type (file :must-match t)))

(Based on your comments, it's not very clear what you really want.)

7
  • TBH, this is not as good as the alist type. With the alist, the keys specified in :options are shown be default; here, they are not. Also, the user is still able to add extraneous elements with INS in the Customize interface. Commented Feb 10, 2017 at 21:26
  • '(list (cons (const :tag "Foo-1 path" foo-1) (file :must-match t)) (cons (const :tag "Foo-2 path" foo-2) (file :must-match t))) comes close, but I need to provide an initial value that fits, so it needs to be something like: ((foo-1 . "") (foo-2 . "")). Also, the Customize interface will say Cons-cell in for each list element. Commented Feb 10, 2017 at 21:35
  • Too bad. You can use alist or you can use repeat or you can use restricted-sexp. There are multiple possibilities. That just-what-you-want is not offered out of the box is unfortunate. File an enhancement request: M-x report-emacs-bug.
    – Drew
    Commented Feb 11, 2017 at 2:28
  • repeat means you can add more elements (it is a list). But list specifies that each element is locked into only N elements of the specified types. The INS you are referring to is just about adding more elements to the overall value (a list).
    – Drew
    Commented Feb 11, 2017 at 2:29
  • 1
    The most flexible customization type is restricted-sexp. It takes some getting used to, and it can be cumbersome, but you can do pretty much anything you want with it.
    – Drew
    Commented Feb 11, 2017 at 2:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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