" slimv.vim:    The Superior Lisp Interaction Mode for VIM
" Version:      0.9.14
" Last Change:  24 Aug 2021
" 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

let g:slimv_windows = 0
let g:slimv_cygwin  = 0
let g:slimv_osx     = 0

if has( 'win32' ) || has( 'win95' ) || has( 'win64' ) || has( 'win16' )
    let g:slimv_windows = 1
elseif has( 'win32unix' )
    let g:slimv_cygwin = 1
elseif has( 'macunix' )
    let g:slimv_osx = 1
endif

if ( !exists( 'g:slimv_python_version' ) && has( 'python3' ) ) ||
\  (  exists( 'g:slimv_python_version' ) && g:slimv_python_version == 3 )
    let s:py_cmd = 'python3 ' "note space
    let s:pyfile_cmd = 'py3file '
else
    let s:py_cmd = 'python '  "note space
    let s:pyfile_cmd = 'pyfile '
endif


" =====================================================================
"  Functions used by global variable definitions
" =====================================================================

" Convert Cygwin path to Windows path, if needed
function! s:Cygpath( path )
    let path = a:path
    if g:slimv_cygwin
        let path = system( 'cygpath -w ' . path )
        let path = substitute( path, "\n", "", "g" )
        let path = substitute( path, "\\", "/", "g" )
    endif
    return path
endfunction

" Find swank.py in the Vim ftplugin directory (if not given in vimrc)
if !exists( 'g:swank_path' )
    let plugins = split( globpath( &runtimepath, 'ftplugin/**/swank.py'), '\n' )
    if len( plugins ) > 0
        let g:swank_path = s:Cygpath( plugins[0] )
    else
        let g:swank_path = 'swank.py'
    endif
endif

" Get the filetype (Lisp dialect) used by Slimv
function! SlimvGetFiletype()
    if &ft != ''
        " Return Vim filetype if defined
        return &ft
    endif

    if match( tolower( g:slimv_lisp ), 'clojure' ) >= 0 || match( tolower( g:slimv_lisp ), 'clj' ) >= 0
        " Must be Clojure
        return 'clojure'
    endif

    " We have no clue, guess its lisp
    return 'lisp'
endfunction

" Try to autodetect SWANK and build the command to start the SWANK server
function! SlimvSwankCommand()
    if exists( 'g:slimv_swank_clojure' ) && SlimvGetFiletype() =~ '.*clojure.*'
        return g:slimv_swank_clojure
    endif
    if exists( 'g:slimv_swank_scheme' ) && SlimvGetFiletype() == 'scheme'
        return g:slimv_swank_scheme
    endif
    if exists( 'g:slimv_swank_cmd' )
        return g:slimv_swank_cmd
    endif

    if g:slimv_lisp == ''
        let g:slimv_lisp = input( 'Enter Lisp path (or fill g:slimv_lisp in your vimrc): ', '', 'file' )
    endif

    let cmd = SlimvSwankLoader()
    if cmd != ''
        if g:slimv_windows || g:slimv_cygwin
            return '!start /MIN ' . cmd
        elseif $STY != ''
            " GNU screen under Linux or macOS
            return "! screen -X eval 'title swank' 'screen " . cmd . "' 'select swank'"
        elseif $TMUX != ''
            " tmux under Linux or macOS
            return "! tmux new-window -d -n swank '" . cmd . "'"
        elseif g:slimv_osx
            let result = system('osascript -e "exists application \"iterm\""')
                if result[:-2] == 'true'
                    let path2as = globpath( &runtimepath, 'ftplugin/**/iterm.applescript')
                    return '!' . path2as . ' ' . cmd
                else
                    " doubles quotes within 'cmd' need to become '\\\"'
                    return '!osascript -e "tell application \"Terminal\" to do script \"' . escape(escape(cmd, '"'), '\"') . '\""'
                endif 
        elseif $DISPLAY == ''
            " No X, no terminal multiplexer. Cannot run swank server.
            call SlimvErrorWait( 'No X server. Run Vim from screen/tmux or start SWANK server manually.' )
            return ''
        else
            " Must be Linux
            return '! SWANK_PORT=' . g:swank_port . ' xterm -iconic -e ' . cmd . ' &'
        endif
    endif
    return ''
endfunction

" =====================================================================
"  Global variable definitions
" =====================================================================

" Host name or IP address of the SWANK server
if !exists( 'g:swank_host' )
    let g:swank_host = 'localhost'
endif

" TCP port number to use for the SWANK server
if !exists( 'g:swank_port' )
    let g:swank_port = 4005
endif

" Find Lisp (if not given in vimrc)
if !exists( 'g:slimv_lisp' )
    let lisp = ['', '']
    if exists( 'g:slimv_preferred' )
        let lisp = SlimvAutodetect( tolower(g:slimv_preferred) )
    endif
    if lisp[0] == ''
        let lisp = SlimvAutodetect( '' )
    endif
    let g:slimv_lisp = lisp[0]
    if !exists( 'g:slimv_impl' )
        let g:slimv_impl = lisp[1]
    endif
endif

" Try to find out the Lisp implementation
" if not autodetected and not given in vimrc
if !exists( 'g:slimv_impl' )
    let g:slimv_impl = SlimvImplementation()
endif

" REPL buffer name
if !exists( 'g:slimv_repl_name' )
    let g:slimv_repl_name = 'REPL'
endif

" SLDB buffer name
if !exists( 'g:slimv_sldb_name' )
    let g:slimv_sldb_name = 'SLDB'
endif

" INSPECT buffer name
if !exists( 'g:slimv_inspect_name' )
    let g:slimv_inspect_name = 'INSPECT'
endif

" THREADS buffer name
if !exists( 'g:slimv_threads_name' )
    let g:slimv_threads_name = 'THREADS'
endif

" Shall we open REPL buffer in split window?
if !exists( 'g:slimv_repl_split' )
    let g:slimv_repl_split = 1
endif

" Size of the split window
if !exists( 'g:slimv_repl_split_size' )
    let g:slimv_repl_split_size = ''
endif

" Wrap long lines in REPL buffer
if !exists( 'g:slimv_repl_wrap' )
    let g:slimv_repl_wrap = 1
endif

" Wrap long lines in SLDB buffer
if !exists( 'g:slimv_sldb_wrap' )
    let g:slimv_sldb_wrap = 0
endif

" Maximum number of lines echoed from the evaluated form
if !exists( 'g:slimv_echolines' )
    let g:slimv_echolines = 4
endif

" Syntax highlighting for the REPL buffer
if !exists( 'g:slimv_repl_syntax' )
    let g:slimv_repl_syntax = 1
endif

" Specifies the behaviour of insert mode <CR>, <Up>, <Down> in the REPL buffer:
" 1: <CR>   evaluates,      <Up>/<Down>     brings up command history
" 0: <C-CR> evaluates,      <C-Up>/<C-Down> brings up command history,
"    <CR>   opens new line, <Up>/<Down>     moves cursor up/down
if !exists( 'g:slimv_repl_simple_eval' )
    let g:slimv_repl_simple_eval = 1
endif

" Alternative value (in msec) for 'updatetime' while the REPL buffer is changing
if !exists( 'g:slimv_updatetime' )
    let g:slimv_updatetime = 500
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

" Build the ctags command capable of generating lisp tags file
" The command can be run with execute 'silent !' . g:slimv_ctags
if !exists( 'g:slimv_ctags' )
    let ctags = split( globpath( '$vim,$vimruntime', 'ctags.exe' ), '\n' )
    if len( ctags ) > 0
        " Remove -a option to regenerate every time
        let g:slimv_ctags = '"' . ctags[0] . '" -a --language-force=lisp *.lisp *.clj'
    endif
endif

" Name of tags file used by slimv for find-definitions
" If this is the empty string then no tags file is used
if !exists( 'g:slimv_tags_file' )
    let g:slimv_tags_file = tempname()
endif

" Prepend tags file to the tags list
if g:slimv_tags_file != ''
    if &tags == ''
        let &tags=g:slimv_tags_file
    else
        let &tags=g:slimv_tags_file . ',' . &tags
    endif
endif

" Package/namespace handling
if !exists( 'g:slimv_package' )
    let g:slimv_package = 1
endif

" General timeout for various startup and connection events (seconds)
if !exists( 'g:slimv_timeout' )
    let g:slimv_timeout = 20
endif

" Use balloonexpr to display symbol description
if !exists( 'g:slimv_balloon' )
    let g:slimv_balloon = 1
endif

" Shall we use simple or fuzzy completion?
if !exists( 'g:slimv_simple_compl' )
    let g:slimv_simple_compl = 0
endif

" Custom <Leader> for the Slimv plugin
if !exists( 'g:slimv_leader' )
    if exists( 'mapleader' ) && mapleader != ' '
        let g:slimv_leader = mapleader
    else
        let g:slimv_leader = ','
    endif
endif

" Maximum number of lines searched backwards for indenting special forms
if !exists( 'g:slimv_indent_maxlines' )
    let g:slimv_indent_maxlines = 50
endif

" Special indentation for keyword lists
if !exists( 'g:slimv_indent_keylists' )
    let g:slimv_indent_keylists = 1
endif

" Maximum length of the REPL buffer
if !exists( 'g:slimv_repl_max_len' )
    let g:slimv_repl_max_len = 0
endif

" Shall we strip ANSI escape sequences from the REPL output?
if !exists( 'g:slimv_strip_ansi' )
    let g:slimv_strip_ansi = 0
endif

" =====================================================================
"  Template definitions
" =====================================================================

if !exists( 'g:slimv_template_apropos' )
    if SlimvGetFiletype() =~ '.*clojure.*'
        let g:slimv_template_apropos = '(find-doc "%1")'
    else
        let g:slimv_template_apropos = '(apropos "%1")'
    endif
endif


" =====================================================================
"  Other non-global script variables
" =====================================================================

let s:indent = ''                                         " Most recent indentation info
let s:last_update = 0                                     " The last update time for the REPL buffer
let s:save_updatetime = &updatetime                       " The original value for 'updatetime'
let s:save_showmode = &showmode                           " The original value for 'showmode'
let s:python_initialized = 0                              " Is the embedded Python initialized?
let s:swank_version = ''                                  " SWANK server version string
let s:swank_connected = 0                                 " Is the SWANK server connected?
let s:swank_package = ''                                  " Package to use at the next SWANK eval
let s:swank_package_form = ''                             " The entire form that was used to set current package
let s:swank_form = ''                                     " Form to send to SWANK
let s:refresh_disabled = 0                                " Set this variable temporarily to avoid recursive REPL rehresh calls
let s:sldb_level = -1                                     " Are we in the SWANK debugger? -1 == no, else SLDB level
let s:break_on_exception = 0                              " Enable debugger break on exceptions (for ritz-swank)
let s:compiled_file = ''                                  " Name of the compiled file
let s:win_id = 0                                          " Counter for generating unique window id
let s:repl_buf = -1                                       " Buffer number for the REPL buffer
let s:current_buf = -1                                    " Swank action was requested from this buffer
let s:current_win = 0                                     " Swank action was requested from this window
let s:read_string_mode = 0                                " Read string mode indicator
let s:arglist_line = 0                                    " Arglist was requested in this line ...
let s:arglist_col = 0                                     " ... and column
let s:inspect_path = []                                   " Inspection path of the current object
let s:skip_sc = 'synIDattr(synID(line("."), col("."), 0), "name") =~ "[Ss]tring\\|[Cc]omment"'
                                                          " Skip matches inside string or comment 
let s:skip_q = 'getline(".")[col(".")-2] == "\\"'         " Skip escaped double quote characters in matches
let s:frame_def = '^\s\{0,2}\d\{1,}:'                     " Regular expression to match SLDB restart or frame identifier
let s:spec_indent = 'flet\|labels\|macrolet\|symbol-macrolet'
                                                          " List of symbols need special indenting
let s:spec_param = 'defmacro'                             " List of symbols with special parameter list
let s:binding_form = 'let\|let\*'                         " List of symbols with binding list

" =====================================================================
"  General utility functions
" =====================================================================

" Check that current SWANK version is same or newer than the given parameter
function! s:SinceVersion( ver )
    " Before ver 2.18 SWANK version string was a date of form YYYY-MM-DD
    if len( a:ver ) >= 8
        " Checking for old style version string YYYY-MM-DD
        if len( s:swank_version ) < 8
            " Current version is new style -> must be newer than the one we are checking for
            return 1
        endif
    else
        " Checking for new style version string X.XX
        if len( s:swank_version ) >= 8
            " Current version is old style -> must be older than the one we are checking for
            return 0
        endif
    endif
    if s:swank_version >= a:ver
        return 1
    else
        return 0
    endif
endfunction 

" Display an error message
function! SlimvError( msg )
    echohl ErrorMsg
    echo a:msg
    echohl None
endfunction 

" Display an error message and a question, return user response
function! SlimvErrorAsk( msg, question )
    echohl ErrorMsg
    let answer = input( a:msg . a:question )
    echo ""
    echohl None
    return answer
endfunction 

" Display an error message and wait for ENTER
function! SlimvErrorWait( msg )
    call SlimvErrorAsk( a:msg, " Press ENTER to continue." )
endfunction 

" Shorten long messages to fit status line
function! SlimvShortEcho( msg )
    let saved=&shortmess
    set shortmess+=T
    exe "normal! :echomsg a:msg\n" 
    let &shortmess=saved
endfunction

" Go to the end of buffer, make sure the cursor is positioned
" after the last character of the buffer when in insert mode
function s:EndOfBuffer()
    normal! G$
    if &virtualedit != 'all'
        call cursor( line('$'), 99999 )
    endif
endfunction

" Position the cursor at the end of the REPL buffer
" Optionally mark this position in Vim mark 's'
function! SlimvEndOfReplBuffer( force )
    if line( '.' ) >= b:repl_prompt_line - 1 || a:force
        " Go to the end of file only if the user did not move up from here
        call s:EndOfBuffer()
    endif
endfunction

" Remember the end of the REPL buffer: user may enter commands here
" Also remember the prompt, because the user may overwrite it
function! SlimvMarkBufferEnd( force )
    if exists( 'b:slimv_repl_buffer' )
        setlocal nomodified
        call SlimvEndOfReplBuffer( a:force )
        let b:repl_prompt_line = line( '$' )
        let b:repl_prompt_col = len( getline('$') ) + 1
        let b:repl_prompt = getline( b:repl_prompt_line )
    endif
endfunction

" Get REPL prompt line. Fix stored prompt position when corrupted
" (e.g. some lines were deleted from the REPL buffer)
function! s:GetPromptLine()
    if b:repl_prompt_line > line( '$' )
        " Stored prompt line is corrupt
        let b:repl_prompt_line = line( '$' )
        let b:repl_prompt_col = len( getline('$') ) + 1
        let b:repl_prompt = getline( b:repl_prompt_line )
    endif
    return b:repl_prompt_line
endfunction

" Generate unique window id for the current window
function s:MakeWindowId()
    if g:slimv_repl_split && !exists('w:id')
        let s:win_id = s:win_id + 1
        let w:id = s:win_id
    endif
endfunction

" Find and switch to window with the specified window id
function s:SwitchToWindow( id )
    for winnr in range( 1, winnr('$') )
        if getwinvar( winnr, 'id' ) is a:id
            execute winnr . "wincmd w"
        endif
    endfor
endfunction

" Save caller buffer identification
function! SlimvBeginUpdate()
    call s:MakeWindowId()
    let s:current_buf = bufnr( "%" )
    let s:current_win = getwinvar( winnr(), 'id' )
endfunction

" Switch to the buffer/window that was active before a swank action
function! SlimvRestoreFocus( hide_current_buf )
    if exists("b:previous_buf")
        let new_buf = b:previous_buf
        let new_win = b:previous_win
    else
        let new_buf = s:current_buf
        let new_win = s:current_win
    endif
    let buf = bufnr( "%" )
    let win = getwinvar( winnr(), 'id' )
    if a:hide_current_buf
        set nobuflisted
        b #
    endif
    if winnr('$') > 1 && new_win != '' && new_win != win
        " Switch to the caller window
        call s:SwitchToWindow( new_win )
    endif
    if new_buf >= 0 && buf != new_buf
        " Switch to the caller buffer
        execute "buf " . new_buf
    endif
endfunction

" Handle response coming from the SWANK listener
function! SlimvSwankResponse()
    let s:swank_ok_result = ''
    let s:refresh_disabled = 1
    silent execute s:py_cmd . 'swank_output(1)'
    let s:refresh_disabled = 0
    let s:swank_action = ''
    let s:swank_result = ''
    silent execute s:py_cmd . 'swank_response("")'

    if s:swank_action == ':describe-symbol' && s:swank_result != ''
        echo substitute(s:swank_result,'^\n*','','')
    elseif s:swank_ok_result != ''
        " Display the :ok result also in status bar in case the REPL buffer is not shown
        let s:swank_ok_result = substitute(s:swank_ok_result,"\<LF>",'','g')
        if s:swank_ok_result == ''
            call SlimvShortEcho( '=> OK' )
        else
            call SlimvShortEcho( '=> ' . s:swank_ok_result )
        endif
    endif
    if s:swank_actions_pending
        let s:last_update = -1
    elseif s:last_update < 0
        " Remember the time when all actions are processed
        let s:last_update = localtime()
    endif
    if s:swank_actions_pending == 0 && s:last_update >= 0 && s:last_update < localtime() - 2
        " All SWANK output handled long ago, restore original update frequency
        if &updatetime == g:slimv_updatetime
            let &updatetime = s:save_updatetime
        endif
    else
        " SWANK output still pending, keep higher update frequency
        if &updatetime != g:slimv_updatetime
            let s:save_updatetime = &updatetime
        endif
        let &updatetime = g:slimv_updatetime
    endif
endfunction

