Commits

Tamas Kovacs committed 1d0faae

Version 0.6.1

Added Split/Join/Wrap/Splice functions to Paredit Mode. Bugfixes: delete commands put erased characters into yank buffer, D does not delete whole line.

Comments (0)

Files changed (4)

-*slimv.txt*                    Slimv                 Last Change: 14 Apr 2010
+*slimv.txt*                    Slimv                 Last Change: 27 Apr 2010
 
 Slimv                                                                  *slimv*
-                               Version 0.6.0
+                               Version 0.6.1
 
 The Superior Lisp Interaction Mode for Vim.
 This plugin is aimed to help Lisp development by interfacing between Vim and
 
 |g:paredit_mode|             If nonzero, paredit mode is switched on.
 
+|g:paredit_shortmaps|        If nonzero, paredit is remapping some one-letter
+                             Vim commands that are not frequently used.
+
 |g:slimv_clhs_root|          Base URL for the Common Lisp Hyperspec.
 
 |g:slimv_clhs_user_db|       User defined extension for Slimv's built-in
 Also the Slimv menu can be shown by pressing <Leader>, (defaults to ,,).
 
 
+                                                         *g:paredit_matchlines*
+Number of lines to look backward and forward when checking if the current
+top level form is balanced in paredit mode. Default is 100.
+
+
                                                                *g:paredit_mode*
 If nonzero then paredit mode is switched on, i.e. Slimv tries to keep the
 balanced state of parens. See |g:slimv-paredit|.
 
 
-                                                         *g:paredit_matchlines*
-Number of lines to look backward and forward when checking if the current
-top level form is balanced in paredit mode. Default is 100.
+                                                          *g:paredit_shortmaps*
+If nonzero, paredit is remapping some one-letter normal mode Vim commands that
+are not frequently used. These are <, >, J, O, W, S. The original function of
+these maps then can be reached via the <Leader> prefix (which is the ","
+character by default in Slimv).
+Otherwise these paredit functions can be reached via the <Leader> prefix.
 
 
                                                              *g:slimv_repl_dir*
     ,.      ,rs      Send Input
     ,/      ,ro      Close and Send Input
     <C-C>   <C-C>    Interrupt Lisp Process
-    ,<      ,rp      Previous Input
-    ,>      ,rn      Next Input
+    ,<Up>   ,rp      Previous Input
+    ,<Down> ,rn      Next Input
     ,z      ,rr      Refresh REPL Buffer
     ,Z      ,rw      Refresh REPL Now
 
 .lisp and .clj files.
 
 
-Here follows a list of paredit keybindings.
+Here follows a list of paredit keybindings:
+
 
 Insert Mode:
 
     )              Finds closing ')' of the current list. Can be pressed
                    repeatedly until the closing of the top level form reached.
 
-    <              If standing on a delimiter (parenthesis or square bracket)
+    <Leader><      If standing on a delimiter (parenthesis or square bracket)
                    then moves it to the left by slurping or barfing the
                    s-expression to the left, depending on the direction of the
                    delimiter:
                    to the left of the ')' going out of the current list.
                    Pressing '<' when standing on a '(' makes the s-expression
                    to the left of the '(' coming into the current list.
+                   For example pressing <Leader>< at position marked with |:
+                       (aaa bbb|)        --->    (aaa) bbb
+                       aaa |(bbb)        --->    (aaa bbb)
 
-    >              If standing on a delimiter (parenthesis or square bracket)
+    <Leader>>      If standing on a delimiter (parenthesis or square bracket)
                    then moves it to the right by slurping or barfing the
                    s-expression to the right, depending on the direction of the
                    delimiter:
                    to the right of the '(' going out of the current list.
                    Pressing '>' when standing on a ')' makes the s-expression
                    to the right of the ')' coming into the current list.
