From 3a6bc059dcc0c29be81a83f70f867897a9890667 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Tue, 26 Oct 2021 10:21:58 +0000 Subject: [PATCH] Options to automatically stop the Emacs server. * lisp/server.el (server-stop-automatically): New function. (server-stop-automatically): New auxiliary variable. (server-stop-automatically--handle-delete-frame): New auxiliary function. (server-save-buffers-kill-terminal): Call the new auxiliary function when necessary. * doc/emacs/misc.texi (Emacs Server): Document the new function. Also mention that an Emacs server can be started with emacsclient. --- doc/emacs/misc.texi | 17 ++++++++ lisp/server.el | 103 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 99 insertions(+), 21 deletions(-) diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi index 5123a716dc..77e416403e 100644 --- a/doc/emacs/misc.texi +++ b/doc/emacs/misc.texi @@ -1697,6 +1697,11 @@ Emacs Server 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, @@ -1763,6 +1768,18 @@ Emacs Server emacs --daemon=foo @end example +@findex server-stop-automatically + If you want to automatically stop the Emacs server when it has no +clients, no unsaved file-visiting buffers and no running processes +anymore, put the expression @code{(server-stop-automatically nil)} in +your init file (@pxref{Init File}). + + If you want to be asked whether each unsaved file-visiting buffer +must be saved and each unfinished process can be stopped when the last +client frame is being closed, and if so, to stop the Emacs server, put +the expression @code{(server-stop-automatically t)} in your init file +(@pxref{Init File}). + @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 diff --git a/lisp/server.el b/lisp/server.el index 6359a76199..f6dc0cc145 100644 --- a/lisp/server.el +++ b/lisp/server.el @@ -1716,6 +1716,9 @@ 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. @@ -1724,27 +1727,85 @@ 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." - (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 a frame when `server-stop-automatically' is t." + (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))))) + +;;;###autoload +(defun server-stop-automatically (arg) + "Automatically stop server when possible. +When ARG is nil, 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 non-nil, when the last frame is being closed, the +user is asked whether each unsaved file-visiting buffer must be +saved and each running process with a query-on-exit flag must be +killed, 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) + (if arg + (add-hook 'delete-frame-functions + #'server-stop-automatically--handle-delete-frame) + (run-with-timer + 10 2 + (lambda () + (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))))) + (print "killing server" #'external-debugging-output) + (kill-emacs)))))))) (define-key ctl-x-map "#" 'server-edit) -- 2.33.0