aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEli Zaretskii2011-09-24 16:23:58 +0300
committerEli Zaretskii2011-09-24 16:23:58 +0300
commite3cbd34b8793d52fb3252c8e0406bc5d67c2db44 (patch)
treeabe74fe6623c25dea8c65abb05806b05baaf76b5 /src
parentfac7ae53a7832a967e351c6dbfa5552bb313d4bc (diff)
downloademacs-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/ChangeLog12
-rw-r--r--src/indent.c101
-rw-r--r--src/xdisp.c169
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 @@
12011-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
12011-09-24 Michael Albinus <michael.albinus@gmx.de> 132011-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. */
1215static Lisp_Object
1216string_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