* bug#74091: 31.0.50; string-pixel-width in mode line disables region
@ 2024-10-29 17:27 Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-10-30 14:59 ` Eli Zaretskii
0 siblings, 1 reply; 8+ messages in thread
From: Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-10-29 17:27 UTC (permalink / raw)
To: 74091
The new implementation of string-pixel-width has some unexpected effect
when it is called from mode-line-format, as happens for example when
mode-line-format-right-align occurs in mode-line-format:
1. emacs -Q
2. (setq-default mode-line-format
'("" (:eval (progn (string-pixel-width "foo") nil))))
3. C-x C-f /path/to/emacs/lisp/subr.el
4. C-SPC
5. C-n
At this point the region is expected to be active since we activated it
in step 4. But in step 5 the mode line is updated, which calls
string-pixel-width, which in turn unexpectedly disables the region.
I'm not really sure why this happens... Also, if I now trace
string-pixel-width, then it no longer disables the region and everything
starts working as expected.
Thanks,
Eshel
^ permalink raw reply [flat|nested] 8+ messages in thread
* bug#74091: 31.0.50; string-pixel-width in mode line disables region
2024-10-29 17:27 bug#74091: 31.0.50; string-pixel-width in mode line disables region Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-10-30 14:59 ` Eli Zaretskii
2024-10-30 15:26 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
0 siblings, 1 reply; 8+ messages in thread
From: Eli Zaretskii @ 2024-10-30 14:59 UTC (permalink / raw)
To: Eshel Yaron; +Cc: 74091
> Date: Tue, 29 Oct 2024 18:27:05 +0100
> From: Eshel Yaron via "Bug reports for GNU Emacs,
> the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
>
>
>
> The new implementation of string-pixel-width has some unexpected effect
> when it is called from mode-line-format, as happens for example when
> mode-line-format-right-align occurs in mode-line-format:
>
> 1. emacs -Q
> 2. (setq-default mode-line-format
> '("" (:eval (progn (string-pixel-width "foo") nil))))
> 3. C-x C-f /path/to/emacs/lisp/subr.el
> 4. C-SPC
> 5. C-n
>
> At this point the region is expected to be active since we activated it
> in step 4. But in step 5 the mode line is updated, which calls
> string-pixel-width, which in turn unexpectedly disables the region.
Thanks, should be fixed now.
> I'm not really sure why this happens...
It happens because string-pixel-width modifies a buffer, and that sets
deactivate-mark, which then causes the region to be deactivated when
a command finishes. When you inject string-pixel-width into
mode-line-format, you indirectly cause it to be called from C-n and
the like, because those evaluate the mode-line format. So doing that
is quite a risky thing, in general.
^ permalink raw reply [flat|nested] 8+ messages in thread
* bug#74091: 31.0.50; string-pixel-width in mode line disables region
2024-10-30 14:59 ` Eli Zaretskii
@ 2024-10-30 15:26 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-10-30 16:01 ` Eli Zaretskii
0 siblings, 1 reply; 8+ messages in thread
From: Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-10-30 15:26 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 74091
Hi Eli,
Eli Zaretskii <eliz@gnu.org> writes:
>> Date: Tue, 29 Oct 2024 18:27:05 +0100
>> From: Eshel Yaron via "Bug reports for GNU Emacs,
>> the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
>>
>>
>>
>> The new implementation of string-pixel-width has some unexpected effect
>> when it is called from mode-line-format, as happens for example when
>> mode-line-format-right-align occurs in mode-line-format:
>>
>> 1. emacs -Q
>> 2. (setq-default mode-line-format
>> '("" (:eval (progn (string-pixel-width "foo") nil))))
>> 3. C-x C-f /path/to/emacs/lisp/subr.el
>> 4. C-SPC
>> 5. C-n
>>
>> At this point the region is expected to be active since we activated it
>> in step 4. But in step 5 the mode line is updated, which calls
>> string-pixel-width, which in turn unexpectedly disables the region.
>
> Thanks, should be fixed now.
Great! That seems to work.
>> I'm not really sure why this happens...
>
> It happens because string-pixel-width modifies a buffer, and that sets
> deactivate-mark, which then causes the region to be deactivated when
> a command finishes.
Hmm but string-pixel-width used to modify a buffer also in the old
implementation, and that never caused this issue... And in both the old
implementation and in the new one, the modification is in a different
buffer, is that expected to disable the mark in the original buffer?
> When you inject string-pixel-width into mode-line-format, you
> indirectly cause it to be called from C-n and the like, because those
> evaluate the mode-line format. So doing that is quite a risky thing,
> in general.
Well, that's how Emacs implements mode-line-format-right-align :)
Thanks for the quick response,
Eshel
^ permalink raw reply [flat|nested] 8+ messages in thread
* bug#74091: 31.0.50; string-pixel-width in mode line disables region
2024-10-30 15:26 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-10-30 16:01 ` Eli Zaretskii
2024-10-31 11:09 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
0 siblings, 1 reply; 8+ messages in thread
From: Eli Zaretskii @ 2024-10-30 16:01 UTC (permalink / raw)
To: Eshel Yaron; +Cc: 74091-done
> From: Eshel Yaron <me@eshelyaron.com>
> Cc: 74091@debbugs.gnu.org
> Date: Wed, 30 Oct 2024 16:26:38 +0100
>
> >> At this point the region is expected to be active since we activated it
> >> in step 4. But in step 5 the mode line is updated, which calls
> >> string-pixel-width, which in turn unexpectedly disables the region.
> >
> > Thanks, should be fixed now.
>
> Great! That seems to work.
Thanks for testing, I will close this bug.
> >> I'm not really sure why this happens...
> >
> > It happens because string-pixel-width modifies a buffer, and that sets
> > deactivate-mark, which then causes the region to be deactivated when
> > a command finishes.
>
> Hmm but string-pixel-width used to modify a buffer also in the old
> implementation, and that never caused this issue...
The new implementation also didn't cause this issue in some buffers.
For example, in *scratch*. Trying to understand the logic of a bug is
never a good investment of time.
> And in both the old
> implementation and in the new one, the modification is in a different
> buffer, is that expected to disable the mark in the original buffer?
The variable deactivate-mark only becomes buffer-local if set;
otherwise the global value will be changed.
> > When you inject string-pixel-width into mode-line-format, you
> > indirectly cause it to be called from C-n and the like, because those
> > evaluate the mode-line format. So doing that is quite a risky thing,
> > in general.
>
> Well, that's how Emacs implements mode-line-format-right-align :)
One reason why I don't like it. mode-line-format is evaluated in many
more contexts than most people realize, so putting arbitrary calls
there without a good understanding what those calls do and how is not
the best idea, although it will mostly work.
^ permalink raw reply [flat|nested] 8+ messages in thread
* bug#74091: 31.0.50; string-pixel-width in mode line disables region
2024-10-30 16:01 ` Eli Zaretskii
@ 2024-10-31 11:09 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-10-31 11:41 ` Eli Zaretskii
0 siblings, 1 reply; 8+ messages in thread
From: Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-10-31 11:09 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 74091-done
Eli Zaretskii <eliz@gnu.org> writes:
>> From: Eshel Yaron <me@eshelyaron.com>
>> Cc: 74091@debbugs.gnu.org
>> Date: Wed, 30 Oct 2024 16:26:38 +0100
>>
>> >> At this point the region is expected to be active since we activated it
>> >> in step 4. But in step 5 the mode line is updated, which calls
>> >> string-pixel-width, which in turn unexpectedly disables the region.
>> >
>> > Thanks, should be fixed now.
>>
>> Great! That seems to work.
>
> Thanks for testing, I will close this bug.
Since we don't fully understand the issue, it may manifest in more ways.
So I think closing the bug is premature.
After looking a bit more into it, it seems like the problem has to do
with the call to kill-all-local-variables in work-buffer--release, the
following patch circumvents the unexpected behavior, although I still
don't understand why:
diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el
index 5b47deb880e..b5cbe28afad 100644
--- a/lisp/emacs-lisp/subr-x.el
+++ b/lisp/emacs-lisp/subr-x.el
@@ -361,7 +361,7 @@ work-buffer--release
(erase-buffer))
(delete-all-overlays)
(let (change-major-mode-hook)
- (kill-all-local-variables t))
+ (kill-all-local-variables))
;; Make the buffer available again.
(push buffer work-buffer--list)))
;; If the maximum number of reusable work buffers is exceeded, kill
Here's another interesting data point:
--8<---------------cut here---------------start------------->8---
(defun foo ()
(interactive)
(with-current-buffer (get-buffer-create "some-buffer")
(kill-all-local-variables t)
(insert "foo")))
--8<---------------cut here---------------end--------------->8---
Invoking this command twice in a row in subr.el deactivates the region,
while the same without the argument to kill-all-local-variables keeps
the region active.
So the problem seems to be in a lower level than string-pixel-width...
>> >> I'm not really sure why this happens...
>> >
>> > It happens because string-pixel-width modifies a buffer, and that sets
>> > deactivate-mark, which then causes the region to be deactivated when
>> > a command finishes.
>>
>> Hmm but string-pixel-width used to modify a buffer also in the old
>> implementation, and that never caused this issue...
>
> The new implementation also didn't cause this issue in some buffers.
> For example, in *scratch*. Trying to understand the logic of a bug is
> never a good investment of time.
:)
As I'm sure you know, applying a crude fix without fully understanding
the problem is likely to hide other subtle bugs that may then be harder
to investigate.
>> And in both the old
>> implementation and in the new one, the modification is in a different
>> buffer, is that expected to disable the mark in the original buffer?
>
> The variable deactivate-mark only becomes buffer-local if set;
> otherwise the global value will be changed.
Could you perhaps elaborate? I see that running a command that modifies
a different buffer does not deactivate the region in the current buffer,
which is basically what I would expect.
Best,
Eshel
^ permalink raw reply related [flat|nested] 8+ messages in thread
* bug#74091: 31.0.50; string-pixel-width in mode line disables region
2024-10-31 11:09 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-10-31 11:41 ` Eli Zaretskii
2024-10-31 12:24 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
0 siblings, 1 reply; 8+ messages in thread
From: Eli Zaretskii @ 2024-10-31 11:41 UTC (permalink / raw)
To: Eshel Yaron; +Cc: 74091
> From: Eshel Yaron <me@eshelyaron.com>
> Cc: 74091-done@debbugs.gnu.org
> Date: Thu, 31 Oct 2024 12:09:20 +0100
>
> Invoking this command twice in a row in subr.el deactivates the region,
> while the same without the argument to kill-all-local-variables keeps
> the region active.
>
> So the problem seems to be in a lower level than string-pixel-width...
Killing local variables makes the global value of deactivate-mark be
in effect when the command loop decides whether to deactivate the
region after a command finishes.
> As I'm sure you know, applying a crude fix without fully understanding
> the problem is likely to hide other subtle bugs that may then be harder
> to investigate.
That's not a crude fix, that's what the ELisp manual tells us to do:
-- Variable: deactivate-mark
If an editor command sets this variable non-‘nil’, then the editor
command loop deactivates the mark after the command returns (if
Transient Mark mode is enabled). All the primitives that change
the buffer set ‘deactivate-mark’, to deactivate the mark when the
command is finished. Setting this variable makes it buffer-local.
To write Lisp code that modifies the buffer without causing
deactivation of the mark at the end of the command, bind
‘deactivate-mark’ to ‘nil’ around the code that does the
modification. For example:
(let (deactivate-mark)
(insert " "))
> >> And in both the old
> >> implementation and in the new one, the modification is in a different
> >> buffer, is that expected to disable the mark in the original buffer?
> >
> > The variable deactivate-mark only becomes buffer-local if set;
> > otherwise the global value will be changed.
>
> Could you perhaps elaborate? I see that running a command that modifies
> a different buffer does not deactivate the region in the current buffer,
> which is basically what I would expect.
You are asking me to elaborate about what? about the local value of
deactivate-mark or about why you see what you see (in a scenario you
haven't described)?
Look, you are welcome to keep debugging this if you are interested. I
invested enough of my time into figuring out why the region was
deactivated by C-n, and the solution I installed satisfies me. But
you are welcome to keep digging, and let me tell you what I found to
save you some non-trivial tinkering:
. The region is deactivated because of this in our command loop:
if (!NILP (Vdeactivate_mark))
/* If `select-active-regions' is non-nil, this call to
`deactivate-mark' also sets the PRIMARY selection. */
call0 (Qdeactivate_mark);
This is consistent with what the ELisp manual says, see the above
citation.
. Deactivate-mark is set non-nil by the low-level subroutines that
modify the buffer, again according to the manual. Two such
buffer-modification calls were present in string-pixel-width: one
in string-pixel-width itself, when it inserts the string into a
work buffer, the other in work-buffer--release where it erases the
work buffer. This happens in in prepare_to_modify_buffer_1:
signal_before_change (start, end, preserve_ptr);
Fset (Qdeactivate_mark, Qt);
. Here is a Lisp-level backtrace from one call which sets
deactivate-mark in your recipe, as collected by GDB:
Lisp Backtrace:
"string-pixel-width" (0x65a8940)
"progn" (0x65a8c40)
"eval" (0x65a8f90)
"posn-at-point" (0xa084200)
"line-move-visual" (0xa084170)
"line-move" (0xa084108)
"next-line" (0x65aea10)
"funcall-interactively" (0x65aea08)
"call-interactively" (0xa084078)
"command-execute" (0x65af798)
As you see, next-line calls posn-at-point, which formats the mode
line (to calculate is height), and that invokes the :eval form.
Here's the C backtrace which captures the details missing from the
above Lisp backtrace:
Thread 1 hit Hardware watchpoint 4: Vdeactivate_mark
Old value = XIL(0)
New value = XIL(0x30)
0x00bd2da5 in store_symval_forwarding (valcontents=..., newval=XIL(0x30),
buf=0x8f4c48) at data.c:1430
1430 *XOBJFWD (valcontents)->objvar = newval;
(gdb) bt
#0 0x00bd2da5 in store_symval_forwarding (valcontents=..., newval=XIL(0x30),
buf=0x8f4c48) at data.c:1430
#1 0x00bd3da3 in set_internal (symbol=XIL(0x6210), newval=XIL(0x30),
where=XIL(0xa0000000008f4c48), bindflag=SET_INTERNAL_SET) at data.c:1759
#2 0x00bd37c5 in Fset (symbol=XIL(0x6210), newval=XIL(0x30)) at data.c:1630
#3 0x00b5b757 in prepare_to_modify_buffer_1 (start=1, end=1, preserve_ptr=0x0)
at insdel.c:2073
#4 0x00b5b784 in prepare_to_modify_buffer (start=1, end=1, preserve_ptr=0x0)
at insdel.c:2083
#5 0x00b57e1b in insert_from_string_1 (string=XIL(0x800000000bc07ad8), pos=0,
pos_byte=0, nchars=3, nbytes=3, inherit=false, before_markers=false)
at insdel.c:1023
#6 0x00b57c15 in insert_from_string (string=XIL(0x800000000bc07ad8), pos=0,
pos_byte=0, length=3, length_byte=3, inherit=false) at insdel.c:974
#7 0x00be5dbf in general_insert_function (insert_func=0xb57249 <insert>,
insert_from_string_func=0xb57b92 <insert_from_string>, inherit=false,
nargs=1, args=0xa084250) at editfns.c:1336
#8 0x00be5e59 in Finsert (nargs=1, args=0xa084250) at editfns.c:1372
#9 0x00c71edc in exec_byte_code (fun=XIL(0xa0000000008f45b0),
args_template=513, nargs=1, args=0x65a8948) at bytecode.c:1417
#10 0x00c019a3 in funcall_lambda (fun=XIL(0xa0000000008f45b0), nargs=1,
arg_vector=0x65a8940) at eval.c:3238
#11 0x00c017be in apply_lambda (fun=XIL(0xa0000000008f45b0),
args=XIL(0xc00000000071c2d0), count=640) at eval.c:3201
#12 0x00bff56b in eval_sub (form=XIL(0xc00000000071c2e0)) at eval.c:2631
#13 0x00bf7cde in Fprogn (body=XIL(0xc00000000071c2b0)) at eval.c:439
#14 0x00bfec17 in eval_sub (form=XIL(0xc00000000071c2f0)) at eval.c:2535
#15 0x00bfe644 in Feval (form=XIL(0xc00000000071c2f0), lexical=XIL(0x30))
at eval.c:2448
#16 0x00c010df in funcall_subr (subr=0x128a1c0 <Seval>, numargs=2,
args=0x65a8f90) at eval.c:3149
#17 0x00c00a02 in funcall_general (fun=XIL(0xa00000000128a1c0), numargs=2,
args=0x65a8f90) at eval.c:3026
#18 0x00c00d92 in Ffuncall (nargs=3, args=0x65a8f88) at eval.c:3079
#19 0x00bfbd5f in internal_condition_case_n (bfun=0xc00c46 <Ffuncall>,
nargs=3, args=0x65a8f88, handlers=XIL(0x30),
hfun=0x9dfc51 <dsafe_eval_handler>) at eval.c:1687
#20 0x009dfd7d in dsafe__call (inhibit_quit=true, f=0xc00c46 <Ffuncall>,
nargs=3, args=0x65a8f88) at xdisp.c:3093
#21 0x009dfef9 in dsafe_eval (sexpr=XIL(0xc00000000071c2f0)) at xdisp.c:3129
#22 0x00a3033e in display_mode_element (it=0x65a93d0, depth=2, field_width=0,
precision=0, elt=XIL(0xc00000000071c300), props=XIL(0), risky=false)
at xdisp.c:28039
#23 0x00a308f2 in display_mode_element (it=0x65a93d0, depth=1, field_width=0,
precision=0, elt=XIL(0xc00000000071c290), props=XIL(0), risky=false)
at xdisp.c:28125
#24 0x00a2e976 in display_mode_line (w=0xbb0b428,
face_id=MODE_LINE_ACTIVE_FACE_ID, format=XIL(0xc00000000071c310))
at xdisp.c:27550
#25 0x009d54b2 in pos_visible_p (w=0xbb0b428, charpos=1, x=0x65adfec,
y=0x65adfe8, rtop=0x65adffc, rbot=0x65adff8, rowh=0x65adff4,
vpos=0x65adff0) at xdisp.c:1732
#26 0x00a65e8e in Fpos_visible_in_window_p (pos=XIL(0),
window=XIL(0xa00000000bb0b428), partially=XIL(0x30)) at window.c:2018
#27 0x00b2810d in Fposn_at_point (pos=XIL(0), window=XIL(0xa00000000bb0b428))
at keyboard.c:12552
Translation: posn-at-point called pos-visible-in-window-p, , which
called pos_visible_p, which called display_mode_line. That
eventually called the :eval form, and inserted the string via
insert_from_string_1, which called prepare_to_modify_buffer_1,
which set deactivate-mark to t.
That's what I saw and what led me to my solution, according to what
the ELisp manual says.
^ permalink raw reply [flat|nested] 8+ messages in thread
* bug#74091: 31.0.50; string-pixel-width in mode line disables region
2024-10-31 11:41 ` Eli Zaretskii
@ 2024-10-31 12:24 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-10-31 14:35 ` Eli Zaretskii
0 siblings, 1 reply; 8+ messages in thread
From: Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-10-31 12:24 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 74091
Eli Zaretskii <eliz@gnu.org> writes:
>> > The variable deactivate-mark only becomes buffer-local if set;
>> > otherwise the global value will be changed.
>>
>> Could you perhaps elaborate? I see that running a command that modifies
>> a different buffer does not deactivate the region in the current buffer,
>> which is basically what I would expect.
>
> You are asking me to elaborate about what? about the local value of
> deactivate-mark or about why you see what you see (in a scenario you
> haven't described)?
What I'm asking is: under what circumstances is it expected that
after changing deactivate-mark in another buffer the mark is deactivated
in the current buffer?
> Look, you are welcome to keep debugging this if you are interested. I
> invested enough of my time into figuring out why the region was
> deactivated by C-n, and the solution I installed satisfies me. But
> you are welcome to keep digging, and let me tell you what I found to
> save you some non-trivial tinkering:
Thank you, I'll keep digging and let you know if I figure it out.
^ permalink raw reply [flat|nested] 8+ messages in thread
* bug#74091: 31.0.50; string-pixel-width in mode line disables region
2024-10-31 12:24 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-10-31 14:35 ` Eli Zaretskii
0 siblings, 0 replies; 8+ messages in thread
From: Eli Zaretskii @ 2024-10-31 14:35 UTC (permalink / raw)
To: Eshel Yaron; +Cc: 74091
> From: Eshel Yaron <me@eshelyaron.com>
> Cc: 74091@debbugs.gnu.org
> Date: Thu, 31 Oct 2024 13:24:34 +0100
>
> Eli Zaretskii <eliz@gnu.org> writes:
>
> >> > The variable deactivate-mark only becomes buffer-local if set;
> >> > otherwise the global value will be changed.
> >>
> >> Could you perhaps elaborate? I see that running a command that modifies
> >> a different buffer does not deactivate the region in the current buffer,
> >> which is basically what I would expect.
> >
> > You are asking me to elaborate about what? about the local value of
> > deactivate-mark or about why you see what you see (in a scenario you
> > haven't described)?
>
> What I'm asking is: under what circumstances is it expected that
> after changing deactivate-mark in another buffer the mark is deactivated
> in the current buffer?
AFAIU, whenever deactivate-mark is not local to the buffer which is
being changed.
> > Look, you are welcome to keep debugging this if you are interested. I
> > invested enough of my time into figuring out why the region was
> > deactivated by C-n, and the solution I installed satisfies me. But
> > you are welcome to keep digging, and let me tell you what I found to
> > save you some non-trivial tinkering:
>
> Thank you, I'll keep digging and let you know if I figure it out.
TIA.
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2024-10-31 14:35 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-29 17:27 bug#74091: 31.0.50; string-pixel-width in mode line disables region Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-10-30 14:59 ` Eli Zaretskii
2024-10-30 15:26 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-10-30 16:01 ` Eli Zaretskii
2024-10-31 11:09 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-10-31 11:41 ` Eli Zaretskii
2024-10-31 12:24 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-10-31 14:35 ` Eli Zaretskii
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).