From d751915ef4c10f2dc10555c404fac3c981320b4f Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Tue, 6 Jun 2023 12:15:25 +0100 Subject: [PATCH 1/4] eval-command-interactive-spec: Shorten code * lisp/emacs-lisp/subr-x.el (eval-command-interactive-spec): Don't reimplement checking for an 'interactive-form symbol property. `interactive-form' already does this. Thanks to Stefan Monnier. --- lisp/emacs-lisp/subr-x.el | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index a7d8f785508..38f85c242c7 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -507,9 +507,7 @@ Used by `emacs-authors-mode' and `emacs-news-mode'." (defun eval-command-interactive-spec (command) "Evaluate COMMAND's interactive form and return resultant list. If COMMAND has no interactive form, return nil." - (advice-eval-interactive-spec - (cadr (or (and (symbolp command) (get command 'interactive-form)) - (interactive-form command))))) + (advice-eval-interactive-spec (cadr (interactive-form command)))) (provide 'subr-x) From 4f66cbbfe520ee31ef26676e09a926217d9736fe Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Sun, 4 Jun 2023 19:41:20 +0300 Subject: [PATCH 2/4] Avoid header line with some empty non-nil formats Allow the value of 'header-line-format' to indicate that no header line should be displayed when it trivially yields 'nil', even if it is not plain 'nil'. Previously, any non-nil 'header-line-format' resulted in a (possibly empty) header line. This change adds some flexibility by also taking a non-nil value of 'header-line-format' to mean that no header line should be displayed if it's a list whose 'car' is a symbol and either that symbol is ':eval' and the second list element evaluates to 'nil', or the symbol's value as a variable is 'nil' or void. (Bug#63825) * src/xdisp.c (safe_eval_inhibit_quit): New function. * src/lisp.h (safe_eval_inhibit_quit): Declare it. * src/window.c (null_header_line_format): New function. (window_wants_header_line): Use it. * doc/lispref/modes.texi (Header Line): Update to reflect new conditions for displaying a window's header line. * etc/NEWS: Announce updated treatment of 'header-line-format'. --- doc/lispref/modes.texi | 9 ++++++++ etc/NEWS | 8 +++++++ src/lisp.h | 1 + src/window.c | 50 ++++++++++++++++++++++++++++++++++++++++-- src/xdisp.c | 6 +++++ 5 files changed, 72 insertions(+), 2 deletions(-) diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi index c2698da6d99..d2a05fe29e4 100644 --- a/doc/lispref/modes.texi +++ b/doc/lispref/modes.texi @@ -2597,6 +2597,15 @@ It is normally @code{nil}, so that ordinary buffers have no header line. @end defvar +Emacs displays the header line for a window unless +@code{header-line-format} is either @code{nil}, or it's a list whose +@sc{car} is a symbol, and either that symbol is @code{:eval} and the +second list element evaluates to @code{nil} or the symbol's value as a +variable is @code{nil} or void. Note that there are other possible +values @code{header-line-format} that result in an empty header line +(for example, @code{""}), but all other values tell Emacs to display a +header line, whether or not it is empty. + If @code{display-line-numbers-mode} is turned on in a buffer (@pxref{Display Custom, display-line-numbers-mode,, emacs, The GNU Emacs Manual}), the buffer text is indented on display by the amount diff --git a/etc/NEWS b/etc/NEWS index 910472e5519..ec1b40aa0a7 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -451,6 +451,14 @@ hooks named after the feature name, like 'esh-mode-unload-hook'. +++ ** 'copy-tree' now copies records when its optional 2nd argument is non-nil. ++++ +** Certain values of 'header-line-format' now inhibit empty header line. +Emacs now avoids displaying a header line, instead of displaying an +empty one, when 'header-line-format' is a list whose 'car' is a +symbol, and either that symbol is ':eval' and the second element of +the list evaluates to 'nil' or the symbol's value as a variable is +'nil' or void. + * Lisp Changes in Emacs 30.1 diff --git a/src/lisp.h b/src/lisp.h index 2bfcd1a1983..2978de962d9 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4174,6 +4174,7 @@ void set_frame_cursor_types (struct frame *, Lisp_Object); extern void syms_of_xdisp (void); extern void init_xdisp (void); extern Lisp_Object safe_eval (Lisp_Object); +extern Lisp_Object safe_eval_inhibit_quit (Lisp_Object); extern bool pos_visible_p (struct window *, ptrdiff_t, int *, int *, int *, int *, int *, int *); diff --git a/src/window.c b/src/window.c index f4e09f49eae..9429679061e 100644 --- a/src/window.c +++ b/src/window.c @@ -5471,6 +5471,48 @@ window_wants_mode_line (struct window *w) } +/** + * null_header_line_format: + * + * Return non-zero when header line format FMT indicates that the + * header line should not be displayed at all. + * + * This is when FMT is nil, or if FMT is a cons cell and either its + * car is a symbol whose value as a variable is nil or void, or its + * car is the symbol ':eval' and its cadr evaluates to nil. + */ +static bool +null_header_line_format (Lisp_Object fmt, struct frame * f) +{ + Lisp_Object car; + Lisp_Object val; + + if (NILP (fmt)) + return true; + + if (CONSP (fmt)) + { + car = XCAR (fmt); + if (SYMBOLP (car)) + { + if (EQ (car, QCeval)) + { + val = safe_eval_inhibit_quit (XCAR (XCDR (fmt))); + if (!FRAME_LIVE_P (f)) + signal_error (":eval deleted the frame being displayed", fmt); + return NILP (val); + } + val = find_symbol_value (car); + return (SYMBOLP (car) + && (EQ (val, Qunbound) + || NILP (val))); + } + } + + return false; +} + + /** * window_wants_header_line: * @@ -5491,12 +5533,16 @@ window_wants_header_line (struct window *w) Lisp_Object window_header_line_format = window_parameter (w, Qheader_line_format); + struct frame * f = WINDOW_XFRAME(w); + return (WINDOW_LEAF_P (w) && !MINI_WINDOW_P (w) && !WINDOW_PSEUDO_P (w) && !EQ (window_header_line_format, Qnone) - && (!NILP (window_header_line_format) - || !NILP (BVAR (XBUFFER (WINDOW_BUFFER (w)), header_line_format))) + && (!null_header_line_format (window_header_line_format, f) + || !null_header_line_format (BVAR (XBUFFER (WINDOW_BUFFER (w)), + header_line_format), + f)) && (WINDOW_PIXEL_HEIGHT (w) > (window_wants_mode_line (w) ? 2 * WINDOW_FRAME_LINE_HEIGHT (w) diff --git a/src/xdisp.c b/src/xdisp.c index a6ec966ea3c..5e25857322f 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -3074,6 +3074,12 @@ safe__eval (bool inhibit_quit, Lisp_Object sexpr) return safe__call1 (inhibit_quit, Qeval, sexpr); } +Lisp_Object +safe_eval_inhibit_quit (Lisp_Object sexpr) +{ + return safe__eval (true, sexpr); +} + /* Call function FN with two arguments ARG1 and ARG2. Return the result, or nil if something went wrong. */ From 05c2be28a3e97bd920d0bf8c8b59ec682a420cce Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Tue, 6 Jun 2023 15:15:34 +0300 Subject: [PATCH 3/4] ; Fix last change * src/window.c (window_wants_header_line): Fix whitespace and commentary in last change. (Bug#63825) --- src/window.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/window.c b/src/window.c index 9429679061e..7d89885b65f 100644 --- a/src/window.c +++ b/src/window.c @@ -5475,14 +5475,14 @@ window_wants_mode_line (struct window *w) * null_header_line_format: * * Return non-zero when header line format FMT indicates that the - * header line should not be displayed at all. + * header line should not be displayed at all, for windows on frame F. * * This is when FMT is nil, or if FMT is a cons cell and either its * car is a symbol whose value as a variable is nil or void, or its * car is the symbol ':eval' and its cadr evaluates to nil. */ static bool -null_header_line_format (Lisp_Object fmt, struct frame * f) +null_header_line_format (Lisp_Object fmt, struct frame *f) { Lisp_Object car; Lisp_Object val; @@ -5533,7 +5533,7 @@ window_wants_header_line (struct window *w) Lisp_Object window_header_line_format = window_parameter (w, Qheader_line_format); - struct frame * f = WINDOW_XFRAME(w); + struct frame *f = WINDOW_XFRAME(w); return (WINDOW_LEAF_P (w) && !MINI_WINDOW_P (w) @@ -5541,8 +5541,7 @@ window_wants_header_line (struct window *w) && !EQ (window_header_line_format, Qnone) && (!null_header_line_format (window_header_line_format, f) || !null_header_line_format (BVAR (XBUFFER (WINDOW_BUFFER (w)), - header_line_format), - f)) + header_line_format), f)) && (WINDOW_PIXEL_HEIGHT (w) > (window_wants_mode_line (w) ? 2 * WINDOW_FRAME_LINE_HEIGHT (w) From bf28b019a85fcc4e16bc7ecad6304c30e48a3223 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Tue, 6 Jun 2023 21:00:44 +0800 Subject: [PATCH 4/4] Fix problems resulting from modification of the undo list * doc/lispref/text.texi (Atomic Changes): Describe what not to do inside an atomic change group. * lisp/elec-pair.el (electric-pair-inhibit-if-helps-balance): Don't call `delete-char'; that edits the undo list by removing boundary markers. * lisp/subr.el (atomic-change-group, prepare-change-group): Warn against modifying the undo list inside. --- doc/lispref/text.texi | 11 +++++++++++ lisp/elec-pair.el | 4 +++- lisp/subr.el | 9 +++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index f15b3c33e0c..dac8d0d8ce9 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi @@ -6181,6 +6181,17 @@ would expect. Non-nested use of change groups for the same buffer will get Emacs confused, so don't let it happen; the first change group you start for any given buffer should be the last one finished. + Emacs keeps track of change groups by assuming that by following +each cdr in @code{buffer-undo-list}, it will eventually arrive at the +cons it was set to at the time @code{prepare-change-group} was called. + + If @code{buffer-undo-list} no longer contains that cons, Emacs will +lose track of any change groups, resulting in an error when the change +group is cancelled. To avoid this, do not call any functions which +may edit the undo list in such a manner, when a change group is +active: notably, ``amalgamating'' commands such as @code{delete-char}, +which call @code{undo-auto-amalgamate}. + @node Change Hooks @section Change Hooks @cindex change hooks diff --git a/lisp/elec-pair.el b/lisp/elec-pair.el index b894965eae4..416c95e7a34 100644 --- a/lisp/elec-pair.el +++ b/lisp/elec-pair.el @@ -439,7 +439,9 @@ happened." ;; position some markers. The real fix would be to compute the ;; result without having to modify the buffer at all. (atomic-change-group - (delete-char -1) + ;; Don't use `delete-char'; that may modify the head of the + ;; undo list. + (delete-region (point) (1- (point))) (throw 'done (cond ((eq ?\( syntax) diff --git a/lisp/subr.el b/lisp/subr.el index cef631a69c3..1384215498b 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3773,6 +3773,9 @@ This means that if BODY exits abnormally, all of its changes to the current buffer are undone. This works regardless of whether undo is enabled in the buffer. +Do not call functions which edit the undo list within BODY; see +`prepare-change-group'. + This mechanism is transparent to ordinary use of undo; if undo is enabled in the buffer and BODY succeeds, the user can undo the change normally." @@ -3839,6 +3842,12 @@ Once you finish the group, don't use the handle again--don't try to finish the same group twice. For a simple example of correct use, see the source code of `atomic-change-group'. +As long as this handle is still in use, do not call functions +which edit the undo list: if it no longer contains its current +value, Emacs will not be able to cancel the change group. This +includes any \"amalgamating\" commands, such as `delete-char', +which call `undo-auto-amalgamate'. + The handle records only the specified buffer. To make a multibuffer change group, call this function once for each buffer you want to cover, then use `nconc' to combine the returned values, like this: