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 (insert-after-comment) noremap cgI (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 in insert " mode. For example, suffixing the current word with 'ed' can be " accomplished with either: " " Zaeed " " or " " ce^R^P"ed " " or Prefixing with sub can be accomplished with " " Zibsub " " or " " cbsub^R^P" " " 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 (insert-before-motion) noremap Za (append-after-motion) endif noremap (insert-after-comment) call insert_comment_count(v:count1)1c(insert-comment-obj-nog) noremap (insert-after-comment-g)> call insert_comment_count(v:count1)1c(insert-comment-obj-g) onoremap (insert-comment-obj-nog) call insert_comment_obj(0) onoremap (insert-comment-obj-g) call insert_comment_obj(1) " 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 normal! 0 let i = 0 call search('^\s*\zs\S', '', line('.')) while i < s:count if col('.') == col('$') - 1 let end = 1 break endif if a:g let pattern = '\S*\zs\ze\s\+' else let pattern = '\S*\s\+\zs' endif if ! search(pattern, '', line('.')) let end = 1 break endif let i += 1 endwhile " Cheese because 0-width visual selections aren't a thing, I don't think, so " instead insert an ephemeral space and VIsual highlight that space, the c " command will then remove that ephemeral space, all with the user being " none-the-wiser. if end exec "normal! A \v" else exec "normal! i \v" endif else endif endfunction noremap (insert-before-motion) call save_state('i') \call start_recording() \set operatorfunc=recordopg@ noremap (append-after-motion) call save_state('a') \call start_recording() \set operatorfunc=recordopg@ onoremap (insert-about-obj) call insert_before_recorded() nnoremap (insert-about-obj) call insert_before_recorded() " 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 (ñóþ) " 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 = "\(ñóþ)" . 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! \a \v" else exec "normal! \i \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 (insert-about-obj). This is the part that can be repeated. call feedkeys("c\(insert-about-obj)") endfunction