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) noremap Zo (open-after-motion) noremap ZO (OPEN-after-motion) noremap Z[ (to-object-start) noremap Z] (to-object-end) noremap Zn (to-object-pos-post) " Zx commands, run x on the last recorded action. The equivalelent of xZr noremap Zd "d" . getrecorded() noremap Zc "c" . getrecorded() noremap Zg~ "g~" . getrecorded() noremap Zv "v" . getrecorded() " The last recorded object. onoremap Zr getrecorded() " Zgi -- start another insert using the same parameters from the last Z[ia] noremap Zgi c(insert-about-obj) vnoremap Zi "\'<" . (visualmode() == "V" ? "0" : "") . "i" vnoremap Za "\'>" . (visualmode() == "V" ? "$" : "") . "a" vnoremap Z[ "\'<" . (visualmode() == "V" ? "0" : "") . "i" vnoremap Z] "\'<" . (visualmode() == "V" ? "0" : "") . "i" vnoremap Zo "\'>o" vnoremap ZO "\'(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 prepare_operation('i', "c\(insert-about-obj)") \call start_recording() \set operatorfunc=recordopg@ noremap (append-after-motion) \ call prepare_operation('a', "c\(insert-about-obj)") \call start_recording() \set operatorfunc=recordopg@ noremap (open-after-motion) \ call prepare_operation('o', "c\(insert-about-obj)") \call start_recording() \set operatorfunc=recordopg@ noremap (OPEN-after-motion) \ call prepare_operation('O', "c\(insert-about-obj)") \call start_recording() \set operatorfunc=recordopg@ nnoremap (to-object-start) \ call prepare_operation('[', "\(to-object-pos-post)") \call start_recording() \set operatorfunc=recordopg@ nnoremap (to-object-end) \ call prepare_operation(']', "\(to-object-pos-post)") \call start_recording() \set operatorfunc=recordopg@ " The Z[ and Z] commands as text motions. These are done in a similar hacky way as other plugin " mappings in this file " " What they do, is: " " 1. prepare the operation, and prepare to send \(to-object-pos-post) " 2. escape operator-pending mode. This mapping is not the actual mapping we want to use for the " operation, we just use this as a dummy to set up all the variables needed for the actual " operation. " 3. start the recording macro. " 4. enter operating-pending mode again. This will record the macro. Once the user has entered the " operator, it will then call the \(to-object-pos-post). This is the command " that we really want to be able repeat with the dot (.) operator. onoremap (to-object-start) \ call prepare_operation('[', printf("%s\(to-object-pos-post)", v:operator)) \ \call start_recording() \set operatorfunc=recordopg@ onoremap (to-object-end) \ call prepare_operation(']', printf("%s\(to-object-pos-post)", v:operator)) \ \call start_recording() \set operatorfunc=recordopg@ onoremap (to-object-pos-post) call to_object_start() nnoremap (to-object-pos-post) call to_object_start() 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' function! s:start_recording() exec "normal! q" . s:reg_to_clobber endfunction let s:operate_keys='' function! s:prepare_operation(instype, operate_keys) " 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 let s:operate_keys = a:operate_keys endfunction " 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:to_object_start() abort set operatorfunc=s:save_object exec "normal g@" . s:recorded let pos = s:instype == '[' ? s:object.start : s:object.end call setpos('.', pos) endfunction function! s:insert_before_recorded() abort let l:instype = s:instype " 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, but if s:recorded =~ '^[ia]' || s:recorded =~ 'g[nN]' || s:recorded =~ '[_]' " 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 l:instype == 'a' call setpos(".", s:object.end) if s:object.type == 'line' normal! $ endif elseif l:instype == 'i' call setpos(".", s:object.start) if s:object.type == 'line' normal! 0 endif if s:object.start[2] > col(".") " Trying to insert at the $. Needs to be append. let l:instype = 'a' endif elseif l:instype == 'o' call setpos(".", s:object.end) elseif l:instype == 'O' call setpos(".", s:object.start) endif else exec "normal " . s:recorded endif if l:instype == 'a' exec "normal! \a \v" elseif l:instype == 'i' exec "normal! \i \v" elseif l:instype == 'o' exec "normal! \o \v" elseif l:instype == 'O' exec "normal! \O \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) " A limitation of normal! is that it can't start with a space, so prefix with " a 1 to get around this if s:recorded =~ '^\s' let s:recorded = "1" . s:recorded endif " 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(s:operate_keys) endfunction function! s:getrecorded() return s:recorded endfunction