Как изящно обрабатывать ошибки в файле инициализации

20

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

Есть ли способ изящно завершить процесс инициализации при возникновении ошибки?

nispio
источник

Ответы:

9

На ум приходят два варианта, ни один из которых не идеален. Во-первых, вы можете обернуть большую часть кода вашей ранней инициализации (т.е. до того, как он попадет в ваши настройки) в (ignore-errors ...). Если есть ошибки, однако, не будет много обратной связи - ignore-errorsпросто вернуться nil.

Более сложный вариант заключался бы в том, чтобы обернуть потенциально ошибочный код в комбинацию unwind-protectи with-demoted-errors(при debug-on-errorзначении nil). Последний будет изящно выдавать ошибку при первой обнаруженной ошибке и сообщать об ошибке в *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
@lunaryorn Спасибо, не знал этого.
Малабарба
На самом деле, версия, на которой я работаю, - 24.3.1.
Дэн
8

@ Дэн хорошо описал, как можно превратить ошибки в сообщения. Вы также можете делать что угодно с ошибками, используя 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)))
Malabarba
источник
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 загружается сейчас, несмотря на сообщение об ошибке. После этого я просто загружаю свой файл инициализации и eval-buffer. Спасибо за публикацию.
Кевин