Mercurial > emacs
changeset 30744:6181f12f7f51
(trace_move) [GLYPH_DEBUG]: New variable.
(TRACE_MOVE) [GLYPH_DEBUG]: New macro.
(move_it_in_display_line_to): Record iterator's ascent and descent
before producing glyphs, and restore them when we know the glyph
doesn't fit on the line.
(move_it_to): Restructured so that it's easier to debug. If
moving to a vpos, and not moving to an x or character position,
stop as soon as the specified vpos is reached; don't move further
into that line because that might change the computed line height.
(try_cursor_movement): New function, extracted from the cursor
movement branch of redisplay_window. If ending on a partially
visible line, don't try to scroll if the cursor line is taller
than the window.
(redisplay_window): Use try_cursor_movement.
author | Gerd Moellmann <gerd@gnu.org> |
---|---|
date | Thu, 10 Aug 2000 19:15:37 +0000 |
parents | 51d0f4273e89 |
children | b0d3d8c7fa88 |
files | src/xdisp.c |
diffstat | 1 files changed, 293 insertions(+), 186 deletions(-) [+] |
line wrap: on
line diff
--- a/src/xdisp.c Thu Aug 10 19:14:59 2000 +0000 +++ b/src/xdisp.c Thu Aug 10 19:15:37 2000 +0000 @@ -516,13 +516,25 @@ #define TEXT_PROP_DISTANCE_LIMIT 100 +#if GLYPH_DEBUG + /* Non-zero means print traces of redisplay if compiled with GLYPH_DEBUG != 0. */ -#if GLYPH_DEBUG int trace_redisplay_p; + +/* Non-zero means trace with TRACE_MOVE to stderr. */ + +int trace_move; + +#ifdef DEBUG_TRACE_MOVE +#define TRACE_MOVE(x) if (trace_move) fprintf x; else (void) 0 +#else +#define TRACE_MOVE(x) (void) 0; #endif +#endif /* GLYPH_DEBUG */ + /* Non-zero means automatically scroll windows horizontally to make point visible. */ @@ -655,6 +667,7 @@ static int append_space P_ ((struct it *, int)); static void make_cursor_line_fully_visible P_ ((struct window *)); static int try_scrolling P_ ((Lisp_Object, int, int, int, int)); +static int try_cursor_movement P_ ((Lisp_Object, struct text_pos, int *)); static int trailing_whitespace_p P_ ((int)); static int message_log_check_duplicate P_ ((int, int, int, int)); int invisible_p P_ ((Lisp_Object, Lisp_Object)); @@ -4265,7 +4278,7 @@ while (1) { - int x, i; + int x, i, ascent, descent; /* Stop when ZV or TO_CHARPOS reached. */ if (!get_next_display_element (it) @@ -4282,6 +4295,15 @@ x-position before this display element in case it does not fit on the line. */ x = it->current_x; + + /* Remember the line height so far in case the next element doesn't + fit on the line. */ + if (!it->truncate_lines_p) + { + ascent = it->max_ascent; + descent = it->max_descent; + } + PRODUCE_GLYPHS (it); if (it->area != TEXT_AREA) @@ -4347,8 +4369,14 @@ set_iterator_to_next (it); } else - it->current_x = x; - + { + it->current_x = x; + it->max_ascent = ascent; + it->max_descent = descent; + } + + TRACE_MOVE ((stderr, "move_it_in: continued at %d\n", + IT_CHARPOS (*it))); result = MOVE_LINE_CONTINUED; break; } @@ -4423,8 +4451,9 @@ { enum move_it_result skip, skip2 = MOVE_X_REACHED; int line_height; - - while (1) + int reached = 0; + + for (;;) { if (op & MOVE_TO_VPOS) { @@ -4433,31 +4462,46 @@ if ((op & (MOVE_TO_X | MOVE_TO_POS)) == 0) { if (it->vpos == to_vpos) - break; - skip = move_it_in_display_line_to (it, -1, -1, 0); + { + reached = 1; + break; + } + else + skip = move_it_in_display_line_to (it, -1, -1, 0); } else { /* TO_VPOS >= 0 means stop at TO_X in the line at TO_VPOS, or at TO_POS, whichever comes first. */ + if (it->vpos == to_vpos) + { + reached = 2; + break; + } + skip = move_it_in_display_line_to (it, to_charpos, to_x, op); if (skip == MOVE_POS_MATCH_OR_ZV || it->vpos == to_vpos) - break; + { + reached = 3; + break; + } else if (skip == MOVE_X_REACHED && it->vpos != to_vpos) { /* We have reached TO_X but not in the line we want. */ skip = move_it_in_display_line_to (it, to_charpos, -1, MOVE_TO_POS); if (skip == MOVE_POS_MATCH_OR_ZV) - break; + { + reached = 4; + break; + } } } } else if (op & MOVE_TO_Y) { struct it it_backup; - int done_p; /* TO_Y specified means stop at TO_X in the line containing TO_Y---or at TO_CHARPOS if this is reached first. The @@ -4479,22 +4523,27 @@ /* If TO_CHARPOS is reached or ZV, we don't have to do more. */ if (skip == MOVE_POS_MATCH_OR_ZV) - break; + { + reached = 5; + break; + } /* If TO_X was reached, we would like to know whether TO_Y is in the line. This can only be said if we know the total line height which requires us to scan the rest of the line. */ - done_p = 0; if (skip == MOVE_X_REACHED) { it_backup = *it; + TRACE_MOVE ((stderr, "move_it: from %d\n", IT_CHARPOS (*it))); skip2 = move_it_in_display_line_to (it, to_charpos, -1, op & MOVE_TO_POS); + TRACE_MOVE ((stderr, "move_it: to %d\n", IT_CHARPOS (*it))); } /* Now, decide whether TO_Y is in this line. */ line_height = it->max_ascent + it->max_descent; + TRACE_MOVE ((stderr, "move_it: line_height = %d\n", line_height)); if (to_y >= it->current_y && to_y < it->current_y + line_height) @@ -4504,16 +4553,16 @@ we scanned too far. We have to restore IT's settings to the ones before skipping. */ *it = it_backup; - done_p = 1; + reached = 6; } else if (skip == MOVE_X_REACHED) { skip = skip2; if (skip == MOVE_POS_MATCH_OR_ZV) - done_p = 1; - } - - if (done_p) + reached = 7; + } + + if (reached) break; } else @@ -4522,7 +4571,8 @@ switch (skip) { case MOVE_POS_MATCH_OR_ZV: - return; + reached = 8; + goto out; case MOVE_NEWLINE_OR_CR: set_iterator_to_next (it); @@ -4534,7 +4584,10 @@ reseat_at_next_visible_line_start (it, 0); if ((op & MOVE_TO_POS) != 0 && IT_CHARPOS (*it) > to_charpos) - goto out; + { + reached = 9; + goto out; + } break; case MOVE_LINE_CONTINUED: @@ -4554,7 +4607,10 @@ last_max_ascent = it->max_ascent; it->max_ascent = it->max_descent = 0; } - out:; + + out: + + TRACE_MOVE ((stderr, "move_it_to: reached %d\n", reached)); } @@ -4603,6 +4659,7 @@ MOVE_TO_POS | MOVE_TO_VPOS); xassert (IT_CHARPOS (*it) >= BEGV); line_height = it2.max_ascent + it2.max_descent; + move_it_to (&it2, start_pos, -1, -1, -1, MOVE_TO_POS); xassert (IT_CHARPOS (*it) >= BEGV); h = it2.current_y - it->current_y; @@ -4657,8 +4714,10 @@ move_it_vertically_backward (it, -dy); else if (dy > 0) { + TRACE_MOVE ((stderr, "move_it_v: from %d, %d\n", IT_CHARPOS (*it), dy)); move_it_to (it, ZV, -1, it->current_y + dy, -1, MOVE_TO_POS | MOVE_TO_Y); + TRACE_MOVE ((stderr, "move_it_v: to %d\n", IT_CHARPOS (*it))); /* If buffer ends in ZV without a newline, move to the start of the line to satisfy the post-condition. */ @@ -8511,7 +8570,7 @@ /* Point is in the scroll margin at the bottom of the window, or below. Compute a new window start that makes point visible. */ - + /* Compute the distance from the scroll margin to PT. Give up if the distance is greater than scroll_max. */ start_display (&it, w, scroll_margin_pos); @@ -8522,6 +8581,7 @@ ? it.max_ascent + it.max_descent : last_height); dy = it.current_y + line_height - y0; + if (dy > scroll_max) return 0; @@ -8706,6 +8766,209 @@ } +/* Try cursor movement in case text has not changes in window WINDOW, + with window start STARTP. Value is + + 1 if successful + + 0 if this method cannot be used + + -1 if we know we have to scroll the display. *SCROLL_STEP is + set to 1, under certain circumstances, if we want to scroll as + if scroll-step were set to 1. See the code. */ + +static int +try_cursor_movement (window, startp, scroll_step) + Lisp_Object window; + struct text_pos startp; + int *scroll_step; +{ + struct window *w = XWINDOW (window); + struct frame *f = XFRAME (w->frame); + int rc = 0; + + /* Handle case where text has not changed, only point, and it has + not moved off the frame. */ + if (/* Point may be in this window. */ + PT >= CHARPOS (startp) + /* If we don't check this, we are called to move the cursor in a + horizontally split window with a current matrix that doesn't + fit the display. */ + && !windows_or_buffers_changed + /* Selective display hasn't changed. */ + && !current_buffer->clip_changed + /* If force-mode-line-update was called, really redisplay; + that's how redisplay is forced after e.g. changing + buffer-invisibility-spec. */ + && NILP (w->update_mode_line) + /* Can't use this case if highlighting a region. When a + region exists, cursor movement has to do more than just + set the cursor. */ + && !(!NILP (Vtransient_mark_mode) + && !NILP (current_buffer->mark_active)) + && NILP (w->region_showing) + && NILP (Vshow_trailing_whitespace) + /* Right after splitting windows, last_point may be nil. */ + && INTEGERP (w->last_point) + /* This code is not used for mini-buffer for the sake of the case + of redisplaying to replace an echo area message; since in + that case the mini-buffer contents per se are usually + unchanged. This code is of no real use in the mini-buffer + since the handling of this_line_start_pos, etc., in redisplay + handles the same cases. */ + && !EQ (window, minibuf_window) + /* When splitting windows or for new windows, it happens that + redisplay is called with a nil window_end_vpos or one being + larger than the window. This should really be fixed in + window.c. I don't have this on my list, now, so we do + approximately the same as the old redisplay code. --gerd. */ + && INTEGERP (w->window_end_vpos) + && XFASTINT (w->window_end_vpos) < w->current_matrix->nrows + && (FRAME_WINDOW_P (f) + || !MARKERP (Voverlay_arrow_position) + || current_buffer != XMARKER (Voverlay_arrow_position)->buffer)) + { + int this_scroll_margin; + struct glyph_row *row; + +#if GLYPH_DEBUG + debug_method_add (w, "cursor movement"); +#endif + + /* Scroll if point within this distance from the top or bottom + of the window. This is a pixel value. */ + this_scroll_margin = max (0, scroll_margin); + this_scroll_margin = min (this_scroll_margin, XFASTINT (w->height) / 4); + this_scroll_margin *= CANON_Y_UNIT (f); + + /* Start with the row the cursor was displayed during the last + not paused redisplay. Give up if that row is not valid. */ + if (w->last_cursor.vpos >= w->current_matrix->nrows) + rc = -1; + else + { + row = MATRIX_ROW (w->current_matrix, w->last_cursor.vpos); + if (row->mode_line_p) + ++row; + if (!row->enabled_p) + rc = -1; + } + + if (rc == 0) + { + int scroll_p = 0; + + if (PT > XFASTINT (w->last_point)) + { + /* Point has moved forward. */ + int last_y = window_text_bottom_y (w) - this_scroll_margin; + + while (MATRIX_ROW_END_CHARPOS (row) < PT + && MATRIX_ROW_BOTTOM_Y (row) < last_y) + { + xassert (row->enabled_p); + ++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. Exceptions are when the + row ends in the middle of a character, or ends in + ZV. */ + if (MATRIX_ROW_BOTTOM_Y (row) < last_y + && MATRIX_ROW_END_CHARPOS (row) == PT + && !MATRIX_ROW_ENDS_IN_MIDDLE_OF_CHAR_P (row) + && !row->ends_at_zv_p) + { + xassert (row->enabled_p); + ++row; + } + + /* If within the scroll margin, scroll. Note that + MATRIX_ROW_BOTTOM_Y gives the pixel position at which + the next line would be drawn, and that + this_scroll_margin can be zero. */ + if (MATRIX_ROW_BOTTOM_Y (row) > last_y + || PT > MATRIX_ROW_END_CHARPOS (row) + /* Line is completely visible last line in window + and PT is to be set in the next line. */ + || (MATRIX_ROW_BOTTOM_Y (row) == last_y + && PT == MATRIX_ROW_END_CHARPOS (row) + && !row->ends_at_zv_p + && !MATRIX_ROW_ENDS_IN_MIDDLE_OF_CHAR_P (row))) + scroll_p = 1; + } + else if (PT < XFASTINT (w->last_point)) + { + /* Cursor has to be moved backward. Note that PT >= + CHARPOS (startp) because of the outer + if-statement. */ + while (!row->mode_line_p + && (MATRIX_ROW_START_CHARPOS (row) > PT + || (MATRIX_ROW_START_CHARPOS (row) == PT + && MATRIX_ROW_STARTS_IN_MIDDLE_OF_CHAR_P (row))) + && (row->y > this_scroll_margin + || CHARPOS (startp) == BEGV)) + { + xassert (row->enabled_p); + --row; + } + + /* Consider the following case: Window starts at BEGV, + there is invisible, intangible text at BEGV, so that + display starts at some point START > BEGV. It can + happen that we are called with PT somewhere between + BEGV and START. Try to handle that case. */ + if (row < w->current_matrix->rows + || row->mode_line_p) + { + row = w->current_matrix->rows; + if (row->mode_line_p) + ++row; + } + + /* Due to newlines in overlay strings, we may have to + skip forward over overlay strings. */ + while (MATRIX_ROW_END_CHARPOS (row) == PT + && MATRIX_ROW_ENDS_IN_OVERLAY_STRING_P (row) + && !row->ends_at_zv_p) + ++row; + + /* If within the scroll margin, scroll. */ + if (row->y < this_scroll_margin + && CHARPOS (startp) != BEGV) + scroll_p = 1; + } + + if (PT < MATRIX_ROW_START_CHARPOS (row) + || PT > MATRIX_ROW_END_CHARPOS (row)) + { + /* if PT is not in the glyph row, give up. */ + rc = -1; + } + else if (MATRIX_ROW_PARTIALLY_VISIBLE_P (row) + && window_box_height (w) > row->height) + { + /* If we end up in a partially visible line, let's make it + fully visible, except when it's taller than the window, + in which case we can't do much about it. */ + *scroll_step = 1; + rc = -1; + } + else if (scroll_p) + rc = -1; + else + { + set_cursor_from_row (w, row, w->current_matrix, 0, 0, 0, 0); + rc = 1; + } + } + } + + return rc; +} + + /* Redisplay leaf window WINDOW. JUST_THIS_ONE_P non-zero means only selected_window is redisplayed. */ @@ -8726,6 +8989,7 @@ int current_matrix_up_to_date_p = 0; int temp_scroll_step = 0; int count = specpdl_ptr - specpdl; + int rc; SET_TEXT_POS (lpoint, PT, PT_BYTE); opoint = lpoint; @@ -8886,7 +9150,6 @@ && CHARPOS (startp) <= ZV) { w->optional_new_start = Qnil; - /* This takes a mini-buffer prompt into account. */ start_display (&it, w, startp); move_it_to (&it, PT, 0, it.last_visible_y, -1, MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y); @@ -8983,170 +9246,14 @@ /* Handle case where text has not changed, only point, and it has not moved off the frame. */ if (current_matrix_up_to_date_p - /* Point may be in this window. */ - && PT >= CHARPOS (startp) - /* If we don't check this, we are called to move the cursor in a - horizontally split window with a current matrix that doesn't - fit the display. */ - && !windows_or_buffers_changed - /* Selective display hasn't changed. */ - && !current_buffer->clip_changed - /* If force-mode-line-update was called, really redisplay; - that's how redisplay is forced after e.g. changing - buffer-invisibility-spec. */ - && NILP (w->update_mode_line) - /* Can't use this case if highlighting a region. When a - region exists, cursor movement has to do more than just - set the cursor. */ - && !(!NILP (Vtransient_mark_mode) - && !NILP (current_buffer->mark_active)) - && NILP (w->region_showing) - && NILP (Vshow_trailing_whitespace) - /* Right after splitting windows, last_point may be nil. */ - && INTEGERP (w->last_point) - /* This code is not used for mini-buffer for the sake of the case - of redisplaying to replace an echo area message; since in - that case the mini-buffer contents per se are usually - unchanged. This code is of no real use in the mini-buffer - since the handling of this_line_start_pos, etc., in redisplay - handles the same cases. */ - && !EQ (window, minibuf_window) - /* When splitting windows or for new windows, it happens that - redisplay is called with a nil window_end_vpos or one being - larger than the window. This should really be fixed in - window.c. I don't have this on my list, now, so we do - approximately the same as the old redisplay code. --gerd. */ - && INTEGERP (w->window_end_vpos) - && XFASTINT (w->window_end_vpos) < w->current_matrix->nrows - && (FRAME_WINDOW_P (f) - || !MARKERP (Voverlay_arrow_position) - || current_buffer != XMARKER (Voverlay_arrow_position)->buffer)) - { - int this_scroll_margin; - struct glyph_row *row; - int scroll_p; - -#if GLYPH_DEBUG - debug_method_add (w, "cursor movement"); -#endif - - /* Scroll if point within this distance from the top or bottom - of the window. This is a pixel value. */ - this_scroll_margin = max (0, scroll_margin); - this_scroll_margin = min (this_scroll_margin, XFASTINT (w->height) / 4); - this_scroll_margin *= CANON_Y_UNIT (f); - - /* Start with the row the cursor was displayed during the last - not paused redisplay. Give up if that row is not valid. */ - if (w->last_cursor.vpos >= w->current_matrix->nrows) - goto try_to_scroll; - row = MATRIX_ROW (w->current_matrix, w->last_cursor.vpos); - if (row->mode_line_p) - ++row; - if (!row->enabled_p) + && (rc = try_cursor_movement (window, startp, &temp_scroll_step), + rc != 0)) + { + if (rc == -1) goto try_to_scroll; - - scroll_p = 0; - if (PT > XFASTINT (w->last_point)) - { - /* Point has moved forward. */ - int last_y = window_text_bottom_y (w) - this_scroll_margin; - - while (MATRIX_ROW_END_CHARPOS (row) < PT - && MATRIX_ROW_BOTTOM_Y (row) < last_y) - { - xassert (row->enabled_p); - ++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. Exceptions are when the row ends in - the middle of a character, or ends in ZV. */ - if (MATRIX_ROW_BOTTOM_Y (row) < last_y - && MATRIX_ROW_END_CHARPOS (row) == PT - && !MATRIX_ROW_ENDS_IN_MIDDLE_OF_CHAR_P (row) - && !row->ends_at_zv_p) - { - xassert (row->enabled_p); - ++row; - } - - /* If within the scroll margin, scroll. Note that - MATRIX_ROW_BOTTOM_Y gives the pixel position at which the - next line would be drawn, and that this_scroll_margin can - be zero. */ - if (MATRIX_ROW_BOTTOM_Y (row) > last_y - || PT > MATRIX_ROW_END_CHARPOS (row) - /* Line is completely visible last line in window and PT - is to be set in the next line. */ - || (MATRIX_ROW_BOTTOM_Y (row) == last_y - && PT == MATRIX_ROW_END_CHARPOS (row) - && !row->ends_at_zv_p - && !MATRIX_ROW_ENDS_IN_MIDDLE_OF_CHAR_P (row))) - scroll_p = 1; - } - else if (PT < XFASTINT (w->last_point)) - { - /* Cursor has to be moved backward. Note that PT >= - CHARPOS (startp) because of the outer if-statement. */ - while (!row->mode_line_p - && (MATRIX_ROW_START_CHARPOS (row) > PT - || (MATRIX_ROW_START_CHARPOS (row) == PT - && MATRIX_ROW_STARTS_IN_MIDDLE_OF_CHAR_P (row))) - && (row->y > this_scroll_margin - || CHARPOS (startp) == BEGV)) - { - xassert (row->enabled_p); - --row; - } - - /* Consider the following case: Window starts at BEGV, there - is invisible, intangible text at BEGV, so that display - starts at some point START > BEGV. It can happen that - we are called with PT somewhere between BEGV and START. - Try to handle that case. */ - if (row < w->current_matrix->rows - || row->mode_line_p) - { - row = w->current_matrix->rows; - if (row->mode_line_p) - ++row; - } - - /* Due to newlines in overlay strings, we may have to skip - forward over overlay strings. */ - while (MATRIX_ROW_END_CHARPOS (row) == PT - && MATRIX_ROW_ENDS_IN_OVERLAY_STRING_P (row) - && !row->ends_at_zv_p) - ++row; - - /* If within the scroll margin, scroll. */ - if (row->y < this_scroll_margin - && CHARPOS (startp) != BEGV) - scroll_p = 1; - } - - /* if PT is not in the glyph row, give up. */ - if (PT < MATRIX_ROW_START_CHARPOS (row) - || PT > MATRIX_ROW_END_CHARPOS (row)) - goto try_to_scroll; - - /* If we end up in a partially visible line, let's make it fully - visible. This can be done most easily by using the existing - scrolling code. */ - if (MATRIX_ROW_PARTIALLY_VISIBLE_P (row)) - { - temp_scroll_step = 1; - goto try_to_scroll; - } - else if (scroll_p) - goto try_to_scroll; - - set_cursor_from_row (w, row, w->current_matrix, 0, 0, 0, 0); - goto done; - } - + else + goto done; + } /* If current starting point was originally the beginning of a line but no longer is, find a new starting point. */ else if (!NILP (w->start_at_line_beg)