reddit / emacs

Emacs throw error when hook fails

thiagoaraujos · 2022-03-07 15:11
After some investigation, I discovered one of my hooks were failing: (add-hook 'web-mode-hook 'something) Tracking back a bit, I was wondering why .dir.locals were not being read, and it was because of the hook failure - all later hooks were being halted as a result, so it was not reading .dir.locals. I was only able to notice that by going to the \*Messages\* buffer. Is there a way to configure Emacs to throw an error when a hook fails? That would be much better for debugging and for making any errors obvious. I don't want to be lenient to hook failures.
orzechod · 2022-03-07 15:24
`M-x toggle-debug-on-error` will pop open the debugger in a new buffer upon errors, but that's kind of a big global hammer if you're just trying to debug a single function. maybe have your hook set `debug-on-error` to `t` at the beginning of your hook function and then `nil` at the end of it?
thiagoaraujos · 2022-03-07 16:14
Thank you, that's useful! However, I was looking more into making such errors obvious without globally turning on toggle-debug-on-error. Wrapping hooks with toggle-debug-on-error seems a bit heavy-handed, but it's a feasible solution and could be alleviated with a macro.
ieure · 2022-03-07 16:46
Emacs will surface an error *if the hook signals one*. If the hook doesn't signal an error, Emacs has no way to know what happened inside it. Update your hook to signal an error (with `error`) when it meets whatever conditions you define as failure.
thiagoaraujos · 2022-03-07 20:58
If I understood it correctly, does that mean re-throwing the error? For example: (add-hook 'web-mode-hook (lamba () (condition-case nil (progn (dangerous-code)) (error (error "Rethrowing the error..."))))) I just wish there was a global setting to do that automatically.
7890yuiop · 2022-03-07 21:24
All you've done there is suppress the original error.
thiagoaraujos · 2022-03-07 22:06
I know, I think my language was not quite right. Instead, my question attempted to bring attention to the fact that we're signaling an error (even if not properly rethrowing) and that would be enough to make the hook fail loudly.
7890yuiop · 2022-03-07 22:50
But the original error, if it was available to be caught by your condition-case, would have had the same effect as your "rethrown" error would have, as far as any effect on running the hook is concerned. (Unless `run-mode-hooks` was explicitly ignoring certain errors and you were signalling a different one. But it doesn't do that.) If the original code didn't signal an error then it didn't "fail", and your problem never arose in the first place. If there *was* an error then merely replacing one error with another error is of somewhat limited value. I do get that all you actually want to do is increase visibility of such errors though, and if all you're interested in is your custom hook functions then your error handler could certainly do something noisier than the default. (Perhaps propertize the buffer-local `mode-name` with an `error` face?)
7890yuiop · 2022-03-07 22:54
> Wrapping hooks with toggle-debug-on-error seems a bit heavy-handed You definitely wouldn't use `toggle-debug-on-error` for that; you would use `(let ((debug-on-error t)) ...)`
ieure · 2022-03-07 22:58
No, I mean: you say the hook "failed." But whatever failing entails, it didn't signal an error.
[deleted] · 2022-03-07 23:05
[deleted]
7890yuiop · 2022-03-07 23:06
You could try this: (define-advice run-mode-hooks (:around (orig-fun &rest hooks) debug-on-error) "Ensure errors during mode hooks trigger the debugger." (let ((debug-on-error t)) (apply orig-fun hooks))) ;; (advice-remove 'run-mode-hooks 'run-mode-hooks@debug-on-error)
7890yuiop · 2022-03-07 23:11
But if it didn't signal an error then it also didn't prevent the processing of local variables (which was the original problem). I think there was some confusion around whether or not an actual error had occurred, but I think the issue is simply that the user didn't notice the error when it occurred, and wished it was more apparent.
7890yuiop · 2022-03-07 23:42
How about this? (define-advice run-mode-hooks (:around (orig-fun &rest hooks) handle-error) "Ensure errors during mode hooks are more apparent." (condition-case err (apply orig-fun hooks) (error (setq mode-name `(:propertize ,mode-name face error help-echo ,(format "%s during run-mode-hooks" (error-message-string err)))) (funcall #'signal (car err) (cdr err))))) ;; (advice-remove 'run-mode-hooks 'run-mode-hooks@handle-error) I think I'm going to use this myself, just in case that situation ever arises for me.
thiagoaraujos · 2022-03-09 15:11
>(define-advice run-mode-hooks (:around (orig-fun &rest hooks) handle-error) "Ensure errors during mode hooks are more apparent." (condition-case err (apply orig-fun hooks) (error (setq mode-name \`(:propertize ,mode-name face error help-echo ,(format "%s during run-mode-hooks" (error-message-string err)))) (funcall #'signal (car err) (cdr err))))) ;; (advice-remove 'run-mode-hooks 'run-mode-hooks@handle-error) That's very useful, thank you!
thiagoaraujos · 2022-03-09 15:40
In my version, I also added a line to display the `*Messages*` buffer in another window. I want to be extra annoying as I may miss the red `mode-name`. ```elisp (define-advice run-mode-hooks (:around (orig-fun &rest hooks) handle-error) "Ensure errors during mode hooks are more apparent." (condition-case err (apply orig-fun hooks) (error (setq mode-name `(:propertize ,mode-name face error help-echo ,(format "%s during run-mode-hooks" (error-message-string err)))) (display-buffer (get-buffer "*Messages*")) (funcall #'signal (car err) (cdr err))))) ```
thiagoaraujos · 2022-03-09 15:42
Oh ok. Now I get that we signal an error with the `signal` function. Thanks!
ieure · 2022-03-09 15:47
`signal` or `error`. While `error` is roughly equivalent to `(signal 'error …)`, the `signal` function allows signaling any condition, including non-error ones.