Download
(defgroup tempbuf nil
"Kill unused buffers in the background."
:group 'convenience :prefix 'tempbuf-)
(defcustom tempbuf-life-extension-ratio 2
"Ratio at which to extend the life expectancy of a used buffer.
This value should be greater than 1."
:group 'tempbuf :type 'number)
(defcustom tempbuf-kill-message "Killed inactive buffer: %s."
"Message used to signal the killing of a buffer.
If nil, do not show any message. If a %s appears in the message, it
will get replaced with the name of the buffer being killed."
:group 'tempbuf :type '(choice (const :tag "No message." nil)
string))
(defcustom tempbuf-kill-message-function (function message)
"Function used to deliver the message that a buffer was killed."
:group 'tempbuf :type 'function)
(defcustom tempbuf-mode-hook nil
"Hook run after tempbuf mode is activated in a buffer."
:group 'tempbuf :type 'hook)
(defcustom tempbuf-expire-hook nil
"Hook run when a buffer expires to to inactivity.
The difference between this and `tempbuf-kill-hook' is that this hook
will be called even on buffers visiting files with unsaved content or
with active processes. This hook can thus be used to kill, or perform
any other reasonable action on such buffers when they become
inactive."
:group 'tempbuf :type 'hook)
(defcustom tempbuf-kill-hook nil
"Hook run before a buffer gets killed due to inactivity.
It is possible for any function called by this hook to throw the tag
tempbuf-skip-kill. When this happens the buffer killing will be
postponed."
:group 'tempbuf :type 'hook)
(defcustom tempbuf-minimum-timeout 18
"Wait at least this many seconds before killing a buffer.
The actual timeout for killing a buffer increases with user activity
on it. This value prevents a completely unused buffer from being
killed too early."
:group 'tempbuf :type 'number)
(defcustom tempbuf-mode-line-string " TmpB"
"String displayed on the mode line when tempbuf is active."
:group 'tempbuf :type '(choice (const :tag "No indicator." nil)
string))
(or (assq 'tempbuf-mode minor-mode-alist)
(setq minor-mode-alist
(cons '(tempbuf-mode tempbuf-mode-line-string) minor-mode-alist)))
(defvar tempbuf-mode nil)
(make-variable-buffer-local 'tempbuf-mode)
(defun tempbuf-mode (&optional arg)
"Toggle tempbuf mode.
With prefix ARG, turn the mode on if ARG is positive.
After mode activation, `tempbuf-mode-hook' is run."
(interactive "P")
(cond
((null arg)
(if tempbuf-mode (turn-off-tempbuf-mode) (turn-on-tempbuf-mode)))
((> (prefix-numeric-value arg) 0) (turn-on-tempbuf-mode))
(t (turn-off-tempbuf-mode))))
(defvar tempbuf-timer nil
"Timer used internally by tempbuf mode.")
(defvar tempbuf-last-time nil
"Holds the last time a command was executed on the buffer.")
(make-variable-buffer-local 'tempbuf-last-time)
(defvar tempbuf-timeout tempbuf-minimum-timeout
"Current timeout for buffer expiring.")
(make-variable-buffer-local 'tempbuf-timeout)
(defvar tempbuf-activation-time nil
"Time at which tempbuf mode was activated in the current buffer.")
(make-variable-buffer-local 'tempbuf-activation-time)
(defun tempbuf-time-diff (a b)
"Return the number of seconds between two timestamps A and B."
(let ((diff 0.0))
(setq diff (+ diff (- (car a) (car b))))
(setq diff (* diff (* 256 256)))
(setq diff (+ diff (- (cadr a) (cadr b))))
(when (and (< diff 4900.0) (cdr (cdr a)) (cdr (cdr b)))
(setq diff
(+ diff (/ (float (- (cadr (cdr a)) (cadr (cdr b)))) 1000000))))
diff))
(defun tempbuf-grace (&optional ct)
"Extend the life expectancy of the current buffer.
The optional argument CT specifies a pre-calculated `current-time'
value."
(setq tempbuf-timeout
(+ tempbuf-minimum-timeout
(* tempbuf-life-extension-ratio
(tempbuf-time-diff
(or ct (current-time))
tempbuf-activation-time)))))
(defun tempbuf-check-buffers ()
"Check all the buffers with tempbuf mode turned on.
Inactive buffers will expire and eventually get killed, active ones
will get additional life expectancy."
(let ((ct (current-time)))
(mapcar
(lambda (buffer)
(with-current-buffer buffer
(when tempbuf-mode
(if (get-buffer-window buffer t)
(progn
(tempbuf-post-command)
(tempbuf-grace ct))
(when (and (> (tempbuf-time-diff ct tempbuf-last-time)
tempbuf-timeout))
(tempbuf-expire ct))))))
(buffer-list))))
(defun tempbuf-expire (&optional ct)
"Expire the current buffer.
This function gets called after a certain period of inactivity in the
current buffer.
The hook `tempbuf-expire-hook' is run at first.
If the functions in that hook did not already take care about it,
the current buffer will be killed if it has no unsaved content and no
processes running.
The optional argument CT specifies a pre-calculated \"(current-time)\"
value."
(let ((buffer (current-buffer)))
(run-hooks 'tempbuf-expire-hook)
(when (buffer-live-p buffer)
(if (or buffer-offer-save
(and buffer-file-name (buffer-modified-p))
(get-buffer-process buffer))
(progn
(tempbuf-post-command)
(tempbuf-grace ct))
(let ((name (buffer-name buffer)))
(catch 'tempbuf-skip-kill
(run-hooks 'tempbuf-kill-hook)
(kill-buffer buffer))
(when tempbuf-kill-message
(unless (buffer-live-p buffer)
(funcall tempbuf-kill-message-function (format tempbuf-kill-message name)))))))))
(defun tempbuf-post-command ()
"Update `tempbuf-last-time'."
(setq tempbuf-last-time (current-time)))
(defun turn-on-tempbuf-mode ()
"Turn on tempbuf mode.
See also function `tempbuf-mode'."
(when (not tempbuf-timer)
(setq tempbuf-timer (run-at-time 15 15 'tempbuf-check-buffers)))
(setq tempbuf-activation-time (current-time))
(setq tempbuf-mode t)
(tempbuf-grace)
(add-hook 'post-command-hook 'tempbuf-post-command nil t)
(tempbuf-post-command)
(run-hooks 'tempbuf-mode-hook))
(defun turn-off-tempbuf-mode ()
"Turn off tempbuf mode.
See also function `tempbuf-mode'."
(setq tempbuf-mode nil))
(provide 'tempbuf)