Kembalikan semua buffer terbuka (dan abaikan kesalahan)

12

Ketika mengerjakan proyek di bawah kontrol versi dengan git, saya sering ingin melakukan beberapa hal dalam shell yang memengaruhi banyak file terbuka saya, lalu kembalikan setiap buffer yang saya buka untuk memastikan bahwa saya tidak secara tidak sengaja merusak versi baru dengan apa pun yang saya buka. Saya tahu magitbisa membantu di sini, tapi saya terbiasa dengan alur kerja saya di shell dan saya ingin menyimpannya untuk saat ini. Jadi sebagai gantinya, saya ingin mengembalikan semua buffer yang terbuka, dan mungkin menutup semua yang sudah berhenti ada (misalnya karena git checkoutcabang yang tidak lagi memiliki file itu).

Saya memiliki cuplikan elisp berikut yang saya ambil dari pencarian 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"))

Tapi istirahat ini jika hits kesalahan dalam salah satu file yang terbuka saya, yaitu ketika mengembalikan B1, B2, B3, ..., Bnkesalahan saat mencoba untuk kembali B2mencegah B3- Bndari yang dikembalikan.

Bagaimana saya bisa memberitahu emacs untuk mengabaikan kesalahan yang muncul dalam kasus ini? Saya tidak ingin menggunakan global-auto-revert-modekarena setiap pengembalian memicu beberapa tugas berat seperti auto-complete dan sintaks pemeriksa ulang mengurai file, menggantung emacs selama satu detik atau lebih.

Patrick Collins
sumber
Jenis kesalahan apa yang mencegah pengembalian B2buffer pada contoh Anda. Saya menggunakan fungsi yang sangat mirip (kemungkinan besar berasal dari cuplikan ini) dan telah berfungsi dengan baik.
Kaushal Modi
@ Kaushal: sepertinya "file tidak ada lagi" yang dilakukan seseorang, dan / atau kesalahan dilemparkan oleh paket yang saya miliki yang menjalankan kembali buffer revert. Kebanyakan saya perhatikan bahwa setelah menjalankannya saya masih akan mendapatkan "File telah berubah sejak terakhir dikunjungi!" padaC-x s
Patrick Collins
"file no longer exists".. aha! versi saya memperbaiki itu :) Akan mempostingnya segera.
Kaushal Modi

Jawaban:

12

Asli

Ini cuplikan versi saya yang sedikit ditingkatkan dalam pertanyaan. Meninjau riwayat VC saya, saya mengonfirmasi bahwa cuplikan di bawah ini dimulai sebagai cuplikan yang diposting oleh OP. Jadi saya membayar atribut untuk itu.

Inilah kode yang stabil untuk saya:

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

Memperbarui

Berikut adalah versi yang lebih baik dan terdokumentasi dari atas setelah melihat solusi @ 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."))

Referensi

Kaushal Modi
sumber
5

Lain:

(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)))))))
Drew
sumber
Terima kasih. Saya mencuri dolistgaya untuk menggantikan cardan pop. Lucu bagaimana Anda dapat terus meningkatkan konfigurasi Anda saat Anda mempelajari lebih lanjut elisp :)
Kaushal Modi
@ KaushalModi Itu sebabnya saya mempostingnya, sebagian. ;-)
Drew
1

Saya menerima jawaban Kausal karena itu paling dekat dengan apa yang saya inginkan, tetapi saya mengambil bagian dari solusi Drew juga. Aku membungkus revert-bufferdi with-demoted-errorsdan menjatuhkan :preserve-modesparameter sehingga checker sintaks saya akan kembali mengurai semua file yang terbuka saya. Saya juga membiarkannya membunuh file yang dimodifikasi dan tidak dimodifikasi, karena saya sering mendapat masalah dengan secara tidak sengaja- C-x ssetelah git checkoutfile yang dimodifikasi terbuka.

Versi terakhir adalah:

(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
sumber
Menambahkan pesan kemajuan karena ini dapat muncul hang dengan banyak file terbuka: emacs.stackexchange.com/a/50730/2418
ideasman42
1

Saya akan memperbaikinya dengan condition-caseatau ignore-errors(docs di sini ) Saya tidak tahu persis apa yang ingin Anda lakukan ; jika Anda ingin melakukan sesuatu dengan kesalahan, jika Anda dapat menggunakan condition-caseuntuk menentukan hasilnya, atau Anda dapat menggunakan ignore-errorsuntuk melanjutkan saja. Sesuatu seperti:

(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
sumber
0

Berdasarkan jawaban @ Drew, dengan tambahan:

  • Pelaporan kemajuan (karena bisa lambat dengan banyak file terbuka) .
  • Hapus kondisi undo (dengan dukungan untuk paket yang memuat riwayat undo saat memuat ulang buffer - undo-fu-session untuk mis) .
(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))))))
gagasanman42
sumber