diff --git a/init.el b/init.el index fdd36f9..a06f2af 100644 --- a/init.el +++ b/init.el @@ -196,10 +196,112 @@ Interactively, force the recompile if called with a prefix." "*dashboard*") (add-to-list 'clean-buffer-list-kill-never-buffer-names "*elfeed-search*") - (add-to-list 'clean-buffer-list-kill-never-buffer-names - "*mu4e-last-update*") (midnight-mode 1)) +(defvar my/kill-some-buffers-exclude-names + '("*mu4e-main*" "*Async-native-compile-log*" "*dashboard*" "*elfeed-search*" + "*Messages*" "*scratch*") + "List of literal buffer names that `my/kill-some-buffers' should not kill.") + +(defun my/kill-some-buffers-excluded-buffer-p (buffer) + "Return non-nil if BUFFER should be excluded from `my/kill-some-buffers'." + (cl-find (buffer-name buffer) my/kill-some-buffers-exclude-names + :test 'equal)) + +(defun my/buffer-visible-p (buffer) + "Return non-nil if BUFFER is visible. +BUFFER can be a string or a buffer." + (cond + ((stringp buffer) + (not (string-prefix-p " " buffer))) + ((bufferp buffer) + (my/buffer-visible-p (buffer-name buffer))) + (t + (signal 'wrong-type-argument `((or bufferp stringp) ,buffer))))) + +(defvar my/kill-some-buffers-default-pred 'my/buffer-visible-p + "Default predicate for `my/kill-some-buffers'.") + +(defun my/kill-some-buffers-prompt-for (buffer) + "Generate a prompt for BUFFER." + (let* ((process (get-buffer-process buffer)) + (process-p (and (process-live-p process) + (not (process-query-on-exit-flag process)))) + (modified-p (and (buffer-file-name buffer) + (buffer-modified-p buffer)))) + (format "Buffer \"%s\" %s. Kill? " + (buffer-name buffer) + (cond + ((and process-p modified-p) + "HAS BEEN EDITED AND HAS A LIVE PROCESS") + (modified-p + "HAS BEEN EDITED") + (process-p + "HAS A LIVE PROCESS") + (t "is unmodified"))))) + +(cl-defun my/kill-some-buffers (&optional auto-unmod pred) + "Improved version of `kill-some-buffers'. +Ask the user weather to kill each visible buffer whose name is not in +`my/kill-some-buffers-exclude-names'. + +When AUTO-UNMOD is non-nil, as it is with a prefix argument, automatically kill +unmodified buffers, and then ask about the rest. + +When PRED is non-nil, it is a function that will be run in each buffer (not just +visible ones). If it returns t, that buffer will be considered for killing. If +PRED is nil, the value of `my/kill-some-buffers-default-pred' is used." + (interactive "P") + ;; we already ask, no need to do it again + (let ((kill-buffer-query-functions nil) + (all-action (when auto-unmod 'unmod)) + (ask-again-buffers)) + (cl-flet ((ask-about (buffer allow-unmod) + (unless all-action + (read-answer + (my/kill-some-buffers-prompt-for buffer) + `(("yes" ?y "save and kill this buffer") + ("no" ?n "skip this buffer") + ("all" ?! "save and kill all remaining buffers") + ("nosave" ?l "kill this buffer without saving") + ,@(when allow-unmod + '(("unmod" ?a + "kill unmodified buffers, ask about the rest"))) + ("quit" ?q "exit"))))) + (act-on (ans buffer allow-unmod) + (when (equal ans "all") + (setq all-action 'all)) + (when (and allow-unmod + (equal ans "unmod")) + (setq all-action 'unmod)) + (cond + ((and (eq all-action 'unmod) + (buffer-file-name buffer) + (buffer-modified-p buffer)) + (push buffer ask-again-buffers)) + ((or (eq all-action 'all) + (eq all-action 'unmod) + (equal ans "yes")) + (when (buffer-file-name buffer) + (with-current-buffer buffer + (save-buffer))) + (kill-buffer buffer)) + ((equal ans "nosave") + (with-current-buffer buffer + (set-buffer-modified-p nil)) + (kill-buffer buffer)) + ;; Skip buffer + ;; ((equal ans "no")) + ((equal ans "quit") + (cl-return-from my/kill-some-buffers))))) + (dolist (buffer (buffer-list)) + (when (and (not (my/kill-some-buffers-excluded-buffer-p buffer)) + (funcall (or pred my/kill-some-buffers-default-pred) buffer)) + (act-on (ask-about buffer t) buffer t))) + (setq all-action nil) + (dolist (buffer ask-again-buffers) + (act-on (ask-about buffer nil) buffer nil))))) + (use-package tab-bar :ensure nil :init