Timers and deferred actions

Vim version 8.0 introduced timers, which invoke a Vim function once or repeatedly at intervals. VPE provides the Timer class to make using Vim’s timers easy and natural in Python code. It also provides a call_soon function that allows actions to be deferred until Vim is able to properly handle them.

The Timer class

Below is a simple example that performs a ‘:wall’ command every 20 seconds.

def auto_save(t):
    vpe.commands.wall()

# Arrange to save all files every 20 seconds. Setting repeat=-1 causes the
# timer to repeat indefinitely.
auto_save_timer = Timer(ms=20_000, func=auto_save, repeat=-1)

The Timer instance provides a convenient interface for the various timer related operations.

# Stop auto saving for a while.
auto_save_timer.pause()
assert auto_save_timer.paused
...

# Re-enable auto saving.
auto_save_timer.resume()
assert not auto_save_timer.paused

# Completely stop the timer.
auto_save_timer.stop()

Timer housekeeping

VPE performs a small amount of behind-the-scenes housekeeping for some Timer instances. Obviously this housekeeping might not work properly if you mix use of the Timer class with direct invocation of the Vim functions. So you should avoid using vim.timer_stop, etc. for timers started using the Timer class.

Vim provides a timer_stopall() as a kind of emergency stop. VPE provides vpe.timer_stopall which will also perform VPE’s timer housekeeping.

Use of call_soon

A lot of the time plug-in code may execute when Vim is in a state that does not allow some actions to be performed. For example, trying to print a message can silently fail. Vim’s timers provide a way to work around such problems.

def check_files_exists(src, dst):
    """Check that the source and destination files actually exist.

    This function may get invoked in a callback.
    """
    def write_err(name):
        vpe.error_msg(f'File {name} does not exist')

    if not os.path.exists(src):
        Timer(0, write_err, args=(src,))
    if not os.path.exists(dst):
        Timer(0, write_err, args=(dst,))

The above code will cause the ‘write_err’ function to be invoked only once Vim returns to a suitable state. This means that the messages should be displayed OK.

This technique can be very useful, but the above code is unwieldy and, for the above example, the order in which the callbacks are invoked is not defined. So Vpe provides call_soon, which allows the above code in check_files_exists to simplified as:

if not os.path.exists(src):
    call_soon(vpe.error_msg, f'File {src} does not exist')
if not os.path.exists(dst):
    call_soon(vpe.error_msg, f'File {dst} does not exist')

VPE ensures that each each invocation occurs in the order they were passed to the call_soon function.

In fact, the need to generate messages during callback code is common enough that the echo_msg, warning_msg and error_msg functions provide a soon keyword argument. So, for the above example, things can be further simplified.

if not os.path.exists(src):
    vpe.error_msg(f'File {src} does not exist', soon=True)
if not os.path.exists(dst):
    vpe.error_msg(f'File {dst} does not exist', soon=True)