aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Mackenzie2016-05-08 13:24:20 +0000
committerAlan Mackenzie2016-05-08 13:24:20 +0000
commit2eb6817ba971184cc109f8530f4b3b38f65650ea (patch)
treeb23258db9a4d1720583784fd1b90824a45835f91
parent344eb61ab3607827930354589174bb8d270241b9 (diff)
downloademacs-2eb6817ba971184cc109f8530f4b3b38f65650ea.tar.gz
emacs-2eb6817ba971184cc109f8530f4b3b38f65650ea.zip
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.
-rw-r--r--doc/lispref/modes.texi26
-rw-r--r--etc/NEWS6
-rw-r--r--lisp/emacs-lisp/derived.el15
-rw-r--r--lisp/subr.el19
4 files changed, 53 insertions, 13 deletions
diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi
index 76e5174bd20..7b76e6af9c3 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
752@item 752@item
753The new mode has its own mode hook, @code{@var{variant}-hook}. It 753The new mode has its own mode hook, @code{@var{variant}-hook}. It
754runs this hook, after running the hooks of its ancestor modes, with 754runs this hook, after running the hooks of its ancestor modes, with
755@code{run-mode-hooks}, as the last thing it does. @xref{Mode Hooks}. 755@code{run-mode-hooks}, as the last thing it does, apart from running
756any @code{:after-hook} form it may have. @xref{Mode Hooks}.
756@end itemize 757@end itemize
757 758
758In addition, you can specify how to override other aspects of 759In 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
776documentation string. If you omit @var{docstring}, 777documentation string. If you omit @var{docstring},
777@code{define-derived-mode} generates a documentation string. 778@code{define-derived-mode} generates a documentation string.
778 779
779The @var{keyword-args} are pairs of keywords and values. The values 780The @var{keyword-args} are pairs of keywords and values. The values,
780are evaluated. The following keywords are currently supported: 781except for @code{:after-hook}'s, are evaluated. The following
782keywords are currently supported:
781 783
782@table @code 784@table @code
783@item :syntax-table 785@item :syntax-table
@@ -801,6 +803,15 @@ this mode. (Not all major modes have one.) Only the (still
801experimental and unadvertised) command @code{customize-mode} currently 803experimental and unadvertised) command @code{customize-mode} currently
802uses this. @code{define-derived-mode} does @emph{not} automatically 804uses this. @code{define-derived-mode} does @emph{not} automatically
803define the specified customization group. 805define the specified customization group.
806
807@item :after-hook
808This optional keyword specifies a single Lisp form to evaluate as the
809final act of the mode function, after the mode hooks have been run.
810It should not be quoted. Since the form might be evaluated after the
811mode function has terminated, it should not access any element of the
812mode function's local state. An @code{:after-hook} form is useful for
813setting up aspects of the mode which depend on the user's settings,
814which in turn may have been changed in a mode hook.
804@end table 815@end table
805 816
806Here is a hypothetical example: 817Here is a hypothetical example:
@@ -912,12 +923,15 @@ Major modes should run their mode hook using this function. It is
912similar to @code{run-hooks} (@pxref{Hooks}), but it also runs 923similar to @code{run-hooks} (@pxref{Hooks}), but it also runs
913@code{change-major-mode-after-body-hook}, @code{hack-local-variables} 924@code{change-major-mode-after-body-hook}, @code{hack-local-variables}
914(when the buffer is visiting a file) (@pxref{File Local Variables}), 925(when the buffer is visiting a file) (@pxref{File Local Variables}),
915and @code{after-change-major-mode-hook}. 926and @code{after-change-major-mode-hook}. The last thing it does is to
927evaluate any @code{:after-hook} forms declared by parent modes
928(@pxref{Derived Modes}).
916 929
917When this function is called during the execution of a 930When this function is called during the execution of a
918@code{delay-mode-hooks} form, it does not run the hooks or 931@code{delay-mode-hooks} form, it does not run the hooks or
919@code{hack-local-variables} immediately. Instead, it arranges for the 932@code{hack-local-variables} or evaluate the forms immediately.
920next call to @code{run-mode-hooks} to run them. 933Instead, it arranges for the next call to @code{run-mode-hooks} to run
934them.
921@end defun 935@end defun
922 936
923@defmac delay-mode-hooks body@dots{} 937@defmac delay-mode-hooks body@dots{}
diff --git a/etc/NEWS b/etc/NEWS
index 92d301ecd6f..22eb2eabc37 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -369,6 +369,12 @@ variable.
369 369
370** New var syntax-ppss-table to control the syntax-table used in syntax-ppss. 370** New var syntax-ppss-table to control the syntax-table used in syntax-ppss.
371 371
372+++
373** `define-derived-mode' can now specify an :after-hook form, which
374gets evaluated after the new mode's hook has run. This can be used to
375incorporate configuration changes made in the mode hook into the
376mode's setup.
377
372** Autoload files can be generated without timestamps, 378** Autoload files can be generated without timestamps,
373by setting 'autoload-timestamps' to nil. 379by setting 'autoload-timestamps' to nil.
374FIXME As an experiment, nil is the current default. 380FIXME As an experiment, nil is the current default.
diff --git a/lisp/emacs-lisp/derived.el b/lisp/emacs-lisp/derived.el
index a615f9a5854..0f7691af0f4 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
137:abbrev-table TABLE 137:abbrev-table TABLE
138 Use TABLE instead of the default (CHILD-abbrev-table). 138 Use TABLE instead of the default (CHILD-abbrev-table).
139 A nil value means to simply use the same abbrev-table as the parent. 139 A nil value means to simply use the same abbrev-table as the parent.
140:after-hook FORM
141 A single lisp form which is evaluated after the mode hooks have been
142 run. It should not be quoted.
140 143
141Here is how you could define LaTeX-Thesis mode as a variant of LaTeX mode: 144Here is how you could define LaTeX-Thesis mode as a variant of LaTeX mode:
142 145
@@ -184,7 +187,8 @@ See Info node `(elisp)Derived Modes' for more details."
184 (declare-abbrev t) 187 (declare-abbrev t)
185 (declare-syntax t) 188 (declare-syntax t)
186 (hook (derived-mode-hook-name child)) 189 (hook (derived-mode-hook-name child))
187 (group nil)) 190 (group nil)
191 (after-hook nil))
188 192
189 ;; Process the keyword args. 193 ;; Process the keyword args.
190 (while (keywordp (car body)) 194 (while (keywordp (car body))
@@ -192,6 +196,7 @@ See Info node `(elisp)Derived Modes' for more details."
192 (`:group (setq group (pop body))) 196 (`:group (setq group (pop body)))
193 (`:abbrev-table (setq abbrev (pop body)) (setq declare-abbrev nil)) 197 (`:abbrev-table (setq abbrev (pop body)) (setq declare-abbrev nil))
194 (`:syntax-table (setq syntax (pop body)) (setq declare-syntax nil)) 198 (`:syntax-table (setq syntax (pop body)) (setq declare-syntax nil))
199 (`:after-hook (setq after-hook (pop body)))
195 (_ (pop body)))) 200 (_ (pop body))))
196 201
197 (setq docstring (derived-mode-make-docstring 202 (setq docstring (derived-mode-make-docstring
@@ -272,7 +277,11 @@ No problems result if this variable is not bound.
272 ,@body 277 ,@body
273 ) 278 )
274 ;; Run the hooks, if any. 279 ;; Run the hooks, if any.
275 (run-mode-hooks ',hook))))) 280 (run-mode-hooks ',hook)
281 ,@(when after-hook
282 `((if delay-mode-hooks
283 (push ',after-hook delayed-after-hook-forms)
284 ,after-hook)))))))
276 285
277;; PUBLIC: find the ultimate class of a derived mode. 286;; PUBLIC: find the ultimate class of a derived mode.
278 287
@@ -344,7 +353,7 @@ which more-or-less shadow%s %s's corresponding table%s."
344 (format "`%s' " parent)) 353 (format "`%s' " parent))
345 "might have run,\nthis mode ")) 354 "might have run,\nthis mode "))
346 (format "runs the hook `%s'" hook) 355 (format "runs the hook `%s'" hook)
347 ", as the final step\nduring initialization."))) 356 ", as the final or penultimate step\nduring initialization.")))
348 357
349 (unless (string-match "\\\\[{[]" docstring) 358 (unless (string-match "\\\\[{[]" docstring)
350 ;; And don't forget to put the mode's keymap. 359 ;; And don't forget to put the mode's keymap.
diff --git a/lisp/subr.el b/lisp/subr.el
index 094710b026c..0fa6404d37d 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -1736,6 +1736,11 @@ if it is empty or a duplicate."
1736(make-variable-buffer-local 'delayed-mode-hooks) 1736(make-variable-buffer-local 'delayed-mode-hooks)
1737(put 'delay-mode-hooks 'permanent-local t) 1737(put 'delay-mode-hooks 'permanent-local t)
1738 1738
1739(defvar delayed-after-hook-forms nil
1740 "List of delayed :after-hook forms waiting to be run.
1741These forms come from `define-derived-mode'.")
1742(make-variable-buffer-local 'delayed-after-hook-forms)
1743
1739(defvar change-major-mode-after-body-hook nil 1744(defvar change-major-mode-after-body-hook nil
1740 "Normal hook run in major mode functions, before the mode hooks.") 1745 "Normal hook run in major mode functions, before the mode hooks.")
1741 1746
@@ -1751,9 +1756,12 @@ If the variable `delay-mode-hooks' is non-nil, does not do anything,
1751just adds the HOOKS to the list `delayed-mode-hooks'. 1756just adds the HOOKS to the list `delayed-mode-hooks'.
1752Otherwise, runs hooks in the sequence: `change-major-mode-after-body-hook', 1757Otherwise, runs hooks in the sequence: `change-major-mode-after-body-hook',
1753`delayed-mode-hooks' (in reverse order), HOOKS, then runs 1758`delayed-mode-hooks' (in reverse order), HOOKS, then runs
1754`hack-local-variables' and finally runs the hook 1759`hack-local-variables', runs the hook `after-change-major-mode-hook', and
1755`after-change-major-mode-hook'. Major mode functions should use 1760finally evaluates the forms in `delayed-after-hook-forms' (see
1756this instead of `run-hooks' when running their FOO-mode-hook." 1761`define-derived-mode').
1762
1763Major mode functions should use this instead of `run-hooks' when
1764running their FOO-mode-hook."
1757 (if delay-mode-hooks 1765 (if delay-mode-hooks
1758 ;; Delaying case. 1766 ;; Delaying case.
1759 (dolist (hook hooks) 1767 (dolist (hook hooks)
@@ -1765,7 +1773,10 @@ this instead of `run-hooks' when running their FOO-mode-hook."
1765 (if (buffer-file-name) 1773 (if (buffer-file-name)
1766 (with-demoted-errors "File local-variables error: %s" 1774 (with-demoted-errors "File local-variables error: %s"
1767 (hack-local-variables 'no-mode))) 1775 (hack-local-variables 'no-mode)))
1768 (run-hooks 'after-change-major-mode-hook))) 1776 (run-hooks 'after-change-major-mode-hook)
1777 (dolist (form (nreverse delayed-after-hook-forms))
1778 (eval form))
1779 (setq delayed-after-hook-forms nil)))
1769 1780
1770(defmacro delay-mode-hooks (&rest body) 1781(defmacro delay-mode-hooks (&rest body)
1771 "Execute BODY, but delay any `run-mode-hooks'. 1782 "Execute BODY, but delay any `run-mode-hooks'.