mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-06 06:20:55 -08:00
Implement new autoload macro expansion declare form
Currently, a hard-coded set of macros is automatically expanded during generation of autoloads. To allow user macros to request such expansion, this implements a new declare form `autoload-macro' (Bug#78995), with supported value `expand'. For example, macros which wrap `define-minor-mode', can declare `(autoload-macro expand)' to request that ;;;###autoload-adorned calls to the macro are expanded during generation, such that an autoload for the resulting function is created. * lisp/emacs-lisp/byte-run.el (byte-run--set-autoload-macro): Handle autoload-macro declare forms. (macro-declarations-alist) Add handler for 'autoload-macro declare forms. (defmacro, defun): * lisp/emacs-lisp/cl-generic.el (cl-defgeneric, cl-defun) (cl-iter-defun, cl-defmacro, cl-defstruct): * lisp/emacs-lisp/easy-mmode.el (define-minor-mode, define-globalized-minor-mode, iter-defun): * lisp/emacs-lisp/inline.el (define-inline): * lisp/emacs-lisp/pcase.el (pcase-defmacro): Declare (autoload-macro expand) to request expansion of the macro during autoload generation. * lisp/emacs-lisp/loaddefs-gen.el (loaddefs-generate--make-autoload): Handle the `autoload-macro=expand' property for macros. Load the ;;;###autoload-containing file if an unknown symbol is encountered in the car of the following form, to give packages a chance to define their macros and request expansion. Factor list of special function-defining macros out as a constant variable: `loaddefs--defining-macros'. * doc/lispref/functions.texi (Declare Form): * doc/lispref/loading.texi (Autoload): Document `autoload-macro'.
This commit is contained in:
parent
3d7f51d872
commit
7486e5c368
10 changed files with 164 additions and 56 deletions
|
|
@ -286,6 +286,12 @@ This is used by `declare'.")
|
|||
(list 'put (list 'quote name)
|
||||
''edebug-form-spec (list 'quote spec)))))
|
||||
|
||||
(defalias 'byte-run--set-autoload-macro
|
||||
#'(lambda (name _args spec)
|
||||
(list 'function-put (list 'quote name)
|
||||
''autoload-macro (list 'quote spec)))
|
||||
"Handle autoload-macro declarations")
|
||||
|
||||
(defalias 'byte-run--set-no-font-lock-keyword
|
||||
#'(lambda (name _args val)
|
||||
(list 'function-put (list 'quote name)
|
||||
|
|
@ -365,8 +371,13 @@ This is used by `declare'.")
|
|||
(cons
|
||||
(list 'debug #'byte-run--set-debug)
|
||||
(cons
|
||||
(list 'no-font-lock-keyword #'byte-run--set-no-font-lock-keyword)
|
||||
defun-declarations-alist))
|
||||
;; macros can declare (autoload-macro expand) to request expansion
|
||||
;; during autoload generation of forms calling them. See
|
||||
;; `loaddefs-generate--make-autoload'.
|
||||
(list 'autoload-macro #'byte-run--set-autoload-macro)
|
||||
(cons
|
||||
(list 'no-font-lock-keyword #'byte-run--set-no-font-lock-keyword)
|
||||
defun-declarations-alist)))
|
||||
"List associating properties of macros to their macro expansion.
|
||||
Each element of the list takes the form (PROP FUN) where FUN is a function.
|
||||
For each (PROP . VALUES) in a macro's declaration, the FUN corresponding
|
||||
|
|
@ -412,6 +423,8 @@ The return value is undefined.
|
|||
(if declarations
|
||||
(cons 'prog1 (cons def (car declarations)))
|
||||
def))))))
|
||||
;; Expand to defalias and related forms on autoload gen
|
||||
(function-put 'defmacro 'autoload-macro 'expand) ; Since we cannot `declare' it
|
||||
|
||||
;; Now that we defined defmacro we can use it!
|
||||
(defmacro defun (name arglist &rest body)
|
||||
|
|
@ -424,7 +437,9 @@ INTERACTIVE is an optional `interactive' specification.
|
|||
The return value is undefined.
|
||||
|
||||
\(fn NAME ARGLIST [DOCSTRING] [DECL] [INTERACTIVE] BODY...)"
|
||||
(declare (doc-string 3) (indent 2))
|
||||
(declare (doc-string 3) (indent 2)
|
||||
;; Expand to defalias on autoload gen
|
||||
(autoload-macro expand))
|
||||
(or name (error "Cannot define '%s' as a function" name))
|
||||
(if (null
|
||||
(and (listp arglist)
|
||||
|
|
|
|||
|
|
@ -258,7 +258,9 @@ DEFAULT-BODY, if present, is used as the body of a default method.
|
|||
cl--generic-edebug-make-name in:method]
|
||||
lambda-doc
|
||||
def-body)]]
|
||||
def-body)))
|
||||
def-body))
|
||||
;; Expand to defun and related forms on autoload definition
|
||||
(autoload-macro expand))
|
||||
(let* ((doc (if (stringp (car-safe options-and-methods))
|
||||
(pop options-and-methods)))
|
||||
(declarations nil)
|
||||
|
|
|
|||
|
|
@ -396,7 +396,9 @@ more details.
|
|||
[&optional ("interactive" interactive)]
|
||||
def-body))
|
||||
(doc-string 3)
|
||||
(indent 2))
|
||||
(indent 2)
|
||||
;; expand to function definition on autoload gen
|
||||
(autoload-macro expand))
|
||||
`(defun ,name ,@(cl--transform-lambda (cons args body) name)))
|
||||
|
||||
;;;###autoload
|
||||
|
|
@ -414,7 +416,9 @@ and BODY is implicitly surrounded by (cl-block NAME ...).
|
|||
[&optional ("interactive" interactive)]
|
||||
def-body))
|
||||
(doc-string 3)
|
||||
(indent 2))
|
||||
(indent 2)
|
||||
;; expand (eventually) to function definition on autoload gen
|
||||
(autoload-macro expand))
|
||||
(require 'generator)
|
||||
`(iter-defun ,name ,@(cl--transform-lambda (cons args body) name)))
|
||||
|
||||
|
|
@ -473,7 +477,8 @@ more details.
|
|||
(declare (debug
|
||||
(&define name cl-macro-list cl-declarations-or-string def-body))
|
||||
(doc-string 3)
|
||||
(indent 2))
|
||||
(indent 2)
|
||||
(autoload-macro expand)) ; expand to defmacro on autoload gen
|
||||
`(defmacro ,name ,@(cl--transform-lambda (cons args body) name)))
|
||||
|
||||
(def-edebug-elem-spec 'cl-lambda-expr
|
||||
|
|
@ -3087,7 +3092,9 @@ To see the documentation for a defined struct type, use
|
|||
sexp])]
|
||||
[&optional stringp]
|
||||
;; All the above is for the following def-form.
|
||||
&rest &or symbolp (symbolp &optional def-form &rest sexp))))
|
||||
&rest &or symbolp (symbolp &optional def-form &rest sexp)))
|
||||
;; expand to function definitions and related forms on autoload gen
|
||||
(autoload-macro expand))
|
||||
(let* ((name (if (consp struct) (car struct) struct))
|
||||
(warning nil)
|
||||
(opts (cdr-safe struct))
|
||||
|
|
|
|||
|
|
@ -222,10 +222,12 @@ INIT-VALUE LIGHTER KEYMAP.
|
|||
(indent defun)
|
||||
(debug (&define name string-or-null-p
|
||||
[&optional [¬ keywordp] sexp
|
||||
&optional [¬ keywordp] sexp
|
||||
&optional [¬ keywordp] sexp]
|
||||
&optional [¬ keywordp] sexp
|
||||
&optional [¬ keywordp] sexp]
|
||||
[&rest [keywordp sexp]]
|
||||
def-body)))
|
||||
def-body))
|
||||
;; expand to the command definition on autoload gen
|
||||
(autoload-macro expand))
|
||||
|
||||
(let* ((last-message (make-symbol "last-message"))
|
||||
(mode-name (symbol-name mode))
|
||||
|
|
@ -488,7 +490,9 @@ after running the major mode's hook. However, MODE is not turned
|
|||
on if the hook has explicitly disabled it.
|
||||
|
||||
\(fn GLOBAL-MODE MODE TURN-ON [KEY VALUE]... BODY...)"
|
||||
(declare (doc-string 2) (indent defun))
|
||||
(declare (doc-string 2) (indent defun)
|
||||
;; expand to the minor-mode definition on autoload gen
|
||||
(autoload-macro expand))
|
||||
(let* ((global-mode-name (symbol-name global-mode))
|
||||
(mode-name (symbol-name mode))
|
||||
(pretty-name (easy-mmode-pretty-mode-name mode))
|
||||
|
|
|
|||
|
|
@ -675,7 +675,8 @@ encapsulates the state of a computation that produces a sequence
|
|||
of values. Callers can retrieve each value using `iter-next'."
|
||||
(declare (indent defun)
|
||||
(debug (&define name lambda-list lambda-doc &rest sexp))
|
||||
(doc-string 3))
|
||||
(doc-string 3)
|
||||
(autoload-macro expand)) ; expand to the defun on autoload gen
|
||||
(cl-assert lexical-binding)
|
||||
(let* ((parsed-body (macroexp-parse-body body))
|
||||
(declarations (car parsed-body))
|
||||
|
|
|
|||
|
|
@ -135,7 +135,8 @@ After VARS is handled, BODY is evaluated in the new environment."
|
|||
This is like `defmacro', but has several advantages.
|
||||
See Info node `(elisp)Defining Functions' for more details."
|
||||
;; FIXME: How can this work with CL arglists?
|
||||
(declare (indent defun) (debug defun) (doc-string 3))
|
||||
(declare (indent defun) (debug defun) (doc-string 3)
|
||||
(autoload-macro expand)) ; expand to the defun on autoload gen
|
||||
(let ((doc (if (stringp (car-safe body)) (list (pop body))))
|
||||
(declares (if (eq (car-safe (car-safe body)) 'declare) (pop body)))
|
||||
(cm-name (intern (format "%s--inliner" name)))
|
||||
|
|
|
|||
|
|
@ -143,12 +143,37 @@ scanning for autoloads and will be in the `load-path'."
|
|||
3)
|
||||
form))
|
||||
|
||||
;; The following macros are known to define functions, and are treated
|
||||
;; specially when encountered during autoload generation, translating
|
||||
;; calls to them directly into appropriate (autoload function ...)
|
||||
;; forms.
|
||||
;;
|
||||
;; An alternative to appearing on this list is for a macro to declare
|
||||
;; (autoload-macro expand), so calls to it get expanded into more basic
|
||||
;; forms during generation. Macros may be removed from this list once
|
||||
;; they request such expansion and produce suitable output (e.g. by
|
||||
;; employing :autoload-end to omit unneeded forms).
|
||||
(defconst loaddefs--defining-macros
|
||||
'( define-skeleton define-derived-mode define-compilation-mode
|
||||
define-generic-mode define-globalized-minor-mode define-minor-mode
|
||||
cl-defun defun* cl-defmacro defmacro* define-overloadable-function
|
||||
transient-define-prefix transient-define-suffix transient-define-infix
|
||||
transient-define-argument transient-define-group
|
||||
;; Obsolete; keep until the alias is removed.
|
||||
easy-mmode-define-global-mode
|
||||
easy-mmode-define-minor-mode
|
||||
define-global-minor-mode))
|
||||
|
||||
(defvar loaddefs--load-error-files nil)
|
||||
(defun loaddefs-generate--make-autoload (form file &optional expansion)
|
||||
"Turn FORM into an autoload or defvar for source file FILE.
|
||||
Returns nil if FORM is not a special autoload form (i.e. a function definition
|
||||
or macro definition or a defcustom).
|
||||
If EXPANSION is non-nil, we're processing the macro expansion of an
|
||||
expression, in which case we want to handle forms differently."
|
||||
expression, in which case we want to handle forms differently.
|
||||
|
||||
Note that macros can request expansion by including `(autoload-macro
|
||||
expand)' among their `declare' forms."
|
||||
(let ((car (car-safe form)) expand)
|
||||
(cond
|
||||
((and expansion (eq car 'defalias))
|
||||
|
|
@ -192,42 +217,40 @@ expression, in which case we want to handle forms differently."
|
|||
(setq form (copy-sequence form))
|
||||
(setcdr (memq :autoload-end form) nil))
|
||||
(let ((exps (delq nil (mapcar (lambda (form)
|
||||
(loaddefs-generate--make-autoload
|
||||
form file expansion))
|
||||
(unless (eq form :autoload-end)
|
||||
(loaddefs-generate--make-autoload
|
||||
form file expansion)))
|
||||
(cdr form)))))
|
||||
(when exps (cons 'progn exps)))))
|
||||
|
||||
;; For complex cases, try again on the macro-expansion.
|
||||
((and (memq car '( define-globalized-minor-mode defun defmacro
|
||||
define-minor-mode define-inline
|
||||
cl-defun cl-defmacro cl-defgeneric
|
||||
cl-defstruct pcase-defmacro iter-defun cl-iter-defun
|
||||
;; Obsolete; keep until the alias is removed.
|
||||
easy-mmode-define-global-mode
|
||||
easy-mmode-define-minor-mode
|
||||
define-global-minor-mode))
|
||||
(macrop car)
|
||||
(setq expand (let ((load-true-file-name file)
|
||||
(load-file-name file))
|
||||
(macroexpand form)))
|
||||
(memq (car expand) '(progn prog1 defalias)))
|
||||
;; For macros which request it, try again on their expansion.
|
||||
((progn
|
||||
;; If the car is an unknown symbol, we load the file first to
|
||||
;; give packages a chance to define their macros.
|
||||
(unless (or (not (symbolp car)) (fboundp car)
|
||||
;; Special cases handled below
|
||||
(memq car loaddefs--defining-macros)
|
||||
(memq car '(defclass defcustom deftheme defgroup nil))
|
||||
(assoc file load-history)
|
||||
(member file loaddefs--load-error-files))
|
||||
(let ((load-path (cons (file-name-directory file) load-path)))
|
||||
(message "loaddefs-gen: loading file %s (for %s)" file car)
|
||||
(condition-case e (load file)
|
||||
(error
|
||||
(push file loaddefs--load-error-files) ; do not attempt again
|
||||
(warn "loaddefs-gen: load error\n\t%s" e)))))
|
||||
(and (macrop car)
|
||||
(eq 'expand (function-get car 'autoload-macro))
|
||||
(setq expand (let ((load-true-file-name file)
|
||||
(load-file-name file))
|
||||
(macroexpand form)))
|
||||
(not (eq car (car expand)))))
|
||||
;; Recurse on the expansion.
|
||||
(loaddefs-generate--make-autoload expand file 'expansion))
|
||||
|
||||
;; For special function-like operators, use the `autoload' function.
|
||||
((memq car '( define-skeleton define-derived-mode
|
||||
define-compilation-mode define-generic-mode
|
||||
define-globalized-minor-mode
|
||||
define-minor-mode
|
||||
cl-defun defun* cl-defmacro defmacro*
|
||||
define-overloadable-function
|
||||
transient-define-prefix transient-define-suffix
|
||||
transient-define-infix transient-define-argument
|
||||
transient-define-group
|
||||
;; Obsolete; keep until the alias is removed.
|
||||
easy-mmode-define-global-mode
|
||||
easy-mmode-define-minor-mode
|
||||
define-global-minor-mode))
|
||||
;; For known special macros which define functions, use `autoload'
|
||||
;; directly.
|
||||
((memq car loaddefs--defining-macros)
|
||||
(let* ((macrop (memq car '(defmacro cl-defmacro defmacro*)))
|
||||
(name (nth 1 form))
|
||||
(args (pcase car
|
||||
|
|
|
|||
|
|
@ -544,7 +544,9 @@ to this macro.
|
|||
By convention, DOC should use \"EXPVAL\" to stand
|
||||
for the result of evaluating EXP (first arg to `pcase').
|
||||
\n(fn NAME ARGS [DOC] &rest BODY...)"
|
||||
(declare (indent 2) (debug defun) (doc-string 3))
|
||||
(declare (indent 2) (debug defun) (doc-string 3)
|
||||
;; Expand to defun and related forms on autoload gen
|
||||
(autoload-macro expand))
|
||||
;; Add the function via `fsym', so that an autoload cookie placed
|
||||
;; on a pcase-defmacro will cause the macro to be loaded on demand.
|
||||
(let ((fsym (intern (format "%s--pcase-macroexpander" name)))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue