GNU bug report logs - #77217
31.0.50; cursor stuck on long image under display-line-numbers-mode and visual-line-mode

Previous Next

Package: emacs;

Reported by: Yifan Zhu <fanzhuyifan <at> gmail.com>

Date: Sun, 23 Mar 2025 18:46:01 UTC

Severity: normal

Found in version 31.0.50

Done: Eli Zaretskii <eliz <at> gnu.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 77217 in the body.
You can then email your comments to 77217 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#77217; Package emacs. (Sun, 23 Mar 2025 18:46:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Yifan Zhu <fanzhuyifan <at> gmail.com>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Sun, 23 Mar 2025 18:46:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Yifan Zhu <fanzhuyifan <at> gmail.com>
To: bug-gnu-emacs <at> gnu.org
Subject: 31.0.50; cursor stuck on long image under display-line-numbers-mode
 and visual-line-mode
Date: Sun, 23 Mar 2025 11:45:12 -0700
[Message part 1 (text/plain, inline)]
--text follows this line--

emacs -Q test.txt (attached)
insert image (long.png, attached) after last word in second line, no space
(set-frame-size (selected-frame) 80 36)
increase size (ctrl+scroll) (see reference.png, attached)
move cursor to bottom and try to move cursor back up
cursor gets stuck at the image


In GNU Emacs 31.0.50 (build 22, x86_64-pc-linux-gnu, GTK+ Version
3.24.49, cairo version 1.18.4)
Repository revision: 9e005e9da06f71441e643f7ecbe309e35f68680a
Repository branch: master
System Description: Arch Linux

Configured using:
'configure --with-pgtk --sysconfdir=/etc --localstatedir=/var
--disable-build-details --with-cairo --with-harfbuzz --with-libsystemd
--with-modules --with-native-compilation=aot --with-tree-sitter
'CFLAGS=-ggdb3 -O0' 'CXXFLAGS=-ggdb3 -O0' LDFLAGS=-ggdb3
--prefix=/home/yifan/packages/emacs-git/build
--exec-prefix=/home/yifan/packages/emacs-git/build'

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG
LCMS2 LIBOTF LIBSYSTEMD LIBXML2 MODULES NATIVE_COMP NOTIFY INOTIFY
PDUMPER PGTK PNG RSVG SECCOMP SOUND SQLITE3 THREADS TIFF
TOOLKIT_SCROLL_BARS TREE_SITTER WEBP XIM GTK3 ZLIB

Important settings:
value of $LANG: en_US.utf8
value of $XMODIFIERS: @im=fcitx
locale-coding-system: utf-8-unix

Major mode: Text

Minor modes in effect:
text-scale-mode: t
display-line-numbers-mode: t
tooltip-mode: t
global-eldoc-mode: t
show-paren-mode: t
electric-indent-mode: t
mouse-wheel-mode: t
tool-bar-mode: t
menu-bar-mode: t
file-name-shadow-mode: t
global-font-lock-mode: t
font-lock-mode: t
blink-cursor-mode: t
minibuffer-regexp-mode: t
line-number-mode: t
visual-line-mode: t
indent-tabs-mode: t
transient-mark-mode: t
auto-composition-mode: t
auto-encryption-mode: t
auto-compression-mode: t

Load-path shadows:
None found.

Features:
(shadow sort mail-extr emacsbug lisp-mnt message mailcap yank-media puny
dired dired-loaddefs rfc822 mml mml-sec password-cache epa derived epg
rfc6068 epg-config gnus-util mm-decode mm-bodies mm-encode mail-parse
rfc2231 mailabbrev gmm-utils mailheader sendmail rfc2047 rfc2045
ietf-drums mm-util mail-prsvr mail-utils face-remap compile
text-property-search comint ansi-osc ansi-color ring comp-run bytecomp
byte-compile comp-common rx time-date subr-x cl-loaddefs cl-lib
display-line-numbers rmc iso-transl tooltip cconv eldoc paren electric
uniquify ediff-hook vc-hooks lisp-float-type elisp-mode mwheel
term/pgtk-win pgtk-win term/common-win touch-screen pgtk-dnd tool-bar
dnd fontset image regexp-opt fringe tabulated-list replace newcomment
text-mode lisp-mode prog-mode register page tab-bar menu-bar rfn-eshadow
isearch easymenu timer select scroll-bar mouse jit-lock font-lock syntax
font-core term/tty-colors frame minibuffer nadvice seq simple cl-generic
indonesian philippine cham georgian utf-8-lang misc-lang vietnamese
tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek
romanian slovak czech european ethiopic indian cyrillic chinese
composite emoji-zwj charscript charprop case-table epa-hook
jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs
theme-loaddefs faces cus-face macroexp files window text-properties
overlay sha1 md5 base64 format env code-pages mule custom widget keymap
hashtable-print-readable backquote threads dbusbind inotify
dynamic-setting system-font-setting font-render-setting cairo gtk pgtk
lcms2 multi-tty move-toolbar make-network-process tty-child-frames
native-compile emacs)

