Rétablir tous les tampons ouverts (et ignorer les erreurs)

12

Lorsque je travaille sur un projet sous contrôle de version avec git, je veux souvent faire certaines choses dans un shell qui affectent un grand nombre de mes fichiers ouverts, puis rétablir tous les tampons que j'ai ouverts pour m'assurer que je n'encombre pas accidentellement la nouvelle version avec tout ce que j'avais ouvert. Je sais que cela magitpeut être utile ici, mais je suis habitué à mon flux de travail dans le shell et je voudrais le garder pour l'instant. Donc, à la place, j'aimerais rétablir tous les tampons ouverts, et peut-être fermer ceux qui ont cessé d'exister (par exemple à cause git checkoutd'une branche qui n'a plus ce fichier).

J'ai l'extrait de code Elisp suivant que j'ai extrait d'une recherche 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"))

Mais cette pause si elle frappe une erreur dans l' un de mes fichiers ouverts, à savoir quand revenir B1, B2, B3, ..., Bnune erreur en essayant de revenir B2empêche B3- Bnd'être inversé.

Comment puis-je dire à emacs d'ignorer les erreurs qui apparaissent dans ce cas? Je ne veux pas l'utiliser global-auto-revert-modeparce que chaque retour déclenche des tâches lourdes comme mon vérificateur de saisie automatique et de syntaxe réanalysant le fichier, suspendant emacs pendant une seconde environ.

Patrick Collins
la source
Quel type d'erreur empêche de rétablir le B2tampon dans votre exemple. J'utilise une fonction très similaire (très probablement dérivée de cet extrait) et cela a bien fonctionné.
Kaushal Modi
@Kaushal: il semble que le "fichier n'existe plus", et / ou les erreurs lancées par les packages que j'ai ré-exécuter la restauration du tampon. Généralement, j'ai remarqué qu'après l'avoir exécuté, j'obtiendrai toujours un "Fichier modifié depuis la dernière visite!" leC-x s
Patrick Collins
"file no longer exists".. aha! ma version corrige cela :) Je le publierai sous peu.
Kaushal Modi

Réponses:

12

Original

Voici ma version légèrement améliorée de l'extrait de code dans la question. En examinant mon historique VC, je confirme que l'extrait ci-dessous a commencé comme l'extrait publié par l'OP. Je paie donc cet attribut.

Voici le code qui a été stable pour moi:

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

Mise à jour

Voici une version améliorée et mieux documentée de ce qui précède après avoir examiné la solution 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."))

Référence

Kaushal Modi
la source
5

Un autre:

(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)))))))
A dessiné
la source
Merci. Je vole le doliststyle à remplacer caret pop. Amusant comment vous pouvez continuer à améliorer votre configuration à mesure que vous en apprenez plus. :)
Kaushal Modi
@KaushalModi C'est pourquoi je l'ai publié, en partie. ;-)
Drew
1

J'ai accepté la réponse de Kausal car elle était la plus proche de ce que je voulais, mais j'ai également saisi une partie de la solution de Drew. J'ai enveloppé revert-bufferdans with-demoted-errorset laissé tomber le :preserve-modesparamètre pour que mon vérificateur de syntaxe réanalysera tous mes fichiers ouverts. Je le laisse également tuer les fichiers modifiés ainsi que les non modifiés, car j'ai souvent des problèmes en accidentellement- C-x saprès un git checkoutfichier ouvert modifié.

La version finale est:

(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
la source
Ajout de messages de progression car cela peut apparaître se bloquer avec de nombreux fichiers ouverts: emacs.stackexchange.com/a/50730/2418
ideasman42
1

Je corrigerais cela avec un condition-caseou ignore-errors(docs ici ). Je ne sais pas exactement ce que vous voudrez qu'il fasse ; si vous voulez faire quelque chose avec des erreurs, si vous pouvez utiliser condition-casepour spécifier le résultat, ou vous pouvez utiliser ignore-errorspour continuer. Quelque chose comme:

(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
la source
0

Basé sur la réponse de @ Drew, avec des ajouts:

  • Rapports de progression (car il peut être lent avec de nombreux fichiers ouverts) .
  • Effacer l'état d'annulation (avec prise en charge des packages qui chargent l'historique d'annulation lors du rechargement du tampon - undo-fu-session par exemple) .
(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
la source