unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Eli Zaretskii <eliz@gnu.org>
To: Eshel Yaron <me@eshelyaron.com>
Cc: 74091@debbugs.gnu.org
Subject: bug#74091: 31.0.50; string-pixel-width in mode line disables region
Date: Thu, 31 Oct 2024 13:41:02 +0200	[thread overview]
Message-ID: <86ed3w33g1.fsf@gnu.org> (raw)
In-Reply-To: <m11pzwplzz.fsf@macbookpro.home> (message from Eshel Yaron on Thu, 31 Oct 2024 12:09:20 +0100)

> 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.





  reply	other threads:[~2024-10-31 11:41 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=86ed3w33g1.fsf@gnu.org \
    --to=eliz@gnu.org \
    --cc=74091@debbugs.gnu.org \
    --cc=me@eshelyaron.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).