Jak z wdziękiem obsługiwać błędy w pliku inicjującym

20

Chciałbym znaleźć sposób na wychwycenie błędów podczas uruchamiania mojego pliku init, a następnie z gracją sobie z nimi poradzić. Wiele moich najważniejszych dostosowań i skrótów klawiszowych pojawia się na końcu mojego pliku init, aby upewnić się, że inne ustawienia nie zostaną zastosowane ponad nimi. Problem polega na tym, że gdy inicjalizacja przerywa się wcześnie, czuję się całkowicie kaleką, próbując rozwiązać problem bez zastosowania moich znanych powiązań klawiszy i ustawień.

Czy jest jakiś sposób, aby z wdziękiem zakończyć proces inicjalizacji, gdy wystąpi błąd?

nispio
źródło

Odpowiedzi:

9

Przychodzą mi na myśl dwie opcje, z których żadna nie jest idealna. Po pierwsze, możesz owinąć większość kodu wczesnej inicjalizacji (tj. Zanim dotrze on do twoich dostosowań) (ignore-errors ...). Jeśli nie błędy, jednak nie będzie tam dużo informacji zwrotnych - ignore-errorspo prostu wrócić nil.

Bardziej skomplikowaną opcją byłoby zawinięcie potencjalnie błędnego kodu w kombinację unwind-protecti with-demoted-errors(z debug-on-errorustawieniem na zero). Ten drugi wyskoczy z wdziękiem przy pierwszym napotkanym błędzie i zgłosi komunikat o błędzie do *Messages*bufora w celu sprawdzenia. Tymczasem unwind-protectzostanie poddana ocenie reszta ciała (prawdopodobnie twoje dostosowania). Więc np .:

(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
źródło
1
Fajnie, nie myślałem with-demoted-errors. Możesz dodać do niej argument łańcuchowy "LOOK OVER HERE!!! %s", więc mniej prawdopodobne jest pominięcie błędu w buforze komunikatów.
Malabarba
@Malabarba Ta forma with-demoted-errorsjest dostępna tylko w 24.4
lunaryorn
@lunaryorn Dzięki, nie wiedziałem o tym.
Malabarba
W rzeczywistości moja wersja to 24.3.1.
Dan
8

@Dan dobrze opisał, jak zamieniać błędy w wiadomości. Za pomocą błędów możesz także robić, co chcesz, z błędami condition-case. Jeszcze inną opcją jest użycie unwind-protect.

Będę się condition-casetutaj trzymał , bez żadnego powodu.

Łapanie błędu

To zawsze powinno gwarantować, że kluczowe definicje zostaną ocenione, niezależnie od tego, co wydarzyło się w środku condition-case. Każdy błąd zostanie zapisany w środku 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!)

Odrzucając to z powrotem

Następnie ponownie rzuć błąd. Możesz to zrobić na kilka sposobów, oto jeden.

;;; Throw the error again here.
(when init-error
  (funcall #'signal (car init-error) (cdr init-error)))
Malabarba
źródło
unwind-protectpowoduje natychmiastowe ponowne zgłoszenie błędu po wykonaniu dowolnego kodu umieszczonego w jego klauzuli ratunkowej. To jest jak finallyw języku takim jak Java catch.
sanityinc
2

Inne odpowiedzi dość dobrze obejmowały niskopoziomowe narzędzia do obsługi błędów, które będą przydatne w takim przypadku. Innym podejściem, które może pomóc, jest modułowość. Na przykład dzielę mój plik inicjujący na kilka różnych plików (używając provideodpowiednio) i ładuję je za pomocą tej funkcji zamiast 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)))

Błąd podczas ładowania pliku w ten sposób nadal spowoduje wydrukowanie komunikatu, ale nie uniemożliwi wykonywania niczego poza plikiem, w którym błąd rzeczywiście wystąpił.

Oczywiście ta funkcja nie różni się tak naprawdę od zawijania requirerozmowy with-demoted-errors(napisałem ją, zanim się dowiedziałem with-demoted-errors), ale ważne jest to, że można zasadniczo zaimplementować coś takiego jak kombinacja Dana with-demoted-errorsi unwind-protectbez zawijania (potencjalnie bardzo długa) bloki kodu.

Aaron Harris
źródło
Ta funkcja była dokładnie tym, czego szukałem. Mój emacs uruchamia się teraz pomimo zgłoszenia błędu. Następnie po prostu ładuję mój plik init i eval-buffer. Dzięki za opublikowanie.
Kevin