New upstream version 0.2.6
Víctor Cuadrado Juan
5 years ago
0 | Metadata-Version: 1.1 | |
0 | Metadata-Version: 2.1 | |
1 | 1 | Name: neovim |
2 | Version: 0.2.0 | |
2 | Version: 0.2.6 | |
3 | 3 | Summary: Python client to neovim |
4 | 4 | Home-page: http://github.com/neovim/python-client |
5 | 5 | Author: Thiago de Arruda |
6 | 6 | Author-email: tpadilha84@gmail.com |
7 | 7 | License: Apache |
8 | Download-URL: https://github.com/neovim/python-client/archive/0.2.0.tar.gz | |
8 | Download-URL: https://github.com/neovim/python-client/archive/0.2.6.tar.gz | |
9 | 9 | Description: UNKNOWN |
10 | 10 | Platform: UNKNOWN |
11 | Provides-Extra: pyuv |
0 | ### Python client to [Neovim](https://github.com/neovim/neovim) | |
0 | ### Pynvim: Python client to [Neovim](https://github.com/neovim/neovim) | |
1 | 1 | |
2 | 2 | [![Build Status](https://travis-ci.org/neovim/python-client.svg?branch=master)](https://travis-ci.org/neovim/python-client) |
3 | [![Documentation Status](https://readthedocs.org/projects/pynvim/badge/?version=latest)](http://pynvim.readthedocs.io/en/latest/?badge=latest) | |
3 | 4 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/neovim/python-client/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/neovim/python-client/?branch=master) |
4 | 5 | [![Code Coverage](https://scrutinizer-ci.com/g/neovim/python-client/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/neovim/python-client/?branch=master) |
5 | 6 | |
6 | Implements support for python plugins in Nvim. Also works as a library for | |
7 | Pynvim implements support for python plugins in Nvim. It also works as a library for | |
7 | 8 | connecting to and scripting Nvim processes through its msgpack-rpc API. |
8 | 9 | |
9 | 10 | #### Installation |
10 | 11 | |
11 | Supports python 2.7, and 3.3 or later. | |
12 | Supports python 2.7, and 3.4 or later. | |
12 | 13 | |
13 | 14 | ```sh |
14 | 15 | pip2 install neovim |
35 | 36 | |
36 | 37 | #### Python Plugin API |
37 | 38 | |
38 | Neovim has a new mechanism for defining plugins, as well as a number of | |
39 | extensions to the python API. The API extensions are accessible no matter if the | |
40 | traditional `:python` interface or the new mechanism is used, as discussed | |
41 | below. | |
39 | Pynvim supports python _remote plugins_ (via the language-agnostic Nvim rplugin | |
40 | interface), as well as _Vim plugins_ (via the `:python[3]` interface). Thus when | |
41 | pynvim is installed Neovim will report support for the `+python[3]` Vim feature. | |
42 | 42 | |
43 | * `vim.funcs` exposes vimscript functions (both builtin and global user defined | |
44 | functions) as a python namespace. For instance to set the value of a register | |
45 | ``` | |
46 | vim.funcs.setreg('0', ["some", "text"], 'l') | |
47 | ``` | |
43 | The rplugin interface allows plugins to handle vimL function calls as well as | |
44 | defining commands and autocommands, and such plugins can operate asynchronously | |
45 | without blocking nvim. For details on the new rplugin interface, | |
46 | see the [Remote Plugin](http://pynvim.readthedocs.io/en/latest/usage/remote-plugins.html) documentation. | |
48 | 47 | |
49 | * `vim.api` exposes nvim API methods. For instance to call `nvim_strwidth`, | |
50 | ``` | |
51 | result = vim.api.strwidth("some text") | |
52 | ``` | |
53 | Note the initial `nvim_` is not included. Also, object methods can be called | |
54 | directly on their object, | |
55 | ``` | |
56 | buf = vim.current.buffer | |
57 | len = buf.api.line_count() | |
58 | ``` | |
59 | calls `nvim_buf_line_count`. Alternatively msgpack requests can be invoked | |
60 | directly, | |
61 | ``` | |
62 | result = vim.request("nvim_strwith", "some text") | |
63 | len = vim.request("nvim_buf_line_count", buf) | |
64 | ``` | |
48 | Pynvim defines some extensions over the vim python API: | |
65 | 49 | |
66 | * The API is not thread-safe in general. However, `vim.async_call` allows a | |
67 | spawned thread to schedule code to be executed on the main thread. This method | |
68 | could also be called from `:python` or a synchronous request handler, to defer | |
69 | some execution that shouldn't block nvim. | |
70 | ``` | |
71 | :python vim.async_call(myfunc, args...) | |
50 | * Builtin and plugin vimL functions are available as `nvim.funcs` | |
51 | * API functions are available as `vim.api` and for objects such as `buffer.api` | |
52 | * Lua functions can be defined using `vim.exec_lua` and called with `vim.lua` | |
53 | * Support for thread-safety and async requests. | |
72 | 54 | |
73 | ``` | |
74 | Note that this code will still block the plugin host if it does long-running | |
75 | computations. Intensive computations should be done in a separate thread (or | |
76 | process), and `vim.async_call` can be used to send results back to nvim. | |
77 | ||
78 | * Some methods accept an `async` keyword argument: `vim.eval`, `vim.command`, | |
79 | `vim.request` as well as the `vim.funcs` and `vim.api` wrappers. When | |
80 | `async=True` is passed the client will not wait for nvim to complete the | |
81 | request (which also means that the return value is unavailable). | |
82 | ||
83 | #### Remote (new-style) plugins | |
84 | ||
85 | Neovim allows python plugins to be defined by placing python files or packages | |
86 | in `rplugin/python3/` (in a runtimepath folder). These follow the structure of | |
87 | this example: | |
88 | ||
89 | ```python | |
90 | import neovim | |
91 | ||
92 | @neovim.plugin | |
93 | class TestPlugin(object): | |
94 | ||
95 | def __init__(self, nvim): | |
96 | self.nvim = nvim | |
97 | ||
98 | @neovim.function("TestFunction", sync=True) | |
99 | def testfunction(self, args): | |
100 | return 3 | |
101 | ||
102 | @neovim.command("TestCommand", range='', nargs='*') | |
103 | def testcommand(self, args, range): | |
104 | self.nvim.current.line = ('Command with args: {}, range: {}' | |
105 | .format(args, range)) | |
106 | ||
107 | @neovim.autocmd('BufEnter', pattern='*.py', eval='expand("<afile>")', sync=True) | |
108 | def on_bufenter(self, filename): | |
109 | self.nvim.out_write("testplugin is in " + filename + "\n") | |
110 | ``` | |
111 | ||
112 | If `sync=True` is supplied nvim will wait for the handler to finish (this is | |
113 | required for function return values), but by default handlers are executed | |
114 | asynchronously. | |
115 | ||
116 | Normally async handlers (`sync=False`, the default) are blocked while a | |
117 | synchronous handler is running. This ensures that async handlers can call | |
118 | requests without nvim confusing these requests with requests from a synchronous | |
119 | handler. To execute an asynchronous handler even when other handlers are | |
120 | running, add `allow_nested=True` to the decorator. The handler must then not | |
121 | make synchronous nvim requests, but it can make asynchronous requests, i e | |
122 | passing `async=True`. | |
123 | ||
124 | You need to run `:UpdateRemotePlugins` in nvim for changes in the specifications | |
125 | to have effect. For details see `:help remote-plugin` in nvim. | |
55 | See the [Python Plugin API](http://pynvim.readthedocs.io/en/latest/usage/python-plugin-api.html) documentation for usage of this new functionality. | |
126 | 56 | |
127 | 57 | #### Development |
128 | 58 | |
131 | 61 | pip2 install . |
132 | 62 | pip3 install . |
133 | 63 | ``` |
134 | for the changes to have effect. Alternatively you could execute neovim | |
135 | with the `$PYTHONPATH` environment variable | |
136 | ``` | |
137 | PYTHONPATH=/path/to/python-client nvim | |
138 | ``` | |
139 | But note this is not completely reliable as installed packages can appear before | |
140 | `$PYTHONPATH` in the python search path. | |
141 | ||
142 | You need to rerun this command if you have changed the code, in order for nvim | |
143 | to use it for the plugin host. | |
144 | ||
145 | To run the tests execute | |
146 | ||
147 | ```sh | |
148 | nosetests | |
149 | ``` | |
150 | ||
151 | This will run the tests in an embedded instance of nvim. | |
152 | If you want to test a different version than `nvim` in `$PATH` use | |
153 | ```sh | |
154 | NVIM_CHILD_ARGV='["/path/to/nvim", "-u", "NONE", "--embed"]' nosetests | |
155 | ``` | |
156 | ||
157 | Alternatively, if you want to see the state of nvim, you could use | |
158 | ||
159 | ```sh | |
160 | export NVIM_LISTEN_ADDRESS=/tmp/nvimtest | |
161 | xterm -e "nvim -u NONE"& | |
162 | nosetests | |
163 | ``` | |
164 | ||
165 | But note you need to restart nvim every time you run the tests! Substitute your | |
166 | favorite terminal emulator for `xterm`. | |
167 | ||
168 | #### Troubleshooting | |
169 | ||
170 | You can run the plugin host in nvim with logging enabled to debug errors: | |
171 | ``` | |
172 | NVIM_PYTHON_LOG_FILE=logfile NVIM_PYTHON_LOG_LEVEL=DEBUG nvim | |
173 | ``` | |
174 | As more than one python host process might be started, the log filenames take | |
175 | the pattern `logfile_pyX_KIND` where `X` is the major python version (2 or 3) | |
176 | and `KIND` is either "rplugin" or "script" (for the `:python[3]` | |
177 | script interface). | |
178 | ||
179 | If the host cannot start at all, the error could be found in `~/.nvimlog` if | |
180 | `nvim` was compiled with logging. | |
64 | for the changes to have effect. For instructions of testing and troubleshooting, | |
65 | see the [development](http://pynvim.readthedocs.io/en/latest/development.html) documentation. | |
181 | 66 | |
182 | 67 | #### Usage through the python REPL |
183 | 68 |
20 | 20 | 'shutdown_hook', 'attach', 'setup_logging', 'ErrorResponse') |
21 | 21 | |
22 | 22 | |
23 | VERSION = Version(major=0, minor=2, patch=0, prerelease='') | |
23 | VERSION = Version(major=0, minor=2, patch=6, prerelease='') | |
24 | 24 | |
25 | 25 | |
26 | 26 | def start_host(session=None): |
95 | 95 | nvim = attach('socket', path=<path>) |
96 | 96 | nvim = attach('child', argv=<argv>) |
97 | 97 | nvim = attach('stdio') |
98 | ||
99 | When the session is not needed anymore, it is recommended to explicitly | |
100 | close it: | |
101 | nvim.close() | |
102 | It is also possible to use the session as a context mangager: | |
103 | with attach('socket', path=thepath) as nvim: | |
104 | print(nvim.funcs.getpid()) | |
105 | print(nvim.current.line) | |
106 | This will automatically close the session when you're done with it, or | |
107 | when an error occured. | |
108 | ||
109 | ||
98 | 110 | """ |
99 | 111 | session = (tcp_session(address, port) if session_type == 'tcp' else |
100 | 112 | socket_session(path) if session_type == 'socket' else |
0 | 0 | """API for working with a Nvim Buffer.""" |
1 | 1 | from .common import Remote |
2 | from ..compat import IS_PYTHON3 | |
2 | from ..compat import IS_PYTHON3, check_async | |
3 | 3 | |
4 | 4 | |
5 | 5 | __all__ = ('Buffer') |
97 | 97 | return Range(self, start, end) |
98 | 98 | |
99 | 99 | def add_highlight(self, hl_group, line, col_start=0, |
100 | col_end=-1, src_id=-1, async=None): | |
100 | col_end=-1, src_id=-1, async_=None, | |
101 | **kwargs): | |
101 | 102 | """Add a highlight to the buffer.""" |
102 | if async is None: | |
103 | async = (src_id != 0) | |
103 | async_ = check_async(async_, kwargs, src_id != 0) | |
104 | 104 | return self.request('nvim_buf_add_highlight', src_id, hl_group, |
105 | line, col_start, col_end, async=async) | |
106 | ||
107 | def clear_highlight(self, src_id, line_start=0, line_end=-1, async=True): | |
105 | line, col_start, col_end, async_=async_) | |
106 | ||
107 | def clear_highlight(self, src_id, line_start=0, line_end=-1, async_=None, | |
108 | **kwargs): | |
108 | 109 | """Clear highlights from the buffer.""" |
110 | async_ = check_async(async_, kwargs, True) | |
109 | 111 | self.request('nvim_buf_clear_highlight', src_id, |
110 | line_start, line_end, async=async) | |
112 | line_start, line_end, async_=async_) | |
113 | ||
114 | def update_highlights(self, src_id, hls, clear_start=0, clear_end=-1, | |
115 | clear=False, async_=True): | |
116 | """Add or update highlights in batch to avoid unnecessary redraws. | |
117 | ||
118 | A `src_id` must have been allocated prior to use of this function. Use | |
119 | for instance `nvim.new_highlight_source()` to get a src_id for your | |
120 | plugin. | |
121 | ||
122 | `hls` should be a list of highlight items. Each item should be a list | |
123 | or tuple on the form `("GroupName", linenr, col_start, col_end)` or | |
124 | `("GroupName", linenr)` to highlight an entire line. | |
125 | ||
126 | By default existing highlights are preserved. Specify a line range with | |
127 | clear_start and clear_end to replace highlights in this range. As a | |
128 | shorthand, use clear=True to clear the entire buffer before adding the | |
129 | new highlights. | |
130 | """ | |
131 | if clear and clear_start is None: | |
132 | clear_start = 0 | |
133 | lua = self._session._get_lua_private() | |
134 | lua.update_highlights(self, src_id, hls, clear_start, clear_end, | |
135 | async_=async_) | |
111 | 136 | |
112 | 137 | @property |
113 | 138 | def name(self): |
68 | 68 | It is used to provide a dict-like API to vim variables and options. |
69 | 69 | """ |
70 | 70 | |
71 | def __init__(self, obj, get_method, set_method=None, self_obj=None): | |
72 | """Initialize a RemoteMap with session, getter/setter and self_obj.""" | |
71 | def __init__(self, obj, get_method, set_method=None): | |
72 | """Initialize a RemoteMap with session, getter/setter.""" | |
73 | 73 | self._get = functools.partial(obj.request, get_method) |
74 | 74 | self._set = None |
75 | 75 | if set_method: |
117 | 117 | |
118 | 118 | For example, the 'windows' property of the `Nvim` class is a RemoteSequence |
119 | 119 | sequence instance, and the expression `nvim.windows[0]` is translated to |
120 | session.request('vim_get_windows')[0]. | |
121 | ||
122 | It can also receive an optional self_obj that will be passed as first | |
123 | argument of the request. For example, `tabpage.windows[0]` is translated | |
124 | to: session.request('tabpage_get_windows', tabpage_instance)[0]. | |
120 | session.request('nvim_list_wins')[0]. | |
125 | 121 | |
126 | 122 | One important detail about this class is that all methods will fetch the |
127 | 123 | sequence into a list and perform the necessary manipulation |
129 | 125 | """ |
130 | 126 | |
131 | 127 | def __init__(self, session, method): |
132 | """Initialize a RemoteSequence with session, method and self_obj.""" | |
128 | """Initialize a RemoteSequence with session, method.""" | |
133 | 129 | self._fetch = functools.partial(session.request, method) |
134 | 130 | |
135 | 131 | def __len__(self): |
0 | 0 | """Main Nvim interface.""" |
1 | import functools | |
2 | 1 | import os |
3 | 2 | import sys |
3 | from functools import partial | |
4 | 4 | from traceback import format_stack |
5 | 5 | |
6 | 6 | from msgpack import ExtType |
18 | 18 | |
19 | 19 | os_chdir = os.chdir |
20 | 20 | |
21 | lua_module = """ | |
22 | local a = vim.api | |
23 | local function update_highlights(buf, src_id, hls, clear_first, clear_end) | |
24 | if clear_first ~= nil then | |
25 | a.nvim_buf_clear_highlight(buf, src_id, clear_first, clear_end) | |
26 | end | |
27 | for _,hl in pairs(hls) do | |
28 | local group, line, col_start, col_end = unpack(hl) | |
29 | if col_start == nil then | |
30 | col_start = 0 | |
31 | end | |
32 | if col_end == nil then | |
33 | col_end = -1 | |
34 | end | |
35 | a.nvim_buf_add_highlight(buf, src_id, group, line, col_start, col_end) | |
36 | end | |
37 | end | |
38 | ||
39 | local chid = ... | |
40 | local mod = {update_highlights=update_highlights} | |
41 | _G["_pynvim_"..chid] = mod | |
42 | """ | |
43 | ||
21 | 44 | |
22 | 45 | class Nvim(object): |
23 | 46 | |
35 | 58 | `SubClass.from_nvim(nvim)` where `SubClass` is a subclass of `Nvim`, which |
36 | 59 | is useful for having multiple `Nvim` objects that behave differently |
37 | 60 | without one affecting the other. |
61 | ||
62 | When this library is used on python3.4+, asyncio event loop is guaranteed | |
63 | to be used. It is available as the "loop" attribute of this class. Note | |
64 | that asyncio callbacks cannot make blocking requests, which includes | |
65 | accessing state-dependent attributes. They should instead schedule another | |
66 | callback using nvim.async_call, which will not have this restriction. | |
38 | 67 | """ |
39 | 68 | |
40 | 69 | @classmethod |
85 | 114 | self.current = Current(self) |
86 | 115 | self.session = CompatibilitySession(self) |
87 | 116 | self.funcs = Funcs(self) |
117 | self.lua = LuaFuncs(self) | |
88 | 118 | self.error = NvimError |
89 | 119 | self._decode = decode |
90 | 120 | self._err_cb = err_cb |
121 | ||
122 | # only on python3.4+ we expose asyncio | |
123 | if IS_PYTHON3 and os.name != 'nt': | |
124 | self.loop = self._session.loop._loop | |
91 | 125 | |
92 | 126 | def _from_nvim(self, obj, decode=None): |
93 | 127 | if decode is None: |
104 | 138 | return ExtType(*obj.code_data) |
105 | 139 | return obj |
106 | 140 | |
141 | def _get_lua_private(self): | |
142 | if not getattr(self._session, "_has_lua", False): | |
143 | self.exec_lua(lua_module, self.channel_id) | |
144 | self._session._has_lua = True | |
145 | return getattr(self.lua, "_pynvim_{}".format(self.channel_id)) | |
146 | ||
107 | 147 | def request(self, name, *args, **kwargs): |
108 | 148 | r"""Send an API request or notification to nvim. |
109 | 149 | |
111 | 151 | functions have python wrapper functions. The `api` object can |
112 | 152 | be also be used to call API functions as methods: |
113 | 153 | |
114 | vim.api.err_write('ERROR\n', async=True) | |
154 | vim.api.err_write('ERROR\n', async_=True) | |
115 | 155 | vim.current.buffer.api.get_mark('.') |
116 | 156 | |
117 | 157 | is equivalent to |
118 | 158 | |
119 | vim.request('nvim_err_write', 'ERROR\n', async=True) | |
159 | vim.request('nvim_err_write', 'ERROR\n', async_=True) | |
120 | 160 | vim.request('nvim_buf_get_mark', vim.current.buffer, '.') |
121 | 161 | |
122 | 162 | |
123 | Normally a blocking request will be sent. If the `async` flag is | |
163 | Normally a blocking request will be sent. If the `async_` flag is | |
124 | 164 | present and True, a asynchronous notification is sent instead. This |
125 | 165 | will never block, and the return value or error is ignored. |
126 | 166 | """ |
179 | 219 | """Stop the event loop being started with `run_loop`.""" |
180 | 220 | self._session.stop() |
181 | 221 | |
222 | def close(self): | |
223 | """Close the nvim session and release its resources.""" | |
224 | self._session.close() | |
225 | ||
226 | def __enter__(self): | |
227 | """Enter nvim session as a context manager.""" | |
228 | return self | |
229 | ||
230 | def __exit__(self, *exc_info): | |
231 | """Exit nvim session as a context manager. | |
232 | ||
233 | Closes the event loop. | |
234 | """ | |
235 | self.close() | |
236 | ||
182 | 237 | def with_decode(self, decode=True): |
183 | 238 | """Initialize a new Nvim instance.""" |
184 | 239 | return Nvim(self._session, self.channel_id, |
226 | 281 | def call(self, name, *args, **kwargs): |
227 | 282 | """Call a vimscript function.""" |
228 | 283 | return self.request('nvim_call_function', name, args, **kwargs) |
284 | ||
285 | def exec_lua(self, code, *args, **kwargs): | |
286 | """Execute lua code. | |
287 | ||
288 | Additional parameters are available as `...` inside the lua chunk. | |
289 | Only statements are executed. To evaluate an expression, prefix it | |
290 | with `return`: `return my_function(...)` | |
291 | ||
292 | There is a shorthand syntax to call lua functions with arguments: | |
293 | ||
294 | nvim.lua.func(1,2) | |
295 | nvim.lua.mymod.myfunction(data, async_=True) | |
296 | ||
297 | is equivalent to | |
298 | ||
299 | nvim.exec_lua("return func(...)", 1, 2) | |
300 | nvim.exec_lua("mymod.myfunction(...)", data, async_=True) | |
301 | ||
302 | Note that with `async_=True` there is no return value. | |
303 | """ | |
304 | return self.request('nvim_execute_lua', code, args, **kwargs) | |
229 | 305 | |
230 | 306 | def strwidth(self, string): |
231 | 307 | """Return the number of display cells `string` occupies. |
438 | 514 | self._nvim = nvim |
439 | 515 | |
440 | 516 | def __getattr__(self, name): |
441 | return functools.partial(self._nvim.call, name) | |
517 | return partial(self._nvim.call, name) | |
518 | ||
519 | ||
520 | class LuaFuncs(object): | |
521 | ||
522 | """Wrapper to allow lua functions to be called like python methods.""" | |
523 | ||
524 | def __init__(self, nvim, name=""): | |
525 | self._nvim = nvim | |
526 | self.name = name | |
527 | ||
528 | def __getattr__(self, name): | |
529 | """Return wrapper to named api method.""" | |
530 | prefix = self.name + "." if self.name else "" | |
531 | return LuaFuncs(self._nvim, prefix + name) | |
532 | ||
533 | def __call__(self, *args, **kwargs): | |
534 | # first new function after keyword rename, be a bit noisy | |
535 | if 'async' in kwargs: | |
536 | raise ValueError('"async" argument is not allowed. ' | |
537 | 'Use "async_" instead.') | |
538 | async_ = kwargs.get('async_', False) | |
539 | pattern = "return {}(...)" if not async_ else "{}(...)" | |
540 | code = pattern.format(self.name) | |
541 | return self._nvim.exec_lua(code, *args, **kwargs) | |
442 | 542 | |
443 | 543 | |
444 | 544 | class NvimError(Exception): |
0 | 0 | """Code for supporting compatibility across python versions.""" |
1 | 1 | |
2 | 2 | import sys |
3 | import warnings | |
3 | 4 | from imp import find_module as original_find_module |
4 | 5 | |
5 | 6 | |
35 | 36 | unicode_errors_default = 'strict' |
36 | 37 | |
37 | 38 | NUM_TYPES = (int, long, float) |
39 | ||
40 | ||
41 | def check_async(async_, kwargs, default): | |
42 | """Return a value of 'async' in kwargs or default when async_ is None. | |
43 | ||
44 | This helper function exists for backward compatibility (See #274). | |
45 | It shows a warning message when 'async' in kwargs is used to note users. | |
46 | """ | |
47 | if async_ is not None: | |
48 | return async_ | |
49 | elif 'async' in kwargs: | |
50 | warnings.warn( | |
51 | '"async" attribute is deprecated. Use "async_" instead.', | |
52 | DeprecationWarning, | |
53 | ) | |
54 | return kwargs.pop('async') | |
55 | else: | |
56 | return default |
26 | 26 | 1: self._on_response, |
27 | 27 | 2: self._on_notification |
28 | 28 | } |
29 | self.loop = msgpack_stream.loop | |
29 | 30 | |
30 | 31 | def threadsafe_call(self, fn): |
31 | 32 | """Wrapper around `MsgpackStream.threadsafe_call`.""" |
68 | 69 | def stop(self): |
69 | 70 | """Stop the event loop.""" |
70 | 71 | self._msgpack_stream.stop() |
72 | ||
73 | def close(self): | |
74 | """Close the event loop.""" | |
75 | self._msgpack_stream.close() | |
71 | 76 | |
72 | 77 | def _on_message(self, msg): |
73 | 78 | try: |
1 | 1 | |
2 | 2 | Tries to use pyuv as a backend, falling back to the asyncio implementation. |
3 | 3 | """ |
4 | try: | |
5 | # libuv is fully implemented in C, use it when available | |
6 | from .uv import UvEventLoop | |
7 | EventLoop = UvEventLoop | |
8 | except ImportError: | |
9 | # asyncio(trollius on python 2) is pure python and should be more portable | |
10 | # across python implementations | |
4 | ||
5 | import os | |
6 | ||
7 | from ...compat import IS_PYTHON3 | |
8 | ||
9 | # on python3 we only support asyncio, as we expose it to plugins | |
10 | if IS_PYTHON3 and os.name != 'nt': | |
11 | 11 | from .asyncio import AsyncioEventLoop |
12 | 12 | EventLoop = AsyncioEventLoop |
13 | else: | |
14 | try: | |
15 | # libuv is fully implemented in C, use it when available | |
16 | from .uv import UvEventLoop | |
17 | EventLoop = UvEventLoop | |
18 | except ImportError: | |
19 | # asyncio(trollius on python 2) is pure python and should be more | |
20 | # portable across python implementations | |
21 | from .asyncio import AsyncioEventLoop | |
22 | EventLoop = AsyncioEventLoop | |
13 | 23 | |
14 | 24 | |
15 | 25 | __all__ = ('EventLoop') |
38 | 38 | def connection_made(self, transport): |
39 | 39 | """Used to signal `asyncio.Protocol` of a successful connection.""" |
40 | 40 | self._transport = transport |
41 | self._raw_transport = transport | |
41 | 42 | if isinstance(transport, asyncio.SubprocessTransport): |
42 | 43 | self._transport = transport.get_pipe_transport(0) |
43 | 44 | |
73 | 74 | self._loop = loop_cls() |
74 | 75 | self._queued_data = deque() |
75 | 76 | self._fact = lambda: self |
77 | self._raw_transport = None | |
76 | 78 | |
77 | 79 | def _connect_tcp(self, address, port): |
78 | 80 | coroutine = self._loop.create_connection(self._fact, address, port) |
111 | 113 | def _stop(self): |
112 | 114 | self._loop.stop() |
113 | 115 | |
116 | def _close(self): | |
117 | if self._raw_transport is not None: | |
118 | self._raw_transport.close() | |
119 | self._loop.close() | |
120 | ||
114 | 121 | def _threadsafe_call(self, fn): |
115 | 122 | self._loop.call_soon_threadsafe(fn) |
116 | 123 |
29 | 29 | - `_init()`: Implementation-specific initialization |
30 | 30 | - `_connect_tcp(address, port)`: connect to Nvim using tcp/ip |
31 | 31 | - `_connect_socket(path)`: Same as tcp, but use a UNIX domain socket or |
32 | or named pipe. | |
32 | named pipe. | |
33 | 33 | - `_connect_stdio()`: Use stdin/stdout as the connection to Nvim |
34 | 34 | - `_connect_child(argv)`: Use the argument vector `argv` to spawn an |
35 | 35 | embedded Nvim that has it's stdin/stdout connected to the event loop. |
84 | 84 | self._on_data = None |
85 | 85 | self._error = None |
86 | 86 | self._init() |
87 | getattr(self, '_connect_{}'.format(transport_type))(*args) | |
87 | try: | |
88 | getattr(self, '_connect_{}'.format(transport_type))(*args) | |
89 | except Exception as e: | |
90 | self.close() | |
91 | raise e | |
88 | 92 | self._start_reading() |
89 | 93 | |
90 | 94 | def connect_tcp(self, address, port): |
147 | 151 | self._stop() |
148 | 152 | debug('Stopped event loop') |
149 | 153 | |
154 | def close(self): | |
155 | """Stop the event loop.""" | |
156 | self._close() | |
157 | debug('Closed event loop') | |
158 | ||
150 | 159 | def _on_signal(self, signum): |
151 | 160 | msg = 'Received {}'.format(self._signames[signum]) |
152 | 161 | debug(msg) |
96 | 96 | def _stop(self): |
97 | 97 | self._loop.stop() |
98 | 98 | |
99 | def _close(self): | |
100 | pass | |
101 | ||
99 | 102 | def _threadsafe_call(self, fn): |
100 | 103 | self._callbacks.append(fn) |
101 | 104 | self._async.send() |
18 | 18 | |
19 | 19 | def __init__(self, event_loop): |
20 | 20 | """Wrap `event_loop` on a msgpack-aware interface.""" |
21 | self._event_loop = event_loop | |
22 | self._packer = Packer(unicode_errors=unicode_errors_default) | |
21 | self.loop = event_loop | |
22 | self._packer = Packer(encoding='utf-8', | |
23 | unicode_errors=unicode_errors_default) | |
23 | 24 | self._unpacker = Unpacker() |
24 | 25 | self._message_cb = None |
25 | 26 | |
26 | 27 | def threadsafe_call(self, fn): |
27 | 28 | """Wrapper around `BaseEventLoop.threadsafe_call`.""" |
28 | self._event_loop.threadsafe_call(fn) | |
29 | self.loop.threadsafe_call(fn) | |
29 | 30 | |
30 | 31 | def send(self, msg): |
31 | 32 | """Queue `msg` for sending to Nvim.""" |
32 | 33 | debug('sent %s', msg) |
33 | self._event_loop.send(self._packer.pack(msg)) | |
34 | self.loop.send(self._packer.pack(msg)) | |
34 | 35 | |
35 | 36 | def run(self, message_cb): |
36 | 37 | """Run the event loop to receive messages from Nvim. |
39 | 40 | a message has been successfully parsed from the input stream. |
40 | 41 | """ |
41 | 42 | self._message_cb = message_cb |
42 | self._event_loop.run(self._on_data) | |
43 | self.loop.run(self._on_data) | |
43 | 44 | self._message_cb = None |
44 | 45 | |
45 | 46 | def stop(self): |
46 | 47 | """Stop the event loop.""" |
47 | self._event_loop.stop() | |
48 | self.loop.stop() | |
49 | ||
50 | def close(self): | |
51 | """Close the event loop.""" | |
52 | self.loop.close() | |
48 | 53 | |
49 | 54 | def _on_data(self, data): |
50 | 55 | self._unpacker.feed(data) |
3 | 3 | from traceback import format_exc |
4 | 4 | |
5 | 5 | import greenlet |
6 | ||
7 | from ..compat import check_async | |
6 | 8 | |
7 | 9 | logger = logging.getLogger(__name__) |
8 | 10 | error, debug, info, warn = (logger.error, logger.debug, logger.info, |
25 | 27 | self._pending_messages = deque() |
26 | 28 | self._is_running = False |
27 | 29 | self._setup_exception = None |
30 | self.loop = async_session.loop | |
28 | 31 | |
29 | 32 | def threadsafe_call(self, fn, *args, **kwargs): |
30 | 33 | """Wrapper around `AsyncSession.threadsafe_call`.""" |
70 | 73 | - Run the loop until the response is available |
71 | 74 | - Put requests/notifications received while waiting into a queue |
72 | 75 | |
73 | If the `async` flag is present and True, a asynchronous notification is | |
74 | sent instead. This will never block, and the return value or error is | |
75 | ignored. | |
76 | If the `async_` flag is present and True, a asynchronous notification | |
77 | is sent instead. This will never block, and the return value or error | |
78 | is ignored. | |
76 | 79 | """ |
77 | async = kwargs.pop('async', False) | |
78 | if async: | |
80 | async_ = check_async(kwargs.pop('async_', None), kwargs, False) | |
81 | if async_: | |
79 | 82 | self._async_session.notify(method, args) |
80 | 83 | return |
81 | 84 | |
138 | 141 | def stop(self): |
139 | 142 | """Stop the event loop.""" |
140 | 143 | self._async_session.stop() |
144 | ||
145 | def close(self): | |
146 | """Close the event loop.""" | |
147 | self._async_session.close() | |
141 | 148 | |
142 | 149 | def _yielding_request(self, method, args): |
143 | 150 | gr = greenlet.getcurrent() |
13 | 13 | def plugin(cls): |
14 | 14 | """Tag a class as a plugin. |
15 | 15 | |
16 | This decorator is required to make the a class methods discoverable by the | |
16 | This decorator is required to make the class methods discoverable by the | |
17 | 17 | plugin_load method of the host. |
18 | 18 | """ |
19 | 19 | cls._nvim_plugin = True |
54 | 54 | |
55 | 55 | if range is not None: |
56 | 56 | opts['range'] = '' if range is True else str(range) |
57 | elif count: | |
57 | elif count is not None: | |
58 | 58 | opts['count'] = count |
59 | 59 | |
60 | 60 | if bang: |
45 | 45 | self._decode_default = IS_PYTHON3 |
46 | 46 | |
47 | 47 | def _on_async_err(self, msg): |
48 | self.nvim.err_write(msg, async=True) | |
48 | self.nvim.err_write(msg, async_=True) | |
49 | 49 | |
50 | 50 | def start(self, plugins): |
51 | 51 | """Start listening for msgpack-rpc requests and notifications.""" |
22 | 22 | |
23 | 23 | if sys.version_info >= (3, 4): |
24 | 24 | from importlib.machinery import PathFinder |
25 | ||
26 | PYTHON_SUBDIR = 'python3' | |
27 | else: | |
28 | PYTHON_SUBDIR = 'python2' | |
25 | 29 | |
26 | 30 | |
27 | 31 | @plugin |
39 | 43 | exec('import sys', self.module.__dict__) |
40 | 44 | self.legacy_vim = LegacyVim.from_nvim(nvim) |
41 | 45 | sys.modules['vim'] = self.legacy_vim |
46 | ||
47 | # Handle DirChanged. #296 | |
48 | nvim.command( | |
49 | 'au DirChanged * call rpcnotify({}, "python_chdir", v:event.cwd)' | |
50 | .format(nvim.channel_id), async_=True) | |
51 | # XXX: Avoid race condition. | |
52 | # https://github.com/neovim/python-client/pull/296#issuecomment-358970531 | |
53 | # TODO(bfredl): when host initialization has been refactored, | |
54 | # to make __init__ safe again, the following should work: | |
55 | # os.chdir(nvim.eval('getcwd()', async_=False)) | |
56 | nvim.command('call rpcnotify({}, "python_chdir", getcwd())' | |
57 | .format(nvim.channel_id), async_=True) | |
42 | 58 | |
43 | 59 | def setup(self, nvim): |
44 | 60 | """Setup import hooks and global streams. |
148 | 164 | """Handle the `pyeval` vim function.""" |
149 | 165 | return eval(expr, self.module.__dict__) |
150 | 166 | |
167 | @rpc_export('python_chdir', sync=False) | |
168 | def python_chdir(self, cwd): | |
169 | """Handle working directory changes.""" | |
170 | os.chdir(cwd) | |
171 | ||
151 | 172 | def _set_current_range(self, start, stop): |
152 | 173 | current = self.legacy_vim.current |
153 | 174 | current.range = current.buffer.range(start, stop) |
167 | 188 | if IS_PYTHON3: |
168 | 189 | num_types = (int, float) |
169 | 190 | else: |
170 | num_types = (int, long, float) | |
191 | num_types = (int, long, float) # noqa: F821 | |
171 | 192 | |
172 | 193 | |
173 | 194 | def num_to_str(obj): |
235 | 256 | |
236 | 257 | def discover_runtime_directories(nvim): |
237 | 258 | rv = [] |
238 | for path in nvim.list_runtime_paths(): | |
239 | if not os.path.exists(path): | |
259 | for rtp in nvim.list_runtime_paths(): | |
260 | if not os.path.exists(rtp): | |
240 | 261 | continue |
241 | path1 = os.path.join(path, 'pythonx') | |
242 | if IS_PYTHON3: | |
243 | path2 = os.path.join(path, 'python3') | |
244 | else: | |
245 | path2 = os.path.join(path, 'python2') | |
246 | if os.path.exists(path1): | |
247 | rv.append(path1) | |
248 | if os.path.exists(path2): | |
249 | rv.append(path2) | |
262 | for subdir in ['pythonx', PYTHON_SUBDIR]: | |
263 | path = os.path.join(rtp, subdir) | |
264 | if os.path.exists(path): | |
265 | rv.append(path) | |
250 | 266 | return rv |
0 | from .util import Version | |
1 | ||
2 | VERSION = Version(major=0, minor=1, patch=14, prerelease="dev") | |
3 | ||
4 | __all__ = ('VERSION',) |
0 | Metadata-Version: 1.1 | |
0 | Metadata-Version: 2.1 | |
1 | 1 | Name: neovim |
2 | Version: 0.2.0 | |
2 | Version: 0.2.6 | |
3 | 3 | Summary: Python client to neovim |
4 | 4 | Home-page: http://github.com/neovim/python-client |
5 | 5 | Author: Thiago de Arruda |
6 | 6 | Author-email: tpadilha84@gmail.com |
7 | 7 | License: Apache |
8 | Download-URL: https://github.com/neovim/python-client/archive/0.2.0.tar.gz | |
8 | Download-URL: https://github.com/neovim/python-client/archive/0.2.6.tar.gz | |
9 | 9 | Description: UNKNOWN |
10 | 10 | Platform: UNKNOWN |
11 | Provides-Extra: pyuv |
5 | 5 | neovim/__init__.py |
6 | 6 | neovim/compat.py |
7 | 7 | neovim/util.py |
8 | neovim/version.py | |
9 | 8 | neovim.egg-info/PKG-INFO |
10 | 9 | neovim.egg-info/SOURCES.txt |
11 | 10 | neovim.egg-info/dependency_links.txt |
32 | 31 | neovim/plugin/script_host.py |
33 | 32 | test/test_buffer.py |
34 | 33 | test/test_client_rpc.py |
35 | test/test_common.py | |
36 | 34 | test/test_concurrency.py |
35 | test/test_decorators.py | |
37 | 36 | test/test_events.py |
38 | 37 | test/test_tabpage.py |
39 | 38 | test/test_vim.py |
0 | 0 | [flake8] |
1 | ignore = D211,E731,F821,D401 | |
1 | ignore = D211,E731,D401 | |
2 | 2 | |
3 | 3 | [egg_info] |
4 | 4 | tag_build = |
4 | 4 | from setuptools import setup |
5 | 5 | |
6 | 6 | install_requires = [ |
7 | 'msgpack-python>=0.4.0', | |
7 | 'msgpack>=0.5.0', | |
8 | 8 | ] |
9 | ||
10 | tests_require = [ | |
11 | 'pytest>=3.4.0', | |
12 | ] | |
13 | ||
9 | 14 | extras_require = { |
10 | 15 | 'pyuv': ['pyuv>=1.0.0'], |
11 | 16 | } |
21 | 26 | install_requires.append('greenlet') |
22 | 27 | |
23 | 28 | setup(name='neovim', |
24 | version='0.2.0', | |
29 | version='0.2.6', | |
25 | 30 | description='Python client to neovim', |
26 | 31 | url='http://github.com/neovim/python-client', |
27 | download_url='https://github.com/neovim/python-client/archive/0.2.0.tar.gz', | |
32 | download_url='https://github.com/neovim/python-client/archive/0.2.6.tar.gz', | |
28 | 33 | author='Thiago de Arruda', |
29 | 34 | author_email='tpadilha84@gmail.com', |
30 | 35 | license='Apache', |
31 | 36 | packages=['neovim', 'neovim.api', 'neovim.msgpack_rpc', |
32 | 37 | 'neovim.msgpack_rpc.event_loop', 'neovim.plugin'], |
33 | 38 | install_requires=install_requires, |
39 | tests_require=tests_require, | |
34 | 40 | extras_require=extras_require, |
35 | 41 | zip_safe=False) |
0 | 0 | import os |
1 | from nose.tools import with_setup, eq_ as eq, ok_ as ok | |
2 | from test_common import vim, cleanup | |
3 | 1 | |
4 | 2 | from neovim.compat import IS_PYTHON3 |
5 | 3 | |
6 | 4 | |
7 | @with_setup(setup=cleanup) | |
8 | def test_get_length(): | |
9 | eq(len(vim.current.buffer), 1) | |
5 | def test_get_length(vim): | |
6 | assert len(vim.current.buffer) == 1 | |
10 | 7 | vim.current.buffer.append('line') |
11 | eq(len(vim.current.buffer), 2) | |
8 | assert len(vim.current.buffer) == 2 | |
12 | 9 | vim.current.buffer.append('line') |
13 | eq(len(vim.current.buffer), 3) | |
10 | assert len(vim.current.buffer) == 3 | |
14 | 11 | vim.current.buffer[-1] = None |
15 | eq(len(vim.current.buffer), 2) | |
12 | assert len(vim.current.buffer) == 2 | |
16 | 13 | vim.current.buffer[-1] = None |
17 | 14 | vim.current.buffer[-1] = None |
18 | 15 | # There's always at least one line |
19 | eq(len(vim.current.buffer), 1) | |
16 | assert len(vim.current.buffer) == 1 | |
20 | 17 | |
21 | 18 | |
22 | @with_setup(setup=cleanup) | |
23 | def test_get_set_del_line(): | |
24 | eq(vim.current.buffer[0], '') | |
19 | def test_get_set_del_line(vim): | |
20 | assert vim.current.buffer[0] == '' | |
25 | 21 | vim.current.buffer[0] = 'line1' |
26 | eq(vim.current.buffer[0], 'line1') | |
22 | assert vim.current.buffer[0] == 'line1' | |
27 | 23 | vim.current.buffer[0] = 'line2' |
28 | eq(vim.current.buffer[0], 'line2') | |
24 | assert vim.current.buffer[0] == 'line2' | |
29 | 25 | vim.current.buffer[0] = None |
30 | eq(vim.current.buffer[0], '') | |
26 | assert vim.current.buffer[0] == '' | |
31 | 27 | # __delitem__ |
32 | 28 | vim.current.buffer[:] = ['line1', 'line2', 'line3'] |
33 | eq(vim.current.buffer[2], 'line3') | |
29 | assert vim.current.buffer[2] == 'line3' | |
34 | 30 | del vim.current.buffer[0] |
35 | eq(vim.current.buffer[0], 'line2') | |
36 | eq(vim.current.buffer[1], 'line3') | |
31 | assert vim.current.buffer[0] == 'line2' | |
32 | assert vim.current.buffer[1] == 'line3' | |
37 | 33 | del vim.current.buffer[-1] |
38 | eq(vim.current.buffer[0], 'line2') | |
39 | eq(len(vim.current.buffer), 1) | |
34 | assert vim.current.buffer[0] == 'line2' | |
35 | assert len(vim.current.buffer) == 1 | |
40 | 36 | |
41 | 37 | |
42 | @with_setup(setup=cleanup) | |
43 | def test_get_set_del_slice(): | |
44 | eq(vim.current.buffer[:], ['']) | |
38 | def test_get_set_del_slice(vim): | |
39 | assert vim.current.buffer[:] == [''] | |
45 | 40 | # Replace buffer |
46 | 41 | vim.current.buffer[:] = ['a', 'b', 'c'] |
47 | eq(vim.current.buffer[:], ['a', 'b', 'c']) | |
48 | eq(vim.current.buffer[1:], ['b', 'c']) | |
49 | eq(vim.current.buffer[1:2], ['b']) | |
50 | eq(vim.current.buffer[1:1], []) | |
51 | eq(vim.current.buffer[:-1], ['a', 'b']) | |
52 | eq(vim.current.buffer[1:-1], ['b']) | |
53 | eq(vim.current.buffer[-2:], ['b', 'c']) | |
42 | assert vim.current.buffer[:] == ['a', 'b', 'c'] | |
43 | assert vim.current.buffer[1:] == ['b', 'c'] | |
44 | assert vim.current.buffer[1:2] == ['b'] | |
45 | assert vim.current.buffer[1:1] == [] | |
46 | assert vim.current.buffer[:-1] == ['a', 'b'] | |
47 | assert vim.current.buffer[1:-1] == ['b'] | |
48 | assert vim.current.buffer[-2:] == ['b', 'c'] | |
54 | 49 | vim.current.buffer[1:2] = ['a', 'b', 'c'] |
55 | eq(vim.current.buffer[:], ['a', 'a', 'b', 'c', 'c']) | |
50 | assert vim.current.buffer[:] == ['a', 'a', 'b', 'c', 'c'] | |
56 | 51 | vim.current.buffer[-1:] = ['a', 'b', 'c'] |
57 | eq(vim.current.buffer[:], ['a', 'a', 'b', 'c', 'a', 'b', 'c']) | |
52 | assert vim.current.buffer[:] == ['a', 'a', 'b', 'c', 'a', 'b', 'c'] | |
58 | 53 | vim.current.buffer[:-3] = None |
59 | eq(vim.current.buffer[:], ['a', 'b', 'c']) | |
54 | assert vim.current.buffer[:] == ['a', 'b', 'c'] | |
60 | 55 | vim.current.buffer[:] = None |
61 | eq(vim.current.buffer[:], ['']) | |
56 | assert vim.current.buffer[:] == [''] | |
62 | 57 | # __delitem__ |
63 | 58 | vim.current.buffer[:] = ['a', 'b', 'c'] |
64 | 59 | del vim.current.buffer[:] |
65 | eq(vim.current.buffer[:], ['']) | |
60 | assert vim.current.buffer[:] == [''] | |
66 | 61 | vim.current.buffer[:] = ['a', 'b', 'c'] |
67 | 62 | del vim.current.buffer[:1] |
68 | eq(vim.current.buffer[:], ['b', 'c']) | |
63 | assert vim.current.buffer[:] == ['b', 'c'] | |
69 | 64 | del vim.current.buffer[:-1] |
70 | eq(vim.current.buffer[:], ['c']) | |
65 | assert vim.current.buffer[:] == ['c'] | |
71 | 66 | |
72 | 67 | |
73 | @with_setup(setup=cleanup) | |
74 | def test_vars(): | |
68 | def test_vars(vim): | |
75 | 69 | vim.current.buffer.vars['python'] = [1, 2, {'3': 1}] |
76 | eq(vim.current.buffer.vars['python'], [1, 2, {'3': 1}]) | |
77 | eq(vim.eval('b:python'), [1, 2, {'3': 1}]) | |
70 | assert vim.current.buffer.vars['python'] == [1, 2, {'3': 1}] | |
71 | assert vim.eval('b:python') == [1, 2, {'3': 1}] | |
78 | 72 | |
79 | 73 | |
80 | @with_setup(setup=cleanup) | |
81 | def test_api(): | |
74 | def test_api(vim): | |
82 | 75 | vim.current.buffer.api.set_var('myvar', 'thetext') |
83 | eq(vim.current.buffer.api.get_var('myvar'), 'thetext') | |
84 | eq(vim.eval('b:myvar'), 'thetext') | |
85 | vim.current.buffer.api.set_lines(0,-1,True,['alpha', 'beta']) | |
86 | eq(vim.current.buffer.api.get_lines(0,-1,True), ['alpha', 'beta']) | |
87 | eq(vim.current.buffer[:], ['alpha', 'beta']) | |
76 | assert vim.current.buffer.api.get_var('myvar') == 'thetext' | |
77 | assert vim.eval('b:myvar') == 'thetext' | |
78 | vim.current.buffer.api.set_lines(0, -1, True, ['alpha', 'beta']) | |
79 | assert vim.current.buffer.api.get_lines(0, -1, True) == ['alpha', 'beta'] | |
80 | assert vim.current.buffer[:] == ['alpha', 'beta'] | |
88 | 81 | |
89 | 82 | |
90 | @with_setup(setup=cleanup) | |
91 | def test_options(): | |
92 | eq(vim.current.buffer.options['shiftwidth'], 8) | |
83 | def test_options(vim): | |
84 | assert vim.current.buffer.options['shiftwidth'] == 8 | |
93 | 85 | vim.current.buffer.options['shiftwidth'] = 4 |
94 | eq(vim.current.buffer.options['shiftwidth'], 4) | |
86 | assert vim.current.buffer.options['shiftwidth'] == 4 | |
95 | 87 | # global-local option |
96 | 88 | vim.current.buffer.options['define'] = 'test' |
97 | eq(vim.current.buffer.options['define'], 'test') | |
89 | assert vim.current.buffer.options['define'] == 'test' | |
98 | 90 | # Doesn't change the global value |
99 | eq(vim.options['define'], '^\s*#\s*define') | |
91 | assert vim.options['define'] == '^\s*#\s*define' | |
100 | 92 | |
101 | 93 | |
102 | @with_setup(setup=cleanup) | |
103 | def test_number(): | |
94 | def test_number(vim): | |
104 | 95 | curnum = vim.current.buffer.number |
105 | 96 | vim.command('new') |
106 | eq(vim.current.buffer.number, curnum + 1) | |
97 | assert vim.current.buffer.number == curnum + 1 | |
107 | 98 | vim.command('new') |
108 | eq(vim.current.buffer.number, curnum + 2) | |
99 | assert vim.current.buffer.number == curnum + 2 | |
109 | 100 | |
110 | 101 | |
111 | @with_setup(setup=cleanup) | |
112 | def test_name(): | |
102 | def test_name(vim): | |
113 | 103 | vim.command('new') |
114 | eq(vim.current.buffer.name, '') | |
104 | assert vim.current.buffer.name == '' | |
115 | 105 | new_name = vim.eval('resolve(tempname())') |
116 | 106 | vim.current.buffer.name = new_name |
117 | eq(vim.current.buffer.name, new_name) | |
107 | assert vim.current.buffer.name == new_name | |
118 | 108 | vim.command('silent w!') |
119 | ok(os.path.isfile(new_name)) | |
109 | assert os.path.isfile(new_name) | |
120 | 110 | os.unlink(new_name) |
121 | 111 | |
122 | 112 | |
123 | @with_setup(setup=cleanup) | |
124 | def test_valid(): | |
113 | def test_valid(vim): | |
125 | 114 | vim.command('new') |
126 | 115 | buffer = vim.current.buffer |
127 | ok(buffer.valid) | |
116 | assert buffer.valid | |
128 | 117 | vim.command('bw!') |
129 | ok(not buffer.valid) | |
118 | assert not buffer.valid | |
130 | 119 | |
131 | 120 | |
132 | @with_setup(setup=cleanup) | |
133 | def test_append(): | |
121 | def test_append(vim): | |
134 | 122 | vim.current.buffer.append('a') |
135 | eq(vim.current.buffer[:], ['', 'a']) | |
123 | assert vim.current.buffer[:] == ['', 'a'] | |
136 | 124 | vim.current.buffer.append('b', 0) |
137 | eq(vim.current.buffer[:], ['b', '', 'a']) | |
125 | assert vim.current.buffer[:] == ['b', '', 'a'] | |
138 | 126 | vim.current.buffer.append(['c', 'd']) |
139 | eq(vim.current.buffer[:], ['b', '', 'a', 'c', 'd']) | |
127 | assert vim.current.buffer[:] == ['b', '', 'a', 'c', 'd'] | |
140 | 128 | vim.current.buffer.append(['c', 'd'], 2) |
141 | eq(vim.current.buffer[:], ['b', '', 'c', 'd', 'a', 'c', 'd']) | |
129 | assert vim.current.buffer[:] == ['b', '', 'c', 'd', 'a', 'c', 'd'] | |
142 | 130 | vim.current.buffer.append(b'bytes') |
143 | eq(vim.current.buffer[:], ['b', '', 'c', 'd', 'a', 'c', 'd', 'bytes']) | |
131 | assert vim.current.buffer[:] == ['b', '', 'c', 'd', 'a', 'c', 'd', 'bytes'] | |
144 | 132 | |
145 | 133 | |
146 | @with_setup(setup=cleanup) | |
147 | def test_mark(): | |
134 | def test_mark(vim): | |
148 | 135 | vim.current.buffer.append(['a', 'bit of', 'text']) |
149 | 136 | vim.current.window.cursor = [3, 4] |
150 | 137 | vim.command('mark V') |
151 | eq(vim.current.buffer.mark('V'), [3, 0]) | |
138 | assert vim.current.buffer.mark('V') == [3, 0] | |
152 | 139 | |
153 | @with_setup(setup=cleanup) | |
154 | def test_invalid_utf8(): | |
140 | ||
141 | def test_invalid_utf8(vim): | |
155 | 142 | vim.command('normal "=printf("%c", 0xFF)\np') |
156 | eq(vim.eval("char2nr(getline(1))"), 0xFF) | |
143 | assert vim.eval("char2nr(getline(1))") == 0xFF | |
157 | 144 | |
158 | eq(vim.current.buffer[:], ['\udcff'] if IS_PYTHON3 else ['\xff']) | |
145 | assert vim.current.buffer[:] == ['\udcff'] if IS_PYTHON3 else ['\xff'] | |
159 | 146 | vim.current.line += 'x' |
160 | eq(vim.eval("getline(1)", decode=False), b'\xFFx') | |
161 | eq(vim.current.buffer[:], ['\udcffx'] if IS_PYTHON3 else ['\xffx']) | |
147 | assert vim.eval("getline(1)", decode=False) == b'\xFFx' | |
148 | assert vim.current.buffer[:] == ['\udcffx'] if IS_PYTHON3 else ['\xffx'] | |
162 | 149 | |
163 | @with_setup(setup=cleanup) | |
164 | def test_get_exceptions(): | |
150 | ||
151 | def test_get_exceptions(vim): | |
165 | 152 | try: |
166 | 153 | vim.current.buffer.options['invalid-option'] |
167 | ok(False) | |
154 | assert False | |
168 | 155 | except vim.error: |
169 | 156 | pass |
170 | 157 | |
171 | @with_setup(setup=cleanup) | |
172 | def test_contains(): | |
173 | ok(vim.current.buffer in vim.buffers) | |
174 | ||
175 | @with_setup(setup=cleanup) | |
176 | def test_set_items_for_range(): | |
158 | def test_set_items_for_range(vim): | |
177 | 159 | vim.current.buffer[:] = ['a', 'b', 'c', 'd', 'e'] |
178 | 160 | r = vim.current.buffer.range(1, 3) |
179 | 161 | r[1:3] = ['foo']*3 |
180 | eq(vim.current.buffer[:], ['a', 'foo', 'foo', 'foo', 'd', 'e']) | |
162 | assert vim.current.buffer[:] == ['a', 'foo', 'foo', 'foo', 'd', 'e'] | |
163 | ||
164 | # NB: we can't easily test the effect of this. But at least run the lua | |
165 | # function sync, so we know it runs without runtime error with simple args. | |
166 | def test_update_highlights(vim): | |
167 | vim.current.buffer[:] = ['a', 'b', 'c'] | |
168 | src_id = vim.new_highlight_source() | |
169 | vim.current.buffer.update_highlights(src_id, [["Comment", 0, 0, -1], ("String", 1, 0, 1)], clear=True, async_=False) |
0 | 0 | # -*- coding: utf-8 -*- |
1 | from nose.tools import with_setup, eq_ as eq | |
2 | from test_common import vim, cleanup | |
3 | ||
4 | cid = vim.channel_id | |
5 | 1 | |
6 | 2 | |
7 | @with_setup(setup=cleanup) | |
8 | def test_call_and_reply(): | |
3 | def test_call_and_reply(vim): | |
4 | cid = vim.channel_id | |
9 | 5 | def setup_cb(): |
10 | 6 | cmd = 'let g:result = rpcrequest(%d, "client-call", 1, 2, 3)' % cid |
11 | 7 | vim.command(cmd) |
12 | eq(vim.vars['result'], [4, 5, 6]) | |
8 | assert vim.vars['result'] == [4, 5, 6] | |
13 | 9 | vim.stop_loop() |
14 | 10 | |
15 | 11 | def request_cb(name, args): |
16 | eq(name, 'client-call') | |
17 | eq(args, [1, 2, 3]) | |
12 | assert name == 'client-call' | |
13 | assert args == [1, 2, 3] | |
18 | 14 | return [4, 5, 6] |
19 | 15 | |
20 | 16 | vim.run_loop(request_cb, None, setup_cb) |
21 | 17 | |
22 | 18 | |
23 | @with_setup(setup=cleanup) | |
24 | def test_call_api_before_reply(): | |
19 | def test_call_api_before_reply(vim): | |
20 | cid = vim.channel_id | |
25 | 21 | def setup_cb(): |
26 | 22 | cmd = 'let g:result = rpcrequest(%d, "client-call2", 1, 2, 3)' % cid |
27 | 23 | vim.command(cmd) |
28 | eq(vim.vars['result'], [7, 8, 9]) | |
24 | assert vim.vars['result'] == [7, 8, 9] | |
29 | 25 | vim.stop_loop() |
30 | 26 | |
31 | 27 | def request_cb(name, args): |
34 | 30 | |
35 | 31 | vim.run_loop(request_cb, None, setup_cb) |
36 | 32 | |
37 | @with_setup(setup=cleanup) | |
38 | def test_async_call(): | |
33 | def test_async_call(vim): | |
39 | 34 | |
40 | 35 | def request_cb(name, args): |
41 | 36 | if name == "test-event": |
43 | 38 | vim.stop_loop() |
44 | 39 | |
45 | 40 | # this would have dead-locked if not async |
46 | vim.funcs.rpcrequest(vim.channel_id, "test-event", async=True) | |
41 | vim.funcs.rpcrequest(vim.channel_id, "test-event", async_=True) | |
47 | 42 | vim.run_loop(request_cb, None, None) |
48 | eq(vim.vars['result'], 17) | |
43 | assert vim.vars['result'] == 17 | |
49 | 44 | |
50 | 45 | |
51 | @with_setup(setup=cleanup) | |
52 | def test_recursion(): | |
46 | def test_recursion(vim): | |
47 | cid = vim.channel_id | |
53 | 48 | def setup_cb(): |
54 | 49 | vim.vars['result1'] = 0 |
55 | 50 | vim.vars['result2'] = 0 |
57 | 52 | vim.vars['result4'] = 0 |
58 | 53 | cmd = 'let g:result1 = rpcrequest(%d, "call", %d)' % (cid, 2,) |
59 | 54 | vim.command(cmd) |
60 | eq(vim.vars['result1'], 4) | |
61 | eq(vim.vars['result2'], 8) | |
62 | eq(vim.vars['result3'], 16) | |
63 | eq(vim.vars['result4'], 32) | |
55 | assert vim.vars['result1'] == 4 | |
56 | assert vim.vars['result2'] == 8 | |
57 | assert vim.vars['result3'] == 16 | |
58 | assert vim.vars['result4'] == 32 | |
64 | 59 | vim.stop_loop() |
65 | 60 | |
66 | 61 | def request_cb(name, args): |
0 | import json | |
1 | import os | |
2 | import sys | |
3 | ||
4 | import neovim | |
5 | ||
6 | from nose.tools import eq_ as eq | |
7 | ||
8 | neovim.setup_logging("test") | |
9 | ||
10 | child_argv = os.environ.get('NVIM_CHILD_ARGV') | |
11 | listen_address = os.environ.get('NVIM_LISTEN_ADDRESS') | |
12 | if child_argv is None and listen_address is None: | |
13 | child_argv = '["nvim", "-u", "NONE", "--embed"]' | |
14 | ||
15 | if child_argv is not None: | |
16 | vim = neovim.attach('child', argv=json.loads(child_argv)) | |
17 | else: | |
18 | vim = neovim.attach('socket', path=listen_address) | |
19 | ||
20 | cleanup_func = ''':function BeforeEachTest() | |
21 | set all& | |
22 | redir => groups | |
23 | silent augroup | |
24 | redir END | |
25 | for group in split(groups) | |
26 | exe 'augroup '.group | |
27 | autocmd! | |
28 | augroup END | |
29 | endfor | |
30 | autocmd! | |
31 | tabnew | |
32 | let curbufnum = eval(bufnr('%')) | |
33 | redir => buflist | |
34 | silent ls! | |
35 | redir END | |
36 | let bufnums = [] | |
37 | for buf in split(buflist, '\\n') | |
38 | let bufnum = eval(split(buf, '[ u]')[0]) | |
39 | if bufnum != curbufnum | |
40 | call add(bufnums, bufnum) | |
41 | endif | |
42 | endfor | |
43 | if len(bufnums) > 0 | |
44 | exe 'silent bwipeout! '.join(bufnums, ' ') | |
45 | endif | |
46 | silent tabonly | |
47 | for k in keys(g:) | |
48 | exe 'unlet g:'.k | |
49 | endfor | |
50 | filetype plugin indent off | |
51 | mapclear | |
52 | mapclear! | |
53 | abclear | |
54 | comclear | |
55 | endfunction | |
56 | ''' | |
57 | ||
58 | vim.input(cleanup_func) | |
59 | ||
60 | ||
61 | def cleanup(): | |
62 | # cleanup nvim | |
63 | vim.command('call BeforeEachTest()') | |
64 | eq(len(vim.tabpages), 1) | |
65 | eq(len(vim.windows), 1) | |
66 | eq(len(vim.buffers), 1) |
0 | from nose.tools import with_setup, eq_ as eq | |
1 | from test_common import vim, cleanup | |
2 | 0 | from threading import Timer |
3 | 1 | |
4 | @with_setup(setup=cleanup) | |
5 | def test_interrupt_from_another_thread(): | |
2 | ||
3 | def test_interrupt_from_another_thread(vim): | |
6 | 4 | timer = Timer(0.5, lambda: vim.async_call(lambda: vim.stop_loop())) |
7 | 5 | timer.start() |
8 | eq(vim.next_message(), None) | |
6 | assert vim.next_message() == None | |
9 | 7 | |
10 | @with_setup(setup=cleanup) | |
11 | def test_exception_in_threadsafe_call(): | |
8 | ||
9 | def test_exception_in_threadsafe_call(vim): | |
12 | 10 | # an exception in a threadsafe_call shouldn't crash the entire host |
13 | 11 | msgs = [] |
14 | 12 | vim.async_call(lambda: [vim.eval("3"), undefined_variable]) |
15 | 13 | timer = Timer(0.5, lambda: vim.async_call(lambda: vim.stop_loop())) |
16 | 14 | timer.start() |
17 | 15 | vim.run_loop(None, None, err_cb=msgs.append) |
18 | eq(len(msgs), 1) | |
16 | assert len(msgs) == 1 | |
19 | 17 | msgs[0].index('NameError') |
20 | 18 | msgs[0].index('undefined_variable') |
0 | from neovim.plugin.decorators import command | |
1 | ||
2 | ||
3 | def test_command_count(): | |
4 | def function(): | |
5 | "A dummy function to decorate." | |
6 | return | |
7 | ||
8 | # ensure absence with default value of None | |
9 | decorated = command('test')(function) | |
10 | assert 'count' not in decorated._nvim_rpc_spec['opts'] | |
11 | ||
12 | # ensure absence with explicit value of None | |
13 | count_value = None | |
14 | decorated = command('test', count=count_value)(function) | |
15 | assert 'count' not in decorated._nvim_rpc_spec['opts'] | |
16 | ||
17 | # Test presesence with value of 0 | |
18 | count_value = 0 | |
19 | decorated = command('test', count=count_value)(function) | |
20 | assert 'count' in decorated._nvim_rpc_spec['opts'] | |
21 | assert decorated._nvim_rpc_spec['opts']['count'] == count_value | |
22 | ||
23 | # Test presence with value of 1 | |
24 | count_value = 1 | |
25 | decorated = command('test', count=count_value)(function) | |
26 | assert 'count' in decorated._nvim_rpc_spec['opts'] | |
27 | assert decorated._nvim_rpc_spec['opts']['count'] == count_value |
0 | 0 | # -*- coding: utf-8 -*- |
1 | from nose.tools import with_setup, eq_ as eq | |
2 | from test_common import vim, cleanup | |
3 | 1 | |
4 | 2 | |
5 | @with_setup(setup=cleanup) | |
6 | def test_receiving_events(): | |
3 | def test_receiving_events(vim): | |
7 | 4 | vim.command('call rpcnotify(%d, "test-event", 1, 2, 3)' % vim.channel_id) |
8 | 5 | event = vim.next_message() |
9 | eq(event[1], 'test-event') | |
10 | eq(event[2], [1, 2, 3]) | |
6 | assert event[1] == 'test-event' | |
7 | assert event[2] == [1, 2, 3] | |
11 | 8 | vim.command('au FileType python call rpcnotify(%d, "py!", bufnr("$"))' % |
12 | 9 | vim.channel_id) |
13 | 10 | vim.command('set filetype=python') |
14 | 11 | event = vim.next_message() |
15 | eq(event[1], 'py!') | |
16 | eq(event[2], [vim.current.buffer.number]) | |
12 | assert event[1] == 'py!' | |
13 | assert event[2] == [vim.current.buffer.number] | |
17 | 14 | |
18 | @with_setup(setup=cleanup) | |
19 | def test_sending_notify(): | |
15 | ||
16 | def test_sending_notify(vim): | |
20 | 17 | # notify after notify |
21 | vim.command("let g:test = 3", async=True) | |
18 | vim.command("let g:test = 3", async_=True) | |
22 | 19 | cmd = 'call rpcnotify(%d, "test-event", g:test)' % vim.channel_id |
23 | vim.command(cmd, async=True) | |
20 | vim.command(cmd, async_=True) | |
24 | 21 | event = vim.next_message() |
25 | eq(event[1], 'test-event') | |
26 | eq(event[2], [3]) | |
22 | assert event[1] == 'test-event' | |
23 | assert event[2] == [3] | |
27 | 24 | |
28 | 25 | # request after notify |
29 | vim.command("let g:data = 'xyz'", async=True) | |
30 | eq(vim.eval('g:data'), 'xyz') | |
26 | vim.command("let g:data = 'xyz'", async_=True) | |
27 | assert vim.eval('g:data') == 'xyz' | |
31 | 28 | |
32 | 29 | |
33 | @with_setup(setup=cleanup) | |
34 | def test_broadcast(): | |
30 | def test_broadcast(vim): | |
35 | 31 | vim.subscribe('event2') |
36 | 32 | vim.command('call rpcnotify(0, "event1", 1, 2, 3)') |
37 | 33 | vim.command('call rpcnotify(0, "event2", 4, 5, 6)') |
38 | 34 | vim.command('call rpcnotify(0, "event2", 7, 8, 9)') |
39 | 35 | event = vim.next_message() |
40 | eq(event[1], 'event2') | |
41 | eq(event[2], [4, 5, 6]) | |
36 | assert event[1] == 'event2' | |
37 | assert event[2] == [4, 5, 6] | |
42 | 38 | event = vim.next_message() |
43 | eq(event[1], 'event2') | |
44 | eq(event[2], [7, 8, 9]) | |
39 | assert event[1] == 'event2' | |
40 | assert event[2] == [7, 8, 9] | |
45 | 41 | vim.unsubscribe('event2') |
46 | 42 | vim.subscribe('event1') |
47 | 43 | vim.command('call rpcnotify(0, "event2", 10, 11, 12)') |
48 | 44 | vim.command('call rpcnotify(0, "event1", 13, 14, 15)') |
49 | 45 | msg = vim.next_message() |
50 | eq(msg[1], 'event1') | |
51 | eq(msg[2], [13, 14, 15]) | |
46 | assert msg[1] == 'event1' | |
47 | assert msg[2] == [13, 14, 15] |
0 | import os | |
1 | from nose.tools import with_setup, eq_ as eq, ok_ as ok | |
2 | from test_common import vim, cleanup | |
0 | def test_windows(vim): | |
1 | vim.command('tabnew') | |
2 | vim.command('vsplit') | |
3 | assert list(vim.tabpages[0].windows) == [vim.windows[0]] | |
4 | assert list(vim.tabpages[1].windows) == [vim.windows[1], vim.windows[2]] | |
5 | assert vim.tabpages[1].window == vim.windows[1] | |
6 | vim.current.window = vim.windows[2] | |
7 | assert vim.tabpages[1].window == vim.windows[2] | |
3 | 8 | |
4 | 9 | |
5 | @with_setup(setup=cleanup) | |
6 | def test_windows(): | |
7 | vim.command('tabnew') | |
8 | vim.command('vsplit') | |
9 | eq(list(vim.tabpages[0].windows), [vim.windows[0]]) | |
10 | eq(list(vim.tabpages[1].windows), [vim.windows[1], vim.windows[2]]) | |
11 | eq(vim.tabpages[1].window, vim.windows[1]) | |
12 | vim.current.window = vim.windows[2] | |
13 | eq(vim.tabpages[1].window, vim.windows[2]) | |
10 | def test_vars(vim): | |
11 | vim.current.tabpage.vars['python'] = [1, 2, {'3': 1}] | |
12 | assert vim.current.tabpage.vars['python'] == [1, 2, {'3': 1}] | |
13 | assert vim.eval('t:python') == [1, 2, {'3': 1}] | |
14 | 14 | |
15 | 15 | |
16 | @with_setup(setup=cleanup) | |
17 | def test_vars(): | |
18 | vim.current.tabpage.vars['python'] = [1, 2, {'3': 1}] | |
19 | eq(vim.current.tabpage.vars['python'], [1, 2, {'3': 1}]) | |
20 | eq(vim.eval('t:python'), [1, 2, {'3': 1}]) | |
16 | def test_valid(vim): | |
17 | vim.command('tabnew') | |
18 | tabpage = vim.tabpages[1] | |
19 | assert tabpage.valid | |
20 | vim.command('tabclose') | |
21 | assert not tabpage.valid | |
21 | 22 | |
22 | 23 | |
23 | @with_setup(setup=cleanup) | |
24 | def test_valid(): | |
25 | vim.command('tabnew') | |
26 | tabpage = vim.tabpages[1] | |
27 | ok(tabpage.valid) | |
28 | vim.command('tabclose') | |
29 | ok(not tabpage.valid) | |
30 | ||
31 | ||
32 | @with_setup(setup=cleanup) | |
33 | def test_number(): | |
24 | def test_number(vim): | |
34 | 25 | curnum = vim.current.tabpage.number |
35 | 26 | vim.command('tabnew') |
36 | eq(vim.current.tabpage.number, curnum + 1) | |
27 | assert vim.current.tabpage.number == curnum + 1 | |
37 | 28 | vim.command('tabnew') |
38 | eq(vim.current.tabpage.number, curnum + 2) | |
29 | assert vim.current.tabpage.number == curnum + 2 |
0 | 0 | # -*- coding: utf-8 -*- |
1 | import os, tempfile | |
2 | from nose.tools import with_setup, eq_ as eq, ok_ as ok | |
3 | from test_common import vim, cleanup | |
1 | import os | |
2 | import sys | |
3 | import tempfile | |
4 | 4 | |
5 | def source(code): | |
5 | ||
6 | def source(vim, code): | |
6 | 7 | fd, fname = tempfile.mkstemp() |
7 | with os.fdopen(fd,'w') as f: | |
8 | with os.fdopen(fd, 'w') as f: | |
8 | 9 | f.write(code) |
9 | vim.command('source '+fname) | |
10 | vim.command('source ' + fname) | |
10 | 11 | os.unlink(fname) |
11 | 12 | |
12 | 13 | |
13 | @with_setup(setup=cleanup) | |
14 | def test_command(): | |
14 | def test_command(vim): | |
15 | 15 | fname = tempfile.mkstemp()[1] |
16 | 16 | vim.command('new') |
17 | vim.command('edit %s' % fname) | |
17 | vim.command('edit {}'.format(fname)) | |
18 | 18 | # skip the "press return" state, which does not handle deferred calls |
19 | 19 | vim.input('\r') |
20 | 20 | vim.command('normal itesting\npython\napi') |
21 | 21 | vim.command('w') |
22 | ok(os.path.isfile(fname)) | |
22 | assert os.path.isfile(fname) | |
23 | 23 | with open(fname) as f: |
24 | eq(f.read(), 'testing\npython\napi\n') | |
24 | assert f.read() == 'testing\npython\napi\n' | |
25 | 25 | os.unlink(fname) |
26 | 26 | |
27 | 27 | |
28 | @with_setup | |
29 | def test_command_output(): | |
30 | eq(vim.command_output('echo test'), 'test') | |
28 | def test_command_output(vim): | |
29 | assert vim.command_output('echo "test"') == 'test' | |
31 | 30 | |
32 | 31 | |
33 | @with_setup(setup=cleanup) | |
34 | def test_eval(): | |
32 | def test_eval(vim): | |
35 | 33 | vim.command('let g:v1 = "a"') |
36 | 34 | vim.command('let g:v2 = [1, 2, {"v3": 3}]') |
37 | eq(vim.eval('g:'), {'v1': 'a', 'v2': [1, 2, {'v3': 3}]}) | |
35 | assert vim.eval('g:'), {'v1': 'a', 'v2': [1, 2 == {'v3': 3}]} | |
38 | 36 | |
39 | @with_setup(setup=cleanup) | |
40 | def test_call(): | |
41 | eq(vim.funcs.join(['first', 'last'], ', '), 'first, last') | |
42 | source(""" | |
37 | ||
38 | def test_call(vim): | |
39 | assert vim.funcs.join(['first', 'last'], ', '), 'first == last' | |
40 | source(vim, """ | |
43 | 41 | function! Testfun(a,b) |
44 | 42 | return string(a:a).":".a:b |
45 | 43 | endfunction |
46 | 44 | """) |
47 | eq(vim.funcs.Testfun(3, 'alpha'), '3:alpha') | |
45 | assert vim.funcs.Testfun(3, 'alpha') == '3:alpha' | |
48 | 46 | |
49 | 47 | |
50 | @with_setup(setup=cleanup) | |
51 | def test_api(): | |
48 | def test_api(vim): | |
52 | 49 | vim.api.command('let g:var = 3') |
53 | eq(vim.api.eval('g:var'), 3) | |
50 | assert vim.api.eval('g:var') == 3 | |
54 | 51 | |
55 | 52 | |
56 | @with_setup(setup=cleanup) | |
57 | def test_strwidth(): | |
58 | eq(vim.strwidth('abc'), 3) | |
53 | def test_strwidth(vim): | |
54 | assert vim.strwidth('abc') == 3 | |
59 | 55 | # 6 + (neovim) |
60 | 56 | # 19 * 2 (each japanese character occupies two cells) |
61 | eq(vim.strwidth('neovimのデザインかなりまともなのになってる。'), 44) | |
57 | assert vim.strwidth('neovimのデザインかなりまともなのになってる。') == 44 | |
62 | 58 | |
63 | @with_setup(setup=cleanup) | |
64 | def test_chdir(): | |
59 | ||
60 | def test_chdir(vim): | |
65 | 61 | pwd = vim.eval('getcwd()') |
66 | 62 | root = os.path.abspath(os.sep) |
67 | 63 | # We can chdir to '/' on Windows, but then the pwd will be the root drive |
68 | 64 | vim.chdir('/') |
69 | eq(vim.eval('getcwd()'), root) | |
65 | assert vim.eval('getcwd()') == root | |
70 | 66 | vim.chdir(pwd) |
71 | eq(vim.eval('getcwd()'), pwd) | |
67 | assert vim.eval('getcwd()') == pwd | |
72 | 68 | |
73 | 69 | |
74 | @with_setup(setup=cleanup) | |
75 | def test_current_line(): | |
76 | eq(vim.current.line, '') | |
70 | def test_current_line(vim): | |
71 | assert vim.current.line == '' | |
77 | 72 | vim.current.line = 'abc' |
78 | eq(vim.current.line, 'abc') | |
73 | assert vim.current.line == 'abc' | |
79 | 74 | |
80 | 75 | |
81 | @with_setup(setup=cleanup) | |
82 | def test_vars(): | |
76 | def test_vars(vim): | |
83 | 77 | vim.vars['python'] = [1, 2, {'3': 1}] |
84 | eq(vim.vars['python'], [1, 2, {'3': 1}]) | |
85 | eq(vim.eval('g:python'), [1, 2, {'3': 1}]) | |
78 | assert vim.vars['python'], [1, 2 == {'3': 1}] | |
79 | assert vim.eval('g:python'), [1, 2 == {'3': 1}] | |
86 | 80 | |
87 | 81 | |
88 | @with_setup(setup=cleanup) | |
89 | def test_options(): | |
90 | eq(vim.options['listchars'], 'tab:> ,trail:-,nbsp:+') | |
82 | def test_options(vim): | |
83 | assert vim.options['listchars'] == 'tab:> ,trail:-,nbsp:+' | |
91 | 84 | vim.options['listchars'] = 'tab:xy' |
92 | eq(vim.options['listchars'], 'tab:xy') | |
85 | assert vim.options['listchars'] == 'tab:xy' | |
93 | 86 | |
94 | 87 | |
95 | @with_setup(setup=cleanup) | |
96 | def test_buffers(): | |
88 | def test_buffers(vim): | |
97 | 89 | buffers = [] |
98 | 90 | |
99 | 91 | # Number of elements |
100 | eq(len(vim.buffers), 1) | |
92 | assert len(vim.buffers) == 1 | |
101 | 93 | |
102 | 94 | # Indexing (by buffer number) |
103 | eq(vim.buffers[vim.current.buffer.number], vim.current.buffer) | |
95 | assert vim.buffers[vim.current.buffer.number] == vim.current.buffer | |
104 | 96 | |
105 | 97 | buffers.append(vim.current.buffer) |
106 | 98 | vim.command('new') |
107 | eq(len(vim.buffers), 2) | |
99 | assert len(vim.buffers) == 2 | |
108 | 100 | buffers.append(vim.current.buffer) |
109 | eq(vim.buffers[vim.current.buffer.number], vim.current.buffer) | |
101 | assert vim.buffers[vim.current.buffer.number] == vim.current.buffer | |
110 | 102 | vim.current.buffer = buffers[0] |
111 | eq(vim.buffers[vim.current.buffer.number], buffers[0]) | |
103 | assert vim.buffers[vim.current.buffer.number] == buffers[0] | |
112 | 104 | |
113 | 105 | # Membership test |
114 | ok(buffers[0] in vim.buffers) | |
115 | ok(buffers[1] in vim.buffers) | |
116 | ok({} not in vim.buffers) | |
106 | assert buffers[0] in vim.buffers | |
107 | assert buffers[1] in vim.buffers | |
108 | assert {} not in vim.buffers | |
117 | 109 | |
118 | 110 | # Iteration |
119 | eq(buffers, list(vim.buffers)) | |
111 | assert buffers == list(vim.buffers) | |
120 | 112 | |
121 | 113 | |
122 | @with_setup(setup=cleanup) | |
123 | def test_windows(): | |
124 | eq(len(vim.windows), 1) | |
125 | eq(vim.windows[0], vim.current.window) | |
114 | def test_windows(vim): | |
115 | assert len(vim.windows) == 1 | |
116 | assert vim.windows[0] == vim.current.window | |
126 | 117 | vim.command('vsplit') |
127 | 118 | vim.command('split') |
128 | eq(len(vim.windows), 3) | |
129 | eq(vim.windows[0], vim.current.window) | |
119 | assert len(vim.windows) == 3 | |
120 | assert vim.windows[0] == vim.current.window | |
130 | 121 | vim.current.window = vim.windows[1] |
131 | eq(vim.windows[1], vim.current.window) | |
122 | assert vim.windows[1] == vim.current.window | |
132 | 123 | |
133 | 124 | |
134 | @with_setup(setup=cleanup) | |
135 | def test_tabpages(): | |
136 | eq(len(vim.tabpages), 1) | |
137 | eq(vim.tabpages[0], vim.current.tabpage) | |
125 | def test_tabpages(vim): | |
126 | assert len(vim.tabpages) == 1 | |
127 | assert vim.tabpages[0] == vim.current.tabpage | |
138 | 128 | vim.command('tabnew') |
139 | eq(len(vim.tabpages), 2) | |
140 | eq(len(vim.windows), 2) | |
141 | eq(vim.windows[1], vim.current.window) | |
142 | eq(vim.tabpages[1], vim.current.tabpage) | |
129 | assert len(vim.tabpages) == 2 | |
130 | assert len(vim.windows) == 2 | |
131 | assert vim.windows[1] == vim.current.window | |
132 | assert vim.tabpages[1] == vim.current.tabpage | |
143 | 133 | vim.current.window = vim.windows[0] |
144 | 134 | # Switching window also switches tabpages if necessary(this probably |
145 | 135 | # isn't the current behavior, but compatibility will be handled in the |
146 | 136 | # python client with an optional parameter) |
147 | eq(vim.tabpages[0], vim.current.tabpage) | |
148 | eq(vim.windows[0], vim.current.window) | |
137 | assert vim.tabpages[0] == vim.current.tabpage | |
138 | assert vim.windows[0] == vim.current.window | |
149 | 139 | vim.current.tabpage = vim.tabpages[1] |
150 | eq(vim.tabpages[1], vim.current.tabpage) | |
151 | eq(vim.windows[1], vim.current.window) | |
140 | assert vim.tabpages[1] == vim.current.tabpage | |
141 | assert vim.windows[1] == vim.current.window | |
152 | 142 | |
153 | 143 | |
154 | @with_setup(setup=cleanup) | |
155 | def test_hash(): | |
144 | def test_hash(vim): | |
156 | 145 | d = {} |
157 | 146 | d[vim.current.buffer] = "alpha" |
158 | eq(d[vim.current.buffer], "alpha") | |
147 | assert d[vim.current.buffer] == 'alpha' | |
159 | 148 | vim.command('new') |
160 | 149 | d[vim.current.buffer] = "beta" |
161 | eq(d[vim.current.buffer], "beta") | |
150 | assert d[vim.current.buffer] == 'beta' | |
162 | 151 | vim.command('winc w') |
163 | eq(d[vim.current.buffer], "alpha") | |
152 | assert d[vim.current.buffer] == 'alpha' | |
164 | 153 | vim.command('winc w') |
165 | eq(d[vim.current.buffer], "beta") | |
154 | assert d[vim.current.buffer] == 'beta' | |
155 | ||
156 | ||
157 | def test_cwd(vim, tmpdir): | |
158 | pycmd = 'python' | |
159 | if sys.version_info >= (3, 0): | |
160 | pycmd = 'python3' | |
161 | ||
162 | vim.command('{} import os'.format(pycmd)) | |
163 | cwd_before = vim.command_output('{} print(os.getcwd())'.format(pycmd)) | |
164 | ||
165 | vim.command('cd {}'.format(tmpdir.strpath)) | |
166 | cwd_vim = vim.command_output('pwd') | |
167 | cwd_python = vim.command_output('{} print(os.getcwd())'.format(pycmd)) | |
168 | assert cwd_python == cwd_vim | |
169 | assert cwd_python != cwd_before | |
170 | ||
171 | lua_code = """ | |
172 | local a = vim.api | |
173 | local y = ... | |
174 | function pynvimtest_func(x) | |
175 | return x+y | |
176 | end | |
177 | ||
178 | local function setbuf(buf,lines) | |
179 | a.nvim_buf_set_lines(buf, 0, -1, true, lines) | |
180 | end | |
181 | ||
182 | ||
183 | local function getbuf(buf) | |
184 | return a.nvim_buf_line_count(buf) | |
185 | end | |
186 | ||
187 | pynvimtest = {setbuf=setbuf,getbuf=getbuf} | |
188 | ||
189 | return "eggspam" | |
190 | """ | |
191 | ||
192 | def test_lua(vim): | |
193 | assert vim.exec_lua(lua_code, 7) == "eggspam" | |
194 | assert vim.lua.pynvimtest_func(3) == 10 | |
195 | testmod = vim.lua.pynvimtest | |
196 | buf = vim.current.buffer | |
197 | testmod.setbuf(buf, ["a", "b", "c", "d"], async_=True) | |
198 | assert testmod.getbuf(buf) == 4 |
0 | import os | |
1 | from nose.tools import with_setup, eq_ as eq, ok_ as ok | |
2 | from test_common import vim, cleanup | |
0 | def test_buffer(vim): | |
1 | assert vim.current.buffer == vim.windows[0].buffer | |
2 | vim.command('new') | |
3 | vim.current.window = vim.windows[1] | |
4 | assert vim.current.buffer == vim.windows[1].buffer | |
5 | assert vim.windows[0].buffer != vim.windows[1].buffer | |
3 | 6 | |
4 | 7 | |
5 | @with_setup(setup=cleanup) | |
6 | def test_buffer(): | |
7 | eq(vim.current.buffer, vim.windows[0].buffer) | |
8 | vim.command('new') | |
9 | vim.current.window = vim.windows[1] | |
10 | eq(vim.current.buffer, vim.windows[1].buffer) | |
11 | ok(vim.windows[0].buffer != vim.windows[1].buffer) | |
8 | def test_cursor(vim): | |
9 | assert vim.current.window.cursor == [1, 0] | |
10 | vim.command('normal ityping\033o some text') | |
11 | assert vim.current.buffer[:] == ['typing', ' some text'] | |
12 | assert vim.current.window.cursor == [2, 10] | |
13 | vim.current.window.cursor = [2, 6] | |
14 | vim.command('normal i dumb') | |
15 | assert vim.current.buffer[:] == ['typing', ' some dumb text'] | |
12 | 16 | |
13 | 17 | |
14 | @with_setup(setup=cleanup) | |
15 | def test_cursor(): | |
16 | eq(vim.current.window.cursor, [1, 0]) | |
17 | vim.command('normal ityping\033o some text') | |
18 | eq(vim.current.buffer[:], ['typing', ' some text']) | |
19 | eq(vim.current.window.cursor, [2, 10]) | |
20 | vim.current.window.cursor = [2, 6] | |
21 | vim.command('normal i dumb') | |
22 | eq(vim.current.buffer[:], ['typing', ' some dumb text']) | |
18 | def test_height(vim): | |
19 | vim.command('vsplit') | |
20 | assert vim.windows[1].height == vim.windows[0].height | |
21 | vim.current.window = vim.windows[1] | |
22 | vim.command('split') | |
23 | assert vim.windows[1].height == vim.windows[0].height // 2 | |
24 | vim.windows[1].height = 2 | |
25 | assert vim.windows[1].height == 2 | |
23 | 26 | |
24 | 27 | |
25 | @with_setup(setup=cleanup) | |
26 | def test_height(): | |
28 | def test_width(vim): | |
29 | vim.command('split') | |
30 | assert vim.windows[1].width == vim.windows[0].width | |
31 | vim.current.window = vim.windows[1] | |
27 | 32 | vim.command('vsplit') |
28 | eq(vim.windows[1].height, vim.windows[0].height) | |
29 | vim.current.window = vim.windows[1] | |
30 | vim.command('split') | |
31 | eq(vim.windows[1].height, vim.windows[0].height // 2) | |
32 | vim.windows[1].height = 2 | |
33 | eq(vim.windows[1].height, 2) | |
33 | assert vim.windows[1].width == vim.windows[0].width // 2 | |
34 | vim.windows[1].width = 2 | |
35 | assert vim.windows[1].width == 2 | |
34 | 36 | |
35 | 37 | |
36 | @with_setup(setup=cleanup) | |
37 | def test_width(): | |
38 | vim.command('split') | |
39 | eq(vim.windows[1].width, vim.windows[0].width) | |
40 | vim.current.window = vim.windows[1] | |
41 | vim.command('vsplit') | |
42 | eq(vim.windows[1].width, vim.windows[0].width // 2) | |
43 | vim.windows[1].width = 2 | |
44 | eq(vim.windows[1].width, 2) | |
38 | def test_vars(vim): | |
39 | vim.current.window.vars['python'] = [1, 2, {'3': 1}] | |
40 | assert vim.current.window.vars['python'] == [1, 2, {'3': 1}] | |
41 | assert vim.eval('w:python') == [1, 2, {'3': 1}] | |
45 | 42 | |
46 | 43 | |
47 | @with_setup(setup=cleanup) | |
48 | def test_vars(): | |
49 | vim.current.window.vars['python'] = [1, 2, {'3': 1}] | |
50 | eq(vim.current.window.vars['python'], [1, 2, {'3': 1}]) | |
51 | eq(vim.eval('w:python'), [1, 2, {'3': 1}]) | |
44 | def test_options(vim): | |
45 | vim.current.window.options['colorcolumn'] = '4,3' | |
46 | assert vim.current.window.options['colorcolumn'] == '4,3' | |
47 | # global-local option | |
48 | vim.current.window.options['statusline'] = 'window-status' | |
49 | assert vim.current.window.options['statusline'] == 'window-status' | |
50 | assert vim.options['statusline'] == '' | |
52 | 51 | |
53 | 52 | |
54 | @with_setup(setup=cleanup) | |
55 | def test_options(): | |
56 | vim.current.window.options['colorcolumn'] = '4,3' | |
57 | eq(vim.current.window.options['colorcolumn'], '4,3') | |
58 | # global-local option | |
59 | vim.current.window.options['statusline'] = 'window-status' | |
60 | eq(vim.current.window.options['statusline'], 'window-status') | |
61 | eq(vim.options['statusline'], '') | |
62 | ||
63 | ||
64 | @with_setup(setup=cleanup) | |
65 | def test_position(): | |
53 | def test_position(vim): | |
66 | 54 | height = vim.windows[0].height |
67 | 55 | width = vim.windows[0].width |
68 | 56 | vim.command('split') |
69 | 57 | vim.command('vsplit') |
70 | eq((vim.windows[0].row, vim.windows[0].col), (0, 0)) | |
58 | assert (vim.windows[0].row, vim.windows[0].col) == (0, 0) | |
71 | 59 | vsplit_pos = width / 2 |
72 | 60 | split_pos = height / 2 |
73 | eq(vim.windows[1].row, 0) | |
74 | ok(vsplit_pos - 1 <= vim.windows[1].col <= vsplit_pos + 1) | |
75 | ok(split_pos - 1 <= vim.windows[2].row <= split_pos + 1) | |
76 | eq(vim.windows[2].col, 0) | |
61 | assert vim.windows[1].row == 0 | |
62 | assert vsplit_pos - 1 <= vim.windows[1].col <= vsplit_pos + 1 | |
63 | assert split_pos - 1 <= vim.windows[2].row <= split_pos + 1 | |
64 | assert vim.windows[2].col == 0 | |
77 | 65 | |
78 | 66 | |
79 | @with_setup(setup=cleanup) | |
80 | def test_tabpage(): | |
67 | def test_tabpage(vim): | |
81 | 68 | vim.command('tabnew') |
82 | 69 | vim.command('vsplit') |
83 | eq(vim.windows[0].tabpage, vim.tabpages[0]) | |
84 | eq(vim.windows[1].tabpage, vim.tabpages[1]) | |
85 | eq(vim.windows[2].tabpage, vim.tabpages[1]) | |
70 | assert vim.windows[0].tabpage == vim.tabpages[0] | |
71 | assert vim.windows[1].tabpage == vim.tabpages[1] | |
72 | assert vim.windows[2].tabpage == vim.tabpages[1] | |
86 | 73 | |
87 | 74 | |
88 | @with_setup(setup=cleanup) | |
89 | def test_valid(): | |
75 | def test_valid(vim): | |
90 | 76 | vim.command('split') |
91 | 77 | window = vim.windows[1] |
92 | 78 | vim.current.window = window |
93 | ok(window.valid) | |
79 | assert window.valid | |
94 | 80 | vim.command('q') |
95 | ok(not window.valid) | |
81 | assert not window.valid | |
96 | 82 | |
97 | 83 | |
98 | @with_setup(setup=cleanup) | |
99 | def test_number(): | |
84 | def test_number(vim): | |
100 | 85 | curnum = vim.current.window.number |
101 | 86 | vim.command('bot split') |
102 | eq(vim.current.window.number, curnum + 1) | |
87 | assert vim.current.window.number == curnum + 1 | |
103 | 88 | vim.command('bot split') |
104 | eq(vim.current.window.number, curnum + 2) | |
89 | assert vim.current.window.number == curnum + 2 | |
105 | 90 | |
106 | 91 | |
107 | @with_setup(setup=cleanup) | |
108 | def test_handle(): | |
92 | def test_handle(vim): | |
109 | 93 | hnd1 = vim.current.window.handle |
110 | 94 | vim.command('bot split') |
111 | 95 | hnd2 = vim.current.window.handle |
112 | ok(hnd2 != hnd1) | |
96 | assert hnd2 != hnd1 | |
113 | 97 | vim.command('bot split') |
114 | 98 | hnd3 = vim.current.window.handle |
115 | ok(hnd3 != hnd1) | |
116 | ok(hnd3 != hnd2) | |
99 | assert hnd1 != hnd2 != hnd3 | |
117 | 100 | vim.command('wincmd w') |
118 | eq(vim.current.window.handle,hnd1) | |
101 | assert vim.current.window.handle == hnd1 |