Subject: Re: bug#51993: 29.0.50; [PATCH] Killing emacsclient terminal with `server-stop-automatically' doesn't prompt to save files

From: Jim Porter <[email-masked]>
Date: Sun, 28 Nov 2021 21:39:46 -0800

To help move things along, I've drafted an initial version of a proposal 
describing possible behaviors of `server-stop-automatically' at a high 
level. Hopefully that will make it easier to discuss how each setting 
should work, and we can refine the proposal until we're happy with it. 
Then any implementation changes should "just" be a matter of following 
the final proposal.

I welcome any comments or suggested additions to this proposal. I 
covered things how I see them, but I may not have provided enough detail 
for everyone to understand all the nuances, and other people might have 
their own use cases they'd like to see addressed.

I can also post this proposal to emacs-devel if people think it would be 
better to get a wider range of feedback on it. Once we can get some 
level of consensus on the proposal, then hopefully we'll know the right 
thing to do about this bug.

--------------------

Automatically Shutting Down the Emacs Daemon
Revision 1

* Introduction

Since Emacs 23, `emacsclient' has had the ability to automatically start 
an Emacs daemon by passing `--alternate-editor=""' (or setting the 
ALTERNATE_EDITOR environment variable to the empty string). In Emacs 29, 
the ability to automatically shut down the daemon was added for 
symmetry. This allows users who prefer to start the Emacs daemon on an 
as-needed basis to configure the daemon to stop when finished. However, 
"finished" is a vague term and doesn't precisely describe the conditions 
that would cause the Emacs daemon to be shut down.

Note: for the discussion below, "clients" refers to both "true clients" 
(members of `server-clients') and "pseudo-clients" (each frame with the 
`client' parameter set to `nowait'). This is essentially the logic used 
by `server-save-buffers-kill-terminal' since Emacs 23.

* Proposed Behaviors

I propose two main ways of determining when the Emacs daemon should be 
shut down automatically. Each has different pros and cons, and so there 
are arguments for supporting one, both, or neither in Emacs going 
forward. However, I think each are distinct enough that supporting both 
would be reasonable.

** Implicit Shutdown

For this behavior, the Emacs daemon will be silently stopped once 
nothing would be lost by stopping it. In particular, this means that the 
Emacs daemon should continue so long as there's at least one a) active 
client, b) unsaved file, or c) running subprocess (with non-nil 
`process-query-on-exit-flag'). If none of these exist, then the Emacs 
daemon will be stopped.

One open question is how this should interact with options that control 
how `save-buffers-kill-emacs' prompts the user. If 
`confirm-kill-processes' is nil, should the Emacs daemon then be stopped 
even if there are active subprocesses?

*** Pros

A benefit of this behavior is that because the shutdown occurs 
implicitly, there's no change to how users interact with Emacs. For 
example, `save-buffers-kill-terminal' will prompt the user to save only 
those files passed to `emacsclient'.

*** Cons

However, the behavior's benefit has another side to it: it can be 
difficult for a user to predict whether the Emacs daemon will be 
stopped. For example, a stray unsaved file that the user forgot about 
could be the difference between the daemon continuing to run or being 
stopped.

Furthermore, since an Emacs daemon with no clients is out of sight, it 
may be some time before the user realizes their mistake, leading to 
confusion about why some other program appears to be reading an 
out-of-date version of the file. That said, this aspect is equally a 
concern when *not* automatically stopping the Emacs daemon at all.

** Explicit Shutdown

(Note: this is the behavior I personally prefer.) In this case, Emacs 
will be stopped as soon as there are no active clients, prompting the 
user to save their work as needed. In other words, when a client is 
about to be deleted for any reason (calling 
`server-save-buffers-kill-terminal', deleting the last frame associated 
with the client, etc), then:

- If this is not the last client, behave as in Emacs 28; that is, prompt 
to save any files passed to `emacsclient' before deleting the client.

- If this is the last client, prompt the user to save all their work 
before killing Emacs entirely; that is, call `save-buffers-kill-emacs'. 
In this case, the behavior should be identical (or as close as possible) 
to killing a "regular", non-server Emacs in a similar state.

One open question is: should Emacs warn the user that it will the daemon 
when deleting the last client? Currently it does so in 
`server-kill-emacs-query-function', asking, "This Emacs session has 
clients; exit anyway?" It may be useful to keep this warning, possibly 
rewording it in this case to explain the situation better. It's also 
possible that it's just extra noise and should be eliminate in this 
case. (It may even make sense to provide a separate configuration option.)

*** Pros

One of the main goals with Explicit Shutdown is to resolve a concern 
mentioned above: an Emacs daemon with no clients is out of sight, so 
it's easy to forget about it. If the daemon is holding onto unsaved 
work, it may take a while until the user realizes this. By prompting to 
save *all* the user's work before closing the last client, it's much 
harder to make this mistake.

*** Cons

This behavior makes it more difficult for a user to know ahead of time 
whether `save-buffers-kill-terminal' (bound by default to `C-x C-c') 
will kill the client or Emacs entirely. The prompt mentioned above in 
`server-kill-emacs-query-function' does let the user bail out though, if 
they didn't mean to kill Emacs entirely.

However, `save-buffers-kill-terminal' already has this complexity 
(almost), since it will kill Emacs entirely when executed from a 
non-client frame. That said, it's possible to tell what will happen in 
this case by looking at the mode line: if it starts with something like 
"U:@---", it's a client frame and thus an observant user will know what 
`save-buffers-kill-terminal' will do.

* Delayed Shutdown

It may also be worth considering whether the Emacs daemon should be 
stopped immediately when the conditions are met, or whether it would be 
better to delay it by a short time. Some operations, such as `git rebase 
-i', can open the $EDITOR, close it, and then reopen it in rapid 
succession. In a case like that, it would be more efficient if the Emacs 
daemon weren't stopped right away.

That said, I don't think this is a critical problem, and believe it 
would be ok to decide what to do about this later.

* Current Behaviors

Currently, Emacs 29 supports Implicit Shutdown (called `empty') and has 
two slightly-different variations on Explicit Shutdown (called 
`kill-terminal' and `delete-frame'). `empty' is implemented as a timer 
that periodically calls `server-stop-automatically--maybe-kill-emacs' to 
check if there's any need to keep the daemon running (non-daemon frames, 
unsaved files, or running processes); if not, it stops the daemon. This 
differs very slightly from the proposed spec above, since it checks for 
frames, not active clients. Some clients (e.g. `emacsclient --eval FOO') 
don't create any frames, so it may be useful to enhance the current 
implementation of Implicit Shutdown to account for this.

`kill-terminal' is implemented in 
`server-stop-automatically--handle-delete-frame' (called by 
`server-save-buffers-kill-terminal'). It first deletes all frames 
associated with the current client *except* the current one. Then, if 
there are any non-daemon frames aside from the current one, it just 
deletes that frame; if it's the last non-daemon frame, it calls 
`save-buffers-kill-emacs'.

`delete-frame' works like `kill-terminal' above, but will also call 
`save-buffers-kill-emacs' when closing the last non-daemon frame using 
other means, such as clicking the "X" in the frame's title bar on a GUI 
system.

As mentioned above, `kill-terminal' and `delete-frame' work similarly to 
the Explicit Shutdown behavior, but there are some differences. For 
example, when killing a non-last client, `kill-terminal' and 
`delete-frame' don't prompt to save files passed to `emacsclient'. When 
killing the last client, they delete all the non-current frames before 
calling `save-buffers-kill-emacs', meaning that pressing `C-g' to cancel 
when prompted will still result in all but one frame going away.