Advertisement
Guest User

Untitled

a guest
Jun 25th, 2017
58
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.89 KB | None | 0 0
  1. import ast
  2. import greenlet
  3. import logging
  4. import neovim
  5. import os
  6. import re
  7. from .base import Base
  8. from functools import partial
  9. from jupyter_client import KernelManager
  10. from jupyter_client.consoleapp import JupyterConsoleApp
  11. from jupyter_client.threaded import ThreadedKernelClient
  12. from jupyter_core.application import JupyterApp
  13.  
  14. imports = re.compile(
  15. r'^\s*(from\s+\.*\w+(\.\w+)*\s+import\s+(\w+,\s+)*|import\s+)')
  16. split_pattern = re.compile('[^= \r\n*()@-]')
  17. keyword = re.compile('[A-Za-z0-9_]')
  18.  
  19. request = '''
  20. try:
  21. _completions = completion_metadata(get_ipython())
  22. except Exception:
  23. pass
  24. '''
  25.  
  26. logger = logging.getLogger(__name__)
  27. error, debug, info, warn = (
  28. logger.error, logger.debug, logger.info, logger.warn,)
  29. if 'NVIM_IPY_DEBUG_FILE' in os.environ:
  30. logfile = os.environ['NVIM_IPY_DEBUG_FILE'].strip()
  31. logger.addHandler(logging.FileHandler(logfile, 'w'))
  32. logger.level = logging.DEBUG
  33.  
  34.  
  35. class RedirectingKernelManager(KernelManager):
  36. def _launch_kernel(self, cmd, **b):
  37. # stdout is used to communicate with nvim, redirect it somewhere else
  38. self._null = open("/dev/null", "wb", 0)
  39. b['stdout'] = self._null.fileno()
  40. b['stderr'] = self._null.fileno()
  41. return super(RedirectingKernelManager, self)._launch_kernel(cmd, **b)
  42.  
  43.  
  44. class JupyterVimApp(JupyterApp, JupyterConsoleApp):
  45. # don't use blocking client; we override call_handlers below
  46. kernel_client_class = ThreadedKernelClient
  47. kernel_manager_class = RedirectingKernelManager
  48. aliases = JupyterConsoleApp.aliases # this the way?
  49. flags = JupyterConsoleApp.flags
  50.  
  51. def init_kernel_client(self):
  52. # TODO: cleanup this (by subclassing kernel_clint or something)
  53. if self.kernel_manager is not None:
  54. self.kernel_client = self.kernel_manager.client()
  55. else:
  56. self.kernel_client = self.kernel_client_class(
  57. session=self.session,
  58. ip=self.ip,
  59. transport=self.transport,
  60. shell_port=self.shell_port,
  61. iopub_port=self.iopub_port,
  62. hb_port=self.hb_port,
  63. connection_file=self.connection_file,
  64. parent=self,
  65. )
  66. self.kernel_client.shell_channel.call_handlers = self.target.on_shell_msg
  67. self.kernel_client.hb_channel.call_handlers = self.target.on_hb_msg
  68. self.kernel_client.start_channels()
  69.  
  70. def initialize(self, target, argv):
  71. self.target = target
  72. super(JupyterVimApp, self).initialize(argv)
  73. JupyterConsoleApp.initialize(self, argv)
  74.  
  75.  
  76. class Async(object):
  77. """Wrapper that defers all method calls on a plugin object to the event
  78. loop, given that the object has vim attribute"""
  79.  
  80. def __init__(self, wraps):
  81. self.wraps = wraps
  82.  
  83. def __getattr__(self, name):
  84. return partial(self.wraps.vim.async_call, getattr(self.wraps, name))
  85.  
  86.  
  87. @neovim.plugin
  88. @neovim.encoding(True)
  89. class IPythonPlugin(object):
  90.  
  91. def __init__(self, vim):
  92. self.vim = vim
  93. self.has_connection = False
  94. self.pending_shell_msgs = {}
  95.  
  96. def connect(self, argv):
  97. self.ip_app = JupyterVimApp.instance()
  98. self.ip_app.initialize(Async(self), argv)
  99. self.ip_app.start()
  100. self.kc = self.ip_app.kernel_client
  101. self.km = self.ip_app.kernel_manager
  102. self.has_connection = True
  103.  
  104. def handle(self, msg_id, handler):
  105. self.pending_shell_msgs[msg_id] = handler
  106.  
  107. def waitfor(self, msg_id, retval=None):
  108. # FIXME: add some kind of timeout
  109. gr = greenlet.getcurrent()
  110. self.handle(msg_id, gr)
  111. return gr.parent.switch(retval)
  112.  
  113. def ignore(self, msg_id):
  114. self.handle(msg_id, None)
  115.  
  116. @neovim.function("IPyConnect", sync=True)
  117. def ipy_connect(self, args):
  118. # 'connect' waits for kernelinfo, and so must be async
  119. Async(self).connect(args)
  120.  
  121. def on_shell_msg(self, m):
  122. self.last_msg = m
  123. debug('shell %s: %r', m['msg_type'], m['content'])
  124. msg_id = m['parent_header']['msg_id']
  125. try:
  126. handler = self.pending_shell_msgs.pop(msg_id)
  127. except KeyError:
  128. debug('unexpected shell msg: %r', m)
  129. return
  130. if isinstance(handler, greenlet.greenlet):
  131. handler.parent = greenlet.getcurrent()
  132. handler.switch(m)
  133. elif handler is not None:
  134. handler(m)
  135.  
  136. # this gets called when heartbeat is lost
  137. def on_hb_msg(self, time_since):
  138. raise Exception()
  139.  
  140.  
  141. class Source(Base):
  142.  
  143. def __init__(self, vim):
  144. super().__init__(vim)
  145. self.__plug = IPythonPlugin(vim)
  146. self.__plug.ipy_connect(['--existing'])
  147. self.name = 'ipython'
  148. self.mark = '[IPy]'
  149. self.filetypes = ['python']
  150. self.is_volatile = True
  151. self.input_pattern = '.*'
  152. self.rank = 2000
  153.  
  154. def get_complete_position(self, context):
  155. line = self.vim.current.line
  156.  
  157. # return immediately for imports
  158. if imports.match(context['input']):
  159. start = len(context['input'])
  160. while start and re.match('[._A-Za-z0-9]', line[start - 1]):
  161. start -= 1
  162. return start
  163.  
  164. # locate the start of the word
  165. col = self.vim.funcs.col('.')
  166. start = col - 1
  167. if start == 0 or (len(line) == start and
  168. not split_pattern.match(line[start - 2]) and
  169. not (start >= 2 and keyword.match(line[start - 3]))
  170. and line[start - 3:start - 1] != '].'):
  171. start = -1
  172. return start
  173.  
  174. line = self.vim.funcs.getline('.')
  175. start = self.vim.funcs.strchars(line[:col]) - 1
  176. bracket_level = 0
  177. while start > 0 and (
  178. split_pattern.match(line[start - 1])
  179. or (line[start - 1] == '.'
  180. and start >= 2 and keyword.match(line[start - 2])
  181. or (line[start - 1] == '-'
  182. and start >= 2 and line[start - 2] == '[')
  183. or ''.join(line[start - 2:start]) == '].')):
  184. if line[start - 1] == '[':
  185. if (start == 1 or not re.match(
  186. '[A-Za-z0-9_\]]', line[start-2])) or bracket_level > 0:
  187. break
  188. bracket_level += 1
  189. elif line[start-1] == ']':
  190. bracket_level -= 1
  191. start -= 1
  192.  
  193. return start
  194.  
  195. def gather_candidates(self, context):
  196. plug = self.__plug
  197. plug.waitfor(plug.kc.complete(context['input'], len(context['input'])))
  198. reply = plug.waitfor(plug.kc.execute(
  199. request, silent=True,
  200. user_expressions={'_completions': '_completions'}))
  201. metadata = reply['content']['user_expressions']['_completions']
  202. return ast.literal_eval(metadata['data']['text/plain'])
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement