Commits

Tamas Kovacs committed 3f65978

Version 0.1: Initial upload

Comments (0)

Files changed (4)

+This is a mirror of http://www.vim.org/scripts/script.php?script_id=2531
+
+Slimv tries to mimic a subset of SLIME's (Superior Lisp Interaction Mode for Emacs) functionality inside Vim on Linux, Windows and Mac OS X. The script defines functions and keybindings to send s-expressions to a console mode Lisp or Clojure REPL (Read-Eval-Print Loop). Slimv runs its own REPL or connects to a running REPL started by a previous Slimv session, the connection is established when the first Slimv command is executed (e.g. an s-expression is evaluated).
+
+The Lisp REPL buffer can also be opened inside Vim as a Vim buffer with syntax highlighting and autoindenting, Lisp commands may be entered in the command line, just as in a regular REPL. The script also has a basic support for Clojure REPL.
+
+Slimv supports the same profiling tool that comes with SLIME. The script also has a Common Lisp Hyperspec lookup feature and it is able to lookup symbols in the Clojure API, as well as in JavaDoc. Symbol name completion is supported via Vim's omni-completion based on the hyperspec symbol database.
+
+Slimv comes with Paredit Mode, which is similar to the functionality of paredit.el in Emacs. Paredit Mode tries to maintain the balanced state of matched characters (parenthesis marks, square brackets, double quotes). Matched characters are inserted and removed in pairs, also when working with a block of text (well, mostly). Slimv also implements many paredit.el s-expression handling functions, like Split/Join/Wrap/Splice. Slurpage and Barfage known from Emacs is also possible but in a different fashion: you don't move the list element in or out of the list, rather you move the opening or closing parenthesis over the element or sub-list.
+
+Check out the screenshot of Slimv having a clisp REPL buffer:
+http://img6.imageshack.us/img6/5104/slimvscreenshotx.png
+
+Another screenshot of Slimv running the Clojure Ants demo:
+http://img21.imageshack.us/img21/3949/slimvants.png
+
+And this is the symbol name completion in action (no, the pink background is not done by Slimv, this is Vim's default):
+http://img18.imageshack.us/img18/5859/slimvcompl.png
+
+Here follows a list of Slimv commands, any similarity with SLIME's menu is not coincidental. :)
+For a more complete description with keybindings see the included documentation.
+
+Edit commands:
+    *  Close Form
+    *  Complete Symbol
+    *  Paredit Toggle
+
+Evaluation commands:
+    *  Eval Defun
+    *  Eval Last Expression
+    *  Pprint Eval Last Expression
+    *  Eval Region
+    *  Eval Buffer
+    *  Interactive Eval
+    *  Undefine Function
+
+Debug commands:
+    *  Macroexpand-1
+    *  Macroexpand
+    *  Trace
+    *  Untrace
+    *  Disassemble
+    *  Inspect
+
+Compile commands:
+    *  Compile Defun
+    *  Compile and Load File
+    *  Compile File
+    *  Compile Region
+
+Profile commands:
+    *  Load Profiler
+    *  Profile
+    *  Unprofile
+    *  Unprofile All
+    *  Show Profiled
+    *  Profile Report
+    *  Profile Reset
+
+Documentation commands:
+    *  Describe Symbol
+    *  Apropos
+    *  Hyperspec
+    *  Generate Tags
+
+REPL commands:
+    *  Connect to Server
+    *  Send Input
+    *  Interrupt Lisp Process
+    *  Close and Send Input
+    *  Previous Input
+    *  Next Input
+
+Many Slimv commands operate on s-expressions or symbols, just like in SLIME. Place the cursor at any location inside the s-expression or on the symbol's name then invoke the command. This builds a command specific form and sends it to the running REPL for evaluation.
+
+For more information see the documentation coupled with the script, please refer to section "External Utilities" for other useful Lisp editing tips not covered by Slimv.
+*slimv.txt*                    Slimv                 Last Change: 30 Jan 2009
+
+Slimv                                                                  *slimv*
+                               Version 0.1
+
+The Superior Lisp Interaction Mode for Vim.
+This plugin is aimed to help Lisp development by interfacing between Vim and
+the Lisp REPL, similarly to Emacs/SLIME.
+
+|slimv-installation|         Installation
+|slimv-customization|        Customization
+|slimv-usage|                Usage
+|slimv-functions|            Function Reference
+|slimv-faq|                  Frequently Asked Questions
+|slimv-changelog|            Change Log
+|slimv-issues|               Known Issues
+|slimv-todo|                 Todo
+|slimv-credits|              Credits
+
+For Vim version 7.0 and above.
+This plugin is only available if 'compatible' is not set.
+
+{Vi does not have any of this}
+
+==============================================================================
+INSTALLATION                                              *slimv-installation*
+
+To install:
+  - Download the slimv.zip.
+  - Extract the zip archive into your vimfiles or runtime directory.
+    See Vim help file |usr_05.txt| for details on adding a plugin.
+    The archive contains plugin/slimv.vim, plugin/slimv.py and doc/slimv.txt.
+  - Start Vim or goto an existing instance of Vim.
+  - Execute the following command:
+>
+      :helptag <your runtime directory/doc
+<
+    This will generate all the help tags for any file located in the doc
+    directory.
+  - Enter path definitions into your vimrc (if the default values are not
+    valid for your Vim/Python/Lisp installation, which is highly probable).
+    See |slimv-customization| below on how to do this.
+
+===============================================================================
+CUSTOMIZATION                                             *slimv-customization*
+
+|slimv-options|              Options
+|slimv-templates|            Templates
+|slimv-keyboard|             Keyboard mappings
+
+-------------------------------------------------------------------------------
+                                                                *slimv_options*
+
+Note: Most options below require to restart the Vim session when modified.
+
+Slimv tries to autodetect the Python and Lisp installation directories,
+however the algorithm is not very sophisticated.
+If the installation directories are put in the path, then the autodetection
+should find them (this is usually the case on Linux). Otherwise (on Windows)
+some frequently used directories are searched under C:\ and C:\Program Files.
+For a minimum, Slimv needs to know the path of the existing Python and Lisp
+installations, so if autodetection does not work for you, then set the
+following global variables in your vimrc.
+
+Note: On Windows use the / (slash) character instead of \ (backslash) as the
+      directory separator to avoid any incidental character escaping problems
+      while the paths are beeing passed between the Slimv processes.
+      On Linux this is not an issue.
+
+                                                               *g:slimv_python*
+This is the installation path of the Python interpreter.
+Example:
+    let g:slimv_python = 'C:/MyPythonDir/python.exe'
+
+                                                                 *g:slimv_lisp*
+This is the installation path of the Lisp interpreter.
+Example:
+    let g:slimv_lisp = 'C:/MyLispDir/mylisp.exe'
+
+                                                                 *g:slimv_port*
+The default port used by Slimv is 5151. If this port is used by another
+program then set this variable to a free port number.
+It is also possible to run multiple REPLs from multiple Vim processes, just
+set a different port number for each Vim instance.
+Example:
+    let g:slimv_port = 10101
+
+                                                               *g:slimv_client*
+You may want to use a shell frontend other then the default one or put
+additional command line parameters for starting the server.
+In this case you can omit the variables above and use g:slimv_client instead.
+This is the complete command that is used to start the client. If not set
+by the user, this is automatically built from the variables above.
+
+The command format is the following:
+    <python> <slimv> -p <port> -r <server_cmd> -c <form_to_REPL>
+Where:
+    <python>       is the command to start the Python interpreter
+    <slimv>        is the path of Slimv.py
+    <port>         is the port number to use (if other than default)
+    <server_cmd>   is the command to start the server
+    <form_to_REPL> is the Lisp form to be sent to the REPL
+
+The format of <server_cmd> is the following:
+    <python> <slimv> -l <lisp> -s
+Where:
+    <python>       is the command to start the Python interpreter
+    <slimv>        is the path of Slimv.py
+    <lisp>         is the command to start the Lisp REPL
+You can also pass the following shortcuts in <server_cmd>:
+    @p             equals <python>
+    @s             equals <slimv>
+    @l             equals <lisp>
+    @@             use it if you need to insert an @ character
+    \"             use it to insert a " character
+
+The reason behind the duplication of the <python> and <slimv> part is that
+you may want to start the server/REPL in a shell frontend that needs
+special command line options. For example on Windows I highly recommend
+to use Console (http://sourceforge.net/projects/console/) which is greatly
+configurable and you also get usable select/copy/paste functions.
+
+Note: Remember to escape with a \ all " characters that are supposed to be
+      inside a command line argument, otherwise the argument will be split
+      by the shell.
+
+Example to start the Slimv server via Console:
+
+  let g:slimv_client = 
+  \ 'python slimv.py -p 5152 -r "console -r \"/k @p @s -l clisp -s\""'
+
+So the server will be started as if we typed at the command line:
+
+  console -r "/k python slimv.py -l clisp -s"
+
+                                                                *g:slimv_debug*
+Defines the debug log level. Level 0 means no debug messages at all. The debug
+messages go into the logfile defined by |slimv-logfile|.
+
+                                                         *g:slimv_debug_client*
+Set this to nonzero only if you want to keep the Slimv client window open.
+Currently works only on Windows.
+
+                                                              *g:slimv_logfile*
+Name of the Slimv logfile. Defaults to slimv.log.
+
+                                                          *g:slimv_keybindings*
+Defines the keybinding set used by Slimv. Value 0 means no keybinding at all.
+Value 1 defines the short keybinding with one-key bindings (after <Leader>).
+Value 2 defines the easy keybinding with two-key bindings (after <Leader>).
+Other values mean no predefined keybinding is wanted.
+
+                                                                 *g:slimv_menu*
+If nonzero then the Slimv menu us added to the end of the global menu.
+Also the Slimv menu can be shown by pressing <Leader>, (defaults to ,,).
+
+
+-------------------------------------------------------------------------------
+                                                              *slimv_templates*
+
+Many Slimv commands are performed by creating a special Lisp form from the
+selected symbol (or form) and send it to the REPL for execution.
+Slimv defines various templates to build these special Lisp forms.
+You can override them to suit your needs. Use %1 for substituting the selected
+symbol's name or the selected form.
+Here follows a list of the templates defined in Slimv.
+
+                                                      *g:slimv_template_pprint*
+Lisp form built when issuing the 'pprint' command.
+Example:
+    let g:slimv_template_pprint = '(dolist (o %1)(pprint o))'
+
+                                                    *g:slimv_template_undefine*
+Lisp form built when issuing the 'undefine' command.
+Example:
+    let g:slimv_template_undefine = '(fmakunbound (read-from-string "%1"))'
+
+                                                    *g:slimv_template_describe*
+Lisp form built when issuing the 'describe' command.
+Example:
+    let g:slimv_template_describe = '(describe (read-from-string "%1"))'
+
+                                                       *g:slimv_template_trace*
+Lisp form built when issuing the 'trace' command.
+Example:
+    let g:slimv_template_trace = "(trace %1)"
+
+                                                     *g:slimv_template_untrace*
+Lisp form built when issuing the 'untrace' command.
+Example:
+    let g:slimv_template_untrace = "(untrace %1)"
+
+                                                     *g:slimv_template_profile*
+Lisp form built when issuing the 'profile' command.
+Example:
+    let g:slimv_template_profile = "(mon:monitor %1)"
+
+                                                   *g:slimv_template_unprofile*
+Lisp form built when issuing the 'unprofile' command.
+Example:
+    let g:slimv_template_unprofile = "(mon:unmonitor %1)"
+
+                                                 *g:slimv_template_disassemble*
+Lisp form built when issuing the 'disassemble' command.
+Example:
+    let g:slimv_template_disassemble = "(disassemble #'%1)"
+
+                                                     *g:slimv_template_inspect*
+Lisp form built when issuing the 'inspect' command.
+Example:
+    let g:slimv_template_inspect = "(inspect %1)"
+
+                                                     *g:slimv_template_apropos*
+Lisp form built when issuing the 'apropos' command.
+Example:
+    let g:slimv_template_apropos = '(apropos "%1")'
+
+                                                 *g:slimv_template_macroexpand*
+Lisp form built when issuing the 'macroexpand-1' command.
+Example:
+    let g:slimv_template_macroexpand = '(pprint %1)'
+
+                                             *g:slimv_template_macroexpand_all*
+Lisp form built when issuing the 'macroexpand-all' command.
+Example:
+    let g:slimv_template_macroexpand_all = '(pprint %1)'
+
+                                                *g:slimv_template_compile_file*
+Lisp form built when issuing the 'compile-file' command.
+Example:
+    let g:slimv_template_compile_file = '(compile-file "%1")'
+
+
+-------------------------------------------------------------------------------
+                                                               *slimv_keyboard*
+
+The default keybindings (g:slimv_keybindings=1) for Slimv are the following:
+
+    ,,  Slimv Menu
+
+    ,S  Connect to Server
+
+    ,d  Eval Defun
+    ,e  Eval Last Expression
+    ,E  Pprint Eval Last Expression
+    ,r  Eval Region
+    ,b  Eval Buffer
+    ,v  Interactive Eval
+    ,u  Undefine Function
+
+    ,1  Macroexpand-1
+    ,m  Macroexpand
+    ,t  Trace
+    ,T  Untrace
+    ,l  Disassemble
+    ,i  Inspect
+
+    ,D  Compile Defun
+    ,L  Compile and Load File
+    ,F  Compile File
+    ,R  Compile Region
+
+    ,p  Profile
+    ,P  Unprofile
+
+    ,s  Describe Symbol
+    ,a  Apropos
+
+There is another easy to remember built in keybinding set
+(g:slimv_keybindings=2):
+ 
+    ,,  Slimv Menu
+
+    Connection commands:
+    ,cs  Connect to Server
+
+    Evaluation commands:
+    ,ed  Eval Defun
+    ,ee  Eval Last Expression
+    ,ep  Pprint Eval Last Expression
+    ,er  Eval Region
+    ,eb  Eval Buffer
+    ,ei  Interactive Eval
+    ,eu  Undefine Function
+
+    Debug commands:
+    ,m1  Macroexpand-1
+    ,ma  Macroexpand
+    ,dt  Trace
+    ,du  Untrace
+    ,dd  Disassemble
+    ,di  Inspect
+
+    Compile commands:
+    ,cd  Compile Defun
+    ,cl  Compile and Load File
+    ,cf  Compile File
+    ,cr  Compile Region
+
+    Profile commands:
+    ,pp  Profile
+    ,pu  Unprofile
+
+    Documentation commands:
+    ,ds  Describe Symbol
+    ,da  Apropos
+
+
+==============================================================================
+USAGE                                                            *slimv-usage*
+
+After proper installation start Vim and load a *.lisp source file into a
+buffer. When the first Slimv command is entered (either from the menu or
+via keyboard shortcut or entering a :call Slimv...() at the Vim command line)
+then Slimv checks if the server/REPL runs and starts it if nedeed.
+When the server is running, the Slimv commands send the appropriate Lisp
+forms to the server/REPL for processing. That's it.
+
+All you need to know then is the list of possible Slimv commands, how to
+enter them and under what conditions.
+
+
+===============================================================================
+FAQ                                                                 *slimv-faq*
+
+- Q: Why is this plugin called 'Slimv'?
+- A: Because it is trying to mimic the popular Emacs extension 'SLIME'.
+     In SLIME 'E' stands for 'Emacs', so here it is replaced with 'V' as Vim.
+     To tell the truth, first I gave the name 'Slimvim' to the plugin but
+     then I found an (already abandoned) project called 'Slim-Vim' and I did
+     not want to interfere with it.
+
+- Q: Why another 'Superior Lisp Mode' if there is already one?
+- A: Because many programmers prefer Vim as a program text editor over Emacs,
+     including me. I don't want to start a holy war or whatsoever, I'm just
+     happy if someone else finds this plugin useful.
+
+- Q: How does Slimv work?
+- A: Slimv consists of three parts: Vim plugin, client and server.
+     The Slimv server is a swank server that embeds a Lisp REPL.
+     The Slimv client interfaces with the server and is responsible
+     for sending Lisp commands to the Lisp REPL.
+     The Vim plugin is translating editor commands to Lisp commands to be
+     sent to the server by the client.
+     So the dataflow is like this:
+     Vim -> Vim plugin -> Slimv client -> Slimv server -> Lisp REPL
+     The plugin resides in 'slimv.vim', the client and the server both
+     reside in 'slimv.py'.
+
+- Q: Why is SLIME functionality XYZ missing from Slimv?
+- A: There are two possible reasons:
+     1. The dataflow of Slimv is one-directional: from client to server.
+        There is no data sent back from the server to the client, so if a
+        functionality requires that Slimv reads data from REPL, then
+        currently it is not possible to implement it.
+     2. It is possible to implement it, but I did not (yet) do it.
+        Maybe future releases will contain it.
+
+- Q: Why is the default port number 5151?
+- A: Hint: what roman numerals are 5,1,5,1? Bingo: VI, doubled.
+
+- Q: Are you a Lisp expert?
+- A: No, not at all. I'm just learning Lisp. Also just learning Vim
+     scripting. And I'm not a Python expert either, however (at the moment)
+     I have more experience with Python than with Lisp.
+
+- Q: Why using Python for the client/server code? Why not Lisp?
+- A: This is for historical reasons and may change in the future.
+     Preliminary versions used Vim's built-in Python support.
+     Later on the client/server code was separated from Vim but still remained
+     written in Python. On Linux this should not be a problem, most Linux
+     distributions contain a Python interpreter with high enough version.
+     On Windows this means, you need to install Python, if you don't have
+     one (at least version 2.4). Anyway, Python is a nice language and
+     also a perfect replacement for calculator.exe :-)
+
+===============================================================================
+CHANGE LOG                                                    *slimv-changelog*
+
+0.1    - Initial release. And hope not the last one :-)
+
+===============================================================================
+KNOWN ISSUES                                                     *slimv-issues*
+
+- Needs Vim version 7.0 or above, because of the intensive use of lists.
+- Vim register 's' is used for all form selections, so its original content is
+  destroyed.
+- (un)profile does not work
+- Needs Python 2.4 or higher (uses the subprocess module)
+- Works only via the Python interpreter, does not work using a Python IDE
+  (like IDLE).
+
+===============================================================================
+TODO                                                               *slimv-todo*
+
+- Rewrite client/server in Lisp, so that no Python would be needed.
+- Add Compile System
+- Add Cross Reference functions
+- Add Profile functions
+- Handle specific REPL output in Vim (like compilation notes)
+- Use a Vim buffer for the REPL. This does not look possible with the current
+  state of Vim, or at least I can not see any easy way to do it.
+
+===============================================================================
+CREDITS                                                         *slimv-credits*
+
+Author: Tamas Kovacs <kovisoft at gmail dot com>
+
+Credit must go out to Bram Moolenaar and all the Vim developers for making
+the world's (one of the) best editor.
+Also special thanks to Eric Marsden and all the Emacs/SLIME developers for
+making SLIME.
+Last but not least many thanks to my wife Andrea (for the Italians out there:
+hey, this is a female name in Hungary :-) for her support and patience.
+
+===============================================================================
+vim:tw=78:noet:wrap:ts=8:ft=help:norl:
+#!/usr/bin/env python
+
+import os
+import sys
+import getopt
+import time
+import shlex
+import socket
+from subprocess import Popen, PIPE, STDOUT
+from threading import Thread
+
+autoconnect = 1             # Start and connect server automatically
+
+HOST        = ''            # Symbolic name meaning the local host
+PORT        = 5151          # Arbitrary non-privileged port
+
+debug_level = 0             # Debug level for diagnostic messages
+terminate   = 0             # Main program termination flag
+
+buffer      = ''            # Text buffer (display queue) to collect socket input and REPL output
+buflen      = 0             # Amount of text currently in the buffer
+
+python_path = 'python'      # Path of the Python interpreter (overridden via command line args)
+lisp_path   = 'clisp.exe'   # Path of the Lisp interpreter (overridden via command line args)
+slimv_path  = 'slimv.py'    # Path of this script (determined later)
+run_cmd     = ''            # Complex server-run command (if given via command line args)
+
+# Are we running on Windows (otherwise assume Linux, sorry for other OS-es)
+mswindows = (sys.platform == 'win32')
+
+
+def log( s, level ):
+    """Print diagnostic messages according to the actual debug level.
+    """
+    if debug_level >= level:
+        print s
+
+
+###############################################################################
+#
+# Client part
+#
+###############################################################################
+
+def connect_server():
+    """Try to connect server, if server not found then spawn it.
+       Return socket object on success, None on failure.
+    """
+    global python_path
+    global run_cmd
+    global autoconnect
+
+    s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
+    try:
+        s.connect( ( 'localhost', PORT ) )
+    except socket.error, msg:
+        if autoconnect:
+            # We need to try to start the server automatically
+            s.close()
+            if run_cmd == '':
+                # Complex run command not given, build it from the information available
+                if mswindows:
+                    cmd = [python_path, slimv_path, '-p', str(PORT), '-l', lisp_path, '-s']
+                else:
+                    cmd = ['xterm', '-T', 'Slimv', '-e', python_path, slimv_path, '-p', str(PORT), '-l', lisp_path, '-s']
+            else:
+                cmd = shlex.split(run_cmd)
+
+            # Start server
+            #TODO: put in try-block
+            if mswindows:
+                from win32process import CREATE_NEW_CONSOLE
+                server = Popen( cmd, creationflags=CREATE_NEW_CONSOLE )
+            else:
+                server = Popen( cmd )
+
+            # Allow subprocess (server) to start
+            time.sleep( 2.0 )
+
+            # Open socket to the server
+            s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
+            try:
+                s.connect( ( 'localhost', PORT ) )
+            except socket.error, msg:
+                s.close()
+                s =  None
+        else:   # not autoconnect
+            print "Server not found"
+            s = None
+    return s
+
+
+def send_line( server, line ):
+    """Send a line to the server:
+       first send line length in 4 bytes, then send the line itself.
+    """
+    l = len(line)
+    lstr = chr(l&255) + chr((l>>8)&255) + chr((l>>16)&255) + chr((l>>24)&255)
+    server.send( lstr )     # send message length first
+    server.send( line )     # then the message itself
+    time.sleep(0.01)
+
+
+def translate_send_line( server, line ):
+    """Send a line to the server.
+       All backslash+n character-pairs are converted to newline.
+    """
+    line = line.replace( '\\n', '\n' )
+    send_line( server, line )
+
+
+def client_file( filename ):
+    """Main client routine - input file version:
+       starts server if needed then send text to server.
+       Input is read from input file.
+    """
+    s = connect_server()
+    if s is None:
+        return
+
+    try:
+        file = open( filename, 'rt' )
+        try:
+            # Send contents of the file to the server
+            for line in file:
+                send_line( s, line.rstrip( '\n' ) )
+        finally:
+            file.close()
+    except:
+        return
+
+    s.close()
+
+
+def client_args( args ):
+    """Main client routine - command line argument version:
+       starts server if needed then send text to server.
+       Input is read from command line argument.
+    """
+    s = connect_server()
+    if s is None:
+        return
+
+    if len( args ) < 1:
+        # No command line arguments specified, read input from stdin
+        while 1:
+            try:
+                line = raw_input()
+                translate_send_line( s, line )
+            except ( EOFError, KeyboardInterrupt ):
+                log( 'breaking', 1 )
+                break
+
+    else:
+        # Send command line arguments to the server
+        print args
+        for line in args:
+            translate_send_line( s, line )
+
+    s.close()
+
+
+###############################################################################
+#
+# Server part
+#
+###############################################################################
+
+class socket_listener( Thread ):
+    """Server thread to receive text from the client via socket.
+    """
+
+    def __init__ ( self, inp ):
+        Thread.__init__( self )
+        self.inp = inp
+
+    def run( self ):
+        global buffer
+        global terminate
+
+        # Open server socket
+        self.s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
+        log( "sl.bind " + str(PORT), 1 )
+        self.s.bind( (HOST, PORT) )
+
+        while not terminate:
+            # Listen server socket
+            log( "sl.listen", 1 )
+            self.s.listen( 1 )
+            conn, addr = self.s.accept()
+
+            while not terminate:
+                l = 0
+                lstr = ''
+                # Read length first, it comes in 4 bytes
+                log( "sl.recv len", 1 )
+                try:
+                    lstr = conn.recv(4)
+                    if len( lstr ) <= 0:
+                        break;
+                except:
+                    break
+                if terminate:
+                    break
+                l = ord(lstr[0]) + (ord(lstr[1])<<8) + (ord(lstr[2])<<16) + (ord(lstr[3])<<24)
+                if l > 0:
+                    # Valid length received, now wait for the message
+                    log( "sl.recv data", 1 )
+                    try:
+                        # Read the message itself
+                        received = conn.recv(l)
+                        if len( received ) < l:
+                            break
+                    except:
+                        break
+
+                    # Fork here: write message to the stdin of REPL
+                    # and also write it to the display (display queue buffer)
+                    self.inp.write( received + '\n' )
+                    buffer = buffer + received + '\n'
+            log( "sl.close", 1 )
+            conn.close()
+
+
+class input_listener( Thread ):
+    """Server thread to receive input from console.
+    """
+
+    def __init__ ( self, inp ):
+        Thread.__init__( self )
+        self.inp = inp
+
+    def run( self ):
+        global terminate
+
+        log( "il.start", 1 )
+        while not terminate:
+            try:
+                # Read input from the console and write it
+                # to the stdin of REPL
+                log( "il.raw_input", 1 )
+                self.inp.write( raw_input() + '\n' )
+            except EOFError:
+                # EOF (Ctrl+Z on Windows, Ctrl+D on Linux) pressed?
+                log( "il.EOFError", 1 )
+                terminate = 1
+            except KeyboardInterrupt:
+                # Interrupted from keyboard (Ctrl+Break, Ctrl+C)?
+                log( "il.KeyboardInterrupt", 1 )
+                terminate = 1
+
+
+class output_listener( Thread ):
+    """Server thread to receive REPL output.
+    """
+
+    def __init__ ( self, out ):
+        Thread.__init__( self )
+        self.out = out
+
+    def run( self ):
+        global buffer
+        global terminate
+
+        log( "ol.start", 1 )
+        while not terminate:
+            log( "ol.read", 1 )
+            try:
+                # Read input from the stdout of REPL
+                # and write it to the display (display queue buffer)
+                c = self.out.read(1)
+                buffer = buffer + c
+            except:
+                #TODO: should we set terminate=1 here as well?
+                break
+
+
+def buffer_read_and_display():
+    """Read and display lines received in global display queue buffer.
+    """
+    global buffer
+    global buflen
+
+    l = len( buffer )
+    while buflen < l:
+        try:
+            # Write all lines in the buffer to the display
+            sys.stdout.write( buffer[buflen] )
+            buflen = buflen + 1
+        except:
+            break
+
+
+def server( args ):
+    """Main server routine: starts REPL and helper threads for
+       sending and receiving data to/from REPL.
+    """
+    global lisp_path
+    global terminate
+
+    # First check if server already runs
+    s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
+    try:
+        s.connect( ( 'localhost', PORT ) )
+    except socket.error, msg:
+        # Server not found, our time has come, we'll start a new server in a moment
+        pass
+    else:
+        # Server found, nothing to do here
+        s.close()
+        print "Server is already running"
+        return
+
+    # Build Lisp-starter command
+    cmd = shlex.split( lisp_path.replace( '\\', '\\\\' ) )
+
+    # Start Lisp
+    if mswindows:
+        from win32con import CREATE_NO_WINDOW
+        repl = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT, creationflags=CREATE_NO_WINDOW )
+    else:
+        repl = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT )
+
+    # Create and start helper threads
+    ol = output_listener( repl.stdout )
+    ol.start()
+    il = input_listener( repl.stdin )
+    il.start()
+    sl = socket_listener( repl.stdin )
+    sl.start()
+
+    # Allow Lisp to start, confuse it with some fancy Slimv messages
+    log( "in.start", 1 )
+    sys.stdout.write( ";;; Slimv server is started on port " + str(PORT) + "\n" )
+    sys.stdout.write( ";;; Slimv is spawning REPL...\n" )
+    time.sleep(0.5)             # wait for Lisp to start
+    buffer_read_and_display()   # read Lisp startup messages
+    sys.stdout.write( ";;; Slimv connection established\n" )
+
+    # Main server loop
+    while not terminate:
+        try:
+            # Constantly display messages in the display queue buffer
+            #TODO: it would be better some wakeup mechanism here
+            log( "in.step", 1 )
+            time.sleep(0.01)
+            buffer_read_and_display()
+
+        except EOFError:
+            # EOF (Ctrl+Z on Windows, Ctrl+D on Linux) pressed?
+            log( "in.EOFError", 1 )
+            terminate = 1
+        except KeyboardInterrupt:
+            # Interrupted from keyboard (Ctrl+Break, Ctrl+C)?
+            log( "in.KeyboardInterrupt", 1 )
+            terminate = 1
+
+    # The socket is opened here only for waking up the server thread
+    # in order to recognize the termination message
+    #TODO: exit REPL if this script is about to exit
+    cs = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
+    try:
+        cs.connect( ( 'localhost', PORT ) )
+        cs.send( " " )
+    finally:
+        # We don't care if this above fails, we'll exit anyway
+        cs.close()
+
+    # Send exit command to child process and
+    # wake output listener up at the same time
+    try:
+        repl.stdin.close()
+    except:
+        # We don't care if this above fails, we'll exit anyway
+        pass
+
+    # Be nice
+    print 'Thank you for using Slimv.'
+
+    # Wait for the child process to exit
+    time.sleep(1)
+
+
+def escape_path( path ):
+    """Surround path containing spaces with backslash + double quote,
+       so that it can be passed as a command line argument.
+    """
+    if path.find( ' ' ) < 0:
+        return path
+    if path[0:2] == '\\\"':
+        return path
+    elif path[0] == '\"':
+        return '\\' + path + '\\'
+    else:
+        return '\\\"' + path + '\\\"'
+
+
+def usage():
+    """Displays program usage information.
+    """
+    progname = os.path.basename( sys.argv[0] )
+    print 'Usage: ', progname + ' [-d LEVEL] [-s] [-c ARGS]'
+    print
+    print 'Options:'
+    print '  -?, -h, --help                show this help message and exit'
+    print '  -l PATH, --lisp=PATH          path of Lisp interpreter'
+    print '  -r PATH, --run=PATH           full command to run the server'
+    print '  -p PORT, --port=PORT          port number to use by the server/client'
+    print '  -d LEVEL, --debug=LEVEL       set debug LEVEL (0..3)'
+    print '  -s                            start server'
+    print '  -f FILENAME, --file=FILENAME  start client and send contents of file'
+    print '                                named FILENAME to server'
+    print '  -c LINE1 LINE2 ... LINEn      start client and send LINE1...LINEn to server'
+    print '                                (if present, this option must be the last one,'
+    print '                                mutually exclusive with the -f option)'
+
+
+###############################################################################
+#
+# Main program
+#
+###############################################################################
+
+if __name__ == '__main__':
+
+    EXIT, SERVER, CLIENT = range( 3 )
+    mode = EXIT
+    slimv_path = sys.argv[0]
+    python_path = sys.executable
+
+    # Always this trouble with the path/filenames containing spaces:
+    # enclose them in double quotes
+    if python_path.find( ' ' ) >= 0:
+        python_path = '"' + python_path + '"'
+
+    # Get command line options
+    try:
+        opts, args = getopt.getopt( sys.argv[1:], '?hcsf:p:l:r:d:', \
+                                    ['help', 'client', 'server', 'file=', 'port=', 'lisp=', 'run=', 'debug='] )
+
+        # Process options
+        for o, a in opts:
+            if o in ('-?', '-h', '--help'):
+                usage()
+                break
+            if o in ('-p', '--port'):
+                try:
+                    PORT = int(a)
+                except:
+                    # If given port number is malformed, then keep default value
+                    pass
+            if o in ('-l', '--lisp'):
+                lisp_path = a
+            if o in ('-r', '--run'):
+                run_cmd = a
+            if o in ('-d', '--debug'):
+                try:
+                    debug_level = int(a)
+                except:
+                    # If given level is malformed, then keep default value
+                    pass
+            if o in ('-s', '--server'):
+                mode = SERVER
+            if o in ('-c', '--client'):
+                mode = CLIENT
+                client_filename = ''
+            if o in ('-f', '--file'):
+                mode = CLIENT
+                client_filename = a
+
+    except getopt.GetoptError:
+        # print help information and exit:
+        usage()
+
+    if mode == SERVER:
+        # We are started in server mode
+        server( args )
+
+    if mode == CLIENT:
+        # We are started in client mode
+        if run_cmd != '':
+            # It is possible to pass special argument placeholders to run_cmd
+            run_cmd = run_cmd.replace( '@p', escape_path( python_path ) )
+            run_cmd = run_cmd.replace( '@s', escape_path( slimv_path ) )
+            run_cmd = run_cmd.replace( '@l', escape_path( lisp_path ) )
+            run_cmd = run_cmd.replace( '@@', '@' )
+            log( run_cmd, 1 )
+        if client_filename != '':
+            client_file( client_filename )
+        else:
+            client_args( args )
+
+# --- END OF FILE ---
+" slimv.vim:    The Superior Lisp Interaction Mode for VIM
+" Last Change:  2008 May 02
+" Maintainer:   Tamas Kovacs <kovisoft at gmail dot com>
+" License:      This file is placed in the public domain.
+"               No warranty, express or implied.
+"               *** ***   Use At-Your-Own-Risk!   *** ***
+"
+" =====================================================================
+"
+"  Load Once:
+if &cp || exists( 'g:slimv_loaded' )
+    finish
+endif
+
+let g:slimv_loaded = 1
+
+if has( 'win32' ) || has( 'win95' ) || has( 'win64' ) || has( 'win16' )
+    let g:slimv_windows = 1
+else
+    " This means Linux only at the moment
+    let g:slimv_windows = 0
+endif
+
+
+" =====================================================================
+"  Functions used by global variable definitions
+" =====================================================================
+
+" Write debug message to logile (message must be a list)
+function! SlimvWriteLog( level, message )
+    if exists( 'g:slimv_debug' ) && exists( 'g:slimv_logfile' ) && g:slimv_debug >= a:level
+        " We need to make a hack: write things into a temporary file
+        " then append temp file contents to the logfile
+        let tmp = tempname()
+        try
+            call writefile( a:message, tmp )
+        finally
+            if g:slimv_windows
+                silent execute '!type ' . tmp . ' >> ' . g:slimv_logfile
+            else
+                silent execute '!cat ' . tmp . ' >> ' . g:slimv_logfile
+            endif
+            call delete(tmp)
+        endtry
+        " Unfortunately I know no way to tell writefile to append the text
+        "call writefile( a:message, g:slimv_logfile )
+    endif
+endfunction
+
+" Write debug message to logile with a timestamp
+function! SlimvLog( level, message )
+    if exists( '*strftime' )
+        let time = strftime( '%Y %b %d %X' )
+    else
+        let time = localtime()
+    endif
+    call SlimvWriteLog( a:level, ['***** ' . time] + a:message + [''] )
+endfunction
+
+" Try to autodetect Python executable
+function! SlimvAutodetectPython()
+    if executable( 'python' )
+        return 'python'
+    endif
+
+    if g:slimv_windows
+        " Try to find Python on the standard installation places
+        let pythons = split( globpath( 'c:/python*,c:/Program Files/python*', 'python.exe' ), '\n' )
+        if len( pythons ) > 0
+            return pythons[0]
+        endif
+        " Go deeper in subdirectories
+        let pythons = split( globpath( 'c:/python*/**,c:/Program Files/python*/**', 'python.exe' ), '\n' )
+        if len( pythons ) > 0
+            return pythons[0]
+        endif
+        return ''
+    else
+        return ''
+    endif
+endfunction
+
+" Try to autodetect Lisp executable
+function! SlimvAutodetectLisp()
+    " Check the easy cases
+    if executable( 'clisp' )
+        " Common Lisp
+        return 'clisp'
+    endif
+    if executable( 'gcl' )
+        " GNU Common Lisp
+        return 'gcl'
+    endif
+    if executable( 'cmucl' )
+        " Carnegie Mellon University Common Lisp
+        return 'cmucl'
+    endif
+    if executable( 'sbcl' )
+        " Steel Bank Common Lisp
+        return 'sbcl'
+    endif
+    if executable( 'ecl' )
+        " Embeddable Common Lisp
+        return 'ecl'
+    endif
+    if executable( 'acl' )
+        " Allegro Common Lisp
+        return 'acl'
+    endif
+    if executable( 'lwl' )
+        " LispWorks
+        return 'lwl'
+    endif
+
+    if g:slimv_windows
+        " Try to find Python on the standard installation places
+        let lisps = split( globpath( 'c:/*lisp*,c:/Program Files/*lisp*', '*lisp.exe' ), '\n' )
+        if len( lisps ) > 0
+            return lisps[0]
+        endif
+        let lisps = split( globpath( 'c:/*lisp*/*,c:/Program Files/*lisp*/*', '*lisp.exe' ), '\n' )
+        if len( lisps ) > 0
+            return lisps[0]
+        endif
+        let lisps = split( globpath( 'c:/*lisp*/**,c:/Program Files/*lisp*/**', '*lisp.exe' ), '\n' )
+        if len( lisps ) > 0
+            return lisps[0]
+        endif
+        let lisps = split( globpath( 'c:/gcl*,c:/Program Files/gcl*', 'gcl.exe' ), '\n' )
+        if len( lisps ) > 0
+            return lisps[0]
+        endif
+        let lisps = split( globpath( 'c:/cmucl*,c:/Program Files/cmucl*', 'cmucl.exe' ), '\n' )
+        if len( lisps ) > 0
+            return lisps[0]
+        endif
+        let lisps = split( globpath( 'c:/sbcl*,c:/Program Files/sbcl*', 'sbcl.exe' ), '\n' )
+        if len( lisps ) > 0
+            return lisps[0]
+        endif
+        let lisps = split( globpath( 'c:/ecl*,c:/Program Files/ecl*', 'ecl.exe' ), '\n' )
+        if len( lisps ) > 0
+            return lisps[0]
+        endif
+        return ''
+    else
+        return ''
+    endif
+endfunction
+
+" Build the command to start the client
+function! SlimvClientCommand()
+    if g:slimv_python == '' || g:slimv_lisp == ''
+        " We don't have enough information to build client command
+        return ''
+    endif
+    if g:slimv_port == 5151
+        let port = ''
+    else
+        " Using port number other than default, must pass it to client
+        let port = ' -p ' . g:slimv_port
+    endif
+    if g:slimv_windows
+        return g:slimv_python . ' "' . g:slimv_path . '"' . port  . ' -l ' . g:slimv_lisp
+        " This one can be used to start Lisp in a 'Console' window
+        " instead of the default DOS box
+        "return g:slimv_python . ' "' . g:slimv_path . '"' . port . ' -r ' .
+        "       \ '"console -w Slimv -r \"/k @p @s -l ' . g:slimv_lisp . ' -s\""'
+    else
+        return g:slimv_python . ' ' . g:slimv_path . port . ' -l ' . g:slimv_lisp
+    endif
+endfunction
+
+" Find slimv.py in the Vim plugin directory (if not given in vimrc)
+if !exists( 'g:slimv_path' )
+    let plugins = split( globpath( &runtimepath, 'plugin/**/slimv.py'), '\n' )
+    if len( plugins ) > 0
+        let g:slimv_path = plugins[0]
+    else
+        let g:slimv_path = 'slimv.py'
+    endif
+endif
+
+" Log global variables to logfile (if debug log set)
+function! SlimvLogGlobals()
+    let info = [ 'Loaded file: ' . fnamemodify( bufname(''), ':p' ) ]
+    call add( info,  printf( 'g:slimv_debug = %d',   g:slimv_debug ) )
+    call add( info,  printf( 'g:slimv_logfile = %s', g:slimv_logfile ) )
+    call add( info,  printf( 'g:slimv_port = %d',    g:slimv_port ) )
+    call add( info,  printf( 'g:slimv_python = %s',  g:slimv_python ) )
+    call add( info,  printf( 'g:slimv_lisp = %s',    g:slimv_lisp ) )
+    call add( info,  printf( 'g:slimv_client = %s',  g:slimv_client ) )
+    call SlimvLog( g:slimv_debug, info )
+endfunction
+
+au BufNewFile,BufRead *.lisp call SlimvLogGlobals()
+
+
+" =====================================================================
+"  Global variable definitions
+" =====================================================================
+
+" Debug level (0 = no debug messages)
+if !exists( 'g:slimv_debug' )
+    let g:slimv_debug = 0
+endif
+
+" Leave client window open for debugging purposes
+" (works only on Windows at the moment)
+if !exists( 'g:slimv_debug_client' )
+    let g:slimv_debug_client = 0
+endif
+
+" Logfile name for debug messages
+if !exists( 'g:slimv_logfile' )
+    let g:slimv_logfile = 'slimv.log'
+endif
+
+" TCP port number to use
+if !exists( 'g:slimv_port' )
+    let g:slimv_port = 5151
+endif
+
+" Find Python (if not given in vimrc)
+if !exists( 'g:slimv_python' )
+    let g:slimv_python = SlimvAutodetectPython()
+endif
+
+" Find Lisp (if not given in vimrc)
+if !exists( 'g:slimv_lisp' )
+    let g:slimv_lisp = SlimvAutodetectLisp()
+endif
+
+" Build client command (if not given in vimrc)
+if !exists( 'g:slimv_client' )
+    let g:slimv_client = SlimvClientCommand()
+endif
+
+" Slimv keybinding set (0 = no keybindings)
+if !exists( 'g:slimv_keybindings' )
+    let g:slimv_keybindings = 1
+endif
+
+" Append Slimv menu to the global menu (0 = no menu)
+if !exists( 'g:slimv_menu' )
+    let g:slimv_menu = 1
+endif
+
+
+" =====================================================================
+"  Template definitions
+" =====================================================================
+
+if !exists( 'g:slimv_template_pprint' )
+    let g:slimv_template_pprint = '(dolist (o %1)(pprint o))'
+endif
+
+if !exists( 'g:slimv_template_undefine' )
+    let g:slimv_template_undefine = '(fmakunbound (read-from-string "%1"))'
+endif
+
+if !exists( 'g:slimv_template_describe' )
+    let g:slimv_template_describe = '(describe (read-from-string "%1"))'
+endif
+
+if !exists( 'g:slimv_template_trace' )
+    let g:slimv_template_trace = '(trace %1)'
+endif
+
+if !exists( 'g:slimv_template_untrace' )
+    let g:slimv_template_untrace = '(untrace %1)'
+endif
+
+if !exists( 'g:slimv_template_profile' )
+    "TODO: support different Lisp implementations
+    let g:slimv_template_profile = '(mon:monitor %1)'
+endif
+
+if !exists( 'g:slimv_template_unprofile' )
+    "TODO: support different Lisp implementations
+    let g:slimv_template_unprofile = '(mon:unmonitor %1)'
+endif
+
+if !exists( 'g:slimv_template_disassemble' )
+    let g:slimv_template_disassemble = "(disassemble #'%1)"
+endif
+
+if !exists( 'g:slimv_template_inspect' )
+    let g:slimv_template_inspect = '(inspect %1)'
+endif
+
+if !exists( 'g:slimv_template_apropos' )
+    let g:slimv_template_apropos = '(apropos "%1")'
+endif
+
+if !exists( 'g:slimv_template_macroexpand' )
+    let g:slimv_template_macroexpand = '(pprint %1)'
+endif
+
+if !exists( 'g:slimv_template_macroexpand_all' )
+    let g:slimv_template_macroexpand_all = '(pprint %1)'
+endif
+
+if !exists( 'g:slimv_template_compile_file' )
+"    let g:slimv_template_compile_file = '(compile-file "%1")'
+    let g:slimv_template_compile_file =
+    \ '(let ((fasl-file (compile-file "%1")))' .
+    \ '  (when (and %2 fasl-file) (load fasl-file)))'
+endif
+
+if !exists( 'g:slimv_template_compile_string' )
+    let g:slimv_template_compile_string = 
+    \ '(funcall (compile nil (read-from-string (format nil "(~S () ~A)" ' . "'" . 'lambda "%1"))))'
+endif
+
+if !exists( 'mapleader' )
+    let mapleader = ','
+endif
+
+
+" =====================================================================
+"  General utility functions
+" =====================================================================
+
+" Select symbol under cursor and copy it to register 's'
+function! SlimvSelectSymbol()
+    "TODO: can we use expand('<cWORD>') here?
+    normal viw"sy
+endfunction
+
+" Select bottom level form the cursor is inside and copy it to register 's'
+function! SlimvSelectForm()
+    normal va(o
+    " Handle '() or #'() etc. type special syntax forms
+    " TODO: what to do with ` operator?
+    let c = col( '.' ) - 2
+    while c > 0 && match( ' \t()', getline( '.' )[c] ) < 0
+        normal h
+        let c = c - 1
+    endwhile
+    normal "sy
+endfunction
+
+" Select top level form the cursor is inside and copy it to register 's'
+function! SlimvSelectToplevelForm()
+    normal 99[(
+    call SlimvSelectForm()
+endfunction
+
+" Return the contents of register 's'
+function! SlimvGetSelection()
+    return getreg( '"s' )
+endfunction
+
+" Prepare argument list to be sent to the client
+function SlimvMakeArgs( args )
+    let ar = a:args
+    let i = 0
+    while i < len(ar)
+        let ar[i] = substitute( ar[i], '"',  '\\"', 'g' )
+        let i = i + 1
+    endwhile
+    let a = join( ar, '" "' )
+    "let a = substitute( a, '"',  '\\"', 'g' )
+    let a = substitute( a, '\n', '\\n', 'g' )
+    let a = '"' . a . '" '
+    return a
+endfunction
+
+" Send text to the client
+function! SlimvSendToClient( args )
+    if g:slimv_debug_client == 0
+        let result = system( g:slimv_client . ' -c ' . SlimvMakeArgs(a:args) )
+    else
+        execute '!' . g:slimv_client . SlimvMakeArgs(a:args)
+    endif
+endfunction
+
+" Send argument to Lisp server for evaluation
+function! SlimvEval( args )
+    if g:slimv_client == ''
+        " No command to start client, we are clueless, ask user for assistance
+        if g:slimv_python == ''
+            let g:slimv_python = input( 'Enter Python path (or fill g:slimv_python in your vimrc): ', '', 'file' )
+        endif
+        if g:slimv_lisp == ''
+            let g:slimv_lisp = input( 'Enter Lisp path (or fill g:slimv_lisp in your vimrc): ', '', 'file' )
+        endif
+        let g:slimv_client = SlimvClientCommand()
+    endif
+
+    if g:slimv_client == ''
+        return
+    endif
+
+    " Hardcoded to use temporary file for passing text to the client
+    let use_temp_file = 1
+    if use_temp_file
+        let tmp = tempname()
+        try
+            let ar = []
+            let i = 0
+            while i < len( a:args )
+                call extend( ar, split( a:args[i], '\n' ) )
+                let i = i + 1
+            endwhile
+            call SlimvLog( 1, a:args )
+            call writefile( ar, tmp )
+            if g:slimv_debug_client == 0
+                let result = system( g:slimv_client . ' -f ' . tmp )
+            else
+                execute '!' . g:slimv_client . ' -f ' . tmp
+            endif
+        finally
+            call delete(tmp)
+        endtry
+    else
+        " Send text to the client via command line arguments
+        " This is problematic due to command line argument size limitations
+        " So currently it is not used
+        let total = 0
+        let i = 0
+        let j = 0
+        while j < len( a:args )
+            let l = len( a:args[j] )
+            if l >= 1000
+                " Check the length of each line
+                echo 'Line #' . j . ' too long'
+                break
+            endif
+            if total + l < 1000
+                " Limit also total length to be passed to the client
+                " in command line args
+                let total = total + l
+            else
+                " Total length would be too large, pass lines collected previously
+                " and start over collecting lines
+                call SlimvSendToClient( a:args[i : j-1] )
+                let i = j
+                let total = 0
+            endif
+            let j = j + 1
+        endwhile
+        if i < j
+            " There are some lines left unsent, send them now
+            call SlimvSendToClient( a:args[i : j-1] )
+        endif
+    endif
+endfunction
+
+" Start and connect slimv server
+" This is a quite dummy function that just evaluates a comment
+function! SlimvConnectServer()
+    call SlimvEval([';;; Slimv client connected successfully'])
+endfunction
+
+function! SlimvGetRegion() range
+    if mode() == 'v' || mode() == 'V'
+        let lines = getline( a:firstline, a:lastline )
+        let firstcol = col( a:firstline ) - 1
+        let lastcol  = col( a:lastline  ) - 2
+    else
+        let lines = getline( "'<", "'>" )
+        let firstcol = col( "'<" ) - 1
+        let lastcol  = col( "'>" ) - 2
+    endif
+    if lastcol >= 0
+        let lines[len(lines)-1] = lines[len(lines)-1][ : lastcol]
+    else
+        let lines[len(lines)-1] = ''
+    endif
+    let lines[0] = lines[0][firstcol : ]
+    return lines
+endfunction
+
+" Eval buffer lines in the given range
+function! SlimvEvalRegion() range
+    let lines = SlimvGetRegion()
+    call SlimvEval( lines )
+endfunction
+
+" Eval contents of the 's' register
+function! SlimvEvalSelection()
+    let lines = [SlimvGetSelection()]
+    call SlimvEval( lines )
+endfunction
+
+" Eval Lisp form.
+" Form given in the template is passed to Lisp without modification.
+function! SlimvEvalForm( template )
+    let lines = [a:template]
+    call SlimvEval( lines )
+endfunction
+
+" Eval Lisp form, with the given parameter substituted in the template.
+" %1 string is substituted with par1
+function! SlimvEvalForm1( template, par1 )
+    let p1 = escape( a:par1, '&' )
+    let p1 = escape( p1, '\\' )
+    let temp1 = substitute( a:template, '%1', p1, 'g' )
+    let lines = [temp1]
+    call SlimvEval( lines )
+endfunction
+
+" Eval Lisp form, with the given parameters substituted in the template.
+" %1 string is substituted with par1
+" %2 string is substituted with par2
+function! SlimvEvalForm2( template, par1, par2 )
+    let p1 = escape( a:par1, '&' )
+    let p2 = escape( a:par2, '&' )
+    let p1 = escape( p1, '\\' )
+    let p2 = escape( p2, '\\' )
+    let temp1 = substitute( a:template, '%1', p1, 'g' )
+    let temp2 = substitute( temp1,      '%2', p2, 'g' )
+    let lines = [temp2]
+    call SlimvEval( lines )
+endfunction
+
+
+" =====================================================================
+"  Special functions
+" =====================================================================
+
+" Evaluate top level form at the cursor pos
+function! SlimvEvalDefun()
+    call SlimvSelectToplevelForm()
+    call SlimvEvalSelection()
+endfunction
+
+" Evaluate the whole buffer
+function! SlimvEvalBuffer()
+    let lines = getline( 1, '$' )
+    call SlimvEval( lines )
+endfunction
+
+" Evaluate last expression
+function! SlimvEvalLastExp()
+    call SlimvSelectForm()
+    call SlimvEvalSelection()
+endfunction
+
+" Evaluate and pretty print last expression
+function! SlimvPprintEvalLastExp()
+    call SlimvSelectForm()
+    call SlimvEvalForm1( g:slimv_template_pprint, SlimvGetSelection() )
+endfunction
+
+" Evaluate expression entered interactively
+function! SlimvInteractiveEval()
+    let e = input( 'Eval: ' )
+    if e != ''
+        call SlimvEval([e])
+    endif
+endfunction
+
+" Undefine function
+function! SlimvUndefineFunction()
+    call SlimvSelectSymbol()
+    call SlimvEvalForm1( g:slimv_template_undefine, SlimvGetSelection() )
+endfunction
+
+" ---------------------------------------------------------------------
+
+" Macroexpand-1 the current top level form
+function! SlimvMacroexpand()
+    normal 99[(vt(%"sy
+    let m = SlimvGetSelection() . '))'
+    let m = substitute( m, "defmacro\\s*", "macroexpand-1 '(", 'g' )
+    call SlimvEvalForm1( g:slimv_template_macroexpand, m )
+endfunction
+
+" Macroexpand the current top level form
+function! SlimvMacroexpandAll()
+    normal 99[(vt(%"sy
+    let m = SlimvGetSelection() . '))'
+    let m = substitute( m, "defmacro\\s*", "macroexpand '(", 'g' )
+    call SlimvEvalForm1( g:slimv_template_macroexpand_all, m )
+endfunction
+
+" Switch trace on for the selected function
+function! SlimvTrace()
+    call SlimvSelectSymbol()
+    let s = input( 'Trace: ', SlimvGetSelection() )
+    echo s
+    if s != ''
+        call SlimvEvalForm1( g:slimv_template_trace, s )
+    endif
+endfunction
+
+" Switch trace off for the selected function
+function! SlimvUntrace()
+    call SlimvSelectSymbol()
+    let s = input( 'Untrace: ', SlimvGetSelection() )
+    if s != ''
+        call SlimvEvalForm1( g:slimv_template_untrace, s )
+    endif
+endfunction
+
+" Disassemble the selected function
+function! SlimvDisassemble()
+    call SlimvSelectSymbol()
+    let s = input( 'Disassemble: ', SlimvGetSelection() )
+    if s != ''
+        call SlimvEvalForm1( g:slimv_template_disassemble, s )
+    endif
+endfunction
+
+" Inspect symbol
+function! SlimvInspect()
+    call SlimvSelectSymbol()
+    let s = input( 'Inspect: ', SlimvGetSelection() )
+    if s != ''
+        call SlimvEvalForm1( g:slimv_template_inspect, s )
+    endif
+endfunction
+
+" Switch profiling on for the selected function
+function! SlimvProfile()
+    call SlimvSelectSymbol()
+    let s = input( 'Profile: ', SlimvGetSelection() )
+    if s != ''
+        call SlimvEvalForm1( g:slimv_template_profile, s )
+    endif
+endfunction
+
+" Switch profiling off for the selected function
+function! SlimvUnProfile()
+    call SlimvSelectSymbol()
+    let s = input( 'Unprofile: ', SlimvGetSelection() )
+    if s != ''
+        call SlimvEvalForm1( g:slimv_template_unprofile, s )
+    endif
+endfunction
+
+" ---------------------------------------------------------------------
+
+" Compile the current top-level form
+function! SlimvCompileDefun()
+    "TODO: handle double quote characters in form
+    call SlimvSelectToplevelForm()
+    call SlimvEvalForm1( g:slimv_template_compile_string, SlimvGetSelection() )
+endfunction
+
+" Compile and load whole file
+function! SlimvCompileLoadFile()
+    let filename = fnamemodify( bufname(''), ':p' )
+    let filename = escape( filename, '\\' )
+    call SlimvEvalForm2( g:slimv_template_compile_file, filename, 'T' )
+endfunction
+
+" Compile whole file
+function! SlimvCompileFile()
+    let filename = fnamemodify( bufname(''), ':p' )
+    let filename = escape( filename, '\\' )
+    call SlimvEvalForm2( g:slimv_template_compile_file, filename, 'NIL' )
+endfunction
+
+function! SlimvCompileRegion() range
+    "TODO: handle double quote characters in form
+    let lines = SlimvGetRegion()
+    let region = join( lines, ' ' )
+    call SlimvEvalForm1( g:slimv_template_compile_string, region )
+endfunction
+
+" Describe the selected symbol
+function! SlimvDescribeSymbol()
+    call SlimvSelectSymbol()
+    call SlimvEvalForm1( g:slimv_template_describe, SlimvGetSelection() )
+endfunction
+
+" ---------------------------------------------------------------------
+
+" Apropos of the selected symbol
+function! SlimvApropos()
+    call SlimvSelectSymbol()
+    call SlimvEvalForm1( g:slimv_template_apropos, SlimvGetSelection() )
+endfunction
+
+" =====================================================================
+"  Slimv keybindings
+" =====================================================================
+
+" <Leader> can be set in .vimrc, it defaults here to ','
+" <Leader> timeouts in 1000 msec by default, if this is too short,
+" then increase 'timeoutlen'
+
+if g:slimv_keybindings == 1
+    " Short (one-key) keybinding set
+
+    noremap <Leader>S  :call SlimvConnectServer()<CR>
+    
+    noremap <Leader>d  :<C-U>call SlimvEvalDefun()<CR>
+    noremap <Leader>e  :<C-U>call SlimvEvalLastExp()<CR>
+    noremap <Leader>E  :<C-U>call SlimvPprintEvalLastExp()<CR>
+    noremap <Leader>r  :call SlimvEvalRegion()<CR>
+    noremap <Leader>b  :<C-U>call SlimvEvalBuffer()<CR>
+    noremap <Leader>v  :call SlimvInteractiveEval()<CR>
+    noremap <Leader>u  :call SlimvUndefineFunction()<CR>
+    
+    noremap <Leader>1  :<C-U>call SlimvMacroexpand()<CR>
+    noremap <Leader>m  :<C-U>call SlimvMacroexpandAll()<CR>
+    noremap <Leader>t  :call SlimvTrace()<CR>
+    noremap <Leader>T  :call SlimvUntrace()<CR>
+    noremap <Leader>l  :call SlimvDisassemble()<CR>
+    noremap <Leader>i  :call SlimvInspect()<CR>
+    
+    noremap <Leader>D  :<C-U>call SlimvCompileDefun()<CR>
+    noremap <Leader>L  :<C-U>call SlimvCompileLoadFile()<CR>
+    noremap <Leader>F  :<C-U>call SlimvCompileFile()<CR>
+    noremap <Leader>R  :call SlimvCompileRegion()<CR>
+    
+    noremap <Leader>p  :call SlimvProfile()<CR>
+    noremap <Leader>P  :call SlimvUnprofile()<CR>
+    
+    noremap <Leader>s  :call SlimvDescribeSymbol()<CR>
+    noremap <Leader>a  :call SlimvApropos()<CR>
+
+elseif g:slimv_keybindings == 2
+    " Easy to remember (two-key) keybinding set
+
+    " Connection commands
+    noremap <Leader>cs  :call SlimvConnectServer()<CR>
+    
+    " Evaluation commands
+    noremap <Leader>ed  :<C-U>call SlimvEvalDefun()<CR>
+    noremap <Leader>ee  :<C-U>call SlimvEvalLastExp()<CR>
+    noremap <Leader>ep  :<C-U>call SlimvPprintEvalLastExp()<CR>
+    noremap <Leader>er  :call SlimvEvalRegion()<CR>
+    noremap <Leader>eb  :<C-U>call SlimvEvalBuffer()<CR>
+    noremap <Leader>ei  :call SlimvInteractiveEval()<CR>
+    noremap <Leader>eu  :call SlimvUndefineFunction()<CR>
+    
+    " Debug commands
+    noremap <Leader>m1  :<C-U>call SlimvMacroexpand()<CR>
+    noremap <Leader>ma  :<C-U>call SlimvMacroexpandAll()<CR>
+    noremap <Leader>dt  :call SlimvTrace()<CR>
+    noremap <Leader>du  :call SlimvUntrace()<CR>
+    noremap <Leader>dd  :call SlimvDisassemble()<CR>
+    noremap <Leader>di  :call SlimvInspect()<CR>
+    
+    " Compile commands
+    noremap <Leader>cd  :<C-U>call SlimvCompileDefun()<CR>
+    noremap <Leader>cl  :<C-U>call SlimvCompileLoadFile()<CR>
+    noremap <Leader>cf  :<C-U>call SlimvCompileFile()<CR>
+    noremap <Leader>cr  :call SlimvCompileRegion()<CR>
+    
+    " Profile commands
+    noremap <Leader>pp  :call SlimvProfile()<CR>
+    noremap <Leader>pu  :call SlimvUnprofile()<CR>
+    
+    " Documentation commands
+    noremap <Leader>ds  :call SlimvDescribeSymbol()<CR>
+    noremap <Leader>da  :call SlimvApropos()<CR>
+
+endif
+
+" =====================================================================
+"  Slimv menu
+" =====================================================================
+
+if g:slimv_menu == 1
+    " Works only if 'wildcharm' is <Tab>
+    ":map <Leader>, :emenu Slimv.<Tab>
+    if &wildcharm == 0
+        set wildcharm=<Tab>
+    endif
+    if &wildcharm != 0
+        execute ':map <Leader>, :emenu Slimv.' . nr2char( &wildcharm )
+    endif
+
+    menu &Slimv.&Evaluation.Eval-&Defun                :<C-U>call SlimvEvalDefun()<CR>
+    menu &Slimv.&Evaluation.Eval-Last-&Exp             :<C-U>call SlimvEvalLastExp()<CR>
+    menu &Slimv.&Evaluation.&Pprint-Eval-Last          :<C-U>call SlimvPprintEvalLastExp()<CR>
+    menu &Slimv.&Evaluation.Eval-&Region               :call SlimvEvalRegion()<CR>
+    menu &Slimv.&Evaluation.Eval-&Buffer               :<C-U>call SlimvEvalBuffer()<CR>
+    menu &Slimv.&Evaluation.Interacti&ve-Eval\.\.\.    :call SlimvInteractiveEval()<CR>
+    menu &Slimv.&Evaluation.&Undefine-Function         :call SlimvUndefineFunction()<CR>
+    
+    menu &Slimv.De&bugging.Macroexpand-&1              :<C-U>call SlimvMacroexpand()<CR>
+    menu &Slimv.De&bugging.&Macroexpand-All            :<C-U>call SlimvMacroexpandAll()<CR>
+    menu &Slimv.De&bugging.&Trace\.\.\.                :call SlimvTrace()<CR>
+    menu &Slimv.De&bugging.U&ntrace\.\.\.              :call SlimvUntrace()<CR>
+    menu &Slimv.De&bugging.Disassemb&le\.\.\.          :call SlimvDisassemble()<CR>
+    menu &Slimv.De&bugging.&Inspect\.\.\.              :call SlimvInspect()<CR>
+    
+    menu &Slimv.&Compilation.Compile-&Defun            :<C-U>call SlimvCompileDefun()<CR>
+    menu &Slimv.&Compilation.Compile-&Load-File        :<C-U>call SlimvCompileLoadFile()<CR>
+    menu &Slimv.&Compilation.Compile-&File             :<C-U>call SlimvCompileFile()<CR>
+    menu &Slimv.&Compilation.Compile-&Region           :call SlimvCompileRegion()<CR>
+    
+    menu &Slimv.&Profiling.&Profile\.\.\.              :call SlimvProfile()<CR>
+    menu &Slimv.&Profiling.&Unprofile\.\.\.            :call SlimvUnprofile()<CR>
+    
+    menu &Slimv.&Documentation.Describe-&Symbol        :call SlimvDescribeSymbol()<CR>
+    menu &Slimv.&Documentation.&Apropos                :call SlimvApropos()<CR>
+endif
+