15.4.5 Defining New Types
In the previous sections we have described how to construct elaborate type specifications for defcustom
. In some cases you may want to give such a type specification a name. The obvious case is when you are using the same type for many user options: rather than repeat the specification for each option, you can give the type specification a name, and use that name each defcustom
. The other case is when a user option’s value is a recursive data structure. To make it possible for a datatype to refer to itself, it needs to have a name.
Since custom types are implemented as widgets, the way to define a new customize type is to define a new widget. We are not going to describe the widget interface here in details, see Introduction in The Emacs Widget Library, for that. Instead we are going to demonstrate the minimal functionality needed for defining new customize types by a simple example.
(define-widget 'binary-tree-of-string 'lazy
"A binary tree made of cons-cells and strings."
:offset 4
:tag "Node"
:type '(choice (string :tag "Leaf" :value "")
(cons :tag "Interior"
:value ("" . "")
binary-tree-of-string
binary-tree-of-string)))
(defcustom foo-bar ""
"Sample variable holding a binary tree of strings."
:type 'binary-tree-of-string)
The function to define a new widget is called define-widget
. The first argument is the symbol we want to make a new widget type. The second argument is a symbol representing an existing widget, the new widget is going to be defined in terms of difference from the existing widget. For the purpose of defining new customization types, the lazy
widget is perfect, because it accepts a :type
keyword argument with the same syntax as the keyword argument to defcustom
with the same name. The third argument is a documentation string for the new widget. You will be able to see that string with the M-x widget-browse RET binary-tree-of-string RET
command.
After these mandatory arguments follow the keyword arguments. The most important is :type
, which describes the data type we want to match with this widget. Here a binary-tree-of-string
is described as being either a string, or a cons-cell whose car and cdr are themselves both binary-tree-of-string
. Note the reference to the widget type we are currently in the process of defining. The :tag
attribute is a string to name the widget in the user interface, and the :offset
argument is there to ensure that child nodes are indented four spaces relative to the parent node, making the tree structure apparent in the customization buffer.
The defcustom
shows how the new widget can be used as an ordinary customization type.
The reason for the name lazy
is that the other composite widgets convert their inferior widgets to internal form when the widget is instantiated in a buffer. This conversion is recursive, so the inferior widgets will convert their inferior widgets. If the data structure is itself recursive, this conversion is an infinite recursion. The lazy
widget prevents the recursion: it convert its :type
argument only when needed.