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
|
endif
|
||||||
endfunction
|
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
|
" }}}1
|
||||||
|
|
||||||
function! s:shellesc(arg) abort
|
function! s:shellesc(arg) abort
|
||||||
@ -198,20 +161,26 @@ function! s:extract_last_stacktrace(nrepl) abort
|
|||||||
return stacktrace
|
return stacktrace
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:nrepl_call(payload) dict abort
|
function! s:nrepl_dispatch(command, ...) dict abort
|
||||||
let in = 'python'
|
let in = 'python'
|
||||||
\ . ' ' . s:shellesc(s:python_dir.'/nrepl_fireplace.py')
|
\ . ' ' . s:shellesc(s:python_dir.'/nrepl_fireplace.py')
|
||||||
|
\ . ' ' . s:shellesc(a:command)
|
||||||
\ . ' ' . s:shellesc(self.host)
|
\ . ' ' . s:shellesc(self.host)
|
||||||
\ . ' ' . s:shellesc(self.port)
|
\ . ' ' . 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)
|
let out = system(in)
|
||||||
if !v:shell_error
|
if !v:shell_error
|
||||||
return nrepl#fireplace_connection#bdecode('l'.out.'e')
|
return eval(out)
|
||||||
endif
|
endif
|
||||||
throw 'nREPL: '.out
|
throw 'nREPL: '.out
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! s:nrepl_call(payload) dict abort
|
||||||
|
return self.dispatch('call', nrepl#fireplace_connection#bencode(a:payload))
|
||||||
|
endfunction
|
||||||
|
|
||||||
let s:nrepl = {
|
let s:nrepl = {
|
||||||
|
\ 'dispatch': s:function('s:nrepl_dispatch'),
|
||||||
\ 'call': s:function('s:nrepl_call'),
|
\ 'call': s:function('s:nrepl_call'),
|
||||||
\ 'eval': s:function('s:nrepl_eval'),
|
\ 'eval': s:function('s:nrepl_eval'),
|
||||||
\ 'path': s:function('s:nrepl_path'),
|
\ 'path': s:function('s:nrepl_path'),
|
||||||
@ -222,7 +191,7 @@ if !has('python')
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
if !exists('s:python')
|
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
|
let s:python = 1
|
||||||
python import nrepl_fireplace
|
python import nrepl_fireplace
|
||||||
else
|
else
|
||||||
@ -232,33 +201,23 @@ endif
|
|||||||
python << EOF
|
python << EOF
|
||||||
import vim
|
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):
|
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():
|
def fireplace_check():
|
||||||
vim.eval('getchar(1)')
|
vim.eval('getchar(1)')
|
||||||
|
|
||||||
def fireplace_repl_interact():
|
def fireplace_repl_dispatch(command, *args):
|
||||||
try:
|
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:
|
except Exception, e:
|
||||||
fireplace_let('err', str(e))
|
fireplace_let('err', str(e))
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
function! s:nrepl_call(payload) dict abort
|
function! s:nrepl_dispatch(command, ...) dict abort
|
||||||
let payload = nrepl#fireplace_connection#bencode(a:payload)
|
python fireplace_repl_dispatch(vim.eval('a:command'), *vim.eval('a:000'))
|
||||||
python fireplace_repl_interact()
|
|
||||||
if !exists('err')
|
if !exists('err')
|
||||||
return nrepl#fireplace_connection#bdecode('l'.out.'e')
|
return out
|
||||||
endif
|
endif
|
||||||
throw 'nREPL Connection Error: '.err
|
throw 'nREPL Connection Error: '.err
|
||||||
endfunction
|
endfunction
|
||||||
|
@ -3,33 +3,102 @@ import select
|
|||||||
import socket
|
import socket
|
||||||
import re
|
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():
|
def noop():
|
||||||
pass
|
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:
|
||||||
|
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:
|
try:
|
||||||
sys.stdout.write(repl_send(host, int(port), payload, noop))
|
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:
|
except Exception, e:
|
||||||
print(e)
|
print(e)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
Loading…
Reference in New Issue
Block a user