]> git.eshelyaron.com Git - emacs.git/commitdiff
Options to automatically stop the Emacs server
authorGregory Heytings <gregory@heytings.org>
Thu, 11 Nov 2021 05:43:10 +0000 (06:43 +0100)
committerLars Ingebrigtsen <larsi@gnus.org>
Thu, 11 Nov 2021 05:43:10 +0000 (06:43 +0100)
* doc/emacs/misc.texi (Emacs Server): Document the new function.
Also mention that an Emacs server can be started with emacsclient.

* etc/NEWS: Describe the new function (bug#51377).
* lisp/server.el (server-stop-automatically): New function.
(server-stop-automatically): New auxiliary variable.
(server-stop-automatically--maybe-kill-emacs)
(server-stop-automatically--handle-delete-frame): New auxiliary
functions.
(server-save-buffers-kill-terminal): Call the new auxiliary
function when necessary.

doc/emacs/misc.texi
etc/NEWS
lisp/server.el

index 4b3c2ea4bd2cd1764f6304f91d56154bcc5d688a..3d423d7675b2fb80ce466402a3cfcabc98914833 100644 (file)
@@ -1703,6 +1703,11 @@ options.  @xref{Initial Options}.  When Emacs is started this way, it
 calls @code{server-start} after initialization and does not open an
 initial frame.  It then waits for edit requests from clients.
 
+@item
+Run the command @code{emacsclient} with the @samp{--alternate-editor=""}
+command-line option.  This starts an Emacs daemon only if no Emacs daemon
+is already running.
+
 @cindex systemd unit file
 @item
 If your operating system uses @command{systemd} to manage startup,
@@ -1769,6 +1774,32 @@ you can give each daemon its own server name like this:
   emacs --daemon=foo
 @end example
 
+@findex 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:
+
+@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
+stopped.
+@end itemize
+
 @findex server-eval-at
   If you have defined a server by a unique server name, it is possible
 to connect to the server from another Emacs instance and evaluate Lisp
index 78ce3c067fe633cdfd24794a29d293cd639190f7..e32446997b9db4634591d9f8cd34a5193c2f7b93 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -139,6 +139,12 @@ suspicious and could be malicious.
 With this command-line option, Emacs reuses an existing graphical client
 frame if one exists; otherwise it creates a new frame.
 
++++
+*** 'server-stop-automatically' can be used to automatically stop the server.
+The Emacs server will be automatically stopped when certain conditions
+are met.  The conditions are given by the argument, which can be
+'empty', 'delete-frame' or 'kill-terminal'.
+
 * Editing Changes in Emacs 29.1
 
 ---
index d99865623774ffe8e6d9c773f11a7de90b549ba2..deaaf07da844f6458767f7842d4d558db61fd0a7 100644 (file)
@@ -1716,6 +1716,9 @@ be a cons cell (LINENUMBER . COLUMNNUMBER)."
     (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.
@@ -1724,27 +1727,101 @@ 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)))
-    (cond ((eq proc 'nowait)
-          ;; Nowait frames have no client buffer list.
-          (if (cdr (frame-list))
-              (progn (save-some-buffers arg)
-                     (delete-frame))
-            ;; If we're the last frame standing, kill Emacs.
-            (save-buffers-kill-emacs arg)))
-         ((processp proc)
-          (let ((buffers (process-get proc 'buffers)))
-            (save-some-buffers
-             arg (if buffers
-                      ;; Only files from emacsclient file list.
-                     (lambda () (memq (current-buffer) buffers))
-                    ;; No emacsclient file list: don't override
-                    ;; `save-some-buffers-default-predicate' (unless
-                    ;; ARG is non-nil), since we're not killing
-                    ;; Emacs (unlike `save-buffers-kill-emacs').
-                   (and arg t)))
-            (server-delete-client proc)))
-         (t (error "Invalid client frame")))))
+  (if server-stop-automatically
+      (server-stop-automatically--handle-delete-frame (selected-frame))
+    (let ((proc (frame-parameter nil 'client)))
+      (cond ((eq proc 'nowait)
+            ;; Nowait frames have no client buffer list.
+            (if (cdr (frame-list))
+                (progn (save-some-buffers arg)
+                       (delete-frame))
+              ;; If we're the last frame standing, kill Emacs.
+              (save-buffers-kill-emacs arg)))
+           ((processp proc)
+            (let ((buffers (process-get proc 'buffers)))
+              (save-some-buffers
+               arg (if buffers
+                        ;; Only files from emacsclient file list.
+                       (lambda () (memq (current-buffer) buffers))
+                      ;; No emacsclient file list: don't override
+                      ;; `save-some-buffers-default-predicate' (unless
+                      ;; ARG is non-nil), since we're not killing
+                      ;; Emacs (unlike `save-buffers-kill-emacs').
+                     (and arg t)))
+              (server-delete-client proc)))
+           (t (error "Invalid client frame"))))))
+
+(defun server-stop-automatically--handle-delete-frame (frame)
+  "Handle deletion of FRAME when `server-stop-automatically' is used."
+  (when server-stop-automatically
+    (if (if (and (processp (frame-parameter frame 'client))
+                (eq this-command 'save-buffers-kill-terminal))
+           (progn
+             (dolist (f (frame-list))
+               (when (and (eq (frame-parameter frame 'client)
+                              (frame-parameter f 'client))
+                          (not (eq frame f)))
+                 (set-frame-parameter f 'client nil)
+                 (let ((server-stop-automatically nil))
+                   (delete-frame f))))
+             (if (cddr (frame-list))
+                 (let ((server-stop-automatically nil))
+                   (delete-frame frame)
+                   nil)
+               t))
+         (null (cddr (frame-list))))
+       (let ((server-stop-automatically nil))
+         (save-buffers-kill-emacs)
+         (delete-frame frame)))))
+
+(defun server-stop-automatically--maybe-kill-emacs ()
+  "Handle closing of Emacs daemon when `server-stop-automatically' is used."
+  (unless (cdr (frame-list))
+    (when (and
+          (not (memq t (mapcar (lambda (b)
+                                 (and (buffer-file-name b)
+                                      (buffer-modified-p b)))
+                               (buffer-list))))
+          (not (memq t (mapcar (lambda (p)
+                                 (and (memq (process-status p)
+                                            '(run stop open listen))
+                                      (process-query-on-exit-flag p)))
+                               (process-list)))))
+      (kill-emacs))))
+
+;;;###autoload
+(defun server-stop-automatically (arg)
+  "Automatically stop server when possible.
+
+When ARG is 'empty, the server is stopped when it has no remaining
+clients, no remaining unsaved file-visiting buffers, and no
+running processes with a query-on-exit flag.
+
+When ARG is 'delete-frame, the user is asked when the last frame is
+being closed 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, the server itself is stopped.
+
+When ARG is 'kill-terminal, the user is asked when the last frame
+is being close 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, the server itself is stopped.
+
+This function is meant to be put in init files."
+  (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")))))
 
 (define-key ctl-x-map "#" 'server-edit)