elisp: Handling wrong-type-argument exception - lisp

I want to kill all buffers visiting a file in a given directory, keeping all others. However, my function bombs out when it reaches the #<buffer *Minibuf-1*>; the minibuffer isn't visiting a file.
(defun my-kill-all-visiting-buffers (dir)
"Kill all buffers visiting DIR."
(interactive)
(mapcar
(lambda (buf)
(and (string-match-p
(regexp-quote dir)
(file-name-directory (buffer-file-name buf)))
(kill-buffer buf)))
(buffer-list)))
In Python-land, I would ask for forgiveness and wrap it in a try-except. How would I handle this in the land of lisp?

You could handle the exception with something like condition-case which elisp has. Or you could just do something like this (avoiding the regexp-quotery as well):
(defun my-kill-all-visiting-buffers (dir)
"Kill all buffers visiting DIR or any subdirectory of DIR"
(interactive "Ddirectory: ")
(mapc
(lambda (buf)
(let ((bfn (buffer-file-name buf)))
(when (and (not (null bfn))
(file-in-directory-p bfn dir))
(kill-buffer buf))))
(buffer-list)))
There are probably better approaches.
Notes
(not (null x)) is the same as x but I use it intentionally so when I read it I know I am checking for x not being a conceptually void value, rather than it just being false.
This avoids regexp operations (originally string= now string-prefix-p), because, famously:
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
Instead it uses file-in-directory-p which asks exactly the question it wants the answer to: is file in a directory or any subdirectory of dir? (Thanks to Stefan for this, which I didn't know about.)
All of the above is legal in Emacs 26.2: I don't keep track of which things in elisp changed & arrived when any more.
Elisp is not that close to many other dialects of Lisp in use other than rather superficially: techniques which are used in elisp may or may not apply to other languages in the family but often don't.

I found that it wasn't necessary to catch all possible errors. Handling the case where a buffer is not associated with a file is sufficient.
The use of regexp-quote is used to handle closing files in subdirectories.
(defun my-kill-all-visiting-buffers (dir)
"Kill all buffers visiting DIR."
(interactive)
(mapcar
(lambda (buf)
(let ((bfn (buffer-file-name buf)))
(and (not (null bfn))
(string-match-p (regexp-quote dir) (file-name-directory bfn))
(kill-buffer buf))))
(buffer-list)))

Related

Idiomatic way serialization in emacs lisp

Currently I am working on a elisp major mode that makes uses of hashtables across sessions. So every time the major mode is initialized, the tables are loaded into memory. During and at the end of the session they are written to a file. My current implementation writes the data in the following way:
(with-temp-buffer
(prin1 hash-table (current-buffer))
(write-file ("path/to/file.el"))))
Loading the data at the beginning of the session is done via read and is something like this:
(setq name-of-table (car
(read-from-string
(with-temp-buffer
(insert-file-contents path-of-file)
(buffer-substring-no-properties
(point-min)
(point-max))))))))
It works but I have the feeling that this is not the most beautiful way to do it. My aim is: I want this major mode to turn into a nice clean package that stores it's own data in the folder where the other data of the package is stored.
This is how I implement
Write to file:
(defun my-write (file data)
(with-temp-file file
(prin1 data (current-buffer))))
Read from file:
(defun my-read (file symbol)
(when (boundp symbol)
(with-temp-buffer
(insert-file-contents file)
(goto-char (point-min))
(set symbol (read (current-buffer))))))
Call to write:
(my-write "~/test.txt" emacs-version)
Call to read
(my-read "~/test.txt" 'my-emacs-version)
I came up with the following solution, inspired by the first answer here:
(with-temp-buffer
(insert "(setq hash-table ")
(prin1 hash-table (current-buffer)
(insert ")")
(write-file (locate-library "my-data-lib"))
And during the init of the major mode, I just do:
(load "my-data-lib")
No need for and read-operation and the plus is that I also don't need to give any filepath, just the fact that there is such a file somewhere on the load-path is enough. Emacs will find it.
elisp rocks. :)
The package desktop can do it for you:
(require 'desktop)
(unless (memq 'hash-table desktop-globals-to-save)
(nconc desktop-globals-to-save (list 'hash-table)))`

Running keep-lines command on all opened buffers in emacs

I have many files opened in emacs buffers. Now I want to run keep-lines command on all this opened buffer.
What is steps for doing this?
I am using emacs version 24.3.
Iterate over all opened buffers: Use dolist to iterate over buffer-list (or any suitable subset you like). Use with-current-buffer for the current iteration's buffer, and call keep-lines inside that.
You can tweak it to have more flexibility (e.g., in the keep-lines call):
(defun foo (regexp)
(interactive (list (read-regexp "Regexp: ")))
(dolist (buf (buffer-list))
(goto-char (point-min)) ; Is this what you want, to start at bob?
(with-current-buffer buf (keep-lines regexp))))
However, buffer-list returns many buffers that you probably do not want to do this to. Filter it as needed, e.g., using remove-if (in cl-lib.el) or whatever.
If you deal only with file buffers, then you might want to use find-file-noselect. There are multiple possibilities, depending on what you really need.
You can practice with something like this:
(defun foo (regexp)
(interactive (list (read-regexp "Regexp: ")))
(dolist (buf (buffer-list))
(with-current-buffer buf
(when (y-or-n-p (format "Do it to `%s'? " buf))
(goto-char (point-min))
(keep-lines regexp)))))
And then you might want to think about whether you also want to save the modified buffer, etc.
For a more manually-controlled approach, you might use ibuffer:
M-x ibuffer RET
Mark some buffers
E (keep-lines "pattern") RET
Or, as per Drew's suggestion, (progn (goto-char (point-min)) (keep-lines "pattern"))
You can mark buffers using lots of different criteria in ibuffer. Browse the "Mark" menu, and see C-hm for details.

dired-x: How to set "Kill buffer of ..., too?" to yes without confirmation?

If you delete a file foo in dired-x, you get asked Kill buffer of foo, too?. How can I skip this question and always answer it with yes?
You can advise the dired-delete-entry function so that any file buffers are closed before the deletion:
(defadvice dired-delete-entry (before force-clean-up-buffers (file) activate)
(kill-buffer (get-file-buffer file)))
The Elisp manual describes advising as "cleaner than redefining the whole function" and it is less likely to break if the definition of the function changes in the future.
Simply redefine dired-clean-up-after-deletion in dired-x.el.
;; redefine the definition in dired-x.el, so that we are not prompted
;; to remove buffers that were associated with deleted
;; files/directories
(eval-after-load "dired-x" '(defun dired-clean-up-after-deletion (fn)
"My. Clean up after a deleted file or directory FN.
Remove expanded subdir of deleted dir, if any."
(save-excursion (and (cdr dired-subdir-alist)
(dired-goto-subdir fn)
(dired-kill-subdir)))
;; Offer to kill buffer of deleted file FN.
(if dired-clean-up-buffers-too
(progn
(let ((buf (get-file-buffer fn)))
(and buf
(save-excursion ; you never know where kill-buffer leaves you
(kill-buffer buf))))
(let ((buf-list (dired-buffers-for-dir (expand-file-name fn)))
(buf nil))
(and buf-list
(while buf-list
(save-excursion (kill-buffer (car buf-list)))
(setq buf-list (cdr buf-list)))))))
;; Anything else?
))
Other answers are stale, Since 26.1 Emacs provide a option to skip confirmation
(setq dired-clean-confirm-killing-deleted-buffers nil)

.emacs.desktop file destroyed by custom function

I use the Emacs desktop module to save my open buffers between sessions. However I found that this accumulates more buffers than I want, so I wrote a small function to clean up the buffer list immediately before saving to the desktop file. This works as expected, but for strange reasons the .emacs.desktop gets scrambled occasionally, i.e. it contains a part of another buffer at its start, then the intended contents and then the result of the other buffer. I don't have the slightest idea, why this happens. Here is an excerpt from my .emacs file:
(defun kill-old-buffers ()
"Kill buffers from end of buffer list (not used recently) until no more than 50 buffers are left. Remove temporary buffers first"
(interactive)
(let* (desktop-buffer (current-buffer))
(dolist (buffer (buffer-list))
(if (or (string-match "^\*" (buffer-name buffer)) (string-match "\.hpp$" (buffer-name buffer)))
(kill-buffer buffer)
)
)
(setq all-buffers (reverse (buffer-list)))
(while (> (safe-length all-buffers) 50)
(setq buffer (pop all-buffers))
(if (not (string-equal (buffer-name buffer) (buffer-name (current-buffer))))
(kill-buffer buffer)
)
)
(switch-to-buffer desktop-buffer)
))
;; Kill old rarely-used buffers before saving
(add-hook 'desktop-save-hook
'(lambda () (kill-old-buffers)))
Any help would be appreciated.
I'm not sure if your function is really the cause of your problem. If it should be the case, the wrong usage of the let* that scottfrazer pointed out might be the cause. But you don't even need that let* (and switch-to-buffer) at all, because
what you're trying to do is better done with Emacs' built in save-excursion, and
you aren't actually switching the buffer ever.
OTOH, you should have used let instead of the setqs in the lower half of your function, because setq will otherwise change a variable from an enclosing lexical scope. In this case you might very well have stomped over a buffer variable from the function that's executing the desktop-save-hook which is another potential cause of your problem.
But you don't need those lets either because you can do the second loop with another dolist. You can get rid of those first 50 buffers that you don't want to loop over with nthcdr.
Here's my improved version of kill-old-buffers:
(defun kill-old-buffers ()
"Kill buffers from end of buffer list (not used recently) until
no more than 50 buffers are left. Remove temporary buffers first."
(interactive)
(save-excursion
(dolist (buffer (buffer-list))
(if (or (string-match "^\*" (buffer-name buffer))
(string-match "\.hpp$" (buffer-name buffer)))
(kill-buffer buffer)))
(dolist (buffer (reverse (nthcdr 50 (buffer-list))))
(unless (eq buffer (current-buffer))
(kill-buffer buffer)))))
It may not fix all your problems, but for starters you need another set of parens around the variable in your let* statement
(let* ((desktop-buffer (current-buffer)))

Close all buffers besides the current one in Emacs

How do I close all but the current buffer in Emacs? Similar to "Close other tabs" feature in modern web browsers?
For a more manual approach, you can list all buffers with C-x C-b, mark buffers in the list for deletion with d, and then use x to remove them.
I also recommend replacing list-buffers with the more advanced ibuffer: (global-set-key (kbd "C-x C-b") 'ibuffer). The above will work with ibuffer, but you could also do this:
m (mark the buffer you want to keep)
t (toggle marks)
D (kill all marked buffers)
I also use this snippet from the Emacs Wiki, which would further streamline this manual approach:
;; Ensure ibuffer opens with point at the current buffer's entry.
(defadvice ibuffer
(around ibuffer-point-to-most-recent) ()
"Open ibuffer with cursor pointed to most recent buffer name."
(let ((recent-buffer-name (buffer-name)))
ad-do-it
(ibuffer-jump-to-buffer recent-buffer-name)))
(ad-activate 'ibuffer)
From EmacsWiki: Killing Buffers:
(defun kill-other-buffers ()
"Kill all other buffers."
(interactive)
(mapc 'kill-buffer
(delq (current-buffer)
(remove-if-not 'buffer-file-name (buffer-list)))))
Edit: updated with feedback from Gilles
There isn't a way directly in emacs to do this.
You could write a function to do this. The following will close all the buffers:
(defun close-all-buffers ()
(interactive)
(mapc 'kill-buffer (buffer-list)))
There is a built in command m-x kill-some-buffers (I'm using 24.3.50) In my nextstep gui (not tried in a terminal but sure it's similar) you can then approve which buffers to kill.
(defun only-current-buffer ()
(interactive)
(let ((tobe-killed (cdr (buffer-list (current-buffer)))))
(while tobe-killed
(kill-buffer (car tobe-killed))
(setq tobe-killed (cdr tobe-killed)))))
It works as you expected.
And after reading #Starkey's answer, I think this will be better:
(defun only-current-buffer ()
(interactive)
(mapc 'kill-buffer (cdr (buffer-list (current-buffer)))))
(buffer-list (current-buffer)) will return a list that contains all the existing buffers, with the current buffer at the head of the list.
This is my first answer on StackOverflow. Hope it helps :)
I found this solution to be the simplest one. This deletes every buffer except the current one. You have to add this code to your .emacs file
(defun kill-other-buffers ()
"Kill all other buffers."
(interactive)
(mapc 'kill-buffer (delq (current-buffer) (buffer-list))))
Of course, then you use it with M-x kill-other-buffers RET or you paste the following code in the .emacs file too and then just press C-xC-b
(global-set-key (kbd "C-x C-b") 'kill-other-buffers)
You can like this one as well - kill all buffers except current one, *Messages* and *scratch* (which are handy to have, I call them "toolkit"), close redundant windows as well, living you which one window which current buffer.
(defun my/kill-all-buffers-except-toolbox ()
"Kill all buffers except current one and toolkit (*Messages*, *scratch*). Close other windows."
(interactive)
(mapc 'kill-buffer (remove-if
(lambda (x)
(or
(eq x (current-buffer))
(member (buffer-name x) '("*Messages*" "*scratch*"))))
(buffer-list)))
(delete-other-windows))
I've use crux-kill-other-buffers for some months.
But I want dired buffers get deleted too. #Euge's and #wenjun.yan's answers solve this. But it will delete special buffers (e.g *git-credential-cache--daemon*, *scratch*, helm operation, and etc). So I came up with this (current) solution.
(defun aza-kill-other-buffers ()
"Kill all buffers but current buffer and special buffers"
(interactive)
(dolist (buffer (delq (current-buffer) (buffer-list)))
(let ((name (buffer-name buffer)))
(when (and name (not (string-equal name ""))
(/= (aref name 0) ?\s)
(string-match "^[^\*]" name))
(funcall 'kill-buffer buffer)))))
Inspired from kill-matching-buffers. You can add more condition on other buffer-name to exclude, if you want to.
Hope it helps :)
I've used one of the solutions in this list for years, but now I have a new one of my own.
(defun kill-all-file-buffers ()
"Kills all buffers that are open to files. Does not kill
modified buffers or special buffers."
(interactive)
(mapc 'kill-buffer (cl-loop for buffer being the buffers
when (and (buffer-file-name buffer)
(not (buffer-modified-p buffer)))
unless (eq buffer (current-buffer))
collect buffer)))
cl-loop has buffers built in as a collection that you can iterate over. It gives you a chance to parse out anything you don't want to close. Here, I've made sure that it doesn't close anything you've modified, and it uses buffer-file-name instead of just buffer-name so it doesn't kill special buffers. I also added an 'unless' to take out the current buffer (though you could obviously add it to the 'when', I just thought this was clearer).
But for an even more generic solution, we can define this as a macro, and pass in a function that will apply to all these buffers.
(defmacro operate-on-file-buffers (func)
"Takes any function that takes a single buffer as an argument
and applies that to all open file buffers that haven't been
modified, and aren't the current one."
`(mapc ,func (cl-loop for buffer being the buffers
when (and (buffer-file-name buffer)
(not (buffer-modified-p buffer)))
unless (eq buffer (current-buffer))
collect buffer)))
Now if you want to kill all buffers that match this, you can call it like this
(operate-on-file-buffers 'kill-buffer)
This is what you want:
C-x 1
source: https://blasphemousbits.wordpress.com/2007/05/04/learning-emacs-part-4-buffers-windows-and-frames/