diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2022-09-15 11:41:52 -0600 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2022-09-15 12:05:58 -0600 |
commit | 1ce32ad32e0006fbc7fe3e5e82f400ee0f7d52fa (patch) | |
tree | 28a810944a9557668fc0b29e6b6aab111840b918 | |
parent | a3caef03c883cd9711ac60996227e13e1199e4db (diff) | |
download | fieldmarshal.vim-1ce32ad32e0006fbc7fe3e5e82f400ee0f7d52fa.tar.gz fieldmarshal.vim-1ce32ad32e0006fbc7fe3e5e82f400ee0f7d52fa.tar.bz2 fieldmarshal.vim-1ce32ad32e0006fbc7fe3e5e82f400ee0f7d52fa.zip |
insert.vim: add Zi and Za commands to insert before/after a text object/motion
So typing Ziib will move the cursor to the next body and enter INSERT
mode.
Zaib will move the cursor to the END of the next body and enter INSERT
mode
Ziiw will enter insert mode before the current word
Zaiw will enter insert mode after the current word.
Ziip will enter insert mode befor the current paragraph
thus, Zi^ is semantically equivalent to I and Za$ is semantically
equivalent to A.
Zib will move the cursor back a word and enter insert mode.
All of these commands are repeatable with the dot (.) operator.
much of this same thing can be accomplished with the change operator (c)
and use of ^R^P", but that's quite obtuse, especially when working on
large text objects.
Right now, without Rahm's patched Neovim using user-defined text objects
will cause these commands to not play well with the dot (.) operator.
This is because do accomplish this, this plugin uses an operator within
an operator which is not well supported in Vim.
-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 + |