0

Is there an insert and append operation that can be followed by a text-object? I've found this plugins:

https://github.com/deris/vim-operator-insert https://github.com/mwgkgk/vim-operator-insert

But they seem to be unmaintained. I think these operators are useful as they are easily repeatable, if you gaiw (go append in word) you can add something and go to another word and also append it (just pressing .), just like with change.

These plugins unfortunately not working with the . operator and they also fail to work in visual mode. Is there any available solution? Is there a reason why this is not interesting?

  • ciwTEXT<C-r>" -- operator insert, ciw<C-r>"TEXT -- operator append (though not repeatable). – Maxim Kim Oct 21 '19 at 10:28
  • @MaximKim macro repeatable though – D. Ben Knoble Oct 21 '19 at 10:30
  • having to record a macro is a lot more keystrokes, my goal with this is that ti shall be as intuitive as other operators, so I can literally speak with vim. If I want to append something at the end of a quote, I want to be able to do gaiq (go append in quote), I find operators really powerful @D.BenKnoble – Blasco Oct 21 '19 at 11:55
  • @Blasco not disagreeing. – D. Ben Knoble Oct 21 '19 at 12:13
2

You can come up with something relatively simple, following and tinkering what vim has described in :h :map-operator:

As an example I came up with in about 15 minutes of experimenting:

nmap <silent> gi :set opfunc=InsertToTextObject<CR>g@
vmap <silent> gi :<C-U>call InsertToTextObject(visualmode(), 1)<CR>

nmap <silent> ga :set opfunc=AppendToTextObject<CR>g@
vmap <silent> ga :<C-U>call AppendToTextObject(visualmode(), 1)<CR>

function! InsertToTextObject(type, ...)
    let sel_save = &selection
    let &selection = "inclusive"
    let reg_save = @@

    if a:0  " Invoked from Visual mode, use gv command.
        silent exe "normal! gv"
    else
        silent exe "normal! `["
    endif

    startinsert

    let &selection = sel_save
    let @@ = reg_save
endfunction


function! AppendToTextObject(type, ...)
    let sel_save = &selection
    let &selection = "inclusive"
    let reg_save = @@

    if a:0
        silent exe "normal! `>"
        call feedkeys('a', 'n')
    elseif a:type == 'line'
        silent exe "normal! ']"
        call feedkeys('A', 'n')
    else
        silent exe "normal! `]"
        call feedkeys('a', 'n')
    endif

    let &selection = sel_save
    let @@ = reg_save
endfunction

Insert to text object is really simple -- you just goto [ mark and startinsert

Append was a bit more difficult, you have to feedkey there, either a or A depending on linewise selection mode.

See it in action:

enter image description here

  • with tpope/repeat, you can make this dot-repeatable (I think) – D. Ben Knoble Oct 21 '19 at 19:26
  • Great work, amazing! I think the plugin I linked does something similar. The problem is that it is not repeatable, I'm trying to add tpope/repeat support as suggested by @D.BenKnoble – Blasco Oct 22 '19 at 5:50
  • Making it repeatable doesn't seem to be straight forward. This is some advice from tpope github.com/tpope/vim-repeat/issues/75 – Blasco Oct 22 '19 at 5:53
  • @D.BenKnoble Almost there, I've post an answer. I manage to repeat the a different operator which pastes the content of the @. register, this is repeatable, but we need to include the range the first time we want to repeat, which is no that bad. – Blasco Oct 23 '19 at 8:24
0

I think I'm almost there, maybe can use this as inspiration to make it work. I'm making use of kana/operator-user as in the suggested plugins.

The problem is entering insert mode during the repetition, so I've created a different operator to repeat using paste:

if exists('g:loaded_operator_insert')
  finish
endif
let g:loaded_operator_insert = 1

let s:save_cpo = &cpo
set cpo&vim


call operator#user#define('insert-i', 'operator#insert#insert_i')
call operator#user#define('insert-a', 'operator#insert#insert_a')
call operator#user#define('insert-i-repeat', 'operator#insert#insert_i_repeat')
call operator#user#define('insert-a-repeat', 'operator#insert#insert_a_repeat')


let &cpo = s:save_cpo
unlet s:save_cpo

Which work like this:

let s:save_cpo = &cpo
set cpo&vim

" Public API {{{1

function! operator#insert#insert_i(motion_wise)
    return s:operator_insert_i(a:motion_wise)
endfunction

function! operator#insert#insert_a(motion_wise)
    return s:operator_insert_a(a:motion_wise)
endfunction

function! operator#insert#insert_i_repeat(motion_wise)
    return s:operator_insert_i_repeat(a:motion_wise)
endfunction

function! operator#insert#insert_a_repeat(motion_wise)
    return s:operator_insert_a_repeat(a:motion_wise)
endfunction

"}}}

" Private {{{1

function! s:operator_insert_i_repeat(motion_wise)
    let s:original_pos = getpos("'[")[1:]
    call cursor(s:original_pos)
    normal! ".P
endfunction

function! s:operator_insert_a_repeat(motion_wise)
    call cursor(getpos("']")[1:])
    if col("']") >= col("$") - 1
        normal! ".p
    else
        normal! l
        normal! ".p
    endif
endfunction

function! s:operator_insert_i(motion_wise)
    let s:original_pos = getpos("'[")[1:]
    call cursor(s:original_pos)
    startinsert
    silent! call repeat#set("\<Plug>(operator-insert-i-repeat)", -1)
endfunction

function! s:operator_insert_a(motion_wise)
    call cursor(getpos("']")[1:])
    if col("']") >= col("$") - 1
        startinsert!
    else
        normal! l
        startinsert
    endif
    silent! call repeat#set("\<Plug>(operator-insert-a-repeat)", -1)
endfunction

"}}}

let &cpo = s:save_cpo
unlet s:save_cpo

Now it does work, but when we do the . we need to reenter the first time the text object. It is not ideal, but something is something.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.