mirror of
https://github.com/neovim/neovim.git
synced 2026-08-28 01:51:52 +00:00
fix(inccommand): enforce timeout during matching, not just after a line #40892
Problem: do_sub() only checks the timeout limit after finishing a line.
A pathological regex will run on a single line input unbounded
until the compute is completed.
Solution: Pass the timeout limit to `vim_regexec_multi()` so the
computation on the regex engine is bounded per-line.
Signed-off-by: XiaowenHu96 <me@xiaowenhu.com>
(cherry picked from commit bd73d8c011)
This commit is contained in:
committed by
github-actions[bot]
parent
290f7e69a1
commit
41163b7731
@@ -3525,10 +3525,11 @@ static int check_regexp_delim(int c)
|
||||
///
|
||||
/// The usual escapes are supported as described in the regexp docs.
|
||||
///
|
||||
/// @param tm Timeout limit
|
||||
/// @param cmdpreview_ns The namespace to show 'inccommand' preview highlights.
|
||||
/// If <= 0, preview shouldn't be shown.
|
||||
/// @return 0, 1 or 2. See cmdpreview_may_show() for more information on the meaning.
|
||||
static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_ns,
|
||||
static int do_sub(exarg_T *eap, proftime_T tm, const int cmdpreview_ns,
|
||||
const handle_T cmdpreview_bufnr)
|
||||
{
|
||||
#define ADJUST_SUB_FIRSTLNUM() \
|
||||
@@ -3759,13 +3760,15 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
|
||||
// If preview: limit to max('cmdwinheight', viewport).
|
||||
linenr_T line2 = eap->line2;
|
||||
|
||||
int timed_out = false;
|
||||
|
||||
for (linenr_T lnum = eap->line1;
|
||||
lnum <= line2 && !got_quit && !aborting()
|
||||
lnum <= line2 && !got_quit && !timed_out && !aborting()
|
||||
&& (cmdpreview_ns <= 0 || preview_lines.lines_needed <= (linenr_T)p_cwh
|
||||
|| lnum <= curwin->w_botline);
|
||||
lnum++) {
|
||||
int nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum,
|
||||
0, NULL, NULL);
|
||||
0, &tm, &timed_out);
|
||||
if (nmatch) {
|
||||
colnr_T copycol;
|
||||
colnr_T matchcol;
|
||||
@@ -4342,7 +4345,7 @@ skip:
|
||||
|| nmatch_tl > 0
|
||||
|| (nmatch = vim_regexec_multi(®match, curwin,
|
||||
curbuf, sub_firstlnum,
|
||||
matchcol, NULL, NULL)) == 0
|
||||
matchcol, &tm, &timed_out)) == 0
|
||||
|| regmatch.startpos[0].lnum > 0) {
|
||||
if (new_start != NULL) {
|
||||
// Copy the rest of the line, that didn't match.
|
||||
@@ -4418,7 +4421,7 @@ skip:
|
||||
}
|
||||
if (nmatch == -1 && !lastone) {
|
||||
nmatch = vim_regexec_multi(®match, curwin, curbuf,
|
||||
sub_firstlnum, matchcol, NULL, NULL);
|
||||
sub_firstlnum, matchcol, &tm, &timed_out);
|
||||
}
|
||||
|
||||
// 5. break if there isn't another match in this line
|
||||
@@ -4475,7 +4478,7 @@ skip:
|
||||
|
||||
line_breakcheck();
|
||||
|
||||
if (profile_passed_limit(timeout)) {
|
||||
if (timed_out || profile_passed_limit(tm)) {
|
||||
got_quit = true;
|
||||
}
|
||||
}
|
||||
@@ -4558,7 +4561,7 @@ skip:
|
||||
|
||||
// Show 'inccommand' preview if there are matched lines.
|
||||
if (cmdpreview_ns > 0 && !aborting()) {
|
||||
if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable.
|
||||
if (got_quit || profile_passed_limit(tm)) { // Too slow, disable.
|
||||
set_option_direct(kOptInccommand, STATIC_CSTR_AS_OPTVAL(""), 0, SID_NONE);
|
||||
} else if (*p_icm != NUL && pat != NULL) {
|
||||
if (pre_hl_id == 0) {
|
||||
|
||||
@@ -1140,6 +1140,24 @@ describe(':substitute, inccommand=split', function()
|
||||
eq('split', eval('&inccommand'))
|
||||
end)
|
||||
|
||||
it('time limit is enforced while matching a single line #40773', function()
|
||||
-- prevent redraws from 'incsearch'
|
||||
api.nvim_set_option_value('incsearch', false, {})
|
||||
-- Assert that 'inccommand' is ENABLED initially.
|
||||
eq('split', eval('&inccommand'))
|
||||
-- Set 'redrawtime' to minimal value, to ensure timeout is triggered.
|
||||
command('set redrawtime=1 nowrap')
|
||||
-- Prepare the text
|
||||
api.nvim_buf_set_lines(0, 0, -1, true, { ('aaaaaaaa/'):rep(6) .. ('b'):rep(200) })
|
||||
feed([[:%s/.\+\/\(.\+\)\+ft\/\1]])
|
||||
screen:expect({ any = vim.pesc([[:%s/.\+\/\(.\+\)\+ft\/\1]]) })
|
||||
-- Assert that 'inccommand' is DISABLED in cmdline mode.
|
||||
eq('', eval('&inccommand'))
|
||||
-- Assert that 'inccommand' is again ENABLED after leaving cmdline mode.
|
||||
feed([[<C-\><C-N>]])
|
||||
eq('split', eval('&inccommand'))
|
||||
end)
|
||||
|
||||
it("deactivates if 'foldexpr' is slow #9557", function()
|
||||
insert([[
|
||||
a
|
||||
|
||||
Reference in New Issue
Block a user