From 761b527a83cf37f821f4b687c2614175aab1387b Mon Sep 17 00:00:00 2001 From: JSDurand Date: Sat, 18 Dec 2021 22:29:57 +0800 Subject: ibuffer: protect buffers * ibuffer.el (durand-default-clear-passlist, durand-clear-passlist): Add a default value variable. (durand-reset-clearlist): Add a function to reset since we are starting to manipulate this variable. (durand-born-equal): Buffer-OR-Name equal. (durand-ibuffer-clear): Make sure the dashboard buffer is correctly recentered, even if it is not displayed right after the clearing operation. (durand-ibuffer-reset-marks): Reset the mark of the current line if this mark is added "automagically" by the macro which defines an ibuffer operation. (durand-ibuffer-restore-mark-before-advice): Check if there are no marks before the operation. In this case a mark will be "automagically" added, and we need to remove that mark later. (ibuffer-do-protect): Protect marked buffers so that they will not be cleared automatically. (ibuffer-do-unprotect): Unprotect the buffers so we can clear them quickly again. (ibuffer-mode-map): Bind the operations. (durand-directory): Modify this filter so that it works correctly. --- ibuffer.el | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 131 insertions(+), 20 deletions(-) (limited to 'ibuffer.el') diff --git a/ibuffer.el b/ibuffer.el index 928bc4b..f270a69 100644 --- a/ibuffer.el +++ b/ibuffer.el @@ -73,11 +73,20 @@ If the optional ARG is non-nil, then produce an IBUFFER buffer (list (cons 'used-mode (buffer-local-value 'major-mode (current-buffer)))))))) +;;;; clear buffers + ;;;###autoload (defvar durand-clear-passlist nil "The list of buffers that should not be deleted \ automatically.") +;;;###autoload +(defvar durand-default-clear-passlist + (list dashboard-buffer-name + "*Group*" + ".newsrc-dribble") + "The default value for `durand-clear-passlist'.") + ;; from dashboard.el (defvar dashboard-buffer-name) @@ -85,10 +94,47 @@ automatically.") ((null dashboard-buffer-name) (setq dashboard-buffer-name ""))) -(setq durand-clear-passlist - (list dashboard-buffer-name - "*Group*" - ".newsrc-dribble")) +(setq durand-clear-passlist durand-default-clear-passlist) + +;;;###autoload +(defun durand-reset-clearlist () + "Reset `durand-clear-passlist' to `durand-default-clear-passlist'." + (interactive) + (setq durand-clear-passlist durand-default-clear-passlist)) + +(autoload #'durand-member "common.el") + +;;;###autoload +(defun durand-born-equal (x y) + "Return t if two buffers or names X and Y are equal. +X and Y could be a buffer or a string that represents the name of +a buffer. + +BORN is an abbreviation of \"Buffer OR Name\"." + (let ((flag 0)) + (cond + ((stringp x) (setq flag (logior flag 1))) + ((bufferp x)) + ((error "X is not a string nor a buffer"))) + (cond + ((stringp y) (setq flag (logior flag 2))) + ((bufferp y)) + ((error "Y is not a string nor a buffer"))) + (cond + ;; both are buffers + ((= flag 0) (eq x y)) + ;; X = string, Y = buffer + ((= flag 1) (string= x (buffer-name y))) + ;; X = buffer, Y = string + ((= flag 2) (string= (buffer-name x) y)) + ;; both are strings + ((= flag 3) (string= x y)) + ((error "Invalid flag: %d" flag))))) + +;; This is not defined via `define-ibuffer-op' as this is supposed to +;; clear ALL buffers by default, not only operating on marked buffers, +;; which is the default behaviour for functions defined by +;; `define-ibuffer-op'. ;;;###autoload (defun durand-ibuffer-clear (&optional arg) @@ -107,15 +153,16 @@ derived modes of `ibuffer-mode'."))) (cond ((durand-member (buffer-name buffer) durand-clear-passlist - #'string=)) + #'durand-born-equal)) ((kill-buffer buffer))))) (cons (current-buffer) (mapcar #'car (ibuffer-current-state-list)))) - (with-current-buffer dashboard-buffer-name - (let ((inhibit-message t)) - (goto-char (point-max)) - (recenter -1)))) + (let ((current (current-buffer))) + (pop-to-buffer dashboard-buffer-name '((display-buffer-same-window))) + (goto-char (point-max)) + (recenter -1) + (pop-to-buffer current '((display-buffer-same-window))))) (t (mapc (function (lambda (buffer-and-mark) @@ -124,13 +171,74 @@ derived modes of `ibuffer-mode'."))) ibuffer-marked-char) (durand-member (buffer-name (car buffer-and-mark)) durand-clear-passlist - #'string=))) + #'durand-born-equal))) ((kill-buffer (car buffer-and-mark)))))) (cons (cons (current-buffer) 32) (ibuffer-current-state-list))) (ibuffer-update nil t)))) +;;;; Protect buffers + +;; NOTE: I know there is `emacs-lock-mode' which seems to do the same +;; thing. But in my opinion my needs are different: indeed I do not +;; want to kill certain buffers, which functionality appears to be +;; covered by the afore-mentionned mode, but in fact, I am actually +;; not trying to protect those buffers from `kill-buffer'. I might +;; actually delete those buffers at a later point, without first +;; unprotecting them. The purpose of this protection is to ensure +;; that the specific operation that I define here for ibuffer does +;; kill those protected buffers. + +;;;###autoload +(defvar durand-ibuffer-reset-marks nil + "Whether to reset the marks in ibuffer.") + +;;;###autoload +(defun durand-ibuffer-restore-mark-before-advice () + (ibuffer-assert-ibuffer-mode) + (cond + ((null (ibuffer-marked-buffer-names)) + (setq durand-ibuffer-reset-marks t)))) + +;;;###autoload +(define-ibuffer-op ibuffer-do-protect () + "Add the marked buffers to `durand-clear-passlist'." + (:opstring "protected" + :active-opstring "protect" + :modifier-p nil + :after (cond + (durand-ibuffer-reset-marks + (setq durand-ibuffer-reset-marks nil) + (ibuffer-set-mark 32))) + :complex t) + (cond + ((durand-member buf durand-clear-passlist #'durand-born-equal) nil) + ((setq durand-clear-passlist (cons buf durand-clear-passlist)) t))) + +(advice-add #'ibuffer-do-protect :before + #'durand-ibuffer-restore-mark-before-advice) + +(define-ibuffer-op ibuffer-do-unprotect () + "Remove the marked buffers from `durand-clear-passlist'." + (:opstring "unprotected" + :active-opstring "unprotect" + :modifier-p nil + :after (cond + (durand-ibuffer-reset-marks + (setq durand-ibuffer-reset-marks nil) + (ibuffer-set-mark 32))) + :complex t) + (cond + ((durand-member buf durand-clear-passlist #'durand-born-equal) + (setq durand-clear-passlist (delq buf durand-clear-passlist)) + t))) + +(advice-add #'ibuffer-do-unprotect :before + #'durand-ibuffer-restore-mark-before-advice) + +;;; key-bindings + (define-key global-map (vector 24 2) #'ibuffer) (define-key global-map (vector ?\s-h) #'ibuffer) (define-key global-map (vector ?\M-\s-b) #'switch-to-buffer-same-mode) @@ -142,6 +250,11 @@ derived modes of `ibuffer-mode'."))) (define-key ibuffer-mode-map (vector ?d) #'ibuffer-do-delete) (define-key ibuffer-mode-map (vector ?D) #'ibuffer-mark-for-delete) (define-key ibuffer-mode-map (vector ?c) #'durand-ibuffer-clear) +(define-key ibuffer-mode-map (vector ?C) #'durand-reset-clearlist) +;; open paren is bound to proect +(define-key ibuffer-mode-map (vector 40) #'ibuffer-do-protect) +;; close paren is bound to unprotect +(define-key ibuffer-mode-map (vector 41) #'ibuffer-do-unprotect) ;;; filter for bongo @@ -175,18 +288,16 @@ QUALIFIER. For a buffer not associated with a file, this matches against the value of `default-directory' in that buffer." (:description "directory name" - :reader (read-from-minibuffer "Filter by directory name (regex): ")) + :reader (read-from-minibuffer + "Filter by directory name (regex): ")) (ibuffer-aif (with-current-buffer buf (ibuffer-buffer-file-name)) (let ((dirname (expand-file-name (file-name-directory it)))) - (when dirname - (string-match-p - (expand-file-name qualifier) - dirname))) - (when (with-current-buffer buf default-directory) - (string-match-p (expand-file-name qualifier) - (expand-file-name - (with-current-buffer buf - default-directory)))))) + (cond (dirname (string-match-p qualifier dirname)))) + (when (buffer-local-value default-directory buf) + (string-match-p + qualifier + (expand-file-name + (buffer-local-value buf default-directory)))))) ;;;###autoload (defun durand-bongo-set-filter () -- cgit v1.2.3-18-g5258