From: Gregory Heytings Date: Thu, 11 Nov 2021 05:43:10 +0000 (+0100) Subject: Options to automatically stop the Emacs server X-Git-Tag: emacs-29.0.90~3668^2~59 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=894dd18804ef766a87ffa4b4109125b4661651be;p=emacs.git Options to automatically stop the Emacs server * 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. --- diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi index 4b3c2ea4bd2..3d423d7675b 100644 --- a/doc/emacs/misc.texi +++ b/doc/emacs/misc.texi @@ -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 diff --git a/etc/NEWS b/etc/NEWS index 78ce3c067fe..e32446997b9 100644 --- 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 --- diff --git a/lisp/server.el b/lisp/server.el index d9986562377..deaaf07da84 100644 --- a/lisp/server.el +++ b/lisp/server.el @@ -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)