Revierta todos los búferes abiertos (e ignore los errores)

12

Cuando trabajo en un proyecto bajo control de versiones con git, a menudo quiero hacer algunas cosas en un shell que afectan a muchos de mis archivos abiertos, y luego revertir cada búfer que tengo abierto para asegurarme de no bloquear accidentalmente la nueva versión con lo que tenga abierto. Sé que magitpuede ser útil aquí, pero estoy acostumbrado a mi flujo de trabajo en el shell y me gustaría mantenerlo por ahora. Entonces, en cambio, me gustaría revertir todos los búferes abiertos, y tal vez cerrar cualquiera que haya dejado de existir (por ejemplo, debido a git checkoutuna rama que ya no tiene ese archivo).

Tengo el siguiente fragmento de elisp que tomé de una búsqueda en 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"))

Pero esto rompe si se realiza un error en una de mis archivos abiertos, es decir, cuando se restituyan B1, B2, B3, ..., Bnun error al tratar de revertir B2previene B3- Bnse revirtió a partir.

¿Cómo puedo decirle a emacs que ignore cualquier error que aparezca en este caso? No quiero usar global-auto-revert-modeporque cada reversión desencadena algunas cosas pesadas como mi autocompletado y el verificador de sintaxis que vuelven a analizar el archivo, colgando emacs por un segundo más o menos.

Patrick Collins
fuente
¿Qué tipo de error impide revertir el B2búfer en su ejemplo? Utilizo una función muy similar (muy probablemente derivada de este fragmento) y ha funcionado bien.
Kaushal Modi
@Kaushal: parece que el "archivo ya no existe" y los errores arrojados por los paquetes que tengo vuelven a ejecutar el búfer revertido. Principalmente me he dado cuenta de que después de ejecutarlo, todavía obtendré un "¡El archivo ha cambiado desde la última vez que lo visité! enC-x s
Patrick Collins
"file no longer exists".. aha! mi versión corrige eso :) Lo publicaré en breve.
Kaushal Modi

Respuestas:

12

Original

Aquí está mi versión ligeramente mejorada del fragmento en la pregunta. Al revisar mi historial de VC, confirmo que el fragmento a continuación comenzó como el fragmento publicado por el OP. Así que le pago el atributo a eso.

Aquí está el código que ha sido estable para mí:

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

Actualizar

Aquí hay una versión mejorada y mejor documentada de lo anterior después de ver la solución de @ 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."))

Referencia

Kaushal Modi
fuente
5

Otro:

(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)))))))
Dibujó
fuente
Gracias. Estoy robando el dolistestilo para reemplazar cary pop. Es curioso cómo puedes seguir mejorando tu configuración a medida que aprendes más elisp :)
Kaushal Modi
@KaushalModi Por eso lo publiqué, en parte. ;-)
Dibujó el
1

Acepté la respuesta de Kausal ya que era la más cercana a lo que quería, pero también tomé parte de la solución de Drew. Me envolvió revert-bufferen with-demoted-errorsy dejó caer el :preserve-modesparámetro para que mi comprobador de sintaxis sería volver a analizar todos los archivos abiertos. También dejé que elimine archivos modificados y no modificados, ya que a menudo me meto en problemas accidentalmente C-x sdespués de git checkoutabrir un archivo modificado.

La versión final es:

(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
fuente
Se agregaron mensajes de progreso ya que esto puede aparecer bloqueado
ideasman42
1

Solucionaría esto con un condition-caseo ignore-errors(documentos aquí ). No sé exactamente qué querrás que haga ; si desea hacer algo con errores, si puede usar condition-casepara especificar el resultado, o puede usar ignore-errorspara continuar. Algo como:

(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"))
Estufa de gas
fuente
0

Basado en la respuesta de @ Drew, con adiciones:

  • Informe de progreso (ya que puede ser lento con muchos archivos abiertos) .
  • Borre el estado de deshacer (con soporte para paquetes que cargan el historial de deshacer al recargar el búfer, por ejemplo , deshacer-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
fuente