恢复所有打开的缓冲区(并忽略错误)


12

当使用git进行版本控制下的项目时,我经常想在shell中做一些会影响许多打开文件的事情,然后还原我打开的每个缓冲区,以确保不会意外破坏新版本无论我打开什么。我知道magit在这里可能会有所帮助,但是我已经习惯了外壳中的工作流程,并且现在想保留它。因此,相反,我想还原所有打开的缓冲区,并可能关闭任何已停止存在的缓冲区(例如,由于git checkout分支的a不再具有该文件)。

我从Google搜索中抓取了以下elisp片段:

(defun revert-all-buffers ()
  "Refreshes all open buffers from their respective files"
  (interactive)
  (let* ((list (buffer-list))
         (buffer (car list)))
    (while buffer
      (when (and (buffer-file-name buffer) 
                 (not (buffer-modified-p buffer)))
        (set-buffer buffer)
        (revert-buffer t t t))
      (setq list (cdr list))
      (setq buffer (car list))))
  (message "Refreshed open files"))

但是,如果它击中这打破了我打开的文件之一,即复原的情况出现错误B1B2B3,...,Bn试图还原一个错误B2阻止B3- Bn被恢复。

在这种情况下,如何告诉emacs忽略出现的任何错误?我不想使用,global-auto-revert-mode因为每次还原都会触发一些繁重的工作,例如我的自动完成功能和语法检查器会重新解析文件,将emacs挂一秒钟左右。


哪种错误会阻止B2您的示例还原缓冲区。我使用了非常相似的功能(很可能是从此代码片段派生的),并且运行良好。
Kaushal Modi

@Kaushal:好像“文件不再存在”,和/或我重新运行缓冲区还原的程序包引发的错误。通常,我已经注意到,运行它后,我仍然会收到“自上次访问以来文件已更改!” 上C-x s
Patrick Collins

"file no longer exists"..啊哈!我的版本修复了该问题:)将很快发布。
Kaushal Modi

Answers:


12

原版的

这是我对问题代码段的改进。查看我的VC历史记录,我确认以下片段作为OP发布的片段开始。所以我确实为此付出了代价。

这是对我稳定的代码:

(defun modi/revert-all-file-buffers ()
  "Refresh all open buffers from their respective files."
  (interactive)
  (let* ((list (buffer-list))
         (buffer (car list)))
    (while buffer
      (let ((filename (buffer-file-name buffer)))
        ;; Revert only buffers containing files, which are not modified;
        ;; do not try to revert non-file buffers like *Messages*.
        (when (and filename
                   (not (buffer-modified-p buffer)))
          (if (file-exists-p filename)
              ;; If the file exists, revert the buffer.
              (with-current-buffer buffer
                (revert-buffer :ignore-auto :noconfirm :preserve-modes))
            ;; If the file doesn't exist, kill the buffer.
            (let (kill-buffer-query-functions) ; No query done when killing buffer
              (kill-buffer buffer)
              (message "Killed non-existing file buffer: %s" filename)))))
      (setq buffer (pop list)))
    (message "Finished reverting buffers containing unmodified files.")))

更新资料

在查看@Drew的 解决方案之后,这是上述内容的改进版本和更好文档版本。

(defun modi/revert-all-file-buffers ()
  "Refresh all open file buffers without confirmation.
Buffers in modified (not yet saved) state in emacs will not be reverted. They
will be reverted though if they were modified outside emacs.
Buffers visiting files which do not exist any more or are no longer readable
will be killed."
  (interactive)
  (dolist (buf (buffer-list))
    (let ((filename (buffer-file-name buf)))
      ;; Revert only buffers containing files, which are not modified;
      ;; do not try to revert non-file buffers like *Messages*.
      (when (and filename
                 (not (buffer-modified-p buf)))
        (if (file-readable-p filename)
            ;; If the file exists and is readable, revert the buffer.
            (with-current-buffer buf
              (revert-buffer :ignore-auto :noconfirm :preserve-modes))
          ;; Otherwise, kill the buffer.
          (let (kill-buffer-query-functions) ; No query done when killing buffer
            (kill-buffer buf)
            (message "Killed non-existing/unreadable file buffer: %s" filename))))))
  (message "Finished reverting buffers containing unmodified files."))

参考


5

另一个:

(defun revert-all-no-confirm ()
  "Revert all file buffers, without confirmation.
Buffers visiting files that no longer exist are ignored.
Files that are not readable (including do not exist) are ignored.
Other errors while reverting a buffer are reported only as messages."
  (interactive)
  (let (file)
    (dolist (buf  (buffer-list))
      (setq file  (buffer-file-name buf))
      (when (and file  (file-readable-p file))
        (with-current-buffer buf
          (with-demoted-errors "Error: %S" (revert-buffer t t)))))))

谢谢。我正在窃取dolist替换car和替换的样式pop。有趣的是,当您学习更多elisp时,如何继续改进您的配置:)
Kaushal Modi 2016年

@KaushalModi这就是我部分发布它的原因。;-)
提请

