Ripristina tutti i buffer aperti (e ignora gli errori)


12

Quando lavoro su un progetto sotto controllo di versione con git, spesso voglio fare alcune cose in una shell che interessano molti dei miei file aperti, quindi ripristinare tutti i buffer che ho aperto per assicurarmi di non bloccare accidentalmente la nuova versione con tutto ciò che avevo aperto. So che magitpuò essere utile qui, ma sono abituato al mio flusso di lavoro nella shell e per ora vorrei mantenerlo. Quindi, invece, vorrei ripristinare tutti i buffer aperti e forse chiudere quelli che hanno smesso di esistere (ad esempio a causa git checkoutdi un ramo di un file che non ha più quel file).

Ho il seguente frammento di elisp che ho preso da una ricerca su 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"))

Ma questo si rompe se colpisce un errore in uno dei miei file aperti, cioè quando tornando B1, B2, B3, ..., Bnun errore durante il tentativo di tornare B2impedisce B3- Bndi essere ripristinato.

Come posso dire a emacs di ignorare gli errori che compaiono in questo caso? Non voglio usarlo global-auto-revert-modeperché ogni ripristino innesca alcune cose pesanti come il mio controllo automatico e il sintassi che ri-analizza il file, sospendendo emacs per un secondo circa.


Che tipo di errore impedisce di ripristinare il B2buffer nel tuo esempio. Uso una funzione molto simile (molto probabilmente derivata da questo frammento) e ha funzionato bene.
Kaushal Modi,

@Kaushal: sembra che il "file non esista più" si faccia, e / o errori generati dai pacchetti che hanno quel buffer di re-run ripristinato. Per lo più ho notato che dopo averlo eseguito riceverò ancora un "File è cambiato dall'ultima visita!" onC-x s
Patrick Collins,

"file no longer exists".. aha! la mia versione lo risolve :) Lo pubblicherò a breve.
Kaushal Modi,

Risposte:


12

Originale

Ecco la mia versione leggermente migliorata dello snippet nella domanda. Esaminando la mia cronologia VC, confermo che lo snippet di seguito è iniziato come snippet pubblicato dall'OP. Quindi pago questo attributo.

Ecco il codice che è stato stabile per me:

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

Aggiornare

Ecco una versione migliorata e meglio documentata di cui sopra dopo aver visto la soluzione di @ 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."))

Riferimento


5

Un altro:

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

Grazie. Sto rubando lo doliststile da sostituire care pop. Divertente come puoi continuare a migliorare la tua configurazione mentre impari di più elisp :)
Kaushal Modi

@KaushalModi Ecco perché l'ho pubblicato, in parte. ;-)
Estratto l'

1

Ho accettato la risposta di Kausal poiché era la più vicina a quello che volevo, ma ho anche preso parte della soluzione di Drew. Ho avvolto revert-bufferin with-demoted-errorse lasciò cadere il :preserve-modesparametro in modo che il mio controllo della sintassi sarebbe ri-analizzare tutti i miei file aperti. Ho anche lasciato che uccidesse i file modificati e non modificati, poiché spesso mi trovo nei guai accidentalmente C-x sdopo aver git checkoutaperto un file modificato.

La versione finale è:

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

Aggiunti messaggi di avanzamento poiché questo può apparire bloccato
ideasman42

1

Lo risolverei con un condition-caseo ignore-errors(documenti qui ). Non so esattamente cosa vorresti che facesse ; se vuoi fare qualcosa con errori, se puoi usare condition-caseper specificare il risultato o puoi ignore-errorssemplicemente continuare. Qualcosa di simile a:

(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

Basato sulla risposta di @ Drew, con aggiunte:

  • Relazione sull'andamento dei lavori (dal momento che può essere lento con molti file aperti) .
  • Cancella lo stato di annullamento (con supporto per i pacchetti che caricano la cronologia degli annullamenti durante il ricaricamento del buffer, ad esempio 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))))))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.