ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Emacs 中重命名打开的文件?

有没有办法在 Emacs 中重命名打开的文件?我在看的时候?类似另存为的东西,但原来的应该消失。


e
e40

是的,使用 dired 模式,您可以:

Cx d 打开dired

RET 选择当前文件的目录

Cx Cj (dired-跳转到当前文件的名称,在 Dired 中)

R 重命名文件(或 dired-do-rename)。

返回(重命名的)文件缓冲区

重命名等效于 shell mv,但它也会更新任何打开的缓冲区,并且与 mv 不同,它不会更改文件系统中文件的访问和修改时间。


这不是直接重命名当前文件。
Cx b 然后你又回到了原来的缓冲区。你可以编写一个 Elisp 函数来完成它,但我怀疑你会用它节省很多击键。
此外,您可以按 Cx k 以返回原始缓冲区,而不是 Cx b。
默认情况下,C-x C-j 对我没有约束。首先执行 M-x load-library RET dired-x RET 使其绑定。
如果 C-x C-j 未绑定,另一种选择是第一次执行 M-x dired-jump。它将自动加载 dired-x(这也将导致从此时开始定义 C-x C-j)。
J
Jim G

只是为了完整起见,因为有些人可能会访问此页面,以为他们会得到 Emacs 的“另存为”功能的答案,即打开文件的 Cx Cw。


Cx Cw 或使用菜单 File > Save as...
不完全是“另存为”,因为您正在编辑的文件仍将是原始文件。
@asmeurer 你错了!保存后,您将编辑新文件。
再说一次,为什么没有功能可以否决评论?!
也许 asmeurer 的意思是,“你[正在]编辑的文件仍然[存在]”。那么,这样对吗?我会检查,但是,你不能拒绝我的评论,哈哈哈。
V
Vladimir Panteleev

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))))))

看看那个页面,那里还有另一个非常有用的相关函数,叫做“move-buffer-file”。


注意:此方法与 (setq uniquify-buffer-name-style 'forward) 不兼容,这意味着如果您有名为 users\index.html 的缓冲区(因为您已有 posts\index.html 的另一个缓冲区)重命名将失败
(set-buffer-modified-p nil) 似乎没有必要。如果您在修改后的缓冲区上调用 rename-file-and-buffer 然后试图杀死它,它会很高兴地执行此操作,而不会警告您未保存的更改。
在检查当前缓冲区是否与文件相关联(在这种情况下它会中止)之前,此函数还会(有点烦人)询问您一个新名称。
T
The Unfun Cat

我最喜欢的是 Magnars 的那个(emacs rocks screencasts 成名。)

与其他替代方案不同,您不必从头开始输入名称 - 您可以获得当前名称来修改。

(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)))))))

感谢 James Yang 的正确版本。


这不能正常工作。这个在文件末尾添加了一个“/”。
感谢您为我发布。 SO 方法是更正答案,而不是发布新的、稍作修改的答案。
S
Shawn Hoover

这是改编自 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))

好东西。现在让我的functions.el感到不寒而栗。
B
Bozhidar Batsov

这是另一个版本,它非常健壮且具有 VC 意识:

(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)))))))

您可以阅读有关它的更多信息here


J
Jason Axelson

如果您使用 Spacemacs,那么您可以免费获得此行为,因为它附带了一个默认绑定到 SPC-f-Rrename-current-buffer-file(基于 magnars)的实现。

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


我刚刚在 Doom Emacs (v27.1) 中对此进行了测试,它也可以正常工作。谢谢你。
R
Rafael Nascimento

有一种方法很简单,你按下命令 Mx 然后输入 vc-rename-file ,然后你只需要在目录中选择你当前的文件,然后选择新的名称。已更改文件的 buff 将被刷新。

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


这具有将新旧文件添加到版本控制中的(不一定是可取的)副作用。但是 +1 因为它使用内置函数来(主要)回答 OP 的问题。
即使在今天也可以使用 GNU Emacs 25.3.2(x86_64-pc-linux-gnu,GTK+ 版本 3.18.9)
当我使用 vc-rename-file 时得到这个,请在移动文件之前更新文件`
J
Jay Rajput

Emacs 26.3 (2020-04-03) 具有 rename-file 函数,可以使用 M-x rename-file 调用该函数来重命名当前文件或任何其他文件。


在 Emacs 25 中,这不会更改缓冲区中文件的名称,即下一次写入将再次转到旧文件名。
J
James Yang

基于 magnars 版本,我修改如下,修复了 INIT 文件名部分:

(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)))))))

这个更好。上面给出的 magnars 版本不能正常工作。在末尾添加一个“/”字符
A
Alan

出色的 crux package 具有 crux-rename-file-and-buffer(以及许多其他有用的功能)。


E
Eric_Chen

它可以通过复制来实现。 shift+c 在文件上,emacs 会要求你为包含文件名的路径指定一个名称,所以你只需提供新名称,然后输入...当然,你必须删除前一个。


您似乎在 dired 模式下指的是“C”?那是复制文件,而不是重命名它,这(正如@ChrisConway 指出的)在 dired 中是用“R”完成的。此外,OP 要求重命名当前缓冲区。