# HG changeset patch # User Per Abrahamsen # Date 1072543273 0 # Node ID 36b31fc002f244a527879fe40444081f05fb73e1 # Parent d7e333de59f9b326ed0973681ed682eafa3a461b 2003-12-12 Jesper Harder * cus-edit.el (custom-add-parent-links): Define "many". 2003-12-08 Per Abrahamsen * wid-edit.el (widget-child-value-get, widget-child-value-inline) (widget-child-validate, widget-type-value-create) (widget-type-default-get, widget-type-match): New functions. (lazy): New widget. (menu-choice, checklist, radio-button-choice, editable-list) (group, documentation-string): Removed redundant (per 2003-10-25 change) calls to `widget-children-value-delete'. (widget-choice-value-get, widget-choice-value-inline): Removed functions. (menu-choice): Updated widget. diff -r d7e333de59f9 -r 36b31fc002f2 lisp/ChangeLog --- a/lisp/ChangeLog Sat Dec 27 08:18:42 2003 +0000 +++ b/lisp/ChangeLog Sat Dec 27 16:41:13 2003 +0000 @@ -76,6 +76,23 @@ * info.el (Info-unescape-quotes, Info-split-parameter-string) (Info-goto-emacs-command-node): Doc fixes. +2003-12-12 Jesper Harder + + * cus-edit.el (custom-add-parent-links): Define "many". + +2003-12-08 Per Abrahamsen + + * wid-edit.el (widget-child-value-get, widget-child-value-inline) + (widget-child-validate, widget-type-value-create) + (widget-type-default-get, widget-type-match): New functions. + (lazy): New widget. + (menu-choice, checklist, radio-button-choice, editable-list) + (group, documentation-string): Removed redundant (per 2003-10-25 + change) calls to `widget-children-value-delete'. + (widget-choice-value-get, widget-choice-value-inline): Removed + functions. + (menu-choice): Updated widget. + 2003-12-03 Kenichi Handa * language/cyrillic.el: Register "microsoft-cp1251" in diff -r d7e333de59f9 -r 36b31fc002f2 lisp/cus-edit.el --- a/lisp/cus-edit.el Sat Dec 27 08:18:42 2003 +0000 +++ b/lisp/cus-edit.el Sat Dec 27 16:41:13 2003 +0000 @@ -1970,7 +1970,8 @@ (setq parents (cons symbol parents)))))) (and (null (get symbol 'custom-links)) ;No links of its own. (= (length parents) 1) ;A single parent. - (let ((links (get (car parents) 'custom-links))) + (let* ((links (get (car parents) 'custom-links)) + (many (> (length links) 2))) (when links (insert "\nParent documentation: ") (while links diff -r d7e333de59f9 -r 36b31fc002f2 lisp/wid-edit.el --- a/lisp/wid-edit.el Sat Dec 27 08:18:42 2003 +0000 +++ b/lisp/wid-edit.el Sat Dec 27 16:41:13 2003 +0000 @@ -1267,6 +1267,42 @@ found (widget-apply child :validate))) found)) +(defun widget-child-value-get (widget) + "Get the value of the first member of :children in WIDGET." + (widget-value (car (widget-get widget :children)))) + +(defun widget-child-value-inline (widget) + "Get the inline value of the first member of :children in WIDGET." + (widget-apply (car (widget-get widget :children)) :value-inline)) + +(defun widget-child-validate (widget) + "The result of validating the first member of :children in WIDGET." + (widget-apply (car (widget-get widget :children)) :validate)) + +(defun widget-type-value-create (widget) + "Convert and instantiate the value of the :type attribute of WIDGET. +Store the newly created widget in the :children attribute. + +The value of the :type attribute should be an unconverted widget type." + (let ((value (widget-get widget :value)) + (type (widget-get widget :type))) + (widget-put widget :children + (list (widget-create-child-value widget + (widget-convert type) + value))))) + +(defun widget-type-default-get (widget) + "Get default value from the :type attribute of WIDGET. + +The value of the :type attribute should be an unconverted widget type." + (widget-default-get (widget-convert (widget-get widget :type)))) + +(defun widget-type-match (widget value) + "Non-nil if the :type value of WIDGET matches VALUE. + +The value of the :type attribute should be an unconverted widget type." + (widget-apply (widget-convert (widget-get widget :type)) :match value)) + (defun widget-types-copy (widget) "Copy :args as widget types in WIDGET." (widget-put widget :args (mapcar 'widget-copy (widget-get widget :args))) @@ -1862,9 +1898,8 @@ :tag "choice" :void '(item :format "invalid (%t)\n") :value-create 'widget-choice-value-create - :value-delete 'widget-children-value-delete - :value-get 'widget-choice-value-get - :value-inline 'widget-choice-value-inline + :value-get 'widget-child-value-get + :value-inline 'widget-child-value-inline :default-get 'widget-choice-default-get :mouse-down-action 'widget-choice-mouse-down-action :action 'widget-choice-action @@ -1901,14 +1936,6 @@ widget void :value value))) (widget-put widget :choice void)))))) -(defun widget-choice-value-get (widget) - ;; Get value of the child widget. - (widget-value (car (widget-get widget :children)))) - -(defun widget-choice-value-inline (widget) - ;; Get value of the child widget. - (widget-apply (car (widget-get widget :children)) :value-inline)) - (defun widget-choice-default-get (widget) ;; Get default for the first choice. (widget-default-get (car (widget-get widget :args)))) @@ -2099,7 +2126,6 @@ :entry-format "%b %v" :greedy nil :value-create 'widget-checklist-value-create - :value-delete 'widget-children-value-delete :value-get 'widget-checklist-value-get :validate 'widget-checklist-validate :match 'widget-checklist-match @@ -2276,7 +2302,6 @@ :format "%v" :entry-format "%b %v" :value-create 'widget-radio-value-create - :value-delete 'widget-children-value-delete :value-get 'widget-radio-value-get :value-inline 'widget-radio-value-inline :value-set 'widget-radio-value-set @@ -2466,7 +2491,6 @@ :format-handler 'widget-editable-list-format-handler :entry-format "%i %d %v" :value-create 'widget-editable-list-value-create - :value-delete 'widget-children-value-delete :value-get 'widget-editable-list-value-get :validate 'widget-children-validate :match 'widget-editable-list-match @@ -2637,7 +2661,6 @@ :copy 'widget-types-copy :format "%v" :value-create 'widget-group-value-create - :value-delete 'widget-children-value-delete :value-get 'widget-editable-list-value-get :default-get 'widget-group-default-get :validate 'widget-children-validate @@ -2803,7 +2826,6 @@ "A documentation string." :format "%v" :action 'widget-documentation-string-action - :value-delete 'widget-children-value-delete :value-create 'widget-documentation-string-value-create) (defun widget-documentation-string-value-create (widget) @@ -3250,6 +3272,62 @@ (widget-group-match widget (widget-apply widget :value-to-internal value)))) +;;; The `lazy' Widget. +;; +;; Recursive datatypes. + +(define-widget 'lazy 'default + "Base widget for recursive datastructures. + +The `lazy' widget will, when instantiated, contain a single inferior +widget, of the widget type specified by the :type parameter. The +value of the `lazy' widget is the same as the value of the inferior +widget. When deriving a new widget from the 'lazy' widget, the :type +parameter is allowed to refer to the widget currently being defined, +thus allowing recursive datastructures to be described. + +The :type parameter takes the same arguments as the defcustom +parameter with the same name. + +Most composite widgets, i.e. widgets containing other widgets, does +not allow recursion. That is, when you define a new widget type, none +of the inferior widgets may be of the same type you are currently +defining. + +In Lisp, however, it is custom to define datastructures in terms of +themselves. A list, for example, is defined as either nil, or a cons +cell whose cdr itself is a list. The obvious way to translate this +into a widget type would be + + (define-widget 'my-list 'choice + \"A list of sexps.\" + :tag \"Sexp list\" + :args '((const nil) (cons :value (nil) sexp my-list))) + +Here we attempt to define my-list as a choice of either the constant +nil, or a cons-cell containing a sexp and my-lisp. This will not work +because the `choice' widget does not allow recursion. + +Using the `lazy' widget you can overcome this problem, as in this +example: + + (define-widget 'sexp-list 'lazy + \"A list of sexps.\" + :tag \"Sexp list\" + :type '(choice (const nil) (cons :value (nil) sexp sexp-list)))" + :format "%{%t%}: %v" + ;; We don't convert :type because we want to allow recursive + ;; datastructures. This is slow, so we should not create speed + ;; critical widgets by deriving from this. + :convert-widget 'widget-value-convert-widget + :value-create 'widget-type-value-create + :value-get 'widget-child-value-get + :value-inline 'widget-child-value-inline + :default-get 'widget-type-default-get + :match 'widget-type-match + :validate 'widget-child-validate) + + ;;; The `plist' Widget. ;; ;; Property lists. diff -r d7e333de59f9 -r 36b31fc002f2 lispref/customize.texi --- a/lispref/customize.texi Sat Dec 27 08:18:42 2003 +0000 +++ b/lispref/customize.texi Sat Dec 27 16:41:13 2003 +0000 @@ -1,6 +1,6 @@ @c -*-texinfo-*- @c This is part of the GNU Emacs Lisp Reference Manual. -@c Copyright (C) 1997, 1998, 1999, 2000, 2002 Free Software Foundation, Inc. +@c Copyright (C) 1997, 1998, 1999, 2000, 2002, 2003 Free Software Foundation, Inc. @c See the file elisp.texi for copying conditions. @setfilename ../info/customize @node Customization, Loading, Macros, Top @@ -373,6 +373,7 @@ * Composite Types:: * Splicing into Lists:: * Type Keywords:: +* Defining New Types:: @end menu All customization types are implemented as widgets; see @ref{Top, , @@ -1056,6 +1057,76 @@ @end ignore @end table +@node Defining New Types +@subsection Defining New Types + +In the previous sections we have described how to construct elaborate +type specifications for @code{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 once, and use that name each @code{defcustom}. The other case is +when a user option accept a recursive datastructure. 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 @ref{Top, , Introduction, +widget, 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. + +@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) +@end example + +The function to define a new widget is name @code{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 +@code{lazy} widget is perfect, because it accept a @code{:type} keyword +argument with the same syntax as the keyword argument to +@code{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 @kbd{M-x widget-browse @key{ret} binary-tree-of-string +@key{ret}} command. + +After these mandatory arguments follows the keyword arguments. The most +important is @code{:type}, which describes the datatype we want to match +with this widget. Here a @code{binary-tree-of-string} is described as +being either a string, or a cons-cell whose car and cdr are themselves +both @code{binary-tree-of-string}. Note the reference to the widget +type we are currently in the process of defining. The @code{:tag} +attribute is a string to name the widget in the user interface, and the +@code{:offset} argument are there to ensure that child nodes are +indented four spaces relatively to the parent node, making the tree +structure apparent in the customization buffer. + +The @code{defcustom} shows how the new widget can be used as an ordinary +customization type. + +If you wonder about the name @code{lazy}, know 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 @emph{their} inferior widgets. If the +datastructure is itself recursive, this conversion will go on forever, +or at least until Emacs run out of stack space. The @code{lazy} widget +stop this recursion, it will only convert its @code{:type} argument when +needed. + @ignore arch-tag: d1b8fad3-f48c-4ce4-a402-f73b5ef19bd2 @end ignore