Cofnij wszystkie otwarte bufory (i ignoruj ​​błędy)

12

Podczas pracy nad projektem pod kontrolą wersji za pomocą git często chcę robić pewne rzeczy w powłoce, które wpływają na wiele moich otwartych plików, a następnie odwracać każdy otwarty bufor, aby upewnić się, że przypadkowo nie zablokuję nowej wersji z tym, co miałem otwarte. Wiem, że magitmoże być tutaj pomocna, ale przyzwyczaiłem się do mojego przepływu pracy w powłoce i na razie chciałbym ją zatrzymać. Zamiast tego chciałbym cofnąć wszystkie otwarte bufory i być może zamknąć te, które przestały istnieć (np. Z powodu git checkoutgałęzi, która nie ma już tego pliku).

Mam następujący fragment elisp, który pobrałem z wyszukiwarki 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"))

Ale to przerwy, jeśli natrafi błąd w jednym z moich otwartych plików, to znaczy, kiedy powraca B1, B2, B3, ..., Bnbłąd, starając się przywrócić B2zapobiega B3- Bnprzed powrócił.

Jak mogę nakazać emacsowi zignorowanie błędów, które pojawią się w tym przypadku? Nie chcę tego używać, global-auto-revert-modeponieważ każde przywracanie uruchamia niektóre ciężkie rzeczy, takie jak automatyczne uzupełnianie i moduł sprawdzania składni, ponownie analizujący plik i zawieszający emacsa na sekundę.

Patrick Collins
źródło
Jaki błąd uniemożliwia przywrócenie B2bufora w twoim przykładzie. Używam bardzo podobnej funkcji (najprawdopodobniej pochodzącej z tego fragmentu) i działa dobrze.
Kaushal Modi
@Kaushal: wygląda na to, że „plik już nie istnieje” i / lub błędy zgłaszane przez pakiety, które mam, ponownie uruchamiają bufor. Zazwyczaj zauważyłem, że po uruchomieniu nadal pojawia się komunikat „Plik zmienił się od ostatniej wizyty!” onC-x s
Patrick Collins,
"file no longer exists".. Aha! moja wersja to naprawia :) Wkrótce opublikuję.
Kaushal Modi

Odpowiedzi:

12

Oryginalny

Oto moja nieco ulepszona wersja fragmentu w pytaniu. Przeglądając moją historię VC, potwierdzam, że poniższy fragment zaczął się jako fragment opublikowany przez OP. Płacę więc temu atrybutowi.

Oto stabilny dla mnie kod:

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

Aktualizacja

Oto ulepszona i lepiej udokumentowana wersja powyższego, po zapoznaniu się z rozwiązaniem @ 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."))

Odniesienie

Kaushal Modi
źródło
5

Inne:

(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)))))))
Rysował
źródło
Dzięki. Kradnę doliststyl, aby zastąpić cari pop. Zabawne, jak możesz ulepszać konfigurację, gdy uczysz się więcej elisp :)
Kaushal Modi
@KaushalModi Właśnie dlatego opublikowałem to częściowo. ;-)
Drew
1

Zaakceptowałem odpowiedź Kausala, ponieważ była ona najbliższa temu, czego chciałem, ale też skorzystałem z części rozwiązania Drew. I zawinięte revert-bufferw with-demoted-errorsi upuścił :preserve-modesparametr tak, że mój sprawdzania składni będzie ponownie przeanalizować wszystkie moje otwartych plików. Pozwalam również zabijać zarówno zmodyfikowane pliki, jak i niezmodyfikowane, ponieważ często mam problemy z przypadkowym C-x suruchomieniem po git checkoutotwarciu zmodyfikowanego pliku.

Ostateczna wersja to:

(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."))
Patrick Collins
źródło
Dodano komunikaty o postępach, ponieważ może się to zawiesić przy otwartych wielu plikach: emacs.stackexchange.com/a/50730/2418
ideasman42
1

Naprawiłbym to za pomocą condition-caselub ignore-errors(dokumenty tutaj ). Nie wiem dokładnie, co będziemy chcieli go zrobić ; jeśli chcesz zrobić coś z błędami, możesz użyć, condition-caseaby określić wynik lub możesz użyć, ignore-errorsaby kontynuować. Coś jak:

(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
źródło
0

Na podstawie odpowiedzi @ Drew z dodatkami:

  • Raportowanie postępów (ponieważ może być powolne przy otwartych wielu plikach) .
  • Wyczyść stan cofania (z obsługą pakietów, które ładują historię cofania podczas ponownego ładowania bufora - na przykład cofnij sesję fu ) .
(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
źródło