]> git.eshelyaron.com Git - emacs.git/commitdiff
Implement visual-order cursor motion.
authorEli Zaretskii <eliz@gnu.org>
Sat, 29 Jun 2013 13:36:19 +0000 (16:36 +0300)
committerEli Zaretskii <eliz@gnu.org>
Sat, 29 Jun 2013 13:36:19 +0000 (16:36 +0300)
 src/xdisp.c (Fmove_point_visually): New function.

 lisp/bindings.el (visual-order-cursor-movement): New defcustom.
 (right-char, left-char): Provide visual-order cursor motion by
 calling move-point-visually.  Update the doc strings.

 doc/emacs/basic.texi (Moving Point): Document visual-order-cursor-movement
 and its effect on right-char and left-char.

 doc/lispref/display.texi (Bidirectional Display): Document move-point-visually.

 etc/NEWS: Document the new feature.

doc/emacs/ChangeLog
doc/emacs/basic.texi
doc/emacs/mule.texi
doc/lispref/ChangeLog
doc/lispref/display.texi
etc/NEWS
lisp/ChangeLog
lisp/bindings.el
src/ChangeLog
src/xdisp.c

index 45f08499215fffb78c0b9eb5cbf2bcb6267c76f0..d5f3095908bf65db12e25b4e5f9cbc627fc448ef 100644 (file)
@@ -1,3 +1,8 @@
+2013-06-29  Eli Zaretskii  <eliz@gnu.org>
+
+       * basic.texi (Moving Point): Document visual-order-cursor-movement
+       and its effect on right-char and left-char.
+
 2013-06-28  Glenn Morris  <rgm@gnu.org>
 
        * ack.texi (Acknowledgments): Small update.
index b9bc391d1cfc0b26e8f23d3d415cf4c0e235fd30..a840f912656b953984ca5b2d596b705b2ee09d3b 100644 (file)
@@ -153,10 +153,17 @@ Move forward one character (@code{forward-char}).
 @item @key{right}
 @kindex RIGHT
 @findex right-char
+@vindex visual-order-cursor-movement
+@cindex cursor, visual-order motion
 This command (@code{right-char}) behaves like @kbd{C-f}, with one
 exception: when editing right-to-left scripts such as Arabic, it
 instead moves @emph{backward} if the current paragraph is a
-right-to-left paragraph.  @xref{Bidirectional Editing}.
+right-to-left paragraph.  @xref{Bidirectional Editing}.  If
+@code{visual-order-cursor-movement} is non-@code{nil}, this command
+moves to the character that is to the right of the current screen
+position, moving to the next or previous screen line as appropriate.
+Note that this might potentially move point many buffer positions
+away, depending on the surrounding bidirectional context.
 
 @item C-b
 @kindex C-b
@@ -168,7 +175,10 @@ Move backward one character (@code{backward-char}).
 @findex left-char
 This command (@code{left-char}) behaves like @kbd{C-b}, except it
 moves @emph{forward} if the current paragraph is right-to-left.
-@xref{Bidirectional Editing}.
+@xref{Bidirectional Editing}.  If @code{visual-order-cursor-movement}
+is non-@code{nil}, this command moves to the character that is to the
+left of the current screen position, moving to the previous or next
+screen line as appropriate.
 
 @item C-n
 @itemx @key{down}
index de3e05777cd14a9ed71972302681b56e6af6a29a..c8bd5027fa088a65fa5b041797029b31fc2d01e7 100644 (file)
@@ -1804,4 +1804,6 @@ jump when point traverses reordered bidirectional text.  Similarly, a
 highlighted region covering a contiguous range of character positions
 may look discontinuous if the region spans reordered text.  This is
 normal and similar to the behavior of other programs that support
-bidirectional text.
+bidirectional text.  If you set @code{visual-order-cursor-movement} to
+a non-@code{nil} value, cursor motion by the arrow keys follows the
+visual order on screen (@pxref{Moving Point, visual-order movement}).
index 199d94585e3bdf821861945679c2dd327a052452..dc0c15647350ea5691a8ba65570ac58d2ef2121e 100644 (file)
@@ -1,3 +1,7 @@
+2013-06-29  Eli Zaretskii  <eliz@gnu.org>
+
+       * display.texi (Bidirectional Display): Document move-point-visually.
+
 2013-06-29  Xue Fuqiao  <xfq.free@gmail.com>
 
        * buffers.texi (Buffer File Name): Fix typo.
