1. Introduction
  2. Coloring
  3. Editing
    1. Completion
    2. Indentation
  4. Interaction
    1. Using VimSh
    2. Using GNU Screen
  5. SWANK / SLIMV
  6. REPL : Minimalistic REPL

Introduction

Following are some settings used to edit Scheme file with VIM.

Coloring

VIM's default syntax file for Scheme already has some CHICKEN-specific settings, we can enable those by adding the following line into ~/.vim/ftplugin/scheme.vim.

let b:is_chicken=1

But still some annoyances remain.

The following code is a patch to syntax/scheme.vim, you can patch the system-side file, or you can copy it to ~/.vim/syntax, and then patch it. This patch adds S-expression comment and Unix hash-bang support.

--- old-scheme.vim      2008-08-16 09:35:17.000000000 +0800
+++ scheme.vim  2008-08-16 09:34:38.000000000 +0800
@@ -29,6 +29,11 @@
 syn match      schemeError     oneline    ![^ \t()\[\]";]*!
 syn match      schemeError     oneline    ")"
 
+" Add TempStruc before Struc and Quoted.
+" although TempStruc will be overwritten by them when do hightlighting,
+" it still can be used to delimit a sexp.
+syn region schemeTempStruc start="(" end=")" contained transparent contains=schemeTempStruc
+
 " Quoted and backquoted stuff
 
 syn region schemeQuoted matchgroup=Delimiter start="['`]" end=![ \t()\[\]";]!me=e-1 contains=ALLBUT,schemeStruc,schemeSyntax,schemeFunc
@@ -231,8 +236,13 @@
 
 
 if exists("b:is_chicken") || exists("is_chicken")
+    syn match schemeChar oneline "#\\return"
+    syn match schemeChar oneline "#!eof"
+
     " multiline comment
     syntax region schemeMultilineComment start=/#|/ end=/|#/ contains=schemeMultilineComment
+    syn region schemeSexpComment start="#;(" end=")" contains=schemeComment,schemeTempStruc
+    hi def link schemeSexpComment Comment
 
     syn match schemeOther oneline    "##[-a-z!$%&*/:<=>?^_~0-9+.@#%]\+"
     syn match schemeExtSyntax oneline    "#:[-a-z!$%&*/:<=>?^_~0-9+.@#%]\+"
@@ -268,6 +278,9 @@
     " suggested by Alex Queiroz
     syn match schemeExtSyntax oneline    "#![-a-z!$%&*/:<=>?^_~0-9+.@#%]\+"
     syn region schemeString start=+#<#\s*\z(.*\)+ end=+^\z1$+ 
+
+    syn match schemeShebang "^#!/.*csi.*$"
+    hi def link schemeShebang Comment
 endif
 
 " Synchronization and the wrapping up...

Editing

Completion

VIM's completion functionality can be configured by 'complete' option. There are several useful settings for Scheme editing.

First is the k flag, we can supply a dict file to it. The following code can be used to generate a Scheme dict file.

#!/usr/local/bin/csi -script
;;; Create a dictionary file to be used by vim insert complete.

(use data-structures extras srfi-1 srfi-4 srfi-13)
(use srfi-14 srfi-69 apropos regex srfi-18 posix utils tcp lolevel)

(call-with-output-file "~/scheme-word-list"
   (lambda (port)
     (for-each (lambda (x) (display x port) (newline port))
               (sort (apropos-list (regexp ".*") #:macros? #t)
                     (lambda (a b)
                       (string<? (symbol->string a)
                                 (symbol->string b)))))))

Then add the following line to ~/.vim/ftplugin/scheme.vim, and we will be able to complete identifier names using CTRL-P and CTRL-N.

setl complete+=,k~/scheme-word-list

Also when we edit c files, VIM finds words in not only opened buffers, but also included files. These are controlled by the following options.

Likewise, VIM can be configured to find words in files which are mentioned in use or require-extension. In the example below, change the path to match your setup. The follwing lines go into ~/.vim/ftplugin/scheme.vim:

setl include=\^\(\\(use\\\|require-extension\\)\\s\\+
setl includeexpr=substitute(v:fname,'$','.scm','')
setl path+=/usr/local/lib/chicken/3
setl suffixesadd=.scm

Indentation

VIM already indents Scheme file well, except it can't recognise some CHICKEN keywords. We just have to add them.

setl lispwords+=let-values,condition-case,with-input-from-string
setl lispwords+=with-output-to-string,handle-exceptions,call/cc,rec,receive
setl lispwords+=call-with-output-file

Also, put those lines in to scheme.vim.

nmap <silent> == :call Scheme_indent_top_sexp()<cr>

" Indent a toplevel sexp.
fun! Scheme_indent_top_sexp()
	let pos = getpos('.')
	silent! exec "normal! 99[(=%"
	call setpos('.', pos)
endfun

Then we can use == to indent a toplevel S-expression.

Interaction

Vim lacks a cross-platform facility to spawn shells and other commandline programs and interact with them, therefore various methods—or workarounds—have been invented and re-invented over time to address the issue. Each has its own advantages and drawbacks.

Using VimSh

This is a Vim extension written in Python that approximates what most users would consider the "ideal" shell interaction from within a text editor. It can spawn any interactive program from inside Vim, display their output in a new buffer (a Vim window) and receive input on the bottom line of the same buffer. Since it's a Vim buffer you can go into normal mode and move around the buffer, yank, paste, use word completion, etc. (Do understand that changing text and typing newlines in places other than the bottom line will give weird results.)

You will need a Vim built with Python support (:version must contain +python) and an OS with pty support (Windows users are SOL.)

Instructions:

let g:vimsh_sh="/bin/bash"
let g:vimsh_pty_prompt_override=0
let g:vimsh_show_workaround_msgs=0

function! VimShRedraw()
  redraw
endf

function! VimShNew()
  if ! exists("g:vimsh_loaded_python_file")
    pyfile ~/.vim/vimsh.py
    let g:vimsh_loaded_python_file=1
  endif
  python spawn_buf('_vimsh_')
endf

function! VimShRun(text)
  " Called on a string or on a list of lines
  " Executes the lines in the VimSh window, one by one
  " (to allow for secondary prompts and/or incremental evaluation)

  " Parse argument
  let t = type(a:text)
  if t == 1
    let lines = split(a:text, '\n')
  elseif t == 3
    let lines = a:text
  else
    echoerr 'Argument is neither a list nor a string'
    return
  endif

  " Find VimSh window
  let win = bufwinnr('_vimsh_')
  if win == -1
    echohl WarningMsg
    echomsg 'Cannot find vimsh window'
    echohl None
    return
  endif

  " Execute commands
  exec win . 'wincmd w'
  for line in lines
    call setline('$', getline('$') . line)
    python lookup_buf('_vimsh_').execute_cmd()
    redraw
    sleep 1m "why do I need a sleep to apply the redraw?
  endfor
  stopinsert
  wincmd p
  echo
endf

function! VimShRunOp(type, ...)
  " Helper function (copied verbatim from Vim help)

  let sel_save = &selection
  let &selection = "inclusive"
  let reg_save = @@

  if a:0
    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"
  else
    silent exe "normal! `[v`]y"
  endif

  call VimShRun(@@)

  let &selection = sel_save
  let @@ = reg_save
endf

nmap Sn :call VimShNew()<CR>
nnoremap S  :set opfunc=VimShRunOp<CR>g@
vnoremap S  :<C-U>call VimShRunOp(visualmode(), 1)<CR>
nnoremap SS :call VimShRun(getline('.'))<CR>
nnoremap Sf :call VimShRun(getline(1,'$'))<CR>
nmap St :norm 99[(<CR>vabS

The code above includes work from various sources and contains a few weird tricks (in other words: here be dragons) It provides several features under the S leading character. Redefining S as Shell (or Send) shouldn't cause problems for most users, as the default Substitute function is just a synonym for Change, but you can alter the mappings if you don't like it.

Sn open a new shell
SS or countSS send the current line (or count lines) to the shell
Smotion send the text over motion to the shell (eg. S3w sends 3 words to the shell)
visual mode S send the selected text (either char-, line-, or block-wise selection)
Sf send the entire file to the shell
St send the current top-level form to the shell

A newline is always added at the end of the text being sent, so as to make the shell execute it. If for some reason you don't want to send a complete command, you will have to use Vim's standard yank & pull (copy & paste) from your editing buffer to the shell buffer.

Care is taken to make the shell react to each individual line as if it was typed interactively. As a result, pasting huge chunks will be a slow operation. You are advised to use a source command in this case: source (or dot) for bash, include for CHICKEN.

Important notice: because of a limitation in Vim, when a line takes more than a given timeout (1/10 of a second) to execute, VimSh returns the command to the editor without waiting for the shell to complete the operation. You will have to manually go to the VimSh buffer every once in a while and hit F5 to refresh the view. This is probably the most annoying drawback of this whole method, but it's not that bad in actuality.

The config block above spawns bash, instead of csi, for several reasons:

One last thing, you might like this if you're not using it already:

set mousefocus

Using GNU Screen

Another way to add interaction facility to Vim is to use GNU Screen.

With this method the csi process is completely independent (detached) from Vim. It must be started manually, in a Screen session with a specific window title, and remains running after quitting the editor. This can be either an advantage or a disadvantage, depending on your requirements.

Source: Jonathan Palardy.

Put following lines into ftplugin/scheme.vim (make sure that there is no trailing whitespace, especially in the "nmap" lines):

nmap <silent> <leader>es :call Scheme_eval_defun()<cr>
nmap <silent> <leader>ef  :call Scheme_send_sexp("(load \"" . expand("%:p") . "\")\n")<cr>

fun! Scheme_send_sexp(sexp)
    let ss = escape(a:sexp, '\"')
    call system("screen -p csi -X stuff \"" . ss . "\n\"")
    "(or for tmux users)
    "call system("tmux send-keys -t csi \"" . ss . "\n\"")
endfun

fun! Scheme_eval_defun()
    let pos = getpos('.')
    silent! exec "normal! 99[(yab"
    call Scheme_send_sexp(@")
    call setpos('.', pos)
endfun

First we create a window in screen with the name of csi and start csi in it. Then open a scheme file, using <leader>es to evaluate a sexp and using <leader>ef to load a file.

Another minimalistic approach is described in the Endoscreen Cut&Paster blogpost.

SWANK / SLIMV

There is a slime client for vim called slimv.

A tutorial screencast on setting it up can be found at vimeo.

REPL : Minimalistic REPL

A vim extension which provides a REPL.