From 2eb6817ba971184cc109f8530f4b3b38f65650ea Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Sun, 8 May 2016 13:24:20 +0000 Subject: [PATCH] Add :after-hook facility to define-derived-mode. This allow a form to be evaluated _after_ a major mode's hooks have been run. It is needed to solve some problems in CC Mode, including bug #16759 and bug #23476. * lisp/emacs-lisp/derived.el (define-derived-mode): introduce the new argument `:after-hook', and generate the requisite code for it. (derived-mode-make-docstring): Take account of the possibility of :after-hook. * lisp/subr.el (delayed-after-hook-forms): New variable. (run-mode-hooks): As the last thing evaluate the forms in delayed-after-hook-forms. * doc/lispref/modes.texi (Derived Modes): Document :after-hook. (Mode Hooks): Document the new feature in run-mode-hooks. * etc/NEWS: Note the new feature. --- doc/lispref/modes.texi | 26 ++++++++++++++++++++------ etc/NEWS | 6 ++++++ lisp/emacs-lisp/derived.el | 15 ++++++++++++--- lisp/subr.el | 19 +++++++++++++++---- 4 files changed, 53 insertions(+), 13 deletions(-) diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi index 76e5174bd2..7b76e6af9c 100644 --- a/doc/lispref/modes.texi +++ b/doc/lispref/modes.texi @@ -752,7 +752,8 @@ The new mode has its own abbrev table, kept in the variable @item The new mode has its own mode hook, @code{@var{variant}-hook}. It runs this hook, after running the hooks of its ancestor modes, with -@code{run-mode-hooks}, as the last thing it does. @xref{Mode Hooks}. +@code{run-mode-hooks}, as the last thing it does, apart from running +any @code{:after-hook} form it may have. @xref{Mode Hooks}. @end itemize In addition, you can specify how to override other aspects of @@ -776,8 +777,9 @@ about the mode's hook, followed by the mode's keymap, at the end of this documentation string. If you omit @var{docstring}, @code{define-derived-mode} generates a documentation string. -The @var{keyword-args} are pairs of keywords and values. The values -are evaluated. The following keywords are currently supported: +The @var{keyword-args} are pairs of keywords and values. The values, +except for @code{:after-hook}'s, are evaluated. The following +keywords are currently supported: @table @code @item :syntax-table @@ -801,6 +803,15 @@ this mode. (Not all major modes have one.) Only the (still experimental and unadvertised) command @code{customize-mode} currently uses this. @code{define-derived-mode} does @emph{not} automatically define the specified customization group. + +@item :after-hook +This optional keyword specifies a single Lisp form to evaluate as the +final act of the mode function, after the mode hooks have been run. +It should not be quoted. Since the form might be evaluated after the +mode function has terminated, it should not access any element of the +mode function's local state. An @code{:after-hook} form is useful for +setting up aspects of the mode which depend on the user's settings, +which in turn may have been changed in a mode hook. @end table Here is a hypothetical example: @@ -912,12 +923,15 @@ Major modes should run their mode hook using this function. It is similar to @code{run-hooks} (@pxref{Hooks}), but it also runs @code{change-major-mode-after-body-hook}, @code{hack-local-variables} (when the buffer is visiting a file) (@pxref{File Local Variables}), -and @code{after-change-major-mode-hook}. +and @code{after-change-major-mode-hook}. The last thing it does is to +evaluate any @code{:after-hook} forms declared by parent modes +(@pxref{Derived Modes}). When this function is called during the execution of a @code{delay-mode-hooks} form, it does not run the hooks or -@code{hack-local-variables} immediately. Instead, it arranges for the -next call to @code{run-mode-hooks} to run them. +@code{hack-local-variables} or evaluate the forms immediately. +Instead, it arranges for the next call to @code{run-mode-hooks} to run +them. @end defun @defmac delay-mode-hooks body@dots{} diff --git a/etc/NEWS b/etc/NEWS index 92d301ecd6..22eb2eabc3 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -369,6 +369,12 @@ variable. ** New var syntax-ppss-table to control the syntax-table used in syntax-ppss. ++++ +** `define-derived-mode' can now specify an :after-hook form, which +gets evaluated after the new mode's hook has run. This can be used to +incorporate configuration changes made in the mode hook into the +mode's setup. + ** Autoload files can be generated without timestamps, by setting 'autoload-timestamps' to nil. FIXME As an experiment, nil is the current default. diff --git a/lisp/emacs-lisp/derived.el b/lisp/emacs-lisp/derived.el index a615f9a585..0f7691af0f 100644 --- a/lisp/emacs-lisp/derived.el +++ b/lisp/emacs-lisp/derived.el @@ -137,6 +137,9 @@ BODY can start with a bunch of keyword arguments. The following keyword :abbrev-table TABLE Use TABLE instead of the default (CHILD-abbrev-table). A nil value means to simply use the same abbrev-table as the parent. +:after-hook FORM + A single lisp form which is evaluated after the mode hooks have been + run. It should not be quoted. Here is how you could define LaTeX-Thesis mode as a variant of LaTeX mode: @@ -184,7 +187,8 @@ See Info node `(elisp)Derived Modes' for more details." (declare-abbrev t) (declare-syntax t) (hook (derived-mode-hook-name child)) - (group nil)) + (group nil) + (after-hook nil)) ;; Process the keyword args. (while (keywordp (car body)) @@ -192,6 +196,7 @@ See Info node `(elisp)Derived Modes' for more details." (`:group (setq group (pop body))) (`:abbrev-table (setq abbrev (pop body)) (setq declare-abbrev nil)) (`:syntax-table (setq syntax (pop body)) (setq declare-syntax nil)) + (`:after-hook (setq after-hook (pop body))) (_ (pop body)))) (setq docstring (derived-mode-make-docstring @@ -272,7 +277,11 @@ No problems result if this variable is not bound. ,@body ) ;; Run the hooks, if any. - (run-mode-hooks ',hook))))) + (run-mode-hooks ',hook) + ,@(when after-hook + `((if delay-mode-hooks + (push ',after-hook delayed-after-hook-forms) + ,after-hook))))))) ;; PUBLIC: find the ultimate class of a derived mode. @@ -344,7 +353,7 @@ which more-or-less shadow%s %s's corresponding table%s." (format "`%s' " parent)) "might have run,\nthis mode ")) (format "runs the hook `%s'" hook) - ", as the final step\nduring initialization."))) + ", as the final or penultimate step\nduring initialization."))) (unless (string-match "\\\\[{[]" docstring) ;; And don't forget to put the mode's keymap. diff --git a/lisp/subr.el b/lisp/subr.el index 094710b026..0fa6404d37 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -1736,6 +1736,11 @@ if it is empty or a duplicate." (make-variable-buffer-local 'delayed-mode-hooks) (put 'delay-mode-hooks 'permanent-local t) +(defvar delayed-after-hook-forms nil + "List of delayed :after-hook forms waiting to be run. +These forms come from `define-derived-mode'.") +(make-variable-buffer-local 'delayed-after-hook-forms) + (defvar change-major-mode-after-body-hook nil "Normal hook run in major mode functions, before the mode hooks.") @@ -1751,9 +1756,12 @@ If the variable `delay-mode-hooks' is non-nil, does not do anything, just adds the HOOKS to the list `delayed-mode-hooks'. Otherwise, runs hooks in the sequence: `change-major-mode-after-body-hook', `delayed-mode-hooks' (in reverse order), HOOKS, then runs -`hack-local-variables' and finally runs the hook -`after-change-major-mode-hook'. Major mode functions should use -this instead of `run-hooks' when running their FOO-mode-hook." +`hack-local-variables', runs the hook `after-change-major-mode-hook', and +finally evaluates the forms in `delayed-after-hook-forms' (see +`define-derived-mode'). + +Major mode functions should use this instead of `run-hooks' when +running their FOO-mode-hook." (if delay-mode-hooks ;; Delaying case. (dolist (hook hooks) @@ -1765,7 +1773,10 @@ this instead of `run-hooks' when running their FOO-mode-hook." (if (buffer-file-name) (with-demoted-errors "File local-variables error: %s" (hack-local-variables 'no-mode))) - (run-hooks 'after-change-major-mode-hook))) + (run-hooks 'after-change-major-mode-hook) + (dolist (form (nreverse delayed-after-hook-forms)) + (eval form)) + (setq delayed-after-hook-forms nil))) (defmacro delay-mode-hooks (&rest body) "Execute BODY, but delay any `run-mode-hooks'. -- 2.39.2