From 2ffedbeff97e33dc9c504b9877272fa5b73707d6 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Tue, 26 Oct 2021 11:54:27 +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--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 (Emacs Server): Document the new function. Also mention that an Emacs server can be started with emacsclient. --- doc/emacs/misc.texi | 20 ++++++++ lisp/server.el | 116 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 115 insertions(+), 21 deletions(-) diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi index 5123a716dc..b8891ed8c3 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,21 @@ 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}). If you want this to happen only when the last +client frame is being closed with @kbd{C-x C-c} +(@code{save-buffers-kill-terminal}), put the expression +@code{(server-stop-automatically 'kill-terminal)} in your 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..a81913fe98 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,98 @@ 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))))) + +(defun server-stop-automatically--maybe-kill-emacs () + "Handle closing of Emacs daemon when `server-stop-automatically' is nil." + (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 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 t, 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 must be +killed, and if so, the server itself is stopped. + +When ARG is any other non-nil value, 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 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) + (cond + ((eq arg t) + (add-hook 'delete-frame-functions + #'server-stop-automatically--handle-delete-frame)) + ((eq arg nil) + (run-with-timer 10 2 + #'server-stop-automatically--maybe-kill-emacs))))) (define-key ctl-x-map "#" 'server-edit) -- 2.33.0