diff options
author | Eli Zaretskii <eliz@gnu.org> | 2011-09-24 16:23:58 +0300 |
---|---|---|
committer | Eli Zaretskii <eliz@gnu.org> | 2011-09-24 16:23:58 +0300 |
commit | e3cbd34b8793d52fb3252c8e0406bc5d67c2db44 (patch) | |
tree | abe74fe6623c25dea8c65abb05806b05baaf76b5 | |
parent | fac7ae53a7832a967e351c6dbfa5552bb313d4bc (diff) | |
download | emacs-e3cbd34b8793d52fb3252c8e0406bc5d67c2db44.tar.gz |
Fix vertical cursor motion when the newline is covered by a display string.
src/indent.c (Fvertical_motion): Compute and apply the overshoot
logic when moving up, not only when moving down. Fix the
confusing name and values of the it_overshoot_expected variable;
logic changes accordingly. (Bug#9254) (Bug#9549)
src/xdisp.c (pos_visible_p): Produce correct pixel coordinates when
CHARPOS is covered by a display string which includes newlines.
(move_it_vertically_backward): Avoid inflooping when START_CHARPOS
is covered by a display string with embedded newlines.
-rw-r--r-- | src/ChangeLog | 12 | ||||
-rw-r--r-- | src/indent.c | 101 | ||||
-rw-r--r-- | src/xdisp.c | 169 |
3 files changed, 233 insertions, 49 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index be80149139f..7c7eaf6c779 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,15 @@ +2011-09-24 Eli Zaretskii <eliz@gnu.org> + + * indent.c (Fvertical_motion): Compute and apply the overshoot + logic when moving up, not only when moving down. Fix the + confusing name and values of the it_overshoot_expected variable; + logic changes accordingly. (Bug#9254) (Bug#9549) + + * xdisp.c (pos_visible_p): Produce correct pixel coordinates when + CHARPOS is covered by a display string which includes newlines. + (move_it_vertically_backward): Avoid inflooping when START_CHARPOS + is covered by a display string with embedded newlines. + 2011-09-24 Michael Albinus <michael.albinus@gmx.de> * dbusbind.c (Fdbus_register_signal): Add match rule to diff --git a/src/indent.c b/src/indent.c index e00d2152577..e6629ef5811 100644 --- a/src/indent.c +++ b/src/indent.c @@ -2019,7 +2019,8 @@ whether or not it is currently displayed in some window. */) else { EMACS_INT it_start; - int first_x, it_overshoot_expected IF_LINT (= 0); + int first_x, it_overshoot_count = 0; + int overshoot_handled = 0; itdata = bidi_shelve_cache (); SET_TEXT_POS (pt, PT, PT_BYTE); @@ -2028,22 +2029,23 @@ whether or not it is currently displayed in some window. */) it_start = IT_CHARPOS (it); /* See comments below for why we calculate this. */ - if (XINT (lines) > 0) + if (it.cmp_it.id >= 0) + it_overshoot_count = 0; + else if (it.method == GET_FROM_STRING) { - if (it.cmp_it.id >= 0) - it_overshoot_expected = 1; - else if (it.method == GET_FROM_STRING) + const char *s = SSDATA (it.string); + const char *e = s + SBYTES (it.string); + while (s < e) { - const char *s = SSDATA (it.string); - const char *e = s + SBYTES (it.string); - while (s < e && *s != '\n') - ++s; - it_overshoot_expected = (s == e) ? -1 : 0; + if (*s++ == '\n') + it_overshoot_count++; } - else - it_overshoot_expected = (it.method == GET_FROM_IMAGE - || it.method == GET_FROM_STRETCH); + if (!it_overshoot_count) + it_overshoot_count == -1; } + else + it_overshoot_count = + !(it.method == GET_FROM_IMAGE || it.method == GET_FROM_STRETCH); /* Scan from the start of the line containing PT. If we don't do this, we start moving with IT->current_x == 0, while PT is @@ -2057,6 +2059,25 @@ whether or not it is currently displayed in some window. */) tell, and it causes Bug#2694 . -- cyd */ move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); + /* IT may move too far if truncate-lines is on and PT lies + beyond the right margin. IT may also move too far if the + starting point is on a Lisp string that has embedded + newlines. In these cases, backtrack. */ + if (IT_CHARPOS (it) > it_start) + { + /* We need to backtrack also if the Lisp string contains no + newlines, but there is a newline right after it. In this + case, IT overshoots if there is an after-string just + before the newline. */ + if (it_overshoot_count < 0 + && it.method == GET_FROM_BUFFER + && it.c == '\n') + it_overshoot_count = 1; + if (it_overshoot_count > 0) + move_it_by_lines (&it, -it_overshoot_count); + + overshoot_handled = 1; + } if (XINT (lines) <= 0) { it.vpos = 0; @@ -2065,47 +2086,31 @@ whether or not it is currently displayed in some window. */) if (XINT (lines) == 0 || IT_CHARPOS (it) > 0) move_it_by_lines (&it, max (INT_MIN, XINT (lines))); } + else if (overshoot_handled) + { + it.vpos = 0; + move_it_by_lines (&it, min (INT_MAX, XINT (lines))); + } else { - if (IT_CHARPOS (it) > it_start) - { - /* IT may move too far if truncate-lines is on and PT - lies beyond the right margin. In that case, - backtrack unless the starting point is on an image, - stretch glyph, composition, or Lisp string. */ - if (!it_overshoot_expected - /* Also, backtrack if the Lisp string contains no - newline, but there is a newline right after it. - In this case, IT overshoots if there is an - after-string just before the newline. */ - || (it_overshoot_expected < 0 - && it.method == GET_FROM_BUFFER - && it.c == '\n')) - move_it_by_lines (&it, -1); - it.vpos = 0; - move_it_by_lines (&it, min (INT_MAX, XINT (lines))); - } - else + /* Otherwise, we are at the first row occupied by PT, which + might span multiple screen lines (e.g., if it's on a + multi-line display string). We want to start from the + last line that it occupies. */ + if (it_start < ZV) { - /* Otherwise, we are at the first row occupied by PT, - which might span multiple screen lines (e.g., if it's - on a multi-line display string). We want to start - from the last line that it occupies. */ - if (it_start < ZV) - { - while (IT_CHARPOS (it) <= it_start) - { - it.vpos = 0; - move_it_by_lines (&it, 1); - } - if (XINT (lines) > 1) - move_it_by_lines (&it, min (INT_MAX, XINT (lines) - 1)); - } - else + while (IT_CHARPOS (it) <= it_start) { it.vpos = 0; - move_it_by_lines (&it, min (INT_MAX, XINT (lines))); + move_it_by_lines (&it, 1); } + if (XINT (lines) > 1) + move_it_by_lines (&it, min (INT_MAX, XINT (lines) - 1)); + } + else + { + it.vpos = 0; + move_it_by_lines (&it, min (INT_MAX, XINT (lines))); } } diff --git a/src/xdisp.c b/src/xdisp.c index 0d870671c69..3f545fae248 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -1210,6 +1210,34 @@ line_bottom_y (struct it *it) return line_top_y + line_height; } +/* Subroutine of pos_visible_p below. Extracts a display string, if + any, from the display spec given as its argument. */ +static Lisp_Object +string_from_display_spec (Lisp_Object spec) +{ + if (CONSP (spec)) + { + while (CONSP (spec)) + { + if (STRINGP (XCAR (spec))) + return XCAR (spec); + spec = XCDR (spec); + } + } + else if (VECTORP (spec)) + { + int i; + + for (i = 0; i < ASIZE (spec); i++) + { + if (STRINGP (AREF (spec, i))) + return AREF (spec, i); + } + return Qnil; + } + + return spec; +} /* Return 1 if position CHARPOS is visible in window W. CHARPOS < 0 means return info about WINDOW_END position. @@ -1304,6 +1332,136 @@ pos_visible_p (struct window *w, EMACS_INT charpos, int *x, int *y, } } } + else if (IT_CHARPOS (it) != charpos) + { + Lisp_Object cpos = make_number (charpos); + Lisp_Object spec = Fget_char_property (cpos, Qdisplay, Qnil); + Lisp_Object string = string_from_display_spec (spec); + int newline_in_string = 0; + + if (STRINGP (string)) + { + const char *s = SSDATA (string); + const char *e = s + SBYTES (string); + while (s < e) + { + if (*s++ == '\n') + { + newline_in_string = 1; + break; + } + } + } + /* The tricky code below is needed because there's a + discrepancy between move_it_to and how we set cursor + when the display line ends in a newline from a + display string. move_it_to will stop _after_ such + display strings, whereas set_cursor_from_row + conspires with cursor_row_p to place the cursor on + the first glyph produced from the display string. */ + + /* We have overshoot PT because it is covered by a + display property whose value is a string. If the + string includes embedded newlines, we are also in the + wrong display line. Backtrack to the correct line, + where the display string begins. */ + if (newline_in_string) + { + Lisp_Object startpos, endpos; + EMACS_INT start, end; + struct it it3; + + /* Find the first and the last buffer positions + covered by the display string. */ + endpos = + Fnext_single_char_property_change (cpos, Qdisplay, + Qnil, Qnil); + startpos = + Fprevious_single_char_property_change (endpos, Qdisplay, + Qnil, Qnil); + start = XFASTINT (startpos); + end = XFASTINT (endpos); + /* Move to the last buffer position before the + display property. */ + start_display (&it3, w, top); + move_it_to (&it3, start - 1, -1, -1, -1, MOVE_TO_POS); + /* Move forward one more line if the position before + the display string is a newline or if it is the + rightmost character on a line that is + continued or word-wrapped. */ + if (it3.method == GET_FROM_BUFFER + && it3.c == '\n') + move_it_by_lines (&it3, 1); + else if (move_it_in_display_line_to (&it3, -1, + it3.current_x + + it3.pixel_width, + MOVE_TO_X) + == MOVE_LINE_CONTINUED) + { + move_it_by_lines (&it3, 1); + /* When we are under word-wrap, the #$@%! + move_it_by_lines moves 2 lines, so we need to + fix that up. */ + if (it3.line_wrap == WORD_WRAP) + move_it_by_lines (&it3, -1); + } + + /* Record the vertical coordinate of the display + line where we wound up. */ + top_y = it3.current_y; + if (it3.bidi_p) + { + /* When characters are reordered for display, + the character displayed to the left of the + display string could be _after_ the display + property in the logical order. Use the + smallest vertical position of these two. */ + start_display (&it3, w, top); + move_it_to (&it3, end + 1, -1, -1, -1, MOVE_TO_POS); + if (it3.current_y < top_y) + top_y = it3.current_y; + } + /* Move from the top of the window to the beginning + of the display line where the display string + begins. */ + start_display (&it3, w, top); + move_it_to (&it3, -1, 0, top_y, -1, MOVE_TO_X | MOVE_TO_Y); + /* Finally, advance the iterator until we hit the + first display element whose character position is + CHARPOS, or until the first newline from the + display string, which signals the end of the + display line. */ + while (get_next_display_element (&it3)) + { + PRODUCE_GLYPHS (&it3); + if (IT_CHARPOS (it3) == charpos + || ITERATOR_AT_END_OF_LINE_P (&it3)) + break; + set_iterator_to_next (&it3, 0); + } + top_x = it3.current_x - it3.pixel_width; + /* Normally, we would exit the above loop because we + found the display element whose character + position is CHARPOS. For the contingency that we + didn't, and stopped at the first newline from the + display string, move back over the glyphs + prfoduced from the string, until we find the + rightmost glyph not from the string. */ + if (IT_CHARPOS (it3) != charpos && EQ (it3.object, string)) + { + struct glyph *g = it3.glyph_row->glyphs[TEXT_AREA] + + it3.glyph_row->used[TEXT_AREA]; + + while (EQ ((g - 1)->object, string)) + { + --g; + top_x -= g->pixel_width; + } + xassert (g < it3.glyph_row->glyphs[TEXT_AREA] + + it3.glyph_row->used[TEXT_AREA]); + } + } + } *x = top_x; *y = max (top_y + max (0, it.max_ascent - it.ascent), window_top_y); @@ -8521,7 +8679,16 @@ move_it_vertically_backward (struct it *it, int dy) move_it_to (&it2, start_pos, -1, -1, it2.vpos + 1, MOVE_TO_POS | MOVE_TO_VPOS); } - while (!IT_POS_VALID_AFTER_MOVE_P (&it2)); + while (!(IT_POS_VALID_AFTER_MOVE_P (&it2) + /* If we are in a display string which starts at START_POS, + and that display string includes a newline, and we are + right after that newline (i.e. at the beginning of a + display line), exit the loop, because otherwise we will + infloop, since move_it_to will see that it is already at + START_POS and will not move. */ + || (it2.method == GET_FROM_STRING + && IT_CHARPOS (it2) == start_pos + && SREF (it2.string, IT_STRING_BYTEPOS (it2) - 1) == '\n'))); xassert (IT_CHARPOS (*it) >= BEGV); SAVE_IT (it3, it2, it3data); |