unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Calling another major mode in a major mode body
@ 2022-11-21 22:07 Yuan Fu
  2022-11-22  0:44 ` Phil Sainty
  0 siblings, 1 reply; 13+ messages in thread
From: Yuan Fu @ 2022-11-21 22:07 UTC (permalink / raw)
  To: emacs-devel

Sometimes it would be nice for the tree-sitter mode to fallback to the non-tree-sitter mode, eg, when the buffer is to large. Sh-mode needs something similar, too, because tree-sitter only supports bash right now. If the shell is some other shell, the tree-sitter mode should fall back to the normal sh-mode.

Fallback in the above two cases are necessary because users can’t easily avoid them: currently there is no easy way to make Emacs use different major modes based on file size, or shell type. (You could use magic-mode-alist for shell, but that’s not TRT, I think)

So I wonder if it’s ok to fall back to another major mode by simply calling that mode.

Yuan


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Calling another major mode in a major mode body
  2022-11-21 22:07 Calling another major mode in a major mode body Yuan Fu
@ 2022-11-22  0:44 ` Phil Sainty
  2022-11-23  2:03   ` Yuan Fu
  0 siblings, 1 reply; 13+ messages in thread
From: Phil Sainty @ 2022-11-22  0:44 UTC (permalink / raw)
  To: Yuan Fu; +Cc: emacs-devel

On 2022-11-22 11:07, Yuan Fu wrote:
> So I wonder if it’s ok to fall back to another major mode by simply
> calling that mode.

I think the following describes what that would do.


Quoting myself from https://stackoverflow.com/a/19295380 (and as a
tangent I'd be happy for some adaptation of that to live somewhere
in the elisp manual, as I think it was a decent explanation of the
processes), when we call `child-mode', the full sequence is:

(run-hooks 'change-major-mode-hook) ;; actually the first thing done by
(kill-all-local-variables)          ;; <-- this function
,@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)
;; plus the following final step, since:
;; commit 2eb6817ba971184cc109f8530f4b3b38f65650ea
;; Add :after-hook facility to define-derived-mode.
(run-hooks delayed-after-hook-functions)


`delay-mode-hooks' is still in effect until child-body has returned,
so I believe calling (fallback-mode) within child-body would result
in this sequence:


(run-hooks 'change-major-mode-hook) ;; actually the first thing done by
(kill-all-local-variables)          ;; <-- this function
,@grandparent-body
,@parent-body
,@child-body
+    (run-hooks 'change-major-mode-hook) ;; actually the first thing 
done by
+    (kill-all-local-variables)          ;; <-- this function
+    ,@fallback-parent-mode-body
+    ,@fallback-mode-body
;; The child-mode binding for `delay-mode-hooks' is now out of scope,
;; so `run-mode-hooks' finally acts...
(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 'fallback-parent-mode-hook)
+    (run-hooks 'fallback-mode-hook)
(run-hooks 'after-change-major-mode-hook)
(run-hooks delayed-after-hook-functions)


It looks like things pushed onto `delayed-after-hook-functions'
would happen in this sequence, though:

- grandparent-mode
- parent-mode
- fallback-parent-mode
- fallback-mode
- child-mode

Although `delayed-after-hook-functions' does not seem to be
permanent-local, so in fact it might be this?

- fallback-parent-mode
- fallback-mode
- child-mode




There's also this, which doesn't seem entirely appropriate, but...

** New functions 'major-mode-suspend' and 'major-mode-restore'
Used when switching temporarily to another major mode, e.g. for 
hexl-mode,
or to switch between c-mode and image-mode in XPM.




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Calling another major mode in a major mode body
  2022-11-22  0:44 ` Phil Sainty
