Permalink
Browse files

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.
  • Loading branch information...
1 parent afba0c3 commit 1d0faae887c6cf453ffca1e8efb1dbe5bcbc39de @kovisoft kovisoft committed with Apr 27, 2010
Showing with 464 additions and 97 deletions.
  1. +93 −11 doc/slimv.txt
  2. +3 −2 ftplugin/clojure/slimv-clojure.vim
  3. +14 −14 ftplugin/slimv.vim
  4. +354 −70 plugin/paredit.vim
View
104 doc/slimv.txt
@@ -1,7 +1,7 @@
-*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
@@ -144,6 +144,9 @@ Below that list follows the detailed explanation on each option.
|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
@@ -353,14 +356,22 @@ If nonzero then the Slimv menu is added to the end of the global menu.
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*
@@ -606,8 +617,8 @@ by Slimv to ',' by default.
,. ,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
@@ -895,7 +906,8 @@ Slimv core will not be loaded but paredit will be loaded and assigned to
.lisp and .clj files.
-Here follows a list of paredit keybindings.
+Here follows a list of paredit keybindings:
+
Insert Mode:
@@ -943,23 +955,66 @@ Normal 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:
Pressing '<' when standing on a ')' makes the s-expression
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:
Pressing '>' when standing on a '(' makes the s-expression
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
@@ -986,7 +1041,7 @@ Normal Mode:
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:
@@ -999,6 +1054,24 @@ 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*
@@ -1182,6 +1255,11 @@ FAQ *slimv-faq*
===============================================================================
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.
@@ -1311,12 +1389,16 @@ TODO *slimv-todo*
- 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.
View
5 ftplugin/clojure/slimv-clojure.vim
@@ -1,7 +1,7 @@
" 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.
@@ -58,6 +58,7 @@ function! b:SlimvAutodetect()
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 )
View
28 ftplugin/slimv.vim
@@ -1,6 +1,6 @@
" 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.
@@ -750,19 +750,19 @@ function! SlimvOpenReplBuffer()
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
View
424 plugin/paredit.vim
@@ -1,7 +1,7 @@
" 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.
@@ -36,6 +36,11 @@ if !exists( 'g:paredit_matchlines' )
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
" =====================================================================
@@ -55,34 +60,68 @@ let s:any_wsopen_char = '\s\|(\|\['
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
@@ -187,14 +226,14 @@ endfunction
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
@@ -207,13 +246,13 @@ function! PareditFindClosing( open, close, select )
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
@@ -263,15 +302,16 @@ function! PareditInsertQuotes()
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 ""
@@ -345,61 +385,113 @@ function! PareditDel()
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 pos = pos - 1
let c = c - 1
endwhile
call setline( '.', line )
+ let @" = reg
endfunction
" Forward erasing a character in normal mode
@@ -413,7 +505,8 @@ function! PareditEraseFwd()
return
endif
- call s:EraseFwd( v:count1 )
+ call s:InitYankPos()
+ call s:EraseFwd( v:count1, -1 )
endfunction
" Backward erasing a character in normal mode
@@ -427,11 +520,24 @@ function! PareditEraseBck()
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
@@ -442,13 +548,8 @@ function! PareditEraseFwdLine()
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
@@ -463,22 +564,18 @@ function! PareditEraseLine()
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
@@ -672,17 +769,35 @@ function! s:MoveChar( l0, c0, l1, c1 )
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
@@ -730,15 +845,14 @@ endfunction
" 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
@@ -784,6 +898,176 @@ function! PareditMoveRight()
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
" =====================================================================

0 comments on commit 1d0faae

Please sign in to comment.