Subject: RE: bug#3811: 23.0.96; custom-group-members

From: "Drew Adams" <[email-masked]>
Date: Wed, 15 Jul 2009 10:13:32 -0700

> It might be useful to have another function (or perhaps 
> another optional arg to this function), which would act 
> recursively to give you all members, indirect or direct, that 
> belong to the group - IOW, anything that belongs to the group 
> or to one of its subgroups (recursively).

To be clear what I meant, something like this:

(defun custom-group-members (symbol groups-only
                             &optional recursivep)
  "Return members of the custom group for SYMBOL.
If GROUPS-ONLY is non-nil, return only those direct members
 that are groups.
If RECURSIVEP is non-nil and GROUPS-ONLY is nil, return
 non-group direct and indirect members."
  (let ((members  ()))
    (cond (groups-only
           (dolist (entry  (get symbol 'custom-group))
             (when (eq (cadr entry) 'custom-group)
               (push entry members)))
           (nreverse members))
          (recursivep
           (let ((direct-members  (custom-group-members symbol nil)))
             (dolist (dm  direct-members)
               (if (eq (cadr dm) 'custom-group)
                   (setq members
                         (nconc (custom-group-members (car dm) nil t)
                                members))
                 (push dm members)))
             (nreverse members)))
          (t
           (get symbol 'custom-group)))))

It would be even better to combine args GROUPS-ONLY and RECURSIVEP, but that
might mean problems for backward incompatibility. But perhaps something like
this would be OK?

(defun custom-group-members (symbol arg)
  "Return members of the custom group for SYMBOL.
ARG nil means return all direct group members: groups and non-groups.
ARG `nongroups' means return all nongroup members, recursively.
ARG anything else means return all direct group members."
  (let ((members  ()))
    (case arg
      ((nil) (get symbol 'custom-group))
      (nongroups
       (let ((direct-members  (custom-group-members symbol nil)))
         (dolist (dm  direct-members)
           (if (eq (cadr dm) 'custom-group)
               (setq members
                     (nconc (custom-group-members
                             (car dm) 'nongroups)
                            members))
             (push dm members)))
         (nreverse members)))
      (t (dolist (entry  (get symbol 'custom-group))
           (when (eq (cadr entry) 'custom-group)
             (push entry members)))
         (nreverse members)))))

That would still work for any existing code that used a value other than
`nongroups' as the second arg, which probably means there would be no problems
in practice.

We might also consider making this a command. Users could use it to print out a
list of the options and faces for a group.

Note that one use of the proposed recursive behavior is for a user to create a
custom group that represents a collection of personal settings (across other
custom groups), and then to share those settings with others. (See the
emacs-devel discussion of "skins" as custom groups.)

A user Jane could, for example, use `custom-add-to-group' with group `jane', and
then she could publish the `jane' settings for others, retrieving them using
`custom-group-members'. The only other piece missing would then be a way for
non-Lisp users to do the equivalent of `custom-add-to-group' using only the
Customize UI. That is, we would provide easy ways to specify that certain
options and faces should be added to group `jane'.