Як витончено поводитися з помилками у файлі init


20

Мені б хотілося зафіксувати помилки під час запуску мого файлу init, а потім витончено обробити їх. У кінці мого файлу init відображається багато моїх найважливіших налаштувань та вкладень клавіш, щоб переконатися, що інші налаштування не застосовуються вгорі. Проблема полягає в тому, що коли ініціалізація припиняється рано, я відчуваю себе повністю калікою, намагаючись налагодити проблему, не застосовуючи мої знайомі прив’язки ключів та налаштування.

Чи є спосіб витончено закінчити процес ініціалізації, коли виникає помилка?

Відповіді:


9

Приходять на думку два варіанти, жоден з яких не ідеальний. По-перше, ви можете зафіксувати більшість коду ранньої ініціалізації (тобто до того, як він перейде до ваших налаштувань) у (ignore-errors ...). Якщо є помилки, проте відгуків не буде багато - ignore-errorsвони просто повернуться nil.

Більш складним варіантом було б обернути потенційно помилковий код у комбінації unwind-protectта with-demoted-errorsdebug-on-errorвстановленим рівнем нуля). Остання помилково вимикається при першій помилці, яка виникає, і повідомляє про помилку в *Messages*буфер для вашої перевірки. Тим часом, решту unwind-protectтіла (імовірно, ваші налаштування) буде оцінено. Так, наприклад:

(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
Добре, я не збирався with-demoted-errors. Ви можете додати до нього рядковий аргумент на зразок "LOOK OVER HERE!!! %s", тому ви рідше пропустите помилку в буфері повідомлень.
Малабарба

@Malabarba Ця форма with-demoted-errorsдоступна лише в 24.4
місячний місяць

@lunaryorn Спасибі, не знав цього.
Малабарба

Власне, версія, на якій я перебуваю, - це 24.3.1.
Дан

8

@Dan добре описав, як можна перетворити помилки на повідомлення. Ви також можете робити все, що завгодно, з помилками, використовуючи condition-case. Ще один варіант - це використовувати unwind-protect.

Я буду дотримуватися condition-caseтут, без жодної причини.

Ловіть помилку

Це завжди повинно гарантувати оцінку ваших ключових визначень незалежно від того, що сталося всередині condition-case. Будь-яка помилка зберігається всередині 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!)

Закидання назад

Після цього просто киньте помилку ще раз. Є кілька способів зробити це, ось один.

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

unwind-protectвикликає помилку негайно повторно, після виконання будь-якого коду, який ви ввели у його рятувальний пункт. Це як finallyу такій мові, як Java, ніж catch.
sanityinc

2

Інші відповіді досить добре висвітлювали засоби низького рівня помилок, які будуть корисні у такому випадку. Ще один підхід, який може допомогти, - модульність. Наприклад, я поділяю свій файл ініціалізації на кілька різних файлів (використовуючи, provideякщо це доречно), і завантажую їх за допомогою цієї функції замість 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)))

Помилка під час завантаження файлу таким чином все одно надрукує повідомлення, але це не завадить виконати щось поза файлом, де помилка фактично сталася.

Звичайно, ця функція насправді не відрізняється від завершення requireвиклику with-demoted-errors(я написав це, перш ніж я про це знав with-demoted-errors), але важливим моментом є те, що ви можете реально реалізувати щось на кшталт комбінації Дена with-demoted-errorsі unwind-protectбез обгортання (можливо, дуже тривалий) блоки коду.


Ця функція була саме такою, якою я був після. Мої Emacs завантажуються зараз, незважаючи на повідомлення про помилку. Після цього я просто завантажую свій файл init і eval-buffer. Дякуємо за публікацію
Кевін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.