index d82b9a4c5a2e56a8d837ff8b1646d4f529064ece..ecefb684ee6f90c319efab6d003ec1b606903525 100644 (file)
@@ -6431,6 +6431,26 @@ determined dynamically by Emacs.  For buffers whose value of
 buffers, this function always returns @code{left-to-right}.
 @end defun
 
+@cindex visual-order cursor motion
+  Sometimes there's a need to move point in strict visual order,
+either to the left or to the right of its current screen position.
+Emacs provides a primitive to do that.
+
+@defun move-point-visually direction
+This function moves point of the currently selected window to the
+buffer position that appears immediately to the right or to the left
+of point on the screen.  If @var{direction} is positive, point will
+move one screen position to the right, otherwise it will move one
+screen position to the left.  Note that, depending on the surrounding
+bidirectional context, this could potentially move point many buffer
+positions away.  If invoked at the end of a screen line, the function
+moves point to the rightmost or leftmost screen position of the next
+or previous screen line, as appropriate for the value of
+@var{direction}.
+
+The function returns the new buffer position as its value.
+@end defun
+
 @cindex layout on display, and bidirectional text
 @cindex jumbled display of bidirectional text
 @cindex concatenating bidirectional strings
index f5ab7c60ce59d72102f7ecf6afd2b4850fc3e615..a6c93e7cf677273975c8cf3b04d78f38a23d3e14 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -131,6 +131,13 @@ bound to <f11> and M-<f10>, respectively.
 ** In keymaps where SPC scrolls, S-SPC now scrolls in the reverse direction.
 Eg View mode, etc.
 
++++
+** New option `visual-order-cursor-movement'.
+If this is non-nil, cursor motion with arrow keys will follow the
+visual order of characters on the screen: <left> always moves to the
+left, <right> always moves to the right, disregarding the surrounding
+bidirectional context.
+
 ** New command `kmacro-to-register' to store keyboard macros in registers.
 
 ** Shell Script mode
index 826f270d8f89ed82afabc3c2df5855272468fcdf..1e1fff6fc251ff6cbddbca592afc70f4d830ddb7 100644 (file)
@@ -1,3 +1,9 @@
+2013-06-29  Eli Zaretskii  <eliz@gnu.org>
+
+       * bindings.el (visual-order-cursor-movement): New defcustom.
+       (right-char, left-char): Provide visual-order cursor motion by
+       calling move-point-visually.  Update the doc strings.
+
 2013-06-28  Kenichi Handa  <handa@gnu.org>
 
        * international/mule.el (define-coding-system): New coding system
index 2013c0798203182ad35f33e59c70bf3085eddc31..7c42cc6c0a82fce6afaa2899e9b3dc09fcff374b 100644 (file)
@@ -696,29 +696,63 @@ language you are using."
 (put 'narrow-to-region 'disabled t)
 
 ;; Moving with arrows in bidi-sensitive direction.
