aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEli Zaretskii <[email protected]>2013-06-29 16:36:19 +0300
committerEli Zaretskii <[email protected]>2013-06-29 16:36:19 +0300
commit4c672a0fec1d18cc1a445acf3e6935d681d4048f (patch)
treec8fb2626c93a226bed5eaa0b92f95925734e893f
parent73b1b3ad6196234984a29298bc66eabf1299de66 (diff)
Implement visual-order cursor motion.
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.
-rw-r--r--doc/emacs/ChangeLog5
-rw-r--r--doc/emacs/basic.texi14
-rw-r--r--doc/emacs/mule.texi4
-rw-r--r--doc/lispref/ChangeLog4
-rw-r--r--doc/lispref/display.texi20
-rw-r--r--etc/NEWS7
-rw-r--r--lisp/ChangeLog6
-rw-r--r--lisp/bindings.el58
-rw-r--r--src/ChangeLog4
-rw-r--r--src/xdisp.c393
10 files changed, 500 insertions, 15 deletions
diff --git a/doc/emacs/ChangeLog b/doc/emacs/ChangeLog
index 45f0849921..d5f3095908 100644
--- a/doc/emacs/ChangeLog
+++ b/doc/emacs/ChangeLog
@@ -1,3 +1,8 @@
+2013-06-29 Eli Zaretskii <[email protected]>
+
+ * basic.texi (Moving Point): Document visual-order-cursor-movement
+ and its effect on right-char and left-char.
+
2013-06-28 Glenn Morris <[email protected]>
* ack.texi (Acknowledgments): Small update.
diff --git a/doc/emacs/basic.texi b/doc/emacs/basic.texi
index b9bc391d1c..a840f91265 100644
--- a/doc/emacs/basic.texi
+++ b/doc/emacs/basic.texi
@@ -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}
diff --git a/doc/emacs/mule.texi b/doc/emacs/mule.texi
index de3e05777c..c8bd5027fa 100644
--- a/doc/emacs/mule.texi
+++ b/doc/emacs/mule.texi
@@ -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}).
diff --git a/doc/lispref/ChangeLog b/doc/lispref/ChangeLog
index 199d94585e..dc0c156473 100644
--- a/doc/lispref/ChangeLog
+++ b/doc/lispref/ChangeLog
@@ -1,3 +1,7 @@
+2013-06-29 Eli Zaretskii <[email protected]>
+
+ * display.texi (Bidirectional Display): Document move-point-visually.
+
2013-06-29 Xue Fuqiao <[email protected]>
* buffers.texi (Buffer File Name): Fix typo.
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index d82b9a4c5a..ecefb684ee 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -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
diff --git a/etc/NEWS b/etc/NEWS
index f5ab7c60ce..a6c93e7cf6 100644
--- 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
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 826f270d8f..1e1fff6fc2 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,9 @@
+2013-06-29 Eli Zaretskii <[email protected]>
+
+ * 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 <[email protected]>
* international/mule.el (define-coding-system): New coding system
diff --git a/lisp/bindings.el b/lisp/bindings.el
index 2013c07982..7c42cc6c0a 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -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).
diff --git a/src/ChangeLog b/src/ChangeLog
index b279f42e6b..1e1b54a72d 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,7 @@
+2013-06-29 Eli Zaretskii <[email protected]>
+
+ * xdisp.c (Fmove_point_visually): New function.
+
2013-06-28 Kenichi Handa <[email protected]>
* coding.h (define_coding_undecided_arg_index): New enum.
diff --git a/src/xdisp.c b/src/xdisp.c
index 54ea325f64..420ff0c918 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -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
+}
/***********************************************************************
@@ -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");