Fehler in der Init-Datei korrekt behandeln

20

Ich möchte eine Möglichkeit, Fehler beim Ausführen meiner Init-Datei abzufangen und sie dann ordnungsgemäß zu behandeln. Viele meiner wichtigsten Anpassungen und Tastenkombinationen werden am Ende meiner Init-Datei angezeigt, um sicherzustellen, dass andere Einstellungen nicht darüber hinaus angewendet werden. Das Problem ist, dass ich mich bei einem frühen Abbruch der Initialisierung total verkrüppelt fühle, wenn ich versuche, das Problem zu debuggen, ohne dass meine vertrauten Tastaturkürzel und Einstellungen angewendet werden.

Gibt es eine Möglichkeit, den Initialisierungsprozess ordnungsgemäß abzuschließen, wenn ein Fehler auftritt?

Nispio
quelle

Antworten:

9

Zwei Optionen, von denen keine perfekt ist, kommen in den Sinn. Erstens können Sie den größten Teil Ihres frühen Initialisierungscodes (dh bevor er an Ihre Anpassungen angepasst wird) in einen Wrap einbinden (ignore-errors ...). Wenn es jedoch Fehler gibt, wird nicht viel Feedback gegeben - es ignore-errorswird einfach zurückgegeben nil.

Eine kompliziertere Option wäre, den möglicherweise fehlerhaften Code in eine Kombination aus unwind-protectund with-demoted-errors(mit debug-on-errordem Wert Null) zu packen. Letzterer wird bei dem ersten aufgetretenen Fehler ordnungsgemäß ausfallen und die Fehlermeldung an den *Messages*Puffer zur Überprüfung weiterleiten. In der Zwischenzeit wird der Rest des unwind-protectKörpers (vermutlich Ihre Anpassungen) bewertet. Also zB:

(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
quelle
1
Schön, ich habe nicht darüber nachgedacht with-demoted-errors. Sie können ein String-Argument hinzufügen "LOOK OVER HERE!!! %s", um die Wahrscheinlichkeit zu verringern, dass der Fehler im Nachrichtenpuffer übersehen wird.
Malabarba
@Malabarba Diese Form von with-demoted-errorsist nur verfügbar in 24.4
lunaryorn
@lunaryorn Danke, wusste das nicht.
Malabarba
Die aktuelle Version ist 24.3.1.
Dan
8

@Dan hat gut beschrieben, wie Sie Fehler in Nachrichten umwandeln können. Sie können mit auch alles Mögliche tun, um Fehler zu beheben condition-case. Eine weitere Option ist die Verwendung unwind-protect.

Ich bleibe condition-casehier ohne jeden Grund.

Den Fehler abfangen

Dies sollte immer gewährleisten, dass Ihre Schlüsseldefinitionen ausgewertet werden, unabhängig davon, was darin passiert ist condition-case. Jeder Fehler wird in gespeichert 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!)

Wirf es zurück

Danach werfen Sie einfach den Fehler erneut. Es gibt verschiedene Möglichkeiten, dies zu tun. Hier eine.

;;; Throw the error again here.
(when init-error
  (funcall #'signal (car init-error) (cdr init-error)))
Malabarba
quelle
unwind-protectBewirkt, dass der Fehler sofort erneut ausgelöst wird, nachdem der Code ausgeführt wurde, den Sie in die Rescue-Klausel eingegeben haben. Es ist eher wie finallyin einer Sprache wie Java als catch.
Sanityinc
2

Die anderen Antworten haben die einfachen Fehlerbehandlungsmöglichkeiten, die in einem solchen Fall nützlich sind, ziemlich gut abgedeckt. Ein weiterer Ansatz, der helfen kann, ist die Modularität. Zum Beispiel teile ich meine Initialisierungsdatei in mehrere verschiedene Dateien auf (je nach provideBedarf) und lade sie mit dieser Funktion anstelle von 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)))

Ein Fehler beim Laden einer Datei auf diese Weise gibt weiterhin eine Meldung aus, verhindert jedoch nicht, dass außerhalb der Datei, in der der Fehler tatsächlich aufgetreten ist, etwas ausgeführt wird.

Natürlich unterscheidet sich diese Funktion nicht wirklich vom Einschließen eines requireAnrufs with-demoted-errors(ich habe sie geschrieben, bevor ich davon wusste with-demoted-errors), aber der wichtige Punkt ist, dass Sie im Wesentlichen so etwas wie Dans Kombination von with-demoted-errorsund unwind-protectohne Einschließen implementieren können (möglicherweise sehr langwierig). Codeblöcke.

Aaron Harris
quelle
Diese Funktion war genau das, wonach ich gesucht habe. Mein Emacs bootet jetzt, obwohl ein Fehler gemeldet wurde. Danach lade ich einfach meine Init-Datei und eval-buffer. Vielen Dank für die Veröffentlichung.
Kevin