+(defcustom visual-order-cursor-movement nil
+  "If non-nil, moving cursor with arrow keys follows the visual order.
+
+When this is non-nil, \\[right-char] will move to the character that is
+to the right of point on display, and \\[left-char] will move to the left,
+disregarding the surrounding bidirectional context.  Depending on the
+bidirectional context of the surrounding characters, this can move point
+many buffer positions away.
+
+When the text is entirely left-to-right, logical-order and visual-order
+cursor movements produce identical results."
+  :type '(choice (const :tag "Logical-order cursor movement" nil)
+                (const :tag "Visual-order cursor movement" t))
+  :group 'display
+  :version "24.5")
+
 (defun right-char (&optional n)
   "Move point N characters to the right (to the left if N is negative).
 On reaching beginning or end of buffer, stop and signal error.
 
-Depending on the bidirectional context, this may move either forward
-or backward in the buffer.  This is in contrast with \\[forward-char]
-and \\[backward-char], which see."
+If `visual-order-cursor-movement' is non-nil, this always moves
+to the right on display, wherever that is in the buffer.
+Otherwise, depending on the bidirectional context, this may move
+one position either forward or backward in the buffer.  This is
+in contrast with \\[forward-char] and \\[backward-char], which
+see."
   (interactive "^p")
-  (if (eq (current-bidi-paragraph-direction) 'left-to-right)
-      (forward-char n)
-    (backward-char n)))
+  (if visual-order-cursor-movement
+      (dotimes (i (if (numberp n) (abs n) 1))
+       (if (< n 0)
+           (move-point-visually -1)
+         (move-point-visually 1))
+       (sit-for 0))
+    (if (eq (current-bidi-paragraph-direction) 'left-to-right)
+       (forward-char n)
+      (backward-char n))))
 
 (defun left-char ( &optional n)
   "Move point N characters to the left (to the right if N is negative).
 On reaching beginning or end of buffer, stop and signal error.
 
-Depending on the bidirectional context, this may move either backward
-or forward in the buffer.  This is in contrast with \\[backward-char]
-and \\[forward-char], which see."
+If `visual-order-cursor-movement' is non-nil, this always moves
+to the left on display, wherever that is in the buffer.
+Otherwise, depending on the bidirectional context, this may move
+one position either backward or forward in the buffer.  This is
+in contrast with \\[forward-char] and \\[backward-char], which
+see."
   (interactive "^p")
-  (if (eq (current-bidi-paragraph-direction) 'left-to-right)
-      (backward-char n)
-    (forward-char n)))
+  (if visual-order-cursor-movement
+      (dotimes (i (if (numberp n) (abs n) 1))
+       (if (< n 0)
+           (move-point-visually 1)
+         (move-point-visually -1))
+       (sit-for 0))
+    (if (eq (current-bidi-paragraph-direction) 'left-to-right)
+       (backward-char n)
+      (forward-char n))))
 
 (defun right-word (&optional n)
   "Move point N words to the right (to the left if N is negative).
index b279f42e6b63a85ef34ff92e47c9059ca1243ca1..1e1b54a72d02bf154f3a2e3660cb8484c9c44adc 100644 (file)
@@ -1,3 +1,7 @@
+2013-06-29  Eli Zaretskii  <eliz@gnu.org>
+
+       * xdisp.c (Fmove_point_visually): New function.
+
 2013-06-28  Kenichi Handa  <handa@gnu.org>
 
        * coding.h (define_coding_undecided_arg_index): New enum.
index 54ea325f642e3e2181fec0acd165ab3c10d531d4..420ff0c918b6c509c21846ca53f74f99c8daffd8 100644 (file)
@@ -20051,6 +20051,398 @@ See also `bidi-paragraph-direction'.  */)
     }
 }
 
+DEFUN ("move-point-visually", Fmove_point_visually,
+       Smove_point_visually, 1, 1, 0,
+       doc: /* 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.  */)
+  (Lisp_Object direction)
+{
+  struct window *w = XWINDOW (selected_window);
+  struct buffer *b = NULL;
+  struct glyph_row *row;
+  int dir;
+  Lisp_Object paragraph_dir;
+
+#define ROW_GLYPH_NEWLINE_P(ROW,GLYPH)         \
+  (!(ROW)->continued_p                         \
+   && INTEGERP ((GLYPH)->object)               \
+   && (GLYPH)->type == CHAR_GLYPH              \
+   && (GLYPH)->u.ch == ' '                     \
+   && (GLYPH)->charpos >= 0                    \
+   && !(GLYPH)->avoid_cursor_p)
+
+  CHECK_NUMBER (direction);
+  dir = XINT (direction);
+  if (dir > 0)
+    dir = 1;
+  else
+    dir = -1;
+
+  if (BUFFERP (w->contents))
+    b = XBUFFER (w->contents);
+
+  /* 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
+      && w->last_modified >= BUF_MODIFF (b)
+      && w->last_overlay_modified >= BUF_OVERLAY_MODIFF (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);
+             return make_number (PT);
+           }
+         else if (!INTEGERP (g->object) && 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);;
+               }
+             else if (BUFFERP (g->object))
+               new_pos = g->charpos;
+             else
+               break;
+             SET_PT (new_pos);
+             return make_number (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;
+             return make_number (PT);
+           }
+       }
+      if (g == e || INTEGERP (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 (row < MATRIX_FIRST_TEXT_ROW (w->current_matrix)
+             || 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);
+                 return make_number (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
+                         && INTEGERP (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;
+                     return make_number (PT);
+                   }
+               }
+           }
+         else
+           {
+             if (!row->reversed_p && !row->continued_p)
+               {
+                 SET_PT (MATRIX_ROW_END_CHARPOS (row) - 1);
+                 return make_number (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
+                         && INTEGERP (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;
+                     return make_number (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 disp_string_at_start_p = 0;
+      bool overshoot_expected = false;
+      bool target_is_eol_p = false;
+
+      /* Setup the arena.  */
+      SET_TEXT_POS (pt, PT, PT_BYTE);
+      start_display (&it, w, pt);
+
+      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_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);
+      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 at point, we are actually at the
+        glyph to the left of point, so we need to correct the X
+        coordinate.  */
+      if (overshoot_expected)
+       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);
+             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;
+           }
+       }
+      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.  */
+#ifdef HAVE_WINDOW_SYSTEM
+      /* 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 = it.current.pos;
+         enum move_it_result rc = MOVE_X_REACHED;
+
+         while (it.current_x + it.pixel_width <= target_x
+                && rc == MOVE_X_REACHED)
+           {
+             int new_x = it.current_x + it.pixel_width;
+
+             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;
+           }
+         /* If we ended up on a composed character inside
+            bidi-reordered text (e.g., Hebrew text with diacriticals),
+            the iterator gives us the buffer position of the last (in
+            logical order) character of the composed grapheme cluster,
+            which is not what we want.  So we cheat: we compute the
+            character position of the character that follows (in the
+            logical order) the one where the above loop stopped.  That
+            character will appear on display to the left of point.  */
+         if (it.bidi_p
+             && it.bidi_it.scan_dir == -1
+             && new_pos.charpos - IT_CHARPOS (it) > 1)
+           {
+             new_pos.charpos = IT_CHARPOS (it) + 1;
+             new_pos.bytepos = CHAR_TO_BYTE (new_pos.charpos);
+           }
+         it.current.pos = new_pos;
+       }
+      else
+#endif
+      if (it.current_x != target_x)
+       move_it_in_display_line_to (&it, ZV, target_x, MOVE_TO_POS | MOVE_TO_X);
+
+      /* When lines are truncated, the above loop will stop at the
+        window edge.  But we want to get to the end of line, even if
+        it is beyond the window edge; automatic hscroll will then
+        scroll the window to show point as appropriate.  */
+      if (target_is_eol_p && it.line_wrap == TRUNCATE
+         && get_next_display_element (&it))
+       {
+         struct text_pos new_pos = it.current.pos;
+
+         while (!ITERATOR_AT_END_OF_LINE_P (&it))
+           {
+             set_iterator_to_next (&it, 0);
+             if (it.method == GET_FROM_BUFFER)
+               new_pos = it.current.pos;
+             if (!get_next_display_element (&it))
+               break;
+           }
+
+         it.current.pos = new_pos;
+       }
+
+      /* 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, 0);
+             if (!get_next_display_element (&it))
+               break;
+           }
+       }
+
+      /* Move point to that position.  */
+      SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
+    }
+
+  return make_number (PT);
+
+#undef ROW_GLYPH_NEWLINE_P
+}
 
 \f
 /***********************************************************************
@@ -28713,6 +29105,7 @@ syms_of_xdisp (void)
   defsubr (&Sformat_mode_line);
   defsubr (&Sinvisible_p);
   defsubr (&Scurrent_bidi_paragraph_direction);
+  defsubr (&Smove_point_visually);
 
   DEFSYM (Qmenu_bar_update_hook, "menu-bar-update-hook");
   DEFSYM (Qoverriding_terminal_local_map, "overriding-terminal-local-map");