Отменить все открытые буферы (и игнорировать ошибки)

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: кажется, что «файл больше не существует», который делают, и / или ошибки, сгенерированные пакетами, которые я имею, повторно запускают буфер. В основном я заметил, что после запуска я все равно получу «Файл изменился с момента последнего посещения!» наC-x s
Патрик Коллинз
"file no longer exists".. ага! моя версия исправляет это :) Будет опубликовано в ближайшее время.
Каушал Моди

Ответы:

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

Обновить

Вот улучшенный и лучше документированы версия выше, посмотрев на @ Дрю решения .

(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выбирая после 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
ideasman42
1

Я бы исправил это с помощью condition-caseили ignore-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"))
Gastove
источник
0

На основании ответа @ Дрю, с дополнениями:

  • Отчет о прогрессе (поскольку он может быть медленным с открытым количеством файлов) .
  • Очистить состояние отмены (с поддержкой пакетов, которые загружают историю отмен при перезагрузке буфера - например, 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))))))
ideasman42
источник