efficient vim config
[dotfiles/.git] / .local / lib / python2.7 / site-packages / pynvim / api / common.py
1 """Code shared between the API classes."""
2 import functools
3
4 from msgpack import unpackb
5
6 from pynvim.compat import unicode_errors_default
7
8 __all__ = ()
9
10
11 class NvimError(Exception):
12     pass
13
14
15 class Remote(object):
16
17     """Base class for Nvim objects(buffer/window/tabpage).
18
19     Each type of object has it's own specialized class with API wrappers around
20     the msgpack-rpc session. This implements equality which takes the remote
21     object handle into consideration.
22     """
23
24     def __init__(self, session, code_data):
25         """Initialize from session and code_data immutable object.
26
27         The `code_data` contains serialization information required for
28         msgpack-rpc calls. It must be immutable for Buffer equality to work.
29         """
30         self._session = session
31         self.code_data = code_data
32         self.handle = unpackb(code_data[1])
33         self.api = RemoteApi(self, self._api_prefix)
34         self.vars = RemoteMap(self, self._api_prefix + 'get_var',
35                               self._api_prefix + 'set_var',
36                               self._api_prefix + 'del_var')
37         self.options = RemoteMap(self, self._api_prefix + 'get_option',
38                                  self._api_prefix + 'set_option')
39
40     def __repr__(self):
41         """Get text representation of the object."""
42         return '<%s(handle=%r)>' % (
43             self.__class__.__name__,
44             self.handle,
45         )
46
47     def __eq__(self, other):
48         """Return True if `self` and `other` are the same object."""
49         return (hasattr(other, 'code_data')
50                 and other.code_data == self.code_data)
51
52     def __hash__(self):
53         """Return hash based on remote object id."""
54         return self.code_data.__hash__()
55
56     def request(self, name, *args, **kwargs):
57         """Wrapper for nvim.request."""
58         return self._session.request(name, self, *args, **kwargs)
59
60
61 class RemoteApi(object):
62
63     """Wrapper to allow api methods to be called like python methods."""
64
65     def __init__(self, obj, api_prefix):
66         """Initialize a RemoteApi with object and api prefix."""
67         self._obj = obj
68         self._api_prefix = api_prefix
69
70     def __getattr__(self, name):
71         """Return wrapper to named api method."""
72         return functools.partial(self._obj.request, self._api_prefix + name)
73
74
75 def transform_keyerror(exc):
76     if isinstance(exc, NvimError):
77         if exc.args[0].startswith('Key not found:'):
78             return KeyError(exc.args[0])
79         if exc.args[0].startswith('Invalid option name:'):
80             return KeyError(exc.args[0])
81     return exc
82
83
84 class RemoteMap(object):
85     """Represents a string->object map stored in Nvim.
86
87     This is the dict counterpart to the `RemoteSequence` class, but it is used
88     as a generic way of retrieving values from the various map-like data
89     structures present in Nvim.
90
91     It is used to provide a dict-like API to vim variables and options.
92     """
93
94     _set = None
95     _del = None
96
97     def __init__(self, obj, get_method, set_method=None, del_method=None):
98         """Initialize a RemoteMap with session, getter/setter."""
99         self._get = functools.partial(obj.request, get_method)
100         if set_method:
101             self._set = functools.partial(obj.request, set_method)
102         if del_method:
103             self._del = functools.partial(obj.request, del_method)
104
105     def __getitem__(self, key):
106         """Return a map value by key."""
107         try:
108             return self._get(key)
109         except NvimError as exc:
110             raise transform_keyerror(exc)
111
112     def __setitem__(self, key, value):
113         """Set a map value by key(if the setter was provided)."""
114         if not self._set:
115             raise TypeError('This dict is read-only')
116         self._set(key, value)
117
118     def __delitem__(self, key):
119         """Delete a map value by associating None with the key."""
120         if not self._del:
121             raise TypeError('This dict is read-only')
122         try:
123             return self._del(key)
124         except NvimError as exc:
125             raise transform_keyerror(exc)
126
127     def __contains__(self, key):
128         """Check if key is present in the map."""
129         try:
130             self._get(key)
131             return True
132         except Exception:
133             return False
134
135     def get(self, key, default=None):
136         """Return value for key if present, else a default value."""
137         try:
138             return self.__getitem__(key)
139         except KeyError:
140             return default
141
142
143 class RemoteSequence(object):
144
145     """Represents a sequence of objects stored in Nvim.
146
147     This class is used to wrap msgapck-rpc functions that work on Nvim
148     sequences(of lines, buffers, windows and tabpages) with an API that
149     is similar to the one provided by the python-vim interface.
150
151     For example, the 'windows' property of the `Nvim` class is a RemoteSequence
152     sequence instance, and the expression `nvim.windows[0]` is translated to
153     session.request('nvim_list_wins')[0].
154
155     One important detail about this class is that all methods will fetch the
156     sequence into a list and perform the necessary manipulation
157     locally(iteration, indexing, counting, etc).
158     """
159
160     def __init__(self, session, method):
161         """Initialize a RemoteSequence with session, method."""
162         self._fetch = functools.partial(session.request, method)
163
164     def __len__(self):
165         """Return the length of the remote sequence."""
166         return len(self._fetch())
167
168     def __getitem__(self, idx):
169         """Return a sequence item by index."""
170         if not isinstance(idx, slice):
171             return self._fetch()[idx]
172         return self._fetch()[idx.start:idx.stop]
173
174     def __iter__(self):
175         """Return an iterator for the sequence."""
176         items = self._fetch()
177         for item in items:
178             yield item
179
180     def __contains__(self, item):
181         """Check if an item is present in the sequence."""
182         return item in self._fetch()
183
184
185 def _identity(obj, session, method, kind):
186     return obj
187
188
189 def decode_if_bytes(obj, mode=True):
190     """Decode obj if it is bytes."""
191     if mode is True:
192         mode = unicode_errors_default
193     if isinstance(obj, bytes):
194         return obj.decode("utf-8", errors=mode)
195     return obj
196
197
198 def walk(fn, obj, *args, **kwargs):
199     """Recursively walk an object graph applying `fn`/`args` to objects."""
200     if type(obj) in [list, tuple]:
201         return list(walk(fn, o, *args) for o in obj)
202     if type(obj) is dict:
203         return dict((walk(fn, k, *args), walk(fn, v, *args)) for k, v in
204                     obj.items())
205     return fn(obj, *args, **kwargs)