'inccommand': Detect "non-interactive", "too slow".

command_line_changed:
  - Check (current_SID == 0) instead of KeyTyped
    - We want to update during mappings (KeyTyped is false then).
  - Check vpeekc_any()
    - Avoids unnecessary work.
    - Avoids triggering live preview during macros.
    - Caveat: This makes the redraw "stutter" if user spams (holds a key)
      in the replace pattern. But that scenario is not important.
  - Update screen if the command is changed to a non-live command.
    (`s->live` goes from true => false) => clears the preview
command_line_execute:
  - Let CTRL-C cancel live preview
do_sub:
  - Enforce a time limit ('redrawtime').
  - Unset 'inccommand' if time limit is reached.

Closes #5602
Closes #5585
This commit is contained in:
Justin M. Keyes 2016-11-14 04:16:13 +01:00
parent 4539d867d4
commit 91507c271e
5 changed files with 78 additions and 58 deletions

View File

@ -3418,6 +3418,9 @@ A jump table for the options with a short description can be found at |Q_op|.
Works for |:substitute|, |:smagic|, |:snomagic|. |hl-Substitute| Works for |:substitute|, |:smagic|, |:snomagic|. |hl-Substitute|
If the preview is too slow (exceeds 'redrawtime') then 'inccommand' is
automatically disabled until |Command-line-mode| is done.
*'include'* *'inc'* *'include'* *'inc'*
'include' 'inc' string (default "^\s*#\s*include") 'include' 'inc' string (default "^\s*#\s*include")
global or local to buffer |global-local| global or local to buffer |global-local|
@ -4733,8 +4736,8 @@ A jump table for the options with a short description can be found at |Q_op|.
global global
{only available when compiled with the |+reltime| {only available when compiled with the |+reltime|
feature} feature}
The time in milliseconds for redrawing the display. This applies to Time in milliseconds for redrawing the display. Applies to
searching for patterns for 'hlsearch' and |:match| highlighting. 'hlsearch', 'inccommand' and |:match| highlighting.
When redrawing takes more than this many milliseconds no further When redrawing takes more than this many milliseconds no further
matches will be highlighted. This is used to avoid that Vim hangs matches will be highlighted. This is used to avoid that Vim hangs
when using a very complicated pattern. when using a very complicated pattern.

View File

