This function attempts to redisplay a window by reusing parts of
its existing display. It finds and reuses the part that was not
- changed, and redraws the rest.
+ changed, and redraws the rest. (The "id" part in the function's
+ name stands for "insert/delete", not for "identification" or
+ somesuch.)
. try_window
optimizations were successful, redisplay calls redisplay_windows,
which performs a full redisplay of all windows.
+ Note that there's one more important optimization up Emacs's
+ sleeve, but it is related to actually redrawing the potentially
+ changed portions of the window/frame, not to reproducing the
+ desired matrices of those potentially changed portions. Namely,
+ the function update_frame and its subroutines, which you will find
+ in dispnew.c, compare the desired matrices with the current
+ matrices, and only redraw the portions that changed. So it could
+ happen that the functions in this file for some reason decide that
+ the entire desired matrix needs to be regenerated from scratch, and
+ still only parts of the Emacs display, or even nothing at all, will
+ be actually delivered to the glass, because update_frame has found
+ that the new and the old screen contents are similar or identical.
+
Desired matrices.
Desired matrices are always built per Emacs window. The function
struct it it;
struct text_pos pt;
struct window *w = XWINDOW (selected_window);
+ struct buffer *old_buffer = NULL;
+ Lisp_Object result;
+ if (XBUFFER (w->contents) != current_buffer)
+ {
+ old_buffer = current_buffer;
+ set_buffer_internal_1 (XBUFFER (w->contents));
+ }
SET_TEXT_POS (pt, PT, PT_BYTE);
start_display (&it, w, pt);
it.vpos = it.current_y = 0;
last_height = 0;
- return make_number (line_bottom_y (&it));
+ result = make_number (line_bottom_y (&it));
+ if (old_buffer)
+ set_buffer_internal_1 (old_buffer);
+
+ return result;
}
/* Return the default pixel height of text lines in window W. The
following. Return the result, or nil if something went
wrong. Prevent redisplay during the evaluation. */
-Lisp_Object
-safe_call (ptrdiff_t nargs, Lisp_Object func, ...)
+static Lisp_Object
+safe__call (bool inhibit_quit, ptrdiff_t nargs, Lisp_Object func, va_list ap)
{
Lisp_Object val;
val = Qnil;
else
{
- va_list ap;
ptrdiff_t i;
ptrdiff_t count = SPECPDL_INDEX ();
struct gcpro gcpro1;
Lisp_Object *args = alloca (nargs * word_size);
args[0] = func;
- va_start (ap, func);
for (i = 1; i < nargs; i++)
args[i] = va_arg (ap, Lisp_Object);
- va_end (ap);
GCPRO1 (args[0]);
gcpro1.nvars = nargs;
specbind (Qinhibit_redisplay, Qt);
+ if (inhibit_quit)
+ specbind (Qinhibit_quit, Qt);
/* Use Qt to ensure debugger does not run,
so there is no possibility of wanting to redisplay. */
val = internal_condition_case_n (Ffuncall, nargs, args, Qt,
return val;
}
+Lisp_Object
+safe_call (ptrdiff_t nargs, Lisp_Object func, ...)
+{
+ Lisp_Object retval;
+ va_list ap;
+
+ va_start (ap, func);
+ retval = safe__call (false, nargs, func, ap);
+ va_end (ap);
+ return retval;
+}
/* Call function FN with one argument ARG.
Return the result, or nil if something went wrong. */
return safe_call (2, fn, arg);
}
+static Lisp_Object
+safe__call1 (bool inhibit_quit, Lisp_Object fn, ...)
+{
+ Lisp_Object retval;
+ va_list ap;
+
+ va_start (ap, fn);
+ retval = safe__call (inhibit_quit, 2, fn, ap);
+ va_end (ap);
+ return retval;
+}
+
static Lisp_Object Qeval;
Lisp_Object
safe_eval (Lisp_Object sexpr)
{
- return safe_call1 (Qeval, sexpr);
+ return safe__call1 (false, Qeval, sexpr);
+}
+
+static Lisp_Object
+safe__eval (bool inhibit_quit, Lisp_Object sexpr)
+{
+ return safe__call1 (inhibit_quit, Qeval, sexpr);
}
/* Call function FN with two arguments ARG1 and ARG2.
}
else
{
- if (it->line_wrap == WORD_WRAP)
+ if (it->line_wrap == WORD_WRAP && it->area == TEXT_AREA)
{
if (IT_DISPLAYING_WHITESPACE (it))
may_wrap = 1;
doesn't fit on the line, e.g. a wide image. */
it->hpos == 0
|| (new_x == it->last_visible_x
- && FRAME_WINDOW_P (it->f)))
+ && FRAME_WINDOW_P (it->f)
+ /* When word-wrap is ON and we have a valid
+ wrap point, we don't allow the last glyph
+ to "just barely fit" on the line. */
+ && (it->line_wrap != WORD_WRAP
+ || wrap_it.sp < 0)))
{
++it->hpos;
it->current_x = new_x;
if (closest_pos < ZV)
{
RESTORE_IT (it, &ppos_it, ppos_data);
- move_it_in_display_line_to (it, closest_pos, -1,
- MOVE_TO_POS);
+ /* Don't recurse if closest_pos is equal to
+ to_charpos, since we have just tried that. */
+ if (closest_pos != to_charpos)
+ move_it_in_display_line_to (it, closest_pos, -1,
+ MOVE_TO_POS);
result = MOVE_POS_MATCH_OR_ZV;
}
else
&& !at_eob_p && closest_pos < ZV)
{
RESTORE_IT (it, &ppos_it, ppos_data);
- move_it_in_display_line_to (it, closest_pos, -1,
- MOVE_TO_POS);
+ if (closest_pos != to_charpos)
+ move_it_in_display_line_to (it, closest_pos, -1,
+ MOVE_TO_POS);
}
result = MOVE_POS_MATCH_OR_ZV;
break;
if (closest_pos < ZV)
{
RESTORE_IT (it, &ppos_it, ppos_data);
- move_it_in_display_line_to (it, closest_pos, -1, MOVE_TO_POS);
+ if (closest_pos != to_charpos)
+ move_it_in_display_line_to (it, closest_pos, -1,
+ MOVE_TO_POS);
}
result = MOVE_POS_MATCH_OR_ZV;
break;
ptrdiff_t start_charpos, i;
int nchars_per_row
= (it->last_visible_x - it->first_visible_x) / FRAME_COLUMN_WIDTH (it->f);
+ bool hit_pos_limit = false;
ptrdiff_t pos_limit;
/* Start at the beginning of the screen line containing IT's
pos_limit = BEGV;
else
pos_limit = max (start_charpos + dvpos * nchars_per_row, BEGV);
+
for (i = -dvpos; i > 0 && IT_CHARPOS (*it) > pos_limit; --i)
back_to_previous_visible_line_start (it);
+ if (i > 0 && IT_CHARPOS (*it) <= pos_limit)
+ hit_pos_limit = true;
reseat (it, it->current.pos, 1);
/* Move further back if we end up in a string or an image. */
else
bidi_unshelve_cache (it2data, 1);
}
+ else if (hit_pos_limit && pos_limit > BEGV
+ && dvpos < 0 && it2.vpos < -dvpos)
+ {
+ /* If we hit the limit, but still didn't make it far enough
+ back, that means there's a display string with a newline
+ covering a large chunk of text, and that caused
+ back_to_previous_visible_line_start try to go too far.
+ Punish those who commit such atrocities by going back
+ until we've reached DVPOS, after lifting the limit, which
+ could make it slow for very long lines. "If it hurts,
+ don't do that!" */
+ dvpos += it2.vpos;
+ RESTORE_IT (it, it, it2data);
+ for (i = -dvpos; i > 0; --i)
+ {
+ back_to_previous_visible_line_start (it);
+ it->vpos--;
+ }
+ }
else
RESTORE_IT (it, it, it2data);
}
defaults to the maximum accessible position of the buffer. If TO is t,
use the maximum accessible position that is not a newline character.
-The optional argument X_LIMIT, if non-nil, specifies the maximum text
-width that can be returned. X_LIMIT nil or omitted, means to use the
+The optional argument X-LIMIT, if non-nil, specifies the maximum text
+width that can be returned. X-LIMIT nil or omitted, means to use the
pixel-width of WINDOW's body; use this if you do not intend to change
the width of WINDOW. Use the maximum width WINDOW may assume if you
-intend to change WINDOW's width.
+intend to change WINDOW's width. In any case, text whose x-coordinate
+is beyond X-LIMIT is ignored. Since calculating the width of long lines
+can take some time, it's always a good idea to make this argument as
+small as possible; in particular, if the buffer contains long lines that
+shall be truncated anyway.
-The optional argument Y_LIMIT, if non-nil, specifies the maximum text
+The optional argument Y-LIMIT, if non-nil, specifies the maximum text
height that can be returned. Text lines whose y-coordinate is beyond
-Y_LIMIT are ignored. Since calculating the text height of a large
+Y-LIMIT are ignored. Since calculating the text height of a large
buffer can take some time, it makes sense to specify this argument if
the size of the buffer is unknown.
-Optional argument MODE_AND_HEADER_LINE nil or omitted means do not
+Optional argument MODE-AND-HEADER-LINE nil or omitted means do not
include the height of the mode- or header-line of WINDOW in the return
value. If it is either the symbol `mode-line' or `header-line', include
only the height of that line, if present, in the return value. If t,
}
}
}
- safe_call1 (Vpre_redisplay_function, windows);
+ safe__call1 (true, Vpre_redisplay_function, windows);
}
/* Update all frame titles based on their buffer names, etc. We do
pos_after, 0);
if (prop_pos >= pos_before)
- bpos_max = prop_pos - 1;
+ bpos_max = prop_pos;
}
if (INTEGERP (chprop))
{
pos_after, 0);
if (prop_pos >= pos_before)
- bpos_max = prop_pos - 1;
+ bpos_max = prop_pos;
}
if (INTEGERP (chprop))
{
GLYPH_BEFORE and GLYPH_AFTER. */
if (!((row->reversed_p ? glyph > glyphs_end : glyph < glyphs_end)
&& BUFFERP (glyph->object) && glyph->charpos == pt_old)
- && !(bpos_max < pt_old && pt_old <= bpos_covered))
+ && !(bpos_max <= pt_old && pt_old <= bpos_covered))
{
/* An empty line has a single glyph whose OBJECT is zero and
whose CHARPOS is the position of a newline on that line.
selected_window is redisplayed.
We can return without actually redisplaying the window if fonts has been
- changed on window's frame. In that case, redisplay_internal will retry. */
+ changed on window's frame. In that case, redisplay_internal will retry.
+
+ As one of the important parts of redisplaying a window, we need to
+ decide whether the previous window-start position (stored in the
+ window's w->start marker position) is still valid, and if it isn't,
+ recompute it. Some details about that:
+
+ . The previous window-start could be in a continuation line, in
+ which case we need to recompute it when the window width
+ changes. See compute_window_start_on_continuation_line and its
+ call below.
+
+ . The text that changed since last redisplay could include the
+ previous window-start position. In that case, we try to salvage
+ what we can from the current glyph matrix by calling
+ try_scrolling, which see.
+
+ . Some Emacs command could force us to use a specific window-start
+ position by setting the window's force_start flag, or gently
+ propose doing that by setting the window's optional_new_start
+ flag. In these cases, we try using the specified start point if
+ that succeeds (i.e. the window desired matrix is successfully
+ recomputed, and point location is within the window). In case
+ of optional_new_start, we first check if the specified start
+ position is feasible, i.e. if it will allow point to be
+ displayed in the window. If using the specified start point
+ fails, e.g., if new fonts are needed to be loaded, we abort the
+ redisplay cycle and leave it up to the next cycle to figure out
+ things.
+
+ . Note that the window's force_start flag is sometimes set by
+ redisplay itself, when it decides that the previous window start
+ point is fine and should be kept. Search for "goto force_start"
+ below to see the details. Like the values of window-start
+ specified outside of redisplay, these internally-deduced values
+ are tested for feasibility, and ignored if found to be
+ unfeasible.
+
+ . Note that the function try_window, used to completely redisplay
+ a window, accepts the window's start point as its argument.
+ This is used several times in the redisplay code to control
+ where the window start will be, according to user options such
+ as scroll-conservatively, and also to ensure the screen line
+ showing point will be fully (as opposed to partially) visible on
+ display. */
static void
redisplay_window (Lisp_Object window, bool just_this_one_p)
eassert (XMARKER (w->start)->buffer == buffer);
eassert (XMARKER (w->pointm)->buffer == buffer);
+ /* We come here again if we need to run window-text-change-functions
+ below. */
restart:
reconsider_clip_changes (w);
frame_line_height = default_line_pixel_height (w);
&& !current_buffer->prevent_redisplay_optimizations_p
&& !window_outdated (w));
- /* Run the window-bottom-change-functions
+ /* Run the window-text-change-functions
if it is possible that the text on the screen has changed
(either due to modification of the text, or any other reason). */
if (!current_matrix_up_to_date_p
/* 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. */
+ PT somewhere between BEGV and START. Try to handle that case,
+ and similar ones. */
if (w->cursor.vpos < 0)
{
- struct glyph_row *row = w->current_matrix->rows;
- if (row->mode_line_p)
- ++row;
+ /* First, try locating the proper glyph row for PT. */
+ struct glyph_row *row =
+ row_containing_pos (w, PT, w->current_matrix->rows, NULL, 0);
+
+ /* Sometimes point is at the beginning of invisible text that is
+ before the 1st character displayed in the row. In that case,
+ row_containing_pos fails to find the row, because no glyphs
+ with appropriate buffer positions are present in the row.
+ Therefore, we next try to find the row which shows the 1st
+ position after the invisible text. */
+ if (!row)
+ {
+ Lisp_Object val =
+ get_char_property_and_overlay (make_number (PT), Qinvisible,
+ Qnil, NULL);
+
+ if (TEXT_PROP_MEANS_INVISIBLE (val))
+ {
+ ptrdiff_t alt_pos;
+ Lisp_Object invis_end =
+ Fnext_single_char_property_change (make_number (PT), Qinvisible,
+ Qnil, Qnil);
+
+ if (NATNUMP (invis_end))
+ alt_pos = XFASTINT (invis_end);
+ else
+ alt_pos = ZV;
+ row = row_containing_pos (w, alt_pos, w->current_matrix->rows,
+ NULL, 0);
+ }
+ }
+ /* Finally, fall back on the first row of the window after the
+ header line (if any). This is slightly better than not
+ displaying the cursor at all. */
+ if (!row)
+ {
+ row = w->current_matrix->rows;
+ if (row->mode_line_p)
+ ++row;
+ }
set_cursor_from_row (w, row, w->current_matrix, 0, 0, 0, 0);
}
truncate_it.current_x = 0;
truncate_it.face_id = DEFAULT_FACE_ID;
truncate_it.glyph_row = &scratch_glyph_row;
+ truncate_it.area = TEXT_AREA;
truncate_it.glyph_row->used[TEXT_AREA] = 0;
CHARPOS (truncate_it.position) = BYTEPOS (truncate_it.position) = -1;
truncate_it.object = make_number (0);
&& !b->clip_changed
&& !b->prevent_redisplay_optimizations_p
&& !window_outdated (w)
+ /* We rely below on the cursor coordinates to be up to date, but
+ we cannot trust them if some command moved point since the
+ last complete redisplay. */
+ && w->last_point == BUF_PT (b)
&& w->cursor.vpos >= 0
&& w->cursor.vpos < w->current_matrix->nrows
&& (row = MATRIX_ROW (w->current_matrix, w->cursor.vpos))->enabled_p)
move_it_by_lines (&it, -1);
target_x = it.last_visible_x - !FRAME_WINDOW_P (it.f);
target_is_eol_p = true;
+ /* Under word-wrap, we don't know the x coordinate of
+ the last character displayed on the previous line,
+ which immediately precedes the wrap point. To find
+ out its x coordinate, we try moving to the right
+ margin of the window, which will stop at the wrap
+ point, and then reset target_x to point at the
+ character that precedes the wrap point. This is not
+ needed on GUI frames, because (see below) there we
+ move from the left margin one grapheme cluster at a
+ time, and stop when we hit the wrap point. */
+ if (!FRAME_WINDOW_P (it.f) && it.line_wrap == WORD_WRAP)
+ {
+ void *it_data = NULL;
+ struct it it2;
+
+ SAVE_IT (it2, it, it_data);
+ move_it_in_display_line_to (&it, ZV, target_x,
+ MOVE_TO_POS | MOVE_TO_X);
+ /* If we arrived at target_x, that _is_ the last
+ character on the previous line. */
+ if (it.current_x != target_x)
+ target_x = it.current_x - 1;
+ RESTORE_IT (&it, &it2, it_data);
+ }
}
}
else
if (CONSP (XCDR (elt)))
{
Lisp_Object spec;
- spec = safe_eval (XCAR (XCDR (elt)));
+ spec = safe__eval (true, XCAR (XCDR (elt)));
n += display_mode_element (it, depth, field_width - n,
precision - n, spec, props,
risky);
if (/* If window is in the process of being destroyed, don't bother
to do anything. */
w->current_matrix != NULL
- /* Don't update mouse highlight if hidden */
+ /* Don't update mouse highlight if hidden. */
&& (draw != DRAW_MOUSE_FACE || !hlinfo->mouse_face_hidden)
/* Recognize when we are called to operate on rows that don't exist
anymore. This can happen when a window is split. */