Mercurial > emacs
changeset 108201:e61225c9e186
Fix display of R2L lines and cursor motion in bidi buffers.
xdisp.c (find_row_end): New function, refactored from display_line.
(display_line): Use it.
(extend_face_to_end_of_line): In almost-filled rows, extend only
if the row is R2L and not continued.
(display_line): Fix prepending of truncation glyphs to R2L rows.
Preserve overlay and string info in row->end.
(insert_left_trunc_glyphs): Support addition of left truncation
glyphs to R2L rows.
(set_cursor_from_row): Don't place cursor on the vertical border
glyph between adjacent windows. Fix a crash when a display string
is continued to the next line. Don't return zero if cursor was
found by `cursor' property of a display string.
(try_cursor_movement): Don't assume that row->end == (row+1)->start,
test for that explicitly.
author | Eli Zaretskii <eliz@gnu.org> |
---|---|
date | Sat, 01 May 2010 21:11:20 +0300 |
parents | ed33609a7e36 (current diff) 97b67e89ecc5 (diff) |
children | 05cdbfffcad4 |
files | src/ChangeLog src/xdisp.c |
diffstat | 2 files changed, 227 insertions(+), 138 deletions(-) [+] |
line wrap: on
line diff
--- a/src/ChangeLog Sat May 01 12:08:42 2010 +0200 +++ b/src/ChangeLog Sat May 01 21:11:20 2010 +0300 @@ -1,3 +1,23 @@ +2010-05-01 Eli Zaretskii <eliz@gnu.org> + + Miscellaneous fixes of bidi display. + + * xdisp.c (find_row_end): New function, refactored from + display_line. + (display_line): Use it. + (extend_face_to_end_of_line): In almost-filled rows, extend only + if the row is R2L and not continued. + (display_line): Fix prepending of truncation glyphs to R2L rows. + Preserve overlay and string info in row->end. + (insert_left_trunc_glyphs): Support addition of left truncation + glyphs to R2L rows. + (set_cursor_from_row): Don't place cursor on the vertical border + glyph between adjacent windows. Fix a crash when a display string + is continued to the next line. Don't return zero if cursor was + found by `cursor' property of a display string. + (try_cursor_movement): Don't assume that row->end == (row+1)->start, + test for that explicitly. + 2010-05-01 Glenn Morris <rgm@gnu.org> * Makefile.in (gmallocobj, rallocobj, vmlimitobj): Initialize to null,
--- a/src/xdisp.c Sat May 01 12:08:42 2010 +0200 +++ b/src/xdisp.c Sat May 01 21:11:20 2010 +0300 @@ -12776,6 +12776,13 @@ rightmost glyph. Case in point: an empty last line that is part of an R2L paragraph. */ cursor = end - 1; + /* Avoid placing the cursor on the last glyph of the row, where + on terminal frames we hold the vertical border between + adjacent windows. */ + if (!FRAME_WINDOW_P (WINDOW_XFRAME (w)) + && !WINDOW_RIGHTMOST_P (w) + && cursor == row->glyphs[LAST_AREA] - 1) + cursor--; x = -1; /* will be computed below, at label compute_x */ } @@ -13015,7 +13022,8 @@ cursor = glyph; for (glyph += incr; - EQ (glyph->object, str); + (row->reversed_p ? glyph > stop : glyph < stop) + && EQ (glyph->object, str); glyph += incr) { Lisp_Object cprop; @@ -13055,8 +13063,9 @@ /* If we reached the end of the line, and END was from a string, the cursor is not on this line. */ - if (glyph == end - && STRINGP ((glyph - incr)->object) + if (cursor == NULL + && (row->reversed_p ? glyph <= end : glyph >= end) + && STRINGP (end->object) && row->continued_p) return 0; } @@ -13714,11 +13723,14 @@ ++row; } - /* The end position of a row equals the start position - of the next row. If PT is there, we would rather - display it in the next line. */ + /* If the end position of a row equals the start + position of the next row, and PT is at that position, + we would rather display cursor in the next line. */ while (MATRIX_ROW_BOTTOM_Y (row) < last_y && MATRIX_ROW_END_CHARPOS (row) == PT + && row < w->current_matrix->rows + + w->current_matrix->nrows - 1 + && MATRIX_ROW_START_CHARPOS (row+1) == PT && !cursor_row_p (w, row)) ++row; @@ -16737,24 +16749,61 @@ produce_special_glyphs (&truncate_it, IT_TRUNCATION); /* Overwrite glyphs from IT with truncation glyphs. */ - from = truncate_it.glyph_row->glyphs[TEXT_AREA]; - end = from + truncate_it.glyph_row->used[TEXT_AREA]; - to = it->glyph_row->glyphs[TEXT_AREA]; - toend = to + it->glyph_row->used[TEXT_AREA]; - - while (from < end) - *to++ = *from++; - - /* There may be padding glyphs left over. Overwrite them too. */ - while (to < toend && CHAR_GLYPH_PADDING_P (*to)) + if (!it->glyph_row->reversed_p) { from = truncate_it.glyph_row->glyphs[TEXT_AREA]; + end = from + truncate_it.glyph_row->used[TEXT_AREA]; + to = it->glyph_row->glyphs[TEXT_AREA]; + toend = to + it->glyph_row->used[TEXT_AREA]; + while (from < end) *to++ = *from++; - } - - if (to > toend) - it->glyph_row->used[TEXT_AREA] = to - it->glyph_row->glyphs[TEXT_AREA]; + + /* There may be padding glyphs left over. Overwrite them too. */ + while (to < toend && CHAR_GLYPH_PADDING_P (*to)) + { + from = truncate_it.glyph_row->glyphs[TEXT_AREA]; + while (from < end) + *to++ = *from++; + } + + if (to > toend) + it->glyph_row->used[TEXT_AREA] = to - it->glyph_row->glyphs[TEXT_AREA]; + } + else + { + /* In R2L rows, overwrite the last (rightmost) glyphs, and do + that back to front. */ + end = truncate_it.glyph_row->glyphs[TEXT_AREA]; + from = end + truncate_it.glyph_row->used[TEXT_AREA] - 1; + toend = it->glyph_row->glyphs[TEXT_AREA]; + to = toend + it->glyph_row->used[TEXT_AREA] - 1; + + while (from >= end && to >= toend) + *to-- = *from--; + while (to >= toend && CHAR_GLYPH_PADDING_P (*to)) + { + from = + truncate_it.glyph_row->glyphs[TEXT_AREA] + + truncate_it.glyph_row->used[TEXT_AREA] - 1; + while (from >= end && to >= toend) + *to-- = *from--; + } + if (from >= end) + { + /* Need to free some room before prepending additional + glyphs. */ + int move_by = from - end + 1; + struct glyph *g0 = it->glyph_row->glyphs[TEXT_AREA]; + struct glyph *g = g0 + it->glyph_row->used[TEXT_AREA] - 1; + + for ( ; g >= g0; g--) + g[move_by] = *g; + while (from >= end) + *to-- = *from--; + it->glyph_row->used[TEXT_AREA] += move_by; + } + } } @@ -16946,8 +16995,13 @@ /* If line is already filled, do nothing. Non window-system frames get a grace of one more ``pixel'' because their characters are - 1-``pixel'' wide, so they hit the equality too early. */ - if (it->current_x >= it->last_visible_x + !FRAME_WINDOW_P (f)) + 1-``pixel'' wide, so they hit the equality too early. This grace + is needed only for R2L rows that are not continued, to produce + one extra blank where we could display the cursor. */ + if (it->current_x >= it->last_visible_x + + (!FRAME_WINDOW_P (f) + && it->glyph_row->reversed_p + && !it->glyph_row->continued_p)) return; /* Face extension extends the background and box of IT->face_id @@ -17368,6 +17422,123 @@ glyph[-n] = *glyph; } +/* Find the positions in a bidi-reordered ROW to serve as ROW->start + and ROW->end. */ +static struct display_pos +find_row_end (it, row) + struct it *it; + struct glyph_row *row; +{ + /* FIXME: Revisit this when glyph ``spilling'' in continuation + lines' rows is implemented for bidi-reordered rows. */ + EMACS_INT min_pos = ZV + 1, max_pos = 0; + struct glyph *g; + struct it save_it; + struct text_pos tpos; + struct display_pos row_end = it->current; + + for (g = row->glyphs[TEXT_AREA]; + g < row->glyphs[TEXT_AREA] + row->used[TEXT_AREA]; + g++) + { + if (BUFFERP (g->object)) + { + if (g->charpos > 0 && g->charpos < min_pos) + min_pos = g->charpos; + if (g->charpos > max_pos) + max_pos = g->charpos; + } + } + /* Empty lines have a valid buffer position at their first + glyph, but that glyph's OBJECT is zero, as if it didn't come + from a buffer. If we didn't find any valid buffer positions + in this row, maybe we have such an empty line. */ + if (max_pos == 0 && row->used[TEXT_AREA]) + { + for (g = row->glyphs[TEXT_AREA]; + g < row->glyphs[TEXT_AREA] + row->used[TEXT_AREA]; + g++) + { + if (INTEGERP (g->object)) + { + if (g->charpos > 0 && g->charpos < min_pos) + min_pos = g->charpos; + if (g->charpos > max_pos) + max_pos = g->charpos; + } + } + } + + /* ROW->start is the value of min_pos, the minimal buffer position + we have in ROW. */ + if (min_pos <= ZV) + { + /* Avoid calling the costly CHAR_TO_BYTE if possible. */ + if (min_pos != row->start.pos.charpos) + SET_TEXT_POS (row->start.pos, min_pos, CHAR_TO_BYTE (min_pos)); + if (max_pos == 0) + max_pos = min_pos; + } + + /* For ROW->end, we need the position that is _after_ max_pos, in + the logical order, unless we are at ZV. */ + if (row->ends_at_zv_p) + { + if (!row->used[TEXT_AREA]) + row->start.pos = row_end.pos; + } + else if (row->used[TEXT_AREA] && max_pos) + { + int at_eol_p; + + SET_TEXT_POS (tpos, max_pos, CHAR_TO_BYTE (max_pos)); + save_it = *it; + it->bidi_p = 0; + reseat (it, tpos, 0); + if (!get_next_display_element (it)) + abort (); /* this row cannot be at ZV, see above */ + at_eol_p = ITERATOR_AT_END_OF_LINE_P (it); + set_iterator_to_next (it, 1); + row_end = it->current; + /* If the character at max_pos is not a newline and the + characters at max_pos+1 is a newline, skip that newline as + well. Note that this may skip some invisible text. */ + if (!at_eol_p + && get_next_display_element (it) + && ITERATOR_AT_END_OF_LINE_P (it)) + { + set_iterator_to_next (it, 1); + /* Record the position after the newline of a continued row. + We will need that to set ROW->end of the last row + produced for a continued line. */ + if (row->continued_p) + save_it.eol_pos = it->current.pos; + else + { + row_end = it->current; + save_it.eol_pos.charpos = save_it.eol_pos.bytepos = 0; + } + } + else if (!row->continued_p + && MATRIX_ROW_CONTINUATION_LINE_P (row) + && it->eol_pos.charpos > 0) + { + /* Last row of a continued line. Use the position recorded + in IT->eol_pos, to the effect that the newline belongs to + this row, not to the row which displays the character + with the largest buffer position before the newline. */ + row_end.pos = it->eol_pos; + it->eol_pos.charpos = it->eol_pos.bytepos = 0; + } + *it = save_it; + /* The members of ROW->end that are not taken from buffer + positions are copied from IT->current. */ + row_end.string_pos = it->current.string_pos; + row_end.overlay_string_index = it->current.overlay_string_index; + row_end.dpvec_index = it->current.dpvec_index; + } + return row_end; +} /* Construct the glyph row IT->glyph_row in the desired matrix of IT->w from text at the current position of IT. See dispextern.h @@ -17386,7 +17557,6 @@ int wrap_row_used = -1, wrap_row_ascent, wrap_row_height; int wrap_row_phys_ascent, wrap_row_phys_height; int wrap_row_extra_line_spacing; - struct display_pos row_end; int cvpos; /* We always start displaying at hpos zero even if hscrolled. */ @@ -17815,10 +17985,14 @@ for (i = 0; i < row->used[TEXT_AREA]; i++) if (!CHAR_GLYPH_PADDING_P (row->glyphs[TEXT_AREA][i])) break; - /* Remove padding glyphs at the front of ROW, to + /* Remove any padding glyphs at the front of ROW, to make room for the truncation glyphs we will be - adding below. */ - unproduce_glyphs (it, i); + adding below. The loop below always inserts at + least one truncation glyph, so also remove the + last glyph added to ROW. */ + unproduce_glyphs (it, i + 1); + /* Adjust i for the loop below. */ + i = row->used[TEXT_AREA] - (i + 1); } for (n = row->used[TEXT_AREA]; i < n; ++i) @@ -17910,118 +18084,13 @@ compute_line_metrics (it); /* Remember the position at which this line ends. */ - row->end = row_end = it->current; + row->end = it->current; + /* ROW->start and ROW->end must be the smallest and the largest + buffer positions in ROW. But if ROW was bidi-reordered, these + two positions can be anywhere in the row, so we must rescan all + of the ROW's glyphs to find them. */ if (it->bidi_p) - { - /* ROW->start and ROW->end must be the smallest and largest - buffer positions in ROW. But if ROW was bidi-reordered, - these two positions can be anywhere in the row, so we must - rescan all of the ROW's glyphs to find them. */ - /* FIXME: Revisit this when glyph ``spilling'' in continuation - lines' rows is implemented for bidi-reordered rows. */ - EMACS_INT min_pos = ZV + 1, max_pos = 0; - struct glyph *g; - struct it save_it; - struct text_pos tpos; - - for (g = row->glyphs[TEXT_AREA]; - g < row->glyphs[TEXT_AREA] + row->used[TEXT_AREA]; - g++) - { - if (BUFFERP (g->object)) - { - if (g->charpos > 0 && g->charpos < min_pos) - min_pos = g->charpos; - if (g->charpos > max_pos) - max_pos = g->charpos; - } - } - /* Empty lines have a valid buffer position at their first - glyph, but that glyph's OBJECT is zero, as if it didn't come - from a buffer. If we didn't find any valid buffer positions - in this row, maybe we have such an empty line. */ - if (min_pos == ZV + 1 && row->used[TEXT_AREA]) - { - for (g = row->glyphs[TEXT_AREA]; - g < row->glyphs[TEXT_AREA] + row->used[TEXT_AREA]; - g++) - { - if (INTEGERP (g->object)) - { - if (g->charpos > 0 && g->charpos < min_pos) - min_pos = g->charpos; - if (g->charpos > max_pos) - max_pos = g->charpos; - } - } - } - if (min_pos <= ZV) - { - if (min_pos != row->start.pos.charpos) - { - row->start.pos.charpos = min_pos; - row->start.pos.bytepos = CHAR_TO_BYTE (min_pos); - } - if (max_pos == 0) - max_pos = min_pos; - } - /* For ROW->end, we need the position that is _after_ max_pos, - in the logical order, unless we are at ZV. */ - if (row->ends_at_zv_p) - { - row_end = row->end = it->current; - if (!row->used[TEXT_AREA]) - { - row->start.pos.charpos = row_end.pos.charpos; - row->start.pos.bytepos = row_end.pos.bytepos; - } - } - else if (row->used[TEXT_AREA] && max_pos) - { - SET_TEXT_POS (tpos, max_pos, CHAR_TO_BYTE (max_pos)); - save_it = *it; - it->bidi_p = 0; - reseat (it, tpos, 0); - if (!get_next_display_element (it)) - abort (); /* row at ZV was already handled above */ - set_iterator_to_next (it, 1); - row_end = it->current; - /* If the character at max_pos+1 is a newline, skip that as - well. Note that this may skip some invisible text. */ - if (get_next_display_element (it) - && ITERATOR_AT_END_OF_LINE_P (it)) - { - set_iterator_to_next (it, 1); - /* Record the position after the newline of a continued - row. We will need that to set ROW->end of the last - row produced for a continued line. */ - if (row->continued_p) - { - save_it.eol_pos.charpos = IT_CHARPOS (*it); - save_it.eol_pos.bytepos = IT_BYTEPOS (*it); - } - else - { - row_end = it->current; - save_it.eol_pos.charpos = save_it.eol_pos.bytepos = 0; - } - } - else if (!row->continued_p - && MATRIX_ROW_CONTINUATION_LINE_P (row) - && it->eol_pos.charpos > 0) - { - /* Last row of a continued line. Use the position - recorded in ROW->eol_pos, to the effect that the - newline belongs to this row, not to the row which - displays the character with the largest buffer - position. */ - row_end.pos = it->eol_pos; - it->eol_pos.charpos = it->eol_pos.bytepos = 0; - } - *it = save_it; - row->end = row_end; - } - } + row->end = find_row_end (it, row); /* Record whether this row ends inside an ellipsis. */ row->ends_in_ellipsis_p @@ -18075,7 +18144,7 @@ the flag accordingly. */ if (it->glyph_row < MATRIX_BOTTOM_TEXT_ROW (it->w->desired_matrix, it->w)) it->glyph_row->reversed_p = row->reversed_p; - it->start = row_end; + it->start = row->end; return row->displays_text_p; }