@ 2022-11-23  2:03   ` Yuan Fu
  2022-11-23  2:15     ` Yuan Fu
  2022-11-23  2:46     ` Stefan Monnier
  0 siblings, 2 replies; 13+ messages in thread
From: Yuan Fu @ 2022-11-23  2:03 UTC (permalink / raw)
  To: Phil Sainty; +Cc: emacs-devel



> On Nov 21, 2022, at 4:44 PM, Phil Sainty <psainty@orcon.net.nz> wrote:
> 
> On 2022-11-22 11:07, Yuan Fu wrote:
>> So I wonder if it’s ok to fall back to another major mode by simply
>> calling that mode.
> 
> I think the following describes what that would do.
> 
> 
> Quoting myself from https://stackoverflow.com/a/19295380 (and as a
> tangent I'd be happy for some adaptation of that to live somewhere
> in the elisp manual, as I think it was a decent explanation of the
> processes), when we call `child-mode', the full sequence is:
> 
> (run-hooks 'change-major-mode-hook) ;; actually the first thing done by
> (kill-all-local-variables)          ;; <-- this function
> ,@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)
> ;; plus the following final step, since:
> ;; commit 2eb6817ba971184cc109f8530f4b3b38f65650ea
> ;; Add :after-hook facility to define-derived-mode.
> (run-hooks delayed-after-hook-functions)
> 
> 
> `delay-mode-hooks' is still in effect until child-body has returned,
> so I believe calling (fallback-mode) within child-body would result
> in this sequence:
> 
> 
> (run-hooks 'change-major-mode-hook) ;; actually the first thing done by
> (kill-all-local-variables)          ;; <-- this function
> ,@grandparent-body
> ,@parent-body
> ,@child-body
> +    (run-hooks 'change-major-mode-hook) ;; actually the first thing done by
> +    (kill-all-local-variables)          ;; <-- this function
> +    ,@fallback-parent-mode-body
> +    ,@fallback-mode-body
> ;; The child-mode binding for `delay-mode-hooks' is now out of scope,
> ;; so `run-mode-hooks' finally acts...
> (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 'fallback-parent-mode-hook)
> +    (run-hooks 'fallback-mode-hook)
> (run-hooks 'after-change-major-mode-hook)
> (run-hooks delayed-after-hook-functions)
> 
> 
> It looks like things pushed onto `delayed-after-hook-functions'
> would happen in this sequence, though:
> 
> - grandparent-mode
> - parent-mode
> - fallback-parent-mode
> - fallback-mode
> - child-mode
> 
> Although `delayed-after-hook-functions' does not seem to be
> permanent-local, so in fact it might be this?
> 
> - fallback-parent-mode
> - fallback-mode
> - child-mode

Thanks for that detailed explanation :-)

It seems the current mode’s after-hook is ran the very last. So it might be a good place to call the fallback major mode. The call to run-hooks in a major mode invocation command is outside the scope delay-mode-hooks, so simply calling the fallback major mode should be fine?

IMO, the sequence would be
- parent-mode
- child-mode
- parent-hook
- child-hook
- parent-after-hook
- child-after-hook: calls fallback-mode
- fallback-parent ...

Yuan


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Calling another major mode in a major mode body
  2022-11-23  2:03   ` Yuan Fu
@ 2022-11-23  2:15     ` Yuan Fu
  2022-11-23  2:46     ` Stefan Monnier
  1 sibling, 0 replies; 13+ messages in thread
From: Yuan Fu @ 2022-11-23  2:15 UTC (permalink / raw)
  To: Phil Sainty; +Cc: emacs-devel, Stefan Monnier



