Generalize interface between Vim and Python
This commit is contained in:
parent
1b2e58db97
commit
2073263c07
@ -28,43 +28,6 @@ function! nrepl#fireplace_connection#bencode(value) abort
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! nrepl#fireplace_connection#bdecode(value) abort
|
||||
return s:bdecode({'pos': 0, 'value': a:value})
|
||||
endfunction
|
||||
|
||||
function! s:bdecode(state) abort
|
||||
let value = a:state.value
|
||||
if value[a:state.pos] =~# '\d'
|
||||
let pos = a:state.pos
|
||||
let length = matchstr(value[pos : -1], '^\d\+')
|
||||
let a:state.pos += strlen(length) + length + 1
|
||||
return value[pos+strlen(length)+1 : pos+strlen(length)+length]
|
||||
elseif value[a:state.pos] ==# 'i'
|
||||
let int = matchstr(value[a:state.pos+1:-1], '[^e]*')
|
||||
let a:state.pos += 2 + strlen(int)
|
||||
return str2nr(int)
|
||||
elseif value[a:state.pos] ==# 'l'
|
||||
let values = []
|
||||
let a:state.pos += 1
|
||||
while value[a:state.pos] !=# 'e' && value[a:state.pos] !=# ''
|
||||
call add(values, s:bdecode(a:state))
|
||||
endwhile
|
||||
let a:state.pos += 1
|
||||
return values
|
||||
elseif value[a:state.pos] ==# 'd'
|
||||
let values = {}
|
||||
let a:state.pos += 1
|
||||
while value[a:state.pos] !=# 'e' && value[a:state.pos] !=# ''
|
||||
let key = s:bdecode(a:state)
|
||||
let values[key] = s:bdecode(a:state)
|
||||
endwhile
|
||||
let a:state.pos += 1
|
||||
return values
|
||||
else
|
||||
throw 'bencode parse error: '.string(a:state)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" }}}1
|
||||
|
||||
function! s:shellesc(arg) abort
|
||||
@ -198,20 +161,26 @@ function! s:extract_last_stacktrace(nrepl) abort
|
||||
return stacktrace
|
||||
endfunction
|
||||
|
||||
function! s:nrepl_call(payload) dict abort
|
||||
function! s:nrepl_dispatch(command, ...) dict abort
|
||||
let in = 'python'
|
||||
\ . ' ' . s:shellesc(s:python_dir.'/nrepl_fireplace.py')
|
||||
\ . ' ' . s:shellesc(a:command)
|
||||
\ . ' ' . s:shellesc(self.host)
|
||||
\ . ' ' . s:shellesc(self.port)
|
||||
\ . ' ' . s:shellesc(nrepl#fireplace_connection#bencode(a:payload))
|
||||
\ . ' ' . join(map(copy(a:000), 's:shellesc(v:val)'), ' ')
|
||||
let out = system(in)
|
||||
if !v:shell_error
|
||||
return nrepl#fireplace_connection#bdecode('l'.out.'e')
|
||||
return eval(out)
|
||||
endif
|
||||
throw 'nREPL: '.out
|
||||
endfunction
|
||||
|
||||
function! s:nrepl_call(payload) dict abort
|
||||
return self.dispatch('call', nrepl#fireplace_connection#bencode(a:payload))
|
||||
endfunction
|
||||
|
||||
let s:nrepl = {
|
||||
\ 'dispatch': s:function('s:nrepl_dispatch'),
|
||||
\ 'call': s:function('s:nrepl_call'),
|
||||
\ 'eval': s:function('s:nrepl_eval'),
|
||||
\ 'path': s:function('s:nrepl_path'),
|
||||
@ -222,7 +191,7 @@ if !has('python')
|
||||
endif
|
||||
|
||||
if !exists('s:python')
|
||||
exe 'python sys.path.insert(0, "'.s:python_dir.'")'
|
||||
exe 'python sys.path.insert(0, "'.escape(s:python_dir, '\"').'")'
|
||||
let s:python = 1
|
||||
python import nrepl_fireplace
|
||||
else
|
||||
@ -232,33 +201,23 @@ endif
|
||||
python << EOF
|
||||
import vim
|
||||
|
||||
def fireplace_string_encode(input):
|
||||
str_list = []
|
||||
for c in input:
|
||||
if (000 <= ord(c) and ord(c) <= 037) or c == '"' or c == "\\":
|
||||
str_list.append("\\{0:03o}".format(ord(c)))
|
||||
else:
|
||||
str_list.append(c)
|
||||
return '"' + ''.join(str_list) + '"'
|
||||
|
||||
def fireplace_let(var, value):
|
||||
return vim.command('let ' + var + " = " + fireplace_string_encode(value))
|
||||
return vim.command('let ' + var + ' = ' + nrepl_fireplace.vim_encode(value))
|
||||
|
||||
def fireplace_check():
|
||||
vim.eval('getchar(1)')
|
||||
|
||||
def fireplace_repl_interact():
|
||||
def fireplace_repl_dispatch(command, *args):
|
||||
try:
|
||||
fireplace_let('out', nrepl_fireplace.repl_send(vim.eval('self.host'), int(vim.eval('self.port')), vim.eval('payload'), fireplace_check))
|
||||
fireplace_let('out', nrepl_fireplace.dispatch(command, vim.eval('self.host'), vim.eval('self.port'), fireplace_check, *args))
|
||||
except Exception, e:
|
||||
fireplace_let('err', str(e))
|
||||
EOF
|
||||
|
||||
function! s:nrepl_call(payload) dict abort
|
||||
let payload = nrepl#fireplace_connection#bencode(a:payload)
|
||||
python fireplace_repl_interact()
|
||||
function! s:nrepl_dispatch(command, ...) dict abort
|
||||
python fireplace_repl_dispatch(vim.eval('a:command'), *vim.eval('a:000'))
|
||||
if !exists('err')
|
||||
return nrepl#fireplace_connection#bdecode('l'.out.'e')
|
||||
return out
|
||||
endif
|
||||
throw 'nREPL Connection Error: '.err
|
||||
endfunction
|
||||
|
@ -3,33 +3,102 @@ import select
|
||||
import socket
|
||||
import re
|
||||
|
||||
def repl_send(host, port, payload, callback):
|
||||
buffer = ''
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(8)
|
||||
try:
|
||||
s.connect((host, port))
|
||||
s.setblocking(1)
|
||||
s.sendall(payload)
|
||||
while True:
|
||||
while len(select.select([s], [], [], 0.1)[0]) == 0:
|
||||
callback()
|
||||
body = s.recv(8192)
|
||||
if re.search("=> $", body) != None:
|
||||
raise Exception("not an nREPL server: upgrade to Leiningen 2")
|
||||
buffer += body
|
||||
if re.search('6:statusl(5:error|14:session-closed)?4:done', body):
|
||||
break
|
||||
return buffer
|
||||
finally:
|
||||
s.close()
|
||||
|
||||
def noop():
|
||||
pass
|
||||
|
||||
def main(host, port, payload):
|
||||
def vim_encode(data):
|
||||
if isinstance(data, list):
|
||||
return "[" + ",".join([vim_encode(x) for x in data]) + "]"
|
||||
elif isinstance(data, dict):
|
||||
return "{" + ",".join([vim_encode(x)+":"+vim_encode(y) for x,y in data.items()]) + "}"
|
||||
elif isinstance(data, str):
|
||||
str_list = []
|
||||
for c in data:
|
||||
if (000 <= ord(c) and ord(c) <= 037) or c == '"' or c == "\\":
|
||||
str_list.append("\\{0:03o}".format(ord(c)))
|
||||
else:
|
||||
str_list.append(c)
|
||||
return '"' + ''.join(str_list) + '"'
|
||||
elif isinstance(data, int):
|
||||
return str(data)
|
||||
else:
|
||||
raise TypeError("can't encode a " + type(data).__name__)
|
||||
|
||||
def bdecode(f, char=None):
|
||||
if char == None:
|
||||
char = f.read(1)
|
||||
if char == 'l':
|
||||
l = []
|
||||
while True:
|
||||
char = f.read(1)
|
||||
if char == 'e':
|
||||
return l
|
||||
l.append(bdecode(f, char))
|
||||
elif char == 'd':
|
||||
d = {}
|
||||
while True:
|
||||
char = f.read(1)
|
||||
if char == 'e':
|
||||
return d
|
||||
key = bdecode(f, char)
|
||||
d[key] = bdecode(f)
|
||||
elif char == 'i':
|
||||
i = 0
|
||||
while True:
|
||||
char = f.read(1)
|
||||
if char == 'e':
|
||||
return i
|
||||
i = 10 * i + int(char)
|
||||
else:
|
||||
i = int(char)
|
||||
while True:
|
||||
char = f.read(1)
|
||||
if char == ':':
|
||||
return f.read(i)
|
||||
i = 10 * i + int(char)
|
||||
|
||||
|
||||
class Connection:
|
||||
def __init__(self, host, port, poll=noop):
|
||||
self.poll = poll
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(8)
|
||||
s.connect((host, int(port)))
|
||||
s.setblocking(1)
|
||||
self.socket = s
|
||||
|
||||
def close(self):
|
||||
return self.socket.close()
|
||||
|
||||
def send(self, payload):
|
||||
self.socket.sendall(payload)
|
||||
return ''
|
||||
|
||||
def receive(self, char=None):
|
||||
f = self.socket.makefile()
|
||||
try:
|
||||
sys.stdout.write(repl_send(host, int(port), payload, noop))
|
||||
return bdecode(f)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
def call(self, payload):
|
||||
self.send(payload)
|
||||
responses = []
|
||||
while True:
|
||||
responses.append(self.receive())
|
||||
if 'status' in responses[-1] and 'done' in responses[-1]['status']:
|
||||
return responses
|
||||
|
||||
def dispatch(command, host, port, poll, *args):
|
||||
conn = Connection(host, port, poll)
|
||||
try:
|
||||
return getattr(conn, command)(*args)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def main(command, host, port, *args):
|
||||
try:
|
||||
sys.stdout.write(vim_encode(dispatch(command, host, port, noop, *args)))
|
||||
except Exception, e:
|
||||
print(e)
|
||||
exit(1)
|
||||
|
Loading…
Reference in New Issue
Block a user