I recently rediscovered on old post over at Daring Fireball where author John Gruber makes the following observation:
A feature I’ve always wanted, but which no application I’m aware of offers, is the ability to rename an open document. “Save As” is not the same thing — because while it allows you to save a document with a new name, it leaves behind the previously-saved copy of the file with the old name.
Gruber goes on to demonstrate how this can be achieved in BBEdit with some deftly written AppleScript. Of course, this got me wondering how to accomplish the same thing in Emacs. Here is what I discovered.
Running M-x rename-file on the current file will work as advertised, but will not refresh the buffer with the new file name. Instead, it simply writes a new file in the current directory. In that sense, it is like “Save As” Gruber describes above, but with the added annoyance of not making the renamed file the active document.
Dired is a better option:
- Type C-x d RET to enter Dired mode
- Move the point to the current file’s name
- Type R to rename the file
- Type C-x k RET to kill the Dired buffer and return to the current file
Sure enough, the current file exhibits the new name without leaving behind the previous copy with the old name. But this method is a bit too circuitous for my tastes, and smacks of the switch-to-the-Finder-to-rename routine Gruber wanted to avoid in the first place.
So in this case there is no substitute for a bit of custom Lisp code for our .emacs to do what we want:
(defun rename-current-file-or-buffer ()
(interactive)
(if (not (buffer-file-name))
(call-interactively 'rename-buffer)
(let ((file (buffer-file-name)))
(with-temp-buffer
(set-buffer (dired-noselect file))
(dired-do-rename)
(kill-buffer nil))))
nil)
(global-set-key "C-cR" 'rename-current-file-or-buffer)
Like Gruber’s ‘Rename Active Document’ script, the function above (so named to reflect Emacs parlance) allows for the renaming of buffers that have no associated file. When a buffer does have a file, a Dired routine similar to the one outlined above is performed, only programmatically and behind the scenes.