Bagaimana menangani kesalahan dengan anggun dalam file init

20

Saya ingin cara untuk menangkap kesalahan ketika menjalankan file init saya, dan kemudian menanganinya dengan anggun. Banyak kustomisasi dan keybindings terpenting saya muncul di akhir file init saya untuk memastikan bahwa pengaturan lain tidak diterapkan di atasnya. Masalahnya adalah ketika inisialisasi dibatalkan lebih awal, saya merasa benar-benar lumpuh mencoba men-debug masalah tanpa ikatan kunci dan pengaturan yang saya kenal sedang diterapkan.

Apakah ada cara untuk menyelesaikan proses inisialisasi dengan anggun ketika kesalahan terjadi?

nispio
sumber

Jawaban:

9

Dua pilihan, yang keduanya tidak sempurna, muncul di pikiran. Pertama, Anda dapat membungkus sebagian besar kode inisialisasi awal Anda (yaitu, sebelum sampai ke kustomisasi Anda) di (ignore-errors ...). Jika ada yang kesalahan, namun, tidak akan ada banyak masukan - ignore-errorshanya akan kembali nil.

Opsi yang lebih rumit adalah membungkus kode yang berpotensi buggy dalam kombinasi unwind-protectdan with-demoted-errors(dengan debug-on-errorset ke nil). Yang terakhir akan kesalahan keluar dengan anggun-ish pada kesalahan pertama yang ditemui dan melaporkan pesan kesalahan ke *Messages*buffer untuk inspeksi Anda. Sementara itu, bagian unwind-protecttubuh lainnya (mungkin kustomisasi Anda) akan dievaluasi. Jadi, mis:

(unwind-protect
    (let ((debug-on-error nil))
      (with-demoted-errors
        (message "The world is about to end")
        (sleep-for 2)
        (/ 10 0)                        ; divide by zero signals an error
        (message "This will never evaluate")
        (sleep-for 2)
        (setq some-var 5)))
  (message "Here are the unwind forms that will always evaluate")
  (sleep-for 2)
  (setq some-var 10)
  (setq another-var "puppies")
  (message "All done!"))
Dan
sumber
1
Bagus, saya tidak tahu with-demoted-errors. Anda bisa menambahkan argumen string seperti itu "LOOK OVER HERE!!! %s", sehingga Anda tidak akan kehilangan kesalahan di buffer pesan.
Malabarba
@Malabarba Bentuk with-demoted-errorsini hanya tersedia dalam 24,4
lunaryorn
@Lunaryorn Terima kasih, tidak tahu itu.
Malabarba
Sebenarnya, versi yang saya gunakan adalah 24.3.1.
Dan
8

@Dan dijelaskan dengan baik bagaimana Anda dapat mengubah kesalahan menjadi pesan. Anda juga dapat melakukan apa pun yang Anda inginkan dengan menggunakan kesalahan condition-case. Namun pilihan lain adalah menggunakan unwind-protect.

Saya akan tetap di condition-casesini, tanpa alasan apa pun.

Menangkap Kesalahan

Ini harus selalu menjamin definisi kunci Anda dievaluasi, terlepas dari apa yang terjadi di dalamnya condition-case. Setiap kesalahan disimpan di dalam init-error.

(defvar init-error nil 
  "The error which happened.")

(condition-case the-error
    (progn
      ;; Do the dangerous stuff here.
      (require 'what-I-want))
  (error
   ;; This is only evaluated if there's an error.
   (setq init-error the-error)))

;;; Do the safe stuff here.
(define-key uncrippling-map "\C-h" 'help!)

Membuangnya Kembali

Setelah itu, buang saja kesalahannya lagi. Ada beberapa cara Anda bisa melakukan itu, ini dia.

;;; Throw the error again here.
(when init-error
  (funcall #'signal (car init-error) (cdr init-error)))
Malabarba
sumber
unwind-protectmenyebabkan kesalahan untuk segera diangkat kembali, setelah mengeksekusi kode apa pun yang Anda masukkan dalam klausa penyelamatannya. Ini seperti finallydalam bahasa seperti Java, bukan catch.
sanityinc
2

Jawaban lain cukup baik mencakup fasilitas penanganan kesalahan tingkat rendah yang akan berguna dalam kasus seperti ini. Pendekatan lain yang dapat membantu adalah modularitas. Sebagai contoh, saya membagi file inisialisasi saya menjadi beberapa file yang berbeda (menggunakan yang providesesuai), dan saya memuatnya menggunakan fungsi ini alih-alih require:

(defun my/require-softly (feature &optional filename)
  "As `require', but instead of an error just print a message.

If there is an error, its message will be included in the message
printed.

Like `require', the return value will be FEATURE if the load was
successful (or unnecessary) and nil if not."
  (condition-case err
      (require feature filename) 
    (error (message "Error loading %s: \"%s\""
                    (if filename (format "%s (%s)" feature filename) feature)
                    (error-message-string err))
           nil)))

Kesalahan saat memuat file dengan cara ini masih akan mencetak pesan, tetapi itu tidak akan mencegah eksekusi apa pun di luar file di mana kesalahan itu sebenarnya terjadi.

Tentu saja, fungsi ini tidak jauh berbeda dari membungkus requirepanggilan with-demoted-errors(saya menulisnya sebelum saya tahu with-demoted-errors), tetapi poin pentingnya adalah bahwa Anda pada dasarnya dapat mengimplementasikan sesuatu seperti kombinasi Dan dari with-demoted-errorsdan unwind-protecttanpa pembungkus (berpotensi sangat panjang) blok kode.

Aaron Harris
sumber
Fungsi ini persis seperti apa yang saya cari. Emacs saya melakukan booting sekarang meskipun melaporkan kesalahan. Setelah itu, saya hanya memuat file init saya dan eval-buffer. Terima kasih telah mempostingnya.
Kevin