;;; syd-buffers.el -*- lexical-binding: t; -*- (require 'syd-prelude) (syd-define-stub syd/kill-all-buffers :desc "Kill all buffers. See `doom/kill-all-buffers'." :interactive t) (syd-define-stub syd/kill-other-buffers :desc "Kill other buffers. See `doom/kill-other-buffers'." :interactive t) (syd-define-stub syd/kill-burried-buffers :desc "Kill burried buffers. See `doom/kill-burried-buffers'." :interactive t) (syd-define-stub syd/save-buffer-as-root :desc "Sudo save buffer as root. See `doom/sudo-save-buffer'" :interactive t) (defvar syd-fallback-buffer-name "*scratch*" "The name of the buffer to fall back to if no other buffers exist (will create it if it doesn't exist).") (defun syd-fallback-buffer () "Returns the fallback buffer, creating it if necessary. By default this is the scratch buffer. See `syd-fallback-buffer-name' to change this." (let (buffer-list-update-hook) (get-buffer-create syd-fallback-buffer-name))) (defvar-local syd-real-buffer-p nil "If non-nil, this buffer should be considered real no matter what. See `syd-real-buffer-p' for more information.") (defun syd-special-buffer-p (buf) "Returns non-nil if BUF's name starts and ends with an *." (char-equal ?* (aref (buffer-name buf) 0))) (defun syd-non-file-visiting-buffer-p (buf) (with-current-buffer buf (if buffer-file-name t nil))) (defvar syd-unreal-buffer-functions '(minibufferp syd-special-buffer-p syd-non-file-visiting-buffer-p) "A list of predicate functions run to determine if a buffer is *not* real, unlike `syd-real-buffer-functions'. They are passed one argument: the buffer to be tested. Should any of these functions return non-nil, the rest of the functions are ignored and the buffer is considered unreal. See `syd-real-buffer-p' for more information.") (defun syd-dired-buffer-p (buf) "Returns non-nil if BUF is a dired buffer." (provided-mode-derived-p (buffer-local-value 'major-mode buf) 'dired-mode)) (defvar syd-real-buffer-functions '(syd-dired-buffer-p) "A list of predicate functions run to determine if a buffer is real, unlike `syd-unreal-buffer-functions'. They are passed one argument: the buffer to be tested. Should any of its function returns non-nil, the rest of the functions are ignored and the buffer is considered real. See `syd-real-buffer-p' for more information.") (defun syd-temp-buffer-p (buf) "Returns non-nil if BUF is temporary." (char-equal ?\s (aref (buffer-name buf) 0))) (defun syd-real-buffer-p (buffer-or-name) "Returns t if BUFFER-OR-NAME is a 'real' buffer. A real buffer is a useful buffer; a first class citizen. Real ones should get special treatment, because we will be spending most of our time in them. Unreal ones should be low-profile and easy to cast aside, so we can focus on real ones. The exact criteria for a real buffer is: 1. A non-nil value for the buffer-local value of the `syd-real-buffer-p' variable OR 2. Any function in `syd-real-buffer-functions' returns non-nil OR 3. None of the functions in `syd-unreal-buffer-functions' must return non-nil. If BUFFER-OR-NAME is omitted or nil, the current buffer is tested." (or (bufferp buffer-or-name) (stringp buffer-or-name) (signal 'wrong-type-argument (list '(bufferp stringp) buffer-or-name))) (when-let (buf (get-buffer buffer-or-name)) (when-let (basebuf (buffer-base-buffer buf)) (setq buf basebuf)) (and (buffer-live-p buf) (not (syd-temp-buffer-p buf)) (or (buffer-local-value 'syd-real-buffer-p buf) (run-hook-with-args-until-success 'syd-real-buffer-functions buf) (not (run-hook-with-args-until-success 'syd-unreal-buffer-functions buf)))))) (defun syd-unreal-buffer-p (buffer-or-name) "Return t if BUFFER-OR-NAME is an 'unreal' buffer. See `syd-real-buffer-p' for details on what that means." (not (syd-real-buffer-p buffer-or-name))) (defun syd-fixup-windows (windows) "Ensure that each of WINDOWS is showing a real buffer or the fallback buffer." (dolist (window windows) (with-selected-window window (when (syd-unreal-buffer-p (window-buffer)) (previous-buffer) (when (syd-unreal-buffer-p (window-buffer)) (switch-to-buffer (syd-fallback-buffer))))))) ;;;###autoload (defun syd-set-buffer-realness (buffer realness) (with-current-buffer buffer (setq syd-real-buffer-p realness))) ;;;###autoload (defun syd-mark-buffer-as-real () (syd-set-buffer-realness (current-buffer) t)) (defun syd-kill-buffer-fixup-windows (buffer) "Kill the BUFFER and ensure all the windows it was displayed in have switched to a real buffer or the fallback buffer." (let ((windows (get-buffer-window-list buffer))) (kill-buffer buffer) (syd-fixup-windows (cl-remove-if-not #'window-live-p windows)))) (defun syd/kill-this-buffer-in-all-windows (buffer &optional dont-save) "Kill BUFFER globally and ensure all windows previously showing this buffer have switched to a real buffer or the fallback buffer. If DONT-SAVE, don't prompt to save modified buffers (discarding their changes)." (interactive (list (current-buffer) current-prefix-arg)) (cl-assert (bufferp buffer) t) (when (and (buffer-modified-p buffer) dont-save) (with-current-buffer buffer (set-buffer-modified-p nil))) (syd-kill-buffer-fixup-windows buffer)) (provide 'syd-buffers)