To get around this problem, I wrote a function that tries to determine if I am working on a software project. If so, a yes or no confirmation will be issued prior to exiting. If not, no confirmation will be issued prior to exiting. All my projects are located in a parent directory called projects. Hence, it is simple enough to search for the string "projects" in the buffer list. If the string is found, introduce a yes or no confirmation. Add the following code to your .emacs file.
"Search for buffers that come from the projects directory and return t if found else nil."
(interactive)
(defvar list (buffer-list))
(defvar found nil)
(while list
(defvar element (car list))
(when element
(defvar file-name (buffer-file-name element))
(if file-name
(progn
(if (string-match "projects" file-name)
(setq found t)))))
(setq list (cdr list)))
found)
(defun my-quit-hook ()
"Ask if the user really wants to quit Emacs."
(interactive)
(if (have-projects-buffer)
(y-or-n-p "Really quit Emacs? ")
t))
(add-hook 'kill-emacs-query-functions 'my-quit-hook)
Thanks Jürgen for the suggestions. Here is a cleaner version.
(defun have-projects-buffer ()
(delq nil (mapcar (lambda (buf1)
(string-match "projects" buf1))
(delq nil (mapcar (lambda (buf2)
(buffer-file-name buf2))
(buffer-list))))))
Raimon Grau at puntoblogspot has some different approaches to solving this problem.
Cleaner versions with and without using Common Lisp.
(require 'cl)
(defun project-buffers-p ()
(some (lambda (buf)
(let ((file (buffer-file-name buf)))
(when file
(string-match "projects" file))))
(buffer-list)))
(defun project-buffers-p ()
(not (null (delq nil (mapcar (lambda (buf)
(let ((file (buffer-file-name buf)))
(when file
(string-match "projects" file))))
(buffer-list))))))
(require 'cl)
(defun project-buffers-p ()
(loop for b being the buffers
when (string-match "projects" (or (buffer-file-name b) ""))
return t))
Cleaner versions with and without using Common Lisp.
(require 'cl)
(defun project-buffers-p ()
(some (lambda (buf)
(let ((file (buffer-file-name buf)))
(when file
(string-match "projects" file))))
(buffer-list)))
(defun project-buffers-p ()
(not (null (delq nil (mapcar (lambda (buf)
(let ((file (buffer-file-name buf)))
(when file
(string-match "projects" file))))
(buffer-list))))))
(require 'cl)
(defun project-buffers-p ()
(loop for b being the buffers
when (string-match "projects" (or (buffer-file-name b) ""))
return t))
Looks quite imperative. How about this idiomatic elisp:
ReplyDelete(defun projects-buffers-p ()
(some (apply-partially 'string-match "projects") (mapcar 'buffer-name (buffer-list))))
Thanks Jürgen for the optimization. It's a big step in the right direction. I think you want to use buffer-file-name because we want to match a string in the file path. The other problem is that the special Emacs buffers, eg. *Messages*, do not have file names associated with them. Thus, buffer-file-name returns nil for them. String-match on a nil breaks.
ReplyDeletedoing extra checking if the buffer is visiting a file:
ReplyDelete(some (lambda (b) (when-let (f (buffer-file-name b)) (string-match "projects" f))) (buffer-list))
When I try this, I get "Symbol's function definition is void: f" upon exit. Am I missing something? I've added an update to the original post. It feels like it can still be improved.
ReplyDeleteA couple of different approaches to avoid accidental shutdowns.
ReplyDeleteHave fun!
Thanks Raimon Grau for the new ideas.
ReplyDeleteWhy not just unassign the key mapping for exiting?
ReplyDeletejstolle, unassigning the key mapping for exit is a solution. I guess the difference is the amount of effort needed to exit Emacs.
ReplyDelete