diff --git a/README.md b/README.md index 771a420e..9a72c449 100644 --- a/README.md +++ b/README.md @@ -76,10 +76,10 @@ below. computations. Intensive computations should be done in a separate thread (or process), and `vim.async_call` can be used to send results back to nvim. -* Some methods accept an `async` keyword argument: `vim.eval`, - `vim.command`, `vim.request` as well as the `vim.funcs` and `vim.api` wrappers. - The python host will not wait for nvim to complete the request (which also - means that the return value is unavailable). +* Some methods accept an `async` keyword argument: `vim.eval`, `vim.command`, + `vim.request` as well as the `vim.funcs` and `vim.api` wrappers. When + `async=True` is passed the client will not wait for nvim to complete the + request (which also means that the return value is unavailable). #### Remote (new-style) plugins @@ -114,6 +114,14 @@ If `sync=True` is supplied nvim will wait for the handler to finish (this is required for function return values), but by default handlers are executed asynchronously. +Normally async handlers (`sync=False`, the default) are blocked while a +synchronous handler is running. This ensures that async handlers can call +requests without nvim confusing these requests with requests from a synchronous +handler. To execute an asynchronous handler even when other handlers are +running, add `allow_nested=True` to the decorator. The handler must then not +make synchronous nvim requests, but it can make asynchronous requests, i e +passing `async=True`. + You need to run `:UpdateRemotePlugins` in nvim for changes in the specifications to have effect. For details see `:help remote-plugin` in nvim. diff --git a/neovim/__init__.py b/neovim/__init__.py index d8ea0e9e..09e51553 100644 --- a/neovim/__init__.py +++ b/neovim/__init__.py @@ -126,11 +126,11 @@ def setup_logging(name): logging.root.addHandler(handler) level = logging.INFO if 'NVIM_PYTHON_LOG_LEVEL' in os.environ: - l = getattr(logging, - os.environ['NVIM_PYTHON_LOG_LEVEL'].strip(), - level) - if isinstance(l, int): - level = l + lvl = getattr(logging, + os.environ['NVIM_PYTHON_LOG_LEVEL'].strip(), + level) + if isinstance(lvl, int): + level = lvl logger.setLevel(level) diff --git a/neovim/api/nvim.py b/neovim/api/nvim.py index 9107aa0f..7886a240 100644 --- a/neovim/api/nvim.py +++ b/neovim/api/nvim.py @@ -2,7 +2,6 @@ import functools import os import sys - from traceback import format_stack from msgpack import ExtType @@ -298,9 +297,9 @@ def replace_termcodes(self, string, from_part=False, do_lt=True, return self.request('nvim_replace_termcodes', string, from_part, do_lt, special) - def out_write(self, msg): + def out_write(self, msg, **kwargs): """Print `msg` as a normal message.""" - return self.request('nvim_out_write', msg) + return self.request('nvim_out_write', msg, **kwargs) def err_write(self, msg, **kwargs): """Print `msg` as an error message.""" diff --git a/neovim/msgpack_rpc/session.py b/neovim/msgpack_rpc/session.py index de87c976..3e03a24f 100644 --- a/neovim/msgpack_rpc/session.py +++ b/neovim/msgpack_rpc/session.py @@ -1,7 +1,6 @@ """Synchronous msgpack-rpc session layer.""" import logging from collections import deque - from traceback import format_exc import greenlet diff --git a/neovim/plugin/decorators.py b/neovim/plugin/decorators.py index defceb3b..7015a955 100644 --- a/neovim/plugin/decorators.py +++ b/neovim/plugin/decorators.py @@ -43,7 +43,7 @@ def dec(f): def command(name, nargs=0, complete=None, range=None, count=None, bang=False, - register=False, sync=False, eval=None): + register=False, sync=False, allow_nested=False, eval=None): """Tag a function or plugin method as a Nvim command handler.""" def dec(f): f._nvim_rpc_method_name = 'command:{}'.format(name) @@ -73,17 +73,22 @@ def dec(f): if eval: opts['eval'] = eval + if not sync and allow_nested: + rpc_sync = "urgent" + else: + rpc_sync = sync + f._nvim_rpc_spec = { 'type': 'command', 'name': name, - 'sync': sync, + 'sync': rpc_sync, 'opts': opts } return f return dec -def autocmd(name, pattern='*', sync=False, eval=None): +def autocmd(name, pattern='*', sync=False, allow_nested=False, eval=None): """Tag a function or plugin method as a Nvim autocommand handler.""" def dec(f): f._nvim_rpc_method_name = 'autocmd:{}:{}'.format(name, pattern) @@ -98,17 +103,22 @@ def dec(f): if eval: opts['eval'] = eval + if not sync and allow_nested: + rpc_sync = "urgent" + else: + rpc_sync = sync + f._nvim_rpc_spec = { 'type': 'autocmd', 'name': name, - 'sync': sync, + 'sync': rpc_sync, 'opts': opts } return f return dec -def function(name, range=False, sync=False, eval=None): +def function(name, range=False, sync=False, allow_nested=False, eval=None): """Tag a function or plugin method as a Nvim function handler.""" def dec(f): f._nvim_rpc_method_name = 'function:{}'.format(name) @@ -124,10 +134,15 @@ def dec(f): if eval: opts['eval'] = eval + if not sync and allow_nested: + rpc_sync = "urgent" + else: + rpc_sync = sync + f._nvim_rpc_spec = { 'type': 'function', 'name': name, - 'sync': sync, + 'sync': rpc_sync, 'opts': opts } return f diff --git a/neovim/plugin/host.py b/neovim/plugin/host.py index d1b7ab55..6db26e55 100644 --- a/neovim/plugin/host.py +++ b/neovim/plugin/host.py @@ -1,12 +1,11 @@ """Implements a Nvim host for python plugins.""" -import functools import imp import inspect import logging import os import os.path import re - +from functools import partial from traceback import format_exc from . import script_host @@ -181,8 +180,8 @@ def predicate(o): if fn._nvim_prefix_plugin_path: method = '{}:{}'.format(plugin_path, method) - fn_wrapped = functools.partial(self._wrap_function, fn, - sync, decode, nvim_bind, method) + fn_wrapped = partial(self._wrap_function, fn, + sync, decode, nvim_bind, method) self._copy_attributes(fn, fn_wrapped) # register in the rpc handler dict if sync: