vim-fireplace/autoload/nrepl/fireplace_connection.vim

309 lines
9.1 KiB
VimL
Raw Normal View History

2013-03-12 19:37:37 +00:00
" autoload/nrepl/fireplace_connection.vim
2012-12-04 06:06:22 +00:00
" Maintainer: Tim Pope <http://tpo.pe/>
2013-03-12 19:37:37 +00:00
if exists("g:autoloaded_nrepl_fireplace_connection") || &cp
2012-12-04 06:06:22 +00:00
finish
endif
2013-03-12 19:37:37 +00:00
let g:autoloaded_nrepl_fireplace_connection = 1
2012-12-04 06:06:22 +00:00
2014-01-07 03:41:03 +00:00
let s:python_dir = fnamemodify(expand("<sfile>"), ':p:h:h:h') . '/python'
2012-12-04 06:06:22 +00:00
function! s:function(name) abort
return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
endfunction
" Bencode {{{1
2013-03-12 19:37:37 +00:00
function! nrepl#fireplace_connection#bencode(value) abort
2012-12-04 06:06:22 +00:00
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([])
2014-01-01 00:30:54 +00:00
return 'l'.join(map(copy(a:value),'nrepl#fireplace_connection#bencode(v:val)'),'').'e'
2012-12-04 06:06:22 +00:00
elseif type(a:value) == type({})
2014-01-01 00:30:54 +00:00
return 'd'.join(values(map(copy(a:value),'nrepl#fireplace_connection#bencode(v:key).nrepl#fireplace_connection#bencode(v:val)')),'').'e'
2012-12-04 06:06:22 +00:00
else
throw "Can't bencode ".string(a:value)
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').'"'
2012-12-04 06:06:22 +00:00
else
let escaped = shellescape(a:arg)
if &shell =~# 'sh' && &shell !~# 'csh'
return substitute(escaped, '\\\n', '\n', 'g')
else
return escaped
endif
endif
endfunction
2014-01-08 01:03:06 +00:00
if !exists('s:id')
let s:vim_id = localtime()
let s:id = 0
endif
function! s:id() abort
let s:id += 1
return 'fireplace-'.hostname().'-'.s:vim_id.'-'.s:id
endfunction
2013-03-12 19:37:37 +00:00
function! nrepl#fireplace_connection#prompt() abort
return fireplace#input_host_port()
2012-12-04 06:06:22 +00:00
endfunction
2014-01-09 01:40:42 +00:00
if !exists('g:nrepl_fireplace_sessions')
let g:nrepl_fireplace_sessions = {}
endif
augroup nrepl_fireplace_connection
autocmd!
autocmd VimLeave * for s:session in values(g:nrepl_fireplace_sessions)
\ | call s:session.close()
\ | endfor
augroup END
2013-03-12 19:37:37 +00:00
function! nrepl#fireplace_connection#open(arg) abort
2012-12-04 06:06:22 +00:00
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
let client.session = client.process({'op': 'clone', 'session': 0})['new-session']
let response = client.process({'op': 'eval', 'code':
\ '(do (println "success") (symbol (str (System/getProperty "path.separator") (System/getProperty "java.class.path"))))'})
let client._path = response.value[-1]
2014-01-09 01:40:42 +00:00
if has_key(response, 'out')
let g:nrepl_fireplace_sessions[client.session] = client
else
unlet client.session
endif
2012-12-04 06:06:22 +00:00
return client
endfunction
2014-01-09 01:40:42 +00:00
function! s:nrepl_close() dict abort
if has_key(self, 'session')
try
unlet! g:nrepl_fireplace_sessions[self.session]
call self.message({'op': 'close'})
2014-01-09 01:40:42 +00:00
catch //
finally
unlet self.session
endtry
endif
return self
endfunction
2012-12-04 06:06:22 +00:00
function! s:nrepl_path() dict abort
return split(self._path[1:-1], self._path[0])
endfunction
function! s:nrepl_process(responses) dict abort
2012-12-15 05:07:46 +00:00
let combined = {'status': [], 'session': []}
for response in self.message(a:responses)
2012-12-15 05:07:46 +00:00
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 msg = {"op": "eval"}
let msg.code = a:expr
let options = a:0 ? a:1 : {}
if has_key(options, 'ns')
let msg.ns = options.ns
2012-12-04 06:06:22 +00:00
elseif has_key(self, 'ns')
let msg.ns = self.ns
2012-12-04 06:06:22 +00:00
endif
2014-01-08 00:46:40 +00:00
if has_key(options, 'session')
let msg.session = options.session
endif
if has_key(options, 'file_path')
let msg.op = 'load-file'
let msg['file-path'] = options.file_path
let msg['file-name'] = fnamemodify(options.file_path, ':t')
if has_key(msg, 'ns')
let msg.file = "(in-ns '".msg.ns.") ".msg.code
call remove(msg, 'ns')
else
let msg.file = msg.code
endif
call remove(msg, 'code')
endif
let response = self.process(msg)
2012-12-15 05:07:46 +00:00
if has_key(response, 'ns') && !a:0
let self.ns = response.ns
endif
if has_key(response, 'ex') && !empty(get(msg, 'session', 1))
let response.stacktrace = s:extract_last_stacktrace(self)
endif
if has_key(response, 'value')
let response.value = response.value[-1]
2012-12-15 05:07:46 +00:00
endif
return response
2012-12-04 06:06:22 +00:00
endfunction
2014-01-07 03:58:43 +00:00
function! s:extract_last_stacktrace(nrepl) abort
let format_st = '(clojure.core/symbol (clojure.core/str "\n\b" (clojure.core/apply clojure.core/str (clojure.core/interleave (clojure.core/repeat "\n") (clojure.core/map clojure.core/str (.getStackTrace *e)))) "\n\b\n"))'
let stacktrace = split(get(split(a:nrepl.process({'op': 'eval', 'code': '['.format_st.' *3 *2 *1]', 'session': a:nrepl.session}).value[0], "\n\b\n"), 1, ""), "\n")
call a:nrepl.message({'op': 'eval', 'code': '(nth *1 1)', 'session': a:nrepl.session})
call a:nrepl.message({'op': 'eval', 'code': '(nth *2 2)', 'session': a:nrepl.session})
call a:nrepl.message({'op': 'eval', 'code': '(nth *3 3)', 'session': a:nrepl.session})
return stacktrace
endfunction
let s:keepalive = tempname()
call writefile([getpid()], s:keepalive)
function! s:nrepl_command(cmd, args) dict abort
2014-01-08 05:50:46 +00:00
return 'python'
2014-01-07 03:41:03 +00:00
\ . ' ' . s:shellesc(s:python_dir.'/nrepl_fireplace.py')
\ . ' ' . s:shellesc(self.host)
\ . ' ' . s:shellesc(self.port)
\ . ' ' . s:shellesc(s:keepalive)
\ . ' ' . s:shellesc(a:cmd)
\ . ' ' . join(map(copy(a:args), 's:shellesc(nrepl#fireplace_connection#bencode(v:val))'), ' ')
2014-01-08 05:50:46 +00:00
endfunction
function! s:nrepl_dispatch(cmd, ...) dict abort
let in = self.command(a:cmd, a:000)
2012-12-04 06:06:22 +00:00
let out = system(in)
if !v:shell_error
return eval(out)
2012-12-04 06:06:22 +00:00
endif
2014-01-07 03:41:03 +00:00
throw 'nREPL: '.out
2012-12-04 06:06:22 +00:00
endfunction
function! s:nrepl_prepare(msg) dict abort
let msg = copy(a:msg)
if !has_key(msg, 'id')
let msg.id = s:id()
2014-01-08 01:03:06 +00:00
endif
if empty(get(msg, 'session', 1))
unlet msg.session
2014-01-08 00:46:40 +00:00
elseif !has_key(self, 'session')
if &verbose
echohl WarningMSG
echo "nREPL: server has bug preventing session support"
echohl None
endif
unlet! msg.session
elseif !has_key(msg, 'session')
let msg.session = self.session
2014-01-08 00:46:40 +00:00
endif
return msg
2014-01-08 04:28:39 +00:00
endfunction
function! nrepl#fireplace_connection#callback(body, fn)
let response = {'body': a:body}
if has_key(a:body, 'session')
let response.session = g:nrepl_fireplace_sessions[a:body.session]
endif
call call(a:fn, [response])
endfunction
function! s:nrepl_call(msg, ...) dict abort
let payload = nrepl#fireplace_connection#bencode(a:msg)
let terms = a:0 ? a:1 : ['done']
let sels = a:0 > 1 ? a:2 : {}
let response = self.dispatch('call', payload, terms, sels)
if a:0 > 2
return map(response, 'nrepl#fireplace_connection#callback(v:val, a:3)')
else
return response
endif
endfunction
function! s:nrepl_message(msg, ...) dict abort
let msg = self.prepare(a:msg)
let sel = {'id': msg.id}
return call(self.call, [msg, ['done'], sel] + a:000, self)
endfunction
2012-12-04 06:06:22 +00:00
let s:nrepl = {
2014-01-09 01:40:42 +00:00
\ 'close': s:function('s:nrepl_close'),
2014-01-08 05:50:46 +00:00
\ 'command': s:function('s:nrepl_command'),
\ 'dispatch': s:function('s:nrepl_dispatch'),
2014-01-08 04:28:39 +00:00
\ 'prepare': s:function('s:nrepl_prepare'),
2012-12-04 06:06:22 +00:00
\ 'call': s:function('s:nrepl_call'),
\ 'message': s:function('s:nrepl_message'),
2012-12-04 06:06:22 +00:00
\ 'eval': s:function('s:nrepl_eval'),
\ 'path': s:function('s:nrepl_path'),
\ 'process': s:function('s:nrepl_process')}
2014-01-08 04:26:26 +00:00
if !has('python') || $FIREPLACE_NO_IF_PYTHON
finish
endif
2014-01-07 03:41:03 +00:00
if !exists('s:python')
exe 'python sys.path.insert(0, "'.escape(s:python_dir, '\"').'")'
2014-01-07 03:41:03 +00:00
let s:python = 1
python import nrepl_fireplace
else
python reload(nrepl_fireplace)
endif
python << EOF
import vim
2013-03-12 19:37:37 +00:00
def fireplace_let(var, value):
return vim.command('let ' + var + ' = ' + nrepl_fireplace.vim_encode(value))
2014-01-07 03:41:03 +00:00
def fireplace_check():
vim.eval('getchar(1)')
def fireplace_repl_dispatch(command, *args):
try:
2014-01-08 04:53:20 +00:00
fireplace_let('out', nrepl_fireplace.dispatch(vim.eval('self.host'), vim.eval('self.port'), fireplace_check, None, command, *args))
2014-01-07 03:41:03 +00:00
except Exception, e:
fireplace_let('err', str(e))
EOF
function! s:nrepl_dispatch(command, ...) dict abort
python fireplace_repl_dispatch(vim.eval('a:command'), *vim.eval('a:000'))
if !exists('err')
return out
endif
throw 'nREPL Connection Error: '.err
endfunction
2012-12-04 06:06:22 +00:00
" vim:set et sw=2: