As a disclaimer, this is the first function I have EVER written in Lisp or ELisp. Please, bash it as much as you would any other piece, but don't judge me for it please! :)
So, I realized I wanted a way to rename files without all the hassle of reopening the file etc. I googled and found a function at https://stackoverflow.com/questions/384284/can-i-rename-an-open-file-in-emacs (credit Steve Yegge).
However, it did not seem to work well with uniquify
. Which was a problem. Having two files open and trying to rename the first one to the same name as the second one (for example CMakeLists.txt, which there might be many of) caused it to bail since it only checked the name of the buffer (which wasn't unique yet, as uniquify doesn't do it's thing before something needs uniquifying).
The only way I found to force a "re-uniquification" was to close the previous buffer and open the new file. This seems very inelegant...
(defun is-unique-name-for-buffer (new-name)
(let ((file-name (buffer-file-name))
(dir-name (file-name-directory buffer-file-name)))
(let ((new-complete-name (concat dir-name new-name)))
(progn (not (string-equal file-name new-complete-name))))))
(defun rename-file-and-buffer (new-name)
"Renames both current buffer and file it's visiting to NEW-NAME."
(interactive "sNew name: ")
(let ((name (buffer-name))
(filename (buffer-file-name))
(dir-name (file-name-directory (buffer-file-name))))
(if (not filename)
(message "Buffer '%s' is not visiting a file!" name)
(if (not (is-unique-name-for-buffer new-name))
(message "A buffer named '%s' already exists in that location!" new-name)
(progn
(rename-file (file-name-nondirectory filename) new-name 1)
(kill-buffer)
(set-buffer (find-file (concat dir-name new-name))))))))
1 Answer 1
Judgment circuits deactivated; you have nothing to fear.
I was going to suggest that there must already be a function that you can call to force uniquify
a buffer, but M-x apropos RET ^uniq
doesn't show any documented functions that would do that for you (take a look and try a few out if you like, you may find a more elegant solution).
Lisp (actually, Elisp and Common Lisp; Scheme does its own thing) convention is to end predicate functions with -p
or p
rather than begin them with is-
.
(defun unique-name-for-buffer-p (new-name)
(let ((file-name (buffer-file-name))
(dir-name (file-name-directory buffer-file-name)))
(let ((new-complete-name (concat dir-name new-name)))
(progn (not (string-equal file-name new-complete-name))))))
Your predicate seems to be nesting let
s only because you need new-complete-name
to refer to dir-name
. When you're in a situation like that, you can instead use let*
. It's the same as let
except that its clauses are guaranteed to be evaluated in order (it's still good style to use let
when you can, but one let*
is better than many nested let
s).
(let* ((file-name (buffer-file-name))
(dir-name (file-name-directory buffer-file-name))
(new-complete-name (concat dir-name new-name)))
(progn (not (string-equal file-name new-complete-name))))
You only need explicit progn
when you want to evaluate multiple expressions as one (you use it correctly in your nested if
s, but read on).
(defun unique-name-for-buffer-p (new-name)
(let* ((file-name (buffer-file-name))
(dir-name (file-name-directory buffer-file-name))
(new-complete-name (concat dir-name new-name)))
(not (string-equal file-name new-complete-name))))
The nested if
s in your second function can be expressed as a single cond
(which also gets rid of that otherwise legitimate progn
).
(defun rename-file-and-buffer (new-name)
"Renames both current buffer and file it's visiting to NEW-NAME."
(interactive "sNew name: ")
(let ((name (buffer-name))
(filename (buffer-file-name))
(dir-name (file-name-directory (buffer-file-name))))
(cond ((not filename)
(message "Buffer '%s' is not visiting a file!" name))
((not (unique-name-for-buffer-p new-name))
(message "A buffer named '%s' already exists in that location!" new-name))
(t (rename-file (file-name-nondirectory filename) new-name 1)
(kill-buffer)
(set-buffer (find-file (concat dir-name new-name)))))))
concat
to build absolute file names. Useexpand-file-name
:(expand-file-name dir-name new-name)
. That way, Emacs handles file names for different systems automatically. \$\endgroup\$find-file-noselect
, notfind-file
. \$\endgroup\$