+                   For example pressing <Leader>< at position marked with |:
+                       (aaa|) bbb        --->    (aaa bbb)
+                       |(aaa bbb)        --->    aaa (bbb)
+
+    <Leader>J      Join two subsequent lists or strings. The first one must end
+                   before the cursor, the second one must start after the
+                   cursor position.
+                   For example pressing <Leader>J at position marked with |:
+                       (aaa)| (bbb)      --->    (aaa bbb)
+                       "aaa"| "bbb"      --->    "aaa bbb"
+
+    <Leader>O      Split ("Open") current list or string at the cursor position.
+                   Opposite of Join. Key O is selected because for the original
+                   Vim mapping J and O are also kind of opposites.
+                   For example pressing <Leader>O at position marked with |:
+                       (aaa |bbb)        --->    (aaa) (bbb)
+                       "aaa|bbb"         --->    "aaa" "bbb"
+
+    <Leader>W      Wrap the current symbol in a pair of parentheses. The cursor
+    <Leader>w(     is then positioned on the opening parenthesis, as wrapping
+                   is usually done because one wants to call a function with
+                   the symbol as parameter, so by pressing "a" one can enter
+                   the function name right after the newly inserted "(".
+                   For example pressing <Leader>W at position marked with |:
+                       (aaa b|bb ccc)    --->    (aaa (bbb) ccc)
+
+    <Leader>w[     Wrap the current symbol in a pair of square brackets,
+                   similarly to <Leader>W.
+                   For example pressing <Leader>w[ at position marked with |:
+                       (aaa b|bb ccc)    --->    (aaa [bbb] ccc)
+
+    <Leader>w"     Wrap the current symbol in a pair of double quotes,
+                   similarly to <Leader>W.
+                   For example pressing <Leader>w" at position marked with |:
+                       (aaa b|bb ccc)    --->    (aaa "bbb" ccc)
+
+    <Leader>S      Splice the current list into the containing list, i.e.
+                   remove the opening and closing parens. Opposite of wrap.
+                   For example pressing <Leader>S at position marked with |:
+                       (aaa (b|bb) ccc)  --->    (aaa bbb ccc)
 
     x  or  <Del>   When about to delete a (, ), [, ], or " and there are other
                    characters inside, then just skip it to the right. When
                    When preceded by a <count> value then delete this many
                    lines.
 
-    S              Same as 'dd' but go to insert mode at the end.
+    <Leader>S      Same as 'dd' but go to insert mode at the end.
 
 
 Visual Mode:
                    list. Can be pressed repeatedly until the top level form
                    selected.
 
+    <Leader>W      Wrap the current visual selection in a pair of parentheses.
+    <Leader>w(     The visual selection is kept.
+
+    <Leader>w[     Wrap the current visual selection in a pair of square
+                   brackets. The visual selection is kept.
+
+    <Leader>w"     Wrap the current visual selection in a pair of double
+                   quotes. The visual selection is kept.
+
+
+Please note that if variable |g:paredit_shortmaps| is nonzero then the
+following normal mode mappings don't get a <Leader> prefix, they are mapped
+to existing (but infrequently used) Vim functions and instead the original Vim
+functions are mapped with the <Leader> prefix:
+
+                   <, >, J, O, W, S
+
+
 
 ===============================================================================
 EXTERNAL UTILITIES                                             *slimv-external*
 ===============================================================================
 CHANGE LOG                                                    *slimv-changelog*
 
+0.6.1  - Added Split, Join, Wrap, Splice functions to Paredit Mode.
+       - Added g:paredit_shortmaps to select short/long paredit keymaps.
+       - Bugfix: delete commands put erased characters into yank buffer.
+       - Bugfix: D deletes only characters after the cursor position.
+
 0.6.0  - Added paredit mode.
        - Set wrap mode for REPL buffer with keybindings.
 
 - Or put the (Python) client part back into the .vim script, rewrite only the
   server part in Lisp.
 - Handle specific REPL output in Vim (like compilation notes).
+- Add all paredit.el functions.
 
 ===============================================================================
 CREDITS                                                         *slimv-credits*
 
 Author: Tamas Kovacs <kovisoft at gmail dot com>
 
+Please send comments, bug reports, suggestions, etc. to the e-mail address
+above.
+
 Credit must go out to Bram Moolenaar and all the Vim developers for making
 the world's (one of the) best editor.
 Thanks to Eric Marsden and all the Emacs/SLIME developers for making SLIME.

ftplugin/clojure/slimv-clojure.vim

 " slimv-clojure.vim:
 "               Clojure filetype plugin for Slimv
-" Version:      0.6.0
-" Last Change:  12 Apr 2010
+" Version:      0.6.1
+" Last Change:  27 Apr 2010
 " Maintainer:   Tamas Kovacs <kovisoft at gmail dot com>
 " License:      This file is placed in the public domain.
 "               No warranty, express or implied.
         endif
     else
         " Try to find Clojure in the home directory
+        "TODO: add /usr/local/bin/clojure
         let lisps = split( globpath( '~/*clojure*', 'clojure*.jar' ), '\n' )
         if len( lisps ) > 0
             return b:SlimvBuildStartCmd( lisps )

ftplugin/slimv.vim

 " slimv.vim:    The Superior Lisp Interaction Mode for VIM
-" Version:      0.6.0
-" Last Change:  19 Mar 2010
+" Version:      0.6.1
+" Last Change:  26 Apr 2010
 " Maintainer:   Tamas Kovacs <kovisoft at gmail dot com>
 " License:      This file is placed in the public domain.
 "               No warranty, express or implied.
     endif
 
     if g:slimv_keybindings == 1
-        noremap <buffer> <silent> <Leader>.  :call SlimvSendCommand(0,0)<CR>
-        noremap <buffer> <silent> <Leader>/  :call SlimvSendCommand(0,1)<CR>
-        noremap <buffer> <silent> <Leader><  :call SlimvPreviousCommand()<CR>
-        noremap <buffer> <silent> <Leader>>  :call SlimvNextCommand()<CR>
-        noremap <buffer> <silent> <Leader>z  :call SlimvRefresh()<CR>
-        noremap <buffer> <silent> <Leader>Z  :call SlimvRefreshNow()<CR>
+        noremap <buffer> <silent> <Leader>.      :call SlimvSendCommand(0,0)<CR>
+        noremap <buffer> <silent> <Leader>/      :call SlimvSendCommand(0,1)<CR>
+        noremap <buffer> <silent> <Leader><Up>   :call SlimvPreviousCommand()<CR>
+        noremap <buffer> <silent> <Leader><Down> :call SlimvNextCommand()<CR>
+        noremap <buffer> <silent> <Leader>z      :call SlimvRefresh()<CR>
+        noremap <buffer> <silent> <Leader>Z      :call SlimvRefreshNow()<CR>
     elseif g:slimv_keybindings == 2
-        noremap <buffer> <silent> <Leader>rs  :call SlimvSendCommand(0,0)<CR>
-        noremap <buffer> <silent> <Leader>ro  :call SlimvSendCommand(0,1)<CR>
-        noremap <buffer> <silent> <Leader>rp  :call SlimvPreviousCommand()<CR>
-        noremap <buffer> <silent> <Leader>rn  :call SlimvNextCommand()<CR>
-        noremap <buffer> <silent> <Leader>rr  :call SlimvRefresh()<CR>
-        noremap <buffer> <silent> <Leader>rw  :call SlimvRefreshNow()<CR>
+        noremap <buffer> <silent> <Leader>rs     :call SlimvSendCommand(0,0)<CR>
+        noremap <buffer> <silent> <Leader>ro     :call SlimvSendCommand(0,1)<CR>
+        noremap <buffer> <silent> <Leader>rp     :call SlimvPreviousCommand()<CR>
+        noremap <buffer> <silent> <Leader>rn     :call SlimvNextCommand()<CR>
+        noremap <buffer> <silent> <Leader>rr     :call SlimvRefresh()<CR>
+        noremap <buffer> <silent> <Leader>rw     :call SlimvRefreshNow()<CR>
     endif
 
     if g:slimv_repl_wrap

plugin/paredit.vim

 " paredit.vim:
 "               Paredit mode for Slimv
-" Version:      0.6.0
-" Last Change:  14 Apr 2010
+" Version:      0.6.1
+" Last Change:  27 Apr 2010
 " Maintainer:   Tamas Kovacs <kovisoft at gmail dot com>
 " License:      This file is placed in the public domain.
 "               No warranty, express or implied.
     let g:paredit_matchlines = 100
 endif
 
+" Use short keymaps, i.e. J instead of <Leader>J
+if !exists( 'g:paredit_shortmaps' )
+    let g:paredit_shortmaps = 0
+endif
+
 " =====================================================================
 "  Other variable definitions
 " =====================================================================
 let s:any_wsclose_char   = '\s\|)\|\]'
 let s:any_macro_prefix   = "'" . '\|`\|#\|@\|\~'
 
+let s:yank_pos           = []
+
 " =====================================================================
 "  General utility functions
 " =====================================================================
 " Buffer specific initialization
 function! PareditInitBuffer()
     "  Buffer specific keybindings
-    inoremap <buffer> <expr>   (     PareditInsertOpening('(',')')
-    inoremap <buffer> <expr>   )     PareditInsertClosing('(',')')
-    inoremap <buffer> <expr>   [     PareditInsertOpening('[',']')
-    inoremap <buffer> <expr>   ]     PareditInsertClosing('[',']')
-    inoremap <buffer> <expr>   "     PareditInsertQuotes()
-    inoremap <buffer> <expr>   <BS>  PareditBackspace(0)
-    inoremap <buffer> <expr>   <Del> PareditDel()
-    nnoremap <buffer> <silent> (     :<C-U>call PareditFindOpening('(',')',0)<CR>
-    nnoremap <buffer> <silent> )     :<C-U>call PareditFindClosing('(',')',0)<CR>
-    vnoremap <buffer> <silent> (     <Esc>:<C-U>call PareditFindOpening('(',')',1)<CR>
-    vnoremap <buffer> <silent> )     <Esc>:<C-U>call PareditFindClosing('(',')',1)<CR>
-    nnoremap <buffer> <silent> <     :<C-U>call PareditMoveLeft()<CR>
-    nnoremap <buffer> <silent> >     :<C-U>call PareditMoveRight()<CR>
-    nnoremap <buffer> <silent> x     :<C-U>call PareditEraseFwd()<CR>
-    nnoremap <buffer> <silent> <Del> :<C-U>call PareditEraseFwd()<CR>
-    nnoremap <buffer> <silent> X     :<C-U>call PareditEraseBck()<CR>
-    nnoremap <buffer> <silent> s     :<C-U>call PareditEraseFwd()<CR>i
-    nnoremap <buffer> <silent> D     :<C-U>call PareditEraseFwdLine()<CR>
-    nnoremap <buffer> <silent> C     :<C-U>call PareditEraseFwdLine()<CR>A
-    nnoremap <buffer> <silent> S     0:<C-U>call PareditEraseFwdLine()<CR>A
-    nnoremap <buffer> <silent> dd    :<C-U>call PareditEraseLine()<CR>
+    inoremap <buffer> <expr>   (            PareditInsertOpening('(',')')
+    inoremap <buffer> <expr>   )            PareditInsertClosing('(',')')
+    inoremap <buffer> <expr>   [            PareditInsertOpening('[',']')
+    inoremap <buffer> <expr>   ]            PareditInsertClosing('[',']')
+    inoremap <buffer> <expr>   "            PareditInsertQuotes()
+    inoremap <buffer> <expr>   <BS>         PareditBackspace(0)
+    inoremap <buffer> <expr>   <Del>        PareditDel()
+    nnoremap <buffer> <silent> (            :<C-U>call PareditFindOpening('(',')',0)<CR>
+    nnoremap <buffer> <silent> )            :<C-U>call PareditFindClosing('(',')',0)<CR>
+    vnoremap <buffer> <silent> (            <Esc>:<C-U>call PareditFindOpening('(',')',1)<CR>
+    vnoremap <buffer> <silent> )            <Esc>:<C-U>call PareditFindClosing('(',')',1)<CR>
+    nnoremap <buffer> <silent> x            :<C-U>call PareditEraseFwd()<CR>
+    nnoremap <buffer> <silent> <Del>        :<C-U>call PareditEraseFwd()<CR>
+    nnoremap <buffer> <silent> X            :<C-U>call PareditEraseBck()<CR>
+    nnoremap <buffer> <silent> s            :<C-U>call PareditEraseFwd()<CR>i
+    nnoremap <buffer> <silent> D            :<C-U>call PareditEraseFwdLine()<CR>
+    nnoremap <buffer> <silent> C            :<C-U>call PareditEraseFwdLine()<CR>A
+    nnoremap <buffer> <silent> dd           :<C-U>call PareditEraseLine()<CR>
+    nnoremap <buffer> <silent> cc           0:<C-U>call PareditEraseFwdLine()<CR>A
+    nnoremap <buffer> <silent> <Leader>w(   :<C-U>call PareditWrap('(',')')<CR>
+    vnoremap <buffer> <silent> <Leader>w(   :<C-U>call PareditWrapSelection('(',')')<CR>
+    nnoremap <buffer> <silent> <Leader>w[   :<C-U>call PareditWrap('[',']')<CR>
+    vnoremap <buffer> <silent> <Leader>w[   :<C-U>call PareditWrapSelection('[',']')<CR>
+    nnoremap <buffer> <silent> <Leader>w"   :<C-U>call PareditWrap('"','"')<CR>
+    vnoremap <buffer> <silent> <Leader>w"   :<C-U>call PareditWrapSelection('"','"')<CR>
+
+    if g:paredit_shortmaps
+        " Shorter keymaps: old functionality of KEY is remapped to <Leader>KEY
+        nnoremap <buffer> <silent> <            :<C-U>call PareditMoveLeft()<CR>
+        nnoremap <buffer> <silent> >            :<C-U>call PareditMoveRight()<CR>
+        nnoremap <buffer> <silent> O            :<C-U>call PareditSplit()<CR>
+        nnoremap <buffer> <silent> J            :<C-U>call PareditJoin()<CR>
+        nnoremap <buffer> <silent> W            :<C-U>call PareditWrap()<CR>
+        vnoremap <buffer> <silent> W            :<C-U>call PareditWrapSelection()<CR>
+        nnoremap <buffer> <silent> S            :<C-U>call PareditSplice()<CR>
+        nnoremap <buffer> <silent> <Leader><    :<C-U>normal! <<CR>
+        nnoremap <buffer> <silent> <Leader>>    :<C-U>normal! ><CR>
+        nnoremap <buffer> <silent> <Leader>O    :<C-U>normal! O<CR>
+        nnoremap <buffer> <silent> <Leader>J    :<C-U>normal! J<CR>
+        nnoremap <buffer> <silent> <Leader>W    :<C-U>normal! W<CR>
+        vnoremap <buffer> <silent> <Leader>W    :<C-U>normal! W<CR>
+        nnoremap <buffer> <silent> <Leader>S    :<C-U>normal! S<CR>
+    else
+        " Longer keymaps with <Leader> prefix
+        nnoremap <buffer> <silent> S            0:<C-U>call PareditEraseFwdLine()<CR>A
+        nnoremap <buffer> <silent> <Leader><    :<C-U>call PareditMoveLeft()<CR>
+        nnoremap <buffer> <silent> <Leader>>    :<C-U>call PareditMoveRight()<CR>
+        nnoremap <buffer> <silent> <Leader>O    :<C-U>call PareditSplit()<CR>
+        nnoremap <buffer> <silent> <Leader>J    :<C-U>call PareditJoin()<CR>
+        nnoremap <buffer> <silent> <Leader>W    :<C-U>call PareditWrap('(',')')<CR>
+        vnoremap <buffer> <silent> <Leader>W    :<C-U>call PareditWrapSelection('(',')')<CR>
+        nnoremap <buffer> <silent> <Leader>S    :<C-U>call PareditSplice()<CR>
+    endif
 endfunction
 
 " Toggle paredit mode
 function! PareditFindOpening( open, close, select )
     let open  = escape( a:open , '[]' )
     let close = escape( a:close, '[]' )
-    call searchpair( a:open, '', a:close, 'bW', s:skip_sc )
+    call searchpair( open, '', close, 'bW', s:skip_sc )
     if a:select
-        call searchpair( a:open, '', a:close, 'W', s:skip_sc )
+        call searchpair( open, '', close, 'W', s:skip_sc )
         let save_ve = &ve
         set ve=all 
         normal! lvh
         let &ve = save_ve
-        call searchpair( a:open, '', a:close, 'bW', s:skip_sc )
+        call searchpair( open, '', close, 'bW', s:skip_sc )
     endif
 endfunction
 
         if line[col('.')-1] != a:open
             normal! h
         endif
-        call searchpair( a:open, '', a:close, 'W', s:skip_sc )
-        call searchpair( a:open, '', a:close, 'bW', s:skip_sc )
+        call searchpair( open, '', close, 'W', s:skip_sc )
+        call searchpair( open, '', close, 'bW', s:skip_sc )
         normal! v
-        call searchpair( a:open, '', a:close, 'W', s:skip_sc )
+        call searchpair( open, '', close, 'W', s:skip_sc )
         normal! l
     else
-        call searchpair( a:open, '', a:close, 'W', s:skip_sc )
+        call searchpair( open, '', close, 'W', s:skip_sc )
     endif
 endfunction
 
     if s:InsideString()
         let line = getline( '.' )
         let pos = col( '.' ) - 1
+        "TODO: skip comments in search(...)
         if line[pos] == '"'
             " Standing on a ", just move to the right
             return "\<Right>"
-        elseif (pos > 0 && line[pos-1] == '\') || search('[^\\]"\|^"', 'nW', s:skip_c) == 0
+        elseif (pos > 0 && line[pos-1] == '\') || search('[^\\]"\|^"', 'nW') == 0
             " We don't have any closing ", insert one
             return '"'
         else
             " Move to the closing "
-            return "\<C-O>:call search('" . '[^\\]"\|^"' . "','eW','" . s:skip_c . "')\<CR>\<Right>"
+            return "\<C-O>:call search('" . '[^\\]"\|^"' . "','eW')\<CR>\<Right>"
         endif
     else
         " Outside of string: insert a pair of ""
     endif
 endfunction
 
+" Initialize yank position list
+function! s:InitYankPos()
+    let @" = ''
+    let s:yank_pos = []
+endfunction
+
+" Add position to the yank list
+function! s:AddYankPos( pos )
+"echo input('111 '.a:pos)
+    let s:yank_pos = [a:pos] + s:yank_pos
+endfunction
+
+" Remove the head of yank position list and return it
+function! s:RemoveYankPos()
+    if len(s:yank_pos) > 0
+        let pos = s:yank_pos[0]
+        let s:yank_pos = s:yank_pos[1:]
+        return pos
+    else
+        return 0
+    endif
+endfunction
+
 " Forward erasing a character in normal mode, do not check if current form balanced
-function! s:EraseFwd( count )
+function! s:EraseFwd( count, startcol )
     let line = getline( '.' )
     let pos = col( '.' ) - 1
-    if pos == len(line)
-        let pos = pos - 1
-    endif
+    let reg = @"
     let c = a:count
     while c > 0
         if s:InsideString() && line[pos : pos+1] == '\"'
+            " Erasing a \" inside string
+            let reg = reg . line[pos : pos+1]
             let line = strpart( line, 0, pos ) . strpart( line, pos+2 )
+        elseif s:InsideComment() && line[pos] == ';' && a:startcol >= 0
+            " Erasing the whole comment, only when erasing a block of characters
+            let reg = reg . strpart( line, pos )
+            let line = strpart( line, 0, pos )
         elseif s:InsideComment() || ( s:InsideString() && line[pos] != '"' )
+            " Erasing any character inside string or comment
+            let reg = reg . line[pos]
             let line = strpart( line, 0, pos ) . strpart( line, pos+1 )
         elseif pos > 0 && line[pos-1:pos] =~ s:any_matched_pair
-            " Erasing an empty character-pair
-            let line = strpart( line, 0, pos-1 ) . strpart( line, pos+1 )
-            let pos = pos - 1
-            normal! h
+            if pos > a:startcol
+                " Erasing an empty character-pair
+                let p2 = s:RemoveYankPos()
+                let reg = strpart( reg, 0, p2 ) . line[pos-1] . strpart( reg, p2 )
+                let reg = reg . line[pos]
+                let line = strpart( line, 0, pos-1 ) . strpart( line, pos+1 )
+                let pos = pos - 1
+                normal! h
+            else
+                " Can't erase character-pair: it would move the cursor before startcol
+                let pos = pos + 1
+                normal! l
+            endif
         elseif line[pos] =~ s:any_matched_char
             " Character-pair is not empty, don't erase just move inside
+            call s:AddYankPos( len(reg) )
             let pos = pos + 1
             normal! l
-        else
+        elseif pos < len(line)
             " Erasing a non-special character
+            let reg = reg . line[pos]
             let line = strpart( line, 0, pos ) . strpart( line, pos+1 )
         endif
         let c = c - 1
     endwhile
     call setline( '.', line )
+    let @" = reg
 endfunction
 
 " Backward erasing a character in normal mode, do not check if current form balanced
 function! s:EraseBck( count )
     let line = getline( '.' )
     let pos = col( '.' ) - 1
+    let reg = @"
     let c = a:count
     while c > 0 && pos > 0
         if s:InsideString() && pos > 1 && line[pos-2:pos-1] == '\"'
+            let reg = reg . line[pos-2 : pos-1]
             let line = strpart( line, 0, pos-2 ) . strpart( line, pos )
             normal! h
             let pos = pos - 1
         elseif s:InsideComment() || ( s:InsideString() && line[pos-1] != '"' )
+            let reg = reg . line[pos-1]
             let line = strpart( line, 0, pos-1 ) . strpart( line, pos )
         elseif line[pos-1:pos] =~ s:any_matched_pair
             " Erasing an empty character-pair
+            let p2 = s:RemoveYankPos()
+            let reg = strpart( reg, 0, p2 ) . line[pos-1] . strpart( reg, p2 )
+            let reg = reg . line[pos]
             let line = strpart( line, 0, pos-1 ) . strpart( line, pos+1 )
-        elseif line[pos-1] !~ s:any_matched_char
+        elseif line[pos-1] =~ s:any_matched_char
+            " Character-pair is not empty, don't erase
+            call s:AddYankPos( len(reg) )
+        else
             " Erasing a non-special character
+            let reg = reg . line[pos-1]
             let line = strpart( line, 0, pos-1 ) . strpart( line, pos )
         endif
         normal! h
         let c = c - 1
     endwhile
     call setline( '.', line )
+    let @" = reg
 endfunction
 
 " Forward erasing a character in normal mode
         return
     endif
 
-    call s:EraseFwd( v:count1 )
+    call s:InitYankPos()
+    call s:EraseFwd( v:count1, -1 )
 endfunction
 
 " Backward erasing a character in normal mode
         return
     endif
 
+    call s:InitYankPos()
     call s:EraseBck( v:count1 )
 endfunction
 
 " Forward erasing character till the end of line in normal mode
 " Keeping the balanced state
+function! s:EraseFwdLine( startcol )
+    let lastcol = -1
+    let lastlen = -1
+    while col( '.' ) != lastcol || len( getline( '.' ) ) != lastlen
+        let lastcol = col( '.' )
+        let lastlen = len( getline( '.' ) )
+        call s:EraseFwd( 1, a:startcol )
+    endwhile
+endfunction
+
+" Forward erasing character till the end of line in normal mode
+" But first check if we are allowed to do it in paredit way
 function! PareditEraseFwdLine()
     if !g:paredit_mode || !s:IsBalanced()
         if v:count > 0
         return
     endif
 
-    let lastcol = -1
-    let lastlen = -1
-    while col( '.' ) != lastcol || len( getline( '.' ) ) != lastlen
-        let lastcol = col( '.' )
-        let lastlen = len( getline( '.' ) )
-        call s:EraseFwd( 1 )
-    endwhile
+    call s:InitYankPos()
+    call s:EraseFwdLine( col( '.' ) - 1 )
 endfunction
 
 " Erasing all characters in the line in normal mode
         return
     endif
 
+    normal! 0
+    call s:InitYankPos()
     let c = v:count1
     while c > 0
-        call PareditEraseFwdLine()
-
-        let lastcol = -1
-        let lastlen = -1
-        while col( '.' ) != lastcol || len( getline( '.' ) ) != lastlen
-            let lastcol = col( '.' )
-            let lastlen = len( getline( '.' ) )
-            call s:EraseBck( 1 )
-        endwhile
-
+        call s:EraseFwdLine( -1 )
         if len( getline( '.' ) ) == 0
+            let reg = @"
             normal! dd
+            let @" = reg . "\n"
         elseif c > 1
             normal! J
+            let @" = @" . "\n"
         endif
         let c = c - 1
     endwhile
     endif
 endfunction
 
+" Find a paren nearby to move
+function! s:FindParenNearby()
+    let line = getline( '.' )
+    let c0 =  col( '.' )
+    if line[c0-1] !~ s:any_openclose_char
+        " OK, we are not standing on a paren to move, but check if there is one nearby
+        if (c0 < 2 || line[c0-2] !~ s:any_openclose_char) && line[c0] =~ s:any_openclose_char
+            normal! l
+        elseif c0 > 1 && line[c0-2] =~ s:any_openclose_char && line[c0] !~ s:any_openclose_char
+            normal! h
+        endif
+    endif
+
+    " Skip macro prefix character    
+    let c0 =  col( '.' )
+    if line[c0-1] =~ s:any_macro_prefix && line[c0] =~ s:any_opening_char
+        normal! l
+    endif
+endfunction
+
 " Move delimiter one atom or s-expression to the left
 function! PareditMoveLeft()
+    call s:FindParenNearby()
+
     let line = getline( '.' )
     let l0 = line( '.' )
     let c0 =  col( '.' )
 
-    if line[c0-1] =~ s:any_macro_prefix && line[c0] =~ s:any_opening_char
-        let closing = 0
-        normal! l
-        let c0 = c0 + 1
-    elseif line[c0-1] =~ s:any_opening_char
+    if line[c0-1] =~ s:any_opening_char
         let closing = 0
     elseif line[c0-1] =~ s:any_closing_char
         let closing = 1
 
 " Move delimiter one atom or s-expression to the right
 function! PareditMoveRight()
+    call s:FindParenNearby()
+
+    "TODO: move ')' in '() xxx' leaves space
     let line = getline( '.' )
     let l0 = line( '.' )
     let c0 =  col( '.' )
 
-    if line[c0-1] =~ s:any_macro_prefix && line[c0] =~ s:any_opening_char
-        let opening = 1
-        normal! l
-        let c0 = c0 + 1
-    elseif line[c0-1] =~ s:any_opening_char
+    if line[c0-1] =~ s:any_opening_char
         let opening = 1
     elseif line[c0-1] =~ s:any_closing_char
         let opening = 0
     return
 endfunction
 
+" Find closing of the innermost structure: (...) or [...]
+" Return a list where first element is the closing character,
+" second and third is its position (line, column)
+function! s:FindClosing()
+    let l = line( '.' )
+    let c = col( '.' )
+    call PareditFindClosing( '(', ')', 0 )
+    let lp = line( '.' )
+    let cp = col( '.' )
+    call setpos( '.', [0, l, c, 0] )
+    call PareditFindClosing( '[', ']', 0 )
+    let lb = line( '.' )
+    let cb = col( '.' )
+    call setpos( '.', [0, l, c, 0] )
+    if [lp, cp] == [l, c] && [lb, cb] == [l, c]
+        " Not found any kind of paren
+        return ['', 0, 0]
+    elseif [lb, cb] == [l, c] || lp < lb || (lp == lb && cp < cb)
+        " The innermost structure is a (...)
+        return [')', lp, cp]
+    else
+        " The innermost structure is a [...]
+        return [']', lb, cb]
+    endif
+endfunction
+
+" Split list or string at the cursor position
+" Current symbol will be split into the second part
+function! PareditSplit()
+    if !g:paredit_mode || s:InsideComment()
+        return
+    endif
+
+    if s:InsideString()
+        normal! i" "
+    else
+        " Go back to the beginning of the current symbol
+        let c = col('.') - 1
+        if getline('.')[c] =~ '\S'
+            if c == 0 || (c > 0 && getline('.')[c-1] =~ s:any_wsopen_char)
+                " OK, we are standing on the first character of the symbol
+            else
+                normal! b
+            endif
+        endif
+
+        " First find which kind of paren is the innermost
+        let [p, l, c] = s:FindClosing()
+        if p !~ s:any_closing_char
+            " Not found any kind of parens
+            return
+        endif
+
+        " Delete all whitespaces around cursor position
+        while getline('.')[col('.')-1] =~ '\s'
+            normal! x
+        endwhile
+        while col('.') > 1 && getline('.')[col('.')-2] =~ '\s'
+            normal! X
+        endwhile
+
+        if p == ')'
+            normal! i) (
+        else
+            normal! i] [
+        endif
+    endif
+endfunction
+
+" Join two neighboring lists or strings
+function! PareditJoin()
+    if !g:paredit_mode || s:InsideCommentOrString()
+        return
+    endif
+
+    "TODO: skip parens in comments
+    let [l0, c0] = searchpos(s:any_matched_char, 'nbW')
+    let [l1, c1] = searchpos(s:any_matched_char, 'ncW')
+    if [l0, c0] == [0, 0] || [l1, c1] == [0, 0]
+        return
+    endif
+    let line0 = getline( l0 )
+    let line1 = getline( l1 )
+    if (line0[c0-1] == ')' && line1[c1-1] == '(') || (line0[c0-1] == ']' && line1[c1-1] == '[') || (line0[c0-1] == '"' && line1[c1-1] == '"')
+        if l0 == l1
+            " First list ends on the same line where the second list begins
+            let line0 = strpart( line0, 0, c0-1 ) . ' ' . strpart( line0, c1 )
+            call setline( l0, line0 )
+        else
+            " First list ends on a line different from where the second list begins
+            let line0 = strpart( line0, 0, c0-1 )
+            let line1 = strpart( line1, 0, c1-1 ) . strpart( line1, c1 )
+            call setline( l0, line0 )
+            call setline( l1, line1 )
+        endif
+    endif
+endfunction
+
+" Wrap current visual block in parens of the given kind
+function! s:WrapSelection( open, close )
+    let l0 = line( "'<" )
+    let l1 = line( "'>" )
+    let c0 = col( "'<" )
+    let c1 = col( "'>" )
+    if [l0, c0] == [0, 0] || [l1, c1] == [0, 0]
+        " No selection
+        return
+    endif
+    if l0 > l1 || (l0 == l1 && c0 > c1)
+        " Swap both ends of selection to make [l0, c0] < [l1, c1]
+        let [ltmp, ctmp] = [l0, c0]
+        let [l0, c0] = [l1, c1]
+        let [l1, c1] = [ltmp, ctmp]
+    endif
+    let save_ve = &ve
+    set ve=all 
+    call setpos( '.', [0, l0, c0, 0] )
+    execute "normal! i" . a:open
+    call setpos( '.', [0, l1, c1 + (l0 == l1), 0] )
+    execute "normal! i" . a:close
+    let &ve = save_ve
+endfunction
+
+" Wrap current visual block in parens of the given kind
+" Keep visual mode
+function! PareditWrapSelection( open, close )
+    call s:WrapSelection( a:open, a:close )
+    if line( "'<" ) == line( "'>" )
+        normal! gvolol
+    else
+        normal! gvolo
+    endif
+endfunction
+
+" Wrap current symbol in parens of the given kind
+" Stand on the opening paren (if not wrapping in "")
+function! PareditWrap( open, close )
+    execute "normal! " . "viw\<Esc>"
+    call s:WrapSelection( a:open, a:close )
+    if a:open != '"'
+        normal! %
+    endif
+endfunction
+
+" Splice current list into the containing list
+function! PareditSplice()
+    if !g:paredit_mode
+        return
+    endif
+
+    " First find which kind of paren is the innermost
+    let [p, l, c] = s:FindClosing()
+    if p !~ s:any_closing_char
+        " Not found any kind of parens
+        return
+    endif
+
+    call setpos( '.', [0, l, c, 0] )
+    normal! %
+    let l = line( '.' )
+    let c = col( '.' )
+    normal! %x
+    call setpos( '.', [0, l, c, 0] )
+    normal! x
+    if c > 1 && getline('.')[c-2] =~ s:any_macro_prefix
+        normal! X
+    endif
+endfunction
+
+
 " =====================================================================
 "  Autocommands
 " =====================================================================