Come gestire con grazia errori nel file init


20

Vorrei un modo per rilevare errori durante l'esecuzione del mio file init e quindi gestirli con grazia. Molte delle mie personalizzazioni e combinazioni di tasti più importanti vengono visualizzate alla fine del mio file init per assicurarsi che altre impostazioni non vengano applicate al di sopra di esse. Il problema è che quando l'inizializzazione si interrompe presto, mi sento totalmente paralizzato nel tentativo di eseguire il debug del problema senza che vengano applicate le mie associazioni e impostazioni chiave.

C'è un modo per completare con grazia il processo di inizializzazione quando si verifica un errore?

Risposte:


9

Mi vengono in mente due opzioni, nessuna delle quali perfetta. Innanzitutto, potresti includere la maggior parte del codice di inizializzazione iniziale (ovvero prima che arrivi alle tue personalizzazioni) (ignore-errors ...). Se ci sono errori, tuttavia, non ci saranno molti feedback ignore-errors, semplicemente ritorneranno nil.

Un'opzione più complessa sarebbe quella di racchiudere il codice potenzialmente errato in una combinazione di unwind-protecte with-demoted-errors(con debug-on-errorimpostato su zero). Quest'ultimo sbaglierà con grazia al primo errore riscontrato e segnalerà il messaggio di errore al *Messages*buffer per l'ispezione. Nel frattempo, il resto del unwind-protectcorpo (presumibilmente le tue personalizzazioni) verrà valutato. Quindi, ad esempio:

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

1
Bello, non ci ho pensato with-demoted-errors. È possibile aggiungere un argomento stringa come questo "LOOK OVER HERE!!! %s", quindi è meno probabile che si perda l'errore nel buffer dei messaggi.
Malabarba,

@Malabarba Questa forma di with-demoted-errorsè disponibile solo in 24.4
lunaryorn

@lunaryorn Grazie, non lo sapevo.
Malabarba,

In realtà, la versione in cui mi trovo è il 24.3.1.
Dan

8

@Dan ha descritto bene come trasformare gli errori in messaggi. Puoi anche fare quello che vuoi con errori usando condition-case. Un'altra opzione è usare unwind-protect.

Mi atterrò condition-casequi, senza motivo.

Cattura dell'errore

Ciò dovrebbe sempre garantire che le definizioni chiave vengano valutate, indipendentemente da ciò che è accaduto all'interno condition-case. Qualsiasi errore viene memorizzato all'interno 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!)

Lanciandolo indietro

Successivamente, lancia nuovamente l'errore. Esistono diversi modi per farlo, eccone uno.

;;; Throw the error again here.
(when init-error
  (funcall #'signal (car init-error) (cdr init-error)))

unwind-protectprovoca immediatamente la ricompilazione dell'errore, dopo aver eseguito il codice inserito nella clausola di salvataggio. È come finallyin un linguaggio come Java, piuttosto che catch.
sanityinc

2

Le altre risposte hanno coperto abbastanza bene le strutture di gestione degli errori di basso livello che saranno utili in un caso come questo. Un altro approccio che può aiutare è la modularità. Ad esempio, divido il mio file di inizializzazione in diversi file (usando providecome appropriato) e li carico usando questa funzione invece di 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)))

Un errore durante il caricamento di un file in questo modo stamperà comunque un messaggio, ma non impedirà l'esecuzione di qualsiasi cosa al di fuori del file in cui si è effettivamente verificato l'errore.

Naturalmente, questa funzione non è poi così diversa dall'avvolgere una requirechiamata with-demoted-errors(l'ho scritta prima di sapere with-demoted-errors), ma il punto importante è che puoi essenzialmente implementare qualcosa come la combinazione di Dan di with-demoted-errorse unwind-protectsenza il wrapping (potenzialmente molto lungo) blocchi di codice.


Questa funzione era esattamente quello che cercavo. Il mio emacs si avvia ora nonostante abbia segnalato un errore. Dopodiché, ho appena caricato il mio file init e eval-buffer. Grazie per averlo pubblicato.
Kevin
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.