1

我接受了Kausal的回答,因为它最接近我想要的答案,但是我也抓住了Drew解决方案的一部分。我裹revert-bufferwith-demoted-errors和下降的:preserve-modes参数,以便我的语法检查器将重新解析所有的我打开的文件。我也让它杀了修改的文件和未修改的,因为我经常遇到麻烦的意外C-x s后-ing git checkout用修改后的文件打开。

最终版本是:

(defun revert-all-buffers ()
  "Refresh all open buffers from their respective files."
  (interactive)
  (let* ((list (buffer-list))
         (buffer (car list)))
    (while buffer
      (let ((filename (buffer-file-name buffer)))
        ;; Revert only buffers containing files, which are not modified;
        ;; do not try to revert non-file buffers like *Messages*.
        (when filename
          (if (file-exists-p filename)
              ;; If the file exists, revert the buffer.
              (with-demoted-errors "Error: %S"
                (with-current-buffer buffer
                  (revert-buffer :ignore-auto :noconfirm)))
            ;; If the file doesn't exist, kill the buffer.
            (let (kill-buffer-query-functions) ; No query done when killing buffer
              (kill-buffer buffer)
              (message "Killed non-existing file buffer: %s" buffer))))
        (setq buffer (pop list)))))
  (message "Finished reverting non-file buffers."))

添加了进度消息,因为它可能会因许多文件打开而挂起:emacs.stackexchange.com/a/50730/2418
ideaman42

1

我会用condition-caseignore-errors此处的文档)修复此问题。我不确切知道你想要它做什么 ; 如果您想做一些有错误的事情,可以使用它condition-case来指定结果,或者可以使用ignore-errors继续。就像是:

(defun revert-all-buffers ()
  "Refreshes all open buffers from their respective files"
  (interactive)
  (let* ((list (buffer-list))
         (buffer (car list)))
    (while buffer
      (when (and (buffer-file-name buffer) 
                 (not (buffer-modified-p buffer)))
        (set-buffer buffer)
        (ignore-errors (revert-buffer t t t)))
      (setq list (cdr list))
      (setq buffer (car list))))
  (message "Refreshed open files"))

0

基于@Drew的答案,并附带以下内容:

  • 进度报告(因为打开许多文件可能很慢)
  • 清除撤消状态(例如,在重新加载buffer- undo-fu-session时支持加载撤消历史记录的软件包)
(defun revert-all-buffers ()
  "Refresh all open buffers from their respective files.

Buffers which no longer exist are closed.

This can be useful when updating or checking out branches outside of Emacs."
  (interactive)
  (let* ((filename-and-buffer-list ;; Pairs of '(filename . buf)'.
          (let ((temp-list nil))
            (dolist (buf (buffer-list))
              (let ((filename (buffer-file-name buf)))
                (when filename
                  (push (cons filename buf) temp-list))))
            temp-list))

         (count (length filename-and-buffer-list))
         (count-final 0)
         (count-close 0)
         (count-error 0)
         ;; Keep text at a fixed width when redrawing.
         (format-count
          (format "%%%dd" (length (number-to-string count))))
         (format-text
          (concat "Reverting [" format-count " of " format-count "] %3d%%: %s"))
         (index 1))

    (message "Begin reverting %d buffers..." count)
    (while filename-and-buffer-list
      (pcase-let ((`(,filename . ,buf) (pop filename-and-buffer-list)))
        ;; Revert only buffers containing files, which are not modified;
        ;; do not try to revert non-file buffers such as '*Messages*'.
        (message format-text
                 index count (round (* 100 (/ (float index) count))) filename)
        (if (file-exists-p filename)
            ;; If the file exists, revert the buffer.
            (if (with-demoted-errors "Error: %S"
                  (with-current-buffer buf
                    (let ((no-undo (eq buffer-undo-list t)))

                      ;; Disable during revert.
                      (unless no-undo
                        (setq buffer-undo-list t)
                        (setq pending-undo-list nil))

                      (unwind-protect
                          (revert-buffer :ignore-auto :noconfirm)

                        ;; Enable again (always run).
                        (unless no-undo
                          ;; It's possible a plugin loads undo data from disk,
                          ;; check if this is still unset.
                          (when (and (eq buffer-undo-list t)
                                     (null pending-undo-list))
                            (setq buffer-undo-list nil))))))
                  t)
                (setq count-final (1+ count-final))
              (setq count-error (1+ count-error)))

          ;; If the file doesn't exist, kill the buffer.
          (let (kill-buffer-query-functions) ;; No query done when killing buffer.
            (message "Closing non-existing file buffer: %s" buf)
            (kill-buffer buf)
            (setq count-close (1+ count-close))))
        (setq index (1+ index))))
    (message
     (concat
      "Finished Revert All: " (format "%d buffer(s)" count-final)
      (if (zerop count-close)
          ""
        (format ", %d closed" count-close))
      (if (zerop count-error)
          ""
        (format ", %d error (see message buffer)" count-error))))))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.