如何妥善处理init文件中的错误


20

我想要一种在运行初始化文件时捕获错误,然后优雅地处理它们的方法。我的许多最重要的自定义项和键绑定都显示在初始化文件的末尾,以确保其他设置不会套用在它们之上。问题是,当初始化中止得很早时,在没有应用我熟悉的键绑定和设置的情况下尝试调试问题会感到完全瘫痪。

发生错误时,是否有任何方法可以正常完成初始化过程?

Answers:


9

我想到了两个选择,但都不是完美的。首先,您可以将大多数早期初始化代码(即,在进入自定义项之前)包装在中(ignore-errors ...)。如果错误,但不会有很大的回响- ignore-errors将简单地返回nil

一个更复杂的选择是将unwind-protect和组合在一起的潜在错误代码with-demoted-errorsdebug-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 2014年

@lunaryorn谢谢,不知道。
马拉巴巴2014年

实际上,我使用的版本是24.3.1。
Dan

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执行您在其营救子句中放置的任何代码后,都会立即引发该错误。就像finallyJava 之类的语言,而不是catch
sanityinc 2014年

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),但是重要的一点是,它实际上可以实现类似Dan的组合,with-demoted-errorsunwind-protect无需包裹(可能很长)代码块。


该功能正是我所追求的。尽管报告了错误,我的emacs现在仍然启动。之后,我只加载了init文件和eval-buffer。感谢您发布。
凯文
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.