]> code.delx.au - gnu-emacs/commitdiff
Add :after-hook facility to define-derived-mode.
authorAlan Mackenzie <acm@muc.de>
Sun, 8 May 2016 13:24:20 +0000 (13:24 +0000)
committerAlan Mackenzie <acm@muc.de>
Sun, 8 May 2016 13:24:20 +0000 (13:24 +0000)
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
etc/NEWS
lisp/emacs-lisp/derived.el
lisp/subr.el

index 76e5174bd205706e159ca420b93682e8f3194ca8..7b76e6af9c3d362db66e10168f2e4b0dbb4b09c5 100644 (file)
@@ -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{}
index 92d301ecd6f8d0c13e62789fc5067403df8ecf65..22eb2eabc3706f243ed0db4b71bbe3cbf4f773c2 100644 (file)
--- 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.
index a615f9a5854510db22a82a0954c2cceb6f412c99..0f7691af0f400a762ca5d661da1e54d80c15f73a 100644 (file)
@@ -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.
index 094710b026cae367aac397e32992949023cade8b..0fa6404d37dcb692171294e816231b3dd8481708 100644 (file)
@@ -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'.