0

Consider the following text file.

1 1 3
1
1
5

One can rank row 1 with the command y$ with the cursor on row 1, column 1. However, to yank the first column, the only way I know is to <C-v>3jy.

Mostly out of curiosity, is it possible to yank the column without ever entering visual block mode?

1
  • At a glance, yank all the lines via :line,line yank, paste them, and delete (possibly via :subsitute) all the characters except a specific column. Can write an answer later perhaps.
    – D. Ben Knoble
    May 23, 2019 at 23:45

3 Answers 3

2

You can create a blockwise text object to do this kind of job, if you bound it to ic , you can copy with yic, select it with vic, no count needed. But it's not easy, feel free to skip it and use some existing plugins, forgot the plugin name, you can google it.

Text object can be created directly or from a motion. I prefer motion in this case, there has no vertical word style motion in vim, you must create it by yourself. B Layer gives some good idea on how to do it here. My code is based on his:

" vertical E,W,B. it's too much trouble to implement e,w,b.
function! misc#mo#vertical_motion(motion)

  " changed from https://vi.stackexchange.com/questions/15151/move-to-the-first-last-non-whitespace-character-of-the-column-vertical-w-b-e
  let curcol = virtcol('.')
  let nextcol = curcol + 1

  if a:motion ==# 'E'

    " %dv : restrict virtual column
    " \S.* : start from non-blank character
    " (\n.*%%<%dv$|\n.*%%%dv\s|%%$)
    "   \n.*%%<%dv$ : next line that's too short
    "   \n.*%%%dv\s : next line that's empty in this column
    "   %%$         : end of file
    let pattern = printf('\v%%%dv\S.*(\n.*%%<%dv$|\n.*%%%dv\s|%%$)', curcol, nextcol, curcol)
    let flag = 'W'
  elseif a:motion == 'B'
    let pattern = printf('\v^.*(%%<%dv$|%%%dv\s.*)\n.*\zs%%%dv\S', nextcol, curcol, curcol)
    let flag = 'bW'
  elseif a:motion == 'W'
    let pattern = printf('\v^.*(%%<%dv$|%%%dv\s.*)\n.*\zs%%%dv\S', nextcol, curcol, curcol)
    let flag = 'W'
  else
    echohl ErrorMsg | echo 'Not a valid motion: ' . a:motion | echohl None
  endif
  call search(pattern, flag)
endfunction

I bind these motions to sequence starts with , :

nnoremap ,e :call misc#mo#vertical_motion('E')<cr>
nnoremap ,w :call misc#mo#vertical_motion('W')<cr>
nnoremap ,b :call misc#mo#vertical_motion('B')<cr>
vnoremap ,e :<c-u>exec 'norm! gv' <bar> call misc#mo#vertical_motion('E')<cr>
vnoremap ,w :<c-u>exec 'norm! gv' <bar> call misc#mo#vertical_motion('W')<cr>
vnoremap ,b :<c-u>exec 'norm! gv' <bar> call misc#mo#vertical_motion('B')<cr>
onoremap ,e :normal v,e<cr>
onoremap ,w :normal v,w<cr>
onoremap ,b :normal v,b<cr>

After that, it's trivial to create text object:

function! misc#to#verticalE() abort
  exec "norm! \<c-v>"
  call misc#mo#vertical_motion('E')
endfunction

vnoremap ic :<c-u>call misc#to#verticalE()<cr>
onoremap ic :normal vic<cr>
1
  • +1 for the shout out. I have fond memories of that answer. ;)
    – B Layer
    May 25, 2019 at 2:47
1

y<c-v>G

This yanks to the bottom of the file, forcing block mode. This only works in some cases, when set nostartofline and when the last line isn't blank.

Alternatively, to yank a set number of lines, keeping horizontal position if possible,

y<c-v>3j.

3
  • 1
    As G place cursor on the 1st non-blank character, it only works under certain circumstance.
    – dedowsdi
    May 24, 2019 at 0:47
  • 1
    @dedowsdi, good point. but look at the option nostartofline
    – Mass
    May 24, 2019 at 1:14
  • Thanks, didn't know that.
    – dedowsdi
    May 24, 2019 at 1:22
0

Since you specifically mentioned that this is out of curiosity, here is a preposterous solution you would never use in real life:

:call setreg('"', '') | g/^/call setreg('"', nr2char(strgetchar(getline('.'), 0)), 'ab')

The above assumes you want yank all the way down to the bottom of the file. If you just want the first four lines of a longer file, then one solution is to specify a range:

:call setreg('"', '') | 1,4g/^/call setreg('"', nr2char(strgetchar(getline('.'), 0)), 'ab')
                        ^^^

If you like this, I can think of several other ridiculous solutions to the problem.

Your Answer

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

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