如何理解这里 interactive 的用法?

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

有两个问题:

  1. 这里 interactive 后面接的不是 “p” 这样的 code, 而是一个别的函数, 意义是什么? 对此我看了文档, 文档举了一个这样的例子:
For example, write
 (defun foo (arg buf) "Doc string" (interactive "P\nbbuffer: ") .... )
 to make ARG be the raw prefix argument, and set BUF to an existing buffer,
 when ‘foo’ is called as a command.

但我完全不理解因果关系.

  1. 函数有参数 new-file, 如果直接执行 M-x rename-file-and-buffer, 也不会出错, 但函数定义中有一行是:
(if (equal new-name "")
      (error "Aborted rename"))

按理说就应该出错. 为什么没出错呢? 怀疑与 interactive 这个问题有关.

你没往下看

     It may be a Lisp expression that is not a string; then it should be
     a form that is evaluated to get a list of arguments to pass to the
     command.  Usually this form will call various functions to read
     input from the user, most often through the minibuffer (*note
     Minibuffers::) or directly from the keyboard (*note Reading
     Input::).

如果现有的 code (如 “f”) 不能满足自己的需求,就可以自己写个传递参数的函数。

这里就是自己写了个传递文件名的函数

问题 2 是问题 1 的衍生

2 个赞

感谢大佬! 不过虽然问题解决了, 我的 C-h f 为什么和你不一样呢? 是这个函数的说明文档又更新了么?

c-h f 里只是简单的介绍用法, elisp info 文档里有更详细的说明

1 个赞