" Execute the given command and write its output at the end of the REPL buffer
function! SlimvCommand( cmd )
    silent execute a:cmd
    if &updatetime != g:slimv_updatetime
        let s:save_updatetime = &updatetime
    endif
    " Update more frequently until all swank responses processed
    let &updatetime = g:slimv_updatetime
    let s:last_update = -1
endfunction

" Execute the given SWANK command, wait for and return the response
function! SlimvCommandGetResponse( name, cmd, timeout )
    let s:refresh_disabled = 1
    call SlimvCommand( a:cmd )
    let s:swank_action = ''
    let s:swank_result = ''
    let starttime = localtime()
    let cmd_timeout = a:timeout
    if cmd_timeout == 0
        let cmd_timeout = 3
    endif
    while s:swank_action == '' && localtime()-starttime < cmd_timeout
        execute s:py_cmd . "swank_output( 0 )"
        silent execute s:py_cmd . 'swank_response("' . a:name . '")'
    endwhile
    let s:refresh_disabled = 0
    return s:swank_result
endfunction

" Reload the contents of the REPL buffer from the output file if changed
function! SlimvRefreshReplBuffer()
    if s:refresh_disabled
        " Refresh is unwanted at the moment, probably another refresh is going on
        return
    endif

    if s:repl_buf == -1
        " REPL buffer not loaded
        return
    endif

    if s:swank_connected
        call SlimvSwankResponse()
    endif

    if exists("s:input_prompt") && s:input_prompt != ''
        let answer = input( s:input_prompt )
        unlet s:input_prompt
        echo ""
        call SlimvCommand( s:py_cmd . 'swank_return("' . answer . '")' )
    endif
endfunction

" This function re-triggers the CursorHold event
" after refreshing the REPL buffer
function! SlimvTimer()
    if v:count > 0
        " Skip refreshing if the user started a command prefixed with a count
        return
    endif
    " We don't want autocommands trigger during the quick switch to/from the REPL buffer
    noautocmd call SlimvRefreshReplBuffer()
    if mode() == 'i' || mode() == 'I' || mode() == 'r' || mode() == 'R'
        if bufname('%') != g:slimv_sldb_name && bufname('%') != g:slimv_inspect_name && bufname('%') != g:slimv_threads_name
            " Put '<Insert>' twice into the typeahead buffer, which should not do anything
            " just switch to replace/insert mode then back to insert/replace mode
            " But don't do this for readonly buffers
            call feedkeys("\<insert>\<insert>")
        endif
    else
        " Put an incomplete 'f' command and an Esc into the typeahead buffer
        call feedkeys("f\e", 'n')
    endif
endfunction

" Switch refresh mode on:
" refresh REPL buffer on frequent Vim events
function! SlimvRefreshModeOn()
    augroup SlimvCursorHold
        au!
        execute "au CursorHold   * :call SlimvTimer()"
        execute "au CursorHoldI  * :call SlimvTimer()"
    augroup END
endfunction

" Switch refresh mode off
function! SlimvRefreshModeOff()
    augroup SlimvCursorHold
        au!
    augroup END
endfunction

" Called when entering REPL buffer
function! SlimvReplEnter()
    call SlimvAddReplMenu()
    augroup SlimvReplChanged
        au!
        execute "au FileChangedRO " . g:slimv_repl_name . " :call SlimvRefreshModeOff()"
    augroup END
    call SlimvRefreshModeOn()
endfunction

" Called when leaving REPL buffer
function! SlimvReplLeave()
    try
        " Check if REPL menu exists, then remove it
        aunmenu REPL
        execute ':unmap ' . g:slimv_leader . '\'
    catch
        " REPL menu not found, we cannot remove it
    endtry
    if g:slimv_repl_split
        call SlimvRefreshModeOn()
    else
        call SlimvRefreshModeOff()
    endif
endfunction

" Refresh cursor position in the REPL buffer after new lines appended
function! SlimvReplSetCursorPos( force )
    " We do not want these autocommands to fire, the buffer switch will be temporary
    let savemark = getpos("'`'")
    let save_ei = &eventignore
    set eventignore=BufEnter,BufLeave,BufWinEnter
    let win = winnr()
    windo call SlimvMarkBufferEnd( a:force )
    execute win . "wincmd w"
    let &eventignore = save_ei
    call setpos("'`", savemark)
endfunction

" View the given file in a top/bottom/left/right split window
function! s:SplitView( filename )
    " Check if we have at least two windows used by slimv (have a window id assigned)
    let winnr1 = 0
    let winnr2 = 0
    for winnr in range( 1, winnr('$') )
        if getwinvar( winnr, 'id' ) != ''
            let winnr2 = winnr1
            let winnr1 = winnr
        endif
    endfor
    " create a unique buffer name that does not collide with any file or directory name
    let bname = a:filename
    let i = 0
    while filereadable(bname) || isdirectory(bname)
        let i = i+1
        let bname = a:filename . i
    endwhile
    if winnr1 > 0 && winnr2 > 0
        " We have already at least two windows used by slimv
        let winid = getwinvar( winnr(), 'id' )
        if bufnr("%") == s:current_buf && winid == s:current_win
            " Keep the current window on screen, use the other window for the new buffer
            if winnr1 != winnr()
                execute winnr1 . "wincmd w"
            else
                execute winnr2 . "wincmd w"
            endif
        endif
        execute "silent view! " . bname
    else
        " Generate unique window id for the old window if not yet done
        call s:MakeWindowId()
        " No windows yet, need to split
        if g:slimv_repl_split == 1
            execute "silent topleft " . g:slimv_repl_split_size . "sview! " . bname
        elseif g:slimv_repl_split == 2
            execute "silent botright " . g:slimv_repl_split_size . "sview! " . bname
        elseif g:slimv_repl_split == 3
            execute "silent topleft vertical " . g:slimv_repl_split_size . "sview! " . bname
        elseif g:slimv_repl_split == 4
            execute "silent botright vertical " . g:slimv_repl_split_size . "sview! " . bname
        else
            execute "silent view! " . bname
        endif
        " Generate unique window id for the new window as well
        call s:MakeWindowId()
    endif
    stopinsert
endfunction

" Open a buffer with the given name if not yet open, and switch to it
function! SlimvOpenBuffer( name )
    let buf = bufnr( '^' . a:name . '$' )
    if buf == -1
        " Create a new buffer
        call s:SplitView( a:name )
    else
        if g:slimv_repl_split
            " Buffer is already created. Check if it is open in a window
            let win = bufwinnr( buf )
            if win == -1
                " Create windows
                call s:SplitView( a:name )
            else
                " Switch to the buffer's window
                if winnr() != win
                    execute win . "wincmd w"
                endif
            endif
        else
            execute "buffer " . buf
            stopinsert
        endif
    endif
    if s:current_buf != bufnr( "%" )
        " Keep track of the previous buffer and window
        let b:previous_buf = s:current_buf
        let b:previous_win = s:current_win
    endif
    setlocal buftype=nofile
    setlocal noswapfile
    setlocal modifiable
endfunction

" Go to the end of the screen line
function s:EndOfScreenLine()
    if len(getline('.')) < &columns
        " g$ moves the cursor to the rightmost column if virtualedit=all
        normal! $
    else
        normal! g$
    endif
endfunction

" Set special syntax rules for the REPL buffer
function! SlimvSetSyntaxRepl()
    if SlimvGetFiletype() == 'scheme'
        syn cluster replListCluster contains=@schemeListCluster,lispList
    else
        syn cluster replListCluster contains=@lispListCluster
    endif

if exists("g:lisp_rainbow") && g:lisp_rainbow != 0

    if &bg == "dark"
        hi def hlLevel0 ctermfg=red         guifg=red1
        hi def hlLevel1 ctermfg=yellow      guifg=orange1
        hi def hlLevel2 ctermfg=green       guifg=yellow1
        hi def hlLevel3 ctermfg=cyan        guifg=greenyellow
        hi def hlLevel4 ctermfg=magenta     guifg=green1
        hi def hlLevel5 ctermfg=red         guifg=springgreen1
        hi def hlLevel6 ctermfg=yellow      guifg=cyan1
        hi def hlLevel7 ctermfg=green       guifg=slateblue1
        hi def hlLevel8 ctermfg=cyan        guifg=magenta1
        hi def hlLevel9 ctermfg=magenta     guifg=purple1
    else
        hi def hlLevel0 ctermfg=red         guifg=red3
        hi def hlLevel1 ctermfg=darkyellow  guifg=orangered3
        hi def hlLevel2 ctermfg=darkgreen   guifg=orange2
        hi def hlLevel3 ctermfg=blue        guifg=yellow3
        hi def hlLevel4 ctermfg=darkmagenta guifg=olivedrab4
        hi def hlLevel5 ctermfg=red         guifg=green4
        hi def hlLevel6 ctermfg=darkyellow  guifg=paleturquoise3
        hi def hlLevel7 ctermfg=darkgreen   guifg=deepskyblue4
        hi def hlLevel8 ctermfg=blue        guifg=darkslateblue
        hi def hlLevel9 ctermfg=darkmagenta guifg=darkviolet
    endif

 if SlimvGetFiletype() =~ '.*\(clojure\|scheme\|racket\).*'

    syn region lispParen9 matchgroup=hlLevel9 start="`\=(" matchgroup=hlLevel9 end=")"  matchgroup=replPrompt end="^\S\+>" contains=TOP,@Spell
    syn region lispParen0 matchgroup=hlLevel8 start="`\=(" end=")" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen0,lispParen1,lispParen2,lispParen3,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen1 matchgroup=hlLevel7 start="`\=(" end=")" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen1,lispParen2,lispParen3,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen2 matchgroup=hlLevel6 start="`\=(" end=")" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen2,lispParen3,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen3 matchgroup=hlLevel5 start="`\=(" end=")" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen3,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen4 matchgroup=hlLevel4 start="`\=(" end=")" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen5 matchgroup=hlLevel3 start="`\=(" end=")" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen6 matchgroup=hlLevel2 start="`\=(" end=")" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen7 matchgroup=hlLevel1 start="`\=(" end=")" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen7,lispParen8,NoInParens
    syn region lispParen8 matchgroup=hlLevel0 start="`\=(" end=")" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen8,NoInParens

    syn region lispParen9 matchgroup=hlLevel9 start="`\=\[" matchgroup=hlLevel9 end="\]"  matchgroup=replPrompt end="^\S\+>" contains=TOP,@Spell
    syn region lispParen0 matchgroup=hlLevel8 start="`\=\[" end="\]" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen0,lispParen1,lispParen2,lispParen3,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen1 matchgroup=hlLevel7 start="`\=\[" end="\]" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen1,lispParen2,lispParen3,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen2 matchgroup=hlLevel6 start="`\=\[" end="\]" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen2,lispParen3,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen3 matchgroup=hlLevel5 start="`\=\[" end="\]" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen3,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen4 matchgroup=hlLevel4 start="`\=\[" end="\]" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen5 matchgroup=hlLevel3 start="`\=\[" end="\]" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen6 matchgroup=hlLevel2 start="`\=\[" end="\]" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen7 matchgroup=hlLevel1 start="`\=\[" end="\]" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen7,lispParen8,NoInParens
    syn region lispParen8 matchgroup=hlLevel0 start="`\=\[" end="\]" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen8,NoInParens

    syn region lispParen9 matchgroup=hlLevel9 start="`\={" matchgroup=hlLevel9 end="}"  matchgroup=replPrompt end="^\S\+>" contains=TOP,@Spell
    syn region lispParen0 matchgroup=hlLevel8 start="`\={" end="}" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen0,lispParen1,lispParen2,lispParen3,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen1 matchgroup=hlLevel7 start="`\={" end="}" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen1,lispParen2,lispParen3,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen2 matchgroup=hlLevel6 start="`\={" end="}" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen2,lispParen3,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen3 matchgroup=hlLevel5 start="`\={" end="}" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen3,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen4 matchgroup=hlLevel4 start="`\={" end="}" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen4,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen5 matchgroup=hlLevel3 start="`\={" end="}" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen5,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen6 matchgroup=hlLevel2 start="`\={" end="}" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen6,lispParen7,lispParen8,NoInParens
    syn region lispParen7 matchgroup=hlLevel1 start="`\={" end="}" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen7,lispParen8,NoInParens
    syn region lispParen8 matchgroup=hlLevel0 start="`\={" end="}" matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=TOP,lispParen8,NoInParens

 else

    syn region lispParen0           matchgroup=hlLevel0 start="`\=("  skip="|.\{-}|" end=")"  matchgroup=replPrompt end="^\S\+>"              contains=@replListCluster,lispParen1,replPrompt
    syn region lispParen1 contained matchgroup=hlLevel1 start="`\=("  skip="|.\{-}|" end=")"  matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=@replListCluster,lispParen2
    syn region lispParen2 contained matchgroup=hlLevel2 start="`\=("  skip="|.\{-}|" end=")"  matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=@replListCluster,lispParen3
    syn region lispParen3 contained matchgroup=hlLevel3 start="`\=("  skip="|.\{-}|" end=")"  matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=@replListCluster,lispParen4
    syn region lispParen4 contained matchgroup=hlLevel4 start="`\=("  skip="|.\{-}|" end=")"  matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=@replListCluster,lispParen5
    syn region lispParen5 contained matchgroup=hlLevel5 start="`\=("  skip="|.\{-}|" end=")"  matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=@replListCluster,lispParen6
    syn region lispParen6 contained matchgroup=hlLevel6 start="`\=("  skip="|.\{-}|" end=")"  matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=@replListCluster,lispParen7
    syn region lispParen7 contained matchgroup=hlLevel7 start="`\=("  skip="|.\{-}|" end=")"  matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=@replListCluster,lispParen8
    syn region lispParen8 contained matchgroup=hlLevel8 start="`\=("  skip="|.\{-}|" end=")"  matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=@replListCluster,lispParen9
    syn region lispParen9 contained matchgroup=hlLevel9 start="`\=("  skip="|.\{-}|" end=")"  matchgroup=replPrompt end="^\S\+>"me=s-1,re=s-1 contains=@replListCluster,lispParen0

 endif

else

  if SlimvGetFiletype() !~ '.*clojure.*'
    syn region lispList             matchgroup=Delimiter start="("    skip="|.\{-}|" end=")"  matchgroup=replPrompt end="^\S\+>" contains=@replListCluster
    syn region lispBQList           matchgroup=PreProc   start="`("   skip="|.\{-}|" end=")"  matchgroup=replPrompt end="^\S\+>" contains=@replListCluster
  endif

