Yank a region in VIM without the cursor moving to the top of the block?
Asked Answered
O

8

86

Is there a simple way (i.e. without writing a script or elaborate keymap sequence) to Yank a group of lines and leave the cursor wherever the Yank was performed, as opposed to at the start of the block?

According to VIM's help: "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." Line-wise seems to behave similarly.

This is a bit annoying for me since I tend to select a large region from top to bottom, Yank, and then paste near or below the bottom of the selected region. Today I'm setting a mark (m-x) just before Yank and then jumping back, but I suspect there may be a different Yank sequence that will do what I need.

I've searched SO and the web for this numerous times. There is so much existing "VIM shortcuts" material to wade through yet I've not found a solution to this one yet.

Thanks in advance.

Occidental answered 27/9, 2010 at 18:18 Comment(2)
Can you give an example of a yank command that changes the cursor position?Heinrick
I'm on line 1 and press V to start Visual Line mode. I move down 10 lines and press y to yank the region. After that command I'm back at line 1. Maybe the "thing" I moved during select isn't a technically the cursor, noneless it is where I'd like to end up when the yank is done. (So maybe the question should have read "how do I move the cursor after...")Occidental
M
90

Not quite answering your question, but perhaps '] would solve your problem?

 ']  `]         To the last character of the previously changed or
                yanked text.  {not in Vi} 
Mollescent answered 27/9, 2010 at 18:29 Comment(5)
Nice tip and definitely better than what I've been doing.Occidental
Which leads to: vmap y y`]Contortionist
@LVB, @Curt, no, this mapping does not really work great: if i yank from bottom to top, then my cursor will be moved back to bottom. Try mapping that @Greg Hewgill implicitly suggested: :vmap y ygv<Esc> . This one, really, works like a charm.Joell
obviously (but should be mentioned) '[ goes to the first character of the previously yanked/pasted text.Been
Documented in :h motion.txt.Yestreen
H
59

If you're using visual blocks (v), then after yanking the block you can use gv to re-select the same block (which also moves your cursor position back to where it was before yanking). If you then press Esc, the block is un-selected without moving the cursor.

Also of interest might be the ctrl-o command in visual block mode, which jumps between the start and end of the selected block.

Heinrick answered 27/9, 2010 at 18:26 Comment(3)
Oooh, @Greh Hewgill, thank you, thank you very much! I tried to find the solution so long, and so many times i've seen suggestions like zigdon provided: trick with '], but it does not work properly: if i yank from bottom to top, then my cursor will be moved back to bottom. But now, after reading you post, i defined mapping :vmap y ygv<Esc> , and it works like a charm. Great thanks again.Joell
I found out that this mapping causes flashing, especially on large yanks. To avoid this, you can do this: :set lazyredrawJoell
Unfortunately this causes a jump to the view for yanks that extend multiple lines beyond the current view when yanking top to bottom.Burrus
C
11

If yanking in visual mode, you could also use '> or `> to go to the last line/character of the just yanked visual selection. In this context this is essentially the same as '] and `] which is apparently not supported e.g. in VsVim.

Chalice answered 16/8, 2016 at 20:20 Comment(0)
M
2

:y3 will yank three whole lines from current current line, If you know exactly how many line to yank, this command is very handy. :help :yank for details.

:%y will select the whole buffer without moving the cursor, like ggvG$y, without the flash of selection highlight and modifying the "* register.

I use this insert mode map:

function! SelectAll()
  %y*
endfun
imap <expr> <F3> SelectAll()

ps: if you prefer <C-V> to paste(outside vim), use %y+

check https://mcmap.net/q/53869/-copy-all-the-lines-to-clipboard

Mallorie answered 12/1, 2014 at 13:4 Comment(1)
Besides :y3, y3y also works the same.Carangid
S
2

I'm not sure sure if YankRing has changed since the vmap ygv<Esc> solution was posted but that didn't persist for me after adding it to my .vimrc. YankRing actually overwrote it.

Here's my solution in .vimrc.

function! YRRunAfterMaps()
  vmap y ygv<Esc>
endfunction
Shark answered 28/4, 2014 at 20:3 Comment(0)
M
1

I don't know what happened in the meantime but in IdeaVim the accepted answers don't work as I'd like them to. (Don't move the cursor on yank)

For me what did the trick was just setting a mark before yank and go to that afterwards.

vmap y mxy`x
Machiavelli answered 20/11, 2018 at 14:44 Comment(1)
i used this, but modified to avoid infinite-loop recursion (may not have that behavior depending on your remap settings): vnoremap y mxy`xCalotte
A
1

If you are using Lua, you can define this keybinding, this will yank and go back to position before yanking:

vim.api.nvim_set_keymap("x", "y", "ygv<Esc>", { noremap = true, silent = true })
Abarca answered 5/1 at 18:11 Comment(0)
B
0

Use the ModeChanged event and vim.v.operator variable.

local api = vim.api
local autocmd = api.nvim_create_autocmd
local augroup = api.nvim_create_augroup

local g = augroup("user/keep_yank_position", { clear = true })

autocmd("ModeChanged", {
    pattern = { "n:no", "no:n" },
    group = g,
    callback = function(ev)
        if vim.v.operator == "y" then
            if ev.match == "n:no" then
                vim.b.user_yank_last_pos = vim.fn.getpos(".")
            else
                if vim.b.user_yank_last_pos then
                    vim.fn.setpos(".", vim.b.user_yank_last_pos)
                    vim.b.user_yank_last_pos = nil
                end
            end
        end
    end,
})

autocmd("ModeChanged", {
    pattern = {
        "V:n",
        "n:V",
        "v:n",
        "n:v",
    },
    group = g,
    callback = function(ev)
        local match = ev.match
        if vim.tbl_contains({ "n:V", "n:v" }, match) then
            -- vim.b.user_yank_last_pos = vim.fn.getpos(".")
            vim.b.user_yank_last_pos = vim.api.nvim_win_get_cursor(0)
        else
            -- if vim.tbl_contains({ "V:n", "v:n" }, match) then
            if vim.v.operator == "y" then
                local last_pos = vim.b.user_yank_last_pos
                if last_pos then
                    -- vim.fn.setpos(".", last_pos)
                    vim.api.nvim_win_set_cursor(0, last_pos)
                end
            end
            vim.b.user_yank_last_pos = nil
            -- end
        end
    end,
})

Birdseed answered 21/5 at 6:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.