diff options
| author | Eli Zaretskii | 2011-09-24 16:23:58 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2011-09-24 16:23:58 +0300 |
| commit | e3cbd34b8793d52fb3252c8e0406bc5d67c2db44 (patch) | |
| tree | abe74fe6623c25dea8c65abb05806b05baaf76b5 /src | |
| parent | fac7ae53a7832a967e351c6dbfa5552bb313d4bc (diff) | |
| download | emacs-e3cbd34b8793d52fb3252c8e0406bc5d67c2db44.tar.gz emacs-e3cbd34b8793d52fb3252c8e0406bc5d67c2db44.zip | |
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.
Diffstat (limited to 'src')
| -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 @@ | |||
| 1 | 2011-09-24 Eli Zaretskii <eliz@gnu.org> | ||
| 2 | |||
| 3 | * indent.c (Fvertical_motion): Compute and apply the overshoot | ||
| 4 | logic when moving up, not only when moving down. Fix the | ||
| 5 | confusing name and values of the it_overshoot_expected variable; | ||
| 6 | logic changes accordingly. (Bug#9254) (Bug#9549) | ||
| 7 | |||
| 8 | * xdisp.c (pos_visible_p): Produce correct pixel coordinates when | ||
| 9 | CHARPOS is covered by a display string which includes newlines. | ||
| 10 | (move_it_vertically_backward): Avoid inflooping when START_CHARPOS | ||
| 11 | is covered by a display string with embedded newlines. | ||
| 12 | |||
| 1 | 2011-09-24 Michael Albinus <michael.albinus@gmx.de> | 13 | 2011-09-24 Michael Albinus <michael.albinus@gmx.de> |
| 2 | 14 | ||
| 3 | * dbusbind.c (Fdbus_register_signal): Add match rule to | 15 | * 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. */) | |||
| 2019 | else | 2019 | else |
| 2020 | { | 2020 | { |
| 2021 | EMACS_INT it_start; | 2021 | EMACS_INT it_start; |
| 2022 | int first_x, it_overshoot_expected IF_LINT (= 0); | 2022 | int first_x, it_overshoot_count = 0; |
| 2023 | int overshoot_handled = 0; | ||
| 2023 | 2024 | ||
| 2024 | itdata = bidi_shelve_cache (); | 2025 | itdata = bidi_shelve_cache (); |
| 2025 | SET_TEXT_POS (pt, PT, PT_BYTE); | 2026 | SET_TEXT_POS (pt, PT, PT_BYTE); |
| @@ -2028,22 +2029,23 @@ whether or not it is currently displayed in some window. */) | |||
| 2028 | it_start = IT_CHARPOS (it); | 2029 | it_start = IT_CHARPOS (it); |
| 2029 | 2030 | ||
| 2030 | /* See comments below for why we calculate this. */ | 2031 | /* See comments below for why we calculate this. */ |
| 2031 | if (XINT (lines) > 0) | 2032 | if (it.cmp_it.id >= 0) |
| 2033 | it_overshoot_count = 0; | ||
| 2034 | else if (it.method == GET_FROM_STRING) | ||
| 2032 | { | 2035 | { |
| 2033 | if (it.cmp_it.id >= 0) | 2036 | const char *s = SSDATA (it.string); |
| 2034 | it_overshoot_expected = 1; | 2037 | const char *e = s + SBYTES (it.string); |
| 2035 | else if (it.method == GET_FROM_STRING) | 2038 | while (s < e) |
| 2036 | { | 2039 | { |
| 2037 | const char *s = SSDATA (it.string); | 2040 | if (*s++ == '\n') |
| 2038 | const char *e = s + SBYTES (it.string); | 2041 | it_overshoot_count++; |
| 2039 | while (s < e && *s != '\n') | ||
| 2040 | ++s; | ||
| 2041 | it_overshoot_expected = (s == e) ? -1 : 0; | ||
| 2042 | } | 2042 | } |
| 2043 | else | 2043 | if (!it_overshoot_count) |
| 2044 | it_overshoot_expected = (it.method == GET_FROM_IMAGE | 2044 | it_overshoot_count == -1; |
| 2045 | || it.method == GET_FROM_STRETCH); | ||
| 2046 | } | 2045 | } |
| 2046 | else | ||
| 2047 | it_overshoot_count = | ||
| 2048 | !(it.method == GET_FROM_IMAGE || it.method == GET_FROM_STRETCH); | ||
| 2047 | 2049 | ||
| 2048 | /* Scan from the start of the line containing PT. If we don't | 2050 | /* Scan from the start of the line containing PT. If we don't |
| 2049 | do this, we start moving with IT->current_x == 0, while PT is | 2051 | 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. */) | |||
| 2057 | tell, and it causes Bug#2694 . -- cyd */ | 2059 | tell, and it causes Bug#2694 . -- cyd */ |
| 2058 | move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); | 2060 | move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); |
| 2059 | 2061 | ||
| 2062 | /* IT may move too far if truncate-lines is on and PT lies | ||
| 2063 | beyond the right margin. IT may also move too far if the | ||
| 2064 | starting point is on a Lisp string that has embedded | ||
| 2065 | newlines. In these cases, backtrack. */ | ||
| 2066 | if (IT_CHARPOS (it) > it_start) | ||
| 2067 | { | ||
| 2068 | /* We need to backtrack also if the Lisp string contains no | ||
| 2069 | newlines, but there is a newline right after it. In this | ||
| 2070 | case, IT overshoots if there is an after-string just | ||
| 2071 | before the newline. */ | ||
| 2072 | if (it_overshoot_count < 0 | ||
| 2073 | && it.method == GET_FROM_BUFFER | ||
| 2074 | && it.c == '\n') | ||
| 2075 | it_overshoot_count = 1; | ||
| 2076 | if (it_overshoot_count > 0) | ||
| 2077 | move_it_by_lines (&it, -it_overshoot_count); | ||
| 2078 | |||
| 2079 | overshoot_handled = 1; | ||
| 2080 | } | ||
| 2060 | if (XINT (lines) <= 0) | 2081 | if (XINT (lines) <= 0) |
| 2061 | { | 2082 | { |
| 2062 | it.vpos = 0; | 2083 | it.vpos = 0; |
| @@ -2065,47 +2086,31 @@ whether or not it is currently displayed in some window. */) | |||
| 2065 | if (XINT (lines) == 0 || IT_CHARPOS (it) > 0) | 2086 | if (XINT (lines) == 0 || IT_CHARPOS (it) > 0) |
| 2066 | move_it_by_lines (&it, max (INT_MIN, XINT (lines))); | 2087 | move_it_by_lines (&it, max (INT_MIN, XINT (lines))); |
| 2067 | } | 2088 | } |
| 2089 | else if (overshoot_handled) | ||
| 2090 | { | ||
| 2091 | it.vpos = 0; | ||
| 2092 | move_it_by_lines (&it, min (INT_MAX, XINT (lines))); | ||
| 2093 | } | ||
| 2068 | else | 2094 | else |
| 2069 | { | 2095 | { |
| 2070 | if (IT_CHARPOS (it) > it_start) | 2096 | /* Otherwise, we are at the first row occupied by PT, which |
| 2071 | { | 2097 | might span multiple screen lines (e.g., if it's on a |
| 2072 | /* IT may move too far if truncate-lines is on and PT | 2098 | multi-line display string). We want to start from the |
| 2073 | lies beyond the right margin. In that case, | 2099 | last line that it occupies. */ |
| 2074 | backtrack unless the starting point is on an image, | 2100 | if (it_start < ZV) |
| 2075 | stretch glyph, composition, or Lisp string. */ | ||
| 2076 | if (!it_overshoot_expected | ||
| 2077 | /* Also, backtrack if the Lisp string contains no | ||
| 2078 | newline, but there is a newline right after it. | ||
| 2079 | In this case, IT overshoots if there is an | ||
| 2080 | after-string just before the newline. */ | ||
| 2081 | || (it_overshoot_expected < 0 | ||
| 2082 | && it.method == GET_FROM_BUFFER | ||
| 2083 | && it.c == '\n')) | ||
| 2084 | move_it_by_lines (&it, -1); | ||
| 2085 | it.vpos = 0; | ||
| 2086 | move_it_by_lines (&it, min (INT_MAX, XINT (lines))); | ||
| 2087 | } | ||
| 2088 | else | ||
| 2089 | { | 2101 | { |
| 2090 | /* Otherwise, we are at the first row occupied by PT, | 2102 | while (IT_CHARPOS (it) <= it_start) |
| 2091 | which might span multiple screen lines (e.g., if it's | ||
| 2092 | on a multi-line display string). We want to start | ||
| 2093 | from the last line that it occupies. */ | ||
| 2094 | if (it_start < ZV) | ||
| 2095 | { | ||
| 2096 | while (IT_CHARPOS (it) <= it_start) | ||
| 2097 | { | ||
| 2098 | it.vpos = 0; | ||
| 2099 | move_it_by_lines (&it, 1); | ||
| 2100 | } | ||
| 2101 | if (XINT (lines) > 1) | ||
| 2102 | move_it_by_lines (&it, min (INT_MAX, XINT (lines) - 1)); | ||
| 2103 | } | ||
| 2104 | else | ||
| 2105 | { | 2103 | { |
| 2106 | it.vpos = 0; | 2104 | it.vpos = 0; |
| 2107 | move_it_by_lines (&it, min (INT_MAX, XINT (lines))); | 2105 | move_it_by_lines (&it, 1); |
| 2108 | } | 2106 | } |
| 2107 | if (XINT (lines) > 1) | ||
| 2108 | move_it_by_lines (&it, min (INT_MAX, XINT (lines) - 1)); | ||
| 2109 | } | ||
| 2110 | else | ||
| 2111 | { | ||
| 2112 | it.vpos = 0; | ||
| 2113 | move_it_by_lines (&it, min (INT_MAX, XINT (lines))); | ||
| 2109 | } | 2114 | } |
| 2110 | } | 2115 | } |
| 2111 | 2116 | ||
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) | |||
| 1210 | return line_top_y + line_height; | 1210 | return line_top_y + line_height; |
| 1211 | } | 1211 | } |
| 1212 | 1212 | ||
| 1213 | /* Subroutine of pos_visible_p below. Extracts a display string, if | ||
| 1214 | any, from the display spec given as its argument. */ | ||
| 1215 | static Lisp_Object | ||
| 1216 | string_from_display_spec (Lisp_Object spec) | ||
| 1217 | { | ||
| 1218 | if (CONSP (spec)) | ||
| 1219 | { | ||
| 1220 | while (CONSP (spec)) | ||
| 1221 | { | ||
| 1222 | if (STRINGP (XCAR (spec))) | ||
| 1223 | return XCAR (spec); | ||
| 1224 | spec = XCDR (spec); | ||
| 1225 | } | ||
| 1226 | } | ||
| 1227 | else if (VECTORP (spec)) | ||
| 1228 | { | ||
| 1229 | int i; | ||
| 1230 | |||
| 1231 | for (i = 0; i < ASIZE (spec); i++) | ||
| 1232 | { | ||
| 1233 | if (STRINGP (AREF (spec, i))) | ||
| 1234 | return AREF (spec, i); | ||
| 1235 | } | ||
| 1236 | return Qnil; | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | return spec; | ||
| 1240 | } | ||
| 1213 | 1241 | ||
| 1214 | /* Return 1 if position CHARPOS is visible in window W. | 1242 | /* Return 1 if position CHARPOS is visible in window W. |
| 1215 | CHARPOS < 0 means return info about WINDOW_END position. | 1243 | 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, | |||
| 1304 | } | 1332 | } |
| 1305 | } | 1333 | } |
| 1306 | } | 1334 | } |
| 1335 | else if (IT_CHARPOS (it) != charpos) | ||
| 1336 | { | ||
| 1337 | Lisp_Object cpos = make_number (charpos); | ||
| 1338 | Lisp_Object spec = Fget_char_property (cpos, Qdisplay, Qnil); | ||
| 1339 | Lisp_Object string = string_from_display_spec (spec); | ||
| 1340 | int newline_in_string = 0; | ||
| 1341 | |||
| 1342 | if (STRINGP (string)) | ||
| 1343 | { | ||
| 1344 | const char *s = SSDATA (string); | ||
| 1345 | const char *e = s + SBYTES (string); | ||
| 1346 | while (s < e) | ||
| 1347 | { | ||
| 1348 | if (*s++ == '\n') | ||
| 1349 | { | ||
| 1350 | newline_in_string = 1; | ||
| 1351 | break; | ||
| 1352 | } | ||
| 1353 | } | ||
| 1354 | } | ||
| 1355 | /* The tricky code below is needed because there's a | ||
| 1356 | discrepancy between move_it_to and how we set cursor | ||
| 1357 | when the display line ends in a newline from a | ||
| 1358 | display string. move_it_to will stop _after_ such | ||
| 1359 | display strings, whereas set_cursor_from_row | ||
| 1360 | conspires with cursor_row_p to place the cursor on | ||
| 1361 | the first glyph produced from the display string. */ | ||
| 1362 | |||
| 1363 | /* We have overshoot PT because it is covered by a | ||
| 1364 | display property whose value is a string. If the | ||
| 1365 | string includes embedded newlines, we are also in the | ||
| 1366 | wrong display line. Backtrack to the correct line, | ||
| 1367 | where the display string begins. */ | ||
| 1368 | if (newline_in_string) | ||
| 1369 | { | ||
| 1370 | Lisp_Object startpos, endpos; | ||
| 1371 | EMACS_INT start, end; | ||
| 1372 | struct it it3; | ||
| 1373 | |||
| 1374 | /* Find the first and the last buffer positions | ||
| 1375 | covered by the display string. */ | ||
| 1376 | endpos = | ||
| 1377 | Fnext_single_char_property_change (cpos, Qdisplay, | ||
| 1378 | Qnil, Qnil); | ||
| 1379 | startpos = | ||
| 1380 | Fprevious_single_char_property_change (endpos, Qdisplay, | ||
| 1381 | Qnil, Qnil); | ||
| 1382 | start = XFASTINT (startpos); | ||
| 1383 | end = XFASTINT (endpos); | ||
| 1384 | /* Move to the last buffer position before the | ||
| 1385 | display property. */ | ||
| 1386 | start_display (&it3, w, top); | ||
| 1387 | move_it_to (&it3, start - 1, -1, -1, -1, MOVE_TO_POS); | ||
| 1388 | /* Move forward one more line if the position before | ||
| 1389 | the display string is a newline or if it is the | ||
| 1390 | rightmost character on a line that is | ||
| 1391 | continued or word-wrapped. */ | ||
| 1392 | if (it3.method == GET_FROM_BUFFER | ||
| 1393 | && it3.c == '\n') | ||
| 1394 | move_it_by_lines (&it3, 1); | ||
| 1395 | else if (move_it_in_display_line_to (&it3, -1, | ||
| 1396 | it3.current_x | ||
| 1397 | + it3.pixel_width, | ||
| 1398 | MOVE_TO_X) | ||
| 1399 | == MOVE_LINE_CONTINUED) | ||
| 1400 | { | ||
| 1401 | move_it_by_lines (&it3, 1); | ||
| 1402 | /* When we are under word-wrap, the #$@%! | ||
| 1403 | move_it_by_lines moves 2 lines, so we need to | ||
| 1404 | fix that up. */ | ||
| 1405 | if (it3.line_wrap == WORD_WRAP) | ||
| 1406 | move_it_by_lines (&it3, -1); | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | /* Record the vertical coordinate of the display | ||
| 1410 | line where we wound up. */ | ||
| 1411 | top_y = it3.current_y; | ||
| 1412 | if (it3.bidi_p) | ||
| 1413 | { | ||
| 1414 | /* When characters are reordered for display, | ||
| 1415 | the character displayed to the left of the | ||
| 1416 | display string could be _after_ the display | ||
| 1417 | property in the logical order. Use the | ||
| 1418 | smallest vertical position of these two. */ | ||
| 1419 | start_display (&it3, w, top); | ||
| 1420 | move_it_to (&it3, end + 1, -1, -1, -1, MOVE_TO_POS); | ||
| 1421 | if (it3.current_y < top_y) | ||
| 1422 | top_y = it3.current_y; | ||
| 1423 | } | ||
| 1424 | /* Move from the top of the window to the beginning | ||
| 1425 | of the display line where the display string | ||
| 1426 | begins. */ | ||
| 1427 | start_display (&it3, w, top); | ||
| 1428 | move_it_to (&it3, -1, 0, top_y, -1, MOVE_TO_X | MOVE_TO_Y); | ||
| 1429 | /* Finally, advance the iterator until we hit the | ||
| 1430 | first display element whose character position is | ||
| 1431 | CHARPOS, or until the first newline from the | ||
| 1432 | display string, which signals the end of the | ||
| 1433 | display line. */ | ||
| 1434 | while (get_next_display_element (&it3)) | ||
| 1435 | { | ||
| 1436 | PRODUCE_GLYPHS (&it3); | ||
| 1437 | if (IT_CHARPOS (it3) == charpos | ||
| 1438 | || ITERATOR_AT_END_OF_LINE_P (&it3)) | ||
| 1439 | break; | ||
| 1440 | set_iterator_to_next (&it3, 0); | ||
| 1441 | } | ||
| 1442 | top_x = it3.current_x - it3.pixel_width; | ||
| 1443 | /* Normally, we would exit the above loop because we | ||
| 1444 | found the display element whose character | ||
| 1445 | position is CHARPOS. For the contingency that we | ||
| 1446 | didn't, and stopped at the first newline from the | ||
| 1447 | display string, move back over the glyphs | ||
| 1448 | prfoduced from the string, until we find the | ||
| 1449 | rightmost glyph not from the string. */ | ||
| 1450 | if (IT_CHARPOS (it3) != charpos && EQ (it3.object, string)) | ||
| 1451 | { | ||
| 1452 | struct glyph *g = it3.glyph_row->glyphs[TEXT_AREA] | ||
| 1453 | + it3.glyph_row->used[TEXT_AREA]; | ||
| 1454 | |||
| 1455 | while (EQ ((g - 1)->object, string)) | ||
| 1456 | { | ||
| 1457 | --g; | ||
| 1458 | top_x -= g->pixel_width; | ||
| 1459 | } | ||
| 1460 | xassert (g < it3.glyph_row->glyphs[TEXT_AREA] | ||
| 1461 | + it3.glyph_row->used[TEXT_AREA]); | ||
| 1462 | } | ||
| 1463 | } | ||
| 1464 | } | ||
| 1307 | 1465 | ||
| 1308 | *x = top_x; | 1466 | *x = top_x; |
| 1309 | *y = max (top_y + max (0, it.max_ascent - it.ascent), window_top_y); | 1467 | *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) | |||
| 8521 | move_it_to (&it2, start_pos, -1, -1, it2.vpos + 1, | 8679 | move_it_to (&it2, start_pos, -1, -1, it2.vpos + 1, |
| 8522 | MOVE_TO_POS | MOVE_TO_VPOS); | 8680 | MOVE_TO_POS | MOVE_TO_VPOS); |
| 8523 | } | 8681 | } |
| 8524 | while (!IT_POS_VALID_AFTER_MOVE_P (&it2)); | 8682 | while (!(IT_POS_VALID_AFTER_MOVE_P (&it2) |
| 8683 | /* If we are in a display string which starts at START_POS, | ||
| 8684 | and that display string includes a newline, and we are | ||
| 8685 | right after that newline (i.e. at the beginning of a | ||
| 8686 | display line), exit the loop, because otherwise we will | ||
| 8687 | infloop, since move_it_to will see that it is already at | ||
| 8688 | START_POS and will not move. */ | ||
| 8689 | || (it2.method == GET_FROM_STRING | ||
| 8690 | && IT_CHARPOS (it2) == start_pos | ||
| 8691 | && SREF (it2.string, IT_STRING_BYTEPOS (it2) - 1) == '\n'))); | ||
| 8525 | xassert (IT_CHARPOS (*it) >= BEGV); | 8692 | xassert (IT_CHARPOS (*it) >= BEGV); |
| 8526 | SAVE_IT (it3, it2, it3data); | 8693 | SAVE_IT (it3, it2, it3data); |
| 8527 | 8694 | ||