From de0503df97a507a523a192e877a8d5c7439c4846 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Thu, 13 Jun 2013 18:24:52 -0400 Subject: [PATCH] * lisp/subr.el (with-eval-after-load): New macro. (eval-after-load): Allow form to be a function. take advantage of lexical-binding. (do-after-load-evaluation): Use dolist and adjust to new format. * lisp/simple.el (bad-packages-alist): Use dolist and with-eval-after-load. * doc/lispref/loading.texi (Hooks for Loading): Document with-eval-after-load instead of eval-after-load. Don't document after-load-alist. * src/lread.c (syms_of_lread): * src/fns.c (Fprovide): Adjust to new format of after-load-alist. --- doc/lispref/ChangeLog | 5 +++ doc/lispref/loading.texi | 36 ++++++------------- etc/NEWS | 5 +-- lisp/ChangeLog | 8 +++++ lisp/simple.el | 11 +++--- lisp/subr.el | 74 ++++++++++++++++++++++++---------------- src/ChangeLog | 5 +++ src/fns.c | 5 ++- src/lread.c | 6 ++-- 9 files changed, 88 insertions(+), 67 deletions(-) diff --git a/doc/lispref/ChangeLog b/doc/lispref/ChangeLog index 259bf9a78a..e14f754344 100644 --- a/doc/lispref/ChangeLog +++ b/doc/lispref/ChangeLog @@ -1,3 +1,8 @@ +2013-06-13 Stefan Monnier + + * loading.texi (Hooks for Loading): Don't document after-load-alist. + Document with-eval-after-load instead of eval-after-load. + 2013-06-11 Xue Fuqiao * files.texi (File Name Expansion): Make the example more diff --git a/doc/lispref/loading.texi b/doc/lispref/loading.texi index 5c92307f7d..4c0f0d73e4 100644 --- a/doc/lispref/loading.texi +++ b/doc/lispref/loading.texi @@ -990,19 +990,18 @@ file that was just loaded. @end defvar If you want code to be executed when a @emph{particular} library is -loaded, use the function @code{eval-after-load}: +loaded, use the macro @code{with-eval-after-load}: -@defun eval-after-load library form -This function arranges to evaluate @var{form} at the end of loading +@defmac with-eval-after-load library body@dots{} +This macro arranges to evaluate @var{body} at the end of loading the file @var{library}, each time @var{library} is loaded. If -@var{library} is already loaded, it evaluates @var{form} right away. -Don't forget to quote @var{form}! +@var{library} is already loaded, it evaluates @var{body} right away. You don't need to give a directory or extension in the file name @var{library}. Normally, you just give a bare file name, like this: @example -(eval-after-load "edebug" '(def-edebug-spec c-point t)) +(with-eval-after-load "edebug" (def-edebug-spec c-point t)) @end example To restrict which files can trigger the evaluation, include a @@ -1014,16 +1013,16 @@ example, @file{my_inst.elc} or @file{my_inst.elc.gz} in some directory @file{my_inst.el}: @example -(eval-after-load "foo/bar/my_inst.elc" @dots{}) +(with-eval-after-load "foo/bar/my_inst.elc" @dots{}) @end example @var{library} can also be a feature (i.e., a symbol), in which case -@var{form} is evaluated at the end of any file where +@var{body} is evaluated at the end of any file where @code{(provide @var{library})} is called. -An error in @var{form} does not undo the load, but does prevent -execution of the rest of @var{form}. -@end defun +An error in @var{body} does not undo the load, but does prevent +execution of the rest of @var{body}. +@end defmac Normally, well-designed Lisp programs should not use @code{eval-after-load}. If you need to examine and set the variables @@ -1031,18 +1030,3 @@ defined in another library (those meant for outside use), you can do it immediately---there is no need to wait until the library is loaded. If you need to call functions defined by that library, you should load the library, preferably with @code{require} (@pxref{Named Features}). - -@defvar after-load-alist -This variable stores an alist built by @code{eval-after-load}, -containing the expressions to evaluate when certain libraries are -loaded. Each element looks like this: - -@example -(@var{regexp-or-feature} @var{forms}@dots{}) -@end example - -The key @var{regexp-or-feature} is either a regular expression or a -symbol, and the value is a list of forms. The forms are evaluated -when the key matches the absolute true name or feature name of the -library being loaded. -@end defvar diff --git a/etc/NEWS b/etc/NEWS index 44e4ada50c..0501b4e97e 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -452,8 +452,9 @@ file using `set-file-extended-attributes'. * Lisp Changes in Emacs 24.4 -FIXME - someone who knows what they are talking about, please improve -this - see http://debbugs.gnu.org/14596 ++++ +** New macro with-eval-after-load. Like eval-after-load, but better behaved. + ** The default file coding for Emacs Lisp files is now utf-8. (See file-coding-system-alist.) In most cases, this change is totally transparent. Files that contain unusual characters but do diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 98e0382853..88f218d15f 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,11 @@ +2013-06-13 Stefan Monnier + + * subr.el (with-eval-after-load): New macro. + (eval-after-load): Allow form to be a function. + take advantage of lexical-binding. + (do-after-load-evaluation): Use dolist and adjust to new format. + * simple.el (bad-packages-alist): Use dolist and with-eval-after-load. + 2013-06-13 Juri Linkov * replace.el (perform-replace): Display "symbol " and other search diff --git a/lisp/simple.el b/lisp/simple.el index 15bf8779f5..3fd94e96d3 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -7295,8 +7295,7 @@ version and use the one distributed with Emacs.")) "Alist of packages known to cause problems in this version of Emacs. Each element has the form (PACKAGE SYMBOL REGEXP STRING). PACKAGE is either a regular expression to match file names, or a -symbol (a feature name); see the documentation of -`after-load-alist', to which this variable adds functions. +symbol (a feature name), like for `with-eval-after-load'. SYMBOL is either the name of a string variable, or `t'. Upon loading PACKAGE, if SYMBOL is t or matches REGEXP, display a warning using STRING as the message.") @@ -7314,10 +7313,10 @@ warning using STRING as the message.") (display-warning package (nth 3 list) :warning))) (error nil))) -(mapc (lambda (elem) - (eval-after-load (car elem) `(bad-package-check ',(car elem)))) - bad-packages-alist) - +(dolist (elem bad-packages-alist) + (let ((pkg (car elem))) + (with-eval-after-load pkg + (bad-package-check pkg)))) (provide 'simple) diff --git a/lisp/subr.el b/lisp/subr.el index 380b2ba66e..05f9167c69 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3729,6 +3729,8 @@ Return nil if there isn't one." (defun eval-after-load (file form) "Arrange that if FILE is loaded, FORM will be run immediately afterwards. If FILE is already loaded, evaluate FORM right now. +FORM can be an Elisp expression (in which case it's passed to `eval'), +or a function (in which case it's passed to `funcall' with no argument). If a matching file is loaded again, FORM will be evaluated again. @@ -3756,43 +3758,58 @@ Usually FILE is just a library name like \"font-lock\" or a feature name like 'font-lock. This function makes or adds to an entry on `after-load-alist'." + (declare (compiler-macro + (lambda (whole) + (if (eq 'quote (car-safe form)) + ;; Quote with lambda so the compiler can look inside. + `(eval-after-load ,file (lambda () ,(nth 1 form))) + whole)))) ;; Add this FORM into after-load-alist (regardless of whether we'll be ;; evaluating it now). (let* ((regexp-or-feature (if (stringp file) (setq file (purecopy (load-history-regexp file))) file)) - (elt (assoc regexp-or-feature after-load-alist))) + (elt (assoc regexp-or-feature after-load-alist)) + (func + (if (functionp form) form + ;; Try to use the "current" lexical/dynamic mode for `form'. + (eval `(lambda () ,form) lexical-binding)))) (unless elt (setq elt (list regexp-or-feature)) (push elt after-load-alist)) - ;; Make sure `form' is evalled in the current lexical/dynamic code. - (setq form `(funcall ',(eval `(lambda () ,form) lexical-binding))) ;; Is there an already loaded file whose name (or `provide' name) ;; matches FILE? (prog1 (if (if (stringp file) (load-history-filename-element regexp-or-feature) (featurep file)) - (eval form)) - (when (symbolp regexp-or-feature) - ;; For features, the after-load-alist elements get run when `provide' is - ;; called rather than at the end of the file. So add an indirection to - ;; make sure that `form' is really run "after-load" in case the provide - ;; call happens early. - (setq form - `(if load-file-name - (let ((fun (make-symbol "eval-after-load-helper"))) - (fset fun `(lambda (file) - (if (not (equal file ',load-file-name)) - nil - (remove-hook 'after-load-functions ',fun) - ,',form))) - (add-hook 'after-load-functions fun)) - ;; Not being provided from a file, run form right now. - ,form))) - ;; Add FORM to the element unless it's already there. - (unless (member form (cdr elt)) - (nconc elt (list form)))))) + (funcall func)) + (let ((delayed-func + (if (not (symbolp regexp-or-feature)) func + ;; For features, the after-load-alist elements get run when + ;; `provide' is called rather than at the end of the file. + ;; So add an indirection to make sure that `func' is really run + ;; "after-load" in case the provide call happens early. + (lambda () + (if (not load-file-name) + ;; Not being provided from a file, run func right now. + (funcall func) + (let ((lfn load-file-name)) + (letrec ((fun (lambda (file) + (when (equal file lfn) + (remove-hook 'after-load-functions fun) + (funcall func))))) + (add-hook 'after-load-functions fun)))))))) + ;; Add FORM to the element unless it's already there. + (unless (member delayed-func (cdr elt)) + (nconc elt (list delayed-func))))))) + +(defmacro with-eval-after-load (file &rest body) + "Execute BODY after FILE is loaded. +FILE is normally a feature name, but it can also be a file name, +in case that file does not provide any feature." + (declare (indent 1) (debug t)) + `(eval-after-load ,file (lambda () ,@body))) (defvar after-load-functions nil "Special hook run after loading a file. @@ -3804,12 +3821,11 @@ name of the file just loaded.") ABS-FILE, a string, should be the absolute true name of a file just loaded. This function is called directly from the C code." ;; Run the relevant eval-after-load forms. - (mapc #'(lambda (a-l-element) - (when (and (stringp (car a-l-element)) - (string-match-p (car a-l-element) abs-file)) - ;; discard the file name regexp - (mapc #'eval (cdr a-l-element)))) - after-load-alist) + (dolist (a-l-element after-load-alist) + (when (and (stringp (car a-l-element)) + (string-match-p (car a-l-element) abs-file)) + ;; discard the file name regexp + (mapc #'funcall (cdr a-l-element)))) ;; Complain when the user uses obsolete files. (when (string-match-p "/obsolete/[^/]*\\'" abs-file) (run-with-timer 0 nil diff --git a/src/ChangeLog b/src/ChangeLog index 4d6e669612..827092e7f8 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,8 @@ +2013-06-13 Stefan Monnier + + * lread.c (syms_of_lread): + * fns.c (Fprovide): Adjust to new format of after-load-alist. + 2013-06-13 Kelly Dean (tiny change) * fileio.c (Fdo_auto_save): Trap errors in auto-save-hook. (Bug#14479) diff --git a/src/fns.c b/src/fns.c index 08c6f055f3..06d4e358f1 100644 --- a/src/fns.c +++ b/src/fns.c @@ -2545,6 +2545,8 @@ SUBFEATURE can be used to check a specific subfeature of FEATURE. */) return (NILP (tem)) ? Qnil : Qt; } +static Lisp_Object Qfuncall; + DEFUN ("provide", Fprovide, Sprovide, 1, 2, 0, doc: /* Announce that FEATURE is a feature of the current Emacs. The optional argument SUBFEATURES should be a list of symbols listing @@ -2567,7 +2569,7 @@ particular subfeatures supported in this version of FEATURE. */) /* Run any load-hooks for this file. */ tem = Fassq (feature, Vafter_load_alist); if (CONSP (tem)) - Fprogn (XCDR (tem)); + Fmapc (Qfuncall, XCDR (tem)); return feature; } @@ -4866,6 +4868,7 @@ syms_of_fns (void) Used by `featurep' and `require', and altered by `provide'. */); Vfeatures = Fcons (intern_c_string ("emacs"), Qnil); DEFSYM (Qsubfeatures, "subfeatures"); + DEFSYM (Qfuncall, "funcall"); #ifdef HAVE_LANGINFO_CODESET DEFSYM (Qcodeset, "codeset"); diff --git a/src/lread.c b/src/lread.c index 3ca644bb45..b57665e365 100644 --- a/src/lread.c +++ b/src/lread.c @@ -4485,15 +4485,15 @@ customize `jka-compr-load-suffixes' rather than the present variable. */); DEFSYM (Qload_in_progress, "load-in-progress"); DEFVAR_LISP ("after-load-alist", Vafter_load_alist, - doc: /* An alist of expressions to be evalled when particular files are loaded. -Each element looks like (REGEXP-OR-FEATURE FORMS...). + doc: /* An alist of functions to be evalled when particular files are loaded. +Each element looks like (REGEXP-OR-FEATURE FUNCS...). REGEXP-OR-FEATURE is either a regular expression to match file names, or a symbol \(a feature name). When `load' is run and the file-name argument matches an element's REGEXP-OR-FEATURE, or when `provide' is run and provides the symbol -REGEXP-OR-FEATURE, the FORMS in the element are executed. +REGEXP-OR-FEATURE, the FUNCS in the element are called. An error in FORMS does not undo the load, but does prevent execution of the rest of the FORMS. */); -- 2.20.1