endif

    syn match   replPrompt /^[^(]\S\+>/
    syn match   replPrompt /^(\S\+)>/
    hi def link replPrompt Type
endfunction

" Open a new REPL buffer
function! SlimvOpenReplBuffer()
    call SlimvOpenBuffer( g:slimv_repl_name )
    setlocal noreadonly
    let s:repl_buf = bufnr( "%" )
    let b:slimv_repl_buffer = 1
    call SlimvInitRepl()
    if g:slimv_repl_syntax
        call SlimvSetSyntaxRepl()
    else
        set syntax=
    endif

    " Prompt and its line and column number in the REPL buffer
    if !exists( 'b:repl_prompt' )
        let b:repl_prompt = ''
        let b:repl_prompt_line = 1
        let b:repl_prompt_col = 1
    endif

    " Add keybindings valid only for the REPL buffer
    inoremap <buffer> <silent>        <C-CR> <End><C-O>:call SlimvSendCommand(1)<CR><End>
    inoremap <buffer> <silent>        <C-C>  <C-O>:call SlimvInterrupt()<CR>
    inoremap <buffer> <silent> <expr> <C-W>  SlimvHandleCW()

    if g:slimv_repl_simple_eval
        inoremap <buffer> <silent>        <CR>     <C-R>=pumvisible() ? "\<lt>C-Y>"  : "\<lt>End>\<lt>C-O>:call SlimvSendCommand(0)\<lt>CR>\<lt>End>"<CR>
        inoremap <buffer> <silent>        <Up>     <C-R>=pumvisible() ? "\<lt>Up>"   : SlimvHandleUp()<CR>
        inoremap <buffer> <silent>        <Down>   <C-R>=pumvisible() ? "\<lt>Down>" : SlimvHandleDown()<CR>
    else
        inoremap <buffer> <silent>        <CR>     <C-R>=pumvisible() ? "\<lt>C-Y>"  : SlimvHandleEnterRepl()<CR><C-R>=SlimvArglistOnEnter()<CR>
        inoremap <buffer> <silent>        <C-Up>   <C-R>=pumvisible() ? "\<lt>Up>"   : SlimvHandleUp()<CR>
        inoremap <buffer> <silent>        <C-Down> <C-R>=pumvisible() ? "\<lt>Down>" : SlimvHandleDown()<CR>
    endif

    if exists( 'g:paredit_loaded' )
        inoremap <buffer> <silent> <expr> <BS>   PareditBackspace(1)
    else
        inoremap <buffer> <silent> <expr> <BS>   SlimvHandleBS()
    endif

    if g:slimv_keybindings == 1
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'.      :call SlimvSendCommand(0)<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'/      :call SlimvSendCommand(1)<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'<Up>   :call SlimvPreviousCommand()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'<Down> :call SlimvNextCommand()<CR>'
    elseif g:slimv_keybindings == 2
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'rs     :call SlimvSendCommand(0)<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'ro     :call SlimvSendCommand(1)<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'rp     :call SlimvPreviousCommand()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'rn     :call SlimvNextCommand()<CR>'
    endif

    if g:slimv_repl_wrap
        inoremap <buffer> <silent>        <Home> <C-O>g<Home>
        inoremap <buffer> <silent>        <End>  <C-O>:call <SID>EndOfScreenLine()<CR>
        noremap  <buffer> <silent>        <Up>   gk
        noremap  <buffer> <silent>        <Down> gj
        noremap  <buffer> <silent>        <Home> g<Home>
        noremap  <buffer> <silent>        <End>  :call <SID>EndOfScreenLine()<CR>
        noremap  <buffer> <silent>        k      gk
        noremap  <buffer> <silent>        j      gj
        noremap  <buffer> <silent>        0      g0
        noremap  <buffer> <silent>        $      :call <SID>EndOfScreenLine()<CR>
        setlocal wrap
    endif

    hi SlimvNormal term=none cterm=none gui=none
    hi SlimvCursor term=reverse cterm=reverse gui=reverse

    augroup SlimvReplAutoCmd
        au!
        " Add autocommands specific to the REPL buffer
        execute "au FileChangedShell " . g:slimv_repl_name . " :call SlimvRefreshReplBuffer()"
        execute "au FocusGained "      . g:slimv_repl_name . " :call SlimvRefreshReplBuffer()"
        execute "au BufEnter "         . g:slimv_repl_name . " :call SlimvReplEnter()"
        execute "au BufLeave "         . g:slimv_repl_name . " :call SlimvReplLeave()"
        execute "au BufWinEnter "      . g:slimv_repl_name . " :call SlimvMarkBufferEnd(1)"
        execute "au TabEnter *"        . " :call SlimvReplSetCursorPos(1)"
    augroup END

    call SlimvRefreshReplBuffer()
endfunction

" Clear the contents of the REPL buffer, keeping the last prompt only
function! SlimvClearReplBuffer()
    let this_buf = bufnr( "%" )
    if s:repl_buf == -1
        call SlimvError( "There is no REPL buffer." )
        return
    endif
    if this_buf != s:repl_buf
        let oldpos = winsaveview()
        execute "buf " . s:repl_buf
    endif
    if b:repl_prompt_line > 1
        execute "normal! gg0d" . (b:repl_prompt_line-1) . "GG$"
        let b:repl_prompt_line = 1
    endif
    if this_buf != s:repl_buf
        execute "buf " . this_buf
        call winrestview( oldpos )
    endif
endfunction

" Open a new Inspect buffer
function SlimvOpenInspectBuffer()
    call SlimvOpenBuffer( g:slimv_inspect_name )
    let b:range_start = 0
    let b:range_end   = 0
    let b:help = SlimvHelpInspect()

    " Add keybindings valid only for the Inspect buffer
    noremap  <buffer> <silent>        <F1>   :call SlimvToggleHelp()<CR>
    noremap  <buffer> <silent>        <CR>   :call SlimvHandleEnterInspect()<CR>
    noremap  <buffer> <silent> <Backspace>   :call SlimvSendSilent(['[-1]'])<CR>
    execute 'noremap <buffer> <silent> ' . g:slimv_leader.'q      :call SlimvQuitInspect(1)<CR>'

    if version < 703
        " conceal mechanism is defined since Vim 7.3
        syn region inspectItem   matchgroup=Ignore start="{\[\d\+\]\s*" end="\[]}"
        syn region inspectAction matchgroup=Ignore start="{<\d\+>\s*"   end="<>}"
    else
        syn region inspectItem   matchgroup=Ignore start="{\[\d\+\]\s*" end="\[]}" concealends
        syn region inspectAction matchgroup=Ignore start="{<\d\+>\s*"   end="<>}" concealends
        setlocal conceallevel=3 concealcursor=nc
    endif

    hi def link inspectItem   Special
    hi def link inspectAction String

    syn match Special /^\[<<\].*$/
    syn match Special /^\[--....--\]$/
endfunction

" Open a new Threads buffer
function SlimvOpenThreadsBuffer()
    call SlimvOpenBuffer( g:slimv_threads_name )
    let b:help = SlimvHelpThreads()

    " Add keybindings valid only for the Threads buffer
    "noremap  <buffer> <silent>        <CR>   :call SlimvHandleEnterThreads()<CR>
    noremap  <buffer> <silent>        <F1>                        :call SlimvToggleHelp()<CR>
    noremap  <buffer> <silent> <Backspace>                        :call SlimvKillThread()<CR>
    execute 'noremap <buffer> <silent> ' . g:slimv_leader.'r      :call SlimvListThreads()<CR>'
    execute 'noremap <buffer> <silent> ' . g:slimv_leader.'d      :call SlimvDebugThread()<CR>'
    execute 'noremap <buffer> <silent> ' . g:slimv_leader.'k      :call SlimvKillThread()<CR>'
    execute 'noremap <buffer> <silent> ' . g:slimv_leader.'q      :call SlimvQuitThreads()<CR>'
endfunction

" Open a new SLDB buffer
function SlimvOpenSldbBuffer()
    call SlimvOpenBuffer( g:slimv_sldb_name )

    " Add keybindings valid only for the SLDB buffer
    noremap  <buffer> <silent>        <CR>   :call SlimvHandleEnterSldb()<CR>
    if g:slimv_keybindings == 1
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'a      :call SlimvDebugAbort()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'q      :call SlimvDebugQuit()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'n      :call SlimvDebugContinue()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'N      :call SlimvDebugRestartFrame()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'si      :call SlimvDebugStepInto()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'sn      :call SlimvDebugStepNext()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'so      :call SlimvDebugStepOut()<CR>'
    elseif g:slimv_keybindings == 2
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'da     :call SlimvDebugAbort()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'dq     :call SlimvDebugQuit()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'dn     :call SlimvDebugContinue()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'dr     :call SlimvDebugRestartFrame()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'si      :call SlimvDebugStepInto()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'sn      :call SlimvDebugStepNext()<CR>'
        execute 'noremap <buffer> <silent> ' . g:slimv_leader.'so      :call SlimvDebugStepOut()<CR>'
    endif

    " Set folding parameters
    setlocal foldmethod=marker
    setlocal foldmarker={{{,}}}
    setlocal foldtext=substitute(getline(v:foldstart),'{{{','','')
    call s:SetKeyword()
    if g:slimv_sldb_wrap
        setlocal wrap
    endif

    if version < 703
        " conceal mechanism is defined since Vim 7.3
        syn match Ignore /{{{/
        syn match Ignore /}}}/
    else
        setlocal conceallevel=3 concealcursor=nc
        syn match Comment /{{{/ conceal
        syn match Comment /}}}/ conceal
    endif
    syn match Type /^\s\{0,2}\d\{1,3}:/
    syn match Type /^\s\+in "\(.*\)" \(line\|byte\) \(\d\+\)$/
endfunction

" End updating an otherwise readonly buffer
function SlimvEndUpdate()
    setlocal nomodifiable
    setlocal nomodified
endfunction

" Quit Inspector
function SlimvQuitInspect( force )
    " Clear the contents of the Inspect buffer
    if exists( 'b:inspect_pos' )
        unlet b:inspect_pos
    endif
    setlocal modifiable
    silent! %d _
    call SlimvEndUpdate()
    if a:force
        call SlimvCommand( s:py_cmd . 'swank_quit_inspector()' )
    endif
    call SlimvRestoreFocus(1)
endfunction

" Quit Threads
function SlimvQuitThreads()
    " Clear the contents of the Threads buffer
    setlocal modifiable
    silent! %d _
    call SlimvEndUpdate()
    call SlimvRestoreFocus(1)
endfunction

" Quit Sldb
function SlimvQuitSldb()
    " Clear the contents of the Sldb buffer
    setlocal modifiable
    silent! %d _
    call SlimvEndUpdate()
    call SlimvRestoreFocus(1)
endfunction

" Create help text for Inspect buffer
function SlimvHelpInspect()
    let help = []
    call add( help, '<F1>        : toggle this help' )
    call add( help, '<Enter>     : open object or select action under cursor' )
    call add( help, '<Backspace> : go back to previous object' )
    call add( help, g:slimv_leader . 'q          : quit' )
    return help
endfunction

" Create help text for Threads buffer
function SlimvHelpThreads()
    let help = []
    call add( help, '<F1>        : toggle this help' )
    call add( help, '<Backspace> : kill thread' )
    call add( help, g:slimv_leader . 'k          : kill thread' )
    call add( help, g:slimv_leader . 'd          : debug thread' )
    call add( help, g:slimv_leader . 'r          : refresh' )
    call add( help, g:slimv_leader . 'q          : quit' )
    return help
endfunction

" Write help text to current buffer at given line
function SlimvHelp( line )
    setlocal modifiable
    if exists( 'b:help_shown' )
        let help = b:help
    else
        let help = ['Press <F1> for Help']
    endif
    let b:help_line = a:line
    call append( b:help_line, help )
endfunction

" Toggle help
function SlimvToggleHelp()
    if exists( 'b:help_shown' )
        let lines = len( b:help )
        unlet b:help_shown
    else
        let lines = 1
        let b:help_shown = 1
    endif
    setlocal modifiable
    execute ":" . (b:help_line+1) . "," . (b:help_line+lines) . "d"
    call SlimvHelp( b:help_line )
    call SlimvEndUpdate()
endfunction

" Open SLDB buffer and place cursor on the given frame
function SlimvGotoFrame( frame )
    call SlimvOpenSldbBuffer()
    let bcktrpos = search( '^Backtrace:', 'bcnw' )
    let line = getline( '.' )
    let item = matchstr( line, '^\s*' . a:frame .  ':' )
    if item != '' && line('.') > bcktrpos
        " Already standing on the frame
        return
    endif

    " Must locate the frame starting from the 'Backtrace:' string
    call search( '^Backtrace:', 'bcw' )
    call search( '^\s*' . a:frame .  ':', 'w' )
endfunction

" Set 'iskeyword' option depending on file type
function! s:SetKeyword()
    let old_value = &iskeyword
    if match(old_value, '\^$') >= 0
        " remove trailing ^ because it will be added as chr 94
        setlocal iskeyword-=^
    endif
    if SlimvGetFiletype() =~ '.*\(clojure\|scheme\|racket\).*'
        setlocal iskeyword+=+,-,*,/,%,<,=,>,:,$,?,!,@-@,94,~,#,\|,&
    else
        setlocal iskeyword+=+,-,*,/,%,<,=,>,:,$,?,!,@-@,94,~,#,\|,&,.,{,},[,]
    endif
endfunction

" Select symbol under cursor and return it
function! SlimvSelectSymbol()
    call s:SetKeyword()
    let oldpos = winsaveview()
    if col('.') > 1 && getline('.')[col('.')-1] =~ '\s'
        normal! h
    endif
    let symbol = expand('<cword>')
    call winrestview( oldpos )
    return symbol
endfunction

" Select symbol with possible prefixes under cursor and return it
function! SlimvSelectSymbolExt()
    let save_iskeyword = &iskeyword
    call s:SetKeyword()
    setlocal iskeyword+='
    let symbol = expand('<cword>')
    let &iskeyword = save_iskeyword
    return symbol
endfunction

" Find the matching pair
function! SlimvFindMatchingPair()
    let c = col( '.' ) - 1
    let firstchar = getline( '.' )[c]
    while c < len( getline( '.' ) ) && getline( '.' )[c] !~ '(\|)\|\[\|\]\|{\|}'
        normal! l
        let c = c + 1
    endwhile
    if getline( '.' )[c] =~ '(\|\[\|{'
        call searchpair( '(', '', ')', 'W', s:skip_sc )
    else
        call searchpair( '(', '', ')', 'bW', s:skip_sc )
    endif
endfunction

" Select bottom level form the cursor is inside and copy it to register 's'
function! SlimvSelectForm( extended )
    if SlimvGetFiletype() == 'r'
        silent! normal va(
        silent! normal "sY
        return 1
    endif
    " Search the opening '(' if we are standing on a special form prefix character
    let c = col( '.' ) - 1
    let firstchar = getline( '.' )[c]
    while c < len( getline( '.' ) ) && match( "'`#", getline( '.' )[c] ) >= 0
        normal! l
        let c = c + 1
    endwhile
    " select the whole form
"    if firstchar != '('
    if getline( '.' )[c] != '('
        call searchpair( '(', '', ')', 'bW', s:skip_sc )
    endif
    silent! normal v
    call searchpair( '(', '', ')', 'W', s:skip_sc )
    if &selection == 'exclusive' 
        silent! normal l
    endif
    let p1 = getpos('.')
    normal! o
    let p2 = getpos('.')
    if firstchar != '(' && p1[1] == p2[1] && (p1[2] == p2[2] || p1[2] == p2[2]+1)
        " Empty selection and no paren found, select current word instead
        normal! vvaw
    elseif a:extended || firstchar != '('
        " Handle '() or #'() etc. type special syntax forms (but stop at prompt)
        let c = col( '.' ) - 2
        while c >= 0 && match( ' \t()>', getline( '.' )[c] ) < 0
            normal! h
            let c = c - 1
        endwhile
    endif
    silent normal! "sy
    let sel = SlimvGetSelection()
    if sel == ''
        call SlimvError( "Form is empty." )
        return 0
    elseif sel == '(' || sel == '[' || sel == '{'
        call SlimvError( "Form is unbalanced." )
        return 0
    else
        return 1
    endif
endfunction

" Find starting '(' of a top level form
function! SlimvFindDefunStart()
    let l = line( '.' )
    let matchb = max( [l-200, 1] )
    if SlimvGetFiletype() == 'r'
        while searchpair( '(', '', ')', 'bW', s:skip_sc, matchb ) || searchpair( '{', '', '}', 'bW', s:skip_sc, matchb ) || searchpair( '\[', '', '\]', 'bW', s:skip_sc, matchb )
        endwhile
    else
        while searchpair( '(', '', ')', 'bW', s:skip_sc, matchb )
        endwhile
    endif
endfunction

" Select top level form the cursor is inside and copy it to register 's'
function! SlimvSelectDefun()
    call SlimvFindDefunStart()
    if SlimvGetFiletype() == 'r'
        " The cursor must be on the enclosing paren character
        silent! normal v%"sY
        return 1
    else
        return SlimvSelectForm( 1 )
    endif
endfunction

" Return the contents of register 's'
function! SlimvGetSelection()
    return getreg( 's' )
endfunction

" Find language specific package/namespace definition backwards
" Set it as the current package for the next swank action
function! SlimvFindPackage()
    if !g:slimv_package || SlimvGetFiletype() == 'scheme'
        return
    endif
    let oldpos = winsaveview()
    let save_ic = &ignorecase
    set ignorecase
    if SlimvGetFiletype() =~ '.*clojure.*'
        let string = '\(in-ns\|ns\)'
    else
        let string = '\(cl:\|common-lisp:\|\)in-package'
    endif
    let found = 0
    let searching = search( '(\s*' . string . '\s', 'bcW' )
    while searching
        " Search for the previos occurrence
        if synIDattr( synID( line('.'), col('.'), 0), 'name' ) !~ '[Ss]tring\|[Cc]omment'
            " It is not inside a comment or string
            let found = 1
            break
        endif
        let searching = search( '(\s*' . string . '\s', 'bW' )
    endwhile
    if found
        " Find the package name with all folds open
        normal! zn
        silent normal! w
        let l:package_command = expand('<cword>')
        silent normal! w
        let l:packagename_tokens = split(expand('<cWORD>'),')\|\s')
        normal! zN
        if l:packagename_tokens != []
            " Remove quote character from package name
            let s:swank_package = substitute( l:packagename_tokens[0], "'", '', '' )
            let s:swank_package_form = "(" . l:package_command . " " . l:packagename_tokens[0] . ")\n"
        else
            let s:swank_package = ''
            let s:swank_package_form = ''
        endif
    endif
    let &ignorecase = save_ic
    call winrestview( oldpos )
endfunction

" Execute the given SWANK command with current package defined
function! SlimvCommandUsePackage( cmd )
    call SlimvFindPackage()
    let s:refresh_disabled = 1
    call SlimvCommand( a:cmd )
    let s:swank_package = ''
    let s:swank_package_form = ''
    let s:refresh_disabled = 0
    call SlimvRefreshReplBuffer()
endfunction

" Initialize embedded Python and connect to SWANK server
function! SlimvConnectSwank()
    if !s:python_initialized
        if ( s:py_cmd == 'python3 ' && ! has('python3') ) ||
         \ ( s:py_cmd == 'python '  && ! has('python' ) )
            call SlimvErrorWait( 'Vim is compiled without the Python feature or Python is not installed. Unable to run SWANK client.' )
            return 0
        endif
        execute s:py_cmd . 'import vim'
        execute s:pyfile_cmd . g:swank_path
        let s:python_initialized = 1
    endif


    if !s:swank_connected
        let s:swank_version = ''
        let s:lisp_version = ''
        if g:swank_host == ''
            let g:swank_host = input( 'Swank server host name: ', 'localhost' )
        endif
        execute s:py_cmd . 'swank_connect("' . g:swank_host . '", ' . g:swank_port . ', "result" )'
        if result != '' && ( g:swank_host == 'localhost' || g:swank_host == '127.0.0.1' )
            " SWANK server is not running, start server if possible
            let swank = SlimvSwankCommand()
            if swank != ''
                redraw
                echon "\rStarting SWANK server..."
                silent execute swank
                let starttime = localtime()
                while result != '' && localtime()-starttime < g:slimv_timeout
                    sleep 500m
                    execute s:py_cmd . 'swank_connect("' . g:swank_host . '", ' . g:swank_port . ', "result" )'
                endwhile
                redraw!
            endif
        endif
        if result != ''
            " Display connection error message
            call SlimvErrorWait( result )
            return 0
        endif

        " Connected to SWANK server
        redraw
        echon "\rGetting SWANK connection info..."
        let starttime = localtime()
        while s:swank_version == '' && localtime()-starttime < g:slimv_timeout
            call SlimvSwankResponse()
        endwhile

        " Require some contribs
        let contribs = 'swank-presentations swank-fancy-inspector swank-c-p-c swank-arglists'
        if SlimvGetFiletype() == 'lisp'
            let contribs = 'swank-asdf swank-package-fu ' . contribs
        endif
        if g:slimv_simple_compl == 0
            let contribs = contribs . ' swank-fuzzy'
        endif
        execute s:py_cmd . "swank_require('(" . contribs . ")')"
        call SlimvSwankResponse()
        if s:SinceVersion( '2011-12-04' )
            execute s:py_cmd . "swank_require('swank-repl')"
            call SlimvSwankResponse()
        endif
        if s:SinceVersion( '2008-12-23' )
            call SlimvCommandGetResponse( ':create-repl', s:py_cmd . 'swank_create_repl()', g:slimv_timeout )
        endif
        let s:swank_connected = 1
        redraw
        echon "\rConnected to SWANK server on port " . g:swank_port . "."
        if exists( "g:swank_block_size" ) && SlimvGetFiletype() == 'lisp'
            " Override SWANK connection output buffer size
            if s:SinceVersion( '2014-09-08' )
                let cmd = "(progn (setf (slot-value (swank::connection.user-output swank::*emacs-connection*) 'swank/gray::buffer)"
            else
                let cmd = "(progn (setf (slot-value (swank::connection.user-output swank::*emacs-connection*) 'swank-backend::buffer)"
            endif
            let cmd = cmd . " (make-string " . g:swank_block_size . ")) nil)"
            call SlimvSend( [cmd], 0, 1 )
        endif
        if exists( "*SlimvReplInit" )
            " Perform implementation specific REPL initialization if supplied
            call SlimvReplInit( s:lisp_version )
        endif
    endif

    return s:swank_connected
endfunction

" Send argument to Lisp server for evaluation
function! SlimvSend( args, echoing, output )
    if ! SlimvConnectSwank()
        return
    endif

    " Send the lines to the client for evaluation
    let text = join( a:args, "\n" ) . "\n"

    let s:refresh_disabled = 1
    let s:swank_form = text
    if a:echoing && g:slimv_echolines != 0
        if g:slimv_echolines > 0
            let nlpos = match( s:swank_form, "\n", 0, g:slimv_echolines )
            if nlpos > 0
                " Echo only the first g:slimv_echolines number of lines
                let trimmed = strpart( s:swank_form, nlpos )
                let s:swank_form = strpart( s:swank_form, 0, nlpos )
                let ending = s:CloseForm( s:swank_form )
                if ending != 'ERROR'
                    if substitute( trimmed, '\s\|\n', '', 'g' ) == ''
                        " Only whitespaces are trimmed
                        let s:swank_form = s:swank_form . ending . "\n"
                    else
                        " Valuable characters trimmed, indicate it by printing "..."
                        let s:swank_form = s:swank_form . " ..." . ending . "\n"
                    endif
                endif
            endif
        endif
        if a:output
            silent execute s:py_cmd . 'append_repl("s:swank_form", 1)'
        endif
        let s:swank_form = text
    elseif a:output
        " Open a new line for the output
        silent execute s:py_cmd . " append_repl('\\n', 0)"
    endif
    call SlimvCommand( s:py_cmd . 'swank_input("s:swank_form")' )
    let s:swank_package = ''
    let s:swank_package_form = ''
    let s:refresh_disabled = 0
    call SlimvRefreshModeOn()
    call SlimvRefreshReplBuffer()
endfunction

" Eval arguments in Lisp REPL
function! SlimvEval( args )
    call SlimvSend( a:args, 1, 1 )
endfunction

" Send argument silently to SWANK
function! SlimvSendSilent( args )
    call SlimvSend( a:args, 0, 0 )
endfunction

" Set command line after the prompt
function! SlimvSetCommandLine( cmd )
    let line = getline( "." )
    if line( "." ) == s:GetPromptLine()
        " The prompt is in the line marked by b:repl_prompt_line
        let promptlen = len( b:repl_prompt )
    else
        let promptlen = 0
    endif
    if len( line ) > promptlen
        let line = strpart( line, 0, promptlen )
    endif

    if s:GetPromptLine() < line( '$' )
        " Delete extra lines after the prompt
        let c = col( '.' )
        execute (s:GetPromptLine()+1) . ',' . (line('$')) . 'd_'
        call cursor( line('.'), c )
    endif

    let lines = split( a:cmd, '\n' )
    if len(lines) > 0
        let line = line . lines[0]
    endif
    call setline( ".", line )
    if len(lines) > 1
        call append( s:GetPromptLine(), lines[1:] )
    endif
    set nomodified
endfunction

" Add command list to the command history
function! SlimvAddHistory( cmd )
    if !exists( 'g:slimv_cmdhistory' )
        let g:slimv_cmdhistory = []
    endif
    let i = 0
    let form = join( a:cmd, "\n" )
    " Trim leading and trailing whitespaces from the command
    let form = substitute( form, '^\s*\(.*[^ ]\)\s*', '\1', 'g' )
    if len( form ) > 1 || len( g:slimv_cmdhistory ) == 0 || form != g:slimv_cmdhistory[-1]
        " Add command only if differs from the last one
        call add( g:slimv_cmdhistory, form )
    endif
    let g:slimv_cmdhistorypos = len( g:slimv_cmdhistory )
endfunction

" Recall command from the command history at the marked position
function! SlimvRecallHistory( direction )
    let searchtext = ''
    let l = line( '.' )
    let c = col( '.' )
    let set_cursor_pos = 0
    if line( '.' ) == s:GetPromptLine() && c > b:repl_prompt_col
        " Search for lines beginning with the text up to the cursor position
        let searchtext = strpart( getline('.'), b:repl_prompt_col-1, c-b:repl_prompt_col )
        let searchtext = substitute( searchtext, '^\s*$', '', 'g' )
        let searchtext = substitute( searchtext, '^\s*\(.*[^ ]\)', '\1', 'g' )
    endif
    let historypos = g:slimv_cmdhistorypos
    let g:slimv_cmdhistorypos = g:slimv_cmdhistorypos + a:direction
    while g:slimv_cmdhistorypos >= 0 && g:slimv_cmdhistorypos < len( g:slimv_cmdhistory )
        let cmd = g:slimv_cmdhistory[g:slimv_cmdhistorypos]
        if len(cmd) >= len(searchtext) && strpart(cmd, 0, len(searchtext)) == searchtext
            call SlimvSetCommandLine( g:slimv_cmdhistory[g:slimv_cmdhistorypos] )
            return
        endif
        let g:slimv_cmdhistorypos = g:slimv_cmdhistorypos + a:direction
    endwhile
    if searchtext == ''
        call SlimvSetCommandLine( "" )
    else
        let g:slimv_cmdhistorypos = historypos
    endif
endfunction

" Return missing parens, double quotes, etc to properly close form
function! s:CloseForm( form )
    let end = ''
    let i = 0
    while i < len( a:form )
        if a:form[i] == '"'
            " Inside a string
            let end = '"' . end
            let i += 1
            while i < len( a:form )
                if a:form[i] == '\'
                    " Ignore next character
                    let i += 2
                elseif a:form[i] == '"'
                    let end = end[1:]
                    break
                else
                    let i += 1
                endif
            endwhile
        elseif a:form[i] == ';'
            " Inside a comment
            let end = "\n" . end
            let cend = match(a:form, "\n", i)
            if cend == -1
                break
            endif
            let i = cend
            let end = end[1:]
        else
            " We are outside of strings and comments, now we shall count parens
            if a:form[i] == '('
                let end = ')' . end
            elseif a:form[i] == '[' && SlimvGetFiletype() =~ '.*\(clojure\|scheme\|racket\).*'
                let end = ']' . end
            elseif a:form[i] == '{' && SlimvGetFiletype() =~ '.*\(clojure\|scheme\|racket\).*'
                let end = '}' . end
            elseif a:form[i] == ')' || ((a:form[i] == ']' || a:form[i] == '}') && SlimvGetFiletype() =~ '.*\(clojure\|scheme\|racket\).*')
                if len( end ) == 0 || end[0] != a:form[i]
                    " Oops, too many closing parens or invalid closing paren
                    return 'ERROR'
                endif
                let end = end[1:]
            endif
        endif
        let i += 1
    endwhile
    return end
endfunction

" Some multi-byte characters screw up the built-in lispindent()
" This function is a wrapper that tries to fix it
" TODO: implement custom indent procedure and omit lispindent()
function SlimvLispindent( lnum )
    set lisp
    if SlimvGetFiletype() =~ '.*clojure.*' && exists( '*GetClojureIndent' ) && line('.') == a:lnum
        let li = GetClojureIndent()
    else
        let li = lispindent( a:lnum )
    endif
    set nolisp
    let backline = max([a:lnum-g:slimv_indent_maxlines, 1])
    let oldpos = getpos( '.' )
    call cursor( oldpos[1], 1 )
    " Find containing form
    let [lhead, chead] = searchpairpos( '(', '', ')', 'bW', s:skip_sc, backline )
    if lhead == 0
        " No containing form, lispindent() is OK
        call cursor( oldpos[1], oldpos[2] )
        return li
    endif
    " Find outer form
    let [lparent, cparent] = searchpairpos( '(', '', ')', 'bW', s:skip_sc, backline )
    call cursor( oldpos[1], oldpos[2] )
    if lparent == 0 || lhead != lparent
        " No outer form or starting above inner form, lispindent() is OK
        return li
    endif
    " Count extra bytes before the function header
    let header = strpart( getline( lparent ), 0 )
    let total_extra = 0
    let extra = 0
    let c = 0
    while a:lnum > 0 && c < chead-1
        let bytes = byteidx( header, c+1 ) - byteidx( header, c )
        if bytes > 1
            let total_extra = total_extra + bytes - 1
            if c >= cparent && extra < 10
                " Extra bytes in the outer function header
                let extra = extra + bytes - 1
            endif
        endif
        let c = c + 1
    endwhile
    if total_extra == 0  
        " No multi-byte character, lispindent() is OK
        return li
    endif
    " In some cases ending spaces add up to lispindent() if there are multi-byte characters
    let ending_sp = len( matchstr( getline( lparent ), ' *$' ) )
    " Determine how wrong lispindent() is based on the number of extra bytes
    " These values were determined empirically
    if lparent == a:lnum - 1
        " Function header is in the previous line
        if extra == 0 && total_extra > 1
            let ending_sp = ending_sp + 1
        endif
        return li + [0, 1, 0, -3, -3, -3, -5, -5, -7, -7, -8][extra] - ending_sp
    else
        " Function header is in an upper line
        if extra == 0 || total_extra == extra
            let ending_sp = 0
        endif
        return li + [0, 1, 0, -2, -2, -3, -3, -3, -3, -3, -3][extra] - ending_sp
    endif
endfunction

" Return Lisp source code indentation at the given line
" Does not keep the cursor position
function! SlimvIndentUnsafe( lnum )
    if &autoindent == 0 || a:lnum <= 1
        " Start of the file
        return 0
    endif
    let pnum = prevnonblank(a:lnum - 1)
    if pnum == 0
        " Hit the start of the file, use zero indent.
        return 0
    endif
    let oldpos = getpos( '.' )
    let linenum = a:lnum

    " Handle multi-line string
    let plen = len( getline( pnum ) )
    if synIDattr( synID( pnum, plen, 0), 'name' ) =~ '[Ss]tring' && getline(pnum)[plen-1] != '"'
        " Previous non-blank line ends with an unclosed string, so this is a multi-line string
        let [l, c] = searchpairpos( '"', '', '"', 'bnW', s:skip_q )
        if l == pnum && c > 0
            " Indent to the opening double quote (if found)
            return c
        else
            return SlimvLispindent( linenum )
        endif
    endif
    if synIDattr( synID( pnum, 1, 0), 'name' ) =~ '[Ss]tring' && getline(pnum)[0] != '"'
        " Previous non-blank line is the last line of a multi-line string
        call cursor( pnum, 1 )
        " First find the end of the multi-line string (omit \" characters)
        let [lend, cend] = searchpos( '[^\\]"', 'nW' )
        if lend > 0 && strpart(getline(lend), cend+1) =~ '(\|)\|\[\|\]\|{\|}'
            " Structural change after the string, no special handling
        else
            " Find the start of the multi-line string (omit \" characters)
            let [l, c] = searchpairpos( '"', '', '"', 'bnW', s:skip_q )
            if l > 0 && strpart(getline(l), 0, c-1) =~ '^\s*$'
                " Nothing else before the string: indent to the opening "
                return c - 1
            endif
            if l > 0
                " Pretend that we are really after the first line of the multi-line string
                let pnum = l
                let linenum = l + 1
            endif
        endif
        call cursor( oldpos[1], oldpos[2] )
    endif

    " Handle special indentation style for flet, labels, etc.
    " When searching for containing forms, don't go back
    " more than g:slimv_indent_maxlines lines.
    let backline = max([pnum-g:slimv_indent_maxlines, 1])
    let indent_keylists = g:slimv_indent_keylists

    " Check if the previous line actually ends with a multi-line subform
    let parent = pnum
    let [l, c] = searchpos( ')', 'bW' )
    if l == pnum
        let [l, c] = searchpairpos( '(', '', ')', 'bW', s:skip_sc, backline )
        if l > 0
            " Make sure it is not a top level form and the containing form starts in the same line
            let [l2, c2] = searchpairpos( '(', '', ')', 'bW', s:skip_sc, backline )
            if l2 == l
                " Remember the first line of the multi-line form
                let parent = l
            endif
        endif
    endif

    " Find beginning of the innermost containing form
    call cursor( oldpos[1], 1 )
    let [l, c] = searchpairpos( '(', '', ')', 'bW', s:skip_sc, backline )
    if l > 0
        if SlimvGetFiletype() =~ '.*\(clojure\|scheme\|racket\).*'
            " Is this a clojure form with [] binding list?
            call cursor( oldpos[1], oldpos[2] )
            let [lb, cb] = searchpairpos( '\[', '', '\]', 'bW', s:skip_sc, backline )
            if lb >= l && (lb > l || cb > c)
                return cb
            endif
            " Is this a multi-arity function definition?
            let line = strpart( getline(l), c-1 )
            if match( line, '(\s*\[' ) >= 0
                return c + 1
            endif
        endif
        " Is this a form with special indentation?
        let line = strpart( getline(l), c-1 )
        if match( line, '\c^(\s*\('.s:spec_indent.'\)\>' ) >= 0
            " Search for the binding list and jump to its end
            if search( '(' ) > 0
                call searchpair( '(', '', ')', '', s:skip_sc )
                if line('.') == pnum
                    " We are indenting the first line after the end of the binding list
                    return c + 1
                endif
            endif
        elseif l == pnum
            " If the containing form starts above this line then find the
            " second outer containing form (possible start of the binding list)
            let [l2, c2] = searchpairpos( '(', '', ')', 'bW', s:skip_sc, backline )
            if l2 > 0
                let line2 = strpart( getline(l2), c2-1 )
                if match( line2, '\c^(\s*\('.s:spec_param.'\)\>' ) >= 0
                    if search( '(' ) > 0
                        if line('.') == l && col('.') == c
                            " This is the parameter list of a special form
                            return c
                        endif
                    endif
                endif
                if SlimvGetFiletype() !~ '.*clojure.*'
                    if l2 == l && match( line2, '\c^(\s*\('.s:binding_form.'\)\>' ) >= 0
                        " Is this a lisp form with binding list?
                        return c
                    endif
                    if match( line2, '\c^(\s*cond\>' ) >= 0 && match( line, '\c^(\s*t\>' ) >= 0
                        " Is this the 't' case for a 'cond' form?
                        return c
                    endif
                    if match( line2, '\c^(\s*defpackage\>' ) >= 0
                        let indent_keylists = 0
                    endif
                endif
                " Go one level higher and check if we reached a special form
                let [l3, c3] = searchpairpos( '(', '', ')', 'bW', s:skip_sc, backline )
                if l3 > 0
                    " Is this a form with special indentation?
                    let line3 = strpart( getline(l3), c3-1 )
                    if match( line3, '\c^(\s*\('.s:spec_indent.'\)\>' ) >= 0
                        " This is the first body-line of a binding
                        return c + 1
                    endif
                    if match( line3, '\c^(\s*defsystem\>' ) >= 0
                        let indent_keylists = 0
                    endif
                    " Finally go to the topmost level to check for some forms with special keyword indenting
                    let [l4, c4] = searchpairpos( '(', '', ')', 'brW', s:skip_sc, backline )
                    if l4 > 0
                        let line4 = strpart( getline(l4), c4-1 )
                        if match( line4, '\c^(\s*defsystem\>' ) >= 0
                            let indent_keylists = 0
                        endif
                    endif
                endif
            endif
        endif
    endif

    " Restore all cursor movements
    call cursor( oldpos[1], oldpos[2] )

    " Check if the current form started in the previous nonblank line
    if l == parent
        " Found opening paren in the previous line
        let line = getline(l)
        let form = strpart( line, c )
        " Determine the length of the function part up to the 1st argument
        let funclen = matchend( form, '\s*\S*\s*' ) + 1
        " Contract strings, remove comments
        let form = substitute( form, '".\{-}[^\\]"', '""', 'g' )
        let form = substitute( form, ';.*$', '', 'g' )
        " Contract subforms by replacing them with a single character
        let f = ''
        while form != f
            let f = form
            let form = substitute( form, '([^()]*)',     '0', 'g' )
            let form = substitute( form, '([^()]*$',     '0', 'g' )
            let form = substitute( form, '\[[^\[\]]*\]', '0', 'g' )
            let form = substitute( form, '\[[^\[\]]*$',  '0', 'g' )
            let form = substitute( form, '{[^{}]*}',     '0', 'g' )
            let form = substitute( form, '{[^{}]*$',     '0', 'g' )
        endwhile
        " Find out the function name
        let func = matchstr( form, '\<\k*\>' )
        " If it's a keyword, keep the indentation straight
        if indent_keylists && strpart(func, 0, 1) == ':'
            if form =~ '^:\S*\s\+\S'
                " This keyword has an associated value in the same line
                return c
            else
                " The keyword stands alone in its line with no associated value
                return c + 1
            endif
        endif
        " Fix indentation issues not handled by the default lisp.vim
        if SlimvGetFiletype() =~ '.*clojure.*'
            if match( func, 'defn$' ) >= 0
                return c + 1
            endif
        elseif SlimvGetFiletype() =~ '.*\(scheme\|racket\).*'
            if match( func, 'syntax-rules$' ) >= 0
                return c + 1
            endif
        else
            if match( func, 'defgeneric$' ) >= 0 || match( func, 'defsystem$' ) >= 0 || match( func, 'aif$' ) >= 0
                return c + 1
            endif
        endif
        if match( func, 'if$' ) >= 0 || match( func, 'do$' ) >= 0
            return c + funclen - 1
        endif
        " Remove package specification
        let func = substitute(func, '^.*:', '', '')
        if func != '' && s:swank_connected
            " Look how many arguments are on the same line
            " If an argument is actually a multi-line subform, then replace it with a single character
            let form = substitute( form, "([^()]*$", '0', 'g' )
            let form = substitute( form, "[()\\[\\]{}#'`,]", '', 'g' )
            let args_here = len( split( form ) ) - 1
            " Get swank indent info
            let s:indent = ''
            silent execute s:py_cmd . 'get_indent_info("' . func . '")'
            if s:indent != '' && s:indent == args_here
                " The next one is an &body argument, so indent by 2 spaces from the opening '('
                return c + 1
            endif
            let llen = len( line )
            if synIDattr( synID( l, llen, 0), 'name' ) =~ '[Ss]tring' && line[llen-1] != '"'
                " Parent line ends with a multi-line string
                " lispindent() fails to handle it correctly
                if s:indent == '' && args_here > 0
                    " No &body argument, ignore lispindent() and indent to the 1st argument
                    return c + funclen - 1
                endif
            endif
        endif
    endif

    " Use default Lisp indenting
    let li = SlimvLispindent(linenum)
    let line = strpart( getline(linenum-1), li-1 )
    let gap = matchend( line, '^(\s\+\S' )
    if gap >= 0
        " Align to the gap between the opening paren and the first atom
        return li + gap - 2
    endif
    return li
endfunction 

" Indentation routine, keeps original cursor position
function! SlimvIndent( lnum )
    if exists("g:slimv_indent_disable") && g:slimv_indent_disable
        let indent = lispindent( a:lnum )
    else
        let oldpos = getpos( '.' )
        let indent = SlimvIndentUnsafe( a:lnum )
        call cursor( oldpos[1], oldpos[2] )
    endif
    return indent
endfunction

" Convert indent value to spaces or a mix of tabs and spaces
" depending on the value of 'expandtab'
function! s:MakeIndent( indent )
    if &expandtab
        return repeat( ' ', a:indent )
    else
        return repeat( "\<Tab>", a:indent / &tabstop ) . repeat( ' ', a:indent % &tabstop )
    endif
endfunction

" Send command line to REPL buffer
" Arguments: close = add missing closing parens
function! SlimvSendCommand( close )
    call SlimvRefreshModeOn()
    let lastline = s:GetPromptLine()
    let lastcol  = b:repl_prompt_col
    if lastline > 0
        if line( "." ) >= lastline
            " Trim the prompt from the beginning of the command line
            " The user might have overwritten some parts of the prompt
            let cmdline = getline( lastline )
            let c = 0
            while c < lastcol - 1 && cmdline[c] == b:repl_prompt[c]
                let c = c + 1
            endwhile
            let cmd = [ strpart( getline( lastline ), c ) ]

            " Build a possible multi-line command
            let l = lastline + 1
            while l <= line("$")
                call add( cmd, strpart( getline( l ), 0) )
                let l = l + 1
            endwhile

            " Count the number of opening and closing braces
            let end = s:CloseForm( join( cmd, "\n" ) )
            if end == 'ERROR'
                " Too many closing parens
                call SlimvErrorWait( "Too many or invalid closing parens found." )
                return
            endif
            let echoing = 0
            if a:close && end != ''
                " Close form if necessary and evaluate it
                let cmd[len(cmd)-1] = cmd[len(cmd)-1] . end
                let end = ''
                let echoing = 1
            endif
            if end == ''
                " Expression finished, let's evaluate it
                " but first add it to the history
                call SlimvAddHistory( cmd )
                " Evaluate, but echo only when form is actually closed here
                call SlimvSend( cmd, echoing, 1 )
            else
                " Expression is not finished yet, indent properly and wait for completion
                " Indentation works only if lisp indentation is switched on
                call SlimvArglist()
                let l = line('.') + 1
                call append( '.', '' )
                call setline( l, s:MakeIndent( SlimvIndent(l) ) )
                normal! j$
            endif
        endif
    else
        silent execute s:py_cmd . " append_repl('Slimv error: previous EOF mark not found, re-enter last form:\\n', 0)"
    endif
endfunction

" Close current top level form by adding the missing parens
function! SlimvCloseForm()
    let l2 = line( '.' )
    call SlimvFindDefunStart()
    let l1 = line( '.' )
    let form = []
    let l = l1
    while l <= l2
        call add( form, getline( l ) )
        let l = l + 1
    endwhile
    let end = s:CloseForm( join( form, "\n" ) )
    if end == 'ERROR'
        " Too many closing parens
        call SlimvErrorWait( "Too many or invalid closing parens found." )
    elseif end != ''
        " Add missing parens
        if end[0] == "\n"
            call append( l2, end[1:] )
        else
            call setline( l2, getline( l2 ) . end )
        endif
    endif
    normal! %
endfunction

" Handle insert mode 'Enter' keypress
function! SlimvHandleEnter()
    let s:arglist_line = line('.')
    let s:arglist_col = col('.')
    if pumvisible()
        " Pressing <CR> in a pop up selects entry.
        return "\<C-Y>"
    else
        if exists( 'g:paredit_mode' ) && g:paredit_mode && g:paredit_electric_return
            " Apply electric return
            return PareditEnter()
        else
            " No electric return handling, just enter a newline
            return "\<CR>"
        endif
    endif
endfunction

" Display arglist after pressing Enter
function! SlimvArglistOnEnter()
    let retval = ""
    if s:arglist_line > 0
        if col('.') > len(getline('.'))
            " Stay at the end of line
            let retval = "\<End>"
        endif
        let l = line('.')
        if getline(l) == ''
            " Add spaces to make the correct indentation
            call setline( l, s:MakeIndent( SlimvIndent(l) ) )
            normal! $
        endif
        call SlimvArglist( s:arglist_line, s:arglist_col )
    endif
    let s:arglist_line = 0
    let s:arglist_col = 0

    " This function is called from <C-R>= mappings, return additional keypress
    return retval
endfunction

" Handle insert mode 'Tab' keypress by doing completion or indentation
function! SlimvHandleTab()
    if pumvisible()
        " Completions menu is active, go to next match
        return "\<C-N>"
    endif
    let c = col('.')
    if c > 1 && getline('.')[c-2] =~ '\k'
        " At the end of a keyword, bring up completions
        return "\<C-X>\<C-O>"
    endif
    let indent = SlimvIndent(line('.'))
    if c-1 < indent && getline('.') !~ '\S\+'
        " We are left from the autoindent position, do an autoindent
        call setline( line('.'), s:MakeIndent( indent ) )
        return "\<End>"
    endif
    " No keyword to complete, no need for autoindent, just enter a <Tab>
    return "\<Tab>"
endfunction

" Handle insert mode 'Backspace' keypress in the REPL buffer
function! SlimvHandleBS()
    if line( "." ) == s:GetPromptLine() && col( "." ) <= b:repl_prompt_col
        if col( "." ) == b:repl_prompt_col
            return "\<BS> "
        else
            " No BS allowed before the previous EOF mark
            return ""
        endif
    else
        return "\<BS>"
    endif
endfunction

" Handle insert mode Ctrl-W keypress in the REPL buffer
function! SlimvHandleCW()
    if line( "." ) == s:GetPromptLine()
        let trim_prompt = substitute( b:repl_prompt, '\s\+$', '', 'g' )
        let promptlen = len( trim_prompt )
        if col( "." ) > promptlen
            let after_prompt = strpart( getline("."), promptlen-1, col(".")-promptlen )
        else
            let after_prompt = ''
        endif
        let word = matchstr( after_prompt, '^.*\s\S' )
        if len( word ) == 0
            " No word found after prompt, C-W not allowed
            return ""
        endif
    endif
    return "\<C-W>"
endfunction

" Recall previous command from command history
function! s:PreviousCommand()
    if exists( 'g:slimv_cmdhistory' ) && g:slimv_cmdhistorypos > 0
        call SlimvRecallHistory( -1 )
    endif
endfunction

" Recall next command from command history
function! s:NextCommand()
    if exists( 'g:slimv_cmdhistory' ) && g:slimv_cmdhistorypos < len( g:slimv_cmdhistory )
        call SlimvRecallHistory( 1 )
    else
        call SlimvSetCommandLine( "" )
    endif
endfunction

" Handle insert mode 'Up' keypress in the REPL buffer
function! SlimvHandleUp()
    let save_ve = &virtualedit
    set virtualedit=onemore
    if line( "." ) >= s:GetPromptLine()
        call s:PreviousCommand()
    else
        normal! gk
    endif
    let &virtualedit=save_ve
    return ''
endfunction

" Handle insert mode 'Down' keypress in the REPL buffer
function! SlimvHandleDown()
    let save_ve = &virtualedit
    set virtualedit=onemore
    if line( "." ) >= s:GetPromptLine()
        call s:NextCommand()
    else
        normal! gj
    endif
    let &virtualedit=save_ve
    return ''
endfunction

" Make a fold at the cursor point in the current buffer
function SlimvMakeFold()
    setlocal modifiable
    normal! o    }}}kA {{{0
    setlocal nomodifiable
endfunction

" Handle insert mode 'Enter' keypress in the REPL buffer
function! SlimvHandleEnterRepl()
    " Trim the prompt from the beginning of the command line
    " The user might have overwritten some parts of the prompt
    let lastline = s:GetPromptLine()
    let lastcol  = b:repl_prompt_col
    let cmdline = getline( lastline )
    let c = 0
    while c < lastcol - 1 && cmdline[c] == b:repl_prompt[c]
        let c = c + 1
    endwhile

    " Copy command line up to the cursor position
    if line(".") == lastline
        let cmd = [ strpart( cmdline, c, col(".") - c - 1 ) ]
    else
        let cmd = [ strpart( cmdline, c ) ]
    endif

    " Build a possible multi-line command up to the cursor line/position
    let l = lastline + 1
    while l <= line(".")
        if line(".") == l
            call add( cmd, strpart( getline( l ), 0, col(".") - 1) )
        else
            call add( cmd, strpart( getline( l ), 0) )
        endif
        let l = l + 1
    endwhile

    " Count the number of opening and closing braces in the command before the cursor
    let end = s:CloseForm( join( cmd, "\n" ) )
    if end != 'ERROR' && end != ''
        " Command part before cursor is unbalanced, insert newline
        let s:arglist_line = line('.')
        let s:arglist_col = col('.')
        if pumvisible()
            " Pressing <CR> in a pop up selects entry.
            return "\<C-Y>"
        else
            if exists( 'g:paredit_mode' ) && g:paredit_mode && g:paredit_electric_return && lastline > 0 && line( "." ) >= lastline
                " Apply electric return
                return PareditEnter()
            else
                " No electric return handling, just enter a newline
                return "\<CR>"
            endif
        endif
    else
        " Send current command line for evaluation
        if &virtualedit != 'all'
            call cursor( 0, 99999 )
        endif
        call SlimvSendCommand(0)
    endif
    return ''
endfunction

" Handle normal mode 'Enter' keypress in the SLDB buffer
function! SlimvHandleEnterSldb()
    let line = getline('.')
    if s:sldb_level >= 0
        " Check if Enter was pressed in a section printed by the SWANK debugger
        " The source specification is within a fold, so it has to be tested first
        let mlist = matchlist( line, '^\s\+in "\=\(.*\)"\= \(line\|byte\) \(\d\+\)$' )
        if len(mlist)
            if g:slimv_repl_split
                " Switch back to other window
                execute "wincmd p"
            endif
            " Jump to the file at the specified position
            if mlist[2] == 'line'
                exec ":edit +" . mlist[3] . " " . mlist[1]
            else
                exec ":edit +" . mlist[3] . "go " . mlist[1]
            endif
            return
        endif
        if foldlevel('.')
            " With a fold just toggle visibility
            normal za
            return
        endif
        let item = matchstr( line, s:frame_def )
        if item != ''
            let item = substitute( item, '\s\|:', '', 'g' )
            if search( '^Backtrace:', 'bnW' ) > 0
                " Display item-th frame
                call SlimvMakeFold()
                silent execute s:py_cmd . 'swank_frame_locals("' . item . '")'
                if SlimvGetFiletype() != 'scheme' && g:slimv_impl != 'clisp'
                    " Not implemented for CLISP or scheme
                    silent execute s:py_cmd . 'swank_frame_source_loc("' . item . '")'
                endif
                if SlimvGetFiletype() == 'lisp' && g:slimv_impl != 'clisp' && g:slimv_impl != 'allegro'
                    " Not implemented for CLISP or other lisp dialects
                    silent execute s:py_cmd . 'swank_frame_call("' . item . '")'
                endif
                call SlimvRefreshReplBuffer()
                return
            endif
            if search( '^Restarts:', 'bnW' ) > 0
                " Apply item-th restart
                call SlimvQuitSldb()
                silent execute s:py_cmd . 'swank_invoke_restart("' . s:sldb_level . '", "' . item . '")'
                call SlimvRefreshReplBuffer()
                return
            endif
        endif
    endif

    " No special treatment, perform the original function
    execute "normal! \<CR>"
endfunction

" Restore Inspector cursor position if the referenced title has already been visited
function SlimvSetInspectPos( title )
    if exists( 'b:inspect_pos' ) && has_key( b:inspect_pos, a:title )
        call winrestview( b:inspect_pos[a:title] )
    else
        normal! gg0
    endif
endfunction

" Handle normal mode 'Enter' keypress in the Inspector buffer
function! SlimvHandleEnterInspect()
    let line = getline('.')
    if line[0:9] == 'Inspecting'
        " Reload inspected item
        call SlimvSendSilent( ['[0]'] )
        return
    endif

    " Find the closest [dd] or <dd> token to the left of the cursor
    let [l, c] = searchpos( '{\[\d\+\]', 'bncW' )
    let [l2, c2] = searchpos( '{<\d\+>', 'bncW' )
    if l < line('.') || (l2 == line('.') && c2 > c)
        let l = l2
        let c = c2
    endif

    if l < line('.')
        " No preceding token found, find the closest [dd] or <dd> to the right
        let [l, c] = searchpos( '{\[\d\+\]', 'ncW' )
        let [l2, c2] = searchpos( '{<\d\+>', 'ncW' )
        if l == 0 || l > line('.') || (l2 == line('.') && c2 < c)
            let l = l2
            let c = c2
        endif
    endif

    if l == line( '.' )
        " Keep the relevant part of the line
        let line = strpart( line, c )
    endif

    if exists( 'b:inspect_title' ) && b:inspect_title != ''
        " Save cursor position in case we'll return to this page later on
        if !exists( 'b:inspect_pos' )
            let b:inspect_pos = {}
        endif
	let b:inspect_pos[b:inspect_title] = winsaveview()
    endif

    if line[0] == '['
        if line =~ '^\[--more--\]$'
            " More data follows, fetch next part
            call SlimvCommand( s:py_cmd . 'swank_inspector_range()' )
            call SlimvRefreshReplBuffer()
            return
        elseif line =~ '^\[--all---\]$'
            " More data follows, fetch all parts
            echon "\rFetching all entries, please wait..."
            let b:inspect_more = -1
            call SlimvCommand( s:py_cmd . 'swank_inspector_range()' )
            call SlimvRefreshReplBuffer()
            let starttime = localtime()
            while b:inspect_more < 0 && localtime()-starttime < g:slimv_timeout
                " Wait for the first swank_inspector_range() call to finish
                call SlimvRefreshReplBuffer()
            endwhile
            let starttime = localtime()
            while b:inspect_more > 0 && localtime()-starttime < g:slimv_timeout
                " There are more parts to fetch (1 entry is usually 4 parts)
                echon "\rFetching all entries, please wait [" . (b:inspect_more / 4) . "]"
                call SlimvCommand( s:py_cmd . 'swank_inspector_range()' )
                call SlimvRefreshReplBuffer()
                if getchar(1)
                    " User is impatient, stop fetching
                    break
                endif
            endwhile
            if b:inspect_more > 0
                echon "\rFetch exhausted. Select [--all---] to resume."
            else
                echon "\rSuccessfully fetched all entries."
            endif
            return
        elseif line[0:3] == '[<<]'
            " Pop back up in the inspector
            let item = '-1'
        else
            " Inspect n-th part
            let item = matchstr( line, '\d\+' )
            if item != ''
                " Add item name to the object path
                let entry = matchstr(line, '\[\d\+\]\s*\zs.\{-}\ze\s*\[\]}')
                if entry == ''
                    let entry = matchstr(line, '\[\d\+\]\s*\zs.*')
                endif
                if entry == ''
                    let entry = 'Unknown object'
                endif
                if len( entry ) > 40
                    " Crop if too long
                    let entry = strpart( entry, 0, 37 ) . '...'
                endif
                let s:inspect_path = s:inspect_path + [entry]
            endif
        endif
        if item != ''
            call SlimvSendSilent( ['[' . item . ']'] )
            return
        endif
    endif

    if line[0] == '<'
        " Inspector n-th action
        let item = matchstr( line, '\d\+' )
        if item != ''
            call SlimvSendSilent( ['<' . item . '>'] )
            return
        endif
    endif

    " No special treatment, perform the original function
    execute "normal! \<CR>"
endfunction

" Go to command line and recall previous command from command history
function! SlimvPreviousCommand()
    let save_ve = &virtualedit
    set virtualedit=onemore
    call SlimvEndOfReplBuffer(0)
    if line( "." ) >= s:GetPromptLine()
        call s:PreviousCommand()
    endif
    let &virtualedit=save_ve
endfunction

" Go to command line and recall next command from command history
function! SlimvNextCommand()
    let save_ve = &virtualedit
    set virtualedit=onemore
    call SlimvEndOfReplBuffer(0)
    if line( "." ) >= s:GetPromptLine()
        call s:NextCommand()
    endif
    let &virtualedit=save_ve
endfunction

" Handle interrupt (Ctrl-C) keypress in the REPL buffer
function! SlimvInterrupt()
    call SlimvCommand( s:py_cmd . 'swank_interrupt()' )
    call SlimvRefreshReplBuffer()
endfunction

" Select a specific restart in debugger
" Added in frame argument to support stepper
function! SlimvDebugCommand( name, cmd, frame )
    if SlimvConnectSwank()
        if s:sldb_level >= 0
            if bufname('%') != g:slimv_sldb_name
                call SlimvOpenSldbBuffer()
            endif
            if a:frame == ''
                call SlimvCommand( s:py_cmd . '' . a:cmd . '()' )
            else
                call SlimvCommand( s:py_cmd . '' . a:cmd . '(' . string(a:frame) . ')' )
            endif
            call SlimvRefreshReplBuffer()
            if s:sldb_level < 0
                " Swank exited the debugger
                if bufname('%') != g:slimv_sldb_name
                    call SlimvOpenSldbBuffer()
                endif
                call SlimvQuitSldb()
            else
                echomsg 'Debugger re-activated by the SWANK server.'
            endif
        else
            call SlimvError( "Debugger is not activated." )
        endif
    endif
endfunction

" Various debugger restarts
function! SlimvDebugAbort()
    call SlimvDebugCommand( ":sldb-abort", "swank_invoke_abort", '' )
endfunction

function! SlimvDebugQuit()
    call SlimvDebugCommand( ":throw-to-toplevel", "swank_throw_toplevel", '' )
endfunction

function! SlimvDebugContinue()
    let in_stepper = search ('STEP-CONTINUE', 'bcnw')
    if in_stepper == 0
        call SlimvDebugCommand( ":sldb-continue", "swank_invoke_continue", '' )
    else
        call SlimvQuitSldb()
        silent execute s:py_cmd . 'swank_invoke_restart("' . s:sldb_level . '", "' . string(0) . '")'
        call SlimvRefreshReplBuffer()
    endif
endfunction

" Debugger stepper functions
function! SlimvDebugStepInto()
    let frame = s:DebugFrame()
    if frame != ''
        call SlimvDebugCommand( ":sldb-step", "swank_step_into", frame )
    endif
endfunction

function! SlimvDebugStepNext()
    let frame = s:DebugFrame()
    if frame != ''
        call SlimvDebugCommand( ":sldb-next", "swank_step_next", frame )
    endif
endfunction

function! SlimvDebugStepOut()
    let frame = s:DebugFrame()
    if frame != ''
        call SlimvDebugCommand( ":sldb-out", "swank_step_out", frame )
    endif
endfunction

" Restart execution of the frame with the same arguments
function! SlimvDebugRestartFrame()
    let frame = s:DebugFrame()
    if frame != ''
        call SlimvCommand( s:py_cmd . 'swank_restart_frame("' . frame . '")' )
        call SlimvRefreshReplBuffer()
    endif
endfunction

" List current Lisp threads
function! SlimvListThreads()
    if SlimvConnectSwank()
        call SlimvCommand( s:py_cmd . 'swank_list_threads()' )
        call SlimvRefreshReplBuffer()
    endif
endfunction

" Kill thread(s) selected from the Thread List
function! SlimvKillThread() range
    if SlimvConnectSwank()
        if a:firstline == a:lastline
            let line = getline('.')
            let item = matchstr( line, '\d\+' )
            if bufname('%') != g:slimv_threads_name
                " We are not in the Threads buffer, not sure which thread to kill
                let item = input( 'Thread to kill: ', item )
            endif
            if item != ''
                call SlimvCommand( s:py_cmd . 'swank_kill_thread(' . item . ')' )
                call SlimvRefreshReplBuffer()
            endif
            echomsg 'Thread ' . item . ' is killed.'
        else
            for line in getline(a:firstline, a:lastline)
                let item = matchstr( line, '\d\+' )
                if item != ''
                    call SlimvCommand( s:py_cmd . 'swank_kill_thread(' . item . ')' )
                endif
            endfor
            call SlimvRefreshReplBuffer()
        endif
        call SlimvListThreads()
    endif
endfunction

" Debug thread selected from the Thread List
function! SlimvDebugThread()
    if SlimvConnectSwank()
        let line = getline('.')
        let item = matchstr( line, '\d\+' )
        let item = input( 'Thread to debug: ', item )
        if item != ''
            call SlimvCommand( s:py_cmd . 'swank_debug_thread(' . item . ')' )
            call SlimvRefreshReplBuffer()
        endif
    endif
endfunction

function! SlimvRFunction()
    " search backwards for the alphanums before a '('
    let l = line('.')
    let c = col('.') - 1
    let line = (getline('.'))[0:c]
    let list = matchlist(line, '\([a-zA-Z0-9_.]\+\)\s*(')
    if !len(list)
        return ""
    endif
    let valid = filter(reverse(list), 'v:val != ""')
    return valid[0]
endfunction

" Display function argument list
" Optional argument is the number of characters typed after the keyword
function! SlimvArglist( ... )
    let retval = ''
    let save_ve = &virtualedit
    set virtualedit=all
    if a:0
        " Symbol position supplied
        let l = a:1
        let c = a:2 - 1
        let line = getline(l)
    else
        " Check symbol at cursor position
        let l = line('.')
        let line = getline(l)
        let c = col('.') - 1
        if c >= len(line)
            " Stay at the end of line
            let c = len(line) - 1
            let retval = "\<End>"
        endif
        if line[c-1] == ' '
            " Is this the space we have just inserted in a mapping?
            let c = c - 1
        endif
    endif
    call s:SetKeyword()
    if s:swank_connected && !s:read_string_mode && c > 0 && line[c-1] =~ '\k\|)\|\]\|}\|"'
        " Display only if entering the first space after a keyword
        let arg = ''
        if SlimvGetFiletype() == 'r'
            let arg = SlimvRFunction()
        else
            let matchb = max( [l-200, 1] )
            let [l0, c0] = searchpairpos( '(', '', ')', 'nbW', s:skip_sc, matchb )
            if l0 > 0
                " Found opening paren, let's find out the function name
                while arg == '' && l0 <= l
                    let funcline = substitute( getline(l0), ';.*$', '', 'g' )
                    let arg = matchstr( funcline, '\<\k*\>', c0 )
                    let l0 = l0 + 1
                    let c0 = 0
                endwhile
            endif
        endif

        if arg != ''
            " Ask function argument list from SWANK
            call SlimvFindPackage()
            let msg = SlimvCommandGetResponse( ':operator-arglist', s:py_cmd . 'swank_op_arglist("' . arg . '")', 0 )
            if msg != ''
                " Print argument list in status line with newlines removed.
                " Disable showmode until the next ESC to prevent
                " immeditate overwriting by the "-- INSERT --" text.
                set noshowmode
                let msg = substitute( msg, "\n", "", "g" )
                redraw
                if SlimvGetFiletype() == 'r'
                    call SlimvShortEcho( arg . '(' . msg . ')' )
                elseif match( msg, "\\V" . arg ) != 1 " Use \V ('very nomagic') for exact string match instead of regex 
                    " Function name is not received from REPL
                    call SlimvShortEcho( "(" . arg . ' ' . msg[1:] )
                else
                    call SlimvShortEcho( msg )
                endif
            endif
        endif
    endif

    " This function is also called from <C-R>= mappings, return additional keypress
    let &virtualedit=save_ve
    return retval
endfunction

" Start and connect swank server
function! SlimvConnectServer()
    if s:swank_connected
        execute s:py_cmd . "swank_disconnect()"
        let s:swank_connected = 0
	" Give swank server some time for disconnecting
        sleep 500m
    endif 
    if SlimvConnectSwank()
        let repl_win = bufwinnr( s:repl_buf )
        if s:repl_buf == -1 || ( g:slimv_repl_split && repl_win == -1 )
            call SlimvOpenReplBuffer()
        endif
    endif
endfunction

" Get the last region (visual block)
function! SlimvGetRegion(first, last)
    let oldpos = winsaveview()
    if a:first < a:last || ( a:first == line( "'<" ) && a:last == line( "'>" ) )
        let lines = getline( a:first, a:last )
    else
        " No range was selected, select current paragraph
        normal! vap
        execute "normal! \<Esc>"
        call winrestview( oldpos ) 
        let lines = getline( "'<", "'>" )
        if lines == [] || lines == ['']
            call SlimvError( "No range selected." )
            return []
        endif
    endif
    let firstcol = col( "'<" ) - 1
    let lastcol  = col( "'>" ) - 2
    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 : ]

    " Find and set package/namespace definition preceding the region
    call SlimvFindPackage()
    call winrestview( oldpos ) 
    return lines
endfunction

" Eval buffer lines in the given range
function! SlimvEvalRegion() range
    if v:register == '"' || v:register == '+'
        let lines = SlimvGetRegion(a:firstline, a:lastline)
    else
        " Register was passed, so eval register contents instead
        let reg = getreg( v:register )
        let ending = ""
        if SlimvGetFiletype() != 'r'
            let ending = s:CloseForm( reg )
            if ending == 'ERROR'
                call SlimvError( 'Too many or invalid closing parens in register "' . v:register )
                return
            endif
        endif
        let lines = [reg . ending]
    endif
    if lines != []
        if SlimvGetFiletype() == 'scheme'
            " Swank-scheme requires us to pass a single s-expression
            " so embed buffer lines in a (begin ...) block
            let lines = ['(begin'] + lines + [')']
        endif
        call SlimvEval( lines )
    endif
endfunction

" Eval contents of the 's' register, optionally store it in another register
" Also optionally append a test form for quick testing (not stored in 'outreg')
" If the test form contains '%1' then it 'wraps' the selection around the '%1'
function! SlimvEvalSelection( outreg, testform )
    let sel = SlimvGetSelection()
    if a:outreg != '"' && a:outreg != '+'
        " Register was passed, so store current selection in register
        call setreg( a:outreg, s:swank_package_form . sel)
    endif
    let lines = [sel]
    if a:testform != ''
        if match( a:testform, '%1' ) >= 0
            " We need to wrap the selection in the testform
            if match( sel, "\n" ) < 0
                " The selection is a single line, keep the wrapped form in one line
                let sel = substitute( a:testform, '%1', sel, 'g' )
                let lines = [sel]
            else
                " The selection is multiple lines, wrap it by adding new lines
                let lines = [strpart( a:testform, 0, match( a:testform, '%1' ) ),
                \            sel,
                \            strpart( a:testform, matchend( a:testform, '%1' ) )]
            endif
        else
            " Append optional test form at the tail
            let lines = lines + [a:testform]
        endif
    endif
    if exists( 'b:slimv_repl_buffer' )
        " If this is the REPL buffer then go to EOF
        call s:EndOfBuffer()
    endif
    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 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 temp1 = substitute( a:template, '%1', p1, 'g' )
    let temp2 = substitute( temp1,      '%2', p2, 'g' )
    let lines = [temp2]
    call SlimvEval( lines )
endfunction


" =====================================================================
"  Special functions
" =====================================================================

" Evaluate and test top level form at the cursor pos
function! SlimvEvalTestDefun( testform )
    let outreg = v:register
    let oldpos = winsaveview()
    if !SlimvSelectDefun()
        return
    endif
    call SlimvFindPackage()
    call winrestview( oldpos ) 
    call SlimvEvalSelection( outreg, a:testform )
endfunction

" Evaluate top level form at the cursor pos
function! SlimvEvalDefun()
    call SlimvEvalTestDefun( '' )
endfunction

" Evaluate the whole buffer
function! SlimvEvalBuffer()
    if exists( 'b:slimv_repl_buffer' )
        call SlimvError( "Cannot evaluate the REPL buffer." )
        return
    endif
    let first_line = 1
    if getline( first_line )[0] == '#'
        " skip shebang line
        let first_line += 1
    endif
    let lines = getline( first_line, '$' )
    if SlimvGetFiletype() == 'scheme'
        " Swank-scheme requires us to pass a single s-expression
        " so embed buffer lines in a (begin ...) block
        let lines = ['(begin'] + lines + [')']
    endif
    call SlimvEval( lines )
endfunction

" Return frame number if we are in the Backtrace section of the debugger
function! s:DebugFrame()
    if s:swank_connected && s:sldb_level >= 0
        " Check if we are in SLDB
        let sldb_buf = bufnr( '^' . g:slimv_sldb_name . '$' )
        if sldb_buf != -1 && sldb_buf == bufnr( "%" )
            let bcktrpos = search( '^Backtrace:', 'bcnw' )
            let framepos = line( '.' )
            if matchstr( getline('.'), s:frame_def ) == ''
                let framepos = search( s:frame_def, 'bcnw' )
            endif
            if framepos > 0 && bcktrpos > 0 && framepos > bcktrpos
                let line = getline( framepos )
                let item = matchstr( line, s:frame_def )
                if item != ''
                    return substitute( item, '\s\|:', '', 'g' )
                endif
            endif
        endif
    endif
    return ''
endfunction

" Evaluate and test current s-expression at the cursor pos
function! SlimvEvalTestExp( testform )
    let outreg = v:register
    let oldpos = winsaveview()
    if !SlimvSelectForm( 1 )
        return
    endif
    call SlimvFindPackage()
    call winrestview( oldpos ) 
    call SlimvEvalSelection( outreg, a:testform )
endfunction

" Evaluate current s-expression at the cursor pos
function! SlimvEvalExp()
    call SlimvEvalTestExp( '' )
endfunction

" Evaluate expression entered interactively
function! SlimvInteractiveEval()
    let frame = s:DebugFrame()
    if frame != ''
        " We are in the debugger, eval expression in the frame the cursor stands on
        let e = input( 'Eval in frame ' . frame . ': ' )
        if e != ''
            let result = SlimvCommandGetResponse( ':eval-string-in-frame', s:py_cmd . 'swank_eval_in_frame("' . e . '", ' . frame . ')', 0 )
            if result != ''
                redraw
                echo result
            endif
        endif
    else
        let e = input( 'Eval: ' )
        if e != ''
            call SlimvEval([e])
        endif
    endif
endfunction

" Undefine function
function! SlimvUndefineFunction()
    if s:swank_connected
        call SlimvCommand( s:py_cmd . 'swank_undefine_function("' . SlimvSelectSymbol() . '")' )
        call SlimvRefreshReplBuffer()
    endif
endfunction

" ---------------------------------------------------------------------

" Macroexpand-1 the current top level form
function! SlimvMacroexpand()
    if SlimvConnectSwank()
        if !SlimvSelectForm( 0 )
            return
        endif
        let s:swank_form = SlimvGetSelection()
        if exists( 'b:slimv_repl_buffer' )
            " If this is the REPL buffer then go to EOF
            call s:EndOfBuffer()
        endif
        call SlimvCommandUsePackage( s:py_cmd . 'swank_macroexpand("s:swank_form")' )
    endif
endfunction

" Macroexpand the current top level form
function! SlimvMacroexpandAll()
    if SlimvConnectSwank()
        if !SlimvSelectForm( 0 )
            return
        endif
        let s:swank_form = SlimvGetSelection()
        if exists( 'b:slimv_repl_buffer' )
            " If this is the REPL buffer then go to EOF
            call s:EndOfBuffer()
        endif
        call SlimvCommandUsePackage( s:py_cmd . 'swank_macroexpand_all("s:swank_form")' )
    endif
endfunction

" Toggle debugger break on exceptions
" Only for ritz-swank 0.4.0 and above
function! SlimvBreakOnException()
    if SlimvGetFiletype() =~ '.*clojure.*' && s:SinceVersion( '2010-11-13' )
        " swank-clojure is abandoned at protocol version 20100404, so it must be ritz-swank
        if SlimvConnectSwank()
            let s:break_on_exception = ! s:break_on_exception
            call SlimvCommand( s:py_cmd . 'swank_break_on_exception(' . s:break_on_exception . ')' )
            call SlimvRefreshReplBuffer()
            echomsg 'Break On Exception ' . (s:break_on_exception ? 'enabled.' : 'disabled.')
        endif
    else
        call SlimvError( "This function is implemented only for ritz-swank." )
    endif
endfunction

" Set a breakpoint on the beginning of a function
function! SlimvBreak()
    if SlimvConnectSwank()
        let s = input( 'Set breakpoint: ', SlimvSelectSymbol() )
        if s != ''
            call SlimvCommandUsePackage( s:py_cmd . 'swank_set_break("' . s . '")' )
            redraw!
        endif
    endif
endfunction

" Switch trace on for the selected function (toggle for swank)
function! SlimvTrace()
    if SlimvGetFiletype() == 'scheme'
        call SlimvError( "Tracing is not supported by swank-scheme." )
        return
    endif
    if SlimvConnectSwank()
        let s = input( '(Un)trace: ', SlimvSelectSymbol() )
        if s != ''
            call SlimvCommandUsePackage( s:py_cmd . 'swank_toggle_trace("' . s . '")' )
            redraw!
        endif
    endif
endfunction

" Switch trace off for the selected function (or all functions for swank)
function! SlimvUntrace()
    if SlimvGetFiletype() == 'scheme'
        call SlimvError( "Tracing is not supported by swank-scheme." )
        return
    endif
    if SlimvConnectSwank()
        let s:refresh_disabled = 1
        call SlimvCommand( s:py_cmd . 'swank_untrace_all()' )
        let s:refresh_disabled = 0
        call SlimvRefreshReplBuffer()
    endif
endfunction

" Disassemble the selected function
function! SlimvDisassemble()
    let symbol = SlimvSelectSymbol()
    if SlimvConnectSwank()
        let s = input( 'Disassemble: ', symbol )
        if s != ''
            call SlimvCommandUsePackage( s:py_cmd . 'swank_disassemble("' . s . '")' )
        endif
    endif
endfunction

" Inspect symbol under cursor
function! SlimvInspect()
    if !SlimvConnectSwank()
        return
    endif
    let s:inspect_path = []
    let frame = s:DebugFrame()
    if frame != ''
        " Inspect selected for a frame in the debugger's Backtrace section
        let line = getline( '.' )
        if matchstr( line, s:frame_def ) != ''
            " This is the base frame line in form '  1: xxxxx'
            let sym = ''
        elseif matchstr( line, '^\s\+in "\(.*\)" \(line\|byte\)' ) != ''
            " This is the source location line
            let sym = ''
        elseif matchstr( line, '^\s\+No source line information' ) != ''
            " This is the no source location line
            let sym = ''
        elseif matchstr( line, '^\s\+Locals:' ) != ''
            " This is the 'Locals' line
            let sym = ''
        else
            let sym = SlimvSelectSymbolExt()
        endif
        let s = input( 'Inspect in frame ' . frame . ' (evaluated): ', sym )
        if s != ''
            let s:inspect_path = [s]
            call SlimvCommand( s:py_cmd . 'swank_inspect_in_frame("' . s . '", ' . frame . ')' )
            call SlimvRefreshReplBuffer()
        endif
    else
        let s = input( 'Inspect: ', SlimvSelectSymbolExt() )
        if s != ''
            let s:inspect_path = [s]
            call SlimvCommandUsePackage( s:py_cmd . 'swank_inspect("' . s . '")' )
        endif
    endif
endfunction

" Cross reference: who calls
function! SlimvXrefBase( text, cmd )
    if SlimvConnectSwank()
        let s = input( a:text, SlimvSelectSymbol() )
        if s != ''
            call SlimvCommandUsePackage( s:py_cmd . 'swank_xref("' . s . '", "' . a:cmd . '")' )
        endif
    endif
endfunction

" Cross reference: who calls
function! SlimvXrefCalls()
    call SlimvXrefBase( 'Who calls: ', ':calls' )
endfunction

" Cross reference: who references
function! SlimvXrefReferences()
    call SlimvXrefBase( 'Who references: ', ':references' )
endfunction

" Cross reference: who sets
function! SlimvXrefSets()
    call SlimvXrefBase( 'Who sets: ', ':sets' )
endfunction

" Cross reference: who binds
function! SlimvXrefBinds()
    call SlimvXrefBase( 'Who binds: ', ':binds' )
endfunction

" Cross reference: who macroexpands
function! SlimvXrefMacroexpands()
    call SlimvXrefBase( 'Who macroexpands: ', ':macroexpands' )
endfunction

" Cross reference: who specializes
function! SlimvXrefSpecializes()
    call SlimvXrefBase( 'Who specializes: ', ':specializes' )
endfunction

" Cross reference: list callers
function! SlimvXrefCallers()
    call SlimvXrefBase( 'List callers: ', ':callers' )
endfunction

" Cross reference: list callees
function! SlimvXrefCallees()
    call SlimvXrefBase( 'List callees: ', ':callees' )
endfunction

" ---------------------------------------------------------------------

" Switch or toggle profiling on for the selected function
function! SlimvProfile()
    if SlimvConnectSwank()
        let s = input( '(Un)profile: ', SlimvSelectSymbol() )
        if s != ''
            call SlimvCommandUsePackage( s:py_cmd . 'swank_toggle_profile("' . s . '")' )
            redraw!
        endif
    endif
endfunction

" Switch profiling on based on substring
function! SlimvProfileSubstring()
    if SlimvConnectSwank()
        let s = input( 'Profile by matching substring: ', SlimvSelectSymbol() )
        if s != ''
            let p = input( 'Package (RET for all packages): ' )
            call SlimvCommandUsePackage( s:py_cmd . 'swank_profile_substring("' . s . '","' . p . '")' )
            redraw!
        endif
    endif
endfunction

" Switch profiling completely off
function! SlimvUnprofileAll()
    if SlimvConnectSwank()
        call SlimvCommandUsePackage( s:py_cmd . 'swank_unprofile_all()' )
    endif
endfunction

" Display list of profiled functions
function! SlimvShowProfiled()
    if SlimvConnectSwank()
        call SlimvCommandUsePackage( s:py_cmd . 'swank_profiled_functions()' )
    endif
endfunction

" Report profiling results
function! SlimvProfileReport()
    if SlimvConnectSwank()
        call SlimvCommandUsePackage( s:py_cmd . 'swank_profile_report()' )
    endif
endfunction

" Reset profiling counters
function! SlimvProfileReset()
    if SlimvConnectSwank()
        call SlimvCommandUsePackage( s:py_cmd . 'swank_profile_reset()' )
    endif
endfunction

" ---------------------------------------------------------------------

" Compile the current top-level form
function! SlimvCompileDefun()
    let oldpos = winsaveview()
    if !SlimvSelectDefun()
        call winrestview( oldpos ) 
        return
    endif
    if SlimvConnectSwank()
        let s:swank_form = SlimvGetSelection()
        call SlimvCommandUsePackage( s:py_cmd . 'swank_compile_string("s:swank_form")' )
    endif
    call winrestview( oldpos )
endfunction

" Compile and load whole file
function! SlimvCompileLoadFile()
    if exists( 'b:slimv_repl_buffer' )
        call SlimvError( "Cannot compile the REPL buffer." )
        return
    endif
    let filename = fnamemodify( bufname(''), ':p' )
    let filename = substitute( filename, '\\', '/', 'g' )
    if &modified
        let answer = SlimvErrorAsk( '', "Save file before compiling [Y/n]?" )
        if answer[0] != 'n' && answer[0] != 'N'
            write
        endif
    endif
    if SlimvConnectSwank()
        let s:compiled_file = ''
        call SlimvCommandUsePackage( s:py_cmd . 'swank_compile_file("' . filename . '")' )
        let starttime = localtime()
        while s:compiled_file == '' && localtime()-starttime < g:slimv_timeout
            call SlimvSwankResponse()
        endwhile
        if s:compiled_file != ''
            let s:compiled_file = substitute( s:compiled_file, '\\', '/', 'g' )
            call SlimvCommandUsePackage( s:py_cmd . 'swank_load_file("' . s:compiled_file . '")' )
            let s:compiled_file = ''
        endif
    endif
endfunction

" Compile whole file
function! SlimvCompileFile()
    if exists( 'b:slimv_repl_buffer' )
        call SlimvError( "Cannot compile the REPL buffer." )
        return
    endif
    let filename = fnamemodify( bufname(''), ':p' )
    let filename = substitute( filename, '\\', '/', 'g' )
    if &modified
        let answer = SlimvErrorAsk( '', "Save file before compiling [Y/n]?" )
        if answer[0] != 'n' && answer[0] != 'N'
            write
        endif
    endif
    if SlimvConnectSwank()
        call SlimvCommandUsePackage( s:py_cmd . 'swank_compile_file("' . filename . '")' )
    endif
endfunction

" Compile buffer lines in the given range
function! SlimvCompileRegion() range
    if v:register == '"' || v:register == '+'
        let lines = SlimvGetRegion(a:firstline, a:lastline)
    else
        " Register was passed, so compile register contents instead
        let reg = getreg( v:register )
        let ending = s:CloseForm( reg )
        if ending == 'ERROR'
            call SlimvError( 'Too many or invalid closing parens in register "' . v:register )
            return
        endif
        let lines = [reg . ending]
    endif
    if lines == []
        return
    endif
    let region = join( lines, "\n" )
    if SlimvConnectSwank()
        let s:swank_form = region
        call SlimvCommandUsePackage( s:py_cmd . 'swank_compile_string("s:swank_form")' )
    endif
endfunction

" ---------------------------------------------------------------------

" Describe the selected symbol
function! SlimvDescribeSymbol()
    if SlimvConnectSwank()
        let symbol = SlimvSelectSymbol()
        if symbol == ''
            call SlimvError( "No symbol under cursor." )
            return
        endif
        call SlimvCommandUsePackage( s:py_cmd . 'swank_describe_symbol("' . symbol . '")' )
    endif
endfunction

" Display symbol description in balloonexpr
function! SlimvDescribe(arg)
    let arg=a:arg
    if a:arg == ''
        let arg = expand('<cword>')
    endif
    " We don't want to try connecting here ... the error message would just 
    " confuse the balloon logic
    if !s:swank_connected || s:read_string_mode
        return ''
    endif
    call SlimvFindPackage()
    let arglist = SlimvCommandGetResponse( ':operator-arglist', s:py_cmd . 'swank_op_arglist("' . arg . '")', 0 )
    if arglist == ''
        " Not able to fetch arglist, assuming function is not defined
        " Skip calling describe, otherwise SWANK goes into the debugger
        return ''
    endif
    let msg = SlimvCommandGetResponse( ':describe-function', s:py_cmd . 'swank_describe_function("' . arg . '")', 0 )
    if msg == ''
        " No describe info, display arglist
        if match( arglist, arg ) != 1
            " Function name is not received from REPL
            return "(" . arg . ' ' . arglist[1:]
        else
            return arglist
        endif
    else
        return substitute(msg,'^\n*','','')
    endif
endfunction

" Apropos of the selected symbol
function! SlimvApropos()
    call SlimvEvalForm1( g:slimv_template_apropos, SlimvSelectSymbol() )
endfunction

" Generate tags file using ctags
function! SlimvGenerateTags()
    if exists( 'g:slimv_ctags' ) && g:slimv_ctags != ''
        execute 'silent !' . g:slimv_ctags
    else
        call SlimvError( "Copy ctags to the Vim path or define g:slimv_ctags." )
    endif
endfunction

" ---------------------------------------------------------------------

" Find word in the CLHS symbol database, with exact or partial match.
" Return either the first symbol found with the associated URL,
" or the list of all symbols found without the associated URL.
function! SlimvFindSymbol( word, exact, all, db, root, init )
    if a:word == ''
        return []
    endif
    if !a:all && a:init != []
        " Found something already at a previous db lookup, no need to search this db
        return a:init
    endif
    let lst = a:init
    let i = 0
    let w = tolower( a:word )
    if a:exact
        while i < len( a:db )
            " Try to find an exact match
            if a:db[i][0] == w
                " No reason to check a:all here
                return [a:db[i][0], a:root . a:db[i][1]]
            endif
            let i = i + 1
        endwhile
    else
        while i < len( a:db )
            " Try to find the symbol starting with the given word
            let w2 = escape( w, '~' )
            if match( a:db[i][0], w2 ) == 0
                if a:all
                    call add( lst, a:db[i][0] )
                else
                    return [a:db[i][0], a:root . a:db[i][1]]
                endif
            endif
            let i = i + 1
        endwhile
    endif

    " Return whatever found so far
    return lst
endfunction

" Lookup word in Common Lisp Hyperspec
function! SlimvLookup( word )
    " First try an exact match
    let w = a:word
    let symbol = []
    while symbol == []
        let symbol = SlimvHyperspecLookup( w, 1, 0 )
        if symbol == []
            " Symbol not found, try a match on beginning of symbol name
            let symbol = SlimvHyperspecLookup( w, 0, 0 )
            if symbol == []
                " We are out of luck, can't find anything
                let msg = 'Symbol ' . w . ' not found. Hyperspec lookup word: '
                let val = ''
            else
                let msg = 'Hyperspec lookup word: '
                let val = symbol[0]
            endif
            " Ask user if this is that he/she meant
            let w = input( msg, val )
            if w == ''
                " OK, user does not want to continue
                return
            endif
            let symbol = []
        endif
    endwhile
    if symbol != [] && len(symbol) > 1
        " Symbol found, open HS page in browser
        if match( symbol[1], ':' ) < 0 && exists( 'g:slimv_hs_root' )
            let page = g:slimv_hs_root . symbol[1]
        else
            " URL is already a fully qualified address
            let page = symbol[1]
        endif
        if exists( "g:slimv_browser_cmd" )
            " We have a given shell command to start the browser
            if !exists( "g:slimv_browser_cmd_suffix" )
                " Fork the browser by default
                let g:slimv_browser_cmd_suffix = '&'
            endif
            silent execute '! ' . g:slimv_browser_cmd . ' ' . page . ' ' . g:slimv_browser_cmd_suffix
        elseif exists( "g:slimv_browser_cmd_ex" )
            " We have a given Ex command to start the browser
            silent execute g:slimv_browser_cmd_ex . ' ' . page
        else
            if g:slimv_windows
                " Run the program associated with the .html extension
                silent execute '! start ' . page
            else
                " On Linux it's not easy to determine the default browser
                if executable( 'xdg-open' )
                    silent execute '! xdg-open ' . page . ' &'
                else
                    " xdg-open not installed, ask help from Python webbrowser package
                    let pycmd = "import webbrowser; webbrowser.open('" . page . "')"
                    silent execute '! python -c "' . pycmd . '"'
                endif
            endif
        endif
        " This is needed especially when using text browsers
        redraw!
    endif
endfunction

" Lookup current symbol in the Common Lisp Hyperspec
function! SlimvHyperspec()
    call SlimvLookup( SlimvSelectSymbol() )
endfunction

" Complete symbol name starting with 'base'
function! SlimvComplete( base )
    " Find all symbols starting with "a:base"
    if a:base == ''
        return []
    endif
    if s:swank_connected && !s:read_string_mode
        " Save current buffer and window in case a swank command causes a buffer change
        let buf = bufnr( "%" )
        if winnr('$') < 2
            let win = 0
        else
            let win = winnr()
        endif

        call SlimvFindPackage()
        if g:slimv_simple_compl
            let msg = SlimvCommandGetResponse( ':simple-completions', s:py_cmd . 'swank_completions("' . a:base . '")', 0 )
        else
            let msg = SlimvCommandGetResponse( ':fuzzy-completions', s:py_cmd . 'swank_fuzzy_completions("' . a:base . '")', 0 )
        endif

        " Restore window and buffer, because it is not allowed to change buffer here
        if win > 0 && winnr() != win
            execute win . "wincmd w"
            let msg = ''
        endif
        if bufnr( "%" ) != buf
            execute "buf " . buf
            let msg = ''
        endif

        if msg != ''
            " We have a completion list from SWANK
            let res = split( msg, '\n' )
            return res
        endif
    endif

    " No completion yet, try to fetch it from the Hyperspec database
    let res = []
    let symbol = SlimvHyperspecLookup( a:base, 0, 1 )
    if symbol == []
        return []
    endif
    call sort( symbol )
    for m in symbol
        if m =~ '^' . escape( a:base, '~' )
            call add( res, m )
        endif
    endfor
    return res
endfunction

" Complete function that uses the Hyperspec database
function! SlimvOmniComplete( findstart, base )
    if a:findstart
        " Locate the start of the symbol name
        call s:SetKeyword()
        let upto = strpart( getline( '.' ), 0, col( '.' ) - 1)
        return match(upto, '\k\+$')
    else
        return SlimvComplete( a:base )
    endif
endfunction

" Define complete function only if none is defined yet
if &omnifunc == ''
    set omnifunc=SlimvOmniComplete
endif

" Complete function for user-defined commands
function! SlimvCommandComplete( arglead, cmdline, cursorpos )
    " Locate the start of the symbol name
    call s:SetKeyword()
    let upto = strpart( a:cmdline, 0, a:cursorpos )
    let base = matchstr(upto, '\k\+$')
    let ext  = matchstr(upto, '\S*\k\+$')
    let compl = SlimvComplete( base )
    if len(compl) > 0 && base != ext
        " Command completion replaces whole word between spaces, so we
        " need to add any prefix present in front of the keyword, like '('
        let prefix = strpart( ext, 0, len(ext) - len(base) )
        let i = 0
        while i < len(compl)
            let compl[i] = prefix . compl[i]
            let i = i + 1
        endwhile
    endif
    return compl
endfunction

" Create a tags file containing the definitions
" of the given symbol, then perform a tag lookup
function! SlimvFindDefinitionsForEmacs( symbol )
    if g:slimv_tags_file == ''
        let msg = ''
    else
        let msg = SlimvCommandGetResponse( ':find-definitions-for-emacs', s:py_cmd . 'swank_find_definitions_for_emacs("' . a:symbol . '")', 0 )
    endif
    try
        if msg != ''
            exec ":tjump " . msg
        else
            exec ":tjump " . a:symbol
        endif
    catch
        call SlimvError( "\r" . v:exception )
    endtry
endfunction

" Lookup definition(s) of the symbol under cursor
function! SlimvFindDefinitions()
    if SlimvConnectSwank()
        let symbol = SlimvSelectSymbol()
        if symbol == ''
            call SlimvError( "No symbol under cursor." )
            return
        endif
        call SlimvFindPackage()
        call SlimvFindDefinitionsForEmacs( symbol )
    endif
endfunction

" Lookup definition(s) of symbol entered in prompt
function! SlimvFindDefinitionsPrompt()
    if SlimvConnectSwank()
        let symbol = input( 'Find Definitions For: ', SlimvSelectSymbol() )
        echon "\r"
        call SlimvFindDefinitionsForEmacs( symbol )
    endif
endfunction

" Set current package
function! SlimvSetPackage()
    if SlimvConnectSwank()
        call SlimvFindPackage()
        let pkg = input( 'Package: ', s:swank_package )
        if pkg != ''
            let s:refresh_disabled = 1
            call SlimvCommand( s:py_cmd . 'swank_set_package("' . pkg . '")' )
            let s:refresh_disabled = 0
            call SlimvRefreshReplBuffer()
        endif
    endif
endfunction

" Close lisp process running the swank server
" and quit REPL buffer
function! SlimvQuitRepl()
    if s:swank_connected
        call SlimvCommand( s:py_cmd . 'swank_quit_lisp()' )
        let s:swank_connected = 0
        let buf = bufnr( '^' . g:slimv_repl_name . '$' )
        if buf != -1
            if g:slimv_repl_split
                " REPL buffer exists, check if it is open in a window
                let win = bufwinnr( buf )
                if win != -1
                    " Switch to the REPL window and close it
                    if winnr() != win
                        execute win . "wincmd w"
                    endif
                    execute "wincmd c"
                endif
            endif
            execute "bd " . buf
        endif
    endif
endfunction

" =====================================================================
"  Slimv keybindings
" =====================================================================

" <Leader> timeouts in 1000 msec by default, if this is too short,
" then increase 'timeoutlen'

" Map keyboard keyset dependant shortcut to command and also add it to menu
function! s:MenuMap( name, shortcut1, shortcut2, command )
    if g:slimv_keybindings == 1
        " Short (one-key) keybinding set
        let shortcut = a:shortcut1
    elseif g:slimv_keybindings == 2
        " Easy to remember (two-key) keybinding set
        let shortcut = a:shortcut2
    else
        " No bindings
        let shortcut = ''
    endif

    if shortcut != ''
        execute "noremap <silent> " . shortcut . " " . a:command
        if a:name != '' && g:slimv_menu == 1
            silent execute "amenu " . a:name . "<Tab>" . shortcut . " " . a:command
        endif
    elseif a:name != '' && g:slimv_menu == 1
        silent execute "amenu " . a:name . " " . a:command
    endif
endfunction

" Initialize buffer by adding buffer specific mappings
function! SlimvInitBuffer()
    " Map space to display function argument list in status line
    if SlimvGetFiletype() == 'r'
        inoremap <silent> <buffer> (          (<C-R>=SlimvArglist()<CR>
    else
        if !exists("g:slimv_unmap_space") || g:slimv_unmap_space == 0
            inoremap <silent> <buffer> <Space>    <Space><C-R>=SlimvArglist()<CR>
        endif
        if !exists("g:slimv_unmap_cr") || g:slimv_unmap_cr == 0
            inoremap <silent> <buffer> <CR>       <C-R>=pumvisible() ?  "\<lt>C-Y>" : SlimvHandleEnter()<CR><C-R>=SlimvArglistOnEnter()<CR>
        endif
    endif
    nnoremap <silent> <buffer> %          :call SlimvFindMatchingPair()<CR>
    "noremap  <silent> <buffer> <C-C>      :call SlimvInterrupt()<CR>
    augroup SlimvInsertLeave
        au!
        au InsertEnter * :let s:save_showmode=&showmode
        au InsertLeave * :let &showmode=s:save_showmode
    augroup END
    inoremap <silent> <buffer> <C-X>0     <C-O>:call SlimvCloseForm()<CR>
    if !exists("g:slimv_unmap_tab") || g:slimv_unmap_tab == 0
        inoremap <silent> <buffer> <Tab>      <C-R>=SlimvHandleTab()<CR>
    endif
    inoremap <silent> <buffer> <S-Tab>    <C-R>=pumvisible() ? "\<lt>C-P>" : "\<lt>S-Tab>"<CR>
    if g:slimv_tags_file != ''
        nnoremap <silent> <buffer> <C-]>      :call SlimvFindDefinitions()<CR>
    endif

    " Setup balloonexp to display symbol description
    if g:slimv_balloon && has( 'balloon_eval' )
        "setlocal balloondelay=100
        setlocal ballooneval
        setlocal balloonexpr=SlimvDescribe(v:beval_text)
    endif
    " This is needed for safe switching of modified buffers
    set hidden
    call s:MakeWindowId()
endfunction

" Edit commands
call s:MenuMap( 'Slim&v.Edi&t.Close-&Form',                     g:slimv_leader.')',  g:slimv_leader.'tc',  ':<C-U>call SlimvCloseForm()<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.&Complete-Symbol<Tab>Tab',        '',                  '',                   '<Ins><C-X><C-O>' )
call s:MenuMap( 'Slim&v.Edi&t.Find-&Definitions\.\.\.',         g:slimv_leader.'j',  g:slimv_leader.'fd', ':call SlimvFindDefinitionsPrompt()<CR>' )

if exists( 'g:paredit_loaded' )
call s:MenuMap( 'Slim&v.Edi&t.&Paredit-Toggle',                 g:slimv_leader.'(',  g:slimv_leader.'(t',  ':<C-U>call PareditToggle()<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.-PareditSep-',                    '',                  '',                   ':' )

if g:paredit_shortmaps
call s:MenuMap( 'Slim&v.Edi&t.Paredit-&Wrap<Tab>'                             .'W',  '',  '',              ':<C-U>call PareditWrap("(",")")<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.Paredit-Spli&ce<Tab>'                           .'S',  '',  '',              ':<C-U>call PareditSplice()<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.Paredit-&Split<Tab>'                            .'O',  '',  '',              ':<C-U>call PareditSplit()<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.Paredit-&Join<Tab>'                             .'J',  '',  '',              ':<C-U>call PareditJoin()<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.Paredit-Ra&ise<Tab>'             .g:slimv_leader.'I',  '',  '',              ':<C-U>call PareditRaise()<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.Paredit-Move&Left<Tab>'                         .'<',  '',  '',              ':<C-U>call PareditMoveLeft()<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.Paredit-Move&Right<Tab>'                        .'>',  '',  '',              ':<C-U>call PareditMoveRight()<CR>' )
else
call s:MenuMap( 'Slim&v.Edi&t.Paredit-&Wrap<Tab>'              .g:slimv_leader.'W',  '',  '',              ':<C-U>call PareditWrap("(",")")<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.Paredit-Spli&ce<Tab>'            .g:slimv_leader.'S',  '',  '',              ':<C-U>call PareditSplice()<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.Paredit-&Split<Tab>'             .g:slimv_leader.'O',  '',  '',              ':<C-U>call PareditSplit()<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.Paredit-&Join<Tab>'              .g:slimv_leader.'J',  '',  '',              ':<C-U>call PareditJoin()<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.Paredit-Ra&ise<Tab>'             .g:slimv_leader.'I',  '',  '',              ':<C-U>call PareditRaise()<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.Paredit-Move&Left<Tab>'          .g:slimv_leader.'<',  '',  '',              ':<C-U>call PareditMoveLeft()<CR>' )
call s:MenuMap( 'Slim&v.Edi&t.Paredit-Move&Right<Tab>'         .g:slimv_leader.'>',  '',  '',              ':<C-U>call PareditMoveRight()<CR>' )
endif "g:paredit_shortmaps
endif "g:paredit_loaded

" Evaluation commands
call s:MenuMap( 'Slim&v.&Evaluation.Eval-&Defun',               g:slimv_leader.'d',  g:slimv_leader.'ed',  ':<C-U>call SlimvEvalDefun()<CR>' )
call s:MenuMap( 'Slim&v.&Evaluation.Eval-Current-&Exp',         g:slimv_leader.'e',  g:slimv_leader.'ee',  ':<C-U>call SlimvEvalExp()<CR>' )
call s:MenuMap( 'Slim&v.&Evaluation.Eval-&Region',              g:slimv_leader.'r',  g:slimv_leader.'er',  ':call SlimvEvalRegion()<CR>' )
call s:MenuMap( 'Slim&v.&Evaluation.Eval-&Buffer',              g:slimv_leader.'b',  g:slimv_leader.'eb',  ':<C-U>call SlimvEvalBuffer()<CR>' )
call s:MenuMap( 'Slim&v.&Evaluation.Interacti&ve-Eval\.\.\.',   g:slimv_leader.'v',  g:slimv_leader.'ei',  ':call SlimvInteractiveEval()<CR>' )
call s:MenuMap( 'Slim&v.&Evaluation.&Undefine-Function',        g:slimv_leader.'u',  g:slimv_leader.'eu',  ':call SlimvUndefineFunction()<CR>' )

" Debug commands
call s:MenuMap( 'Slim&v.De&bugging.Macroexpand-&1',             g:slimv_leader.'1',  g:slimv_leader.'m1',  ':<C-U>call SlimvMacroexpand()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.&Macroexpand-All',           g:slimv_leader.'m',  g:slimv_leader.'ma',  ':<C-U>call SlimvMacroexpandAll()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.Toggle-&Trace\.\.\.',        g:slimv_leader.'t',  g:slimv_leader.'dt',  ':call SlimvTrace()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.U&ntrace-All',               g:slimv_leader.'T',  g:slimv_leader.'du',  ':call SlimvUntrace()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.Set-&Breakpoint',            g:slimv_leader.'B',  g:slimv_leader.'db',  ':call SlimvBreak()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.Break-on-&Exception',        g:slimv_leader.'E',  g:slimv_leader.'de',  ':call SlimvBreakOnException()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.Disassemb&le\.\.\.',         g:slimv_leader.'l',  g:slimv_leader.'dd',  ':call SlimvDisassemble()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.&Inspect\.\.\.',             g:slimv_leader.'i',  g:slimv_leader.'di',  ':call SlimvInspect()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.-SldbSep-',                  '',                  '',                   ':' )
call s:MenuMap( 'Slim&v.De&bugging.&Abort',                     g:slimv_leader.'a',  g:slimv_leader.'da',  ':call SlimvDebugAbort()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.&Quit-to-Toplevel',          g:slimv_leader.'q',  g:slimv_leader.'dq',  ':call SlimvDebugQuit()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.&Continue',                  g:slimv_leader.'n',  g:slimv_leader.'dc',  ':call SlimvDebugContinue()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.&Restart-Frame',             g:slimv_leader.'N',  g:slimv_leader.'dr',  ':call SlimvDebugRestartFrame()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.&Step-Into',                 g:slimv_leader.'si',  g:slimv_leader.'si',  ':call SlimvDebugStepInto()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.&Step-Next',                 g:slimv_leader.'sn',  g:slimv_leader.'sn',  ':call SlimvDebugStepNext()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.&Step-Out',                  g:slimv_leader.'so',  g:slimv_leader.'so',  ':call SlimvDebugStepOut()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.-ThreadSep-',                '',                  '',                   ':' )
call s:MenuMap( 'Slim&v.De&bugging.List-T&hreads',              g:slimv_leader.'H',  g:slimv_leader.'dl',  ':call SlimvListThreads()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.&Kill-Thread\.\.\.',         g:slimv_leader.'K',  g:slimv_leader.'dk',  ':call SlimvKillThread()<CR>' )
call s:MenuMap( 'Slim&v.De&bugging.&Debug-Thread\.\.\.',        g:slimv_leader.'G',  g:slimv_leader.'dT',  ':call SlimvDebugThread()<CR>' )

" Compile commands
call s:MenuMap( 'Slim&v.&Compilation.Compile-&Defun',           g:slimv_leader.'D',  g:slimv_leader.'cd',  ':<C-U>call SlimvCompileDefun()<CR>' )
call s:MenuMap( 'Slim&v.&Compilation.Compile-&Load-File',       g:slimv_leader.'L',  g:slimv_leader.'cl',  ':<C-U>call SlimvCompileLoadFile()<CR>' )
call s:MenuMap( 'Slim&v.&Compilation.Compile-&File',            g:slimv_leader.'F',  g:slimv_leader.'cf',  ':<C-U>call SlimvCompileFile()<CR>' )
call s:MenuMap( 'Slim&v.&Compilation.Compile-&Region',          g:slimv_leader.'R',  g:slimv_leader.'cr',  ':call SlimvCompileRegion()<CR>' )

" Xref commands
call s:MenuMap( 'Slim&v.&Xref.Who-&Calls',                      g:slimv_leader.'xc', g:slimv_leader.'xc',  ':call SlimvXrefCalls()<CR>' )
call s:MenuMap( 'Slim&v.&Xref.Who-&References',                 g:slimv_leader.'xr', g:slimv_leader.'xr',  ':call SlimvXrefReferences()<CR>' )
call s:MenuMap( 'Slim&v.&Xref.Who-&Sets',                       g:slimv_leader.'xs', g:slimv_leader.'xs',  ':call SlimvXrefSets()<CR>' )
call s:MenuMap( 'Slim&v.&Xref.Who-&Binds',                      g:slimv_leader.'xb', g:slimv_leader.'xb',  ':call SlimvXrefBinds()<CR>' )
call s:MenuMap( 'Slim&v.&Xref.Who-&Macroexpands',               g:slimv_leader.'xm', g:slimv_leader.'xm',  ':call SlimvXrefMacroexpands()<CR>' )
call s:MenuMap( 'Slim&v.&Xref.Who-S&pecializes',                g:slimv_leader.'xp', g:slimv_leader.'xp',  ':call SlimvXrefSpecializes()<CR>' )
call s:MenuMap( 'Slim&v.&Xref.&List-Callers',                   g:slimv_leader.'xl', g:slimv_leader.'xl',  ':call SlimvXrefCallers()<CR>' )
call s:MenuMap( 'Slim&v.&Xref.List-Call&ees',                   g:slimv_leader.'xe', g:slimv_leader.'xe',  ':call SlimvXrefCallees()<CR>' )

" Profile commands
call s:MenuMap( 'Slim&v.&Profiling.Toggle-&Profile\.\.\.',      g:slimv_leader.'p',  g:slimv_leader.'pp',  ':<C-U>call SlimvProfile()<CR>' )
call s:MenuMap( 'Slim&v.&Profiling.Profile-&By-Substring\.\.\.',g:slimv_leader.'P',  g:slimv_leader.'pb',  ':<C-U>call SlimvProfileSubstring()<CR>' )
call s:MenuMap( 'Slim&v.&Profiling.Unprofile-&All',             g:slimv_leader.'U',  g:slimv_leader.'pa',  ':<C-U>call SlimvUnprofileAll()<CR>' )
call s:MenuMap( 'Slim&v.&Profiling.&Show-Profiled',             g:slimv_leader.'?',  g:slimv_leader.'ps',  ':<C-U>call SlimvShowProfiled()<CR>' )
call s:MenuMap( 'Slim&v.&Profiling.-ProfilingSep-',             '',                  '',                   ':' )
call s:MenuMap( 'Slim&v.&Profiling.Profile-Rep&ort',            g:slimv_leader.'o',  g:slimv_leader.'pr',  ':<C-U>call SlimvProfileReport()<CR>' )
call s:MenuMap( 'Slim&v.&Profiling.Profile-&Reset',             g:slimv_leader.'X',  g:slimv_leader.'px',  ':<C-U>call SlimvProfileReset()<CR>' )

" Documentation commands
call s:MenuMap( 'Slim&v.&Documentation.Describe-&Symbol',       g:slimv_leader.'s',  g:slimv_leader.'ds',  ':call SlimvDescribeSymbol()<CR>' )
call s:MenuMap( 'Slim&v.&Documentation.&Apropos',               g:slimv_leader.'A',  g:slimv_leader.'dp',  ':call SlimvApropos()<CR>' )
call s:MenuMap( 'Slim&v.&Documentation.&Hyperspec',             g:slimv_leader.'h',  g:slimv_leader.'dh',  ':call SlimvHyperspec()<CR>' )
call s:MenuMap( 'Slim&v.&Documentation.Generate-&Tags',         g:slimv_leader.']',  g:slimv_leader.'dg',  ':call SlimvGenerateTags()<CR>' )

" REPL commands
call s:MenuMap( 'Slim&v.&Repl.&Connect-Server',                 g:slimv_leader.'c',  g:slimv_leader.'rc',  ':call SlimvConnectServer()<CR>' )
call s:MenuMap( '',                                             g:slimv_leader.'g',  g:slimv_leader.'rp',  ':call SlimvSetPackage()<CR>' )
call s:MenuMap( 'Slim&v.&Repl.Interrup&t-Lisp-Process',         g:slimv_leader.'y',  g:slimv_leader.'ri',  ':call SlimvInterrupt()<CR>' )
call s:MenuMap( 'Slim&v.&Repl.Clear-&REPL',                     g:slimv_leader.'-',  g:slimv_leader.'-',   ':call SlimvClearReplBuffer()<CR>' )
call s:MenuMap( 'Slim&v.&Repl.&Quit-REPL',                      g:slimv_leader.'Q',  g:slimv_leader.'rq',  ':call SlimvQuitRepl()<CR>' )


" =====================================================================
"  Slimv menu
" =====================================================================

if g:slimv_menu == 1
    " Works only if 'wildcharm' is <Tab>
    if &wildcharm == 0
        set wildcharm=<Tab>
    endif
    if &wildcharm != 0
        execute ':map ' . g:slimv_leader.', :emenu Slimv.' . nr2char( &wildcharm )
    endif
endif

" Add REPL menu. This menu exist only for the REPL buffer.
function! SlimvAddReplMenu()
    if &wildcharm != 0
        execute ':map ' . g:slimv_leader.'\ :emenu REPL.' . nr2char( &wildcharm )
    endif

    amenu &REPL.Send-&Input                            :call SlimvSendCommand(0)<CR>
    amenu &REPL.Cl&ose-Send-Input                      :call SlimvSendCommand(1)<CR>
    amenu &REPL.Set-Packa&ge                           :call SlimvSetPackage()<CR>
    amenu &REPL.Interrup&t-Lisp-Process                <Esc>:<C-U>call SlimvInterrupt()<CR>
    amenu &REPL.-REPLSep-                              :
    amenu &REPL.&Previous-Input                        :call SlimvPreviousCommand()<CR>
    amenu &REPL.&Next-Input                            :call SlimvNextCommand()<CR>
    amenu &REPL.Clear-&REPL                            :call SlimvClearReplBuffer()<CR>
endfunction

" =====================================================================
"  Slimv commands
" =====================================================================

command! -complete=customlist,SlimvCommandComplete -nargs=* Lisp call SlimvEval([<q-args>])
command! -complete=customlist,SlimvCommandComplete -nargs=* Eval call SlimvEval([<q-args>])

" Switch on syntax highlighting
if !exists("g:syntax_on")
    syntax on
endif