Improved Vim IDE
I spent some time today learning about vim scripting basics. (Did you you know that you can script Vim with vimscript, python, perl, ruby, mzscheme, and others?! There goes the last possible reason for learning emacs.)
Putting that new knowledge to work, I set to improving my haskell and python workflow. The goal: hit R to run whatever it is I’m working on.
The .vimrc code is below, but first I want to explain the interface.
Prerequisite: I’m running GNU Screen with windows titled “main”, “ghci”, and “python”. Not surprisingly, “ghci” is running ghci, and “python” is running the python interpreter — this happens automatically, based on the .screenrc file below.
Interface
If I’m working on a haskell file, hitting R runs the project in the “main” window of the terminal with runghc. Selecting text and R runs the selected text in ghci, in the “ghci” window. F5 fully compiles and runs the program in “main”.
If I’m working on a python file, it’s basically the same thing. R runs the project in “main”. Select text and R runs the selected text in the interactive python interpreter in the “python” window.
It’s simple enough to extend the code to compile/run any format (lilypond, groovy, povray, …), and always with just R.
Running the :SendToScreen command sends whatever text follows to the currently active window in the terminal. %p expands to the fully qualified path of the current file. %r to the same, less the extension.
The :SetRunTimeOptions command lets you set options to pass to the compiler. The setting is specific to the current buffer, so your haskell and python buffer can maintain different settings.
The gory details
I send text to the terminal through GNU Screen’s -X stuff command.
autocmd! BufEnter,BufNew lets me run configuration functions when I enter a file of the specified type. Those functions redefine vim shortcuts, which can be specific to the current mode.
command lets me write my own vim command.
Solving the problem like this lets me dump the whole mess into my .vimrc.
.vimrc
" interacting with screen
" =======================
" sending function; user does not call directly
" - window can be ""
" - %p = full path
" - %r = root of file name, no extension
function! SendToScreenWindow(window, text)
let pArg = a:window == "" ? "" : "-p " . a:window
let toStuff = substitute(a:text, "%p", fnamemodify(@%, ":p"), "g")
let toStuff = substitute(toStuff, "%r", fnamemodify(@%, ":p:r"), "g")
call system("screen " . pArg . " -X stuff '" . toStuff . "\n'")
endfunction
" commands to set run-time options
command! -nargs=+ SetRunTimeOptions let b:runTimeOpts=<q-args>
command! ClearRunTimeOptions let b:runTimeOpts=""
" command to send text to active window
command! -nargs=+ SendToScreen call SendToScreenWindow("", <q-args>)
" automatically change settings based on filetype
autocmd! BufEnter,BufNew *.hs call ConfigureHaskell()
autocmd! BufEnter,BufNew *.py call ConfigurePython()
function! ConfigureHaskell()
call InitBuffer()
nmap R :wa<CR> :call SendToScreenWindow("main", "runghc %p " . b:runTimeOpts)<CR>
vmap R y:silent call SendToScreenWindow("ghci", @")<CR>
let b:runHs =
\ "clear;
\ hlint %p;
\ ghc --make %p;
\ time %r " . b:runTimeOpts
nmap <F5> :wa<CR> :call SendToScreenWindow("main", b:runHs)<CR>
endfunction
function! ConfigurePython()
call InitBuffer()
nmap R :wa<CR> :call SendToScreenWindow("main", "python %p " . b:runTimeOpts)<CR>
vmap R y:silent call SendToScreenWindow("python", @")<CR>
endfunction
" utilities
function! InitBuffer()
if !exists("b:runTimeOpts")
let b:runTimeOpts = ""
endif
endfunction
.screenrc
defscrollback 1024 startup_message off caption always screen -t "python" 2 python screen -t "ghci" 1 ghci screen -t "main" 0