Skip to content

fix(emacs-core): log a routine command-loop signal at debug, not error - #343

Merged
eval-exec merged 5 commits into
eval-exec:mainfrom
tag-und-nacht:fix/command-loop-signal-log-level
Sep 4, 2026
Merged

eval-exec merged 5 commits into
eval-exec:mainfrom
tag-und-nacht:fix/command-loop-signal-log-level

Conversation

@tag-und-nacht

Copy link
Copy Markdown
Contributor

Fixes #342.

Pressing an arrow key at the end of a line under evil shows "End of line" in the echo area exactly as GNU does, and also logs

ERROR neovm_core::emacs_core::eval: Command loop error: End of line [signal=(end-of-line nil)] condition=end-of-line

on every keystroke, and the same for every C-g. Evil defines end-of-line as an error and pushes it onto debug-ignored-errors (evil-macros.el).

GNU's command loop never logs: cmd_error_internal (src/keyboard.c:1030-1047, emacs-31.0.90) hands the signal to command-error-function and that is the whole handling. The log line is this port's diagnostic, and it ranked every signal the same. GNU ranks signals in one place, the debugger gate skip_debugger (src/eval.c:2146-2180) over debug-ignored-errors (defaults in lisp/bindings.el:1038-1046: beginning-of-line, end-of-line, end-of-buffer, buffer-read-only, user-error, ...), and a quit is routine by construction (signal_quit_p, src/eval.c:2181-2190).

Context::command_error_severity applies that ranking through the existing skip_debugger matcher and the crate's quit predicate, as a closed CommandErrorSeverity { Routine, Diagnostic }. command_loop_2 logs a routine signal at debug and keeps the error level, with condition and payload, for everything else, so a void function in a command still reaches a bug report. Presentation is unchanged: report_command_error still runs first through Lisp's buffer-local command-error-function.

Declared departures from GNU. GNU consults skip_debugger only when the debugger is otherwise wanted (maybe_call_debugger, src/eval.c:2206-2213); the log has no such gate, so the list is evaluated on every uncaught command signal. And the ranking runs inside command_loop_2, GNU's sole recovery owner, where nothing may signal: a string entry in debug-ignored-errors that is not a valid regexp makes the matcher signal invalid-regexp. The classification is therefore infallible and a matcher failure ranks the original error as a diagnostic; a test pins that.

Tests, red first: command_error_severity_follows_gnus_debug_ignored_errors (evil's end-of-line is a diagnostic with an empty list and routine once listed; user-error, a condition inheriting from it, and a regexp entry over the message are routine; quit and minibuffer-quit are routine whatever the list says; void-function stays a diagnostic) and command_error_severity_never_signals_on_a_bad_ignore_list.

Verified on macOS: the two tests, rustfmt, and a GUI run of the release build feeding the real command loop a command that signals end-of-line and one that calls a void function: the first logs at debug only, the second keeps its ERROR line. Not run: Linux and Windows (platform-neutral Rust in neovm-core); clippy on neovm-core's tests stops on a pre-existing never_loop error in the process tests.

🤖 Generated with Claude Code

https://claude.ai/code/session_0117NsCB7AbwgdEqduGF5Kww

@eval-exec
eval-exec self-requested a review September 3, 2026 06:20
tag-und-nacht and others added 5 commits September 4, 2026 03:18
Pressing an arrow key at the end of a line under evil signals `end-of-line`
(evil-macros.el defines the error and pushes it onto
`debug-ignored-errors`).  The echo area showed "End of line" exactly as GNU
does, and the log said

    ERROR neovm_core::emacs_core::eval: Command loop error: End of line
    [signal=(end-of-line nil)] condition=end-of-line

on every keystroke, and the same for every `C-g`.

GNU's command loop never logs: `cmd_error_internal`
(src/keyboard.c:1030-1047, emacs-31.0.90) hands the signal to
`command-error-function` and that is the whole handling.  The log line is
this port's diagnostic, and it ranked every signal the same.  GNU does rank
signals in one place, the debugger gate `skip_debugger`
(src/eval.c:2146-2180): a condition or message matched by
`debug-ignored-errors` (`beginning-of-line`, `end-of-line`, `end-of-buffer`,
`buffer-read-only`, `user-error`, ... by default, lisp/bindings.el:1038-1046,
plus whatever packages push there) must not interrupt the user, and a quit
is routine by construction (`signal_quit_p`, src/eval.c:2181-2190).

`Context::command_error_severity` applies that ranking through the existing
`skip_debugger` matcher and the crate's quit predicate, as the closed
`CommandErrorSeverity { Routine, Diagnostic }`; `command_loop_2` logs a
routine signal at debug and keeps the error level, with condition and
payload, for everything else, so a void function in a command still reaches
a bug report.  Presentation is unchanged: `report_command_error` still runs
first, through Lisp's buffer-local `command-error-function`.

Two declared departures from GNU.  GNU consults `skip_debugger` only when
the debugger is otherwise wanted (`maybe_call_debugger`,
src/eval.c:2206-2213); the log has no such gate, so the ignore list is
evaluated on every uncaught command signal.  And the ranking runs inside
`command_loop_2`, GNU's sole recovery owner, where nothing may signal: a
string entry in `debug-ignored-errors` that is not a valid regexp makes the
matcher signal `invalid-regexp`, and an adversarial pre-push review found
the first version propagating that with `?` and unwinding the whole command
loop.  The classification is infallible: a matcher failure ranks the
original error as a diagnostic.

Tests, red first: `command_error_severity_follows_gnus_debug_ignored_errors`
(evil's `end-of-line` is a diagnostic with an empty ignore list and routine
once listed; `user-error`, a condition inheriting from it, and a regexp
entry over the message are routine; `quit` and `minibuffer-quit` are
routine whatever the list says; `void-function` stays a diagnostic) and
`command_error_severity_never_signals_on_a_bad_ignore_list`.

Not run: Linux and Windows builds (the change is platform-neutral Rust in
neovm-core); clippy on neovm-core's tests stops on a pre-existing
`never_loop` error in the process tests, unrelated to this change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117NsCB7AbwgdEqduGF5Kww
@eval-exec
eval-exec force-pushed the fix/command-loop-signal-log-level branch from 014a9ef to d010b67 Compare September 4, 2026 07:19
@eval-exec
eval-exec merged commit 89eb51a into eval-exec:main Sep 4, 2026
21 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Command loop logs a routine signal (evil's end-of-line, C-g) at ERROR on every keystroke

2 participants