From: Phil Sainty <psainty@orcon.net.nz>
To: 35351@debbugs.gnu.org
Subject: bug#35351: 27.0.50; Enable derived modes to run their own very-early 'change-major-mode-hook' code
Date: Sun, 21 Apr 2019 14:35:35 +1200 [thread overview]
Message-ID: <85237c18-768d-089b-221a-fe70b0ba4379@orcon.net.nz> (raw)
The library I'm working on (so-long.el) defines a major mode
which needs to remember various buffer-local values as they were
in the original mode, *before* my major mode takes effect.
I'm currently using `change-major-mode-hook' for this, but it has
occurred to me that it would be nicer if this hook code of mine
only ever ran in the case where it is useful (i.e. the major mode
being changed to is in fact my mode). `change-major-mode-hook'
has no knowledge of the mode which has just been invoked, so it
must necessarily run for *every* mode change -- which isn't
relevant to my library in the vast majority of cases.
I think `change-major-mode-hook' would more commonly be used by
modes to handle any subsequent 'unloading' needs of that same
mode in case it gets replaced later on (i.e. the mode body could
set a buffer-local hook value), so my scenario of the new mode
wanting to know things about the previous mode is doubtless a bit
of a niche case; but I thought I'd raise it for discussion.
`define-derived-mode' creates the mode function like so:
(defun ,child ()
,docstring
(interactive)
; Run the parent.
(delay-mode-hooks
(,(or parent 'kill-all-local-variables))
Where PARENT must likewise `kill-all-local-variables' (which runs
`change-major-mode-hook').
What would be nice is a way for the mode definition to provide
code which would be evaluated before that (delay-mode-hooks...)
form, and consequently acted like a `change-major-mode-hook'
which only ever happened if this mode was called.
(A vaguely analogous facility currently in `define-derived-mode'
is the :after-hook keyword, for running code very *late* in the
proceedings.)
I could always define my mode without using the macro, and then
do whatever I wanted before calling `kill-all-local-variables';
but I *want* to have the benefits of using the macro, so I don't
want to resort to that; I just want the equivalent ability,
while still using the macro.
A hack which works in my case (because my mode was not already
derived from another) is:
(defun my-mode-parent ()
(do-my-custom-thing)
(kill-all-local-variables))
(define-derived-mode my-mode my-mode-parent ...)
This feels a little ugly, and `derived-mode-make-docstring' will
point out the existence of the parent, so it's not ideal; but I'm
still tempted to use this approach, and simply document it
sufficiently such that the docstring reference won't cause undue
confusion for those who follow it.
That hack obviously can't be used in cases where a parent is
already defined, but my suggested new feature could still work in
those cases. Something like:
(defun ,child ()
,docstring
(interactive)
,@before-hook
; Run the parent.
(delay-mode-hooks
(,(or parent 'kill-all-local-variables))
If the new child mode is used directly then things pan out almost
identically to the hack version.
More generally, we end up with:
,@child-before-hook
(delay-mode-hooks
,@parent-before-hook
(delay-mode-hooks
,@grandparent-before-hook
(delay-mode-hooks
(kill-all-local-variables) ;; runs change-major-mode-hook
,@grandparent-body)
(run-mode-hooks 'grandparent-mode-hook)
,@parent-body)
(run-mode-hooks 'parent-mode-hook)
,@child-body)
(run-mode-hooks 'child-mode-hook)
Which creates the following sequence of events:
,@child-before-hook
,@parent-before-hook
,@grandparent-before-hook
(kill-all-local-variables) ;; runs change-major-mode-hook
,@grandparent-body
,@parent-body
,@child-body
(run-hooks 'change-major-mode-after-body-hook)
(run-hooks 'grandparent-mode-hook)
(run-hooks 'parent-mode-hook)
(run-hooks 'child-mode-hook)
(run-hooks 'after-change-major-mode-hook)
Obviously that sequence of 'before-hook' instances is in the
reverse sequence to the 'body' and 'mode-hook' sequences. That's
possibly a desirable thing, but I'm not absolutely certain.
A simply way of reversing that sequence would be for each
:before-hook to be added as a buffer-local change-major-mode-hook
entry -- (add-hook 'change-major-mode-hook FOO nil :local) --
rather than evaluating them immediately, which would then build
up a buffer-local hook sequence like:
'(grandparent-before-hook parent-before-hook child-before-hook)
And then `kill-all-local-variables' would run them in that order
(and still ahead of any other pre-existing buffer-local values
for `change-major-mode-hook').
What do people think?
-Phil
next reply other threads:[~2019-04-21 2:35 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-04-21 2:35 Phil Sainty [this message]
2019-04-21 2:45 ` bug#35351: 27.0.50; Enable derived modes to run their own very-early 'change-major-mode-hook' code Phil Sainty
2019-04-21 6:01 ` Richard Stallman
2019-04-21 6:23 ` Eli Zaretskii
2019-04-21 20:54 ` bug#35351: 27.0.50; " Stefan Monnier
2019-04-22 8:16 ` Phil Sainty
2019-04-22 14:39 ` Stefan Monnier
2019-04-22 23:18 ` Phil Sainty
2019-04-22 23:31 ` Phil Sainty
2019-04-23 2:08 ` Stefan Monnier
2019-04-23 3:52 ` Phil Sainty
2019-04-23 11:59 ` Stefan Monnier
2019-04-22 14:41 ` Stefan Monnier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=85237c18-768d-089b-221a-fe70b0ba4379@orcon.net.nz \
--to=psainty@orcon.net.nz \
--cc=35351@debbugs.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.