Memory information:
((conses 16 67339 20133) (symbols 48 6859 0) (strings 32 17412 2459)
(string-bytes 1 703591) (vectors 16 10908)
(vector-slots 8 151951 9248) (floats 8 28 20) (intervals 56 284 0)
(buffers 992 14))
[reference.png (image/png, attachment)]
[test.txt (text/plain, attachment)]
[long.png (image/png, attachment)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#77217; Package emacs. (Tue, 25 Mar 2025 17:15:02 GMT) Full text and rfc822 format available.

Message #8 received at 77217 <at> debbugs.gnu.org (full text, mbox):

From: Yifan Zhu <fanzhuyifan <at> gmail.com>
To: 77217 <at> debbugs.gnu.org
Subject: issue seems to be in vertical-motion
Date: Tue, 25 Mar 2025 10:14:04 -0700
The issue seems to be in `vertical-motion`, as the cursor is stuck on 
that image when directly executing (vertical-motion (cons 10 -1))

(both display-line-numbers-mode and visual-line-mode need to be enabled. 
Forgot to mention that in the original email)






Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#77217; Package emacs. (Tue, 25 Mar 2025 18:23:02 GMT) Full text and rfc822 format available.

Message #11 received at 77217 <at> debbugs.gnu.org (full text, mbox):

From: Yifan Zhu <fanzhuyifan <at> gmail.com>
To: 77217 <at> debbugs.gnu.org
Subject: move_it_to moves point to line after to_charpos when to_charpos is an
 image.
Date: Tue, 25 Mar 2025 11:21:47 -0700
When to_charpos is an image, move_it_to moves the point to to_charpos+1, 
which is on the next line. This effetively undoes the move_it_by_lines 
(&it, max (PTRDIFF_MIN, nlines)); later.


Unsure how this should be fixed though.


See gdb log below:

charpos=126 is long image; charpos=127 is on next visual line


Thread 1 "emacs" hit Breakpoint 9, Fvertical_motion 
(lines=0x5e34b7cbbb63, window=0x0, cur_col=0x0) at indent.c:2191
2191    {
+next
2195      Lisp_Object lcols = Qnil;
+next
2196      void *itdata = NULL;
+next
2197      specpdl_ref count = SPECPDL_INDEX ();
+next
2200      if (CONSP (lines))
+next
2202          lcols = XCAR (lines);
+next
2203          CHECK_NUMBER (lcols);
+next
2204          lines = XCDR (lines);
+next
2207      CHECK_FIXNUM (lines);
+next
2208      w = decode_live_window (window);
+next
2210      if (XBUFFER (w->contents) != current_buffer)
+next
2222      if (noninteractive)
+next
2231          ptrdiff_t it_start, it_overshoot_count = 0;
+next
2233          bool overshoot_handled = 0;
+next
2234          bool disp_string_at_start_p = 0;
+next
2235          ptrdiff_t nlines = XFIXNUM (lines);
+next
2236          int vpos_init = 0;
+next
2237          double start_col UNINIT;
+next
2238          int start_x UNINIT;
+next
2239          int to_x = -1;
+next
2241          bool start_x_given = !NILP (cur_col);
+next
2242          if (start_x_given)
+next
2252          int lnum_width = 0;
+next
2253          int lnum_pixel_width = 0;
+next
2254          if (!NILP (Vdisplay_line_numbers))
+next
2255        line_number_display_width (w, &lnum_width, &lnum_pixel_width);
+next
2256          SET_TEXT_POS (pt, PT, PT_BYTE);
+next
2257          itdata = bidi_shelve_cache ();
+next
2258          record_unwind_protect_void (unwind_display_working_on_window);
+next
2259          display_working_on_window_p = true;
+next
2260          start_display (&it, w, pt);
2: it->current->pos = {charpos = 0, bytepos = 0}
+next
2261          it.lnum_width = lnum_width;
2: it->current->pos = {charpos = 126, bytepos = 126}
+next
2262          first_x = it.first_visible_x;
+next
2263          it_start = IT_CHARPOS (it);
+next
2266          if (it.cmp_it.id >= 0)
+next
2268          else if (it.method == GET_FROM_STRING)
+next
2294          !((it.method == GET_FROM_IMAGE && it.image_id >= 0)
+next
2291        it_overshoot_count =
+next
2297          if (start_x_given)
+next
2307          reseat_at_previous_visible_line_start (&it);
2: it->current->pos = {charpos = 126, bytepos = 126}
+next
2308          it.current_x = it.hpos = 0;
2: it->current->pos = {charpos = 121, bytepos = 121}
+next
2310          if (IT_CHARPOS (it) != PT)
+next
2324                (!disp_string_at_start_p
+next

Thread 1 "emacs" hit Breakpoint 4, Fvertical_motion 
(lines=0xfffffffffffffffe, window=0x0, cur_col=0x0) at indent.c:2323
2323        move_it_to (&it,
+next
2326                ? PT
+next
2323        move_it_to (&it,
2: it->current->pos = {charpos = 121, bytepos = 121}
+next

Thread 1 "emacs" hit Breakpoint 8, Fvertical_motion 
(lines=0xfffffffffffffffe, window=0x0, cur_col=0x0) at indent.c:2335
2335          if (IT_CHARPOS (it) > it_start)
2: it->current->pos = {charpos = 127, bytepos = 127}
+next
2341          if (it_overshoot_count < 0
+next
2345          else if (it_overshoot_count == 1 && it.vpos == 0
+next
2354          else if (disp_string_at_start_p && it.vpos > 0)
+next
2364          if (it.line_wrap == TRUNCATE && it.current_x >= 
it.last_visible_x
+next
2367          if (it_overshoot_count > 0)
+next
2370          overshoot_handled = 1;
+next
2388          if (!NILP (lcols))
+next
2390          window_column_x (w, window, XFLOATINT (lcols), lcols)
+next
2391          + lnum_pixel_width;
+next
2389        to_x =
+next
2392          if (nlines <= 0)
+next
2394          it.vpos = vpos_init;
+next
2395          it.current_y = 0;
+next
2398          if ((nlines < 0 && IT_CHARPOS (it) > BEGV)
+next
2400            move_it_by_lines (&it, max (PTRDIFF_MIN, nlines));
2: it->current->pos = {charpos = 127, bytepos = 127}
+next
2449          if (!NILP (lcols))
2: it->current->pos = {charpos = 126, bytepos = 126}
+next
2451          move_it_in_display_line (&it, ZV, first_x + to_x, MOVE_TO_X);
2: it->current->pos = {charpos = 126, bytepos = 126}
+next
2458          if (nlines >= 0 && it.area == TEXT_AREA)
2: it->current->pos = {charpos = 126, bytepos = 126}
+next
2472          SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
2: it->current->pos = {charpos = 126, bytepos = 126}
+next
2473          bidi_unshelve_cache (itdata, 0);
2: it->current->pos = {charpos = 126, bytepos = 126}
+next
2476      return unbind_to (count, make_fixnum (it.vpos));
2: it->current->pos = {charpos = 126, bytepos = 126}
+next
2477    }
2: it->current->pos = {charpos = 126, bytepos = 126}
+next
0x000073b6f2385ddd in 
F6c696e652d6d6f76652d76697375616c_line_move_visual_0 () from 
/home/yifan/packages/emacs-git/build/bin/../lib/emacs/31.0.50/native-lisp/31.0.50-28637a6f/preloaded/simple-fab5b0cf-4c8dda8b.eln
+next
Single stepping until exit from function 
F6c696e652d6d6f76652d76697375616c_line_move_visual_0,
which has no line number information.





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#77217; Package emacs. (Wed, 26 Mar 2025 14:49:02 GMT) Full text and rfc822 format available.

Message #14 received at 77217 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Yifan Zhu <fanzhuyifan <at> gmail.com>
Cc: 77217 <at> debbugs.gnu.org
Subject: Re: bug#77217: move_it_to moves point to line after to_charpos when
 to_charpos is an image.
Date: Wed, 26 Mar 2025 16:47:51 +0200
> Date: Tue, 25 Mar 2025 11:21:47 -0700
> From: Yifan Zhu <fanzhuyifan <at> gmail.com>
> 
> When to_charpos is an image, move_it_to moves the point to to_charpos+1, 
> which is on the next line. This effetively undoes the move_it_by_lines 
> (&it, max (PTRDIFF_MIN, nlines)); later.
> 
> 
> Unsure how this should be fixed though.

I think the problem is not in vertical-motion at all.  The problem is
that the "normal" display of embedded images allows us to get into a
situation with layout which vertical-motion doesn't expect to happen,
when these two minor modes are turned on.  Please try the patch below
and see if it gives good results.

diff --git a/src/xdisp.c b/src/xdisp.c
index 4e8bb7d..7afa64d 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -31956,12 +31956,13 @@ produce_image_glyph (struct it *it)
      word-wrap, unless the image starts at column zero, because
      wrapping correctly needs the real pixel width of the image.  */
   if ((it->line_wrap != WORD_WRAP
-       || it->hpos == 0
+       || it->hpos == 0 + (it->lnum_width ? it->lnum_width + 2 : 0)
        /* Always crop images larger than the window-width, minus 1 space.  */
        || it->pixel_width > it->last_visible_x - FRAME_COLUMN_WIDTH (it->f))
       && (crop = it->pixel_width - (it->last_visible_x - it->current_x),
 	  crop > 0)
-      && (it->hpos == 0 || it->pixel_width > it->last_visible_x / 4))
+      && (it->hpos == 0 + (it->lnum_width ? it->lnum_width + 2 : 0)
+	  || it->pixel_width > it->last_visible_x / 4))
     {
       it->pixel_width -= crop;
       slice.width -= crop;




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#77217; Package emacs. (Wed, 26 Mar 2025 16:40:01 GMT) Full text and rfc822 format available.

Message #17 received at 77217 <at> debbugs.gnu.org (full text, mbox):

From: Yifan Zhu <fanzhuyifan <at> gmail.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 77217 <at> debbugs.gnu.org
Subject: Re: bug#77217: move_it_to moves point to line after to_charpos when
 to_charpos is an image.
Date: Wed, 26 Mar 2025 09:39:18 -0700
[Message part 1 (text/plain, inline)]
Hi,

Thank you for your prompt response!


On 3/26/25 7:47 AM, Eli Zaretskii wrote:
> I think the problem is not in vertical-motion at all.  The problem is
> that the "normal" display of embedded images allows us to get into a
> situation with layout which vertical-motion doesn't expect to happen,
> when these two minor modes are turned on.  Please try the patch below
> and see if it gives good results.
>
> diff --git a/src/xdisp.c b/src/xdisp.c
> index 4e8bb7d..7afa64d 100644
> --- a/src/xdisp.c
> +++ b/src/xdisp.c
> @@ -31956,12 +31956,13 @@ produce_image_glyph (struct it *it)
>        word-wrap, unless the image starts at column zero, because
>        wrapping correctly needs the real pixel width of the image.  */
>     if ((it->line_wrap != WORD_WRAP
> -       || it->hpos == 0
> +       || it->hpos == 0 + (it->lnum_width ? it->lnum_width + 2 : 0)
>          /* Always crop images larger than the window-width, minus 1 space.  */
>          || it->pixel_width > it->last_visible_x - FRAME_COLUMN_WIDTH (it->f))
>         && (crop = it->pixel_width - (it->last_visible_x - it->current_x),
>   	  crop > 0)
> -      && (it->hpos == 0 || it->pixel_width > it->last_visible_x / 4))
> +      && (it->hpos == 0 + (it->lnum_width ? it->lnum_width + 2 : 0)
> +	  || it->pixel_width > it->last_visible_x / 4))
>       {
>         it->pixel_width -= crop;
>         slice.width -= crop;

Unfortunately with this patch, I could still get the cursor stuck. See 
stuck.png and following gdb logs when it gets stuck:

+bt
#0  produce_image_glyph (it=0x7fff258031d0) at xdisp.c:31958
#1  0x000057ba672d79e1 in gui_produce_glyphs (it=0x7fff258031d0) at 
xdisp.c:33672
#2  0x000057ba672bcb18 in display_line (it=0x7fff258031d0, 
cursor_vpos=6) at xdisp.c:25720
#3  0x000057ba672aeb0a in try_window (window=0x57baa27b9c2d, pos=..., 
flags=1) at xdisp.c:21413
#4  0x000057ba672abaa3 in redisplay_window (window=0x57baa27b9c2d, 
just_this_one_p=true) at xdisp.c:20784
#5  0x000057ba672a2fa9 in redisplay_window_1 (window=0x57baa27b9c2d) at 
xdisp.c:18268
#6  0x000057ba67460bf1 in internal_condition_case_1 (bfun=0x57ba672a2f67 
<redisplay_window_1>, arg=0x57baa27b9c2d, handlers=0x710b5b9a7d33, 
hfun=0x57ba672a2df7 <redisplay_window_error>) at eval.c:1644
#7  0x000057ba672a207d in redisplay_internal () at xdisp.c:17774
#8  0x000057ba6729fb21 in redisplay () at xdisp.c:16802
#9  0x000057ba6739a0cb in read_char (commandflag=1, map=0x57baa32452f3, 
prev_event=0x0, used_mouse_menu=0x7fff2580871a, end_time=0x0) at 
keyboard.c:2672
#10 0x000057ba673ad0fb in read_key_sequence (keybuf=0x7fff25808990, 
prompt=0x0, dont_downcase_last=false, can_return_switch_frame=true, 
fix_current_buffer=true, prevent_redisplay=false, 
disable_text_conversion_p=false) at keyboard.c:10848
#11 0x000057ba67396751 in command_loop_1 () at keyboard.c:1424
#12 0x000057ba67460b4a in internal_condition_case (bfun=0x57ba67396343 
<command_loop_1>, handlers=0x90, hfun=0x57ba67395874 <cmd_error>) at 
eval.c:1620
#13 0x000057ba67395f90 in command_loop_2 (handlers=0x90) at keyboard.c:1163
#14 0x000057ba6746009f in internal_catch (tag=0x11f40, 
func=0x57ba67395f66 <command_loop_2>, arg=0x90) at eval.c:1300
#15 0x000057ba67395f22 in command_loop () at keyboard.c:1141
#16 0x000057ba67395416 in recursive_edit_1 () at keyboard.c:749
#17 0x000057ba673955c2 in Frecursive_edit () at keyboard.c:832
#18 0x000057ba6739167f in main (argc=3, argv=0x7fff25808e78) at emacs.c:2560
+p *it
$11 = {window = 0x57baa27b9c2d, w = 0x57baa27b9c28, f = 0x57baa27b99d0, 
method = GET_FROM_IMAGE, stop_charpos = 127, prev_stop = 126, 
base_level_stop = 126, end_charpos = 302, medium_narrowing_begv = 0, 
medium_narrowing_zv = 0, large_narrowing_begv = 0, large_narrowing_zv = 
0, s = 0x0, string_nchars = 0, multibyte_p = true, tab_line_p = false, 
header_line_p = false, string_from_display_prop_p = false, 
string_from_prefix_prop_p = false, from_disp_prop_p = true, ellipsis_p = 
false, avoid_cursor_p = false, dp = 0x0, dpvec = 0x0, dpend = 0x0, 
dpvec_char_len = 0, dpvec_face_id = 0, saved_face_id = 34, ctl_chars = 
{0x0 <repeats 16 times>}, start = {pos = {charpos = 121, bytepos = 121}, 
overlay_string_index = -1, string_pos = {charpos = -1, bytepos = -1}, 
dpvec_index = -1}, current = {pos = {charpos = 126, bytepos = 126}, 
overlay_string_index = -1, string_pos = {charpos = -1, bytepos = -1}, 
dpvec_index = -1}, n_overlay_strings = 0, overlay_strings_charpos = 126, 
overlay_strings = {0x0 <repeats 16 times>}, string_overlays = {0x0 
<repeats 16 times>}, string = 0x0, from_overlay = 0x0, stack = {{string 
= 0x0, string_nchars = 0, end_charpos = 302, stop_charpos = 127, 
prev_stop = 126, base_level_stop = 126, cmp_it = {stop_pos = 129, id = 
-1, ch = -2, rule_idx = 0, lookback = 0, nglyphs = 0, reversed_p = 
false, parent_it = 0x7fff258031d0, charpos = 0, nchars = 0, nbytes = 0, 
from = 0, to = 0, width = 0}, face_id = 34, u = {image = {object = 0x0, 
slice = {x = 0x0, y = 0x0, width = 0x0, height = 0x0}, image_id = 0}, 
stretch = {object = 0x0}, xwidget = {object = 0x0}}, position = {charpos 
= 127, bytepos = 127}, current = {pos = {charpos = 127, bytepos = 127}, 
overlay_string_index = -1, string_pos = {charpos = -1, bytepos = -1}, 
dpvec_index = -1}, from_overlay = 0x0, area = TEXT_AREA, method = 
GET_FROM_BUFFER, paragraph_embedding = NEUTRAL_DIR, multibyte_p = true, 
string_from_display_prop_p = false, string_from_prefix_prop_p = false, 
display_ellipsis_p = false, avoid_cursor_p = false, bidi_p = true, 
from_disp_prop_p = false, line_wrap = WORD_WRAP, voffset = 0, 
space_width = 0x0, font_height = 0x0}, {string = 0x0, string_nchars = 0, 
end_charpos = 0, stop_charpos = 0, prev_stop = 0, base_level_stop = 0, 
cmp_it = {stop_pos = 0, id = 0, ch = 0, rule_idx = 0, lookback = 0, 
nglyphs = 0, reversed_p = false, parent_it = 0x0, charpos = 0, nchars = 
0, nbytes = 0, from = 0, to = 0, width = 0}, face_id = 0, u = {image = 
{object = 0x0, slice = {x = 0x0, y = 0x0, width = 0x0, height = 0x0}, 
image_id = 0}, stretch = {object = 0x0}, xwidget = {object = 0x0}}, 
position = {charpos = 0, bytepos = 0}, current = {pos = {charpos = 0, 
bytepos = 0}, overlay_string_index = 0, string_pos = {charpos = 0, 
bytepos = 0}, dpvec_index = 0}, from_overlay = 0x0, area = 
LEFT_MARGIN_AREA, method = GET_FROM_BUFFER, paragraph_embedding = 
NEUTRAL_DIR, multibyte_p = false, string_from_display_prop_p = false, 
string_from_prefix_prop_p = false, display_ellipsis_p = false, 
avoid_cursor_p = false, bidi_p = false, from_disp_prop_p = false, 
line_wrap = TRUNCATE, voffset = 0, space_width = 0x0, font_height = 
0x0}, {string = 0x0, string_nchars = 0, end_charpos = 0, stop_charpos = 
0, prev_stop = 0, base_level_stop = 0, cmp_it = {stop_pos = 0, id = 0, 
ch = 0, rule_idx = 0, lookback = 0, nglyphs = 0, reversed_p = false, 
parent_it = 0x0, charpos = 0, nchars = 0, nbytes = 0, from = 0, to = 0, 
width = 0}, face_id = 0, u = {image = {object = 0x0, slice = {x = 0x0, y 
= 0x0, width = 0x0, height = 0x0}, image_id = 0}, stretch = {object = 
0x0}, xwidget = {object = 0x0}}, position = {charpos = 0, bytepos = 0}, 
current = {pos = {charpos = 0, bytepos = 0}, overlay_string_index = 0, 
string_pos = {charpos = 0, bytepos = 0}, dpvec_index = 0}, from_overlay 
= 0x0, area = LEFT_MARGIN_AREA, method = GET_FROM_BUFFER, 
paragraph_embedding = NEUTRAL_DIR, multibyte_p = false, 
string_from_display_prop_p = false, string_from_prefix_prop_p = false, 
display_ellipsis_p = false, avoid_cursor_p = false, bidi_p = false, 
from_disp_prop_p = false, line_wrap = TRUNCATE, voffset = 0, space_width 
= 0x0, font_height = 0x0}, {string = 0x0, string_nchars = 0, end_charpos 
= 0, stop_charpos = 0, prev_stop = 0, base_level_stop = 0, cmp_it = 
{stop_pos = 0, id = 0, ch = 0, rule_idx = 0, lookback = 0, nglyphs = 0, 
reversed_p = false, parent_it = 0x0, charpos = 0, nchars = 0, nbytes = 
0, from = 0, to = 0, width = 0}, face_id = 0, u = {image = {object = 
0x0, slice = {x = 0x0, y = 0x0, width = 0x0, height = 0x0}, image_id = 
0}, stretch = {object = 0x0}, xwidget = {object = 0x0}}, position = 
{charpos = 0, bytepos = 0}, current = {pos = {charpos = 0, bytepos = 0}, 
overlay_string_index = 0, string_pos = {charpos = 0, bytepos = 0}, 
dpvec_index = 0}, from_overlay = 0x0, area = LEFT_MARGIN_AREA, method = 
GET_FROM_BUFFER, paragraph_embedding = NEUTRAL_DIR, multibyte_p = false, 
string_from_display_prop_p = false, string_from_prefix_prop_p = false, 
display_ellipsis_p = false, avoid_cursor_p = false, bidi_p = false, 
from_disp_prop_p = false, line_wrap = TRUNCATE, voffset = 0, space_width 
= 0x0, font_height = 0x0}, {string = 0x0, string_nchars = 0, end_charpos 
= 0, stop_charpos = 0, prev_stop = 0, base_level_stop = 0, cmp_it = 
{stop_pos = 0, id = 0, ch = 0, rule_idx = 0, lookback = 0, nglyphs = 0, 
reversed_p = false, parent_it = 0x0, charpos = 0, nchars = 0, nbytes = 
0, from = 0, to = 0, width = 0}, face_id = 0, u = {image = {object = 
0x0, slice = {x = 0x0, y = 0x0, width = 0x0, height = 0x0}, image_id = 
0}, stretch = {object = 0x0}, xwidget = {object = 0x0}}, position = 
{charpos = 0, bytepos = 0}, current = {pos = {charpos = 0, bytepos = 0}, 
overlay_string_index = 0, string_pos = {charpos = 0, bytepos = 0}, 
dpvec_index = 0}, from_overlay = 0x0, area = LEFT_MARGIN_AREA, method = 
GET_FROM_BUFFER, paragraph_embedding = NEUTRAL_DIR, multibyte_p = false, 
string_from_display_prop_p = false, string_from_prefix_prop_p = false, 
display_ellipsis_p = false, avoid_cursor_p = false, bidi_p = false, 
from_disp_prop_p = false, line_wrap = TRUNCATE, voffset = 0, space_width 
= 0x0, font_height = 0x0}}, sp = 1, selective = 0, what = IT_IMAGE, 
face_id = 34, selective_display_ellipsis_p = true, ctl_arrow_p = true, 
face_box_p = false, start_of_box_run_p = false, end_of_box_run_p = 
false, overlay_strings_at_end_processed_p = false, 
ignore_overlay_strings_at_pos_p = false, glyph_not_available_p = false, 
starts_in_middle_of_char_p = false, face_before_selective_p = false, 
constrain_row_ascent_descent_p = false, line_number_produced_p = true, 
align_visually_p = false, line_wrap = WORD_WRAP, base_face_id = 34, c = 
101, len = 1, cmp_it = {stop_pos = 129, id = -1, ch = -2, rule_idx = 0, 
lookback = 0, nglyphs = 0, reversed_p = false, parent_it = 
0x7fff258031d0, charpos = 0, nchars = 0, nbytes = 0, from = 0, to = 0, 
width = 0}, char_to_display = 101, glyphless_method = 
GLYPHLESS_DISPLAY_THIN_SPACE, image_id = 13, xwidget = 0x0, slice = {x = 
0x0, y = 0x0, width = 0x0, height = 0x0}, space_width = 0x0, voffset = 
0, tab_width = 8, font_height = 0x0, object = 0x57baa2a18f9d, position = 
{charpos = 126, bytepos = 126}, truncation_pixel_width = 0, 
continuation_pixel_width = 20, first_visible_x = 0, last_visible_x = 
1012, last_visible_y = 918, extra_line_spacing = 0, 
max_extra_line_spacing = 0, override_ascent = -1, override_descent = 0, 
override_boff = 0, glyph_row = 0x57baa2af4120, area = TEXT_AREA, nglyphs 
= 1, pixel_width = 948, ascent = 20, descent = 20, max_ascent = 36, 
max_descent = 10, phys_ascent = 20, phys_descent = 20, max_phys_ascent = 
25, max_phys_descent = 8, current_x = 180, wrap_prefix_width = 0, 
continuation_lines_width = 0, eol_pos = {charpos = 0, bytepos = 0}, 
current_y = 230, first_vpos = 0, vpos = 5, hpos = 9, lnum = 3, 
lnum_bytepos = 121, lnum_width = 2, lnum_pixel_width = 80, pt_lnum = 0, 
stretch_adjust = 0, left_user_fringe_bitmap = 0, 
right_user_fringe_bitmap = 0, left_user_fringe_face_id = 0, 
right_user_fringe_face_id = 0, bidi_p = true, bidi_it = {bytepos = 126, 
charpos = 126, ch = 65532, nchars = 1, ch_len = 1, type = STRONG_L, 
type_after_wn = NEUTRAL_ON, orig_type = NEUTRAL_ON, resolved_level = 0 
'\000', isolate_level = 0 '\000', invalid_levels = 0, invalid_isolates = 
0, prev = {charpos = 125, type = STRONG_L, orig_type = STRONG_L}, 
last_strong = {charpos = 125, type = STRONG_L, orig_type = STRONG_L}, 
next_for_neutral = {charpos = -1, type = UNKNOWN_BT, orig_type = 
UNKNOWN_BT}, prev_for_neutral = {charpos = 125, type = STRONG_L, 
orig_type = STRONG_L}, next_for_ws = {charpos = -1, type = UNKNOWN_BT, 
orig_type = UNKNOWN_BT}, bracket_pairing_pos = -1, bracket_enclosed_type 
= UNKNOWN_BT, next_en_pos = 0, next_en_type = UNKNOWN_BT, sos = L2R, 
scan_dir = 1, disp_pos = 302, disp_prop = 0, stack_idx = 0, level_stack 
= {{next_for_neutral_pos = 0, next_for_neutral_type = 0, 
last_strong_type = 0, prev_for_neutral_type = 0, level = 0 '\000', flags 
= 0 '\000'} <repeats 128 times>}, string = {lstring = 0x0, s = 0x0, 
schars = 0, bufpos = 0, from_disp_str = false, unibyte = false}, w = 
0x57baa27b9c28, paragraph_dir = L2R, separator_limit = -1, first_elt = 
false, new_paragraph = false, frame_window_p = true}, 
paragraph_embedding = NEUTRAL_DIR, min_width_property = 0x0, 
min_width_start = 0}


An updated patch resolves the issue (also attached):


diff --git a/src/xdisp.c b/src/xdisp.c
index 4e8bb7d9b97..c6b87b08ae9 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -31956,12 +31956,14 @@ produce_image_glyph (struct it *it)
      word-wrap, unless the image starts at column zero, because
      wrapping correctly needs the real pixel width of the image. */
   if ((it->line_wrap != WORD_WRAP
-       || it->hpos == 0
+       || it->hpos == (it->lnum_width ? it->lnum_width + 2 : 0)
        /* Always crop images larger than the window-width, minus 1 
space.  */
-       || it->pixel_width > it->last_visible_x - FRAME_COLUMN_WIDTH 
(it->f))
+       || it->pixel_width > it->last_visible_x - FRAME_COLUMN_WIDTH (it->f)
+                                - it->lnum_pixel_width)
       && (crop = it->pixel_width - (it->last_visible_x - it->current_x),
-         crop > 0)
-      && (it->hpos == 0 || it->pixel_width > it->last_visible_x / 4))
+          crop > 0)
+      && (it->hpos == (it->lnum_width ? it->lnum_width + 2 : 0)
+          || it->pixel_width > it->last_visible_x / 4))
     {
       it->pixel_width -= crop;
       slice.width -= crop;


However, I think that images wider than the line should always start on 
a new line, instead of being almost clipped on the current line. E.g., 
see bad_clipping.png. Do you think I should file a separate bug report 
for this?
[Message part 2 (text/html, inline)]
[stuck.png (image/png, attachment)]
[bad_clipping.png (image/png, attachment)]
[0001-correctly-handle-image-cropping-under-display-line-n.patch (text/x-patch, attachment)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#77217; Package emacs. (Wed, 26 Mar 2025 17:18:02 GMT) Full text and rfc822 format available.

Message #20 received at 77217 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Yifan Zhu <fanzhuyifan <at> gmail.com>
Cc: 77217 <at> debbugs.gnu.org
Subject: Re: bug#77217: move_it_to moves point to line after to_charpos when
 to_charpos is an image.
Date: Wed, 26 Mar 2025 19:16:53 +0200
> Date: Wed, 26 Mar 2025 09:39:18 -0700
> Cc: 77217 <at> debbugs.gnu.org
> From: Yifan Zhu <fanzhuyifan <at> gmail.com>
> 
> An updated patch resolves the issue (also attached):
> 
> diff --git a/src/xdisp.c b/src/xdisp.c
> index 4e8bb7d9b97..c6b87b08ae9 100644
> --- a/src/xdisp.c
> +++ b/src/xdisp.c
> @@ -31956,12 +31956,14 @@ produce_image_glyph (struct it *it)
>       word-wrap, unless the image starts at column zero, because
>       wrapping correctly needs the real pixel width of the image.  */
>    if ((it->line_wrap != WORD_WRAP
> -       || it->hpos == 0
> +       || it->hpos == (it->lnum_width ? it->lnum_width + 2 : 0)
>         /* Always crop images larger than the window-width, minus 1 space.  */
> -       || it->pixel_width > it->last_visible_x - FRAME_COLUMN_WIDTH (it->f))
> +       || it->pixel_width > it->last_visible_x - FRAME_COLUMN_WIDTH (it->f)
> +                                - it->lnum_pixel_width)
>        && (crop = it->pixel_width - (it->last_visible_x - it->current_x),
> -         crop > 0)
> -      && (it->hpos == 0 || it->pixel_width > it->last_visible_x / 4))
> +          crop > 0)
> +      && (it->hpos == (it->lnum_width ? it->lnum_width + 2 : 0)
> +          || it->pixel_width > it->last_visible_x / 4))
>      {
>        it->pixel_width -= crop;
>        slice.width -= crop;

Thanks, this is almost the same as my original patch, with the
addition of one more fix.  So I merged your addition with my original
changes and installed that on the master branch.

> However, I think that images wider than the line should always start on a new line, instead of being almost
> clipped on the current line. E.g., see bad_clipping.png. Do you think I should file a separate bug report for
> this?

You could file a bug, but I'm not sure this will or even should be
changed.  I certainly don't plan working on such a change.  Emacs
behaved like it does now since v21.1: wide images are cropped at the
right edge of the window.  That's because, unlike with text, there's
no way of continuing a wide image on the next screen line.  Starting
such images on the next line will not solve the problem completely,
because an image could be wider than the window to begin with; the
clear disadvantage is that you waste one more screen line.  So we use
a heuristic: if the image's width is more than 1/4th of the window, we
always crop it.  I don't think there are better alternatives.

If this is somehow problematic in the specific case where you bumped
into this, my suggestion is to break wide images into several narrower
ones, or maybe use the 'slice' feature of the Emacs display of images.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#77217; Package emacs. (Wed, 26 Mar 2025 17:58:02 GMT) Full text and rfc822 format available.

Message #23 received at 77217 <at> debbugs.gnu.org (full text, mbox):

From: Yifan Zhu <fanzhuyifan <at> gmail.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 77217 <at> debbugs.gnu.org
Subject: Re: bug#77217: move_it_to moves point to line after to_charpos when
 to_charpos is an image.
Date: Wed, 26 Mar 2025 10:57:24 -0700
On 3/26/25 10:16 AM, Eli Zaretskii wrote:
>> Date: Wed, 26 Mar 2025 09:39:18 -0700
>> Cc: 77217 <at> debbugs.gnu.org
>> From: Yifan Zhu <fanzhuyifan <at> gmail.com>
>>
>> An updated patch resolves the issue (also attached):
>>
>> diff --git a/src/xdisp.c b/src/xdisp.c
>> index 4e8bb7d9b97..c6b87b08ae9 100644
>> --- a/src/xdisp.c
>> +++ b/src/xdisp.c
>> @@ -31956,12 +31956,14 @@ produce_image_glyph (struct it *it)
>>        word-wrap, unless the image starts at column zero, because
>>        wrapping correctly needs the real pixel width of the image.  */
>>     if ((it->line_wrap != WORD_WRAP
>> -       || it->hpos == 0
>> +       || it->hpos == (it->lnum_width ? it->lnum_width + 2 : 0)
>>          /* Always crop images larger than the window-width, minus 1 space.  */
>> -       || it->pixel_width > it->last_visible_x - FRAME_COLUMN_WIDTH (it->f))
>> +       || it->pixel_width > it->last_visible_x - FRAME_COLUMN_WIDTH (it->f)
>> +                                - it->lnum_pixel_width)
>>         && (crop = it->pixel_width - (it->last_visible_x - it->current_x),
>> -         crop > 0)
>> -      && (it->hpos == 0 || it->pixel_width > it->last_visible_x / 4))
>> +          crop > 0)
>> +      && (it->hpos == (it->lnum_width ? it->lnum_width + 2 : 0)
>> +          || it->pixel_width > it->last_visible_x / 4))
>>       {
>>         it->pixel_width -= crop;
>>         slice.width -= crop;
> Thanks, this is almost the same as my original patch, with the
> addition of one more fix.  So I merged your addition with my original
> changes and installed that on the master branch.
Thanks! I had a small nit about the redundant "0 +"s in your original 
patch. But it doesn't really matter.
>
>> However, I think that images wider than the line should always start on a new line, instead of being almost
>> clipped on the current line. E.g., see bad_clipping.png. Do you think I should file a separate bug report for
>> this?
> You could file a bug, but I'm not sure this will or even should be
> changed.  I certainly don't plan working on such a change.  Emacs
> behaved like it does now since v21.1: wide images are cropped at the
> right edge of the window.  That's because, unlike with text, there's
> no way of continuing a wide image on the next screen line.  Starting
> such images on the next line will not solve the problem completely,
> because an image could be wider than the window to begin with; the
> clear disadvantage is that you waste one more screen line.  So we use
> a heuristic: if the image's width is more than 1/4th of the window, we
> always crop it.  I don't think there are better alternatives.
>
> If this is somehow problematic in the specific case where you bumped
> into this, my suggestion is to break wide images into several narrower
> ones, or maybe use the 'slice' feature of the Emacs display of images.

Makes sense -- it was just a small issue I found when testing out the 
change, and not really something that annoys me in my workflow.


Thanks again!





Reply sent to Eli Zaretskii <eliz <at> gnu.org>:
You have taken responsibility. (Thu, 03 Apr 2025 07:25:02 GMT) Full text and rfc822 format available.

Notification sent to Yifan Zhu <fanzhuyifan <at> gmail.com>:
bug acknowledged by developer. (Thu, 03 Apr 2025 07:25:02 GMT) Full text and rfc822 format available.

Message #28 received at 77217-done <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Yifan Zhu <fanzhuyifan <at> gmail.com>
Cc: 77217-done <at> debbugs.gnu.org
Subject: Re: bug#77217: move_it_to moves point to line after to_charpos when
 to_charpos is an image.
Date: Thu, 03 Apr 2025 10:23:51 +0300
> Date: Wed, 26 Mar 2025 10:57:24 -0700
> Cc: 77217 <at> debbugs.gnu.org
> From: Yifan Zhu <fanzhuyifan <at> gmail.com>
> 
> On 3/26/25 10:16 AM, Eli Zaretskii wrote:
> >> Date: Wed, 26 Mar 2025 09:39:18 -0700
> >> Cc: 77217 <at> debbugs.gnu.org
> >> From: Yifan Zhu <fanzhuyifan <at> gmail.com>
> >>
> >> An updated patch resolves the issue (also attached):
> >>
> >> diff --git a/src/xdisp.c b/src/xdisp.c
> >> index 4e8bb7d9b97..c6b87b08ae9 100644
> >> --- a/src/xdisp.c
> >> +++ b/src/xdisp.c
> >> @@ -31956,12 +31956,14 @@ produce_image_glyph (struct it *it)
> >>        word-wrap, unless the image starts at column zero, because
> >>        wrapping correctly needs the real pixel width of the image.  */
> >>     if ((it->line_wrap != WORD_WRAP
> >> -       || it->hpos == 0
> >> +       || it->hpos == (it->lnum_width ? it->lnum_width + 2 : 0)
> >>          /* Always crop images larger than the window-width, minus 1 space.  */
> >> -       || it->pixel_width > it->last_visible_x - FRAME_COLUMN_WIDTH (it->f))
> >> +       || it->pixel_width > it->last_visible_x - FRAME_COLUMN_WIDTH (it->f)
> >> +                                - it->lnum_pixel_width)
> >>         && (crop = it->pixel_width - (it->last_visible_x - it->current_x),
> >> -         crop > 0)
> >> -      && (it->hpos == 0 || it->pixel_width > it->last_visible_x / 4))
> >> +          crop > 0)
> >> +      && (it->hpos == (it->lnum_width ? it->lnum_width + 2 : 0)
> >> +          || it->pixel_width > it->last_visible_x / 4))
> >>       {
> >>         it->pixel_width -= crop;
> >>         slice.width -= crop;
> > Thanks, this is almost the same as my original patch, with the
> > addition of one more fix.  So I merged your addition with my original
> > changes and installed that on the master branch.
> Thanks! I had a small nit about the redundant "0 +"s in your original 
> patch. But it doesn't really matter.
> >
> >> However, I think that images wider than the line should always start on a new line, instead of being almost
> >> clipped on the current line. E.g., see bad_clipping.png. Do you think I should file a separate bug report for
> >> this?
> > You could file a bug, but I'm not sure this will or even should be
> > changed.  I certainly don't plan working on such a change.  Emacs
> > behaved like it does now since v21.1: wide images are cropped at the
> > right edge of the window.  That's because, unlike with text, there's
> > no way of continuing a wide image on the next screen line.  Starting
> > such images on the next line will not solve the problem completely,
> > because an image could be wider than the window to begin with; the
> > clear disadvantage is that you waste one more screen line.  So we use
> > a heuristic: if the image's width is more than 1/4th of the window, we
> > always crop it.  I don't think there are better alternatives.
> >
> > If this is somehow problematic in the specific case where you bumped
> > into this, my suggestion is to break wide images into several narrower
> > ones, or maybe use the 'slice' feature of the Emacs display of images.
> 
> Makes sense -- it was just a small issue I found when testing out the 
> change, and not really something that annoys me in my workflow.

No further comments, so I'm now closing this bug.




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Thu, 01 May 2025 11:24:06 GMT) Full text and rfc822 format available.

This bug report was last modified 52 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.