7

Vim help's change.txt states that

Note that after a characterwise yank command, Vim leaves the cursor on the first yanked character that is closest to the start of the buffer. This means that "yl" doesn't move the cursor, but "yh" moves the cursor one character left.

Rationale:

In Vi the "y" command followed by a backwards motion would sometimes not move the cursor to the first yanked character, because redisplaying was skipped. In Vim it always moves to the first character, as specified by Posix.

With a linewise yank command the cursor is put in the first line, but the column is unmodified, thus it may not be on the first yanked character.

Sure enough, if I have my cursor at V in someVerySpecificSubclass and do a yiw, the cursor will move to the start of the word.

I would like to yank this word without having the cursor move, because a lot of the time I want to change someVerySpecificSubclass to someSubclass but I'll need someVerySpecificSubclass somewhere else in the code.

This movement is not added to the jumplist, so I can't jump back with CTRL-o. I can force adding it to the jumplist with m', so I could m'yiw<C-o>, but that is somewhat long (and I'm not sure how to map it properly).

In any case, it doesn't make sense to me that a yank operation would move the cursor at all.

Related questions:

None of the solutions provided work for my use case, they all seem to assume you want the cursor to be placed at the end of the yanked text, not necessarily at the original position, which can (and in my case is) somehwere in the middle of the yanked text.

1
  • 2
    I'm not sure how to turn this behavior off in general, but if all you want to do is map that specific case you could do that with something like the following: noremap <C-i> m'yiw`` .
    – Tumbler41
    Commented Jun 23, 2016 at 16:05

4 Answers 4

3

Here's some vimscript I whipped up that seems to work pretty well. (disclaimer: I have not done extensive testing on this. I just threw it together in a couple minutes)

function! YankNoMove()
  let repcount = v:count
  if v:count == 0
    let repcount = 1
  endif
  exe "normal! m'".repcount."y".nr2char(getchar())."^O"
endfunction

function! YankObjectNoMove(object)
  let repcount = v:count
  if v:count == 0
    let repcount = 1
  endif
  exe "normal! m'".repcount."y".a:object.nr2char(getchar())."^O"
endfunction

nnoremap <silent> y :<c-u>call YankNoMove()<cr>
nnoremap <silent> yi :<c-u>call YankObjectNoMove('i')<cr>
nnoremap <silent> ya :<c-u>call YankObjectNoMove('a')<cr>

(Note, ^O is the control character <C-o> and I typed it with <C-v><C-o>)

This works the way you thought of, but neatly wraps it up in a vimscript function. It works with counts, but only if the count is before the yank. For example, 2yw works, but y2w does not. This will also only work with basic motions like yb, yw, yiw, etc. It will not work with y/foo or yfn, and unfortunately, it also breaks them. You could get around this by doing

nnoremap yf yf
nnoremap yF yF
nnoremap yt yt
nnoremap yT yT
nnoremap y/ y/
nnoremap y? y?

Although this is not the ideal solution. Probably the easier way would just be to treat YankNoMove as an alternate operator, separate from y and do

nnoremap <silent> <leader>y :<c-u>call YankNoMove()<cr>
nnoremap <silent> <leader>yi :<c-u>call YankObjectNoMove('i')<cr>
nnoremap <silent> <leader>ya :<c-u>call YankObjectNoMove('a')<cr>

Obviously, you can choose your own key sequence if you do not like <leader>y

6
  • Better yet, write an object for kana's textobj-user framework.
    – lcd047
    Commented Jun 23, 2016 at 16:58
  • @lcd047 I don't think that addresses the problem of the cursor moving though.
    – DJMcMayhem
    Commented Jun 23, 2016 at 17:01
  • It doesn't (not by itself anyway). What I'm saying is, if you're using the framework you automatically gain access to all sorts of other goodies.
    – lcd047
    Commented Jun 23, 2016 at 17:07
  • Sorry, I messed up that comment and deleted it for clarity. Thanks! I'll be using this mapping as a workaround: nnoremap <expr> yi ":normal! m'".v:count1."yi".nr2char(getchar())."^O^M This does basically the same thing you do with your yi mapping, but using the v:count1 variable, which does exactly what your repcount does. Also, I hardcoded the a:object as "i", since I'll be using it only once per mapping (for yi and ya). I'm not super experienced with vimscript either, I wouldn't have been able to write this mapping without your initial suggestion. Thanks again!
    – Vitor Eiji
    Commented Jun 23, 2016 at 17:29
  • 1
    @DJMcMayhem <expr> treats the right hand side of your mapping as an expression, evaluates it and maps the left hand side to the resulting string of that expression. For example, map <expr> zzz ggdG is the same as map zzz "gg"."dG".
    – Vitor Eiji
    Commented Jun 23, 2016 at 17:46
2

You could also do it this way:

vnoremap <A-c> ygv<Esc>a
xnoremap <A-c> ygv<Esc>a
snoremap <A-c> ygv<Esc>a
y gv <Esc> a
^  ^   ^   ^
|  |   |   |____ go back to insert mode (if you wanted to quickly edit)
|  |   |      
|  |   |________ get out of visual/selection mode      
|  |         
|  |____________ select previous selection         
|           
|_______________ yank (copy)           

1

Native vim solution Update:

Source: https://vi.stackexchange.com/a/38180/7339

augroup yank_restore_cursor
    autocmd!
    autocmd VimEnter,CursorMoved *
        \ let s:cursor = getpos('.')
    autocmd TextYankPost *
        \ if v:event.operator ==? 'y' |
            \ call setpos('.', s:cursor) |
        \ endif
augroup END

Using the Preserve function

if !exists('*Preserve')
    function! Preserve(command)
        try
            let l:win_view = winsaveview()
             "silent! keepjumps keeppatterns execute a:command
            silent! execute 'keeppatterns keepjumps ' . a:command
        finally
            call winrestview(l:win_view)
        endtry
    endfunction
endif

Copy a paragraph whithout moving the cursor

:call Preserve('normal yap')

If we could wrap this idea in a more generic solution would be gret

0

I have come up with a simpler solution using autocmd event TextYankPost, it works fine for me:

nnoremap y myy

augroup restore_after_yank
  autocmd!
  autocmd TextYankPost * silent! normal `y
augroup END

my will set a mark y to current cursor poistion. In the autocmd, we restore the cursor position using `y.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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