Fixed vim and zsh

This commit is contained in:
2018-04-05 13:06:54 +02:00
parent f9db886bd3
commit 0331f6518a
2009 changed files with 256303 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
# vim:set et sw=4 ts=8:
import vim
import json
import msgpack
# vim's python binding doesn't have the `call` method, wrap it here
def nvim_call_function(method,args):
vim.vars['_neovim_rpc_tmp_args'] = args
# vim.eval('getcurpos()') return an array of string, it should be an array
# of int. Use json_encode to workaround this
return vim.bindeval('call("%s",g:_neovim_rpc_tmp_args)' % method)
def nvim_get_current_buf():
return vim.current.buffer
def nvim_list_bufs():
return list(vim.buffers)
# {'return_type': 'Integer', 'since': 1, 'method': True, 'parameters': [['Buffer', 'buffer']], 'name': 'nvim_buf_get_number'}
def nvim_buf_get_number(buf):
return buf.number
def nvim_buf_get_name(buffer):
return buffer.name
def nvim_get_var(name):
return vim.vars[name]
def nvim_set_var(name,val):
vim.vars[name] = val
return val
def nvim_buf_get_var(buffer,name):
return json.loads(vim.eval('json_encode(getbufvar(%s, "%s"))' % (buffer.number, name)))
def nvim_buf_set_var(buffer,name,val):
buffer.vars[name] = val
def nvim_buf_get_lines(buffer,start,end,*args):
if start < 0:
start = len(buffer) + 1 + start
if end < 0:
end = len(buffer) + 1 + end
return buffer[start:end]
def nvim_eval(expr):
return nvim_call_function('eval',[expr])
def nvim_buf_set_lines(buffer,start,end,err,lines):
if start < 0:
start = len(buffer) + 1 + start
if end < 0:
end = len(buffer) + 1 + end
buffer[start:end] = lines
if nvim_call_function('bufwinnr',[buffer.number])!=-1:
# vim needs' redraw to update the screen, it seems to be a bug
vim.command('redraw')
buffer_set_lines = nvim_buf_set_lines
def buffer_line_count(buffer):
return len(buffer)
def nvim_buf_line_count(buffer):
return len(buffer)
def nvim_get_option(name):
return vim.options[name]
def nvim_buf_get_option(buf, name):
return buf.options[name]
def nvim_set_option(name, val):
vim.options[name] = val
def nvim_command(cmd):
vim.command(cmd)
def nvim_get_current_win():
return vim.current.window
def nvim_win_get_cursor(window):
return window.cursor
def nvim_out_write(s):
nvim_call_function('neovim_rpc#_nvim_out_write', [s])
def nvim_err_write(s):
nvim_call_function('neovim_rpc#_nvim_err_write', [s])
# NOTE https://github.com/autozimu/LanguageClient-neovim/pull/151#issuecomment-339198527
# TODO
def nvim_buf_add_highlight(buf, src_id, *args):
return src_id
# NOTE https://github.com/autozimu/LanguageClient-neovim/pull/151#issuecomment-339198527
# TODO
def nvim_buf_clear_highlight(*args):
pass

View File

@@ -0,0 +1,56 @@
import json
import socket
import sys
import os
import threading
import vim
import logging
import msgpack
import neovim_rpc_server_api_info
import neovim_rpc_methods
import threading
import socket
import time
from neovim.api.common import decode_if_bytes
BUFFER_TYPE = type(vim.current.buffer)
BUFFER_TYPE_ID = neovim_rpc_server_api_info.API_INFO['types']['Buffer']['id']
WINDOW_TYPE = type(vim.current.window)
WINDOW_TYPE_ID = neovim_rpc_server_api_info.API_INFO['types']['Window']['id']
def walk(fn, obj):
if type(obj) in [list, tuple, vim.List]:
return list(walk(fn, o) for o in obj)
if type(obj) in [dict, vim.Dictionary]:
return dict((walk(fn, k), walk(fn, v)) for k, v in
obj.items())
return fn(obj)
def from_client(msg):
def handler(obj):
if type(obj) is msgpack.ExtType:
if obj.code == BUFFER_TYPE_ID:
return vim.buffers[msgpack.unpackb(obj.data)]
if obj.code == WINDOW_TYPE_ID:
return vim.windows[msgpack.unpackb(obj.data) - 1]
elif obj is None:
return ''
if sys.version_info.major!=2:
# python3 needs decode
obj = decode_if_bytes(obj)
return obj
return walk(handler,msg)
def to_client(msg):
def handler( obj):
if type(obj) == BUFFER_TYPE:
return msgpack.ExtType(BUFFER_TYPE_ID, msgpack.packb(obj.number))
if type(obj) == WINDOW_TYPE:
return msgpack.ExtType(WINDOW_TYPE_ID, msgpack.packb(obj.number))
if type(obj) == vim.Function:
return obj.name
return obj
return walk(handler, msg)

