
Like Slime, for Vim
October 3, 2007I started reading Practical Common Lisp yesterday. No discussion about Lisp can be complete without talking about Slime. Slime basically turns Emacs into an IDE for Lisp development. Peter Siebel thinks this is important enough to dedicate the second chapter to it. He even repackaged Lisp + Emacs + Slime as Lispbox to help people get started faster. For an excellent book (so far), available for free, and Lispbox, thank you Mr.Siebel.
After actually downloading, installing, and running Lispbox, I can see the advantages. Slime solves the problems you would have with any interactive REPL-type environment.
Let’s take Ruby’s irb as an example more people are going to relate to:
- you start irb
- you start a text editor (vim, textmate, emacs, …)
- you do a few tests in irb
- you copy and paste to a text editor
- you clean things up in the text editor
- you copy and paste back to irb
- you make a mistake
- you fix things up in the text editor
- you hesitate copying and pasting, because it’s painful now
- you write some tests
- you exit irb and run the tests to do your experiments
Let me present the dilemma another way: irb is great to get answers quick but it is also temporary because you know nothing you do in irb will be saved. However, the moment you start living in a text editor, you give up a lot of the power of REPL. Or, at least, your REPL becomes 10 seconds instead of 1 second. That changes the way you work. And that explains why Slime exists.
Slime creates a new interactive session with Lisp and you are able to copy and paste text from a text buffer to the session with one keyboard shortcut. That’s great! Now you can type, organize, pretty-print your code in a text editor, type C-c C-c to “refresh” the interactive session. This does not close and create a new session, the function definitions are reloaded into the current session. So, all your testing objects, those carefully crafted lists and hashes of objects (or whatnots) still exist—the world just changed around them.
Yeah, Slime is great. I’m just not an Emacs fan.
I did some research and Vim has no mode to support asynchronous sessions like Emacs. In essence, all that’s needed is a software that will spawn what you really want to run, say irb, control stdin/stdout/stderr, capture its own stdin/stdout/stderr and tunnel those to the child process. Also, it would be nice if it could open a port to receive external commands to be able to script stdin…
That’s when I remembered an article I read about scripting gnu-screen. To make a long story short, screen does everything we want, and more.
Here’s what we want to accomplish:
- start a named screen
- name the screen window
- start irb
- start another terminal
- start vim
- define a function/class/object
- have it “transported” to irb
Here are the instructions:
- screen -S session01
- C-a A—window01
- irb
- vim
- (type code)
- vip (select paragraph)
- “ry (copy to register r)
- :call system(“screen -S session01 -p window01 -X stuff ’” . @r . ”’”
And BLAM, you just did some magic!
At this point, you are coming to 2 realizations:
- this is WAY cool
- you want this automated
Thankfully, I can help with the automation. Get slime.vim and put it in ~/.vim/plugin/ .
A few notes:
- the magic key is C-c C-c (like Slime, surprise!)
- the first time, you’ll be prompted for the “session name” and the “window name”
- subsequent times will be automated
- you can reprompt for “session name” and “window name” with C-c v
- by default, C-c C-c will select the current paragraph and copy-paste it
- but you can make your own selection first, and send it over with C-c C-c
…
As a final note, I’d like to drive the point that this can automate ANYTHING running in a screen:
- bash
- top
- irb
- python
- any lisp/scheme REPL
- mysql
- …
One thing is for sure, this will definitely change the way I work.
(For extra points, write your own Textmate plugin … this hack is not limited to Vim!!)
Couldn’t imagine how would a better writer of skill would write.
Awesome. I’ll try it out.
Not work for my environment (vim in Cygwin in Vista); it says “Command terminated”. Please help.
I neither use Cygwin nor Vista, sorry.
You could try to make it work outside of Vim, directly on the command-line. Using 2 terminals and screen, the “stuff” command of screen will let you script a session externally.
You can refer to the article I mentioned (http://www.jerri.de/blog/archives/2006/05/02/scripting_screen_for_fun_and_profit/) for more details.
Thanks for your reply.
Yes, it works outside Vim.
% screen -p 2 -X stuff ‘(+ 1 2)^M’
But not inside Vim!
:echo system(”screen -p 2 -X stuff ‘(+ 1 2)^M’”
It says: Command terminated
By the way, this works in Vim (under Cygwin):
!screen -p 2 -X stuff ‘(+ 1 2)^M’
But the problem is that the result is not show in Vim status bar. So I must split Screen and use this instead in function Send_to_Screen(text):
silent exe ‘!screen ‘ . b:screen . ‘ -X stuff “‘ . substitute(escape(a:text, ‘”\’), ‘[\r\n]‘, ‘\r’, ‘g’
. ‘”‘ | redraw!
By the way, “screen -X” do not print any output, unlike for example “ls -l”, so I wonder why ‘echo system(”screen …”)’ shows the result (of another screen) in Vim status bar???
Amazing! This is awesome. Thank you!
Hey I modified the script so that it is now fully automated when there’s only one Screen session attached. Should run on any UNIX system. Here it is:
let g:screen_sessionname = “”
let g:screen_windowname = “”
“”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”
function Send_to_Screen(text)
echo system(”screen” . g:screen_sessionname . g:screen_windowname . ” -X stuff ‘” . a:text . “‘”
endfunction
function Screen_Vars()
let g:screen_sessionname = “”
let g:screen_windowname = “”
” automatically find the session name if there’s only one
== 1
if system(”screen -ls | awk ‘/Attached/ {print $1}’ | wc -l”
let g:screen_sessionname = ” -S ” . system(”screen -ls | awk ‘/Attached/ {printf \”%s\”, $1}’”
else
let g:screen_sessionname = ” -S ” . input(”Screen session: “, g:screen_sessionname)
endif
let g:screen_windowname = ” -p ” . input(”Screen window: “, g:screen_windowname)
endfunction
“”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”
vmap “ry:call Send_to_Screen(@r)
nmap vip
nmap v :call Screen_Vars()
Excellent!
I tried your modifications and generalized the idea: provide a list of session names for completion. I overwrote the previous version above.
If there’s only one session name, you press TAB and you’re done.
I also changed the window name to 0 … because there will always be a window named 0. It seems like a better default value.
(I realize my changes don’t automate the case for 1 session name, however)
I discovered bug. The script fails on text region containing single quotes (e.g. Common Lisp sharp quote macro #’). Use a escaped double quote solves.
oh looks like you solve that in the newer version as well lol
So… it works, or not?
If not, can you show me an example where it fails? I tried to reproduce but it worked for me.
Sick,
I have been looking something like this for this for a while!
Thanks,
Courtney
[...] Like Slime, for Vim Scripting screen for fun and [...]
Hey all,
If you only want to code Lisp in Vim, I’d advice you to have a look at my plugin, Limp, which does this and then some more.
It’s available at http://mikael.jansson.be/hacking/limp — I hope you find it useful! I’m always open for suggestions.
Thanks!
– Mikael