ChatGPT解决这个技术问题 Extra ChatGPT

How do I rename an open file in Emacs?

Is there a way to rename an open file in Emacs? While I'm viewing it? Something like save-as, but the original one should go away.


e
e40

Yes, with dired mode, you can:

C-x d to open dired

RET to select directory of current file

C-x C-j (dired-jump to the name of the current file, in Dired)

R to rename the file (or dired-do-rename).

q to go back to the (renamed) file buffer

The rename is equivalent to a shell mv, but it will also update any open buffers, and unlike mv it will not change the access and modify times on the file in the filesystem.


That's not directly renaming the current file.
C-x b and you're back in the original buffer. You could write an Elisp function to do it, but I doubt you'll save many keystrokes with it.
Also, rather than C-x b, you can press C-x k to be back in the original buffer.
The C-x C-j is not bound by default for me. Doing M-x load-library RET dired-x RET first makes it bound.
Another alternative if C-x C-j is unbound is just do M-x dired-jump on the first time. It will automatically load dired-x (which will also cause C-x C-j to be defined from this point on).
J
Jim G

Just for completeness, since some folks may visit this page thinking they will get an answer for the "save as" feature of Emacs, that's C-x C-w for an open file.


C-x C-w or use the menu File > Save as...
Not quite "save as", since the file you are editing will still be the original one.
@asmeurer You are wrong! After saving, you will be editing the new file.
Again, why isn't there a feature to downvote comments?!
Perhaps asmeurer meant, "the file you [were] editing will still [exist]". Then, is that correct? I would check, but then, you cannot downvote my comment, hahaha.
V
Vladimir Panteleev

Try this function from Steve Yegge's .emacs:

;; source: http://steve.yegge.googlepages.com/my-dot-emacs-file
(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)))
    (if (not filename)
        (message "Buffer '%s' is not visiting a file!" name)
      (if (get-buffer new-name)
          (message "A buffer named '%s' already exists!" new-name)
        (progn
          (rename-file filename new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil))))))

Take a look at that page, there's another really useful related function there, called "move-buffer-file".


