aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/eval.txt16
-rw-r--r--src/nvim/eval.c21
-rw-r--r--src/nvim/option.c2
-rw-r--r--src/nvim/testdir/test_timers.vim17
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