如何在退出递归编辑时抛出一个值(不使用setq)

时间:2013-11-24 17:12:32

标签: emacs elisp

在不使用setq退出递归编辑时是否可以抛出一个值?

以下函数与setq一起正常工作,但是,我的目标是消除不必要的全局变量(如果可能) - 尤其是文件名 - 并使用let绑定变量。但是,在退出递归编辑时抛出一个值的上下文中,我无法设计一个不使用全局变量作为文件名的方法。

在此示例中,我将lawlist-save-asdired-read-file-name结合使用以进入dired-mode并选择文件名或路径。按文件名上的回车键可选择文件。按目录名称上的回车键或两(2)个点向上移动一个目录。按下只有一(1)个点的行上的回车键意味着只选择当前目录中的路径。文件名或路径的值通过 lawlist-save-as 的最后一行传递回函数dired-read-file-name

[虽然这个例子不是必需的(但是如果有人好奇的话),使用(dired-get-marked-files)标记文件会被用于不同的情况,例如使用Wanderlust将多个文件附加到电子邮件中 ​​- 在这种情况下,我使用条件((listp lawlist-filename) (throw 'exit nil))。当然,打开文件是不言自明的 - 用于定期输入dired-mode然后只需按下要打开的文件上的返回键的情况。]

(require 'dired)
(defvar lawlist-filename nil)
(defvar save-as-buffer-filename nil)
(defvar save-as-variable nil)
(defvar dired-buffer-name nil)

(defun dired-read-file-name (&optional directory)
  (let ((working-buffer (buffer-name)))
    (if directory
      (dired directory)
    (dired nil))
    (let ((dired-buffer-name (buffer-name)))
      (if save-as-buffer-filename
        (progn
          (goto-char (point-min))
          (re-search-forward (file-name-nondirectory save-as-buffer-filename) nil t)))
      (recursive-edit)
      (kill-buffer dired-buffer-name)
      (switch-to-buffer working-buffer)
      lawlist-filename)))

;; select file or directory.
(define-key dired-mode-map (kbd "<return>") (lambda () (interactive)
  (setq lawlist-filename
    (if (or (re-search-backward "^*" nil t)
            (re-search-forward "^*" nil t))
      (dired-get-marked-files)
      (dired-get-file-for-visit)))
  (cond
    ((listp lawlist-filename)
      (throw 'exit nil))
    ;; open file
    ((and (not (file-directory-p lawlist-filename))
        (file-exists-p lawlist-filename)
        (not (equal lawlist-filename (concat (file-name-directory lawlist-filename) ".")))
        (not save-as-variable))
      (find-file lawlist-filename))
    ;; save-as
    ((and (not (file-directory-p lawlist-filename))
        (file-exists-p lawlist-filename)
        (not (equal lawlist-filename (concat (file-name-directory lawlist-filename) "."))))
      (throw 'exit nil))
    ;; go up one directory
    ((and (file-directory-p lawlist-filename)
        (not (equal lawlist-filename (concat (file-name-directory lawlist-filename) "."))))
      (setq dired-buffer-name (buffer-name))
      (dired-find-file)
      (goto-char (point-min))
      (re-search-forward " \\.\\.$" nil t)
      (kill-buffer dired-buffer-name)
      (setq dired-buffer-name (buffer-name)))
    ;; only use current path for save-as situation.
    ((and (equal lawlist-filename (concat (file-name-directory lawlist-filename) "."))
        save-as-variable)
      (setq lawlist-filename (expand-file-name default-directory))
      (throw 'exit nil)) )))

(defun lawlist-save-as ()
(interactive)
  (setq save-as-variable t)
  (if (buffer-file-name)
    (setq save-as-buffer-filename (buffer-file-name)))
  (let ((proposed-filename (dired-read-file-name)))
    (when proposed-filename ;; needed if aborting recursive-edit
      (setq save-as-variable nil)
      (let ((save-as-filename (read-string "Save-As:  "
        (concat proposed-filename (when (file-directory-p proposed-filename) (buffer-name))))))
      (setq save-as-buffer-filename nil)
      (when (and save-as-filename (file-exists-p save-as-filename))
        (or (y-or-n-p (format "File `%s' exists; overwrite? " save-as-filename))
          (error "Canceled")))
      (set-visited-file-name save-as-filename)
      (set-buffer-modified-p t)
      (and (buffer-file-name)
           (file-writable-p buffer-file-name)
           (setq buffer-read-only nil))
      (save-buffer)))))

1 个答案:

答案 0 :(得分:3)

recursive-edit只是一个运行命令循环的函数。适用于lisp的所有内容都适用。 因此,如果将recursive-edit封装到本地绑定某个变量的let中,比如test,如果在递归编辑会话期间setq这个变量,则此变量只是在本地设置在let

的范围内

实施例: 使用 C-x C-e

运行以下命令
(let (test)
    (recursive-edit)
    (message "test=%S" test))

您将处于递归编辑的命令循环中。然后通过 M - 设置test (setq test "That is my test.")。 然后按 M-C-c 退出递归编辑会话。 消息test=\"That is my test.\"将被打印出来,但符号test仍未绑定。