Merge branch 'abort-redisplay'
This allows abandoning the redisplay of a window that takes too long to complete. Bug#45898 * src/xdisp.c (update_redisplay_ticks): New function. (init_iterator, set_iterator_to_next): Call 'update_redisplay_ticks'. (syms_of_xdisp) <max_redisplay_ticks>: New variable. <list_of_error>: Remove 'void-variable': it is no longer needed, since 'calc_pixel_width_or_height' can no longer signal a void-variable error, and it gets in the way of aborting redisplay via 'redisplay_window_error'. * src/keyboard.c (command_loop_1): Reinitialize the tick count before executing each command in the loop. * src/syntax.c (scan_sexps_forward): Call 'update_redisplay_ticks' after finishing the loop. * src/dispnew.c (make_current): Make sure enabled rows of the current matrix have a valid hash, even if redisplay of a window was aborted due to slowness. This avoids assertion violations in 'scrolling_window' due to the wrong hash value. * src/xdisp.c (display_working_on_window_p): New global variable. (unwind_display_working_on_window): New function. * src/keyboard.c (command_loop_1): Reset 'display_working_on_window_p' before and after executing commands. * src/window.c (Frecenter, window_scroll, displayed_window_lines): * src/indent.c (Fvertical_motion): Set 'display_working_on_window_p' before calling 'start_display'. * src/syntax.c (scan_sexps_forward): Call 'update_redisplay_ticks' after finishing the loop. * src/regex-emacs.c (re_match_2_internal): * src/bidi.c (bidi_find_bracket_pairs, bidi_fetch_char) (bidi_paragraph_init, bidi_find_other_level_edge): Update the redisplay tick count as appropriate, when moving the iterator by one character position actually requires to examine many more positions. * src/xdisp.c (redisplay_window_error): Show messages about aborted redisplay of a window as delayed-warnings. * doc/emacs/trouble.texi (DEL Does Not Delete): Move to the end of the chapter. This issue is no longer frequent or important as it was back in Emacs 20 days. (Long Lines): Document 'max-redisplay-ticks'. * doc/emacs/emacs.texi (Top): Update the detailed menu. * etc/NEWS: Announce 'max-redisplay-ticks'.
This commit is contained in:
commit
289b457cac
12 changed files with 331 additions and 67 deletions
|
|
@ -1183,7 +1183,6 @@ The Emacs Initialization File
|
|||
|
||||
Dealing with Emacs Trouble
|
||||
|
||||
* DEL Does Not Delete:: What to do if @key{DEL} doesn't delete.
|
||||
* Stuck Recursive:: '[...]' in mode line around the parentheses.
|
||||
* Screen Garbled:: Garbage on the screen.
|
||||
* Text Garbled:: Garbage in the text.
|
||||
|
|
@ -1192,6 +1191,7 @@ Dealing with Emacs Trouble
|
|||
* After a Crash:: Recovering editing in an Emacs session that crashed.
|
||||
* Emergency Escape:: What to do if Emacs stops responding.
|
||||
* Long Lines:: Mitigating slowness due to extremely long lines.
|
||||
* DEL Does Not Delete:: What to do if @key{DEL} doesn't delete.
|
||||
|
||||
Reporting Bugs
|
||||
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ garbled displays, running out of memory, and crashes and hangs.
|
|||
Emacs.
|
||||
|
||||
@menu
|
||||
* DEL Does Not Delete:: What to do if @key{DEL} doesn't delete.
|
||||
* Stuck Recursive:: '[...]' in mode line around the parentheses.
|
||||
* Screen Garbled:: Garbage on the screen.
|
||||
* Text Garbled:: Garbage in the text.
|
||||
|
|
@ -160,65 +159,9 @@ Emacs.
|
|||
* After a Crash:: Recovering editing in an Emacs session that crashed.
|
||||
* Emergency Escape:: What to do if Emacs stops responding.
|
||||
* Long Lines:: Mitigating slowness due to extremely long lines.
|
||||
* DEL Does Not Delete:: What to do if @key{DEL} doesn't delete.
|
||||
@end menu
|
||||
|
||||
@node DEL Does Not Delete
|
||||
@subsection If @key{DEL} Fails to Delete
|
||||
@cindex @key{DEL} vs @key{BACKSPACE}
|
||||
@cindex @key{BACKSPACE} vs @key{DEL}
|
||||
@cindex @key{DEL} does not delete
|
||||
|
||||
Every keyboard has a large key, usually labeled @key{BACKSPACE},
|
||||
which is ordinarily used to erase the last character that you typed.
|
||||
In Emacs, this key is supposed to be equivalent to @key{DEL}.
|
||||
|
||||
When Emacs starts up on a graphical display, it determines
|
||||
automatically which key should be @key{DEL}. In some unusual cases,
|
||||
Emacs gets the wrong information from the system, and @key{BACKSPACE}
|
||||
ends up deleting forwards instead of backwards.
|
||||
|
||||
Some keyboards also have a @key{Delete} key, which is ordinarily
|
||||
used to delete forwards. If this key deletes backward in Emacs, that
|
||||
too suggests Emacs got the wrong information---but in the opposite
|
||||
sense.
|
||||
|
||||
On a text terminal, if you find that @key{BACKSPACE} prompts for a
|
||||
Help command, like @kbd{Control-h}, instead of deleting a character,
|
||||
it means that key is actually sending the @samp{BS} character. Emacs
|
||||
ought to be treating @key{BS} as @key{DEL}, but it isn't.
|
||||
|
||||
@findex normal-erase-is-backspace-mode
|
||||
In all of those cases, the immediate remedy is the same: use the
|
||||
command @kbd{M-x normal-erase-is-backspace-mode}. This toggles
|
||||
between the two modes that Emacs supports for handling @key{DEL}, so
|
||||
if Emacs starts in the wrong mode, this should switch to the right
|
||||
mode. On a text terminal, if you want to ask for help when @key{BS}
|
||||
is treated as @key{DEL}, use @key{F1} instead of @kbd{C-h}; @kbd{C-?}
|
||||
may also work, if it sends character code 127.
|
||||
|
||||
To fix the problem in every Emacs session, put one of the following
|
||||
lines into your initialization file (@pxref{Init File}). For the
|
||||
first case above, where @key{BACKSPACE} deletes forwards instead of
|
||||
backwards, use this line to make @key{BACKSPACE} act as @key{DEL}:
|
||||
|
||||
@lisp
|
||||
(normal-erase-is-backspace-mode 0)
|
||||
@end lisp
|
||||
|
||||
@noindent
|
||||
For the other two cases, use this line:
|
||||
|
||||
@lisp
|
||||
(normal-erase-is-backspace-mode 1)
|
||||
@end lisp
|
||||
|
||||
@vindex normal-erase-is-backspace
|
||||
Another way to fix the problem for every Emacs session is to
|
||||
customize the variable @code{normal-erase-is-backspace}: the value
|
||||
@code{t} specifies the mode where @key{BS} or @key{BACKSPACE} is
|
||||
@key{DEL}, and @code{nil} specifies the other mode. @xref{Easy
|
||||
Customization}.
|
||||
|
||||
@node Stuck Recursive
|
||||
@subsection Recursive Editing Levels
|
||||
@cindex stuck in recursive editing
|
||||
|
|
@ -525,6 +468,86 @@ be substantial.
|
|||
Use @kbd{M-x so-long-commentary} to view the documentation for this
|
||||
library and learn more about how to enable and configure it.
|
||||
|
||||
@vindex max-redisplay-ticks
|
||||
If even @code{so-long-mode} doesn't help making Emacs responsive
|
||||
enough, or if you'd rather not disable the display-related features
|
||||
that @code{so-long-mode} turns off, you can instead customize the
|
||||
variable @code{max-redisplay-ticks} to a non-zero value. Then Emacs
|
||||
will abort redisplay of a window and commands, like @kbd{C-n} and
|
||||
@kbd{M-v}, which use the display code to do their job, if processing a
|
||||
window needs more low-level display operations than the value of this
|
||||
variable. The display of the offending window will then remain
|
||||
outdated, and possibly incomplete, on the screen, but Emacs should
|
||||
otherwise be responsive, and you could then switch to another buffer,
|
||||
or kill the problematic buffer, or turn on @code{so-long-mode} or
|
||||
@code{sol-long-minor-mode} in that buffer. When the display of a
|
||||
window is aborted due to this reason, the buffer shown in that window
|
||||
will not have any of its windows redisplayed until the buffer is
|
||||
modified or until you type @kbd{C-l} (@pxref{Recentering}) in one of
|
||||
that buffer's windows.
|
||||
|
||||
If you decide to customize this variable to a non-zero value, we
|
||||
recommend to use a value between 100,000 and 1,000,000, depending on
|
||||
your patience and the speed of your system. The default value is
|
||||
zero, which disables this feature.
|
||||
|
||||
@node DEL Does Not Delete
|
||||
@subsection If @key{DEL} Fails to Delete
|
||||
@cindex @key{DEL} vs @key{BACKSPACE}
|
||||
@cindex @key{BACKSPACE} vs @key{DEL}
|
||||
@cindex @key{DEL} does not delete
|
||||
|
||||
Every keyboard has a large key, usually labeled @key{BACKSPACE},
|
||||
which is ordinarily used to erase the last character that you typed.
|
||||
In Emacs, this key is supposed to be equivalent to @key{DEL}.
|
||||
|
||||
When Emacs starts up on a graphical display, it determines
|
||||
automatically which key should be @key{DEL}. In some unusual cases,
|
||||
Emacs gets the wrong information from the system, and @key{BACKSPACE}
|
||||
ends up deleting forwards instead of backwards.
|
||||
|
||||
Some keyboards also have a @key{Delete} key, which is ordinarily
|
||||
used to delete forwards. If this key deletes backward in Emacs, that
|
||||
too suggests Emacs got the wrong information---but in the opposite
|
||||
sense.
|
||||
|
||||
On a text terminal, if you find that @key{BACKSPACE} prompts for a
|
||||
Help command, like @kbd{Control-h}, instead of deleting a character,
|
||||
it means that key is actually sending the @samp{BS} character. Emacs
|
||||
ought to be treating @key{BS} as @key{DEL}, but it isn't.
|
||||
|
||||
@findex normal-erase-is-backspace-mode
|
||||
In all of those cases, the immediate remedy is the same: use the
|
||||
command @kbd{M-x normal-erase-is-backspace-mode}. This toggles
|
||||
between the two modes that Emacs supports for handling @key{DEL}, so
|
||||
if Emacs starts in the wrong mode, this should switch to the right
|
||||
mode. On a text terminal, if you want to ask for help when @key{BS}
|
||||
is treated as @key{DEL}, use @key{F1} instead of @kbd{C-h}; @kbd{C-?}
|
||||
may also work, if it sends character code 127.
|
||||
|
||||
To fix the problem in every Emacs session, put one of the following
|
||||
lines into your initialization file (@pxref{Init File}). For the
|
||||
first case above, where @key{BACKSPACE} deletes forwards instead of
|
||||
backwards, use this line to make @key{BACKSPACE} act as @key{DEL}:
|
||||
|
||||
@lisp
|
||||
(normal-erase-is-backspace-mode 0)
|
||||
@end lisp
|
||||
|
||||
@noindent
|
||||
For the other two cases, use this line:
|
||||
|
||||
@lisp
|
||||
(normal-erase-is-backspace-mode 1)
|
||||
@end lisp
|
||||
|
||||
@vindex normal-erase-is-backspace
|
||||
Another way to fix the problem for every Emacs session is to
|
||||
customize the variable @code{normal-erase-is-backspace}: the value
|
||||
@code{t} specifies the mode where @key{BS} or @key{BACKSPACE} is
|
||||
@key{DEL}, and @code{nil} specifies the other mode. @xref{Easy
|
||||
Customization}.
|
||||
|
||||
@node Bugs
|
||||
@section Reporting Bugs
|
||||
|
||||
|
|
|
|||
8
etc/NEWS
8
etc/NEWS
|
|
@ -788,6 +788,14 @@ available options can be restored by enabling this option.
|
|||
Use it if you want Imenu to forget the buffer's index alist and
|
||||
recreate it anew next time 'imenu' is invoked.
|
||||
|
||||
+++
|
||||
** Emacs is now capable of abandoning a window's redisplay that takes too long.
|
||||
This is controlled by the new variable 'max-redisplay-ticks'. If that
|
||||
variable is set to a non-zero value, display of a window will be
|
||||
aborted after that many low-level redisplay operations, thus
|
||||
preventing Emacs from becoming wedged when visiting files with very
|
||||
long lines.
|
||||
|
||||
* Editing Changes in Emacs 29.1
|
||||
|
||||
+++
|
||||
|
|
|
|||
42
src/bidi.c
42
src/bidi.c
|
|
@ -1277,6 +1277,12 @@ bidi_fetch_char (ptrdiff_t charpos, ptrdiff_t bytepos, ptrdiff_t *disp_pos,
|
|||
SET_TEXT_POS (pos, charpos, bytepos);
|
||||
*disp_pos = compute_display_string_pos (&pos, string, w, frame_window_p,
|
||||
disp_prop);
|
||||
/* The factor of 100 below is a heuristic that needs to be
|
||||
tuned. It means we consider 100 buffer positions examined by
|
||||
the above call roughly equivalent to the display engine
|
||||
iterating over a single buffer position. */
|
||||
if (*disp_pos > charpos)
|
||||
update_redisplay_ticks ((*disp_pos - charpos) / 100 + 1, w);
|
||||
}
|
||||
|
||||
/* Fetch the character at BYTEPOS. */
|
||||
|
|
@ -1385,6 +1391,8 @@ bidi_fetch_char (ptrdiff_t charpos, ptrdiff_t bytepos, ptrdiff_t *disp_pos,
|
|||
SET_TEXT_POS (pos, charpos + *nchars, bytepos + *ch_len);
|
||||
*disp_pos = compute_display_string_pos (&pos, string, w, frame_window_p,
|
||||
disp_prop);
|
||||
if (*disp_pos > charpos + *nchars)
|
||||
update_redisplay_ticks ((*disp_pos - charpos - *nchars) / 100 + 1, w);
|
||||
}
|
||||
|
||||
return ch;
|
||||
|
|
@ -1583,6 +1591,9 @@ bidi_find_paragraph_start (ptrdiff_t pos, ptrdiff_t pos_byte)
|
|||
return pos_byte;
|
||||
}
|
||||
|
||||
/* This tracks how far we needed to search for first strong character. */
|
||||
static ptrdiff_t nsearch_for_strong;
|
||||
|
||||
/* On a 3.4 GHz machine, searching forward for a strong directional
|
||||
character in a long paragraph full of weaks or neutrals takes about
|
||||
1 ms for each 20K characters. The number below limits each call to
|
||||
|
|
@ -1652,6 +1663,8 @@ find_first_strong_char (ptrdiff_t pos, ptrdiff_t bytepos, ptrdiff_t end,
|
|||
pos += *nchars;
|
||||
bytepos += *ch_len;
|
||||
}
|
||||
|
||||
nsearch_for_strong += pos - pos1;
|
||||
return type;
|
||||
}
|
||||
|
||||
|
|
@ -1681,6 +1694,9 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, bool no_default_p)
|
|||
calls to BYTE_TO_CHAR and its ilk. */
|
||||
ptrdiff_t begbyte = string_p ? 0 : BEGV_BYTE;
|
||||
ptrdiff_t end = string_p ? bidi_it->string.schars : ZV;
|
||||
ptrdiff_t pos = bidi_it->charpos;
|
||||
|
||||
nsearch_for_strong = 0;
|
||||
|
||||
/* Special case for an empty buffer. */
|
||||
if (bytepos == begbyte && bidi_it->charpos == end)
|
||||
|
|
@ -1702,7 +1718,7 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, bool no_default_p)
|
|||
else if (dir == NEUTRAL_DIR) /* P2 */
|
||||
{
|
||||
ptrdiff_t ch_len, nchars;
|
||||
ptrdiff_t pos, disp_pos = -1;
|
||||
ptrdiff_t disp_pos = -1;
|
||||
int disp_prop = 0;
|
||||
bidi_type_t type;
|
||||
const unsigned char *s;
|
||||
|
|
@ -1800,6 +1816,14 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, bool no_default_p)
|
|||
bidi_it->level_stack[0].level = 0;
|
||||
|
||||
bidi_line_init (bidi_it);
|
||||
|
||||
/* The factor of 50 below is a heuristic that needs to be tuned. It
|
||||
means we consider 50 buffer positions examined by this function
|
||||
roughly equivalent to the display engine iterating over a single
|
||||
buffer position. */
|
||||
ptrdiff_t nexamined = bidi_it->charpos - pos + nsearch_for_strong;
|
||||
if (nexamined > 0)
|
||||
update_redisplay_ticks (nexamined / 50, bidi_it->w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2566,6 +2590,7 @@ bidi_find_bracket_pairs (struct bidi_it *bidi_it)
|
|||
bidi_bracket_type_t btype;
|
||||
bidi_type_t type = bidi_it->type;
|
||||
bool retval = false;
|
||||
ptrdiff_t n = 0;
|
||||
|
||||
/* When scanning backwards, we don't expect any unresolved bidi
|
||||
bracket characters. */
|
||||
|
|
@ -2695,6 +2720,7 @@ bidi_find_bracket_pairs (struct bidi_it *bidi_it)
|
|||
}
|
||||
old_sidx = bidi_it->stack_idx;
|
||||
type = bidi_resolve_weak (bidi_it);
|
||||
n++;
|
||||
/* Skip level runs excluded from this isolating run sequence. */
|
||||
new_sidx = bidi_it->stack_idx;
|
||||
if (bidi_it->level_stack[new_sidx].level > current_level
|
||||
|
|
@ -2718,6 +2744,7 @@ bidi_find_bracket_pairs (struct bidi_it *bidi_it)
|
|||
goto give_up;
|
||||
}
|
||||
type = bidi_resolve_weak (bidi_it);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if (type == NEUTRAL_B
|
||||
|
|
@ -2794,6 +2821,12 @@ bidi_find_bracket_pairs (struct bidi_it *bidi_it)
|
|||
}
|
||||
|
||||
give_up:
|
||||
/* The factor of 20 below is a heuristic that needs to be tuned. It
|
||||
means we consider 20 buffer positions examined by this function
|
||||
roughly equivalent to the display engine iterating over a single
|
||||
buffer position. */
|
||||
if (n > 0)
|
||||
update_redisplay_ticks (n / 20 + 1, bidi_it->w);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -3363,6 +3396,7 @@ bidi_find_other_level_edge (struct bidi_it *bidi_it, int level, bool end_flag)
|
|||
else
|
||||
{
|
||||
int new_level;
|
||||
ptrdiff_t pos0 = bidi_it->charpos;
|
||||
|
||||
/* If we are at end of level, its edges must be cached. */
|
||||
if (end_flag)
|
||||
|
|
@ -3398,6 +3432,12 @@ bidi_find_other_level_edge (struct bidi_it *bidi_it, int level, bool end_flag)
|
|||
bidi_cache_iterator_state (bidi_it, 1, 1);
|
||||
}
|
||||
} while (new_level >= level);
|
||||
/* The factor of 50 below is a heuristic that needs to be
|
||||
tuned. It means we consider 50 buffer positions examined by
|
||||
the above call roughly equivalent to the display engine
|
||||
iterating over a single buffer position. */
|
||||
if (bidi_it->charpos > pos0)
|
||||
update_redisplay_ticks ((bidi_it->charpos - pos0) / 50 + 1, bidi_it->w);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3407,6 +3407,8 @@ int partial_line_height (struct it *it_origin);
|
|||
bool in_display_vector_p (struct it *);
|
||||
int frame_mode_line_height (struct frame *);
|
||||
extern bool redisplaying_p;
|
||||
extern bool display_working_on_window_p;
|
||||
extern void unwind_display_working_on_window (void);
|
||||
extern bool help_echo_showing_p;
|
||||
extern Lisp_Object help_echo_string, help_echo_window;
|
||||
extern Lisp_Object help_echo_object, previous_help_echo_string;
|
||||
|
|
@ -3505,6 +3507,8 @@ extern unsigned row_hash (struct glyph_row *);
|
|||
|
||||
extern bool buffer_flipping_blocked_p (void);
|
||||
|
||||
extern void update_redisplay_ticks (int, struct window *);
|
||||
|
||||
/* Defined in image.c */
|
||||
|
||||
#ifdef HAVE_WINDOW_SYSTEM
|
||||
|
|
|
|||
|
|
@ -2732,12 +2732,25 @@ set_frame_matrix_frame (struct frame *f)
|
|||
operations in window matrices of frame_matrix_frame. */
|
||||
|
||||
static void
|
||||
make_current (struct glyph_matrix *desired_matrix, struct glyph_matrix *current_matrix, int row)
|
||||
make_current (struct glyph_matrix *desired_matrix,
|
||||
struct glyph_matrix *current_matrix, int row)
|
||||
{
|
||||
struct glyph_row *current_row = MATRIX_ROW (current_matrix, row);
|
||||
struct glyph_row *desired_row = MATRIX_ROW (desired_matrix, row);
|
||||
bool mouse_face_p = current_row->mouse_face_p;
|
||||
|
||||
/* If we aborted redisplay of this window, a row in the desired
|
||||
matrix might not have its hash computed. But update_window
|
||||
relies on each row having its correct hash, so do it here if
|
||||
needed. */
|
||||
if (!desired_row->hash
|
||||
/* A glyph row that is not completely empty is unlikely to have
|
||||
a zero hash value. */
|
||||
&& !(!desired_row->used[0]
|
||||
&& !desired_row->used[1]
|
||||
&& !desired_row->used[2]))
|
||||
desired_row->hash = row_hash (desired_row);
|
||||
|
||||
/* Do current_row = desired_row. This exchanges glyph pointers
|
||||
between both rows, and does a structure assignment otherwise. */
|
||||
assign_row (current_row, desired_row);
|
||||
|
|
|
|||
|
|
@ -2177,6 +2177,8 @@ whether or not it is currently displayed in some window. */)
|
|||
line_number_display_width (w, &lnum_width, &lnum_pixel_width);
|
||||
SET_TEXT_POS (pt, PT, PT_BYTE);
|
||||
itdata = bidi_shelve_cache ();
|
||||
record_unwind_protect_void (unwind_display_working_on_window);
|
||||
display_working_on_window_p = true;
|
||||
start_display (&it, w, pt);
|
||||
it.lnum_width = lnum_width;
|
||||
first_x = it.first_visible_x;
|
||||
|
|
|
|||
|
|
@ -1501,7 +1501,14 @@ command_loop_1 (void)
|
|||
point_before_last_command_or_undo = PT;
|
||||
buffer_before_last_command_or_undo = current_buffer;
|
||||
|
||||
/* Restart our counting of redisplay ticks before
|
||||
executing the command, so that we don't blame the new
|
||||
command for the sins of the previous one. */
|
||||
update_redisplay_ticks (0, NULL);
|
||||
display_working_on_window_p = false;
|
||||
|
||||
call1 (Qcommand_execute, Vthis_command);
|
||||
display_working_on_window_p = false;
|
||||
|
||||
#ifdef HAVE_WINDOW_SYSTEM
|
||||
/* Do not check display_hourglass_p here, because
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "buffer.h"
|
||||
#include "syntax.h"
|
||||
#include "category.h"
|
||||
#include "dispextern.h"
|
||||
|
||||
/* Maximum number of duplicates an interval can allow. Some systems
|
||||
define this in other header files, but we want our value, so remove
|
||||
|
|
@ -3953,6 +3954,9 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
and need to test it, it's not garbage. */
|
||||
re_char *match_end = NULL;
|
||||
|
||||
/* This keeps track of how many buffer/string positions we examined. */
|
||||
ptrdiff_t nchars = 0;
|
||||
|
||||
#ifdef DEBUG_COMPILES_ARGUMENTS
|
||||
/* Counts the total number of registers pushed. */
|
||||
ptrdiff_t num_regs_pushed = 0;
|
||||
|
|
@ -4209,6 +4213,12 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
|
||||
unbind_to (count, Qnil);
|
||||
SAFE_FREE ();
|
||||
/* The factor of 50 below is a heuristic that needs to be tuned. It
|
||||
means we consider 50 buffer positions examined by this function
|
||||
roughly equivalent to the display engine iterating over a single
|
||||
buffer position. */
|
||||
if (nchars > 0)
|
||||
update_redisplay_ticks (nchars / 50 + 1, NULL);
|
||||
return dcnt;
|
||||
}
|
||||
|
||||
|
|
@ -4261,6 +4271,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
p += pat_charlen;
|
||||
d += buf_charlen;
|
||||
mcnt -= pat_charlen;
|
||||
nchars++;
|
||||
}
|
||||
while (mcnt > 0);
|
||||
else
|
||||
|
|
@ -4298,6 +4309,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
p += pat_charlen;
|
||||
d++;
|
||||
mcnt -= pat_charlen;
|
||||
nchars++;
|
||||
}
|
||||
while (mcnt > 0);
|
||||
|
||||
|
|
@ -4321,6 +4333,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
|
||||
DEBUG_PRINT (" Matched \"%d\".\n", *d);
|
||||
d += buf_charlen;
|
||||
nchars++;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -4373,6 +4386,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
goto fail;
|
||||
|
||||
d += len;
|
||||
nchars++;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -4492,6 +4506,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
goto fail;
|
||||
}
|
||||
d += dcnt, d2 += dcnt;
|
||||
nchars++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -4773,10 +4788,12 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
ptrdiff_t charpos = SYNTAX_TABLE_BYTE_TO_CHAR (offset) - 1;
|
||||
UPDATE_SYNTAX_TABLE (charpos);
|
||||
GET_CHAR_BEFORE_2 (c1, d, string1, end1, string2, end2);
|
||||
nchars++;
|
||||
s1 = SYNTAX (c1);
|
||||
UPDATE_SYNTAX_TABLE_FORWARD (charpos + 1);
|
||||
PREFETCH_NOLIMIT ();
|
||||
GET_CHAR_AFTER (c2, d, dummy);
|
||||
nchars++;
|
||||
s2 = SYNTAX (c2);
|
||||
|
||||
if (/* Case 2: Only one of S1 and S2 is Sword. */
|
||||
|
|
@ -4812,6 +4829,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
UPDATE_SYNTAX_TABLE (charpos);
|
||||
PREFETCH ();
|
||||
GET_CHAR_AFTER (c2, d, dummy);
|
||||
nchars++;
|
||||
s2 = SYNTAX (c2);
|
||||
|
||||
/* Case 2: S2 is not Sword. */
|
||||
|
|
@ -4822,6 +4840,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
if (!AT_STRINGS_BEG (d))
|
||||
{
|
||||
GET_CHAR_BEFORE_2 (c1, d, string1, end1, string2, end2);
|
||||
nchars++;
|
||||
UPDATE_SYNTAX_TABLE_BACKWARD (charpos - 1);
|
||||
s1 = SYNTAX (c1);
|
||||
|
||||
|
|
@ -4852,6 +4871,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
ptrdiff_t charpos = SYNTAX_TABLE_BYTE_TO_CHAR (offset) - 1;
|
||||
UPDATE_SYNTAX_TABLE (charpos);
|
||||
GET_CHAR_BEFORE_2 (c1, d, string1, end1, string2, end2);
|
||||
nchars++;
|
||||
s1 = SYNTAX (c1);
|
||||
|
||||
/* Case 2: S1 is not Sword. */
|
||||
|
|
@ -4863,6 +4883,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
{
|
||||
PREFETCH_NOLIMIT ();
|
||||
GET_CHAR_AFTER (c2, d, dummy);
|
||||
nchars++;
|
||||
UPDATE_SYNTAX_TABLE_FORWARD (charpos + 1);
|
||||
s2 = SYNTAX (c2);
|
||||
|
||||
|
|
@ -4893,6 +4914,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
UPDATE_SYNTAX_TABLE (charpos);
|
||||
PREFETCH ();
|
||||
c2 = RE_STRING_CHAR (d, target_multibyte);
|
||||
nchars++;
|
||||
s2 = SYNTAX (c2);
|
||||
|
||||
/* Case 2: S2 is neither Sword nor Ssymbol. */
|
||||
|
|
@ -4903,6 +4925,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
if (!AT_STRINGS_BEG (d))
|
||||
{
|
||||
GET_CHAR_BEFORE_2 (c1, d, string1, end1, string2, end2);
|
||||
nchars++;
|
||||
UPDATE_SYNTAX_TABLE_BACKWARD (charpos - 1);
|
||||
s1 = SYNTAX (c1);
|
||||
|
||||
|
|
@ -4931,6 +4954,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
ptrdiff_t charpos = SYNTAX_TABLE_BYTE_TO_CHAR (offset) - 1;
|
||||
UPDATE_SYNTAX_TABLE (charpos);
|
||||
GET_CHAR_BEFORE_2 (c1, d, string1, end1, string2, end2);
|
||||
nchars++;
|
||||
s1 = SYNTAX (c1);
|
||||
|
||||
/* Case 2: S1 is neither Ssymbol nor Sword. */
|
||||
|
|
@ -4942,6 +4966,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
{
|
||||
PREFETCH_NOLIMIT ();
|
||||
c2 = RE_STRING_CHAR (d, target_multibyte);
|
||||
nchars++;
|
||||
UPDATE_SYNTAX_TABLE_FORWARD (charpos + 1);
|
||||
s2 = SYNTAX (c2);
|
||||
|
||||
|
|
@ -4973,6 +4998,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
if ((SYNTAX (c) != (enum syntaxcode) mcnt) ^ not)
|
||||
goto fail;
|
||||
d += len;
|
||||
nchars++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -4999,6 +5025,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
if ((!CHAR_HAS_CATEGORY (c, mcnt)) ^ not)
|
||||
goto fail;
|
||||
d += len;
|
||||
nchars++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -5060,6 +5087,9 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
|
|||
unbind_to (count, Qnil);
|
||||
SAFE_FREE ();
|
||||
|
||||
if (nchars > 0)
|
||||
update_redisplay_ticks (nchars / 50 + 1, NULL);
|
||||
|
||||
return -1; /* Failure to match. */
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|||
#include <config.h>
|
||||
|
||||
#include "lisp.h"
|
||||
#include "dispextern.h"
|
||||
#include "character.h"
|
||||
#include "buffer.h"
|
||||
#include "regex-emacs.h"
|
||||
|
|
@ -3195,6 +3196,7 @@ scan_sexps_forward (struct lisp_parse_state *state,
|
|||
ptrdiff_t out_bytepos, out_charpos;
|
||||
int temp;
|
||||
unsigned short int quit_count = 0;
|
||||
ptrdiff_t started_from = from;
|
||||
|
||||
prev_from = from;
|
||||
prev_from_byte = from_byte;
|
||||
|
|
@ -3474,6 +3476,13 @@ do { prev_from = from; \
|
|||
state->levelstarts);
|
||||
state->prev_syntax = (SYNTAX_FLAGS_COMSTARTEND_FIRST (prev_from_syntax)
|
||||
|| state->quoted) ? prev_from_syntax : Smax;
|
||||
|
||||
/* The factor of 10 below is a heuristic that needs to be tuned. It
|
||||
means we consider 10 buffer positions examined by this function
|
||||
roughly equivalent to the display engine iterating over a single
|
||||
buffer position. */
|
||||
if (from > started_from)
|
||||
update_redisplay_ticks ((from - started_from) / 10 + 1, NULL);
|
||||
}
|
||||
|
||||
/* Convert a (lisp) parse state to the internal form used in
|
||||
|
|
|
|||
16
src/window.c
16
src/window.c
|
|
@ -5568,7 +5568,11 @@ window_scroll (Lisp_Object window, EMACS_INT n, bool whole, bool noerror)
|
|||
/* On GUI frames, use the pixel-based version which is much slower
|
||||
than the line-based one but can handle varying line heights. */
|
||||
if (FRAME_WINDOW_P (XFRAME (XWINDOW (window)->frame)))
|
||||
window_scroll_pixel_based (window, n, whole, noerror);
|
||||
{
|
||||
record_unwind_protect_void (unwind_display_working_on_window);
|
||||
display_working_on_window_p = true;
|
||||
window_scroll_pixel_based (window, n, whole, noerror);
|
||||
}
|
||||
else
|
||||
window_scroll_line_based (window, n, whole, noerror);
|
||||
|
||||
|
|
@ -6496,9 +6500,14 @@ displayed_window_lines (struct window *w)
|
|||
CLIP_TEXT_POS_FROM_MARKER (start, w->start);
|
||||
|
||||
itdata = bidi_shelve_cache ();
|
||||
|
||||
specpdl_ref count = SPECPDL_INDEX ();
|
||||
record_unwind_protect_void (unwind_display_working_on_window);
|
||||
display_working_on_window_p = true;
|
||||
start_display (&it, w, start);
|
||||
move_it_vertically (&it, height);
|
||||
bottom_y = line_bottom_y (&it);
|
||||
unbind_to (count, Qnil);
|
||||
bidi_unshelve_cache (itdata, false);
|
||||
|
||||
/* Add in empty lines at the bottom of the window. */
|
||||
|
|
@ -6592,6 +6601,10 @@ and redisplay normally--don't erase and redraw the frame. */)
|
|||
data structures might not be set up yet then. */
|
||||
if (!FRAME_INITIAL_P (XFRAME (w->frame)))
|
||||
{
|
||||
specpdl_ref count = SPECPDL_INDEX ();
|
||||
|
||||
record_unwind_protect_void (unwind_display_working_on_window);
|
||||
display_working_on_window_p = true;
|
||||
if (center_p)
|
||||
{
|
||||
struct it it;
|
||||
|
|
@ -6708,6 +6721,7 @@ and redisplay normally--don't erase and redraw the frame. */)
|
|||
|
||||
bidi_unshelve_cache (itdata, false);
|
||||
}
|
||||
unbind_to (count, Qnil);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
124
src/xdisp.c
124
src/xdisp.c
|
|
@ -1030,6 +1030,15 @@ static struct glyph_slice null_glyph_slice = { 0, 0, 0, 0 };
|
|||
|
||||
bool redisplaying_p;
|
||||
|
||||
/* True while some display-engine code is working on layout of some
|
||||
window.
|
||||
|
||||
WARNING: Use sparingly, preferably only in top level of commands
|
||||
and important functions, because using it in nested calls might
|
||||
reset the flag when the inner call returns, behind the back of
|
||||
the callers. */
|
||||
bool display_working_on_window_p;
|
||||
|
||||
/* If a string, XTread_socket generates an event to display that string.
|
||||
(The display is done in read_char.) */
|
||||
|
||||
|
|
@ -3222,6 +3231,8 @@ init_iterator (struct it *it, struct window *w,
|
|||
|
||||
it->cmp_it.id = -1;
|
||||
|
||||
update_redisplay_ticks (0, w);
|
||||
|
||||
/* Extra space between lines (on window systems only). */
|
||||
if (base_face_id == DEFAULT_FACE_ID
|
||||
&& FRAME_WINDOW_P (it->f))
|
||||
|
|
@ -8175,6 +8186,8 @@ void
|
|||
set_iterator_to_next (struct it *it, bool reseat_p)
|
||||
{
|
||||
|
||||
update_redisplay_ticks (1, it->w);
|
||||
|
||||
switch (it->method)
|
||||
{
|
||||
case GET_FROM_BUFFER:
|
||||
|
|
@ -10957,6 +10970,7 @@ window_text_pixel_size (Lisp_Object window, Lisp_Object from, Lisp_Object to,
|
|||
max_y = XFIXNUM (y_limit);
|
||||
|
||||
itdata = bidi_shelve_cache ();
|
||||
|
||||
start_display (&it, w, startp);
|
||||
|
||||
int start_y = it.current_y;
|
||||
|
|
@ -16724,9 +16738,14 @@ redisplay_internal (void)
|
|||
list_of_error,
|
||||
redisplay_window_error);
|
||||
if (update_miniwindow_p)
|
||||
internal_condition_case_1 (redisplay_window_1,
|
||||
FRAME_MINIBUF_WINDOW (sf), list_of_error,
|
||||
redisplay_window_error);
|
||||
{
|
||||
Lisp_Object mini_window = FRAME_MINIBUF_WINDOW (sf);
|
||||
|
||||
displayed_buffer = XBUFFER (XWINDOW (mini_window)->contents);
|
||||
internal_condition_case_1 (redisplay_window_1, mini_window,
|
||||
list_of_error,
|
||||
redisplay_window_error);
|
||||
}
|
||||
|
||||
/* Compare desired and current matrices, perform output. */
|
||||
|
||||
|
|
@ -16961,6 +16980,13 @@ unwind_redisplay (void)
|
|||
unblock_buffer_flips ();
|
||||
}
|
||||
|
||||
/* Function registered with record_unwind_protect before calling
|
||||
start_display outside of redisplay_internal. */
|
||||
void
|
||||
unwind_display_working_on_window (void)
|
||||
{
|
||||
display_working_on_window_p = false;
|
||||
}
|
||||
|
||||
/* Mark the display of leaf window W as accurate or inaccurate.
|
||||
If ACCURATE_P, mark display of W as accurate.
|
||||
|
|
@ -17135,9 +17161,19 @@ redisplay_windows (Lisp_Object window)
|
|||
}
|
||||
|
||||
static Lisp_Object
|
||||
redisplay_window_error (Lisp_Object ignore)
|
||||
redisplay_window_error (Lisp_Object error_data)
|
||||
{
|
||||
displayed_buffer->display_error_modiff = BUF_MODIFF (displayed_buffer);
|
||||
|
||||
/* When in redisplay, the error is captured and not shown. Arrange
|
||||
for it to be shown later. */
|
||||
if (max_redisplay_ticks > 0
|
||||
&& CONSP (error_data)
|
||||
&& EQ (XCAR (error_data), Qerror)
|
||||
&& STRINGP (XCAR (XCDR (error_data))))
|
||||
Vdelayed_warnings_list = Fcons (list2 (XCAR (error_data),
|
||||
XCAR (XCDR (error_data))),
|
||||
Vdelayed_warnings_list);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
|
@ -17156,6 +17192,68 @@ redisplay_window_1 (Lisp_Object window)
|
|||
redisplay_window (window, true);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
Aborting runaway redisplay
|
||||
***********************************************************************/
|
||||
|
||||
/* Update the redisplay-tick count for window W, and signal an error
|
||||
if the tick count is above some threshold, indicating that
|
||||
redisplay of the window takes "too long".
|
||||
|
||||
TICKS is the amount of ticks to add to the W's current count; zero
|
||||
means to initialize the tick count to zero.
|
||||
|
||||
W can be NULL if TICKS is zero: that means unconditionally
|
||||
re-initialize the current tick count to zero.
|
||||
|
||||
W can also be NULL if the caller doesn't know which window is being
|
||||
processed by the display code. In that case, if TICKS is non-zero,
|
||||
we assume it's the last window that shows the current buffer. */
|
||||
void
|
||||
update_redisplay_ticks (int ticks, struct window *w)
|
||||
{
|
||||
/* This keeps track of the window on which redisplay is working. */
|
||||
static struct window *cwindow;
|
||||
static EMACS_INT window_ticks;
|
||||
|
||||
/* We only initialize the count if this is a different window or
|
||||
NULL. Otherwise, this is a call from init_iterator for the same
|
||||
window we tracked before, and we should keep the count. */
|
||||
if (!ticks && w != cwindow)
|
||||
{
|
||||
cwindow = w;
|
||||
window_ticks = 0;
|
||||
}
|
||||
/* Some callers can be run in contexts unrelated to display code, so
|
||||
don't abort them and don't update the tick count in those cases. */
|
||||
if ((!w && !redisplaying_p && !display_working_on_window_p)
|
||||
/* We never disable redisplay of a mini-window, since that is
|
||||
absolutely essential for communicating with Emacs. */
|
||||
|| (w && MINI_WINDOW_P (w)))
|
||||
return;
|
||||
|
||||
if (ticks > 0)
|
||||
window_ticks += ticks;
|
||||
if (max_redisplay_ticks > 0 && window_ticks > max_redisplay_ticks)
|
||||
{
|
||||
/* In addition to a buffer, this could be a window (for non-leaf
|
||||
windows, not expected here) or nil (for pseudo-windows like
|
||||
the one used for the native tool bar). */
|
||||
Lisp_Object contents = w ? w->contents : Qnil;
|
||||
char *bufname =
|
||||
NILP (contents)
|
||||
? SSDATA (BVAR (current_buffer, name))
|
||||
: (BUFFERP (contents)
|
||||
? SSDATA (BVAR (XBUFFER (contents), name))
|
||||
: (char *) "<unknown>");
|
||||
|
||||
windows_or_buffers_changed = 177;
|
||||
error ("Window showing buffer %s takes too long to redisplay", bufname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Set cursor position of W. PT is assumed to be displayed in ROW.
|
||||
|
|
@ -35777,7 +35875,7 @@ be let-bound around code that needs to disable messages temporarily. */);
|
|||
|
||||
DEFSYM (Qinhibit_free_realized_faces, "inhibit-free-realized-faces");
|
||||
|
||||
list_of_error = list1 (list2 (Qerror, Qvoid_variable));
|
||||
list_of_error = list1 (Qerror);
|
||||
staticpro (&list_of_error);
|
||||
|
||||
/* Values of those variables at last redisplay are stored as
|
||||
|
|
@ -36667,6 +36765,22 @@ and display the most important part of the minibuffer. */);
|
|||
This makes it easier to edit character sequences that are
|
||||
composed on display. */);
|
||||
composition_break_at_point = false;
|
||||
|
||||
DEFVAR_INT ("max-redisplay-ticks", max_redisplay_ticks,
|
||||
doc: /* Maximum number of redisplay ticks before aborting redisplay of a window.
|
||||
|
||||
This allows to abort the display of a window if the amount of low-level
|
||||
redisplay operations exceeds the value of this variable. When display of
|
||||
a window is aborted due to this reason, the buffer shown in that window
|
||||
will not have its windows redisplayed until the buffer is modified or until
|
||||
you type \\[recenter-top-bottom] with one of its windows selected.
|
||||
You can also decide to kill the buffer and visit it in some
|
||||
other way, like under `so-long-mode' or literally.
|
||||
|
||||
The default value is zero, which disables this feature.
|
||||
The recommended non-zero value is between 100000 and 1000000,
|
||||
depending on your patience and the speed of your system. */);
|
||||
max_redisplay_ticks = 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue