diff options
-rw-r--r-- | runtime/doc/eval.txt | 16 | ||||
-rw-r--r-- | src/nvim/eval.c | 21 | ||||
-rw-r--r-- | src/nvim/option.c | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test_timers.vim | 17 |
4 files changed, 49 insertions, 7 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index f11ce2de09..8c81f35331 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4007,14 +4007,14 @@ getchar([expr]) *getchar()* not consumed. Return zero if no character available. Without [expr] and when [expr] is 0 a whole character or - special key is returned. If it is an 8-bit character, the + special key is returned. If it is a single character, the result is a number. Use nr2char() to convert it to a String. Otherwise a String is returned with the encoded character. - For a special key it's a sequence of bytes starting with 0x80 - (decimal: 128). This is the same value as the string - "\<Key>", e.g., "\<Left>". The returned value is also a - String when a modifier (shift, control, alt) was used that is - not included in the character. + For a special key it's a String with a sequence of bytes + starting with 0x80 (decimal: 128). This is the same value as + the String "\<Key>", e.g., "\<Left>". The returned value is + also a String when a modifier (shift, control, alt) was used + that is not included in the character. When [expr] is 0 and Esc is typed, there will be a short delay while Vim waits to see if this is the start of an escape @@ -8096,6 +8096,10 @@ timer_start({time}, {callback} [, {options}]) "repeat" Number of times to repeat calling the callback. -1 means forever. When not present the callback will be called once. + If the timer causes an error three times in a + row the repeat is cancelled. This avoids that + Vim becomes unusable because of all the error + messages. Example: > func MyHandler(timer) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 929304a874..9a67b01307 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -449,6 +449,7 @@ typedef struct { int timer_id; int repeat_count; int refcount; + int emsg_count; ///< Errors in a repeating timer. long timeout; bool stopped; bool paused; @@ -17160,6 +17161,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) timer->refcount = 1; timer->stopped = false; timer->paused = false; + timer->emsg_count = 0; timer->repeat_count = repeat; timer->timeout = timeout; timer->timer_id = last_timer_id++; @@ -17202,6 +17204,9 @@ static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr) static void timer_due_cb(TimeWatcher *tw, void *data) { timer_T *timer = (timer_T *)data; + int save_did_emsg = did_emsg; + int save_called_emsg = called_emsg; + if (timer->stopped || timer->paused) { return; } @@ -17216,8 +17221,24 @@ static void timer_due_cb(TimeWatcher *tw, void *data) argv[0].v_type = VAR_NUMBER; argv[0].vval.v_number = timer->timer_id; typval_T rettv = TV_INITIAL_VALUE; + called_emsg = false; callback_call(&timer->callback, 1, argv, &rettv); + + // Handle error message + if (called_emsg && did_emsg) { + timer->emsg_count++; + if (current_exception != NULL) { + discard_current_exception(); + } + } + did_emsg = save_did_emsg; + called_emsg = save_called_emsg; + + if (timer->emsg_count >= 3) { + timer_stop(timer); + } + tv_clear(&rettv); if (!timer->stopped && timer->timeout == 0) { diff --git a/src/nvim/option.c b/src/nvim/option.c index bfb1de096d..4403861af4 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3383,7 +3383,7 @@ ambw_end: check_redraw(options[opt_idx].flags); return errmsg; -} +} // NOLINT(readability/fn_size) /// Simple int comparison function for use with qsort() static int int_cmp(const void *a, const void *b) diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index fe5d61fad4..5b5f001e6b 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -200,6 +200,23 @@ func Test_input_in_timer() call assert_equal('hello', g:val) endfunc +func FuncWithError(timer) + let g:call_count += 1 + if g:call_count == 4 + return + endif + doesnotexist +endfunc + +func Test_timer_errors() + let g:call_count = 0 + let timer = timer_start(10, 'FuncWithError', {'repeat': -1}) + " Timer will be stopped after failing 3 out of 3 times. + call WaitFor('g:call_count == 3') + sleep 50m + call assert_equal(3, g:call_count) +endfunc + func FuncWithCaughtError(timer) let g:call_count += 1 try |