all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* bug#59668: 29.0.50; [PATCH] Make 'server-stop-automatically' into a defcustom
@ 2022-11-29  4:23 Jim Porter
  2022-12-01 17:08 ` Eli Zaretskii
  0 siblings, 1 reply; 10+ messages in thread
From: Jim Porter @ 2022-11-29  4:23 UTC (permalink / raw)
  To: 59668

[-- Attachment #1: Type: text/plain, Size: 1318 bytes --]

The manual says:

   The Emacs server can optionally be stopped automatically when
   certain conditions are met.  To do this, call the function
   @code{server-stop-automatically} in your init file (@pxref{Init
   File}), with one of the following arguments...

It'd be nice if this were a defcustom so that people who prefer the 
Customize interface could use that instead of editing their init files. 
Here's a patch for that.

I've tried to make sure this is as robust as possible, so that 
everything is always properly set, especially when adjusting the value 
in the Customize UI. It should even work correctly if a user stopped the 
server temporarily and restarted it (you might do this to clear out old 
clients, do some work, and then make the server available again). Maybe 
this last bit is a little paranoid, but it was minimal extra work 
compared to getting the Customize part working.

One question though: should this only go on the master branch, or should 
it go into the 29 branch? To me, it seems like it could go either way, 
though I think it'd be nice to make this easier for users in 29. I'll do 
whatever the maintainers think is best though.

If it goes on the master branch only, I'll add back the function form of 
'server-stop-automatically' for compatibility, and then mark it obsolete.

[-- Attachment #2: 0001-Make-server-stop-automatically-into-a-defcustom.patch --]
[-- Type: text/plain, Size: 9042 bytes --]

From d1273ad3170514bc28460b32886a6b4d7615742b Mon Sep 17 00:00:00 2001
From: Jim Porter <jporterbugs@gmail.com>
Date: Sun, 27 Nov 2022 22:21:10 -0800
Subject: [PATCH] Make 'server-stop-automatically' into a defcustom

* lisp/server.el (server-stop-automatically): Convert the variable to
a defcustom.  Remove the function.
(server-stop-automatically--timer): New variable.
(server-apply-stop-automatically): New function.
(server-stop, server-start): Call 'server-apply-stop-automatically'.
(server-save-buffers-kill-terminal): Adjust
'server-stop-automatically' conditional.
(server-stop-automatically--handle-delete-frame)
(server-stop-automatically--maybe-kill-emacs): Update docstrings.

* doc/emacs/misc.texi (Emacs Server): Update documentation.
---
 doc/emacs/misc.texi | 13 +++----
 lisp/server.el      | 89 +++++++++++++++++++++++++++++----------------
 2 files changed, 63 insertions(+), 39 deletions(-)

diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index 29c0bed19c..e3c013acb1 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -1808,26 +1808,25 @@ Emacs Server
   emacs --daemon=foo
 @end example
 
-@findex server-stop-automatically
+@vindex server-stop-automatically
   The Emacs server can optionally be stopped automatically when
-certain conditions are met.  To do this, call the function
-@code{server-stop-automatically} in your init file (@pxref{Init
-File}), with one of the following arguments:
+certain conditions are met.  To do this, set the option
+@code{server-stop-automatically} to one of the following values:
 
 @itemize
 @item
-With the argument @code{empty}, the server is stopped when it has no
+With the value @code{empty}, the server is stopped when it has no
 clients, no unsaved file-visiting buffers and no running processes
 anymore.
 
 @item
-With the argument @code{delete-frame}, when the last client frame is
+With the value @code{delete-frame}, when the last client frame is
 being closed, you are asked whether each unsaved file-visiting buffer
 must be saved and each unfinished process can be stopped, and if so,
 the server is stopped.
 
 @item
-With the argument @code{kill-terminal}, when the last client frame is
+With the value @code{kill-terminal}, when the last client frame is
 being closed with @kbd{C-x C-c} (@code{save-buffers-kill-terminal}),
 you are asked whether each unsaved file-visiting buffer must be saved
 and each unfinished process can be stopped, and if so, the server is
diff --git a/lisp/server.el b/lisp/server.el
index 1b027f88ce..dac07dfa35 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -273,6 +273,11 @@ server-client-instructions
   :version "28.1"
   :type 'boolean)
 
+(defvar server-stop-automatically)      ; Defined below to avoid recursive load.
+
+(defvar server-stop-automatically--timer nil
+  "The timer object for `server-stop-automatically--maybe-kill-emacs'.")
+
 ;; We do not use `temporary-file-directory' here, because emacsclient
 ;; does not read the init file.
 (defvar server-socket-dir
@@ -636,7 +641,8 @@ server-stop
       (setq stopped-p t
             server-process nil
             server-mode nil
-            global-minor-modes (delq 'server-mode global-minor-modes)))
+            global-minor-modes (delq 'server-mode global-minor-modes))
+      (server-apply-stop-automatically))
     (unwind-protect
         ;; Delete the socket files made by previous server
         ;; invocations.
@@ -757,6 +763,7 @@ server-start
 			 (list :family 'local
 			       :service server-file
 			       :plist '(:authenticated t)))))
+          (server-apply-stop-automatically)
 	  (unless server-process (error "Could not start server process"))
           (server-log "Started server")
 	  (process-put server-process :server-file server-file)
@@ -1769,9 +1776,6 @@ server-switch-buffer
     (when server-raise-frame
       (select-frame-set-input-focus (window-frame)))))
 
-(defvar server-stop-automatically nil
-  "Internal status variable for `server-stop-automatically'.")
-
 ;;;###autoload
 (defun server-save-buffers-kill-terminal (arg)
   ;; Called from save-buffers-kill-terminal in files.el.
@@ -1780,7 +1784,8 @@ server-save-buffers-kill-terminal
 
 If emacsclient was started with a list of filenames to edit, then
 only these files will be asked to be saved."
-  (if server-stop-automatically
+  (if (and (daemonp)
+           (memq server-stop-automatically '(kill-terminal delete-frame))
       (server-stop-automatically--handle-delete-frame (selected-frame))
     (let ((proc (frame-parameter nil 'client)))
       (cond ((eq proc 'nowait)
@@ -1805,7 +1810,7 @@ server-save-buffers-kill-terminal
 	    (t (error "Invalid client frame"))))))
 
 (defun server-stop-automatically--handle-delete-frame (frame)
-  "Handle deletion of FRAME when `server-stop-automatically' is used."
+  "Handle deletion of FRAME when `server-stop-automatically' is `delete-frame'."
   (when server-stop-automatically
     (if (if (and (processp (frame-parameter frame 'client))
 		 (eq this-command 'save-buffers-kill-terminal))
@@ -1828,7 +1833,7 @@ server-stop-automatically--handle-delete-frame
 	  (delete-frame frame)))))
 
 (defun server-stop-automatically--maybe-kill-emacs ()
-  "Handle closing of Emacs daemon when `server-stop-automatically' is used."
+  "Handle closing of Emacs daemon when `server-stop-automatically' is `empty'."
   (unless (cdr (frame-list))
     (when (and
 	   (not (memq t (mapcar (lambda (b)
@@ -1842,41 +1847,61 @@ server-stop-automatically--maybe-kill-emacs
 				(process-list)))))
       (kill-emacs))))
 
-;;;###autoload
-(defun server-stop-automatically (arg)
-  "Automatically stop server as specified by ARG.
-
-If ARG is the symbol `empty', stop the server when it has no
+(defun server-apply-stop-automatically ()
+  "Apply the current value of `server-stop-automatically'.
+This function adds or removes the necessary helpers to manage
+stopping the Emacs server automatically, depending on the whether
+the server is running or not.  This function only applies when
+running Emacs as a daemon."
+  (when (daemonp)
+    (let (empty-timer-p delete-frame-p)
+      (when server-process
+        (pcase server-stop-automatically
+          ('empty        (setq empty-timer-p t))
+          ('delete-frame (setq delete-frame-p t))))
+      ;; Start or stop the timer.
+      (if empty-timer-p
+          (unless server-stop-automatically--timer
+            (setq server-stop-automatically--timer
+                  (run-with-timer
+                   10 2
+		   #'server-stop-automatically--maybe-kill-emacs)))
+        (when server-stop-automatically--timer
+          (cancel-timer server-stop-automatically--timer)
+          (setq server-stop-automatically--timer nil)))
+      ;; Add or remove the delete-frame hook.
+      (if delete-frame-p
+          (add-hook 'delete-frame-functions
+		    #'server-stop-automatically--handle-delete-frame)
+        (remove-hook 'delete-frame-functions
+                     #'server-stop-automatically--handle-delete-frame)))))
+
+(defcustom server-stop-automatically nil
+  "If non-nil, stop the server under the requested conditions.
+
+If this is the symbol `empty', stop the server when it has no
 remaining clients, no remaining unsaved file-visiting buffers,
 and no running processes with a `query-on-exit' flag.
 
-If ARG is the symbol `delete-frame', ask the user when the last
+If this is the symbol `delete-frame', ask the user when the last
 frame is deleted whether each unsaved file-visiting buffer must
 be saved and each running process with a `query-on-exit' flag
 can be stopped, and if so, stop the server itself.
 
-If ARG is the symbol `kill-terminal', ask the user when the
+If this is the symbol `kill-terminal', ask the user when the
 terminal is killed with \\[save-buffers-kill-terminal] \
 whether each unsaved file-visiting
 buffer must be saved and each running process with a `query-on-exit'
-flag can be stopped, and if so, stop the server itself.
-
-Any other value of ARG will cause this function to signal an error.
-
-This function is meant to be called from the user init file."
-  (when (daemonp)
-    (setq server-stop-automatically arg)
-    (cond
-     ((eq arg 'empty)
-      (setq server-stop-automatically nil)
-      (run-with-timer 10 2
-		      #'server-stop-automatically--maybe-kill-emacs))
-     ((eq arg 'delete-frame)
-      (add-hook 'delete-frame-functions
-		#'server-stop-automatically--handle-delete-frame))
-     ((eq arg 'kill-terminal))
-     (t
-      (error "Unexpected argument")))))
+flag can be stopped, and if so, stop the server itself."
+  :type '(choice
+          (const :tag "Never" nil)
+          (const :tag "When empty" empty)
+          (const :tag "When killing last terminal" kill-terminal)
+          (const :tag "When killing last terminal or frame" delete-frame))
+  :set (lambda (symbol value)
+         (set-default symbol value)
+         (server-apply-stop-automatically))
+  :version "29.1")
 
 (define-key ctl-x-map "#" 'server-edit)
 
-- 
2.25.1


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

* bug#59668: 29.0.50; [PATCH] Make 'server-stop-automatically' into a defcustom
  2022-11-29  4:23 bug#59668: 29.0.50; [PATCH] Make 'server-stop-automatically' into a defcustom Jim Porter
@ 2022-12-01 17:08 ` Eli Zaretskii
  2022-12-01 18:33   ` Jim Porter
  2022-12-02  3:30   ` Jim Porter
  0 siblings, 2 replies; 10+ messages in thread
From: Eli Zaretskii @ 2022-12-01 17:08 UTC (permalink / raw)
  To: Jim Porter; +Cc: 59668

> Date: Mon, 28 Nov 2022 20:23:17 -0800
> From: Jim Porter <jporterbugs@gmail.com>
> 
>    The Emacs server can optionally be stopped automatically when
>    certain conditions are met.  To do this, call the function
>    @code{server-stop-automatically} in your init file (@pxref{Init
>    File}), with one of the following arguments...
> 
> It'd be nice if this were a defcustom so that people who prefer the 
> Customize interface could use that instead of editing their init files. 
> Here's a patch for that.

Thanks.

> One question though: should this only go on the master branch, or should 
> it go into the 29 branch? To me, it seems like it could go either way, 
> though I think it'd be nice to make this easier for users in 29. I'll do 
> whatever the maintainers think is best though.

Let's not start installing new features on the release branch.  We have
enough new stuff there already.  Besides, the changes you suggest are hardly
trivial, and saying just "make a function into a defcustom" doesn't come
close to describing it, IMO.

So please install on master, once you take care of the comments below.

> If it goes on the master branch only, I'll add back the function form of 
> 'server-stop-automatically' for compatibility, and then mark it obsolete.

I see no reason to make the function obsolete.  It does not harm to have
both a variable and a function by the same name.

> -@findex server-stop-automatically
> +@vindex server-stop-automatically
>    The Emacs server can optionally be stopped automatically when
> -certain conditions are met.  To do this, call the function
> -@code{server-stop-automatically} in your init file (@pxref{Init
> -File}), with one of the following arguments:
> +certain conditions are met.  To do this, set the option
> +@code{server-stop-automatically} to one of the following values:
>  
>  @itemize
>  @item
> -With the argument @code{empty}, the server is stopped when it has no
> +With the value @code{empty}, the server is stopped when it has no
>  clients, no unsaved file-visiting buffers and no running processes
>  anymore.
>  
>  @item
> -With the argument @code{delete-frame}, when the last client frame is
> +With the value @code{delete-frame}, when the last client frame is
>  being closed, you are asked whether each unsaved file-visiting buffer
>  must be saved and each unfinished process can be stopped, and if so,
>  the server is stopped.

This @itemize list should be converted to a @table, formatted like this:

  @item empty
  This value caused the server to be stopped when...

  @item delete-frame
  This value means that when the last client frame is deleted...

etc., I guess you get the idea.

> @@ -1780,7 +1784,8 @@ server-save-buffers-kill-terminal
>  
>  If emacsclient was started with a list of filenames to edit, then
>  only these files will be asked to be saved."
> -  (if server-stop-automatically
> +  (if (and (daemonp)
> +           (memq server-stop-automatically '(kill-terminal delete-frame))

Why is this needed?  I guess I don't understand why non-trivial code changes
are in a patch that was supposed to just add a defcustom?

> +(defun server-apply-stop-automatically ()
> +  "Apply the current value of `server-stop-automatically'.
> +This function adds or removes the necessary helpers to manage
> +stopping the Emacs server automatically, depending on the whether
> +the server is running or not.  This function only applies when
> +running Emacs as a daemon."

And why this significant refactoring of the original function? was there
something wrong with it?





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

* bug#59668: 29.0.50; [PATCH] Make 'server-stop-automatically' into a defcustom
  2022-12-01 17:08 ` Eli Zaretskii
@ 2022-12-01 18:33   ` Jim Porter
  2022-12-01 18:41     ` Jim Porter
  2022-12-02  3:30   ` Jim Porter
  1 sibling, 1 reply; 10+ messages in thread
From: Jim Porter @ 2022-12-01 18:33 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 59668

On 12/1/2022 9:08 AM, Eli Zaretskii wrote:
>> Date: Mon, 28 Nov 2022 20:23:17 -0800
>> From: Jim Porter <jporterbugs@gmail.com>
>>
>> One question though: should this only go on the master branch, or should
>> it go into the 29 branch? To me, it seems like it could go either way,
>> though I think it'd be nice to make this easier for users in 29. I'll do
>> whatever the maintainers think is best though.
> 
> Let's not start installing new features on the release branch. ...
Sounds good to me.

>> If it goes on the master branch only, I'll add back the function form of
>> 'server-stop-automatically' for compatibility, and then mark it obsolete.
> 
> I see no reason to make the function obsolete.  It does not harm to have
> both a variable and a function by the same name.

Fine by me. The function will just be a one-liner anyway: (setopt 
server-stop-automatically arg).

> This @itemize list should be converted to a @table, formatted like this:
> 
>    @item empty
>    This value caused the server to be stopped when...
> 
>    @item delete-frame
>    This value means that when the last client frame is deleted...
> 
> etc., I guess you get the idea.

Will do. I made the minimal set of changes to the manual, but while I'm 
here, I agree that it would be good to improve things further.

>> @@ -1780,7 +1784,8 @@ server-save-buffers-kill-terminal
>>   
>>   If emacsclient was started with a list of filenames to edit, then
>>   only these files will be asked to be saved."
>> -  (if server-stop-automatically
>> +  (if (and (daemonp)
>> +           (memq server-stop-automatically '(kill-terminal delete-frame))
> 
> Why is this needed?  I guess I don't understand why non-trivial code changes
> are in a patch that was supposed to just add a defcustom?

It's due to a change in the meaning of the 'server-stop-automatically' 
value. Previously, it was an internal variable that was set to nil after 
calling "(server-stop-automatically 'empty)", or when calling 
'server-stop-automatically' in a non-daemon session. Since 
'server-stop-automatically' is now a defcustom, that means that a) it 
can really be set to 'empty', and b) it can be non-nil in non-daemon 
sessions. So this extra code is just to account for the change in meaning.

I could make a helper function that returns the same value as the *old* 
version of the 'server-stop-automatically' variable; either way has the 
same meaning. I could also keep the old variable around, possibly 
renamed to something like 'server-stop-automatically--kill-terminal', 
but I think a helper function would be cleaner.

>> +(defun server-apply-stop-automatically ()
>> +  "Apply the current value of `server-stop-automatically'.
>> +This function adds or removes the necessary helpers to manage
>> +stopping the Emacs server automatically, depending on the whether
>> +the server is running or not.  This function only applies when
>> +running Emacs as a daemon."
> 
> And why this significant refactoring of the original function? was there
> something wrong with it?

The previous implementation was limited in that you could call it once, 
but you couldn't necessarily call it a second time to change the 
setting. For example, this would put you in an inconsistent state:

   (server-stop-automatically 'delete-frame)
   (server-stop-automatically 'empty)

After this, the 'empty' setting would be enabled, and the 'delete-frame' 
setting would still be partially-enabled too.

That wasn't so much of a problem when you'd just call this function only 
once in your init file, but for the Customize interface, I think it's 
important to make sure that users can set the defcustom multiple times, 
e.g. if they change their minds while customizing it. The changes for 
'server-apply-stop-automatically' are to support that.

As you said, these changes are more extensive than "just" adding a 
defcustom, but all the other changes are there to support the Customize 
interface.





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

* bug#59668: 29.0.50; [PATCH] Make 'server-stop-automatically' into a defcustom
  2022-12-01 18:33   ` Jim Porter
@ 2022-12-01 18:41     ` Jim Porter
  0 siblings, 0 replies; 10+ messages in thread
From: Jim Porter @ 2022-12-01 18:41 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 59668

On 12/1/2022 10:33 AM, Jim Porter wrote:
> I could make a helper function that returns the same value as the *old* 
> version of the 'server-stop-automatically' variable; either way has the 
> same meaning.

Oops, the bit after the semicolon should be "this would do the same 
thing as the current conditional."






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

* bug#59668: 29.0.50; [PATCH] Make 'server-stop-automatically' into a defcustom
  2022-12-01 17:08 ` Eli Zaretskii
  2022-12-01 18:33   ` Jim Porter
@ 2022-12-02  3:30   ` Jim Porter
  2022-12-02 14:42     ` Eli Zaretskii
  1 sibling, 1 reply; 10+ messages in thread
From: Jim Porter @ 2022-12-02  3:30 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 59668

[-- Attachment #1: Type: text/plain, Size: 1077 bytes --]

On 12/1/2022 9:08 AM, Eli Zaretskii wrote:
> I see no reason to make the function obsolete.  It does not harm to have
> both a variable and a function by the same name.

Ok, fixed. I've added back a 'server-stop-automatically' function that 
just sets the variable.

> This @itemize list should be converted to a @table, formatted like this:
> 
>    @item empty
>    This value caused the server to be stopped when...
> 
>    @item delete-frame
>    This value means that when the last client frame is deleted...

Done.

>> @@ -1780,7 +1784,8 @@ server-save-buffers-kill-terminal
>>   
>>   If emacsclient was started with a list of filenames to edit, then
>>   only these files will be asked to be saved."
>> -  (if server-stop-automatically
>> +  (if (and (daemonp)
>> +           (memq server-stop-automatically '(kill-terminal delete-frame))
> 
> Why is this needed?  I guess I don't understand why non-trivial code changes
> are in a patch that was supposed to just add a defcustom?

Addressed in my other message, but I've added a defsubst to make this 
clearer (I hope).

[-- Attachment #2: 0001-Make-server-stop-automatically-into-a-defcustom.patch --]
[-- Type: text/plain, Size: 10111 bytes --]

From c0f245bffa7243ad14118e1b6145c7b0e0b3cd3f Mon Sep 17 00:00:00 2001
From: Jim Porter <jporterbugs@gmail.com>
Date: Sun, 27 Nov 2022 22:21:10 -0800
Subject: [PATCH] Make 'server-stop-automatically' into a defcustom

* lisp/server.el (server-stop-automatically): Convert the variable to
a defcustom, and make the function simply set the defcustom.
(server-stop-automatically--timer): New variable.
(server-stop-automatically--when-killing-terminal):
(server-apply-stop-automatically): New functions.
(server-stop, server-start): Call 'server-apply-stop-automatically'.
(server-save-buffers-kill-terminal): Adjust
'server-stop-automatically' conditional.
(server-stop-automatically--handle-delete-frame)
(server-stop-automatically--maybe-kill-emacs): Update docstrings.

* doc/emacs/misc.texi (Emacs Server): Update documentation.
---
 doc/emacs/misc.texi | 41 +++++++++----------
 lisp/server.el      | 96 ++++++++++++++++++++++++++++++---------------
 2 files changed, 84 insertions(+), 53 deletions(-)

diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index 29c0bed19c..702c72bac2 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -1808,31 +1808,28 @@ Emacs Server
   emacs --daemon=foo
 @end example
 
-@findex server-stop-automatically
+@vindex server-stop-automatically
   The Emacs server can optionally be stopped automatically when
-certain conditions are met.  To do this, call the function
-@code{server-stop-automatically} in your init file (@pxref{Init
-File}), with one of the following arguments:
+certain conditions are met.  To do this, set the option
+@code{server-stop-automatically} to one of the following values:
 
-@itemize
-@item
-With the argument @code{empty}, the server is stopped when it has no
-clients, no unsaved file-visiting buffers and no running processes
-anymore.
-
-@item
-With the argument @code{delete-frame}, when the last client frame is
-being closed, you are asked whether each unsaved file-visiting buffer
-must be saved and each unfinished process can be stopped, and if so,
-the server is stopped.
-
-@item
-With the argument @code{kill-terminal}, when the last client frame is
-being closed with @kbd{C-x C-c} (@code{save-buffers-kill-terminal}),
-you are asked whether each unsaved file-visiting buffer must be saved
-and each unfinished process can be stopped, and if so, the server is
+@table @code
+@item empty
+This value causes the server to be stopped when it has no clients, no
+unsaved file-visiting buffers and no running processes anymore.
+
+@item delete-frame
+This value means that when the last client frame is being closed, you
+are asked whether each unsaved file-visiting buffer must be saved and
+each unfinished process can be stopped, and if so, the server is
 stopped.
-@end itemize
+
+@item kill-terminal
+This value means that when the last client frame is being closed with
+@kbd{C-x C-c} (@code{save-buffers-kill-terminal}), you are asked
+whether each unsaved file-visiting buffer must be saved and each
+unfinished process can be stopped, and if so, the server is stopped.
+@end table
 
 @findex server-eval-at
   If you have defined a server by a unique server name, it is possible
diff --git a/lisp/server.el b/lisp/server.el
index 1b027f88ce..e16a9e464e 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -273,6 +273,11 @@ server-client-instructions
   :version "28.1"
   :type 'boolean)
 
+(defvar server-stop-automatically)      ; Defined below to avoid recursive load.
+
+(defvar server-stop-automatically--timer nil
+  "The timer object for `server-stop-automatically--maybe-kill-emacs'.")
+
 ;; We do not use `temporary-file-directory' here, because emacsclient
 ;; does not read the init file.
 (defvar server-socket-dir
@@ -636,7 +641,8 @@ server-stop
       (setq stopped-p t
             server-process nil
             server-mode nil
-            global-minor-modes (delq 'server-mode global-minor-modes)))
+            global-minor-modes (delq 'server-mode global-minor-modes))
+      (server-apply-stop-automatically))
     (unwind-protect
         ;; Delete the socket files made by previous server
         ;; invocations.
@@ -757,6 +763,7 @@ server-start
 			 (list :family 'local
 			       :service server-file
 			       :plist '(:authenticated t)))))
+          (server-apply-stop-automatically)
 	  (unless server-process (error "Could not start server process"))
           (server-log "Started server")
 	  (process-put server-process :server-file server-file)
@@ -1769,8 +1776,10 @@ server-switch-buffer
     (when server-raise-frame
       (select-frame-set-input-focus (window-frame)))))
 
-(defvar server-stop-automatically nil
-  "Internal status variable for `server-stop-automatically'.")
+(defsubst server-stop-automatically--when-killing-terminal ()
+  "Return non-nil if we should potentially stop Emacs when killing a terminal."
+  (and (daemonp)
+       (memq server-stop-automatically '(kill-terminal delete-frame))))
 
 ;;;###autoload
 (defun server-save-buffers-kill-terminal (arg)
@@ -1780,7 +1789,7 @@ server-save-buffers-kill-terminal
 
 If emacsclient was started with a list of filenames to edit, then
 only these files will be asked to be saved."
-  (if server-stop-automatically
+  (if (server-stop-automatically--when-killing-terminal)
       (server-stop-automatically--handle-delete-frame (selected-frame))
     (let ((proc (frame-parameter nil 'client)))
       (cond ((eq proc 'nowait)
@@ -1805,7 +1814,7 @@ server-save-buffers-kill-terminal
 	    (t (error "Invalid client frame"))))))
 
 (defun server-stop-automatically--handle-delete-frame (frame)
-  "Handle deletion of FRAME when `server-stop-automatically' is used."
+  "Handle deletion of FRAME when `server-stop-automatically' is `delete-frame'."
   (when server-stop-automatically
     (if (if (and (processp (frame-parameter frame 'client))
 		 (eq this-command 'save-buffers-kill-terminal))
@@ -1828,7 +1837,7 @@ server-stop-automatically--handle-delete-frame
 	  (delete-frame frame)))))
 
 (defun server-stop-automatically--maybe-kill-emacs ()
-  "Handle closing of Emacs daemon when `server-stop-automatically' is used."
+  "Handle closing of Emacs daemon when `server-stop-automatically' is `empty'."
   (unless (cdr (frame-list))
     (when (and
 	   (not (memq t (mapcar (lambda (b)
@@ -1842,41 +1851,66 @@ server-stop-automatically--maybe-kill-emacs
 				(process-list)))))
       (kill-emacs))))
 
-;;;###autoload
-(defun server-stop-automatically (arg)
-  "Automatically stop server as specified by ARG.
-
-If ARG is the symbol `empty', stop the server when it has no
+(defun server-apply-stop-automatically ()
+  "Apply the current value of `server-stop-automatically'.
+This function adds or removes the necessary helpers to manage
+stopping the Emacs server automatically, depending on the whether
+the server is running or not.  This function only applies when
+running Emacs as a daemon."
+  (when (daemonp)
+    (let (empty-timer-p delete-frame-p)
+      (when server-process
+        (pcase server-stop-automatically
+          ('empty        (setq empty-timer-p t))
+          ('delete-frame (setq delete-frame-p t))))
+      ;; Start or stop the timer.
+      (if empty-timer-p
+          (unless server-stop-automatically--timer
+            (setq server-stop-automatically--timer
+                  (run-with-timer
+                   10 2
+		   #'server-stop-automatically--maybe-kill-emacs)))
+        (when server-stop-automatically--timer
+          (cancel-timer server-stop-automatically--timer)
+          (setq server-stop-automatically--timer nil)))
+      ;; Add or remove the delete-frame hook.
+      (if delete-frame-p
+          (add-hook 'delete-frame-functions
+		    #'server-stop-automatically--handle-delete-frame)
+        (remove-hook 'delete-frame-functions
+                     #'server-stop-automatically--handle-delete-frame)))))
+
+(defcustom server-stop-automatically nil
+  "If non-nil, stop the server under the requested conditions.
+
+If this is the symbol `empty', stop the server when it has no
 remaining clients, no remaining unsaved file-visiting buffers,
 and no running processes with a `query-on-exit' flag.
 
-If ARG is the symbol `delete-frame', ask the user when the last
+If this is the symbol `delete-frame', ask the user when the last
 frame is deleted whether each unsaved file-visiting buffer must
 be saved and each running process with a `query-on-exit' flag
 can be stopped, and if so, stop the server itself.
 
-If ARG is the symbol `kill-terminal', ask the user when the
+If this is the symbol `kill-terminal', ask the user when the
 terminal is killed with \\[save-buffers-kill-terminal] \
 whether each unsaved file-visiting
 buffer must be saved and each running process with a `query-on-exit'
-flag can be stopped, and if so, stop the server itself.
-
-Any other value of ARG will cause this function to signal an error.
-
-This function is meant to be called from the user init file."
-  (when (daemonp)
-    (setq server-stop-automatically arg)
-    (cond
-     ((eq arg 'empty)
-      (setq server-stop-automatically nil)
-      (run-with-timer 10 2
-		      #'server-stop-automatically--maybe-kill-emacs))
-     ((eq arg 'delete-frame)
-      (add-hook 'delete-frame-functions
-		#'server-stop-automatically--handle-delete-frame))
-     ((eq arg 'kill-terminal))
-     (t
-      (error "Unexpected argument")))))
+flag can be stopped, and if so, stop the server itself."
+  :type '(choice
+          (const :tag "Never" nil)
+          (const :tag "When empty" empty)
+          (const :tag "When killing last terminal" kill-terminal)
+          (const :tag "When killing last terminal or frame" delete-frame))
+  :set (lambda (symbol value)
+         (set-default symbol value)
+         (server-apply-stop-automatically))
+  :version "29.1")
+
+(defun server-stop-automatically (value)
+  "Automatically stop the Emacs server as specified by VALUE.
+This sets the variable `server-stop-automatically' (which see)."
+  (setopt server-stop-automatically value))
 
 (define-key ctl-x-map "#" 'server-edit)
 
-- 
2.25.1


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

* bug#59668: 29.0.50; [PATCH] Make 'server-stop-automatically' into a defcustom
  2022-12-02  3:30   ` Jim Porter
@ 2022-12-02 14:42     ` Eli Zaretskii
  2022-12-07  1:39       ` Jim Porter
  0 siblings, 1 reply; 10+ messages in thread
From: Eli Zaretskii @ 2022-12-02 14:42 UTC (permalink / raw)
  To: Jim Porter; +Cc: 59668

> Date: Thu, 1 Dec 2022 19:30:03 -0800
> Cc: 59668@debbugs.gnu.org
> From: Jim Porter <jporterbugs@gmail.com>
> 
> @@ -1805,7 +1814,7 @@ server-save-buffers-kill-terminal
>  	    (t (error "Invalid client frame"))))))
>  
>  (defun server-stop-automatically--handle-delete-frame (frame)
> -  "Handle deletion of FRAME when `server-stop-automatically' is used."
> +  "Handle deletion of FRAME when `server-stop-automatically' is `delete-frame'."
>    (when server-stop-automatically
>      (if (if (and (processp (frame-parameter frame 'client))
>  		 (eq this-command 'save-buffers-kill-terminal))
> @@ -1828,7 +1837,7 @@ server-stop-automatically--handle-delete-frame
>  	  (delete-frame frame)))))

AFAIU, this delete-frame is called after save-buffers-kill-emacs, which is
strange: there will be no Emacs to perform this call after that.  What am I
missing?
> +          (const :tag "When empty" empty)

"When empty" doesn't explain itself well enough.  Can we come up with a
better description?

Otherwise LGTM, thanks.





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

* bug#59668: 29.0.50; [PATCH] Make 'server-stop-automatically' into a defcustom
  2022-12-02 14:42     ` Eli Zaretskii
@ 2022-12-07  1:39       ` Jim Porter
  2022-12-07  1:49         ` Jim Porter
  2022-12-07 12:48         ` Eli Zaretskii
  0 siblings, 2 replies; 10+ messages in thread
From: Jim Porter @ 2022-12-07  1:39 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 59668

[-- Attachment #1: Type: text/plain, Size: 1498 bytes --]

On 12/2/2022 6:42 AM, Eli Zaretskii wrote:
>> Date: Thu, 1 Dec 2022 19:30:03 -0800
>> Cc: 59668@debbugs.gnu.org
>> From: Jim Porter <jporterbugs@gmail.com>
>>
>> @@ -1805,7 +1814,7 @@ server-save-buffers-kill-terminal
>>   	    (t (error "Invalid client frame"))))))
>>   
>>   (defun server-stop-automatically--handle-delete-frame (frame)
>> -  "Handle deletion of FRAME when `server-stop-automatically' is used."
>> +  "Handle deletion of FRAME when `server-stop-automatically' is `delete-frame'."
>>     (when server-stop-automatically
>>       (if (if (and (processp (frame-parameter frame 'client))
>>   		 (eq this-command 'save-buffers-kill-terminal))
>> @@ -1828,7 +1837,7 @@ server-stop-automatically--handle-delete-frame
>>   	  (delete-frame frame)))))
> 
> AFAIU, this delete-frame is called after save-buffers-kill-emacs, which is
> strange: there will be no Emacs to perform this call after that.  What am I
> missing?

Thanks. I think that's just an oversight. I removed it in my patch for 
bug#51993, since this seems more closely-related to that bug.

>> +          (const :tag "When empty" empty)
> 
> "When empty" doesn't explain itself well enough.  Can we come up with a
> better description?

How about "When empty (no clients, unsaved files, or processes)"? That 
seems a bit clumsy to me, but I wanted to keep the word "empty" in there 
somehow, since that's the name of the option value.

Attached is an updated patch (rebased on top of my previous patches for 
bug#51993).

[-- Attachment #2: 0001-Make-server-stop-automatically-into-a-defcustom.patch --]
[-- Type: text/plain, Size: 11341 bytes --]

From 7d9c9bbccf8fdf6851ac3cf5425c564d91919438 Mon Sep 17 00:00:00 2001
From: Jim Porter <jporterbugs@gmail.com>
Date: Sun, 27 Nov 2022 22:21:10 -0800
Subject: [PATCH] Make 'server-stop-automatically' into a defcustom

This changes the meaning of the (formerly internal) variable
'server-stop-automatically': it now always holds the requested
configuration, even when Emacs was not started as a daemon
(bug#59668).

* lisp/server.el (server-stop-automatically): Convert the variable to
a defcustom, and make the function simply set the defcustom.
(server-stop-automatically--timer): New variable.
(server-apply-stop-automatically): New function...
(server-stop, server-start): ... call it.
(server-save-buffers-kill-terminal): Adjust the conditions for
stopping automatically to account for the change of meaning for
'server-stop-automatically'.
(server-stop-automatically--handle-delete-frame): Remove unnecessary
test of the 'server-stop-automatically' option; this hook is only set
when it should do its job.
(server-stop-automatically--maybe-kill-emacs): Update docstring.

* doc/emacs/misc.texi (Emacs Server): Update documentation.
---
 doc/emacs/misc.texi |  41 ++++++++--------
 lisp/server.el      | 111 +++++++++++++++++++++++++++++---------------
 2 files changed, 93 insertions(+), 59 deletions(-)

diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index 29c0bed19c0..702c72bac25 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -1808,31 +1808,28 @@ Emacs Server
   emacs --daemon=foo
 @end example
 
-@findex server-stop-automatically
+@vindex server-stop-automatically
   The Emacs server can optionally be stopped automatically when
-certain conditions are met.  To do this, call the function
-@code{server-stop-automatically} in your init file (@pxref{Init
-File}), with one of the following arguments:
+certain conditions are met.  To do this, set the option
+@code{server-stop-automatically} to one of the following values:
 
-@itemize
-@item
-With the argument @code{empty}, the server is stopped when it has no
-clients, no unsaved file-visiting buffers and no running processes
-anymore.
-
-@item
-With the argument @code{delete-frame}, when the last client frame is
-being closed, you are asked whether each unsaved file-visiting buffer
-must be saved and each unfinished process can be stopped, and if so,
-the server is stopped.
-
-@item
-With the argument @code{kill-terminal}, when the last client frame is
-being closed with @kbd{C-x C-c} (@code{save-buffers-kill-terminal}),
-you are asked whether each unsaved file-visiting buffer must be saved
-and each unfinished process can be stopped, and if so, the server is
+@table @code
+@item empty
+This value causes the server to be stopped when it has no clients, no
+unsaved file-visiting buffers and no running processes anymore.
+
+@item delete-frame
+This value means that when the last client frame is being closed, you
+are asked whether each unsaved file-visiting buffer must be saved and
+each unfinished process can be stopped, and if so, the server is
 stopped.
-@end itemize
+
+@item kill-terminal
+This value means that when the last client frame is being closed with
+@kbd{C-x C-c} (@code{save-buffers-kill-terminal}), you are asked
+whether each unsaved file-visiting buffer must be saved and each
+unfinished process can be stopped, and if so, the server is stopped.
+@end table
 
 @findex server-eval-at
   If you have defined a server by a unique server name, it is possible
diff --git a/lisp/server.el b/lisp/server.el
index aa62b25a891..97265c1a68c 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -273,6 +273,11 @@ server-client-instructions
   :version "28.1"
   :type 'boolean)
 
+(defvar server-stop-automatically)      ; Defined below to avoid recursive load.
+
+(defvar server-stop-automatically--timer nil
+  "The timer object for `server-stop-automatically--maybe-kill-emacs'.")
+
 ;; We do not use `temporary-file-directory' here, because emacsclient
 ;; does not read the init file.
 (defvar server-socket-dir
@@ -636,7 +641,8 @@ server-stop
       (setq stopped-p t
             server-process nil
             server-mode nil
-            global-minor-modes (delq 'server-mode global-minor-modes)))
+            global-minor-modes (delq 'server-mode global-minor-modes))
+      (server-apply-stop-automatically))
     (unwind-protect
         ;; Delete the socket files made by previous server
         ;; invocations.
@@ -757,6 +763,7 @@ server-start
 			 (list :family 'local
 			       :service server-file
 			       :plist '(:authenticated t)))))
+          (server-apply-stop-automatically)
 	  (unless server-process (error "Could not start server process"))
           (server-log "Started server")
 	  (process-put server-process :server-file server-file)
@@ -1769,9 +1776,6 @@ server-switch-buffer
     (when server-raise-frame
       (select-frame-set-input-focus (window-frame)))))
 
-(defvar server-stop-automatically nil
-  "Internal status variable for `server-stop-automatically'.")
-
 ;;;###autoload
 (defun server-save-buffers-kill-terminal (arg)
   ;; Called from save-buffers-kill-terminal in files.el.
@@ -1779,11 +1783,19 @@ server-save-buffers-kill-terminal
 With ARG non-nil, silently save all file-visiting buffers, then kill.
 
 If emacsclient was started with a list of filenames to edit, then
-only these files will be asked to be saved."
-  (let ((proc (frame-parameter nil 'client)))
+only these files will be asked to be saved.
+
+When running Emacs as a daemon and with
+`server-stop-automatically' (which see) set to `kill-terminal' or
+`delete-frame', this function may call `save-buffers-kill-emacs'
+if there are no other active clients."
+  (let ((stop-automatically
+         (and (daemonp)
+              (memq server-stop-automatically '(kill-terminal delete-frame))))
+        (proc (frame-parameter nil 'client)))
     (cond ((eq proc 'nowait)
 	   ;; Nowait frames have no client buffer list.
-	   (if (length> (frame-list) (if server-stop-automatically 2 1))
+	   (if (length> (frame-list) (if stop-automatically 2 1))
                ;; If there are any other frames, only delete this one.
                ;; When `server-stop-automatically' is set, don't count
                ;; the daemon frame.
@@ -1792,7 +1804,7 @@ server-save-buffers-kill-terminal
 	     ;; If we're the last frame standing, kill Emacs.
 	     (save-buffers-kill-emacs arg)))
 	  ((processp proc)
-           (if (or (not server-stop-automatically)
+           (if (or (not stop-automatically)
                    (length> server-clients 1)
                    (seq-some
                     (lambda (frame)
@@ -1819,14 +1831,13 @@ server-save-buffers-kill-terminal
 	  (t (error "Invalid client frame")))))
 
 (defun server-stop-automatically--handle-delete-frame (frame)
-  "Handle deletion of FRAME when `server-stop-automatically' is used."
-  (when (and server-stop-automatically
-             (null (cddr (frame-list))))
+  "Handle deletion of FRAME when `server-stop-automatically' is `delete-frame'."
+  (when (null (cddr (frame-list)))
     (let ((server-stop-automatically nil))
       (save-buffers-kill-emacs))))
 
 (defun server-stop-automatically--maybe-kill-emacs ()
-  "Handle closing of Emacs daemon when `server-stop-automatically' is used."
+  "Handle closing of Emacs daemon when `server-stop-automatically' is `empty'."
   (unless (cdr (frame-list))
     (when (and
 	   (not (memq t (mapcar (lambda (b)
@@ -1840,41 +1851,67 @@ server-stop-automatically--maybe-kill-emacs
 				(process-list)))))
       (kill-emacs))))
 
-;;;###autoload
-(defun server-stop-automatically (arg)
-  "Automatically stop server as specified by ARG.
-
-If ARG is the symbol `empty', stop the server when it has no
+(defun server-apply-stop-automatically ()
+  "Apply the current value of `server-stop-automatically'.
+This function adds or removes the necessary helpers to manage
+stopping the Emacs server automatically, depending on the whether
+the server is running or not.  This function only applies when
+running Emacs as a daemon."
+  (when (daemonp)
+    (let (empty-timer-p delete-frame-p)
+      (when server-process
+        (pcase server-stop-automatically
+          ('empty        (setq empty-timer-p t))
+          ('delete-frame (setq delete-frame-p t))))
+      ;; Start or stop the timer.
+      (if empty-timer-p
+          (unless server-stop-automatically--timer
+            (setq server-stop-automatically--timer
+                  (run-with-timer
+                   10 2
+		   #'server-stop-automatically--maybe-kill-emacs)))
+        (when server-stop-automatically--timer
+          (cancel-timer server-stop-automatically--timer)
+          (setq server-stop-automatically--timer nil)))
+      ;; Add or remove the delete-frame hook.
+      (if delete-frame-p
+          (add-hook 'delete-frame-functions
+		    #'server-stop-automatically--handle-delete-frame)
+        (remove-hook 'delete-frame-functions
+                     #'server-stop-automatically--handle-delete-frame)))))
+
+(defcustom server-stop-automatically nil
+  "If non-nil, stop the server under the requested conditions.
+
+If this is the symbol `empty', stop the server when it has no
 remaining clients, no remaining unsaved file-visiting buffers,
 and no running processes with a `query-on-exit' flag.
 
-If ARG is the symbol `delete-frame', ask the user when the last
+If this is the symbol `delete-frame', ask the user when the last
 frame is deleted whether each unsaved file-visiting buffer must
 be saved and each running process with a `query-on-exit' flag
 can be stopped, and if so, stop the server itself.
 
-If ARG is the symbol `kill-terminal', ask the user when the
+If this is the symbol `kill-terminal', ask the user when the
 terminal is killed with \\[save-buffers-kill-terminal] \
 whether each unsaved file-visiting
 buffer must be saved and each running process with a `query-on-exit'
-flag can be stopped, and if so, stop the server itself.
-
-Any other value of ARG will cause this function to signal an error.
-
-This function is meant to be called from the user init file."
-  (when (daemonp)
-    (setq server-stop-automatically arg)
-    (cond
-     ((eq arg 'empty)
-      (setq server-stop-automatically nil)
-      (run-with-timer 10 2
-		      #'server-stop-automatically--maybe-kill-emacs))
-     ((eq arg 'delete-frame)
-      (add-hook 'delete-frame-functions
-		#'server-stop-automatically--handle-delete-frame))
-     ((eq arg 'kill-terminal))
-     (t
-      (error "Unexpected argument")))))
+flag can be stopped, and if so, stop the server itself."
+  :type '(choice
+          (const :tag "Never" nil)
+          (const :tag "When empty (no clients, unsaved files, or processes)"
+                 empty)
+          (const :tag "When killing last terminal" kill-terminal)
+          (const :tag "When killing last terminal or frame" delete-frame))
+  :set (lambda (symbol value)
+         (set-default symbol value)
+         (server-apply-stop-automatically))
+  :version "29.1")
+
+(defun server-stop-automatically (value)
+  "Automatically stop the Emacs server as specified by VALUE.
+This sets the variable `server-stop-automatically' (which see)."
+  (setopt server-stop-automatically value))
 
 (define-key ctl-x-map "#" 'server-edit)
 
-- 
2.25.1


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

* bug#59668: 29.0.50; [PATCH] Make 'server-stop-automatically' into a defcustom
  2022-12-07  1:39       ` Jim Porter
@ 2022-12-07  1:49         ` Jim Porter
  2022-12-07 12:48         ` Eli Zaretskii
  1 sibling, 0 replies; 10+ messages in thread
From: Jim Porter @ 2022-12-07  1:49 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 59668

[-- Attachment #1: Type: text/plain, Size: 181 bytes --]

On 12/6/2022 5:39 PM, Jim Porter wrote:
> Attached is an updated patch (rebased on top of my previous patches for 
> bug#51993).

Oops. Forgot to save the file after my last change.

[-- Attachment #2: 0001-Make-server-stop-automatically-into-a-defcustom.patch --]
[-- Type: text/plain, Size: 11433 bytes --]

From 68946abc9cd6bec43f04ae85357e701861983b07 Mon Sep 17 00:00:00 2001
From: Jim Porter <jporterbugs@gmail.com>
Date: Sun, 27 Nov 2022 22:21:10 -0800
Subject: [PATCH] Make 'server-stop-automatically' into a defcustom

This changes the meaning of the (formerly internal) variable
'server-stop-automatically': it now always holds the requested
configuration, even when Emacs was not started as a daemon
(bug#59668).

* lisp/server.el (server-stop-automatically): Convert the variable to
a defcustom, and make the function simply set the defcustom.
(server-stop-automatically--timer): New variable.
(server-apply-stop-automatically): New function...
(server-stop, server-start): ... call it.
(server-save-buffers-kill-terminal): Adjust the conditions for
stopping automatically to account for the change of meaning for
'server-stop-automatically'.
(server-stop-automatically--handle-delete-frame): Remove unnecessary
test of the 'server-stop-automatically' option; this hook is only set
when it should do its job.
(server-stop-automatically--maybe-kill-emacs): Update docstring.

* doc/emacs/misc.texi (Emacs Server): Update documentation.
---
 doc/emacs/misc.texi |  41 ++++++++--------
 lisp/server.el      | 113 +++++++++++++++++++++++++++++---------------
 2 files changed, 95 insertions(+), 59 deletions(-)

diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index 29c0bed19c0..702c72bac25 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -1808,31 +1808,28 @@ Emacs Server
   emacs --daemon=foo
 @end example
 
-@findex server-stop-automatically
+@vindex server-stop-automatically
   The Emacs server can optionally be stopped automatically when
-certain conditions are met.  To do this, call the function
-@code{server-stop-automatically} in your init file (@pxref{Init
-File}), with one of the following arguments:
+certain conditions are met.  To do this, set the option
+@code{server-stop-automatically} to one of the following values:
 
-@itemize
-@item
-With the argument @code{empty}, the server is stopped when it has no
-clients, no unsaved file-visiting buffers and no running processes
-anymore.
-
-@item
-With the argument @code{delete-frame}, when the last client frame is
-being closed, you are asked whether each unsaved file-visiting buffer
-must be saved and each unfinished process can be stopped, and if so,
-the server is stopped.
-
-@item
-With the argument @code{kill-terminal}, when the last client frame is
-being closed with @kbd{C-x C-c} (@code{save-buffers-kill-terminal}),
-you are asked whether each unsaved file-visiting buffer must be saved
-and each unfinished process can be stopped, and if so, the server is
+@table @code
+@item empty
+This value causes the server to be stopped when it has no clients, no
+unsaved file-visiting buffers and no running processes anymore.
+
+@item delete-frame
+This value means that when the last client frame is being closed, you
+are asked whether each unsaved file-visiting buffer must be saved and
+each unfinished process can be stopped, and if so, the server is
 stopped.
-@end itemize
+
+@item kill-terminal
+This value means that when the last client frame is being closed with
+@kbd{C-x C-c} (@code{save-buffers-kill-terminal}), you are asked
+whether each unsaved file-visiting buffer must be saved and each
+unfinished process can be stopped, and if so, the server is stopped.
+@end table
 
 @findex server-eval-at
   If you have defined a server by a unique server name, it is possible
diff --git a/lisp/server.el b/lisp/server.el
index aa62b25a891..f6d9a439e32 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -273,6 +273,11 @@ server-client-instructions
   :version "28.1"
   :type 'boolean)
 
+(defvar server-stop-automatically)      ; Defined below to avoid recursive load.
+
+(defvar server-stop-automatically--timer nil
+  "The timer object for `server-stop-automatically--maybe-kill-emacs'.")
+
 ;; We do not use `temporary-file-directory' here, because emacsclient
 ;; does not read the init file.
 (defvar server-socket-dir
@@ -636,7 +641,8 @@ server-stop
       (setq stopped-p t
             server-process nil
             server-mode nil
-            global-minor-modes (delq 'server-mode global-minor-modes)))
+            global-minor-modes (delq 'server-mode global-minor-modes))
+      (server-apply-stop-automatically))
     (unwind-protect
         ;; Delete the socket files made by previous server
         ;; invocations.
@@ -757,6 +763,7 @@ server-start
 			 (list :family 'local
 			       :service server-file
 			       :plist '(:authenticated t)))))
+          (server-apply-stop-automatically)
 	  (unless server-process (error "Could not start server process"))
           (server-log "Started server")
 	  (process-put server-process :server-file server-file)
@@ -1769,9 +1776,6 @@ server-switch-buffer
     (when server-raise-frame
       (select-frame-set-input-focus (window-frame)))))
 
-(defvar server-stop-automatically nil
-  "Internal status variable for `server-stop-automatically'.")
-
 ;;;###autoload
 (defun server-save-buffers-kill-terminal (arg)
   ;; Called from save-buffers-kill-terminal in files.el.
@@ -1779,11 +1783,19 @@ server-save-buffers-kill-terminal
 With ARG non-nil, silently save all file-visiting buffers, then kill.
 
 If emacsclient was started with a list of filenames to edit, then
-only these files will be asked to be saved."
-  (let ((proc (frame-parameter nil 'client)))
+only these files will be asked to be saved.
+
+When running Emacs as a daemon and with
+`server-stop-automatically' (which see) set to `kill-terminal' or
+`delete-frame', this function may call `save-buffers-kill-emacs'
+if there are no other active clients."
+  (let ((stop-automatically
+         (and (daemonp)
+              (memq server-stop-automatically '(kill-terminal delete-frame))))
+        (proc (frame-parameter nil 'client)))
     (cond ((eq proc 'nowait)
 	   ;; Nowait frames have no client buffer list.
-	   (if (length> (frame-list) (if server-stop-automatically 2 1))
+	   (if (length> (frame-list) (if stop-automatically 2 1))
                ;; If there are any other frames, only delete this one.
                ;; When `server-stop-automatically' is set, don't count
                ;; the daemon frame.
@@ -1792,7 +1804,7 @@ server-save-buffers-kill-terminal
 	     ;; If we're the last frame standing, kill Emacs.
 	     (save-buffers-kill-emacs arg)))
 	  ((processp proc)
-           (if (or (not server-stop-automatically)
+           (if (or (not stop-automatically)
                    (length> server-clients 1)
                    (seq-some
                     (lambda (frame)
@@ -1819,14 +1831,13 @@ server-save-buffers-kill-terminal
 	  (t (error "Invalid client frame")))))
 
 (defun server-stop-automatically--handle-delete-frame (frame)
-  "Handle deletion of FRAME when `server-stop-automatically' is used."
-  (when (and server-stop-automatically
-             (null (cddr (frame-list))))
+  "Handle deletion of FRAME when `server-stop-automatically' is `delete-frame'."
+  (when (null (cddr (frame-list)))
     (let ((server-stop-automatically nil))
       (save-buffers-kill-emacs))))
 
 (defun server-stop-automatically--maybe-kill-emacs ()
-  "Handle closing of Emacs daemon when `server-stop-automatically' is used."
+  "Handle closing of Emacs daemon when `server-stop-automatically' is `empty'."
   (unless (cdr (frame-list))
     (when (and
 	   (not (memq t (mapcar (lambda (b)
@@ -1840,41 +1851,69 @@ server-stop-automatically--maybe-kill-emacs
 				(process-list)))))
       (kill-emacs))))
 
-;;;###autoload
-(defun server-stop-automatically (arg)
-  "Automatically stop server as specified by ARG.
-
-If ARG is the symbol `empty', stop the server when it has no
+(defun server-apply-stop-automatically ()
+  "Apply the current value of `server-stop-automatically'.
+This function adds or removes the necessary helpers to manage
+stopping the Emacs server automatically, depending on the whether
+the server is running or not.  This function only applies when
+running Emacs as a daemon."
+  (when (daemonp)
+    (let (empty-timer-p delete-frame-p)
+      (when server-process
+        (pcase server-stop-automatically
+          ('empty        (setq empty-timer-p t))
+          ('delete-frame (setq delete-frame-p t))))
+      ;; Start or stop the timer.
+      (if empty-timer-p
+          (unless server-stop-automatically--timer
+            (setq server-stop-automatically--timer
+                  (run-with-timer
+                   10 2
+		   #'server-stop-automatically--maybe-kill-emacs)))
+        (when server-stop-automatically--timer
+          (cancel-timer server-stop-automatically--timer)
+          (setq server-stop-automatically--timer nil)))
+      ;; Add or remove the delete-frame hook.
+      (if delete-frame-p
+          (add-hook 'delete-frame-functions
+		    #'server-stop-automatically--handle-delete-frame)
+        (remove-hook 'delete-frame-functions
+                     #'server-stop-automatically--handle-delete-frame))))
+  ;; Return the current value of `server-stop-automatically'.
+  server-stop-automatically)
+
+(defcustom server-stop-automatically nil
+  "If non-nil, stop the server under the requested conditions.
+
+If this is the symbol `empty', stop the server when it has no
 remaining clients, no remaining unsaved file-visiting buffers,
 and no running processes with a `query-on-exit' flag.
 
-If ARG is the symbol `delete-frame', ask the user when the last
+If this is the symbol `delete-frame', ask the user when the last
 frame is deleted whether each unsaved file-visiting buffer must
 be saved and each running process with a `query-on-exit' flag
 can be stopped, and if so, stop the server itself.
 
-If ARG is the symbol `kill-terminal', ask the user when the
+If this is the symbol `kill-terminal', ask the user when the
 terminal is killed with \\[save-buffers-kill-terminal] \
 whether each unsaved file-visiting
 buffer must be saved and each running process with a `query-on-exit'
-flag can be stopped, and if so, stop the server itself.
-
-Any other value of ARG will cause this function to signal an error.
-
-This function is meant to be called from the user init file."
-  (when (daemonp)
-    (setq server-stop-automatically arg)
-    (cond
-     ((eq arg 'empty)
-      (setq server-stop-automatically nil)
-      (run-with-timer 10 2
-		      #'server-stop-automatically--maybe-kill-emacs))
-     ((eq arg 'delete-frame)
-      (add-hook 'delete-frame-functions
-		#'server-stop-automatically--handle-delete-frame))
-     ((eq arg 'kill-terminal))
-     (t
-      (error "Unexpected argument")))))
+flag can be stopped, and if so, stop the server itself."
+  :type '(choice
+          (const :tag "Never" nil)
+          (const :tag "When empty (no clients, unsaved files, or processes)"
+                 empty)
+          (const :tag "When killing last terminal" kill-terminal)
+          (const :tag "When killing last terminal or frame" delete-frame))
+  :set (lambda (symbol value)
+         (set-default symbol value)
+         (server-apply-stop-automatically))
+  :version "29.1")
+
+(defun server-stop-automatically (value)
+  "Automatically stop the Emacs server as specified by VALUE.
+This sets the variable `server-stop-automatically' (which see)."
+  (setopt server-stop-automatically value))
 
 (define-key ctl-x-map "#" 'server-edit)
 
-- 
2.25.1


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

* bug#59668: 29.0.50; [PATCH] Make 'server-stop-automatically' into a defcustom
  2022-12-07  1:39       ` Jim Porter
  2022-12-07  1:49         ` Jim Porter
@ 2022-12-07 12:48         ` Eli Zaretskii
  2022-12-08  6:02           ` Jim Porter
  1 sibling, 1 reply; 10+ messages in thread
From: Eli Zaretskii @ 2022-12-07 12:48 UTC (permalink / raw)
  To: Jim Porter; +Cc: 59668

> Date: Tue, 6 Dec 2022 17:39:52 -0800
> Cc: 59668@debbugs.gnu.org
> From: Jim Porter <jporterbugs@gmail.com>
> 
> >> +          (const :tag "When empty" empty)
> > 
> > "When empty" doesn't explain itself well enough.  Can we come up with a
> > better description?
> 
> How about "When empty (no clients, unsaved files, or processes)"?

Just leave the text inside the parentheses (but without the
parentheses themselves), there's no need to have the "empty part there.

> Attached is an updated patch (rebased on top of my previous patches for 
> bug#51993).

OK with the above change.  Thanks.





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

* bug#59668: 29.0.50; [PATCH] Make 'server-stop-automatically' into a defcustom
  2022-12-07 12:48         ` Eli Zaretskii
@ 2022-12-08  6:02           ` Jim Porter
  0 siblings, 0 replies; 10+ messages in thread
From: Jim Porter @ 2022-12-08  6:02 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 59668-done

On 12/7/2022 4:48 AM, Eli Zaretskii wrote:
>> Date: Tue, 6 Dec 2022 17:39:52 -0800
>> Cc: 59668@debbugs.gnu.org
>> From: Jim Porter <jporterbugs@gmail.com>
>>
>> How about "When empty (no clients, unsaved files, or processes)"?
> 
> Just leave the text inside the parentheses (but without the
> parentheses themselves), there's no need to have the "empty part there.

Ok by me.

>> Attached is an updated patch (rebased on top of my previous patches for
>> bug#51993).
> 
> OK with the above change.  Thanks.

Thanks, merged to master as
153c67fa92eaad39410b1809ab9b125616bdc5c1. Closing this bug now.





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

end of thread, other threads:[~2022-12-08  6:02 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-29  4:23 bug#59668: 29.0.50; [PATCH] Make 'server-stop-automatically' into a defcustom Jim Porter
2022-12-01 17:08 ` Eli Zaretskii
2022-12-01 18:33   ` Jim Porter
2022-12-01 18:41     ` Jim Porter
2022-12-02  3:30   ` Jim Porter
2022-12-02 14:42     ` Eli Zaretskii
2022-12-07  1:39       ` Jim Porter
2022-12-07  1:49         ` Jim Porter
2022-12-07 12:48         ` Eli Zaretskii
2022-12-08  6:02           ` Jim Porter

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.