Відновити всі відкриті буфери (і ігнорувати помилки)


12

Працюючи над проектом під контролем версій з git, я часто хочу робити якісь речі в оболонці, які впливають на багато моїх відкритих файлів, а потім повернути кожен буфер, який я відкрив, щоб переконатися, що я випадково не клобую нову версію з чим би я не відкрився. Я знаю, що тут magitможе бути корисним, але я звик до свого робочого процесу в оболонці і хотів би зберегти його поки. Тому замість цього я хотів би відновити всі відкриті буфери та, можливо, закрити будь-які, які припинили існування (наприклад, через git checkoutгілку, у якої цього файлу більше немає).

У мене є такий фрагмент elisp, який я взяв із пошуку в Google:

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

Але це ламається , якщо він потрапить помилку в одному з моїх відкритих файлів, тобто , коли повертаючись B1, B2, B3, ..., Bnпомилка при спробі відновити B2запобігає B3- Bnвід того відкинуті.

Як я можу сказати emacs ігнорувати будь-які помилки, що виникають у цьому випадку? Я не хочу використовувати, global-auto-revert-modeтому що кожне відновлення запускає такі важкі речі, як моя автозаповнення та перевірка синтаксису, повторно розбирає файл, вивішуючи emacs на секунду або близько того.


Яка помилка запобігає поверненню B2буфера у вашому прикладі. Я використовую дуже схожу функцію (швидше за все, отримана з цього фрагмента), і вона спрацювала чудово.
Каушал Моді

@Kaushal: схоже, що "файл більше не існує", і / або помилки, викинуті пакунками, я повертаю повторно запущений буфер. Переважно я помітив, що після його запуску я все одно отримаю "Файл змінився з моменту останнього відвідування!" onC-x s
Патрік Коллінз

"file no longer exists".. ага! моя версія виправляє це :) Опублікуємо її найближчим часом.
Каушал Моді

Відповіді:


12

Оригінал

Ось моя дещо вдосконалена версія фрагмента у питанні. Переглядаючи мою історію ВК, я підтверджую, що фрагмент нижче розпочався як фрагмент, розміщений ОП. Тож я приділяю цьому атрибут.

Ось стабільний для мене код:

(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

@KaushalModi Ось чому я і розмістив його частково. ;-)
Дрю

1

Я прийняв відповідь Кауза, оскільки вона була найближчою до того, що я хотів, але і я схопив частину рішення Дрю. Я загорнув revert-bufferв with-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."))

Додано повідомлення про хід роботи, оскільки це може з’явитися вивішеним
Ideman42

1

Я б виправив це за допомогою ( condition-caseабо тут ). Я точно не знаю, що ви хочете, щоб це робилося ; якщо ви хочете зробити щось з помилками, якщо ви можете вказати результат, або ви можете просто продовжити. Щось на зразок:ignore-errorscondition-caseignore-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

На основі відповіді @ Дрю з доповненнями:

  • Звіт про хід роботи (оскільки він може бути повільним при відкритті багатьох файлів) .
  • Очистити стан скасування (з підтримкою пакетів, які завантажують історію скасування при перезавантаженні буфера - скасувати фу-сесію для напр.) .
(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))))))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.