2013-01-06 22:51:02 +00:00
|
|
|
" autoload/nrepl/foreplay_connection.vim
|
2012-12-04 06:06:22 +00:00
|
|
|
" Maintainer: Tim Pope <http://tpo.pe/>
|
|
|
|
|
|
|
|
if exists("g:autoloaded_nrepl_foreplay_connection") || &cp
|
|
|
|
finish
|
|
|
|
endif
|
|
|
|
let g:autoloaded_nrepl_foreplay_connection = 1
|
|
|
|
|
|
|
|
function! s:function(name) abort
|
|
|
|
return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
" Bencode {{{1
|
|
|
|
|
|
|
|
function! nrepl#foreplay_connection#bencode(value) abort
|
|
|
|
if type(a:value) == type(0)
|
|
|
|
return 'i'.a:value.'e'
|
|
|
|
elseif type(a:value) == type('')
|
|
|
|
return strlen(a:value).':'.a:value
|
|
|
|
elseif type(a:value) == type([])
|
|
|
|
return 'l'.join(map(a:value,'nrepl#foreplay_connection#bencode(v:val)'),'').'e'
|
|
|
|
elseif type(a:value) == type({})
|
|
|
|
return 'd'.join(values(map(a:value,'nrepl#foreplay_connection#bencode(v:key).nrepl#foreplay_connection#bencode(v:val)')),'').'e'
|
|
|
|
else
|
|
|
|
throw "Can't bencode ".string(a:value)
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! nrepl#foreplay_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
|
|
|
|
if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
|
|
|
|
return a:arg
|
|
|
|
elseif &shell =~# 'cmd'
|
|
|
|
return '"'.substitute(substitute(a:arg, '"', '""', 'g'), '%', '"%"', 'g').'"'
|
|
|
|
else
|
|
|
|
let escaped = shellescape(a:arg)
|
|
|
|
if &shell =~# 'sh' && &shell !~# 'csh'
|
|
|
|
return substitute(escaped, '\\\n', '\n', 'g')
|
|
|
|
else
|
|
|
|
return escaped
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! nrepl#foreplay_connection#prompt() abort
|
|
|
|
return foreplay#input_host_port()
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! nrepl#foreplay_connection#open(arg) abort
|
|
|
|
if a:arg =~# '^\d\+$'
|
|
|
|
let host = 'localhost'
|
|
|
|
let port = a:arg
|
|
|
|
elseif a:arg =~# ':\d\+$'
|
|
|
|
let host = matchstr(a:arg, '.*\ze:')
|
|
|
|
let port = matchstr(a:arg, ':\zs.*')
|
|
|
|
else
|
|
|
|
throw "nREPL: Couldn't find [host:]port in " . a:arg
|
|
|
|
endif
|
|
|
|
let client = deepcopy(s:nrepl)
|
|
|
|
let client.host = host
|
|
|
|
let client.port = port
|
2012-12-15 05:07:46 +00:00
|
|
|
let session = client.process({'op': 'clone'})['new-session']
|
|
|
|
let response = client.process({'op': 'eval', 'session': session, 'code':
|
2012-12-11 01:09:13 +00:00
|
|
|
\ '(do (println "success") (symbol (str (System/getProperty "path.separator") (System/getProperty "java.class.path"))))'})
|
2012-12-15 20:36:50 +00:00
|
|
|
let client._path = response.value[-1]
|
2012-12-15 05:07:46 +00:00
|
|
|
if has_key(response, 'out')
|
2012-12-11 01:09:13 +00:00
|
|
|
let client.session = session
|
|
|
|
endif
|
2012-12-04 06:06:22 +00:00
|
|
|
return client
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:nrepl_path() dict abort
|
|
|
|
return split(self._path[1:-1], self._path[0])
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:nrepl_process(payload) dict abort
|
2012-12-15 05:07:46 +00:00
|
|
|
let combined = {'status': [], 'session': []}
|
|
|
|
for response in self.call(a:payload)
|
|
|
|
for key in keys(response)
|
|
|
|
if key ==# 'id' || key ==# 'ns'
|
|
|
|
let combined[key] = response[key]
|
|
|
|
elseif key ==# 'value'
|
|
|
|
let combined.value = extend(get(combined, 'value', []), [response.value])
|
|
|
|
elseif key ==# 'status'
|
|
|
|
for entry in response[key]
|
|
|
|
if index(combined[key], entry) < 0
|
|
|
|
call extend(combined[key], [entry])
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
elseif key ==# 'session'
|
|
|
|
if index(combined[key], response[key]) < 0
|
|
|
|
call extend(combined[key], [response[key]])
|
|
|
|
endif
|
|
|
|
elseif type(response[key]) == type('')
|
|
|
|
let combined[key] = get(combined, key, '') . response[key]
|
|
|
|
else
|
|
|
|
let combined[key] = response[key]
|
|
|
|
endif
|
|
|
|
endfor
|
2012-12-04 06:06:22 +00:00
|
|
|
endfor
|
2012-12-15 05:07:46 +00:00
|
|
|
if index(combined.status, 'error') >= 0
|
|
|
|
throw 'nREPL: ' . tr(combined.status[0], '-', ' ')
|
|
|
|
endif
|
|
|
|
return combined
|
2012-12-04 06:06:22 +00:00
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:nrepl_eval(expr, ...) dict abort
|
|
|
|
let payload = {"op": "eval", "code": a:expr}
|
2013-01-05 03:26:50 +00:00
|
|
|
let options = a:0 ? a:1 : {}
|
|
|
|
if has_key(options, 'ns')
|
|
|
|
let payload.ns = options.ns
|
2012-12-04 06:06:22 +00:00
|
|
|
elseif has_key(self, 'ns')
|
|
|
|
let payload.ns = self.ns
|
|
|
|
endif
|
2013-01-05 03:26:50 +00:00
|
|
|
if get(options, 'session', 1)
|
|
|
|
if has_key(self, 'session')
|
|
|
|
let payload.session = self.session
|
|
|
|
elseif &verbose
|
|
|
|
echohl WarningMSG
|
|
|
|
echo "nREPL: server has bug preventing session support"
|
|
|
|
echohl None
|
|
|
|
endif
|
2012-12-11 01:09:13 +00:00
|
|
|
endif
|
2012-12-15 05:07:46 +00:00
|
|
|
let response = self.process(payload)
|
|
|
|
if has_key(response, 'ns') && !a:0
|
|
|
|
let self.ns = response.ns
|
|
|
|
endif
|
|
|
|
|
2012-12-15 06:32:08 +00:00
|
|
|
if has_key(response, 'value')
|
|
|
|
let response.value = response.value[-1]
|
2012-12-15 05:07:46 +00:00
|
|
|
endif
|
2012-12-15 06:32:08 +00:00
|
|
|
return response
|
2012-12-04 06:06:22 +00:00
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:nrepl_call(payload) dict abort
|
|
|
|
let in = 'ruby -rsocket -e '.s:shellesc(
|
|
|
|
\ 'begin;' .
|
|
|
|
\ 'TCPSocket.open(%(' . self.host . '), ' . self.port . ') {|s|' .
|
|
|
|
\ 's.write(ARGV.first); loop {' .
|
2012-12-31 02:20:41 +00:00
|
|
|
\ 'body = s.readpartial(8192);' .
|
|
|
|
\ 'raise %(not an nREPL server: upgrade to Leiningen 2) if body =~ /=> $/;' .
|
|
|
|
\ 'print body;' .
|
2012-12-04 06:06:22 +00:00
|
|
|
\ 'break if body.include?(%(6:statusl4:done)) }};' .
|
|
|
|
\ 'rescue; abort $!.to_s;' .
|
|
|
|
\ 'end') . ' ' .
|
|
|
|
\ s:shellesc(nrepl#foreplay_connection#bencode(a:payload))
|
|
|
|
let out = system(in)
|
|
|
|
if !v:shell_error
|
|
|
|
return nrepl#foreplay_connection#bdecode('l'.out.'e')
|
|
|
|
endif
|
|
|
|
throw 'nREPL: '.split(out, "\n")[0]
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
let s:nrepl = {
|
|
|
|
\ 'call': s:function('s:nrepl_call'),
|
|
|
|
\ 'eval': s:function('s:nrepl_eval'),
|
|
|
|
\ 'path': s:function('s:nrepl_path'),
|
|
|
|
\ 'process': s:function('s:nrepl_process')}
|
|
|
|
|
2013-01-06 21:48:38 +00:00
|
|
|
if has('ruby')
|
2012-12-04 06:06:22 +00:00
|
|
|
ruby <<
|
|
|
|
require 'timeout'
|
|
|
|
require 'socket'
|
2012-12-06 23:08:44 +00:00
|
|
|
class << ::VIM
|
|
|
|
def string_encode(str)
|
|
|
|
'"' + str.gsub(/[\000-\037"\\]/) { |x| "\\%03o" % (x.respond_to?(:ord) ? x.ord : x[0]) } + '"'
|
|
|
|
end
|
|
|
|
def let(var, value)
|
2012-12-07 14:40:33 +00:00
|
|
|
command("let #{var} = #{string_encode(value)}")
|
2012-12-06 23:08:44 +00:00
|
|
|
end
|
2012-12-04 06:06:22 +00:00
|
|
|
end
|
|
|
|
.
|
|
|
|
|
|
|
|
function! s:nrepl_call(payload) dict abort
|
|
|
|
let payload = nrepl#foreplay_connection#bencode(a:payload)
|
|
|
|
ruby <<
|
|
|
|
begin
|
|
|
|
buffer = ''
|
|
|
|
Timeout.timeout(16) do
|
2012-12-07 14:40:33 +00:00
|
|
|
TCPSocket.open(::VIM.evaluate('self.host'), ::VIM.evaluate('self.port').to_i) do |s|
|
|
|
|
s.write(::VIM.evaluate('payload'))
|
2012-12-04 06:06:22 +00:00
|
|
|
loop do
|
|
|
|
body = s.readpartial(8192)
|
2012-12-31 02:20:41 +00:00
|
|
|
raise "not an nREPL server: upgrade to Leiningen 2" if body =~ /=> $/
|
2012-12-04 06:06:22 +00:00
|
|
|
buffer << body
|
|
|
|
break if body.include?("6:statusl4:done")
|
|
|
|
end
|
2012-12-07 14:40:33 +00:00
|
|
|
::VIM.let('out', buffer)
|
2012-12-04 06:06:22 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
rescue
|
2012-12-07 14:40:33 +00:00
|
|
|
::VIM.let('err', $!.to_s)
|
2012-12-04 06:06:22 +00:00
|
|
|
end
|
|
|
|
.
|
|
|
|
if !exists('err')
|
|
|
|
return nrepl#foreplay_connection#bdecode('l'.out.'e')
|
|
|
|
endif
|
|
|
|
throw 'nREPL: '.err
|
|
|
|
endfunction
|
|
|
|
|
2013-01-06 21:48:38 +00:00
|
|
|
finish
|
|
|
|
endif
|
|
|
|
|
|
|
|
if has('python')
|
|
|
|
python << EOF
|
|
|
|
import vim
|
|
|
|
import socket
|
|
|
|
import string
|
|
|
|
import re
|
|
|
|
|
|
|
|
def foreplay_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 foreplay_let(var, value):
|
|
|
|
return vim.command('let ' + var + " = " + foreplay_string_encode(value))
|
|
|
|
|
|
|
|
def foreplay_repl_interact():
|
|
|
|
buffer = ''
|
|
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
s.settimeout(16.0)
|
|
|
|
try:
|
|
|
|
host = vim.eval('self.host')
|
|
|
|
port = int(vim.eval('self.port'))
|
|
|
|
s.connect((host, port))
|
|
|
|
s.sendall(vim.eval('payload'))
|
|
|
|
while True:
|
|
|
|
body = s.recv(8192)
|
|
|
|
if re.search("=> $", body) != None:
|
|
|
|
raise Exception("not an nREPL server: upgrade to Leiningen 2")
|
|
|
|
buffer += body
|
|
|
|
if string.find(body, '6:statusl4:done') != -1:
|
|
|
|
break
|
|
|
|
foreplay_let('out', buffer)
|
|
|
|
except Exception, e:
|
|
|
|
foreplay_let('err', str(e))
|
|
|
|
finally:
|
|
|
|
s.close()
|
|
|
|
EOF
|
|
|
|
|
|
|
|
function! s:nrepl_call(payload) dict abort
|
|
|
|
let payload = nrepl#foreplay_connection#bencode(a:payload)
|
|
|
|
python << EOF
|
|
|
|
foreplay_repl_interact()
|
|
|
|
EOF
|
|
|
|
if !exists('err')
|
|
|
|
return nrepl#foreplay_connection#bdecode('l'.out.'e')
|
|
|
|
endif
|
|
|
|
throw 'nREPL: '.err
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
finish
|
|
|
|
endif
|
|
|
|
|
2012-12-04 06:06:22 +00:00
|
|
|
" vim:set et sw=2:
|