Note: this method is not compatible with (setq uniquify-buffer-name-style 'forward) meaning if you have buffer named users\index.html (because you already have another buffer for posts\index.html) the renaming will fail
(set-buffer-modified-p nil) seems unnecessary. If you called rename-file-and-buffer on a modified buffer and then attempted to kill it, it would happily do it without warning you about unsaved changes.
This function will also (somewhat annoyingly) ask you for a new name before checking whether the current buffer is associated with a file at all (in which case it aborts).
T
The Unfun Cat

My favorite is the one from Magnars (of emacs rocks screencasts fame.)

Unlike the other alternatives, you don't have to type the name out from scratch - you get the current name to modify.

(defun rename-current-buffer-file ()
  "Renames current buffer and file it is visiting."
  (interactive)
  (let* ((name (buffer-name))
        (filename (buffer-file-name))
        (basename (file-name-nondirectory filename)))
    (if (not (and filename (file-exists-p filename)))
        (error "Buffer '%s' is not visiting a file!" name)
      (let ((new-name (read-file-name "New name: " (file-name-directory filename) basename nil basename)))
        (if (get-buffer new-name)
            (error "A buffer named '%s' already exists!" new-name)
          (rename-file filename new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil)
          (message "File '%s' successfully renamed to '%s'"
                   name (file-name-nondirectory new-name)))))))

Thanks to James Yang for a correct version.


This is NOT working properly. This one adds a "/" at the end of file.
Thanks for posting that out for me. The SO way is to correct answers, not post new, slightly modified ones.
S
Shawn Hoover

Here's a more robust version adapted from stevey.

;; Originally from stevey, adapted to support moving to a new directory.
(defun rename-file-and-buffer (new-name)
  "Renames both current buffer and file it's visiting to NEW-NAME."
  (interactive
   (progn
     (if (not (buffer-file-name))
         (error "Buffer '%s' is not visiting a file!" (buffer-name)))
     ;; Disable ido auto merge since it too frequently jumps back to the original
     ;; file name if you pause while typing. Reenable with C-z C-z in the prompt.
     (let ((ido-auto-merge-work-directories-length -1))
       (list (read-file-name (format "Rename %s to: " (file-name-nondirectory
                                                       (buffer-file-name))))))))
  (if (equal new-name "")
      (error "Aborted rename"))
  (setq new-name (if (file-directory-p new-name)
                     (expand-file-name (file-name-nondirectory
                                        (buffer-file-name))
                                       new-name)
                   (expand-file-name new-name)))
  ;; Only rename if the file was saved before. Update the
  ;; buffer name and visited file in all cases.
  (if (file-exists-p (buffer-file-name))
      (rename-file (buffer-file-name) new-name 1))
  (let ((was-modified (buffer-modified-p)))
    ;; This also renames the buffer, and works with uniquify
    (set-visited-file-name new-name)
    (if was-modified
        (save-buffer)
      ;; Clear buffer-modified flag caused by set-visited-file-name
      (set-buffer-modified-p nil)))

  (setq default-directory (file-name-directory new-name))

  (message "Renamed to %s." new-name))

Nice one. Now chilling in my functions.el.
B
Bozhidar Batsov

Here's another version, that's pretty robust and VC aware:

(defun rename-file-and-buffer ()
  "Rename the current buffer and file it is visiting."
  (interactive)
  (let ((filename (buffer-file-name)))
    (if (not (and filename (file-exists-p filename)))
        (message "Buffer is not visiting a file!")
      (let ((new-name (read-file-name "New name: " filename)))
        (cond
         ((vc-backend filename) (vc-rename-file filename new-name))
         (t
          (rename-file filename new-name t)
          (set-visited-file-name new-name t t)))))))

You can read more about it here.


J
Jason Axelson

If you're using Spacemacs then you get this behavior for free since it comes with an implementation of rename-current-buffer-file (based on magnars) that by default bound to SPC-f-R.

https://github.com/syl20bnr/spacemacs/blob/bd7ef98e4c35fd87538dd2a81356cc83f5fd02f3/layers/%2Bdistributions/spacemacs-base/funcs.el#L294


I've just tested this in Doom Emacs (v27.1) and it works as well. Thank you.
R
Rafael Nascimento

There is a way very easy, you press the command M-x and than type vc-rename-file, after that you just need to select your current file at the directory, and than choose the new name. The buff that has the changed file will be refreshed.

Source:https://www.gnu.org/software/emacs/manual/html_node/emacs/VC-Delete_002fRename.html


This has the (not necessarily desirable) side effect of adding the old and new files to be committed into version control. But +1 because it uses a builtin function to (mostly) answer the OP's question.
Works even today with GNU Emacs 25.3.2 (x86_64-pc-linux-gnu, GTK+ Version 3.18.9)
Got this when I used vc-rename-file please update files before moving them`
J
Jay Rajput

Emacs 26.3 (2020-04-03) has rename-file function which can be invoked using M-x rename-file for renaming the current file or any other file for that matter.


In Emacs 25 this will not change the name of the file that is in buffer, i.e. the next write will go to the old-filename again.
J
James Yang

based on magnars version, I modified as below, fixed the INIT file name part:

(defun rename-current-buffer-file ()
  "Renames current buffer and file it is visiting."
  (interactive)
  (let* ((name (buffer-name))
        (filename (buffer-file-name))
        (basename (file-name-nondirectory filename)))
    (if (not (and filename (file-exists-p filename)))
        (error "Buffer '%s' is not visiting a file!" name)
      (let ((new-name (read-file-name "New name: " (file-name-directory filename) basename nil basename)))
        (if (get-buffer new-name)
            (error "A buffer named '%s' already exists!" new-name)
          (rename-file filename new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil)
          (message "File '%s' successfully renamed to '%s'"
                   name (file-name-nondirectory new-name)))))))

This one is better. The magnars version given above is NOT working properly. Add a "/" character at the end
A
Alan

The excellent crux package has crux-rename-file-and-buffer (along with many other useful functions).


E
Eric_Chen

It can be achieved by copy. shift+c on the file and emacs will ask you to denote a name for the path including the file name, so you just provide the new name,and enter...of course, you have to Delete the former one.


You seem to be referring to "C" in the dired mode? That's copying the file, not renaming it, which (as @ChrisConway noted) in dired is done with "R". And besides, OP asked for a rename of the current buffer.