@ -3100,8 +3100,6 @@ static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags,
return cmd; return cmd;
} }
/// do_sub()
///
/// Perform a substitution from line eap->line1 to line eap->line2 using the /// Perform a substitution from line eap->line1 to line eap->line2 using the
/// command pointed to by eap->arg which should be of the form: /// command pointed to by eap->arg which should be of the form:
/// ///
@ -3110,7 +3108,7 @@ static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags,
/// The usual escapes are supported as described in the regexp docs. /// The usual escapes are supported as described in the regexp docs.
/// ///
/// @return buffer used for 'inccommand' preview /// @return buffer used for 'inccommand' preview
buf_T *do_sub(exarg_T *eap) static buf_T *do_sub(exarg_T *eap)
{ {
long i = 0; long i = 0;
regmmatch_T regmatch; regmmatch_T regmatch;
@ -3139,6 +3137,7 @@ buf_T *do_sub(exarg_T *eap)
bool endcolumn = false; // cursor in last column when done bool endcolumn = false; // cursor in last column when done
MatchedLineVec matched_lines = KV_INITIAL_VALUE; MatchedLineVec matched_lines = KV_INITIAL_VALUE;
pos_T old_cursor = curwin->w_cursor; pos_T old_cursor = curwin->w_cursor;
proftime_T timeout = eap->is_live ? profile_setlimit(p_rdt) : profile_zero();
int start_nsubs; int start_nsubs;
int save_ma = 0; int save_ma = 0;
int save_b_changed = curbuf->b_changed; int save_b_changed = curbuf->b_changed;
@ -3581,7 +3580,7 @@ buf_T *do_sub(exarg_T *eap)
|| typed == intr_char || typed == intr_char
#endif #endif
) { ) {
got_quit = TRUE; got_quit = true;
break; break;
} }
if (typed == 'n') if (typed == 'n')
@ -3882,6 +3881,10 @@ skip:
} }
line_breakcheck(); line_breakcheck();
if (profile_passed_limit(timeout)) {
got_quit = true;
}
} }
if (first_line != 0) { if (first_line != 0) {
@ -3949,10 +3952,15 @@ skip:
// Show 'inccommand' preview if there are matched lines. // Show 'inccommand' preview if there are matched lines.
buf_T *preview_buf = NULL; buf_T *preview_buf = NULL;
if (eap->is_live && *p_icm != NUL && matched_lines.size != 0 && pat != NULL) { if (eap->is_live && !aborting()) {
if (got_quit) { // Substitution is too slow, disable 'inccommand'.
set_string_option_direct((char_u *)"icm", -1, (char_u *)"", OPT_FREE,
SID_NONE);
} else if (*p_icm != NUL && matched_lines.size != 0 && pat != NULL) {
curbuf->b_changed = save_b_changed; // preserve 'modified' during preview curbuf->b_changed = save_b_changed; // preserve 'modified' during preview
preview_buf = show_sub(eap, old_cursor, pat, sub, &matched_lines); preview_buf = show_sub(eap, old_cursor, pat, sub, &matched_lines);
} }
}
for (MatchedLine m; kv_size(matched_lines);) { for (MatchedLine m; kv_size(matched_lines);) {
m = kv_pop(matched_lines); m = kv_pop(matched_lines);
@ -6017,7 +6025,8 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub,
cmdmod.tab = 0; // disable :tab modifier cmdmod.tab = 0; // disable :tab modifier
cmdmod.noswapfile = true; // disable swap for preview buffer cmdmod.noswapfile = true; // disable swap for preview buffer
// disable file info message // disable file info message
set_option_value((char_u *)"shm", 0L, (char_u *)"F", 0); set_string_option_direct((char_u *)"shm", -1, (char_u *)"F", OPT_FREE,
SID_NONE);
bool outside_curline = (eap->line1 != old_cusr.lnum bool outside_curline = (eap->line1 != old_cusr.lnum
|| eap->line2 != old_cusr.lnum); || eap->line2 != old_cusr.lnum);
@ -6096,7 +6105,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub,
win_size_restore(&save_winsizes); win_size_restore(&save_winsizes);
ga_clear(&save_winsizes); ga_clear(&save_winsizes);
set_option_value((char_u *)"shm", 0L, save_shm_p, 0); set_string_option_direct((char_u *)"shm", -1, save_shm_p, OPT_FREE, SID_NONE);
xfree(save_shm_p); xfree(save_shm_p);
// Update screen now. Must do this _before_ close_windows(). // Update screen now. Must do this _before_ close_windows().

View File

@ -103,11 +103,13 @@ typedef struct command_line_state {
linenr_T old_botline; linenr_T old_botline;
int did_incsearch; int did_incsearch;
int incsearch_postponed; int incsearch_postponed;
bool live; // performing 'inccommand' preview
int did_wild_list; // did wild_list() recently int did_wild_list; // did wild_list() recently
int wim_index; // index in wim_flags[] int wim_index; // index in wim_flags[]
int res; int res;
int save_msg_scroll; int save_msg_scroll;
int save_State; // remember State when called int save_State; // remember State when called
char_u *save_p_icm;
int some_key_typed; // one of the keys was typed int some_key_typed; // one of the keys was typed
// mouse drag and release events are ignored, unless they are // mouse drag and release events are ignored, unless they are
// preceded with a mouse down event // preceded with a mouse down event
@ -159,6 +161,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
s->indent = indent; s->indent = indent;
s->save_msg_scroll = msg_scroll; s->save_msg_scroll = msg_scroll;
s->save_State = State; s->save_State = State;
s->save_p_icm = vim_strsave(p_icm);
s->ignore_drag_release = true; s->ignore_drag_release = true;
if (s->firstc == -1) { if (s->firstc == -1) {
@ -323,9 +326,12 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
need_wait_return = false; need_wait_return = false;
} }
set_string_option_direct((char_u *)"icm", -1, s->save_p_icm, OPT_FREE,
SID_NONE);
State = s->save_State; State = s->save_State;
setmouse(); setmouse();
ui_cursor_shape(); // may show different cursor shape ui_cursor_shape(); // may show different cursor shape
xfree(s->save_p_icm);
{ {
char_u *p = ccline.cmdbuff; char_u *p = ccline.cmdbuff;
@ -394,7 +400,8 @@ static int command_line_execute(VimState *state, int key)
if ((s->c == Ctrl_C) if ((s->c == Ctrl_C)
&& s->firstc != '@' && s->firstc != '@'
&& !s->break_ctrl_c && !s->break_ctrl_c
&& !global_busy) { && !global_busy
&& !cmd_is_live(ccline.cmdbuff)) {
got_int = false; got_int = false;
} }
@ -981,7 +988,6 @@ static int command_line_handle_key(CommandLineState *s)
status_redraw_curbuf(); status_redraw_curbuf();
return command_line_not_changed(s); return command_line_not_changed(s);
// case '@': only in very old vi
case Ctrl_U: case Ctrl_U:
// delete all characters left of the cursor // delete all characters left of the cursor
s->j = ccline.cmdpos; s->j = ccline.cmdpos;
@ -996,7 +1002,6 @@ static int command_line_handle_key(CommandLineState *s)
redrawcmd(); redrawcmd();
return command_line_changed(s); return command_line_changed(s);
case ESC: // get here if p_wc != ESC or when ESC typed twice case ESC: // get here if p_wc != ESC or when ESC typed twice
case Ctrl_C: case Ctrl_C:
// In exmode it doesn't make sense to return. Except when // In exmode it doesn't make sense to return. Except when
@ -1489,11 +1494,11 @@ static int command_line_handle_key(CommandLineState *s)
static int command_line_not_changed(CommandLineState *s) static int command_line_not_changed(CommandLineState *s)
{ {
// This part implements incremental searches for "/" and "?" Jump to // Incremental searches for "/" and "?":
// cmdline_not_changed when a character has been read but the command line // Enter command_line_not_changed() when a character has been read but the
// did not change. Then we only search and redraw if something changed in // command line did not change. Then we only search and redraw if something
// the past. Jump to cmdline_changed when the command line did change. // changed in the past.
// (Sorry for the goto's, I know it is ugly). // Enter command_line_changed() when the command line did change.
if (!s->incsearch_postponed) { if (!s->incsearch_postponed) {
return 1; return 1;
} }
@ -1592,11 +1597,13 @@ static int command_line_changed(CommandLineState *s)
redrawcmdline(); redrawcmdline();
s->did_incsearch = true; s->did_incsearch = true;
} else if (s->firstc == ':' } else if (s->firstc == ':'
&& KeyTyped // only if interactive && current_SID == 0 // only if interactive
&& *p_icm != NUL // 'inccommand' is set && *p_icm != NUL // 'inccommand' is set
&& curbuf->b_p_ma // buffer is modifiable && curbuf->b_p_ma // buffer is modifiable
&& cmdline_star == 0 // not typing a password && cmdline_star == 0 // not typing a password
&& cmd_is_live(ccline.cmdbuff)) { && cmd_is_live(ccline.cmdbuff)
&& !vpeekc_any()) {
s->live = true;
// process a "live" command ('inccommand') // process a "live" command ('inccommand')
do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_LIVE); do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_LIVE);
@ -1610,6 +1617,9 @@ static int command_line_changed(CommandLineState *s)
update_topline(); update_topline();
redrawcmdline(); redrawcmdline();
} else if (s->live) {
s->live = false;
update_screen(SOME_VALID); // Clear 'inccommand' preview.
} }
if (cmdmsg_rl || (p_arshape && !p_tbidi && enc_utf8)) { if (cmdmsg_rl || (p_arshape && !p_tbidi && enc_utf8)) {

View File

@ -1583,29 +1583,27 @@ vungetc ( /* unget one character (can only be done once!) */
old_mouse_col = mouse_col; old_mouse_col = mouse_col;
} }
/* /// get a character:
* get a character: /// 1. from the stuffbuffer
* 1. from the stuffbuffer /// This is used for abbreviated commands like "D" -> "d$".
* This is used for abbreviated commands like "D" -> "d$". /// Also used to redo a command for ".".
* Also used to redo a command for ".". /// 2. from the typeahead buffer
* 2. from the typeahead buffer /// Stores text obtained previously but not used yet.
* Stores text obtained previously but not used yet. /// Also stores the result of mappings.
* Also stores the result of mappings. /// Also used for the ":normal" command.
* Also used for the ":normal" command. /// 3. from the user
* 3. from the user /// This may do a blocking wait if "advance" is TRUE.
* This may do a blocking wait if "advance" is TRUE. ///
* /// if "advance" is TRUE (vgetc()):
* if "advance" is TRUE (vgetc()): /// really get the character.
* really get the character. /// KeyTyped is set to TRUE in the case the user typed the key.
* KeyTyped is set to TRUE in the case the user typed the key. /// KeyStuffed is TRUE if the character comes from the stuff buffer.
* KeyStuffed is TRUE if the character comes from the stuff buffer. /// if "advance" is FALSE (vpeekc()):
* if "advance" is FALSE (vpeekc()): /// just look whether there is a character available.
* just look whether there is a character available. ///
* /// When "no_mapping" is zero, checks for mappings in the current mode.
* When "no_mapping" is zero, checks for mappings in the current mode. /// Only returns one byte (of a multi-byte character).
* Only returns one byte (of a multi-byte character). /// K_SPECIAL and CSI may be escaped, need to get two more bytes then.
* K_SPECIAL and CSI may be escaped, need to get two more bytes then.
*/
static int vgetorpeek(int advance) static int vgetorpeek(int advance)
{ {
int c, c1; int c, c1;