Function: move-point-visually

move-point-visually is a function defined in xdisp.c.

Signature

(move-point-visually DIRECTION)

Documentation

Move point in the visual order in the specified DIRECTION.

DIRECTION can be 1, meaning move to the right, or -1, which moves to the left.

Value is the new character position of point.

Source Code

// Defined in /usr/src/emacs/src/xdisp.c
// Skipping highlighting due to helpful-max-highlight.
{
  struct window *w = XWINDOW (selected_window);
  struct buffer *b = XBUFFER (w->contents);
  struct glyph_row *row;
  int dir;
  Lisp_Object paragraph_dir;

#define ROW_GLYPH_NEWLINE_P(ROW,GLYPH)		\
  (!(ROW)->continued_p				\
   && NILP ((GLYPH)->object)			\
   && (GLYPH)->type == CHAR_GLYPH		\
   && (GLYPH)->u.ch == ' '			\
   && (GLYPH)->charpos >= 0			\
   && !(GLYPH)->avoid_cursor_p)

  CHECK_FIXNUM (direction);
  dir = XFIXNUM (direction);
  if (dir > 0)
    dir = 1;
  else
    dir = -1;

  /* If current matrix is up-to-date, we can use the information
     recorded in the glyphs, at least as long as the goal is on the
     screen.  */
  if (w->window_end_valid
      && !windows_or_buffers_changed
      && b
      && !b->clip_changed
      && !b->prevent_redisplay_optimizations_p
      && !window_outdated (w)
      /* We rely below on the cursor coordinates to be up to date, but
	 we cannot trust them if some command moved point since the
	 last complete redisplay.  */
      && w->last_point == BUF_PT (b)
      && w->cursor.vpos >= 0
      && w->cursor.vpos < w->current_matrix->nrows
      && (row = MATRIX_ROW (w->current_matrix, w->cursor.vpos))->enabled_p)
    {
      struct glyph *g = row->glyphs[TEXT_AREA];
      struct glyph *e = dir > 0 ? g + row->used[TEXT_AREA] : g - 1;
      struct glyph *gpt = g + w->cursor.hpos;

      for (g = gpt + dir; (dir > 0 ? g < e : g > e); g += dir)
	{
	  if (BUFFERP (g->object) && g->charpos != PT)
	    {
	      SET_PT (g->charpos);
	      w->cursor.vpos = -1;
	      return make_fixnum (PT);
	    }
	  else if (!NILP (g->object) && !EQ (g->object, gpt->object))
	    {
	      ptrdiff_t new_pos;

	      if (BUFFERP (gpt->object))
		{
		  new_pos = PT;
		  if ((gpt->resolved_level - row->reversed_p) % 2 == 0)
		    new_pos += (row->reversed_p ? -dir : dir);
		  else
		    new_pos -= (row->reversed_p ? -dir : dir);
		  new_pos = clip_to_bounds (BEGV, new_pos, ZV);
		  /* If we didn't move, we've hit BEGV or ZV, so we
		     need to signal a suitable error.  */
		  if (new_pos == PT)
		    break;
		}
	      else if (BUFFERP (g->object))
		new_pos = g->charpos;
	      else
		break;
	      SET_PT (new_pos);
	      w->cursor.vpos = -1;
	      return make_fixnum (PT);
	    }
	  else if (ROW_GLYPH_NEWLINE_P (row, g))
	    {
	      /* Glyphs inserted at the end of a non-empty line for
		 positioning the cursor have zero charpos, so we must
		 deduce the value of point by other means.  */
	      if (g->charpos > 0)
		SET_PT (g->charpos);
	      else if (row->ends_at_zv_p && PT != ZV)
		SET_PT (ZV);
	      else if (PT != MATRIX_ROW_END_CHARPOS (row) - 1)
		SET_PT (MATRIX_ROW_END_CHARPOS (row) - 1);
	      else
		break;
	      w->cursor.vpos = -1;
	      return make_fixnum (PT);
	    }
	}
      if (g == e || NILP (g->object))
	{
	  if (row->truncated_on_left_p || row->truncated_on_right_p)
	    goto simulate_display;
	  if (!row->reversed_p)
	    row += dir;
	  else
	    row -= dir;
	  if (!(MATRIX_FIRST_TEXT_ROW (w->current_matrix) <= row
		&& row < MATRIX_BOTTOM_TEXT_ROW (w->current_matrix, w)))
	    goto simulate_display;

	  if (dir > 0)
	    {
	      if (row->reversed_p && !row->continued_p)
		{
		  SET_PT (MATRIX_ROW_END_CHARPOS (row) - 1);
		  w->cursor.vpos = -1;
		  return make_fixnum (PT);
		}
	      g = row->glyphs[TEXT_AREA];
	      e = g + row->used[TEXT_AREA];
	      for ( ; g < e; g++)
		{
		  if (BUFFERP (g->object)
		      /* Empty lines have only one glyph, which stands
			 for the newline, and whose charpos is the
			 buffer position of the newline.  */
		      || ROW_GLYPH_NEWLINE_P (row, g)
		      /* When the buffer ends in a newline, the line at
			 EOB also has one glyph, but its charpos is -1.  */
		      || (row->ends_at_zv_p
			  && !row->reversed_p
			  && NILP (g->object)
			  && g->type == CHAR_GLYPH
			  && g->u.ch == ' '))
		    {
		      if (g->charpos > 0)
			SET_PT (g->charpos);
		      else if (!row->reversed_p
			       && row->ends_at_zv_p
			       && PT != ZV)
			SET_PT (ZV);
		      else
			continue;
		      w->cursor.vpos = -1;
		      return make_fixnum (PT);
		    }
		}
	    }
	  else
	    {
	      if (!row->reversed_p && !row->continued_p)
		{
		  SET_PT (MATRIX_ROW_END_CHARPOS (row) - 1);
		  w->cursor.vpos = -1;
		  return make_fixnum (PT);
		}
	      e = row->glyphs[TEXT_AREA];
	      g = e + row->used[TEXT_AREA] - 1;
	      for ( ; g >= e; g--)
		{
		  if (BUFFERP (g->object)
		      || (ROW_GLYPH_NEWLINE_P (row, g)
			  && g->charpos > 0)
		      /* Empty R2L lines on GUI frames have the buffer
			 position of the newline stored in the stretch
			 glyph.  */
		      || g->type == STRETCH_GLYPH
		      || (row->ends_at_zv_p
			  && row->reversed_p
			  && NILP (g->object)
			  && g->type == CHAR_GLYPH
			  && g->u.ch == ' '))
		    {
		      if (g->charpos > 0)
			SET_PT (g->charpos);
		      else if (row->reversed_p
			       && row->ends_at_zv_p
			       && PT != ZV)
			SET_PT (ZV);
		      else
			continue;
		      w->cursor.vpos = -1;
		      return make_fixnum (PT);
		    }
		}
	    }
	}
    }

 simulate_display:

  /* If we wind up here, we failed to move by using the glyphs, so we
     need to simulate display instead.  */

  if (b)
    paragraph_dir = Fcurrent_bidi_paragraph_direction (w->contents);
  else
    paragraph_dir = Qleft_to_right;
  if (EQ (paragraph_dir, Qright_to_left))
    dir = -dir;
  if (PT <= BEGV && dir < 0)
    xsignal0 (Qbeginning_of_buffer);
  else if (PT >= ZV && dir > 0)
    xsignal0 (Qend_of_buffer);
  else
    {
      struct text_pos pt;
      struct it it;
      int pt_x, target_x, pixel_width, pt_vpos;
      bool at_eol_p;
      bool overshoot_expected = false;
      bool target_is_eol_p = false;
      void *itdata = bidi_shelve_cache ();

      /* Setup the arena.  */
      SET_TEXT_POS (pt, PT, PT_BYTE);
      start_display (&it, w, pt);
      /* When lines are truncated, we could be called with point
	 outside of the windows edges, in which case move_it_*
	 functions either prematurely stop at window's edge or jump to
	 the next screen line, whereas we rely below on our ability to
	 reach point, in order to start from its X coordinate.  So we
	 need to disregard the window's horizontal extent in that case.  */
      if (it.line_wrap == TRUNCATE)
	it.last_visible_x = DISP_INFINITY;

      if (it.cmp_it.id < 0
	  && it.method == GET_FROM_STRING
	  && it.area == TEXT_AREA
	  && it.string_from_display_prop_p
	  && (it.sp > 0 && it.stack[it.sp - 1].method == GET_FROM_BUFFER))
	overshoot_expected = true;

      /* Find the X coordinate of point.  We start from the beginning
	 of this or previous line to make sure we are before point in
	 the logical order (since the move_it_* functions can only
	 move forward).  */
    reseat:
      reseat_at_previous_visible_line_start (&it);
      it.current_x = it.hpos = it.current_y = it.vpos = 0;
      if (IT_CHARPOS (it) != PT)
	{
	  move_it_to (&it, overshoot_expected ? PT - 1 : PT,
		      -1, -1, -1, MOVE_TO_POS);
	  /* If we missed point because the character there is
	     displayed out of a display vector that has more than one
	     glyph, retry expecting overshoot.  */
	  if (it.method == GET_FROM_DISPLAY_VECTOR
	      && it.current.dpvec_index > 0
	      && !overshoot_expected)
	    {
	      overshoot_expected = true;
	      goto reseat;
	    }
	  else if (IT_CHARPOS (it) != PT && !overshoot_expected)
	    move_it_in_display_line (&it, PT, -1, MOVE_TO_POS);
	}
      pt_x = it.current_x;
      pt_vpos = it.vpos;
      if (dir > 0 || overshoot_expected)
	{
	  struct glyph_row *row = it.glyph_row;

	  /* When point is at beginning of line, we don't have
	     information about the glyph there loaded into struct
	     it.  Calling get_next_display_element fixes that.  */
	  if (pt_x == 0)
	    get_next_display_element (&it);
	  at_eol_p = ITERATOR_AT_END_OF_LINE_P (&it);
	  it.glyph_row = NULL;
	  PRODUCE_GLYPHS (&it);	/* compute it.pixel_width */
	  it.glyph_row = row;
	  /* PRODUCE_GLYPHS advances it.current_x, so we must restore
	     it, lest it will become out of sync with it's buffer
	     position.  */
	  it.current_x = pt_x;
	}
      else
	at_eol_p = ITERATOR_AT_END_OF_LINE_P (&it);
      pixel_width = it.pixel_width;
      if (overshoot_expected && at_eol_p)
	pixel_width = 0;
      else if (pixel_width <= 0)
	pixel_width = 1;

      /* If there's a display string (or something similar) at point,
	 we are actually at the glyph to the left of point, so we need
	 to correct the X coordinate.  */
      if (overshoot_expected)
	{
	  if (it.bidi_p)
	    pt_x += pixel_width * it.bidi_it.scan_dir;
	  else
	    pt_x += pixel_width;
	}

      /* Compute target X coordinate, either to the left or to the
	 right of point.  On TTY frames, all characters have the same
	 pixel width of 1, so we can use that.  On GUI frames we don't
	 have an easy way of getting at the pixel width of the
	 character to the left of point, so we use a different method
	 of getting to that place.  */
      if (dir > 0)
	target_x = pt_x + pixel_width;
      else
	target_x = pt_x - (!FRAME_WINDOW_P (it.f)) * pixel_width;

      /* Target X coordinate could be one line above or below the line
	 of point, in which case we need to adjust the target X
	 coordinate.  Also, if moving to the left, we need to begin at
	 the left edge of the point's screen line.  */
      if (dir < 0)
	{
	  if (pt_x > 0)
	    {
	      start_display (&it, w, pt);
	      if (it.line_wrap == TRUNCATE)
		it.last_visible_x = DISP_INFINITY;
	      reseat_at_previous_visible_line_start (&it);
	      it.current_x = it.current_y = it.hpos = 0;
	      if (pt_vpos != 0)
		move_it_by_lines (&it, pt_vpos);
	    }
	  else
	    {
	      move_it_by_lines (&it, -1);
	      target_x = it.last_visible_x - !FRAME_WINDOW_P (it.f);
	      target_is_eol_p = true;
	      /* Under word-wrap, we don't know the x coordinate of
		 the last character displayed on the previous line,
		 which immediately precedes the wrap point.  To find
		 out its x coordinate, we try moving to the right
		 margin of the window, which will stop at the wrap
		 point, and then reset target_x to point at the
		 character that precedes the wrap point.  This is not
		 needed on GUI frames, because (see below) there we
		 move from the left margin one grapheme cluster at a
		 time, and stop when we hit the wrap point.  */
	      if (!FRAME_WINDOW_P (it.f) && it.line_wrap == WORD_WRAP)
		{
		  void *it_data = NULL;
		  struct it it2;

		  SAVE_IT (it2, it, it_data);
		  move_it_in_display_line_to (&it, ZV, target_x,
					      MOVE_TO_POS | MOVE_TO_X);
		  /* If we arrived at target_x, that _is_ the last
		     character on the previous line.  */
		  if (it.current_x != target_x)
		    target_x = it.current_x - 1;
		  RESTORE_IT (&it, &it2, it_data);
		}
	    }
	}
      else
	{
	  if (at_eol_p
	      || (target_x >= it.last_visible_x
		  && it.line_wrap != TRUNCATE))
	    {
	      if (pt_x > 0)
		move_it_by_lines (&it, 0);
	      move_it_by_lines (&it, 1);
	      target_x = 0;
	    }
	}

      /* Move to the target X coordinate.  */
      /* On GUI frames, as we don't know the X coordinate of the
	 character to the left of point, moving point to the left
	 requires walking, one grapheme cluster at a time, until we
	 find ourself at a place immediately to the left of the
	 character at point.  */
      if (FRAME_WINDOW_P (it.f) && dir < 0)
	{
	  struct text_pos new_pos;
	  enum move_it_result rc = MOVE_X_REACHED;

	  if (it.current_x == 0)
	    get_next_display_element (&it);
	  if (it.what == IT_COMPOSITION)
	    {
	      new_pos.charpos = it.cmp_it.charpos;
	      new_pos.bytepos = -1;
	    }
	  else
	    new_pos = it.current.pos;

	  while (it.current_x + it.pixel_width <= target_x
		 && (rc == MOVE_X_REACHED
		     /* Under word-wrap, move_it_in_display_line_to
			stops at correct coordinates, but sometimes
			returns MOVE_POS_MATCH_OR_ZV.  */
		     || (it.line_wrap == WORD_WRAP
			 && rc == MOVE_POS_MATCH_OR_ZV)))
	    {
	      int new_x = it.current_x + it.pixel_width;

	      /* For composed characters, we want the position of the
		 first character in the grapheme cluster (usually, the
		 composition's base character), whereas it.current
		 might give us the position of the _last_ one, e.g. if
		 the composition is rendered in reverse due to bidi
		 reordering.  */
	      if (it.what == IT_COMPOSITION)
		{
		  new_pos.charpos = it.cmp_it.charpos;
		  new_pos.bytepos = -1;
		}
	      else
		new_pos = it.current.pos;
	      if (new_x == it.current_x)
		new_x++;
	      rc = move_it_in_display_line_to (&it, ZV, new_x,
					       MOVE_TO_POS | MOVE_TO_X);
	      if (ITERATOR_AT_END_OF_LINE_P (&it) && !target_is_eol_p)
		break;
	    }
	  /* The previous position we saw in the loop is the one we
	     want.  */
	  if (new_pos.bytepos == -1)
	    new_pos.bytepos = CHAR_TO_BYTE (new_pos.charpos);
	  it.current.pos = new_pos;
	}
      else if (it.current_x != target_x)
	move_it_in_display_line_to (&it, ZV, target_x, MOVE_TO_POS | MOVE_TO_X);

      /* If we ended up in a display string that covers point, move to
	 buffer position to the right in the visual order.  */
      if (dir > 0)
	{
	  while (IT_CHARPOS (it) == PT)
	    {
	      set_iterator_to_next (&it, false);
	      if (!get_next_display_element (&it))
		break;
	    }
	}

      /* Move point to that position.  */
      SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
      bidi_unshelve_cache (itdata, false);
    }

  return make_fixnum (PT);

#undef ROW_GLYPH_NEWLINE_P
}