Vim Clojure Tooling Redux

  • Evaluate a highlighted form, or the next form, if the cursor is on a (, {, or [.
  • Evaluate the entire buffer, using load-file.
  • Get the doc for the word under the cursor.
augroup Terminal
au!
au TermOpen * let g:term_jid = b:terminal_job_id
augroup END
function! REPLSendSafe()
" Hack to get character under the cursor.
norm "ayl
if index(["(", ")", "[", "]", "{", "}"], @a) >= 0
" Hack to get text using % motion.
norm v%"ay
call REPLSend(@a)
endif
endfunction
function! REPLSend(cmd)
call jobsend(g:term_jid, a:cmd."\n")
endfunction
" If no visual selection, send safely
nnoremap <leader>ef :call REPLSendSafe()<cr>
" If there's a visual selection, just send it
vnoremap <leader>ef "ay:call REPLSend(@a)<cr>
" Send the entire buffer
nnoremap <leader>eb :call REPLSend("(load-file \"".expand('%:p')."\")")<cr>
" Get docs
nnoremap <leader>doc :call REPLSend("(clojure.repl/doc ".expand("<cword>").")")<cr>

Jump to Definition

I'm a little reluctant to admit it, but I miss jump to definition. I haven't been able to use gf or <C-]> since I gave up Fireplace. At the time, I was spending most of my time in ClojureScript and support for jump to definition was pretty flakey. Recently, while rationalizing why I don't actually need this feature, I was reminded of ctags, as an alternative. Again, I was surprised by how simple this was to get up and running.

“ Strip off the symbol’s namespacefunction! SanitizeTag(word) return (split(a:word, ‘/’)[-1]) endfunctionnnoremap tt :exe “:tag “.SanitizeTag(expand(“<cword>”))

Wrapping Up

At some point in their journey, most Neovim users ponder whether to use the :terminal, or stick with tmux. I'd been hesitant to commit to it, but using this setup is definitely motivating me to give :terminal a serious go.

Update 3/2/2018

I’ve added the following mapping to run tests in the current namespace. This code assumes that (ns ...) is the first form in the buffer, which is true enough of the time, in my experience.

nnoremap <leader>tb :norm gg,ef<cr>:call REPLSend("(require '[clojure.test]) (clojure.test/run-tests)")<cr>

Update 4/27/2018

Lately, I’ve needed to modify the code of two different processes. For example, if I want to modify a ClojureScript web application and the Clojure backend services it consumes, in lockstep. My flow is simple. I’ll open up a tab per project. Each tab contains a window or three with code belonging to that project, and a :terminal buffer, running that project’s REPL.

" Returns the job id of the first terminal buffer on the
" current tab.
function! FirstTermOfTabJobId()
let t_id = nvim_get_current_tabpage()
for w_id in nvim_tabpage_list_wins(t_id)
let b_id = nvim_win_get_buf(w_id)
if nvim_buf_get_option(b_id, 'buftype') == 'terminal'
return nvim_buf_get_var(b_id, 'terminal_job_id')
endif
endfor
endfunction
function! REPLSend(cmd)
call jobsend(FirstTermOfTabJobId(), a:cmd."\n")
endfunction

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Jeb

Jeb

Programmer at @Cognitect. Climber of hills. I enjoy improving things.