Commit 3698c4e4 authored by Stefan Monnier's avatar Stefan Monnier
Browse files

* cl-generic.el (cl-defmethod): Make docstring dynamic

* lisp/emacs-lisp/cl-generic.el (cl-defmethod): Make docstring dynamic.
(cl--generic-make-defmethod-docstring): New function for that.
(cl-defmethod, cl-generic-generalizers): Tweak docstrings accordingly.
(cl-generic-define-method, cl--generic-describe): Change `load-history'
format of cl-defmethods, so as not to confused methods with equal
specializers but different qualifiers.
* lisp/emacs-lisp/eieio-core.el (cl-generic-generalizers): Provide docstrings.
parent 5155144b
......@@ -353,6 +353,26 @@ the specializer used will be the one returned by BODY."
,nbody))))))
(f (error "Unexpected macroexpansion result: %S" f))))))
(put 'cl-defmethod 'function-documentation
'(cl--generic-make-defmethod-docstring))
(defun cl--generic-make-defmethod-docstring ()
;; FIXME: Copy&paste from pcase--make-docstring.
(let* ((main (documentation (symbol-function 'cl-defmethod) 'raw))
(ud (help-split-fundoc main 'cl-defmethod)))
;; So that eg emacs -Q -l cl-lib --eval "(documentation 'pcase)" works,
;; where cl-lib is anything using pcase-defmacro.
(require 'help-fns)
(with-temp-buffer
(insert (or (cdr ud) main))
(insert "\n\n\tCurrently supported forms for TYPE:\n\n")
(dolist (method (reverse (cl--generic-method-table
(cl--generic 'cl-generic-generalizers))))
(let* ((info (cl--generic-method-info method)))
(when (nth 2 info)
(insert (nth 2 info) "\n\n"))))
(let ((combined-doc (buffer-string)))
(if ud (help-add-fundoc-usage combined-doc (car ud)) combined-doc)))))
;;;###autoload
(defmacro cl-defmethod (name args &rest body)
......@@ -370,15 +390,17 @@ modifies how the method is combined with other methods, including:
:after - Method will be called after the primary
:around - Method will be called around everything else
The absence of QUALIFIER means this is a \"primary\" method.
The set of acceptable qualifiers and their meaning is defined
\(and can be extended) by the methods of `cl-generic-combine-methods'.
TYPE can be one of the basic types (see the full list and their
hierarchy in `cl--generic-typeof-types'), CL struct type, or an
EIEIO class.
ARGS can also include so-called context specializers, introduced by
`&context' (which should appear right after the mandatory arguments,
before any &optional or &rest). They have the form (EXPR TYPE) where
EXPR is an Elisp expression whose value should match TYPE for the
method to be applicable.
Other than that, TYPE can also be of the form `(eql VAL)' in
which case this method will be invoked when the argument is `eql'
to VAL, or `(head VAL)', in which case the argument is required
to be a cons with VAL as its head.
The set of acceptable TYPEs (also called \"specializers\") is defined
\(and can be extended) by the various methods of `cl-generic-generalizers'.
\(fn NAME [QUALIFIER] ARGS &rest [DOCSTRING] BODY)"
(declare (doc-string 3) (indent 2)
......@@ -464,7 +486,8 @@ to be a cons with VAL as its head.
(cons method mt)
;; Keep the ordering; important for methods with :extra qualifiers.
(mapcar (lambda (x) (if (eq x (car me)) method x)) mt)))
(cl-pushnew `(cl-defmethod . (,(cl--generic-name generic) . ,specializers))
(cl-pushnew `(cl-defmethod . (,(cl--generic-name generic)
,qualifiers . ,specializers))
current-load-list :test #'equal)
;; FIXME: Try to avoid re-constructing a new function if the old one
;; is still valid (e.g. still empty method cache)?
......@@ -737,7 +760,7 @@ methods.")
(fset 'cl-generic-combine-methods #'cl--generic-standard-method-combination))
(cl-defmethod cl-generic-generalizers (specializer)
"Support for the catch-all t specializer."
"Support for the catch-all t specializer which always matches."
(if (eq specializer t) (list cl--generic-t-generalizer)
(error "Unknown specializer %S" specializer)))
......@@ -909,8 +932,9 @@ MET-NAME is a cons (SYMBOL . SPECIALIZERS)."
(let* ((info (cl--generic-method-info method)))
;; FIXME: Add hyperlinks for the types as well.
(insert (format "%s%S" (nth 0 info) (nth 1 info)))
(let* ((met-name (cons function
(cl--generic-method-specializers method)))
(let* ((met-name `(,function
,(cl--generic-method-qualifiers method)
. ,(cl--generic-method-specializers method)))
(file (find-lisp-object-file-name met-name 'cl-defmethod)))
(when file
(insert (substitute-command-keys " in `"))
......@@ -994,7 +1018,8 @@ The value returned is a list of elements of the form
(lambda (tag &rest _) (if (eq (car-safe tag) 'head) (list tag))))
(cl-defmethod cl-generic-generalizers :extra "head" (specializer)
"Support for the `(head VAL)' specializers."
"Support for (head VAL) specializers.
These match if the argument is a cons cell whose car is `eql' to VAL."
;; We have to implement `head' here using the :extra qualifier,
;; since we can't use the `head' specializer to implement itself.
(if (not (eq (car-safe specializer) 'head))
......@@ -1014,7 +1039,8 @@ The value returned is a list of elements of the form
(lambda (tag &rest _) (if (eq (car-safe tag) 'eql) (list tag))))
(cl-defmethod cl-generic-generalizers ((specializer (head eql)))
"Support for the `(eql VAL)' specializers."
"Support for (eql VAL) specializers.
These match if the argument is `eql' to VAL."
(puthash (cadr specializer) specializer cl--generic-eql-used)
(list cl--generic-eql-generalizer))
......@@ -1069,7 +1095,7 @@ The value returned is a list of elements of the form
#'cl--generic-struct-specializers)
(cl-defmethod cl-generic-generalizers :extra "cl-struct" (type)
"Support for dispatch on cl-struct types."
"Support for dispatch on types defined by `cl-defstruct'."
(or
(when (symbolp type)
;; Use the "cl--struct-class*" (inlinable) functions/macros rather than
......@@ -1113,7 +1139,8 @@ The value returned is a list of elements of the form
(and (symbolp tag) (assq tag cl--generic-typeof-types))))
(cl-defmethod cl-generic-generalizers :extra "typeof" (type)
"Support for dispatch on builtin types."
"Support for dispatch on builtin types.
See the full list and their hierarchy in `cl--generic-typeof-types'."
;; FIXME: Add support for other types accepted by `cl-typep' such
;; as `character', `atom', `face', `function', ...
(or
......@@ -1151,7 +1178,8 @@ The value returned is a list of elements of the form
#'cl--generic-derived-specializers)
(cl-defmethod cl-generic-generalizers ((_specializer (head derived-mode)))
"Support for the `(derived-mode MODE)' specializers."
"Support for (derived-mode MODE) specializers.
Used internally for the (major-mode MODE) context specializers."
(list cl--generic-derived-generalizer))
(cl-generic-define-context-rewriter major-mode (mode &rest modes)
......
......@@ -1065,6 +1065,7 @@ method invocation orders of the involved classes."
(eieio--class-precedence-list (symbol-value tag))))))
(cl-defmethod cl-generic-generalizers :extra "class" (specializer)
"Support for dispatch on types defined by EIEIO's `defclass'."
;; CLHS says:
;; A class must be defined before it can be used as a parameter
;; specializer in a defmethod form.
......@@ -1093,6 +1094,8 @@ method invocation orders of the involved classes."
#'eieio--generic-subclass-specializers)
(cl-defmethod cl-generic-generalizers ((_specializer (head subclass)))
"Support for (subclass CLASS) specializers.
These match if the argument is the name of a subclass of CLASS."
(list eieio--generic-subclass-generalizer))
(provide 'eieio-core)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment