Subject: bug#77217: move_it_to moves point to line after to_charpos when to_charpos is an image.

From: Yifan Zhu <[email-masked]>
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.