diff options
-rw-r--r-- | plugin/insert.vim | 191 |
1 files changed, 175 insertions, 16 deletions
diff --git a/plugin/insert.vim b/plugin/insert.vim index c6de7a2..3caca81 100644 --- a/plugin/insert.vim +++ b/plugin/insert.vim @@ -1,26 +1,76 @@ -" Other operators for entering insert mode. - -" Like 'I', but skip past the first v:count1 WORDs. Useful for when one wants -" the 'I' behavior to respect comments. -" -" If there are fewer than v:count1 WORDs in the line, then append to the end of -" the line. -" -" Have to cheese this a little bit to get the redo (.) operator to work with it. -" This is done by making a "sort-of" 0-width text object that exists where the -" change should happen. -" -" If the 'g' is present, that acts similar to gI -- it does not skip -" whitespace and starts insertion directly after the v:count1'th word. -noremap cI <cmd>call <sid>insert_comment_count(v:count1)<cr>1c<Plug><sid>(insert-comment-obj-nog) -noremap cgI <cmd>call <sid>insert_comment_count(v:count1)<cr>1c<Plug><sid>(insert-comment-obj-g) +if !exists('g:field_marshal_insert_include_bindings') + let g:field_marshal_insert_include_bindings = 1 +endif + +if g:field_marshal_insert_include_bindings + " Like 'I', but skip past the first v:count1 WORDs. Useful for when one wants + " the 'I' behavior to respect comments. + " + " If there are fewer than v:count1 WORDs in the line, then append to the end of + " the line. + " + " Have to cheese this a little bit to get the redo (.) operator to work with it. + " This is done by making a "sort-of" 0-width text object that exists where the + " change should happen. + " + " If the 'g' is present, that acts similar to gI -- it does not skip + " whitespace and starts insertion directly after the v:count1'th word. + noremap cI <Plug>(insert-after-comment) + noremap cgI <Plug>(insert-after-comment-g) + + " Insert before/after a motion. + " + " Zi - takes a text object, moves to the beginning of that text object and + " places the user in INSERT mode. + " + " Za - takes a text object, moves to the end of that text object and places + " the user in INSERT mode. + " + " Some of this functionality can be replicated using <C-R><C-P> in insert + " mode. For example, suffixing the current word with 'ed' can be + " accomplished with either: + " + " Zaeed<esc> + " + " or + " + " ce^R^P"ed<esc> + " + " or Prefixing with sub can be accomplished with + " + " Zibsub<esc> + " + " or + " + " cbsub^R^P"<esc> + " + " However the traditional ^R^P" solution is not very pretty and prone to + " typeos. + " + " This also means that certain motions are now redundant and can be + " emulated. For example: + " + " I === Zi^ + " gI === Zi0 + " A === Za$ + " + " + noremap Zi <Plug>(insert-before-motion) + noremap Za <Plug>(append-after-motion) +endif + +noremap <Plug>(insert-after-comment) <cmd>call <sid>insert_comment_count(v:count1)<cr>1c<Plug><sid>(insert-comment-obj-nog) +noremap <Plug>(insert-after-comment-g)> <cmd>call <sid>insert_comment_count(v:count1)<cr>1c<Plug><sid>(insert-comment-obj-g) + onoremap <Plug><sid>(insert-comment-obj-nog) <cmd>call <sid>insert_comment_obj(0)<cr> onoremap <Plug><sid>(insert-comment-obj-g) <cmd>call <sid>insert_comment_obj(1)<cr> +" Set the count for the insert comment command. function! s:insert_comment_count(count) abort let s:count = a:count endfunction +" The function which will set the insert comment. function! s:insert_comment_obj(g, ...) abort if v:operator == 'c' let end = 0 @@ -59,3 +109,112 @@ function! s:insert_comment_obj(g, ...) abort else endif endfunction + +noremap <silent> <Plug>(insert-before-motion) <cmd>call <sid>save_state('i') + \<bar>call <sid>start_recording() + \<bar>set operatorfunc=<sid>recordop<cr>g@ +noremap <silent> <Plug>(append-after-motion) <cmd>call <sid>save_state('a') + \<bar>call <sid>start_recording() + \<bar>set operatorfunc=<sid>recordop<cr>g@ +onoremap <silent> <Plug>(insert-about-obj) <cmd>call <sid>insert_before_recorded()<cr> +nnoremap <silent> <Plug>(insert-about-obj) <cmd>call <sid>insert_before_recorded()<cr> + +" Save the 'l' register. This is the register used for recording the text +" motion. +let s:savereg = [] +let s:savepos = [] +let s:instype = '' + +" Not many people use the 'z' register, right? +let s:reg_to_clobber = 'z' +" if exists('&urf') +" " Neovim has Rahm's multibyte register extension patch. Pick an arbitrary +" " unicode character which is probably not used very often to clobber. +" let s:reg_to_clobber = '∫' +" endif + +function! s:start_recording() + exec "normal! q" . s:reg_to_clobber +endfunction + +function! s:save_state(instype) + " Unfortunately macros kinda break this feature. While I think I might be able + " to patch together a fix to make them work, I figure that I'll see if there's + " any real need to implement it. + if reg_recording() != '' + normal! q + endif + + let s:savereg = [getreg(s:reg_to_clobber, 1, 1), getregtype(s:reg_to_clobber)] + let s:savepos = getpos('.') + let s:instype = a:instype +endfunction + +noremap <plug>(ñóþ) <nop> + +" Save the motion postions, for use with g@. +function s:save_object(t) abort + let s:object = { + \ "type": a:t, + \ "start": getpos("'["), + \ "end": getpos("']") + \ } +endfunction + +function! s:insert_before_recorded() abort + " A limitation of normal! is that it can't start with a space, so use a NOP + " instead. + if s:recorded =~ '^\s' + let s:recorded = "\<plug>(ñóþ)" . s:recorded + endif + + " Something of a hack. If the motion starts with i or a, it is probably a + " text object. + " + " I think there's probably a better way to handle this, butth + if s:recorded =~ '^[ia]' + " Without Rahm's patched Neovim, custom text objects will not work. This is + " because while the redo buffer is saved and restored when calling a user + " function, repeat_cmdline is not, and thus the g@ command clobbers the + " repeat_cmdline which breaks the redobuff. Ergo the first Zi will work, + " but subsequent repititions with the dot operator will not work. + set operatorfunc=s:save_object + exec "normal g@" . s:recorded + + if s:instype == 'a' + call setpos(".", s:object.end) + if s:object.type == 'line' + normal! $ + endif + else + call setpos(".", s:object.start) + if s:object.type == 'line' + normal! 0 + endif + endif + else + exec "normal " . s:recorded + endif + + if s:instype == 'a' + exec "normal! \<esc>a \<esc>v" + else + exec "normal! \<esc>i \<esc>v" + endif +endfunction + +" Record the operator +function! s:recordop(...) abort + " Stop recording + normal! q + " Save the recorded amount + let s:recorded=getreg(s:reg_to_clobber) + " Restore the register + call setreg(s:reg_to_clobber, s:savereg[0], s:savereg[1]) + " Restore the position + call setpos('.', s:savepos) + + " Called <Plug>(insert-about-obj). This is the part that can be repeated. + call feedkeys("c\<Plug>(insert-about-obj)") +endfunction + |