* bug#424: new feature: killed-buffer
@ 2008-06-16 8:16 pierre kobylanski
2008-06-17 20:22 ` Stefan Monnier
2016-02-29 4:56 ` Lars Ingebrigtsen
0 siblings, 2 replies; 4+ messages in thread
From: pierre kobylanski @ 2008-06-16 8:16 UTC (permalink / raw)
To: bug-gnu-emacs
[-- Attachment #1: Type: text/plain, Size: 1960 bytes --]
Hi guys,
I'd like to propose a new feature for emacs: the capability to easily
re-open previously killed buffers. Maybe someone else would appreciate
it.
It proposes three user functions:
* killed-buffers-mode
Activate/Deactivate remembering killed buffers. To avoid tricky
cases, only buffer associated with an existing file are taken
into account.
* restore-last-killed-buffer
Restore previously killed buffer. The number of
killed buffers to remember is configurable.
Ex: (global-set-key [(meta f8 )] 'restore-last-killed-buffer)
* mouse-killed-buffers-menu
Display a menu with last killed buffers. A variable
controls the number of item displayed, to ensure a
reasonable size of the popup menu.
This function respects the `mouse-wheel-follow-mouse'
setting.
I attached a screen-shot of such a menu.
Ex: (global-set-key [C-S-down-mouse-4] 'mouse-killed-buffers-menu)
Compatibilities / Dependencies :
* require emacs 22.1 or above.
Indeed, I make use of the `push' macro and the `find-file-hook'
variables (that obsoletes `find-file-hooks' since v22.1).
* require file-marker.el
This is a second file, that defines the file-marker structure.
Basically, it looks like a marker: it handles a filename and a
point. But I can't use markers as they end with buffer death.
Maybe you'll be interested in integrating this in next emacs version.
But before that, my code has some bugs to correct:
- copyright
- autoload
- maybe find default keystrokes (that follow emacs spirit).
- in text-mode, what about `mouse-killed-buffers-menu' and graphical stuff ?
- when killed-buffer-mode is off, the list of killed buffers is not
updated, so that we can have a buffer opened that still appear in
the killed buffers list. Bug or functionality ?
Of course, you have my email for any questions/demands.
Thanks for your involvement and time,
Pierre
[-- Attachment #2: killed-buffers.el --]
[-- Type: application/octet-stream, Size: 13298 bytes --]
;;; killed-buffers.el Pierre Kobylanski
;;; v1.0.0 2008-06-16
;; DESCRIPTION
;; ===========
;;
;; Remember last killed buffers
;; (only those associated with an existing file).
;;
;;
;; DEPENDENCIES
;; ============
;;
;; * emacs 22.1 or above
;; >= 22 : push
;; >= 22.1: find-file-hook
;;
;; * file-marker.el
;;
;;
;; CONFIGURATION
;; =============
;; put in your .emacs
;;
;; ;; make emacs be able to find killed-buffers.el
;; ;; (let's assume that direcrory /home/me/emacs.aux/
;; ;; contains both files killed-buffers.el and file-marker.el)
;; (push "/home/me/emacs.aux" load-path)
;;
;; ;; killed-buffers: remember last killed buffers
;; (require 'killed-buffers nil t) ; do not fail on error
;;
;; ;; set up key/mouse bindings
;; (when (featurep 'killed-buffers)
;; (global-set-key [(meta f8)] 'restore-last-killed-buffer)
;; (global-set-key [C-S-down-mouse-4] 'mouse-killed-buffers-menu)
;; )
;;
;;
;; CUSTOMIZATION
;; =============
;;
;; M-x customize-group <RET> killed-buffers
(require 'file-marker)
;+------------------+
;| user functions |
;+------------------+
(defalias 'restore-last-killed-buffer 'kb-restore-killed-buffer)
(defalias 'mouse-killed-buffers-menu 'kb-mouse-killed-buffers-menu)
(defalias 'killed-buffers-mode 'kb-killed-buffers-mode)
;+-------------------+
;| group options |
;| (for customize) |
;+-------------------+
(defgroup killed-buffers nil
"Remember killed buffers."
:group 'convenience
;:prefix "kb-"
:version "22.1.1")
;; Supporting unlimited undo for killed buffers is nonsense.
(defcustom kb-max-visible-undo 10
"The maximum number of killed buffers to display in the mouse menu.
0 <= `kb-max-visible-undo' (0 means zero).
If `kb-max-visible-undo' >= `kb-max-memorized-undo', all killed
buffers will appear in the menu. In the other case, if the number
of killed buffer is higher than `kb-max-visible-undo', only the
last killed buffers will be shown."
:type 'integer
:set (lambda (symbol value) (when (>= value 0) (set symbol value)))
:require 'killed-buffers
:group 'killed-buffers)
(defcustom kb-max-memorized-undo 25
"The number of supported undo.
0 <= `kb-max-memorized-undo' (0 means zero)."
:type 'integer
:set (lambda (symbol value) (when (>= value 0) (set symbol value)))
:require 'killed-buffers
:group 'killed-buffers)
(defcustom kb-restore-point t
"When t, also restore the cursor position."
:type 'boolean
:require 'killed-buffers
:group 'killed-buffers)
;+----------------------+
;| internal variables |
;+----------------------+
(defvar kb-killed-buffers-list nil
"The list of killed buffers. Each element is a file-marker.
See function `file-marker'.")
;+-----------------------+
;| killed-buffers mode |
;+-----------------------+
(defvar kb-killed-buffers-mode-on t
"killed-buffers mode activation state.")
(defun kb-killed-buffers-mode ()
"Activate/Deactivate killed-buffers mode."
(interactive)
(setq kb-killed-buffers-mode-on (not kb-killed-buffers-mode-on))
(message "killed-buffers-mode mode is now %s."
(if kb-killed-buffers-mode-on "on" "off")))
;+---------+
;| hooks |
;+---------+
(defun kb-find-file-hook ()
"When opening a file, remove it from the `kb-killed-buffers-list'."
(when kb-killed-buffers-mode-on
(kb-remove-buffer-from-killed-list buffer-file-name)))
(defun kb-kill-buffer-hook ()
"When killing a buffer, save it into the `kb-killed-buffers-list'."
(when kb-killed-buffers-mode-on
(kb-add-buffer-to-killed-list (current-buffer))))
(add-hook 'find-file-hook 'kb-find-file-hook)
(add-hook 'kill-buffer-hook 'kb-kill-buffer-hook)
;+---------------------------+
;| finding a killed buffer |
;+---------------------------+
(defun kb-find-killed-buffer-index (killed-buffer)
"Find the index of a killed buffer in `kb-killed-buffers-list'.
If nil or not found, return nil.
Possible value/type for KILLED-BUFFER are:
nil --> not set (in this case, return nil)
`stringp' --> the killed buffer name
`bufferp' --> an existing buffer
`file-marker-p' --> a killed buffer
<other> --> signal an error"
(let ((killed-buffer-filename
(cond ((null killed-buffer) nil)
((file-marker-p killed-buffer) (file-marker-filename killed-buffer))
((bufferp killed-buffer) (kb-aux-buffer-filename-or-name killed-buffer))
((stringp killed-buffer) killed-buffer)
( t (error "\n\nError in killed-buffer.el: (kb-find-killed-buffer-index killed-buffer)\n KILLED-BUFFER type is \"%s\",\n but acceptable value/types are: nil, string, buffer, and file-marker\n" (prin1-to-string (type-of killed-buffer)))))))
(kb-aux-list-index-find
(lambda (killed-buffer)
(string-equal killed-buffer-filename
(file-marker-filename killed-buffer)))
kb-killed-buffers-list)))
;+--------------------------+
;| adding a killed-buffer |
;+--------------------------+
(defun kb-is-buffer-eligible-for-killed-list (buffer)
"Return t if BUFFER is eligible to be put in `kb-killed-buffers-list'.
BUFFER is eligible if it verifies the following conditions:
- BUFFER is an existing buffer
- BUFFER is associated with an existing file"
(and buffer
(bufferp buffer)
(let ((bfn (buffer-file-name buffer)))
(if bfn (file-exists-p bfn) nil))))
(defun kb-add-buffer-to-killed-list (buffer)
"If BUFFER is eligible, save it into the `kb-killed-buffers-list'."
(when (and ; BUFFER is eligible and not already in kb-killed-buffers-list
(kb-is-buffer-eligible-for-killed-list buffer)
(not (kb-find-killed-buffer-index buffer)))
(let ((fn (kb-aux-buffer-filename-or-name buffer))
(pt (save-excursion (set-buffer buffer) (point))))
;; don't let the list grow indefinitely
(kb-aux-list-ntrunc-inplace
(1- kb-max-memorized-undo) kb-killed-buffers-list)
;; put the killed buffer into the list
(push (file-marker fn pt) kb-killed-buffers-list))))
;+----------------------------+
;| removing a killed-buffer |
;+----------------------------+
(defun kb-remove-buffer-from-killed-list (killed-buffer)
"Remove a killed buffer from `kb-killed-buffers-list',
and return the corresponding file-marker.
See `kb-find-killed-buffer-index' for accepted values of KILLED-BUFFER."
(let ((n (kb-find-killed-buffer-index killed-buffer)))
(when n (prog1
(nth n kb-killed-buffers-list)
(setq kb-killed-buffers-list
(kb-aux-list-nth-remove n kb-killed-buffers-list))))))
;+-----------------------------+
;| restoring a killed-buffer |
;+-----------------------------+
(defun kb-restore-killed-buffer (&optional killed-buffer)
"Restore a killed buffer.
If KILLED-BUFFER is nil, restore the last killed one."
(interactive)
;; restore last one if killed-buffer not specified
(when (null killed-buffer)
(if kb-killed-buffers-list
(setq killed-buffer (nth 0 kb-killed-buffers-list))
(message "%s" "no killed buffers")))
(when killed-buffer
(let ((buf-filename (file-marker-filename killed-buffer))
(buf-point (file-marker-point killed-buffer)))
(find-file buf-filename)
(kb-remove-buffer-from-killed-list buf-filename)
(when (and buf-point kb-restore-point) (goto-char buf-point)))))
;+--------------+
;| mouse menu |
;+--------------+
(defun kb-mouse-killed-buffers-menu (event)
"Display a popup menu with last killed buffers. Re-open the selected one."
(interactive "e")
(if kb-killed-buffers-list
(progn
(mouse-minibuffer-check event)
(let* ((buf-name-max-len 0)
(names (mapcar (lambda (buf)
(let* ((buf-filename (file-marker-filename buf))
(buf-name (file-name-nondirectory buf-filename))
(buf-dir (file-name-directory buf-filename)))
(let ((home-dir (getenv-internal "HOME")))
(if (string-match (concat "^\\(" home-dir "\\)/") buf-dir)
(setq buf-dir
(concat "~" (substring buf-dir (match-end 1))))))
(setq buf-name-max-len (max buf-name-max-len (length buf-name)))
`(,buf-name ,buf-dir ,buf-filename)))
kb-killed-buffers-list))
(buf-name-max-len (min 25 buf-name-max-len))
(buf-dir-max-len 35)
(strs (mapcar (lambda (x)
(let* ((buf-name (car x))
(buf-dir (cadr x))
(buf-name-len (length buf-name)))
(if (> buf-name-len buf-name-max-len)
(kb-aux-string-see-end (car (cddr x))
(+ buf-name-max-len 3 buf-dir-max-len))
(concat
buf-name
(make-string (- buf-name-max-len buf-name-len) ? )
" "
(kb-aux-string-see-end buf-dir buf-dir-max-len))))) names))
(strs (kb-aux-list-ntrunc-inplace kb-max-visible-undo strs))
(menu (kb-aux-simple-1level-x-popup-menu "killed buffers" strs)))
(let ((n (x-popup-menu event menu)))
(when n
(with-selected-window ; respect mouse-wheel-follow-mouse settings
(if (and (boundp 'mouse-wheel-follow-mouse)
mouse-wheel-follow-mouse)
(mwheel-event-window event) (selected-window))
(kb-restore-killed-buffer (nth n kb-killed-buffers-list)))))))
(message "%s" "no killed buffers")))
;+-----------------------+
;| auxiliary functions |
;+-----------------------+
(defun kb-aux-string-see-end (str &optional maxlen)
"`killed-buffers-mode' auxillary function.
Cut the beginning of the string STR if too long.
If MAXLEN is nil, use the current frame width."
(let ((maxlen (cond ((or (null maxlen) (< maxlen 0)) (frame-width))
( t maxlen)))
(len (length str)))
(if (and (> len maxlen) (> maxlen 3))
(concat "..." (substring str (- len maxlen -3)))
str)))
(defun kb-aux-list-index-find (p l)
"`killed-buffers-mode' auxiliary function.
Find the index of the first element of list L that satisfies predicate P."
(let ((len (length l)) (i 0) (ll l))
(while (and (< i len) (not (funcall p (pop ll)))) (setq i (1+ i)))
(if (< i len) i nil)))
(defun kb-aux-list-ntrunc-inplace (n l)
"`killed-buffers-mode' auxiliary function.
Trunc list L to at most N elements (modify the list)."
(let ((len (length l)))
(if (and (> n 0) (< n len)) (setcdr (nthcdr (1- n) l) nil)))
l)
(defun kb-aux-list-nth-remove (n l)
"`killed-buffers-mode' auxiliary function.
Remove the Nth element of list L (do *not* modify the list)."
(let ((len (length l)))
(append (butlast l (- len n)) (last l (- len (1+ n))))))
(defun kb-aux-is-special-buffer (buffer)
"Return t if buffer is a special buffer."
(and
(null (buffer-file-name buffer))
(kb-aux-is-special-buffer-name (buffer-name buffer))))
(defun kb-aux-is-special-buffer-name (buffer-name)
"Return t if buffer-name is standard name for a special buffer.
Special buffers names usually start with a space (' ') or a star ('*')."
(let ((c (elt buffer-name 0)))
(or (= c 32) (= c 42)))) ; SPC or '*'
(defun kb-aux-buffer-filename-or-name (buffer)
"Return buffer-file-name if non nil, buffer-name otherwise."
(let ((bfn (buffer-file-name buffer)))
(if bfn bfn (buffer-name buffer))))
(defun kb-aux-simple-1level-x-popup-menu (title l)
"`killed-buffers-mode' auxiliary function.
Create a one level popup menu, to use in conjunction with `x-popup-menu'.
It returns
- the rank of the item you've selected (beginning at 0),
- or nil if the menu is empty or if nothing has been selected.
TITLE: menu title
L : items list"
(let* ((menu nil) (rl (reverse l)) (len (length rl)) (i len))
(while (>= (setq i (1- i)) 0)
(setq menu (cons (cons (pop rl) i) menu)))
(setq menu (cons "dummy" menu))
(setq menu (cons title (list menu)))
menu))
(provide 'killed-buffers)
;;; killed-buffers.el ends here
[-- Attachment #3: file-marker.el --]
[-- Type: application/octet-stream, Size: 2589 bytes --]
;;; file-marker.el Pierre Kobylanski
;;; v1.0.0 2008-06-15
;; DESCRIPTION
;; ===========
;;
;; A file-marker is a data structure to remember
;; a filename and a point (the cursor position in
;; that file).
;;
;; Unlike a marker, it survives to a buffer death.
(defun file-marker-p (obj)
"Return t if OBJ is a file-marker."
(and ; A file-marker is
(vectorp obj) ; a vector `v'
(= 3 (length obj)) ; - of length 3
(eq 'file-marker (elt obj 0)) ; - with v[0] = 'file-marker
(let ((fn (elt obj 1)) (pt (elt obj 2))) ; - and v[1] the filename, and v[2]
(and (stringp fn) (or (null pt) (integerp pt)))))) ; the cursor position
(defun check-file-marker-p (obj)
"Signal an error if OBJ is not a file-marker."
(when (not (file-marker-p obj))
(error "Not a file-marker (is %s)" (prin1-to-string (type-of obj)))))
(defun file-marker (&optional obj point)
"Construct a file-marker.
OBJ is either a buffer, a marker, a string, or nil (means current buffer).
It is then transformed into a string.
POINT is an integer or nil."
(let* (
(buffer (cond
((bufferp obj) obj)
((markerp obj) (let ((buf (marker-buffer obj)))
(if buf buf
(error "Marker doesn't refer to an existing buffer")) ))
((stringp obj) nil) ; very special value
((null obj) (current-buffer))
( t (error "Bad type %s" (prin1-to-string (type-of obj)))) ))
(filename (if (null buffer) ; very special value => (stringp obj)
obj
(let ((fn (buffer-file-name buffer)))
(if fn fn (buffer-name) ))))
(point (cond (point point)
(buffer (save-excursion (set-buffer buffer) (point)))
(t nil)))
)
`[file-marker ,filename ,point]))
(defun file-marker-filename (file-marker)
"Return the filename of the file-marker.
FILE-MARKER must be a file-marker. An error is signaled otherwise."
(check-file-marker-p file-marker)
(elt file-marker 1))
(defun file-marker-point (file-marker)
"Return the point of the file-marker. Can be nil.
FILE-MARKER must be a file-marker. An error is signaled otherwise."
(check-file-marker-p file-marker)
(elt file-marker 2))
(provide 'file-marker)
;;; file-marker.el ends here
[-- Attachment #4: killed-buffers.png --]
[-- Type: image/png, Size: 4138 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
* bug#424: new feature: killed-buffer
2008-06-16 8:16 bug#424: new feature: killed-buffer pierre kobylanski
@ 2008-06-17 20:22 ` Stefan Monnier
2017-03-14 4:50 ` npostavs
2016-02-29 4:56 ` Lars Ingebrigtsen
1 sibling, 1 reply; 4+ messages in thread
From: Stefan Monnier @ 2008-06-17 20:22 UTC (permalink / raw)
To: pierre kobylanski; +Cc: 424, bug-gnu-emacs
> I'd like to propose a new feature for emacs: the capability to easily
> re-open previously killed buffers. Maybe someone else would appreciate it.
Could this be integrated with recentf, maybe?
> * require file-marker.el
> This is a second file, that defines the file-marker structure.
> Basically, it looks like a marker: it handles a filename and a
> point. But I can't use markers as they end with buffer death.
Why not use a bookmark?
Stefan
^ permalink raw reply [flat|nested] 4+ messages in thread
* bug#424: new feature: killed-buffer
2008-06-16 8:16 bug#424: new feature: killed-buffer pierre kobylanski
2008-06-17 20:22 ` Stefan Monnier
@ 2016-02-29 4:56 ` Lars Ingebrigtsen
1 sibling, 0 replies; 4+ messages in thread
From: Lars Ingebrigtsen @ 2016-02-29 4:56 UTC (permalink / raw)
To: pierre kobylanski; +Cc: 424
"pierre kobylanski" <pierre.kobylanski@gmail.com> writes:
> I'd like to propose a new feature for emacs: the capability to easily
> re-open previously killed buffers. Maybe someone else would appreciate
> it.
>
> It proposes three user functions:
>
> * killed-buffers-mode
> Activate/Deactivate remembering killed buffers. To avoid tricky
> cases, only buffer associated with an existing file are taken
> into account.
>
> * restore-last-killed-buffer
> Restore previously killed buffer. The number of
> killed buffers to remember is configurable.
> Ex: (global-set-key [(meta f8 )] 'restore-last-killed-buffer)
[...]
> * require file-marker.el
> This is a second file, that defines the file-marker structure.
> Basically, it looks like a marker: it handles a filename and a
> point. But I can't use markers as they end with buffer death.
This is perhaps covered by the desktop package (along with recentf) now?
I mean, especially the file marker stuff. But perhaps desktop doesn't
keep track of killed buffers?
--
(domestic pets only, the antidote for overdose, milk.)
bloggy blog: http://lars.ingebrigtsen.no
^ permalink raw reply [flat|nested] 4+ messages in thread
* bug#424: new feature: killed-buffer
2008-06-17 20:22 ` Stefan Monnier
@ 2017-03-14 4:50 ` npostavs
0 siblings, 0 replies; 4+ messages in thread
From: npostavs @ 2017-03-14 4:50 UTC (permalink / raw)
To: Stefan Monnier; +Cc: pierre.kobylanski, 424
tags 424 wontfix
close 424
quit
Stefan Monnier <monnier@iro.umontreal.ca> writes:
>> * require file-marker.el
>> This is a second file, that defines the file-marker structure.
>> Basically, it looks like a marker: it handles a filename and a
>> point. But I can't use markers as they end with buffer death.
>
> Why not use a bookmark?
bojohan+news@dd.chalmers.se (Johan "Bockgård)" writes:
> Stefan Monnier <monnier@iro.umontreal.ca> writes:
>
>>> I'd like to propose a new feature for emacs: the capability to easily
>>> re-open previously killed buffers. Maybe someone else would appreciate it.
>>
>> Could this be integrated with recentf, maybe?
>
> Cf. iswitchb-use-virtual-buffers.
Lars Ingebrigtsen <larsi@gnus.org> writes:
> "pierre kobylanski" <pierre.kobylanski@gmail.com> writes:
>
>> * require file-marker.el
>> This is a second file, that defines the file-marker structure.
>> Basically, it looks like a marker: it handles a filename and a
>> point. But I can't use markers as they end with buffer death.
>
> This is perhaps covered by the desktop package (along with recentf) now?
> I mean, especially the file marker stuff. But perhaps desktop doesn't
> keep track of killed buffers?
Since the OP never responded to any of the comments on their patch, I'm
closing this.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2017-03-14 4:50 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-16 8:16 bug#424: new feature: killed-buffer pierre kobylanski
2008-06-17 20:22 ` Stefan Monnier
2017-03-14 4:50 ` npostavs
2016-02-29 4:56 ` Lars Ingebrigtsen
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.