> On Nov 22, 2022, at 6:03 PM, Yuan Fu <casouri@gmail.com> wrote:
> 
> 
> 
>> On Nov 21, 2022, at 4:44 PM, Phil Sainty <psainty@orcon.net.nz> wrote:
>> 
>> On 2022-11-22 11:07, Yuan Fu wrote:
>>> So I wonder if it’s ok to fall back to another major mode by simply
>>> calling that mode.
>> 
>> I think the following describes what that would do.
>> 
>> 
>> Quoting myself from https://stackoverflow.com/a/19295380 (and as a
>> tangent I'd be happy for some adaptation of that to live somewhere
>> in the elisp manual, as I think it was a decent explanation of the
>> processes), when we call `child-mode', the full sequence is:
>> 
>> (run-hooks 'change-major-mode-hook) ;; actually the first thing done by
>> (kill-all-local-variables)          ;; <-- this function
>> ,@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)
>> ;; plus the following final step, since:
>> ;; commit 2eb6817ba971184cc109f8530f4b3b38f65650ea
>> ;; Add :after-hook facility to define-derived-mode.
>> (run-hooks delayed-after-hook-functions)
>> 
>> 
>> `delay-mode-hooks' is still in effect until child-body has returned,
>> so I believe calling (fallback-mode) within child-body would result
>> in this sequence:
>> 
>> 
>> (run-hooks 'change-major-mode-hook) ;; actually the first thing done by
>> (kill-all-local-variables)          ;; <-- this function
>> ,@grandparent-body
>> ,@parent-body
>> ,@child-body
>> +    (run-hooks 'change-major-mode-hook) ;; actually the first thing done by
>> +    (kill-all-local-variables)          ;; <-- this function
>> +    ,@fallback-parent-mode-body
>> +    ,@fallback-mode-body
>> ;; The child-mode binding for `delay-mode-hooks' is now out of scope,
>> ;; so `run-mode-hooks' finally acts...
>> (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 'fallback-parent-mode-hook)
>> +    (run-hooks 'fallback-mode-hook)
>> (run-hooks 'after-change-major-mode-hook)
>> (run-hooks delayed-after-hook-functions)
>> 
>> 
>> It looks like things pushed onto `delayed-after-hook-functions'
>> would happen in this sequence, though:
>> 
>> - grandparent-mode
>> - parent-mode
>> - fallback-parent-mode
>> - fallback-mode
>> - child-mode
>> 
>> Although `delayed-after-hook-functions' does not seem to be
>> permanent-local, so in fact it might be this?
>> 
>> - fallback-parent-mode
>> - fallback-mode
>> - child-mode
> 
> Thanks for that detailed explanation :-)
> 
> It seems the current mode’s after-hook is ran the very last. So it might be a good place to call the fallback major mode. The call to run-hooks in a major mode invocation command is outside the scope delay-mode-hooks, so simply calling the fallback major mode should be fine?
> 
> IMO, the sequence would be
> - parent-mode
> - child-mode
> - parent-hook
> - child-hook
> - parent-after-hook
> - child-after-hook: calls fallback-mode
> - fallback-parent …

Perhaps it’s more clear with a demonstration:

We define three modes, A for parent, B for child, F for fallback. Both B and F inherits A. When we call B-mode, it automatically falls back to F-mode in its after-hook

(define-derived-mode A-mode nil "A"
  "A mode."
  :after-hook (message "A after-hook")
  (message "A body"))

(define-derived-mode B-mode A-mode "B"
  "B mode."
  :after-hook (progn (message "B after-hook")
                     (F-mode))
  (message "B body"))

(define-derived-mode F-mode A-mode "F"
  "F mode."
  :after-hook (message "F after-hook")
  (message "F body"))

(setq A-mode-hook (list (lambda () (message "A hook"))))
(setq B-mode-hook (list (lambda () (message "B hook"))))
(setq F-mode-hook (list (lambda () (message "F hook"))))

M-x B-mode RET produces:

A body
B body
A hook
A after-hook
B after-hook (here F-mode is called)
A body
F body
A hook
A after-hook
F after-hook

All in all, I don’t see any immediate harm of falling back to another mode like this. A’s body and hook run twice, but so does it when the user manually changes B-mode to F-mode.

If we want to be extra safe, perhaps we can do (run-with-idle-timer 0 nil #'F-mode) in B-mode’s after-hook.

Yuan


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Calling another major mode in a major mode body
  2022-11-23  2:03   ` Yuan Fu
  2022-11-23  2:15     ` Yuan Fu
@ 2022-11-23  2:46     ` Stefan Monnier
  2022-11-23 18:36       ` Yuan Fu
  1 sibling, 1 reply; 13+ messages in thread
From: Stefan Monnier @ 2022-11-23  2:46 UTC (permalink / raw)
  To: Yuan Fu; +Cc: Phil Sainty, emacs-devel

> Thanks for that detailed explanation :-)
>
> It seems the current mode’s after-hook is ran the very last. So it might be
> a good place to call the fallback major mode. The call to run-hooks in
> a major mode invocation command is outside the scope delay-mode-hooks, so
> simply calling the fallback major mode should be fine?

I think even cleaner is if the dispatch can happen before we even call
`kill-all-local-variables`.

That's what `tex-mode` does (tho it gets there in a roundabout way
because I didn't want to change `define-derived-mode`).


        Stefan




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Calling another major mode in a major mode body
  2022-11-23  2:46     ` Stefan Monnier
@ 2022-11-23 18:36       ` Yuan Fu
  2022-11-23 19:21         ` Stefan Monnier
  0 siblings, 1 reply; 13+ messages in thread
From: Yuan Fu @ 2022-11-23 18:36 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Phil Sainty, emacs-devel



> On Nov 22, 2022, at 6:46 PM, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
> 
>> Thanks for that detailed explanation :-)
>> 
>> It seems the current mode’s after-hook is ran the very last. So it might be
>> a good place to call the fallback major mode. The call to run-hooks in
>> a major mode invocation command is outside the scope delay-mode-hooks, so
>> simply calling the fallback major mode should be fine?
> 
> I think even cleaner is if the dispatch can happen before we even call
> `kill-all-local-variables`.
> 
> That's what `tex-mode` does (tho it gets there in a roundabout way
> because I didn't want to change `define-derived-mode`).
> 
> 
>        Stefan
> 

Yes, it’s much cleaner. If we don’t care too much about using advice in our source. I’ll do that instead.

Yuan


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Calling another major mode in a major mode body
  2022-11-23 18:36       ` Yuan Fu
@ 2022-11-23 19:21         ` Stefan Monnier
  2022-12-04  7:54           ` Yuan Fu
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Monnier @ 2022-11-23 19:21 UTC (permalink / raw)
  To: Yuan Fu; +Cc: Phil Sainty, emacs-devel

> Yes, it’s much cleaner. If we don’t care too much about using advice in our
> source. I’ll do that instead.

We really should change `define-derived-mode` to support this
directly, tho.


        Stefan




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Calling another major mode in a major mode body
  2022-11-23 19:21         ` Stefan Monnier
@ 2022-12-04  7:54           ` Yuan Fu
  2022-12-05 22:37             ` Richard Stallman
  0 siblings, 1 reply; 13+ messages in thread
From: Yuan Fu @ 2022-12-04  7:54 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Phil Sainty, emacs-devel



> On Nov 23, 2022, at 11:21 AM, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
> 
>> Yes, it’s much cleaner. If we don’t care too much about using advice in our
>> source. I’ll do that instead.
> 
> We really should change `define-derived-mode` to support this
> directly, tho.
> 
> 
>        Stefan
> 

Yeah, in Emacs 30, probably :-) I used the advice technique/hack similar to tex-mode for bash-ts-mode.

Yuan


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Calling another major mode in a major mode body
  2022-12-04  7:54           ` Yuan Fu
@ 2022-12-05 22:37             ` Richard Stallman
  2022-12-05 22:50               ` Stefan Monnier
  0 siblings, 1 reply; 13+ messages in thread
From: Richard Stallman @ 2022-12-05 22:37 UTC (permalink / raw)
  To: Yuan Fu; +Cc: monnier, psainty, emacs-devel

[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

  > Yeah, in Emacs 30, probably :-) I used the advice technique/hack similar to tex-mode for bash-ts-mode.

Plsee fix that.  To make one part of Emacs put advice on another part
is asking for future development trouble.

The worst thing is that the trouble is likely to happen to some other
developer.

When your code puts advice on a function BAR, that means other code
that calls BAR will get behavior different from what the source code
of BAR says.  Someone else will debug a program that calls BAR and
tear per hair out trying to figure out why it does not do what ALL the
source code says it should do.

Please don't install code that advises other code.
If you've already done that, please fix it now.
Don't add it to the backlog of pitfalls waiting to be fixed.

-- 
Dr Richard Stallman (https://stallman.org)
Chief GNUisance of the GNU Project (https://gnu.org)
Founder, Free Software Foundation (https://fsf.org)
Internet Hall-of-Famer (https://internethalloffame.org)





^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Calling another major mode in a major mode body
  2022-12-05 22:37             ` Richard Stallman
@ 2022-12-05 22:50               ` Stefan Monnier
  2022-12-08 23:07                 ` Richard Stallman
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Monnier @ 2022-12-05 22:50 UTC (permalink / raw)
  To: Richard Stallman; +Cc: Yuan Fu, psainty, emacs-devel

>   > Yeah, in Emacs 30, probably :-) I used the advice technique/hack similar
>   > to tex-mode for bash-ts-mode.
> Plsee fix that.  To make one part of Emacs put advice on another part
> is asking for future development trouble.

Note that the above two uses of advice don't advise *other* code but
advise their own code instead :-)


        Stef




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Calling another major mode in a major mode body
  2022-12-05 22:50               ` Stefan Monnier
@ 2022-12-08 23:07                 ` Richard Stallman
  2022-12-09 12:56                   ` Stefan Monnier
  0 siblings, 1 reply; 13+ messages in thread
From: Richard Stallman @ 2022-12-08 23:07 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: casouri, psainty, emacs-devel

[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

  > Note that the above two uses of advice don't advise *other* code but
  > advise their own code instead :-)

That is less of a problem, I agree.  But still, why not use a flag
or a hook?

-- 
Dr Richard Stallman (https://stallman.org)
Chief GNUisance of the GNU Project (https://gnu.org)
Founder, Free Software Foundation (https://fsf.org)
Internet Hall-of-Famer (https://internethalloffame.org)





^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Calling another major mode in a major mode body
  2022-12-08 23:07                 ` Richard Stallman
@ 2022-12-09 12:56                   ` Stefan Monnier
  2022-12-10 22:05                     ` Richard Stallman
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Monnier @ 2022-12-09 12:56 UTC (permalink / raw)
  To: Richard Stallman; +Cc: casouri, psainty, emacs-devel

>   > Note that the above two uses of advice don't advise *other* code but
>   > advise their own code instead :-)
> That is less of a problem, I agree.  But still, why not use a flag
> or a hook?

Look at the code and you'll see :-)


        Stefan




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Calling another major mode in a major mode body
  2022-12-09 12:56                   ` Stefan Monnier
@ 2022-12-10 22:05                     ` Richard Stallman
  0 siblings, 0 replies; 13+ messages in thread
From: Richard Stallman @ 2022-12-10 22:05 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: casouri, psainty, emacs-devel

[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

  > >   > Note that the above two uses of advice don't advise *other* code but
  > >   > advise their own code instead :-)
  > > That is less of a problem, I agree.  But still, why not use a flag
  > > or a hook?

  > Look at the code and you'll see :-)

1. Is it in master?  If I pull in the latest master,
will this be included?

2. Can you tell me which file it is in?

-- 
Dr Richard Stallman (https://stallman.org)
Chief GNUisance of the GNU Project (https://gnu.org)
Founder, Free Software Foundation (https://fsf.org)
Internet Hall-of-Famer (https://internethalloffame.org)





^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2022-12-10 22:05 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-21 22:07 Calling another major mode in a major mode body Yuan Fu
2022-11-22  0:44 ` Phil Sainty
2022-11-23  2:03   ` Yuan Fu
2022-11-23  2:15     ` Yuan Fu
2022-11-23  2:46     ` Stefan Monnier
2022-11-23 18:36       ` Yuan Fu
2022-11-23 19:21         ` Stefan Monnier
2022-12-04  7:54           ` Yuan Fu
2022-12-05 22:37             ` Richard Stallman
2022-12-05 22:50               ` Stefan Monnier
2022-12-08 23:07                 ` Richard Stallman
2022-12-09 12:56                   ` Stefan Monnier
2022-12-10 22:05                     ` Richard Stallman

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).