View File

@@ -0,0 +1,436 @@
# vim:set et sw=4 ts=8:
import json
import socket
import sys
import os
import threading
import vim
import logging
import msgpack
import neovim_rpc_server_api_info
import neovim_rpc_methods
import threading
import socket
import time
import subprocess
from neovim.api import common as neovim_common
import neovim_rpc_protocol
vim_error = vim.Function('neovim_rpc#_error')
# protable devnull
if sys.version_info.major==2:
DEVNULL = open(os.devnull, 'wb')
else:
from subprocess import DEVNULL
if sys.version_info.major == 2:
from Queue import Queue, Empty as QueueEmpty
else:
from queue import Queue, Empty as QueueEmpty
# NVIM_PYTHON_LOG_FILE=nvim.log NVIM_PYTHON_LOG_LEVEL=INFO vim test.md
try:
# Python 3
import socketserver
except ImportError:
# Python 2
import SocketServer as socketserver
# globals
logger = logging.getLogger(__name__)
# supress the annoying error message:
# No handlers could be found for logger "neovim_rpc_server"
logger.addHandler(logging.NullHandler())
request_queue = Queue()
responses = {}
def _channel_id_new():
with _channel_id_new._lock:
_channel_id_new._counter += 1
return _channel_id_new._counter
# static local
_channel_id_new._counter = 0
_channel_id_new._lock = threading.Lock()
class VimHandler(socketserver.BaseRequestHandler):
_lock = threading.Lock()
_sock = None
@classmethod
def notify(cls,cmd="call neovim_rpc#_callback()"):
try:
if not VimHandler._sock:
return
with VimHandler._lock:
encoded = json.dumps(['ex', cmd])
logger.info("sending notification: %s",encoded)
VimHandler._sock.send(encoded.encode('utf-8'))
except Exception as ex:
logger.exception('VimHandler notify exception for [%s]: %s', cmd, ex)
@classmethod
def notify_exited(cls,channel):
try:
cmd = "call neovim_rpc#_on_exit(%s)" % channel
cls.notify(cmd)
except Exception as ex:
logger.exception('notify_exited for channel [%s] exception: %s',channel,ex)
# each connection is a thread
def handle(self):
logger.info("=== socket opened ===")
data = None
while True:
try:
rcv = self.request.recv(4096)
# 16k buffer by default
if data:
data += rcv
else:
data = rcv
except socket.error:
logger.info("=== socket error ===")
break
except IOError:
logger.info("=== socket closed ===")
break
if len(rcv) == 0:
logger.info("=== socket closed ===")
break
logger.info("received: %s", data)
try:
decoded = json.loads(data.decode('utf-8'))
except ValueError:
logger.exception("json decoding failed, wait for more data")
continue
data = None
# Send a response if the sequence number is positive.
# Negative numbers are used for "eval" responses.
if len(decoded)>=2 and decoded[0] >= 0 and decoded[1] == 'neovim_rpc_setup':
VimHandler._sock = self.request
# initial setup
encoded = json.dumps(['ex', "scall neovim_rpc#_callback()"])
logger.info("sending {0}".format(encoded))
self.request.send(encoded.encode('utf-8'))
else:
# recognize as rpcrequest
reqid = decoded[0]
channel = decoded[1][1]
event = decoded[1][2]
args = decoded[1][3]
rspid = decoded[1][4]
NvimHandler.request(self.request,
channel,
reqid,
event,
args,
rspid)
# wait for response
class SocketToStream():
def __init__(self,sock):
self._sock = sock
def read(self,cnt):
if cnt>4096:
cnt = 4096
return self._sock.recv(cnt)
def write(self,w):
return self._sock.send(w)
class NvimHandler(socketserver.BaseRequestHandler):
channel_sockets = {}
def handle(self):
logger.info("=== socket opened for client ===")
channel = _channel_id_new()
sock = self.request
chinfo = dict(sock=sock)
NvimHandler.channel_sockets[channel] = chinfo
try:
f = SocketToStream(sock)
unpacker = msgpack.Unpacker(f)
for unpacked in unpacker:
logger.info("unpacked: %s", unpacked)
# response format:
# - msg[0]: 1
# - msg[1]: the request id
# - msg[2]: error(if any), format: [code,str]
# - msg[3]: result(if not errored)
if int(unpacked[0]) == 1:
unpacked = neovim_rpc_protocol.from_client(unpacked)
reqid = int(unpacked[1])
rspid, vimsock = chinfo[reqid]
err = unpacked[2]
result = unpacked[3]
# VIM fails to parse response when there a sleep in neovim
# client. I cannot figure out why. Use global responses to
# workaround this issue.
responses[rspid] = [err, result]
content = [reqid, '']
tosend = json.dumps(content)
# vimsock.send
vimsock.send(tosend.encode('utf-8'))
chinfo.pop(reqid)
continue
request_queue.put((f,channel,unpacked))
# notify vim in order to process request in main thread, and
# avoiding the stupid json protocol
VimHandler.notify()
logger.info('channel %s closed.', channel)
except:
logger.exception('unpacker failed.')
finally:
try:
NvimHandler.channel_sockets.pop(channel)
sock.close()
except:
pass
@classmethod
def notify(cls,channel,event,args):
try:
channel = int(channel)
if channel not in cls.channel_sockets:
logger.info("channel[%s] not in NvimHandler", channel)
return
sock = cls.channel_sockets[channel]['sock']
# notification format:
# - msg[0] type, which is 2
# - msg[1] method
# - msg[2] arguments
content = [2, event, args]
logger.info("notify channel[%s]: %s", channel, content)
packed = msgpack.packb(neovim_rpc_protocol.to_client(content))
sock.send(packed)
except Exception as ex:
logger.exception("notify failed: %s", ex)
@classmethod
def request(cls, vimsock, channel, reqid, event, args, rspid):
try:
reqid = int(reqid)
channel = int(channel)
chinfo = cls.channel_sockets[channel]
if channel not in cls.channel_sockets:
logger.info("channel[%s] not in NvimHandler", channel)
return
sock = chinfo['sock']
# request format:
# - msg[0] type, which is 0
# - msg[1] request id
# - msg[2] method
# - msg[3] arguments
content = [0, reqid, event, args]
chinfo[reqid] = [rspid, vimsock]
logger.info("request channel[%s]: %s", channel, content)
packed = msgpack.packb(neovim_rpc_protocol.to_client(content))
sock.send(packed)
except Exception as ex:
logger.exception("request failed: %s", ex)
@classmethod
def shutdown(cls):
# close all sockets
for channel in list(cls.channel_sockets.keys()):
chinfo = cls.channel_sockets.get(channel,None)
if chinfo:
sock = chinfo['sock']
logger.info("closing client %s", channel)
# if don't shutdown the socket, vim will never exit because the
# recv thread is still blocking
sock.shutdown(socket.SHUT_RDWR)
sock.close()
# copied from neovim python-client/neovim/__init__.py
def _setup_logging(name):
"""Setup logging according to environment variables."""
logger = logging.getLogger(__name__)
if 'NVIM_PYTHON_LOG_FILE' in os.environ:
prefix = os.environ['NVIM_PYTHON_LOG_FILE'].strip()
major_version = sys.version_info[0]
logfile = '{}_py{}_{}'.format(prefix, major_version, name)
handler = logging.FileHandler(logfile, 'w', encoding='utf-8')
handler.formatter = logging.Formatter(
'%(asctime)s [%(levelname)s @ '
'%(filename)s:%(funcName)s:%(lineno)s] %(process)s - %(message)s')
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
logger.setLevel(level)
def start():
_setup_logging('neovim_rpc_server')
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
# 0 for random port
global _vim_server
global _nvim_server
_vim_server = ThreadedTCPServer(("127.0.0.1", 0), VimHandler)
_nvim_server = ThreadedTCPServer(("127.0.0.1", 0), NvimHandler)
_vim_server.daemon_threads = True
_nvim_server.daemon_threads = True
# Start a thread with the server -- that thread will then start one
# more thread for each request
main_server_thread = threading.Thread(target=_vim_server.serve_forever)
clients_server_thread = threading.Thread(target=_nvim_server.serve_forever)
# Exit the server thread when the main thread terminates
main_server_thread.daemon = True
main_server_thread.start()
clients_server_thread.daemon = True
clients_server_thread.start()
return ["{addr[0]}:{addr[1]}".format(addr=_nvim_server.server_address), "{addr[0]}:{addr[1]}".format(addr=_vim_server.server_address)]
def process_pending_requests():
logger.info("process_pending_requests")
while True:
item = None
try:
item = request_queue.get(False)
f, channel, msg = item
msg = neovim_rpc_protocol.from_client(msg)
logger.info("get msg from channel [%s]: %s", channel, msg)
# request format:
# - msg[0] type, which is 0
# - msg[1] request id
# - msg[2] method
# - msg[3] arguments
# notification format:
# - msg[0] type, which is 2
# - msg[1] method
# - msg[2] arguments
if msg[0] == 0:
#request
req_typed, req_id, method, args = msg
try:
err=None
result = _process_request(channel,method,args)
except Exception as ex:
logger.exception("process failed: %s", ex)
# error uccor
err = [1,str(ex)]
result = None
result = [1,req_id,err,result]
logger.info("sending result: %s", result)
packed = msgpack.packb(neovim_rpc_protocol.to_client(result))
f.write(packed)
logger.info("sent")
if msg[0] == 2:
# notification
req_typed, method, args = msg
try:
result = _process_request(channel,method,args)
logger.info('notification process result: [%s]', result)
except Exception as ex:
logger.exception("process failed: %s", ex)
except QueueEmpty as em:
pass
except Exception as ex:
logger.exception("exception during process: %s", ex)
finally:
if item:
request_queue.task_done()
else:
# item==None means the queue is empty
break
def _process_request(channel,method,args):
if method=='vim_get_api_info':
# this is the first request send from neovim client
api_info = neovim_rpc_server_api_info.API_INFO
return [channel,api_info]
if hasattr(neovim_rpc_methods,method):
return getattr(neovim_rpc_methods,method)(*args)
else:
logger.error("method %s not implemented", method)
vim_error("rpc method [%s] not implemented in pythonx/neovim_rpc_methods.py. Please send PR or contact the mantainer." % method)
raise Exception('%s not implemented' % method)
def rpcnotify(channel,method,args):
NvimHandler.notify(channel,method,args)
def stop():
logger.info("stop begin")
# close tcp channel server
_nvim_server.shutdown()
_nvim_server.server_close()
# close the main channel
try:
vim.command('call ch_close(g:_neovim_rpc_main_channel)')
except Exception as ex:
logger.info("ch_close failed: %s", ex)
# remove all sockets
NvimHandler.shutdown()
try:
# stop the main channel
_vim_server.shutdown()
except Exception as ex:
logger.info("_vim_server shutodwn failed: %s", ex)
try:
_vim_server.server_close()
except Exception as ex:
logger.info("_vim_server close failed: %s", ex)

File diff suppressed because it is too large Load Diff