vim-fireplace/plugin/fireplace.vim

1569 lines
47 KiB
VimL
Raw Normal View History

2013-03-12 19:37:37 +00:00
" fireplace.vim - Clojure REPL tease
2013-01-16 01:04:46 +00:00
" Maintainer: Tim Pope <http://tpo.pe/>
2012-12-04 06:06:22 +00:00
2013-03-12 19:37:37 +00:00
if exists("g:loaded_fireplace") || v:version < 700 || &cp
2012-12-04 06:06:22 +00:00
finish
endif
2013-03-12 19:37:37 +00:00
let g:loaded_fireplace = 1
2012-12-04 06:06:22 +00:00
" File type {{{1
2013-03-12 19:37:37 +00:00
augroup fireplace_file_type
2012-12-04 06:06:22 +00:00
autocmd!
autocmd BufNewFile,BufReadPost *.clj setfiletype clojure
augroup END
" }}}1
" Escaping {{{1
2012-12-04 06:06:22 +00:00
2014-01-07 03:58:43 +00:00
function! s:str(string) abort
return '"' . escape(a:string, '"\') . '"'
endfunction
2014-01-07 03:58:43 +00:00
function! s:qsym(symbol) abort
2013-02-10 18:25:45 +00:00
if a:symbol =~# '^[[:alnum:]?*!+/=<>.:-]\+$'
return "'".a:symbol
else
return '(symbol '.s:str(a:symbol).')'
endif
endfunction
function! s:to_ns(path) abort
return tr(substitute(a:path, '\.\w\+$', '', ''), '\/_', '..-')
endfunction
2012-12-04 06:06:22 +00:00
" }}}1
" Completion {{{1
2012-12-26 00:01:51 +00:00
let s:jar_contents = {}
2013-03-12 19:37:37 +00:00
function! fireplace#jar_contents(path) abort
if !exists('s:zipinfo')
if executable('zipinfo')
let s:zipinfo = 'zipinfo -1 '
elseif executable('python')
let s:zipinfo = 'python -c '.shellescape('import zipfile, sys; print chr(10).join(zipfile.ZipFile(sys.argv[1]).namelist())').' '
else
let s:zipinfo = ''
endif
endif
if !has_key(s:jar_contents, a:path) && has('python')
python import vim, zipfile
python vim.command("let s:jar_contents[a:path] = split('" + "\n".join(zipfile.ZipFile(vim.eval('a:path')).namelist()) + "', \"\n\")")
elseif !has_key(s:jar_contents, a:path) && !empty(s:zipinfo)
let s:jar_contents[a:path] = split(system(s:zipinfo.shellescape(a:path)), "\n")
2012-12-26 00:01:51 +00:00
if v:shell_error
let s:jar_contents[a:path] = []
2012-12-26 00:01:51 +00:00
endif
endif
2012-12-26 00:01:51 +00:00
return copy(get(s:jar_contents, a:path, []))
endfunction
2013-03-12 19:37:37 +00:00
function! fireplace#eval_complete(A, L, P) abort
2012-12-04 06:06:22 +00:00
let prefix = matchstr(a:A, '\%(.* \|^\)\%(#\=[\[{('']\)*')
let keyword = a:A[strlen(prefix) : -1]
2013-03-12 19:37:37 +00:00
return sort(map(fireplace#omnicomplete(0, keyword), 'prefix . v:val.word'))
2012-12-04 06:06:22 +00:00
endfunction
2013-03-12 19:37:37 +00:00
function! fireplace#ns_complete(A, L, P) abort
2012-12-04 06:06:22 +00:00
let matches = []
2014-01-16 02:36:58 +00:00
for dir in fireplace#path()
2012-12-26 00:01:51 +00:00
if dir =~# '\.jar$'
2013-03-12 19:37:37 +00:00
let files = filter(fireplace#jar_contents(dir), 'v:val =~# "\\.clj$"')
else
let files = split(glob(dir."/**/*.clj", 1), "\n")
call map(files, 'v:val[strlen(dir)+1 : -1]')
endif
let matches += files
2012-12-04 06:06:22 +00:00
endfor
2013-02-10 18:25:45 +00:00
return filter(map(matches, 's:to_ns(v:val)'), 'a:A ==# "" || a:A ==# v:val[0 : strlen(a:A)-1]')
2012-12-04 06:06:22 +00:00
endfunction
2013-03-12 19:37:37 +00:00
function! fireplace#omnicomplete(findstart, base) abort
2012-12-04 06:06:22 +00:00
if a:findstart
let line = getline('.')[0 : col('.')-2]
return col('.') - strlen(matchstr(line, '\k\+$')) - 1
else
try
let omnifier = '(fn [[k v]] (let [{:keys [arglists] :as m} (meta v)]' .
\ ' {:word k :menu (pr-str (or arglists (symbol ""))) :info (str (when arglists (str arglists "\n")) " " (:doc m)) :kind (if arglists "f" "v")}))'
2012-12-04 06:06:22 +00:00
2013-03-12 19:37:37 +00:00
let ns = fireplace#ns()
2012-12-04 06:06:22 +00:00
2013-03-12 19:37:37 +00:00
let [aliases, namespaces, maps] = fireplace#evalparse(
2012-12-04 06:06:22 +00:00
\ '[(ns-aliases '.s:qsym(ns).') (all-ns) '.
\ '(sort-by :word (map '.omnifier.' (ns-map '.s:qsym(ns).')))]')
if a:base =~# '^[^/]*/[^/]*$'
let ns = matchstr(a:base, '^.*\ze/')
let prefix = ns . '/'
let ns = get(aliases, ns, ns)
let keyword = matchstr(a:base, '.*/\zs.*')
2013-03-12 19:37:37 +00:00
let results = fireplace#evalparse(
\ '(sort-by :word (map '.omnifier.' (ns-publics '.s:qsym(ns).')))')
for r in results
let r.word = prefix . r.word
2012-12-04 06:06:22 +00:00
endfor
else
let keyword = a:base
let results = maps + map(sort(keys(aliases) + namespaces), '{"word": v:val."/", "kind": "t", "info": ""}')
2012-12-04 06:06:22 +00:00
endif
if type(results) == type([])
return filter(results, 'a:base ==# "" || a:base ==# v:val.word[0 : strlen(a:base)-1]')
2012-12-04 06:06:22 +00:00
else
return []
endif
catch /.*/
return []
endtry
endif
endfunction
2013-03-12 19:37:37 +00:00
augroup fireplace_completion
2012-12-04 06:06:22 +00:00
autocmd!
2013-03-12 19:37:37 +00:00
autocmd FileType clojure setlocal omnifunc=fireplace#omnicomplete
2012-12-04 06:06:22 +00:00
augroup END
" }}}1
" REPL client {{{1
let s:repl = {"requires": {}}
if !exists('s:repls')
let s:repls = []
let s:repl_paths = {}
let s:repl_portfiles = {}
2012-12-04 06:06:22 +00:00
endif
function! s:repl.user_ns() abort
return 'user'
endfunction
function! s:repl.path() dict abort
return self.connection.path()
endfunction
function! s:conn_try(connection, function, ...) abort
2012-12-04 06:06:22 +00:00
try
return call(a:connection[a:function], a:000, a:connection)
2013-01-24 21:36:09 +00:00
catch /^\w\+ Connection Error:/
call s:unregister_connection(a:connection)
2012-12-04 06:06:22 +00:00
throw v:exception
endtry
2014-01-12 05:48:43 +00:00
endfunction
function! s:repl.eval(expr, options) dict abort
2014-01-16 04:37:41 +00:00
if has_key(a:options, 'ns') && a:options.ns !=# self.user_ns()
let error = self.preload(a:options.ns)
if !empty(error)
return error
endif
endif
return s:conn_try(self.connection, 'eval', a:expr, a:options)
2012-12-04 06:06:22 +00:00
endfunction
2014-01-16 04:37:41 +00:00
function! s:repl.message(payload, ...) dict abort
if has_key(a:payload, 'ns') && a:payload.ns !=# self.user_ns()
let ignored_error = self.preload(a:payload.ns)
2014-01-16 04:37:41 +00:00
endif
return call('s:conn_try', [self.connection, 'message', a:payload] + a:000, self)
endfunction
2014-01-16 04:37:41 +00:00
function! s:repl.preload(lib) dict abort
if !empty(a:lib) && a:lib !=# self.user_ns() && !get(self.requires, a:lib)
let reload = has_key(self.requires, a:lib) ? ' :reload' : ''
let self.requires[a:lib] = 0
let clone = s:conn_try(self.connection, 'clone')
2014-01-12 05:13:32 +00:00
try
2014-01-19 04:15:53 +00:00
let result = clone.eval('(ns '.self.user_ns().' (:require '.a:lib.reload.'))', {'ns': self.user_ns()})
2014-01-12 05:13:32 +00:00
finally
call clone.close()
endtry
2012-12-15 20:41:33 +00:00
let self.requires[a:lib] = !has_key(result, 'ex')
if has_key(result, 'ex')
2014-01-12 05:13:32 +00:00
return result
endif
2012-12-04 06:06:22 +00:00
endif
2014-01-12 05:13:32 +00:00
return {}
2012-12-04 06:06:22 +00:00
endfunction
2014-01-18 00:37:46 +00:00
let s:piggieback = copy(s:repl)
2014-01-19 22:56:32 +00:00
function! s:repl.piggieback(arg, ...) abort
if a:0 && a:1
if len(self.piggiebacks)
call remove(self.piggiebacks, 0)
endif
return {}
endif
if empty(a:arg)
let arg = ''
else
let arg = ' :repl-env ' . a:arg
endif
let connection = s:conn_try(self.connection, 'clone')
let response = connection.eval('(cemerick.piggieback/cljs-repl'.arg.')')
if empty(get(response, 'ex'))
call insert(self.piggiebacks, extend({'connection': connection}, deepcopy(s:piggieback)))
return {}
endif
call connection.close()
return response
endfunction
2014-01-18 00:37:46 +00:00
function! s:piggieback.user_ns() abort
return 'cljs.user'
endfunction
2014-01-18 03:16:18 +00:00
function! s:piggieback.eval(expr, options) abort
let options = copy(a:options)
if has_key(options, 'file_path')
call remove(options, 'file_path')
endif
return call(s:repl.eval, [a:expr, options], self)
endfunction
2014-01-07 03:58:43 +00:00
function! s:register_connection(conn, ...) abort
2014-01-18 00:37:46 +00:00
call insert(s:repls, extend({'connection': a:conn, 'piggiebacks': []}, deepcopy(s:repl)))
2012-12-04 06:06:22 +00:00
if a:0 && a:1 !=# ''
let s:repl_paths[a:1] = s:repls[0]
endif
return s:repls[0]
endfunction
2014-01-17 09:24:46 +00:00
function! s:unregister_connection(conn) abort
call filter(s:repl_paths, 'v:val.connection.transport isnot# a:conn.transport')
call filter(s:repls, 'v:val.connection.transport isnot# a:conn.transport')
call filter(s:repl_portfiles, 'v:val.connection.transport isnot# a:conn.transport')
endfunction
function! fireplace#register_port_file(portfile, ...) abort
let old = get(s:repl_portfiles, a:portfile, {})
if has_key(old, 'time') && getftime(a:portfile) !=# old.time
call s:unregister_connection(old.connection)
let old = {}
endif
if empty(old) && getfsize(a:portfile) > 0
let port = matchstr(readfile(a:portfile, 'b', 1)[0], '\d\+')
let s:repl_portfiles[a:portfile] = {'time': getftime(a:portfile)}
try
let conn = nrepl#fireplace_connection#open(port)
let s:repl_portfiles[a:portfile].connection = conn
call s:register_connection(conn, a:0 ? a:1 : '')
return conn
catch /^nREPL Connection Error:/
if &verbose
echohl WarningMSG
echomsg v:exception
echohl None
endif
return {}
endtry
else
return get(old, 'connection', {})
endif
2014-01-17 09:24:46 +00:00
endfunction
2012-12-04 06:06:22 +00:00
" }}}1
" :Connect {{{1
command! -bar -complete=customlist,s:connect_complete -nargs=* FireplaceConnect :exe s:Connect(<f-args>)
2012-12-04 06:06:22 +00:00
2014-01-07 03:58:43 +00:00
function! fireplace#input_host_port() abort
2012-12-04 06:06:22 +00:00
let arg = input('Host> ', 'localhost')
if arg ==# ''
return ''
endif
echo "\n"
let arg .= ':' . input('Port> ')
if arg =~# ':$'
return ''
endif
echo "\n"
return arg
endfunction
2014-01-07 03:58:43 +00:00
function! s:protos() abort
2013-03-12 19:37:37 +00:00
return map(split(globpath(&runtimepath, 'autoload/*/fireplace_connection.vim'), "\n"), 'fnamemodify(v:val, ":h:t")')
2012-12-04 06:06:22 +00:00
endfunction
2014-01-07 03:58:43 +00:00
function! s:connect_complete(A, L, P) abort
2012-12-04 06:06:22 +00:00
let proto = matchstr(a:A, '\w\+\ze://')
if proto ==# ''
let options = map(s:protos(), 'v:val."://"')
else
let rest = matchstr(a:A, '://\zs.*')
try
2013-03-12 19:37:37 +00:00
let options = {proto}#fireplace_connection#complete(rest)
2012-12-04 06:06:22 +00:00
catch /^Vim(let):E117/
let options = ['localhost:']
endtry
call map(options, 'proto."://".v:val')
endif
if a:A !=# ''
call filter(options, 'v:val[0 : strlen(a:A)-1] ==# a:A')
endif
return options
endfunction
2014-01-07 03:58:43 +00:00
function! s:Connect(...) abort
if (a:0 ? a:1 : '') =~# '^\w\+://'
let [proto, arg] = split(a:1, '://')
elseif a:0
2012-12-04 06:06:22 +00:00
return 'echoerr '.string('Usage: :Connect proto://...')
else
let protos = s:protos()
if empty(protos)
return 'echoerr '.string('No protocols available')
endif
let proto = s:inputlist('Protocol> ', protos)
if proto ==# ''
return
endif
redraw!
echo ':Connect'
echo 'Protocol> '.proto
2013-03-12 19:37:37 +00:00
let arg = {proto}#fireplace_connection#prompt()
2012-12-04 06:06:22 +00:00
endif
try
2013-03-12 19:37:37 +00:00
let connection = {proto}#fireplace_connection#open(arg)
2012-12-04 06:06:22 +00:00
catch /.*/
return 'echoerr '.string(v:exception)
endtry
if type(connection) !=# type({}) || empty(connection)
return ''
endif
let client = s:register_connection(connection)
echo 'Connected to '.proto.'://'.arg
let path = fnamemodify(exists('b:java_root') ? b:java_root : fnamemodify(expand('%'), ':p:s?.*\zs[\/]src[\/].*??'), ':~')
let root = a:0 > 1 ? expand(a:2) : input('Scope connection to: ', path, 'dir')
if root !=# '' && root !=# '-'
let s:repl_paths[fnamemodify(root, ':p:s?.\zs[\/]$??')] = client
2012-12-04 06:06:22 +00:00
endif
return ''
endfunction
2014-01-19 22:56:32 +00:00
function! s:piggieback(arg, remove) abort
let response = fireplace#platform().piggieback(a:arg, a:remove)
call s:output_response(response)
endfunction
2013-03-12 19:37:37 +00:00
augroup fireplace_connect
2012-12-04 06:06:22 +00:00
autocmd!
autocmd FileType clojure command! -bar -complete=customlist,s:connect_complete -nargs=* Connect :FireplaceConnect <args>
autocmd FileType clojure command! -complete=customlist,fireplace#eval_complete -bang -nargs=* Piggieback :call s:piggieback(<q-args>, <bang>0)
2012-12-04 06:06:22 +00:00
augroup END
" }}}1
" Java runner {{{1
let s:oneoff_pr = tempname()
let s:oneoff_ex = tempname()
2012-12-26 00:14:45 +00:00
let s:oneoff_stk = tempname()
2012-12-04 06:06:22 +00:00
let s:oneoff_in = tempname()
let s:oneoff_out = tempname()
let s:oneoff_err = tempname()
2014-01-17 10:03:09 +00:00
function! s:spawning_eval(classpath, expr, ns) abort
if a:ns !=# '' && a:ns !=# 'user'
let ns = '(require '.s:qsym(a:ns).') (in-ns '.s:qsym(a:ns).') '
2012-12-04 06:06:22 +00:00
else
let ns = ''
endif
call writefile([], s:oneoff_pr, 'b')
call writefile([], s:oneoff_ex, 'b')
2012-12-26 00:14:45 +00:00
call writefile([], s:oneoff_stk, 'b')
2012-12-04 06:06:22 +00:00
call writefile(split('(do '.a:expr.')', "\n"), s:oneoff_in, 'b')
call writefile([], s:oneoff_out, 'b')
call writefile([], s:oneoff_err, 'b')
2013-01-07 00:53:37 +00:00
let java_cmd = exists('$JAVA_CMD') ? $JAVA_CMD : 'java'
2014-01-17 10:03:09 +00:00
let command = java_cmd.' -cp '.shellescape(a:classpath).' clojure.main -e ' .
\ shellescape(
\ '(binding [*out* (java.io.FileWriter. '.s:str(s:oneoff_out).')' .
\ ' *err* (java.io.FileWriter. '.s:str(s:oneoff_err).')]' .
2012-12-04 06:06:22 +00:00
\ ' (try' .
\ ' (require ''clojure.repl) '.ns.'(spit '.s:str(s:oneoff_pr).' (pr-str (eval (read-string (slurp '.s:str(s:oneoff_in).')))))' .
2012-12-04 06:06:22 +00:00
\ ' (catch Exception e' .
\ ' (spit *err* (.toString e))' .
\ ' (spit '.s:str(s:oneoff_ex).' (class e))' .
\ ' (spit '.s:str(s:oneoff_stk).' (apply str (interpose "\n" (.getStackTrace e))))))' .
2012-12-04 06:06:22 +00:00
\ ' nil)')
let captured = system(command)
let result = {}
let result.value = join(readfile(s:oneoff_pr, 'b'), "\n")
let result.out = join(readfile(s:oneoff_out, 'b'), "\n")
let result.err = join(readfile(s:oneoff_err, 'b'), "\n")
2012-12-26 01:26:45 +00:00
let result.ex = join(readfile(s:oneoff_ex, 'b'), "\n")
2012-12-26 00:14:45 +00:00
let result.stacktrace = readfile(s:oneoff_stk)
2012-12-26 01:26:45 +00:00
call filter(result, '!empty(v:val)')
if v:shell_error && get(result, 'ex', '') ==# ''
throw 'Error running Java: '.get(split(captured, "\n"), -1, '')
2012-12-04 06:06:22 +00:00
else
return result
2012-12-04 06:06:22 +00:00
endif
endfunction
2014-01-17 10:03:09 +00:00
let s:oneoff = {}
function! s:oneoff.user_ns() abort
return 'user'
endfunction
function! s:oneoff.path() dict abort
2014-04-09 16:43:35 +00:00
return self._path
2014-01-17 10:03:09 +00:00
endfunction
function! s:oneoff.eval(expr, options) dict abort
if !empty(get(a:options, 'session', 1))
throw 'Fireplace: no live REPL connection'
2014-01-17 10:03:09 +00:00
endif
2014-04-09 16:43:35 +00:00
return s:spawning_eval(join(self.path(), has('win32') ? ';' : ':'),
\ a:expr, get(a:options, 'ns', self.user_ns()))
2014-01-17 10:03:09 +00:00
endfunction
2014-01-19 22:56:32 +00:00
function! s:oneoff.message(...) abort
throw 'Fireplace: no live REPL connection'
endfunction
2014-01-19 22:56:32 +00:00
let s:oneoff.piggieback = s:oneoff.message
2012-12-04 06:06:22 +00:00
" }}}1
" Client {{{1
function! s:buf() abort
2013-01-13 15:49:54 +00:00
if exists('s:input')
return s:input
2013-01-13 15:49:54 +00:00
elseif has_key(s:qffiles, expand('%:p'))
return s:qffiles[expand('%:p')].buffer
2013-01-13 15:49:54 +00:00
else
return '%'
2013-01-13 15:49:54 +00:00
endif
endfunction
2014-01-13 00:54:08 +00:00
function! s:includes_file(file, path) abort
let file = substitute(a:file, '\C^zipfile:\(.*\)::', '\1/', '')
let file = substitute(file, '\C^fugitive:[\/][\/]\(.*\)\.git[\/][\/][^\/]\+[\/]', '\1', '')
for path in a:path
if file[0 : len(path)-1] ==? path
return 1
endif
endfor
endfunction
2014-04-09 16:43:35 +00:00
function! s:path_extract(path)
let path = []
if a:path =~# '\.jar'
for elem in split(substitute(a:path, ',$', '', ''), ',')
if elem ==# ''
let path += ['.']
else
let path += split(glob(substitute(elem, '\\\ze[\\ ,]', '', 'g'), 1), "\n")
endif
endfor
2014-04-09 16:43:35 +00:00
endif
return path
2014-04-09 16:43:35 +00:00
endfunction
function! fireplace#path(...) abort
let buf = a:0 ? a:1 : s:buf()
for repl in s:repls
2014-01-13 00:54:08 +00:00
if s:includes_file(fnamemodify(bufname(buf), ':p'), repl.path())
return repl.path()
endif
endfor
2014-04-09 16:43:35 +00:00
return s:path_extract(getbufvar(buf, '&path'))
endfunction
2014-01-18 00:37:46 +00:00
function! fireplace#platform(...) abort
for [k, v] in items(s:repl_portfiles)
if getftime(k) != v.time
call s:unregister_connection(v.connection)
endif
endfor
let portfile = findfile('.nrepl-port', '.;')
if !empty(portfile)
call fireplace#register_port_file(portfile, fnamemodify(portfile, ':h'))
endif
silent doautocmd User FireplacePreConnect
let buf = a:0 ? a:1 : s:buf()
2012-12-04 06:06:22 +00:00
let root = simplify(fnamemodify(bufname(buf), ':p:s?[\/]$??'))
let previous = ""
while root !=# previous
if has_key(s:repl_paths, root)
return s:repl_paths[root]
endif
let previous = root
let root = fnamemodify(root, ':h')
endwhile
for repl in s:repls
2014-01-16 00:25:55 +00:00
if s:includes_file(fnamemodify(bufname(buf), ':p'), repl.path())
2012-12-04 06:06:22 +00:00
return repl
endif
endfor
2014-04-09 16:43:35 +00:00
let path = s:path_extract(getbufvar(buf, '&path'))
if !empty(path) && fnamemodify(bufname(buf), ':e') =~# '^cljx\=$'
return extend({'_path': path, 'nr': bufnr(buf)}, s:oneoff)
endif
throw 'Fireplace: :Connect to a REPL or install classpath.vim'
2012-12-04 06:06:22 +00:00
endfunction
2014-01-18 00:37:46 +00:00
function! fireplace#client(...) abort
let buf = a:0 ? a:1 : s:buf()
let client = fireplace#platform(buf)
if fnamemodify(bufname(buf), ':e') ==# 'cljs'
if !has_key(client, 'connection')
throw 'Fireplace: no live REPL connection'
2014-01-18 00:37:46 +00:00
endif
if empty(client.piggiebacks)
2014-01-19 22:56:32 +00:00
let result = client.piggieback('')
2014-01-18 00:37:46 +00:00
if has_key(result, 'ex')
return result
endif
endif
return client.piggiebacks[0]
endif
return client
endfunction
function! fireplace#message(payload, ...) abort
2014-01-16 02:36:58 +00:00
let client = fireplace#client()
let payload = copy(a:payload)
if !has_key(payload, 'ns')
let payload.ns = fireplace#ns()
elseif empty(payload.ns)
unlet payload.ns
endif
return call(client.message, [payload] + a:000, client)
endfunction
2014-04-13 00:06:12 +00:00
function! fireplace#op_available(op) abort
let client = fireplace#platform()
if has_key(client, 'connection')
return client.connection.has_op(a:op)
endif
endfunction
function! fireplace#findresource(resource, ...) abort
if a:resource ==# ''
return ''
endif
2014-01-15 23:14:32 +00:00
let resource = a:resource
if a:0 > 2 && type(a:3) == type([])
let suffixes = a:3
else
let suffixes = [''] + split(get(a:000, 2, ''), ',')
endif
2014-01-15 23:14:32 +00:00
for dir in a:0 ? a:1 : fireplace#path()
for suffix in suffixes
if fnamemodify(dir, ':e') ==# 'jar' && index(fireplace#jar_contents(dir), resource . suffix) >= 0
return 'zipfile:' . dir . '::' . resource . suffix
elseif filereadable(dir . '/' . resource . suffix)
return dir . (exists('+shellslash') && !&shellslash ? '\' : '/') . resource . suffix
endif
endfor
endfor
return ''
endfunction
2014-01-31 04:13:35 +00:00
function! s:qfmassage(line, path) abort
let entry = {'text': a:line}
let match = matchlist(a:line, '\(\S\+\)\s\=(\(\S\+\))')
if !empty(match)
let [_, class, file; __] = match
if file =~# '^NO_SOURCE_FILE:' || file !~# ':'
let entry.resource = ''
let entry.lnum = 0
else
let truncated = substitute(class, '\.[A-Za-z0-9_]\+\%([$/].*\)$', '', '')
let entry.resource = tr(truncated, '.', '/').'/'.split(file, ':')[0]
let entry.lnum = split(file, ':')[-1]
endif
2014-01-31 04:13:35 +00:00
let entry.filename = fireplace#findresource(entry.resource, a:path)
if empty(entry.filename)
let entry.lnum = 0
else
2014-01-31 04:13:35 +00:00
let entry.text = class
endif
2014-01-31 04:13:35 +00:00
endif
return entry
endfunction
function! fireplace#quickfix_for(stacktrace) abort
let path = fireplace#path()
return map(copy(a:stacktrace), 's:qfmassage(v:val, path)')
endfunction
function! s:output_response(response) abort
let substitution_pat = '\e\[[0-9;]*m\|\r\|\n$'
if get(a:response, 'err', '') !=# ''
echohl ErrorMSG
echo substitute(a:response.err, substitution_pat, '', 'g')
echohl NONE
endif
if get(a:response, 'out', '') !=# ''
echo substitute(a:response.out, substitution_pat, '', 'g')
endif
endfunction
function! s:eval(expr, ...) abort
let options = a:0 ? copy(a:1) : {}
2014-01-16 02:36:58 +00:00
let client = fireplace#client()
if !has_key(options, 'ns')
2013-03-12 19:37:37 +00:00
let options.ns = fireplace#ns()
endif
return client.eval(a:expr, options)
endfunction
2013-01-13 15:49:54 +00:00
function! s:temp_response(response) abort
let output = []
if get(a:response, 'err', '') !=# ''
let output = map(split(a:response.err, "\n"), '";!!".v:val')
endif
2013-01-13 15:49:54 +00:00
if get(a:response, 'out', '') !=# ''
let output = map(split(a:response.out, "\n"), '";".v:val')
endif
if has_key(a:response, 'value')
let output += [a:response.value]
endif
let temp = tempname().'.clj'
call writefile(output, temp)
return temp
endfunction
if !exists('s:history')
let s:history = []
endif
if !exists('s:qffiles')
let s:qffiles = {}
endif
function! s:qfentry(entry) abort
if !has_key(a:entry, 'tempfile')
let a:entry.tempfile = s:temp_response(a:entry.response)
endif
let s:qffiles[a:entry.tempfile] = a:entry
2013-04-11 02:33:48 +00:00
return {'filename': a:entry.tempfile, 'text': a:entry.code, 'type': 'E'}
2013-01-13 15:49:54 +00:00
endfunction
function! s:qfhistory() abort
let list = []
for entry in reverse(s:history)
if !has_key(entry, 'tempfile')
let entry.tempfile = s:temp_response(entry.response)
endif
call extend(list, [s:qfentry(entry)])
endfor
return list
endfunction
2014-01-08 00:46:40 +00:00
function! fireplace#session_eval(expr, ...) abort
let response = s:eval(a:expr, a:0 ? a:1 : {})
if !empty(get(response, 'value', '')) || !empty(get(response, 'err', ''))
2013-03-12 19:37:37 +00:00
call insert(s:history, {'buffer': bufnr(''), 'code': a:expr, 'ns': fireplace#ns(), 'response': response})
2013-01-13 15:49:54 +00:00
endif
if len(s:history) > &history
call remove(s:history, &history, -1)
endif
if !empty(get(response, 'stacktrace', []))
2013-01-13 15:49:54 +00:00
let nr = 0
if has_key(s:qffiles, expand('%:p'))
let nr = winbufnr(s:qffiles[expand('%:p')].buffer)
endif
if nr != -1
2013-03-12 19:37:37 +00:00
call setloclist(nr, fireplace#quickfix_for(response.stacktrace))
2013-01-13 15:49:54 +00:00
endif
endif
call s:output_response(response)
if get(response, 'ex', '') !=# ''
let err = 'Clojure: '.response.ex
elseif has_key(response, 'value')
return response.value
else
2013-03-12 19:37:37 +00:00
let err = 'fireplace.vim: Something went wrong: '.string(response)
endif
throw err
2012-12-04 06:06:22 +00:00
endfunction
function! fireplace#eval(...) abort
return call('fireplace#session_eval', a:000)
endfunction
function! fireplace#echo_session_eval(expr, ...) abort
2013-01-07 06:55:08 +00:00
try
echo fireplace#session_eval(a:expr, a:0 ? a:1 : {})
2013-01-07 06:55:08 +00:00
catch /^Clojure:/
catch
echohl ErrorMSG
echomsg v:exception
echohl NONE
2013-01-07 06:55:08 +00:00
endtry
return ''
endfunction
2013-03-12 19:37:37 +00:00
function! fireplace#evalprint(expr) abort
return fireplace#echo_session_eval(a:expr)
endfunction
function! fireplace#macroexpand(fn, form) abort
return fireplace#evalprint('('.a:fn.' (quote '.a:form.'))')
endfunction
2013-03-12 19:37:37 +00:00
let g:fireplace#reader =
2013-02-08 15:26:14 +00:00
\ '(symbol ((fn *vimify [x]' .
\ ' (cond' .
\ ' (map? x) (str "{" (apply str (interpose ", " (map (fn [[k v]] (str (*vimify k) ": " (*vimify v))) x))) "}")' .
\ ' (coll? x) (str "[" (apply str (interpose ", " (map *vimify x))) "]")' .
\ ' (true? x) "1"' .
\ ' (false? x) "0"' .
\ ' (number? x) (pr-str x)' .
\ ' (keyword? x) (pr-str (name x))' .
\ ' :else (pr-str (str x)))) %s))'
2013-03-12 19:37:37 +00:00
function! fireplace#evalparse(expr, ...) abort
let options = extend({'session': 0}, a:0 ? a:1 : {})
2013-03-12 19:37:37 +00:00
let response = s:eval(printf(g:fireplace#reader, a:expr), options)
call s:output_response(response)
if get(response, 'ex', '') !=# ''
let err = 'Clojure: '.response.ex
elseif has_key(response, 'value')
return empty(response.value) ? '' : eval(response.value)
2012-12-04 06:06:22 +00:00
else
2013-03-12 19:37:37 +00:00
let err = 'fireplace.vim: Something went wrong: '.string(response)
2012-12-04 06:06:22 +00:00
endif
throw err
2012-12-04 06:06:22 +00:00
endfunction
" }}}1
" Eval {{{1
2013-03-12 19:37:37 +00:00
let fireplace#skip = 'synIDattr(synID(line("."),col("."),1),"name") =~? "comment\\|string\\|char"'
2012-12-04 06:06:22 +00:00
function! s:opfunc(type) abort
let sel_save = &selection
let cb_save = &clipboard
let reg_save = @@
try
set selection=inclusive clipboard-=unnamed clipboard-=unnamedplus
if type(a:type) == type(0)
let open = '[[{(]'
let close = '[]})]'
if getline('.')[col('.')-1] =~# close
let [line1, col1] = searchpairpos(open, '', close, 'bn', g:fireplace#skip)
let [line2, col2] = [line('.'), col('.')]
else
let [line1, col1] = searchpairpos(open, '', close, 'bcn', g:fireplace#skip)
let [line2, col2] = searchpairpos(open, '', close, 'n', g:fireplace#skip)
endif
while col1 > 1 && getline(line1)[col1-2] =~# '[#''`~@]'
let col1 -= 1
endwhile
call setpos("'[", [0, line1, col1, 0])
call setpos("']", [0, line2, col2, 0])
silent exe "normal! `[v`]y"
elseif a:type =~# '^.$'
2012-12-04 06:06:22 +00:00
silent exe "normal! `<" . a:type . "`>y"
elseif a:type ==# 'line'
silent exe "normal! '[V']y"
elseif a:type ==# 'block'
silent exe "normal! `[\<C-V>`]y"
elseif a:type ==# 'outer'
2013-03-12 19:37:37 +00:00
call searchpair('(','',')', 'Wbcr', g:fireplace#skip)
2012-12-04 06:06:22 +00:00
silent exe "normal! vaby"
else
silent exe "normal! `[v`]y"
endif
redraw
return repeat("\n", line("'<")-1) . repeat(" ", col("'<")-1) . @@
2012-12-04 06:06:22 +00:00
finally
let @@ = reg_save
let &selection = sel_save
let &clipboard = cb_save
endtry
endfunction
function! s:filterop(type) abort
let reg_save = @@
let sel_save = &selection
let cb_save = &clipboard
2012-12-04 06:06:22 +00:00
try
set selection=inclusive clipboard-=unnamed clipboard-=unnamedplus
2012-12-04 06:06:22 +00:00
let expr = s:opfunc(a:type)
2013-03-12 19:37:37 +00:00
let @@ = matchstr(expr, '^\n\+').fireplace#session_eval(expr).matchstr(expr, '\n\+$')
2012-12-04 06:06:22 +00:00
if @@ !~# '^\n*$'
normal! gvp
endif
catch /^Clojure:/
return ''
finally
let @@ = reg_save
let &selection = sel_save
let &clipboard = cb_save
2012-12-04 06:06:22 +00:00
endtry
endfunction
function! s:macroexpandop(type) abort
call fireplace#macroexpand("clojure.walk/macroexpand-all", s:opfunc(a:type))
endfunction
function! s:macroexpand1op(type) abort
call fireplace#macroexpand("macroexpand-1", s:opfunc(a:type))
endfunction
2012-12-04 06:06:22 +00:00
function! s:printop(type) abort
let s:todo = s:opfunc(a:type)
2013-03-12 19:37:37 +00:00
call feedkeys("\<Plug>FireplacePrintLast")
endfunction
function! s:print_last() abort
call fireplace#echo_session_eval(s:todo, {'file_path': s:buffer_path()})
return ''
2012-12-04 06:06:22 +00:00
endfunction
function! s:editop(type) abort
call feedkeys(&cedit . "\<Home>", 'n')
let input = s:input(substitute(substitute(substitute(
\ s:opfunc(a:type), "\s*;[^\n\"]*\\%(\n\\@=\\|$\\)", '', 'g'),
\ '\n\+\s*', ' ', 'g'),
\ '^\s*', '', ''))
if input !=# ''
2013-03-12 19:37:37 +00:00
call fireplace#echo_session_eval(input)
endif
2012-12-04 06:06:22 +00:00
endfunction
function! s:Eval(bang, line1, line2, count, args) abort
let options = {}
2012-12-04 06:06:22 +00:00
if a:args !=# ''
let expr = a:args
else
if a:count ==# 0
let open = '[[{(]'
let close = '[]})]'
let [line1, col1] = searchpairpos(open, '', close, 'bcrn', g:fireplace#skip)
let [line2, col2] = searchpairpos(open, '', close, 'rn', g:fireplace#skip)
if !line1 && !line2
let [line1, col1] = searchpairpos(open, '', close, 'brn', g:fireplace#skip)
let [line2, col2] = searchpairpos(open, '', close, 'crn', g:fireplace#skip)
endif
while col1 > 1 && getline(line1)[col1-2] =~# '[#''`~@]'
let col1 -= 1
endwhile
2012-12-04 06:06:22 +00:00
else
let line1 = a:line1
let line2 = a:line2
let col1 = 1
let col2 = strlen(getline(line2))
2012-12-04 06:06:22 +00:00
endif
if !line1 || !line2
return ''
endif
let options.file_path = s:buffer_path()
let expr = repeat("\n", line1-1).repeat(" ", col1-1)
if line1 == line2
let expr .= getline(line1)[col1-1 : col2-1]
else
let expr .= getline(line1)[col1-1 : -1] . "\n"
\ . join(map(getline(line1+1, line2-1), 'v:val . "\n"'))
\ . getline(line2)[0 : col2-1]
endif
2012-12-04 06:06:22 +00:00
if a:bang
exe line1.','.line2.'delete _'
endif
endif
if a:bang
try
2014-01-12 06:04:03 +00:00
let result = fireplace#session_eval(expr, options)
2012-12-04 06:06:22 +00:00
if a:args !=# ''
call append(a:line1, result)
exe a:line1
else
call append(a:line1-1, result)
exe a:line1-1
endif
catch /^Clojure:/
endtry
else
call fireplace#echo_session_eval(expr, options)
endif
2012-12-04 06:06:22 +00:00
return ''
endfunction
" If we call input() directly inside a try, and the user opens the command
" line window and tries to switch out of it (such as with ctrl-w), Vim will
" crash when the command line window closes. Adding an indirect function call
" works around this.
2014-01-07 03:58:43 +00:00
function! s:actually_input(...) abort
2012-12-04 06:06:22 +00:00
return call(function('input'), a:000)
endfunction
function! s:input(default) abort
2013-03-12 19:37:37 +00:00
if !exists('g:FIREPLACE_HISTORY') || type(g:FIREPLACE_HISTORY) != type([])
unlet! g:FIREPLACE_HISTORY
let g:FIREPLACE_HISTORY = []
2012-12-04 06:06:22 +00:00
endif
try
let s:input = bufnr('%')
2013-03-12 19:37:37 +00:00
let s:oldhist = s:histswap(g:FIREPLACE_HISTORY)
return s:actually_input(fireplace#ns().'=> ', a:default, 'customlist,fireplace#eval_complete')
2012-12-04 06:06:22 +00:00
finally
unlet! s:input
if exists('s:oldhist')
2013-03-12 19:37:37 +00:00
let g:FIREPLACE_HISTORY = s:histswap(s:oldhist)
2012-12-04 06:06:22 +00:00
endif
endtry
endfunction
function! s:inputclose() abort
let l = substitute(getcmdline(), '"\%(\\.\|[^"]\)*"\|\\.', '', 'g')
let open = len(substitute(l, '[^(]', '', 'g'))
let close = len(substitute(l, '[^)]', '', 'g'))
if open - close == 1
return ")\<CR>"
else
return ")"
endif
endfunction
function! s:inputeval() abort
let input = s:input('')
redraw
2013-01-06 00:54:30 +00:00
if input !=# ''
2013-03-12 19:37:37 +00:00
call fireplace#echo_session_eval(input)
2012-12-04 06:06:22 +00:00
endif
2013-01-06 00:54:30 +00:00
return ''
2012-12-04 06:06:22 +00:00
endfunction
function! s:recall() abort
try
cnoremap <expr> ) <SID>inputclose()
let input = s:input('(')
if input =~# '^(\=$'
return ''
else
2013-03-12 19:37:37 +00:00
return fireplace#session_eval(input)
2012-12-04 06:06:22 +00:00
endif
catch /^Clojure:/
return ''
finally
silent! cunmap )
endtry
endfunction
function! s:histswap(list) abort
2012-12-04 06:06:22 +00:00
let old = []
for i in range(1, histnr('@') * (histnr('@') > 0))
call extend(old, [histget('@', i)])
endfor
call histdel('@')
for entry in a:list
call histadd('@', entry)
endfor
return old
endfunction
2013-03-12 19:37:37 +00:00
nnoremap <silent> <Plug>FireplacePrintLast :exe <SID>print_last()<CR>
nnoremap <silent> <Plug>FireplacePrint :<C-U>set opfunc=<SID>printop<CR>g@
xnoremap <silent> <Plug>FireplacePrint :<C-U>call <SID>printop(visualmode())<CR>
nnoremap <silent> <Plug>FireplaceCountPrint :<C-U>call <SID>printop(v:count)<CR>
2012-12-04 06:06:22 +00:00
2013-03-12 19:37:37 +00:00
nnoremap <silent> <Plug>FireplaceFilter :<C-U>set opfunc=<SID>filterop<CR>g@
xnoremap <silent> <Plug>FireplaceFilter :<C-U>call <SID>filterop(visualmode())<CR>
nnoremap <silent> <Plug>FireplaceCountFilter :<C-U>call <SID>filterop(v:count)<CR>
2012-12-04 06:06:22 +00:00
nnoremap <silent> <Plug>FireplaceMacroExpand :<C-U>set opfunc=<SID>macroexpandop<CR>g@
xnoremap <silent> <Plug>FireplaceMacroExpand :<C-U>call <SID>macroexpandop(visualmode())<CR>
nnoremap <silent> <Plug>FireplaceCountMacroExpand :<C-U>call <SID>macroexpandop(v:count)<CR>
nnoremap <silent> <Plug>Fireplace1MacroExpand :<C-U>set opfunc=<SID>macroexpand1op<CR>g@
xnoremap <silent> <Plug>Fireplace1MacroExpand :<C-U>call <SID>macroexpand1op(visualmode())<CR>
nnoremap <silent> <Plug>Fireplace1MacroExpand :<C-U>call <SID>macroexpand1op(v:count)<CR>
2013-03-12 19:37:37 +00:00
nnoremap <silent> <Plug>FireplaceEdit :<C-U>set opfunc=<SID>editop<CR>g@
xnoremap <silent> <Plug>FireplaceEdit :<C-U>call <SID>editop(visualmode())<CR>
nnoremap <silent> <Plug>FireplaceCountEdit :<C-U>call <SID>editop(v:count)<CR>
2012-12-04 06:06:22 +00:00
2013-03-12 19:37:37 +00:00
nnoremap <Plug>FireplacePrompt :exe <SID>inputeval()<CR>
2012-12-04 06:06:22 +00:00
2013-03-12 19:37:37 +00:00
noremap! <Plug>FireplaceRecall <C-R>=<SID>recall()<CR>
2012-12-04 06:06:22 +00:00
2013-01-13 15:49:54 +00:00
function! s:Last(bang, count) abort
if len(s:history) < a:count
return 'echoerr "History entry not found"'
endif
let history = s:qfhistory()
let last = s:qfhistory()[a:count-1]
execute 'pedit '.last.filename
if !&previewwindow
let nr = winnr()
wincmd p
wincmd P
endif
call setloclist(0, history)
silent exe 'llast '.(len(history)-a:count+1)
if exists('nr') && a:bang
wincmd p
exe nr.'wincmd w'
endif
return ''
endfunction
function! s:setup_eval() abort
2013-03-12 19:37:37 +00:00
command! -buffer -bang -range=0 -nargs=? -complete=customlist,fireplace#eval_complete Eval :exe s:Eval(<bang>0, <line1>, <line2>, <count>, <q-args>)
2013-01-13 15:49:54 +00:00
command! -buffer -bang -bar -count=1 Last exe s:Last(<bang>0, <count>)
2012-12-04 06:06:22 +00:00
2013-03-12 19:37:37 +00:00
nmap <buffer> cp <Plug>FireplacePrint
nmap <buffer> cpp <Plug>FireplaceCountPrint
2012-12-04 06:06:22 +00:00
2013-03-12 19:37:37 +00:00
nmap <buffer> c! <Plug>FireplaceFilter
nmap <buffer> c!! <Plug>FireplaceCountFilter
2012-12-04 06:06:22 +00:00
nmap <buffer> cm <Plug>FireplaceMacroExpand
nmap <buffer> cmm <Plug>FireplaceCountMacroExpand
nmap <buffer> c1m <Plug>Fireplace1MacroExpand
nmap <buffer> c1mm <Plug>FireplaceCount1MacroExpand
2013-03-12 19:37:37 +00:00
nmap <buffer> cq <Plug>FireplaceEdit
nmap <buffer> cqq <Plug>FireplaceCountEdit
2012-12-04 06:06:22 +00:00
2013-03-12 19:37:37 +00:00
nmap <buffer> cqp <Plug>FireplacePrompt
exe 'nmap <buffer> cqc <Plug>FireplacePrompt' . &cedit . 'i'
2012-12-04 06:06:22 +00:00
2013-03-12 19:37:37 +00:00
map! <buffer> <C-R>( <Plug>FireplaceRecall
2012-12-04 06:06:22 +00:00
endfunction
2014-01-07 03:58:43 +00:00
function! s:setup_historical() abort
setlocal readonly nomodifiable
2013-01-13 15:49:54 +00:00
nnoremap <buffer><silent>q :bdelete<CR>
endfunction
2014-01-07 03:58:43 +00:00
function! s:cmdwinenter() abort
2012-12-04 06:06:22 +00:00
setlocal filetype=clojure
endfunction
2014-01-07 03:58:43 +00:00
function! s:cmdwinleave() abort
2012-12-04 06:06:22 +00:00
setlocal filetype< omnifunc<
endfunction
2013-03-12 19:37:37 +00:00
augroup fireplace_eval
2012-12-04 06:06:22 +00:00
autocmd!
autocmd FileType clojure call s:setup_eval()
2013-04-11 02:33:48 +00:00
autocmd BufReadPost * if has_key(s:qffiles, expand('<amatch>:p')) |
2013-01-13 15:49:54 +00:00
\ call s:setup_historical() |
\ endif
2012-12-04 06:06:22 +00:00
autocmd CmdWinEnter @ if exists('s:input') | call s:cmdwinenter() | endif
autocmd CmdWinLeave @ if exists('s:input') | call s:cmdwinleave() | endif
augroup END
" }}}1
" :Require {{{1
function! s:Require(bang, echo, ns) abort
2014-01-19 04:26:27 +00:00
if expand('%:e') ==# 'cljs'
let cmd = '(load-file '.s:str(tr(a:ns ==# '' ? fireplace#ns() : a:ns, '-.', '_/').'.cljs').')'
else
2014-04-13 00:38:33 +00:00
let cmd = ('(require '.s:qsym(a:ns ==# '' ? fireplace#ns() : a:ns).' :reload'.(a:bang ? '-all' : '').')')
2014-01-19 04:26:27 +00:00
endif
if a:echo
echo cmd
endif
2012-12-04 06:06:22 +00:00
try
2014-04-13 00:38:33 +00:00
call fireplace#session_eval(cmd, {'ns': fireplace#client().user_ns()})
2012-12-04 06:06:22 +00:00
return ''
catch /^Clojure:.*/
return ''
endtry
endfunction
function! s:cpr() abort
let echo = "(require ".fireplace#ns().' :reload) (clojure.test/run-tests)'
try
call s:Require(0, 0, '')
catch /^Fireplace: no live REPL connection$/
endtry
call s:RunTests(0, 0)
echo echo
endfunction
2014-01-07 03:58:43 +00:00
function! s:setup_require() abort
command! -buffer -bar -bang -complete=customlist,fireplace#ns_complete -nargs=? Require :exe s:Require(<bang>0, 1, <q-args>)
nnoremap <silent><buffer> cpr :if expand('%:e') ==# 'cljs'<Bar>Require<Bar>else<Bar>call <SID>cpr()<Bar>endif<CR>
endfunction
2013-03-12 19:37:37 +00:00
augroup fireplace_require
2012-12-04 06:06:22 +00:00
autocmd!
autocmd FileType clojure call s:setup_require()
2012-12-04 06:06:22 +00:00
augroup END
" }}}1
" Go to source {{{1
2013-03-12 19:37:37 +00:00
function! fireplace#source(symbol) abort
2012-12-04 06:06:22 +00:00
let cmd =
\ '(when-let [v (resolve ' . s:qsym(a:symbol) .')]' .
\ ' (when-let [filepath (:file (meta v))]' .
\ ' [filepath' .
\ ' (:line (meta v))]))'
2014-01-14 21:28:21 +00:00
let result = fireplace#evalparse(cmd)
if type(result) == type([])
2014-01-15 23:14:32 +00:00
let file = fireplace#findresource(result[0])
if !empty(file)
return '+' . result[1] . ' ' . fnameescape(file)
endif
endif
2014-01-15 23:14:32 +00:00
return ''
2012-12-04 06:06:22 +00:00
endfunction
function! s:Edit(cmd, keyword) abort
2013-04-11 02:33:48 +00:00
try
2014-01-15 23:14:32 +00:00
if a:keyword =~# '^\k\+[/.]$'
2013-04-11 02:33:48 +00:00
let location = fireplace#findfile(a:keyword[0: -2])
elseif a:keyword =~# '^\k\+\.[^/.]\+$'
let location = fireplace#findfile(a:keyword)
else
let location = fireplace#source(a:keyword)
endif
catch /^Clojure:/
return ''
endtry
2012-12-04 06:06:22 +00:00
if location !=# ''
2014-01-15 23:14:32 +00:00
if matchstr(location, '^+\d\+ \zs.*') ==# fnameescape(expand('%:p')) && a:cmd ==# 'edit'
normal! m'
2012-12-04 06:06:22 +00:00
return matchstr(location, '\d\+')
else
return a:cmd.' '.location.'|let &l:path = '.string(&l:path)
endif
endif
2012-12-05 05:06:00 +00:00
let v:errmsg = "Couldn't find source for ".a:keyword
2012-12-04 06:06:22 +00:00
return 'echoerr v:errmsg'
endfunction
2013-03-12 19:37:37 +00:00
nnoremap <silent> <Plug>FireplaceDjump :<C-U>exe <SID>Edit('edit', expand('<cword>'))<CR>
nnoremap <silent> <Plug>FireplaceDsplit :<C-U>exe <SID>Edit('split', expand('<cword>'))<CR>
nnoremap <silent> <Plug>FireplaceDtabjump :<C-U>exe <SID>Edit('tabedit', expand('<cword>'))<CR>
2013-01-03 05:10:11 +00:00
2013-03-12 19:37:37 +00:00
augroup fireplace_source
2012-12-04 06:06:22 +00:00
autocmd!
autocmd FileType clojure setlocal includeexpr=tr(v:fname,'.-','/_')
2014-01-15 23:14:32 +00:00
autocmd FileType clojure
\ if expand('%:e') ==# 'cljs' |
\ setlocal suffixesadd=.cljs,.cljx,.clj,.java |
\ else |
\ setlocal suffixesadd=.clj,.cljx,.cljs,.java |
\ endif
2012-12-04 06:06:22 +00:00
autocmd FileType clojure setlocal define=^\\s*(def\\w*
2013-03-12 19:37:37 +00:00
autocmd FileType clojure command! -bar -buffer -nargs=1 -complete=customlist,fireplace#eval_complete Djump :exe s:Edit('edit', <q-args>)
autocmd FileType clojure command! -bar -buffer -nargs=1 -complete=customlist,fireplace#eval_complete Dsplit :exe s:Edit('split', <q-args>)
autocmd FileType clojure nmap <buffer> [<C-D> <Plug>FireplaceDjump
autocmd FileType clojure nmap <buffer> ]<C-D> <Plug>FireplaceDjump
autocmd FileType clojure nmap <buffer> <C-W><C-D> <Plug>FireplaceDsplit
autocmd FileType clojure nmap <buffer> <C-W>d <Plug>FireplaceDsplit
autocmd FileType clojure nmap <buffer> <C-W>gd <Plug>FireplaceDtabjump
2012-12-04 06:06:22 +00:00
augroup END
" }}}1
" Go to file {{{1
2013-03-12 19:37:37 +00:00
function! fireplace#findfile(path) abort
2012-12-04 06:06:22 +00:00
let path = a:path
2014-01-15 23:14:32 +00:00
if a:path !~# '/'
let path = tr(a:path, '.-', '/_')
else
let path = substitute(a:path, '^/', '')
2014-01-12 06:01:08 +00:00
endif
2014-01-15 23:14:32 +00:00
let resource = fireplace#findresource(path, fireplace#path(), 0, &suffixesadd)
if !empty(resource)
return resource
elseif fnamemodify(a:path, ':p') ==# a:path && filereadable(a:path)
return path
elseif a:path[0] !=# '/' && filereadable(expand('%:h') . '/' . path)
return expand('%:h') . '/' . path
2012-12-04 06:06:22 +00:00
endif
2014-01-15 23:14:32 +00:00
return ''
2012-12-04 06:06:22 +00:00
endfunction
function! s:GF(cmd, file) abort
if a:file =~# '^[^/]*/[^/.]*$' && a:file =~# '^\k\+$'
let [file, jump] = split(a:file, "/")
2014-01-15 23:14:32 +00:00
if file !~# '\.'
try
let file = fireplace#evalparse('((ns-aliases *ns*) '.s:qsym(file).' '.s:qsym(file).')')
catch /^Clojure:/
endtry
endif
2012-12-04 06:06:22 +00:00
else
let file = a:file
endif
2014-01-15 23:14:32 +00:00
let file = fireplace#findfile(file)
2012-12-04 06:06:22 +00:00
if file ==# ''
let v:errmsg = "Couldn't find file for ".a:file
return 'echoerr v:errmsg'
endif
return a:cmd .
\ (exists('jump') ? ' +sil!\ djump\ ' . jump : '') .
\ ' ' . fnameescape(file) .
\ '| let &l:path = ' . string(&l:path)
endfunction
2013-03-12 19:37:37 +00:00
augroup fireplace_go_to_file
2012-12-04 06:06:22 +00:00
autocmd!
autocmd FileType clojure nnoremap <silent><buffer> gf :<C-U>exe <SID>GF('edit', expand('<cfile>'))<CR>
autocmd FileType clojure nnoremap <silent><buffer> <C-W>f :<C-U>exe <SID>GF('split', expand('<cfile>'))<CR>
autocmd FileType clojure nnoremap <silent><buffer> <C-W><C-F> :<C-U>exe <SID>GF('split', expand('<cfile>'))<CR>
autocmd FileType clojure nnoremap <silent><buffer> <C-W>gf :<C-U>exe <SID>GF('tabedit', expand('<cfile>'))<CR>
augroup END
" }}}1
" Documentation {{{1
function! s:buffer_path(...) abort
2014-01-11 04:31:47 +00:00
let buffer = a:0 ? a:1 : s:buf()
2012-12-04 06:06:22 +00:00
if getbufvar(buffer, '&buftype') =~# '^no'
return ''
endif
let path = substitute(fnamemodify(bufname(buffer), ':p'), '\C^zipfile:\(.*\)::', '\1/', '')
for dir in fireplace#path(buffer)
if dir !=# '' && path[0 : strlen(dir)-1] ==# dir && path[strlen(dir)] =~# '[\/]'
return path[strlen(dir)+1:-1]
endif
endfor
2012-12-04 06:06:22 +00:00
return ''
endfunction
2014-01-11 04:31:47 +00:00
function! fireplace#ns(...) abort
let buffer = a:0 ? a:1 : s:buf()
if !empty(getbufvar(buffer, 'fireplace_ns'))
2014-01-11 04:31:47 +00:00
return getbufvar(buffer, 'fireplace_ns')
2014-01-01 22:15:35 +00:00
endif
2014-01-11 04:31:47 +00:00
let head = getbufline(buffer, 1, 500)
let blank = '^\s*\%(;.*\)\=$'
call filter(head, 'v:val !~# blank')
let keyword_group = '[A-Za-z0-9_?*!+/=<>.-]'
2014-01-11 04:31:47 +00:00
let lines = join(head[0:49], ' ')
2013-01-09 05:07:24 +00:00
let lines = substitute(lines, '"\%(\\.\|[^"]\)*"\|\\.', '', 'g')
let lines = substitute(lines, '\^\={[^{}]*}', '', '')
let lines = substitute(lines, '\^:'.keyword_group.'\+', '', 'g')
let ns = matchstr(lines, '\C^(\s*\%(in-ns\s*''\|ns\s\+\)\zs'.keyword_group.'\+\ze')
if ns !=# ''
return ns
endif
2014-01-11 04:31:47 +00:00
let path = s:buffer_path(buffer)
return s:to_ns(path ==# '' ? fireplace#client(buffer).user_ns() : path)
2012-12-04 06:06:22 +00:00
endfunction
2012-12-25 23:56:47 +00:00
function! s:Lookup(ns, macro, arg) abort
2012-12-04 06:06:22 +00:00
try
2014-04-13 00:57:55 +00:00
if has_key(fireplace#client(), 'connection') && fireplace#client().connection.describe.versions.clojure.minor > 2
call fireplace#client().preload(a:ns)
let response = s:eval('('.a:ns.'/'.a:macro.' '.a:arg.')', {'session': 0})
else
" doc is in clojure.core in older Clojure versions
let response = s:eval("(clojure.core/require '".a:ns.") (clojure.core/eval (clojure.core/list (if (ns-resolve 'clojure.core '".a:macro.") 'clojure.core/".a:macro." '".a:ns.'/'.a:macro.") '".a:arg.'))', {'session': 0})
endif
2014-04-09 17:23:19 +00:00
call s:output_response(response)
2012-12-04 06:06:22 +00:00
catch /^Clojure:/
catch /.*/
echohl ErrorMSG
echo v:exception
echohl None
endtry
return ''
endfunction
2014-01-07 03:58:43 +00:00
function! s:inputlist(label, entries) abort
2012-12-04 06:06:22 +00:00
let choices = [a:label]
for i in range(len(a:entries))
let choices += [printf('%2d. %s', i+1, a:entries[i])]
endfor
let choice = inputlist(choices)
if choice
return a:entries[choice-1]
else
return ''
endif
endfunction
function! s:Apropos(pattern) abort
if a:pattern =~# '^#\="'
let pattern = a:pattern
elseif a:pattern =~# '^^'
let pattern = '#"' . a:pattern . '"'
else
let pattern = '"' . a:pattern . '"'
endif
2013-03-12 19:37:37 +00:00
let matches = fireplace#evalparse('(clojure.repl/apropos '.pattern.')')
2012-12-04 06:06:22 +00:00
if empty(matches)
return ''
endif
2012-12-25 17:04:07 +00:00
let choice = s:inputlist('Look up docs for:', matches)
2012-12-04 06:06:22 +00:00
if choice !=# ''
return 'echo "\n"|Doc '.choice
else
return ''
endif
endfunction
2014-01-07 03:58:43 +00:00
function! s:K() abort
let word = expand('<cword>')
let java_candidate = matchstr(word, '^\%(\w\+\.\)*\u\l\w*\ze\%(\.\|\/\w\+\)\=$')
if java_candidate !=# ''
return 'Javadoc '.java_candidate
else
return 'Doc '.word
endif
endfunction
2013-03-12 19:37:37 +00:00
nnoremap <Plug>FireplaceK :<C-R>=<SID>K()<CR><CR>
nnoremap <Plug>FireplaceSource :Source <C-R><C-W><CR>
2013-01-03 05:10:11 +00:00
2013-03-12 19:37:37 +00:00
augroup fireplace_doc
2012-12-04 06:06:22 +00:00
autocmd!
2013-03-12 19:37:37 +00:00
autocmd FileType clojure nmap <buffer> K <Plug>FireplaceK
autocmd FileType clojure nmap <buffer> [d <Plug>FireplaceSource
autocmd FileType clojure nmap <buffer> ]d <Plug>FireplaceSource
2012-12-14 23:08:51 +00:00
autocmd FileType clojure command! -buffer -nargs=1 Apropos :exe s:Apropos(<q-args>)
2012-12-25 23:56:47 +00:00
autocmd FileType clojure command! -buffer -nargs=1 FindDoc :exe s:Lookup('clojure.repl', 'find-doc', printf('#"%s"', <q-args>))
autocmd FileType clojure command! -buffer -bar -nargs=1 Javadoc :exe s:Lookup('clojure.java.javadoc', 'javadoc', <q-args>)
2013-03-12 19:37:37 +00:00
autocmd FileType clojure command! -buffer -bar -nargs=1 -complete=customlist,fireplace#eval_complete Doc :exe s:Lookup('clojure.repl', 'doc', <q-args>)
autocmd FileType clojure command! -buffer -bar -nargs=1 -complete=customlist,fireplace#eval_complete Source :exe s:Lookup('clojure.repl', 'source', <q-args>)
2012-12-04 06:06:22 +00:00
augroup END
" }}}1
" Tests {{{1
function! fireplace#capture_test_run(expr) abort
let expr = '(require ''clojure.test) '
\ . '(binding [clojure.test/report (fn [m]'
\ . ' (case (:type m)'
\ . ' (:fail :error)'
2014-04-13 00:28:21 +00:00
\ . ' (let [{file :file line :line test :name} (meta (last clojure.test/*testing-vars*))]'
\ . ' (clojure.test/with-test-out'
2014-04-13 00:28:21 +00:00
\ . ' (println (clojure.string/join "\t" [file line (name (:type m)) test]))'
\ . ' (when (seq clojure.test/*testing-contexts*) (println (clojure.test/testing-contexts-str)))'
\ . ' (when-let [message (:message m)] (println message))'
\ . ' (println "expected:" (pr-str (:expected m)))'
\ . ' (println " actual:" (pr-str (:actual m)))))'
\ . ' ((.getRawRoot #''clojure.test/report) m)))]'
\ . ' ' . a:expr . ')'
let qflist = []
2014-04-13 00:38:33 +00:00
let response = s:eval(expr, {'session': 0, 'ns': fireplace#client().user_ns()})
if !has_key(response, 'out')
return s:output_response(response)
endif
for line in split(response.out, "\n")
let entry = {'text': line}
if line =~# '\t.*\t.*\t'
let [resource, lnum, type, name] = split(line, "\t", 1)
let entry.lnum = lnum
let entry.type = (type ==# 'fail' ? 'W' : 'E')
let entry.text = name
if resource ==# 'NO_SOURCE_FILE'
let resource = ''
let entry.lnum = 0
endif
let entry.filename = fireplace#findresource(resource, fireplace#path())
if empty(entry.filename)
let entry.lnum = 0
endif
endif
call add(qflist, entry)
endfor
call setqflist(qflist)
cwindow
endfunction
function! s:RunTests(bang, echo, ...) abort
if a:bang && a:0
let expr = '(clojure.test/run-all-tests #"'.join(a:000, '|').'")'
elseif a:bang
let expr = '(clojure.test/run-all-tests)'
else
let expr = '(' .join(['clojure.test/run-tests'] + map(copy(a:000), '"''".v:val'), ' ').')'
endif
call fireplace#capture_test_run(expr)
cwindow
if a:echo
echo expr
endif
endfunction
augroup fireplace_command
autocmd!
autocmd FileType clojure command! -buffer -bar -bang -nargs=*
\ -complete=customlist,fireplace#ns_complete RunTests
\ call s:RunTests(<bang>0, 1, <f-args>)
augroup END
" }}}1
" Alternate {{{1
2013-03-12 19:37:37 +00:00
augroup fireplace_alternate
autocmd!
autocmd FileType clojure command! -buffer -bar -bang A :exe s:Alternate('edit<bang>')
autocmd FileType clojure command! -buffer -bar AS :exe s:Alternate('split')
autocmd FileType clojure command! -buffer -bar AV :exe s:Alternate('vsplit')
autocmd FileType clojure command! -buffer -bar AT :exe s:Alternate('tabedit')
augroup END
2013-08-05 02:41:57 +00:00
function! fireplace#alternates() abort
2013-03-12 19:37:37 +00:00
let ns = fireplace#ns()
if ns =~# '-test$'
let alt = [ns[0:-6]]
elseif ns =~# '\.test\.'
let alt = [substitute(ns, '\.test\.', '.', '')]
2013-02-13 23:30:50 +00:00
elseif ns =~# '-spec$'
let alt = [ns[0:-6], ns . '-test']
2014-01-14 14:02:46 +00:00
elseif ns =~# '\.t-[^\.]*$'
let alt = [substitute(ns, '\.t-\([^\.]*\)$', '\.\1', '')]
else
2014-01-14 13:51:18 +00:00
let alt = [ns . '-test', substitute(ns, '\.', '.test.', ''), ns . '-spec',
2014-01-14 14:02:46 +00:00
\ substitute(ns, '\.\([^\.]*\)$', '.t-\1', '')]
endif
2014-01-17 09:35:03 +00:00
return map(alt, 'tr(v:val, ".-", "/_") . "." . expand("%:e")')
endfunction
function! s:Alternate(cmd) abort
2013-08-05 02:41:57 +00:00
let alternates = fireplace#alternates()
for file in alternates
2013-03-12 19:37:37 +00:00
let path = fireplace#findresource(file)
if !empty(path)
return a:cmd . ' ' . fnameescape(path)
endif
endfor
2013-02-13 17:58:50 +00:00
return 'echoerr '.string("Couldn't find " . alternates[0] . " in class path")
endfunction
2012-12-04 06:06:22 +00:00
" }}}1
" Leiningen {{{1
function! s:hunt(start, anchor, pattern) abort
2012-12-04 06:06:22 +00:00
let root = simplify(fnamemodify(a:start, ':p:s?[\/]$??'))
2012-12-09 19:40:27 +00:00
if !isdirectory(fnamemodify(root, ':h'))
return ''
endif
2012-12-04 06:06:22 +00:00
let previous = ""
while root !=# previous
if filereadable(root . '/' . a:anchor) && join(readfile(root . '/' . a:anchor, '', 50)) =~# a:pattern
2012-12-04 06:06:22 +00:00
return root
endif
let previous = root
let root = fnamemodify(root, ':h')
endwhile
return ''
endfunction
2014-04-04 01:16:42 +00:00
if !exists('s:leiningen_paths')
2014-01-31 04:13:35 +00:00
let s:leiningen_paths = {}
2012-12-04 06:06:22 +00:00
endif
function! s:leiningen_portfile() abort
2012-12-04 06:06:22 +00:00
if !exists('b:leiningen_root')
return ''
endif
let root = b:leiningen_root
let portfiles = [root.'/.nrepl-port', root.'/target/repl-port', root.'/target/repl/repl-port']
for f in portfiles
if filereadable(f)
return f
endif
endfor
return ''
endfunction
function! s:leiningen_connect(auto) abort
if !exists('b:leiningen_root')
return
endif
let portfile = s:leiningen_portfile()
if a:auto && empty(portfile) && exists(':Start') ==# 2
2014-04-05 23:27:41 +00:00
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
let cwd = getcwd()
try
execute cd fnameescape(b:leiningen_root)
2014-04-12 22:15:22 +00:00
execute 'Start! -title='
\ . escape(fnamemodify(b:leiningen_root, ':t') . ' repl', ' ')
\ 'lein repl'
if get(get(g:, 'dispatch_last_start', {}), 'handler', 'headless') ==# 'headless'
return
endif
finally
execute cd fnameescape(cwd)
endtry
let i = 0
while empty(portfile) && i < 300 && !getchar(0)
let i += 1
sleep 100m
let portfile = s:leiningen_portfile()
endwhile
endif
if empty(portfile)
2012-12-04 06:06:22 +00:00
return
endif
let conn = fireplace#register_port_file(portfile, b:leiningen_root)
if has_key(conn, 'path')
let s:leiningen_paths[b:leiningen_root] = conn.path()
2012-12-04 06:06:22 +00:00
endif
endfunction
function! s:leiningen_init() abort
if !exists('b:leiningen_root')
let root = s:hunt(expand('%:p'), 'project.clj', '(\s*defproject')
2012-12-04 06:06:22 +00:00
if root !=# ''
let b:leiningen_root = root
endif
endif
if !exists('b:leiningen_root')
return
endif
let b:java_root = b:leiningen_root
2014-01-31 04:13:35 +00:00
compiler lein
2012-12-04 06:06:22 +00:00
2014-04-09 16:43:35 +00:00
let path = s:path_extract(&path)
if !empty(path)
let s:leiningen_paths[b:leiningen_root] = path
2014-01-31 04:13:35 +00:00
endif
call s:leiningen_connect(0)
2012-12-04 06:06:22 +00:00
endfunction
2014-01-31 04:13:35 +00:00
function! s:massage_quickfix() abort
if &errorformat !~# 'fireplace$'
return
endif
if has_key(s:leiningen_paths, getcwd())
let path = s:leiningen_paths[getcwd()]
else
return
endif
let qflist = getqflist()
for entry in qflist
call extend(entry, s:qfmassage(get(entry, 'text', ''), path))
endfor
call setqflist(qflist, 'replace')
endfunction
2013-03-12 19:37:37 +00:00
augroup fireplace_leiningen
2012-12-04 06:06:22 +00:00
autocmd!
autocmd User FireplacePreConnect call s:leiningen_connect(1)
2012-12-04 06:06:22 +00:00
autocmd FileType clojure call s:leiningen_init()
2014-01-31 04:13:35 +00:00
autocmd QuickFixCmdPost make,cfile,cgetfile call s:massage_quickfix()
2012-12-04 06:06:22 +00:00
augroup END
" }}}1
" vim:set et sw=2: