I often find I want to quickly select all the characters in an aligned column, something like this:

this.p           = obj.['propery'];
this.prop        = obj.['proper'];
this.proper      = obj.['prop'];
this.property    = obj.['p'];

Say I want to select the .s with visual block selection (CtrlV). If it's not immediately obvious how many rows there are (perhaps there are more), I typically use { or } once in visual block select mode to jump the appropriate distance. However, they go one line too far; if the following line is blank the cursor move to the first column, selecting far more than I want.

Is there an equivalent to { or } which jumps to the last line in the paragraph, while maintaining the current column?

My question is similar to How do I move by paragraphs, but end up *in* the paragraph? but the answer given there (mapping j or k after a { or }) won't work for me; the column will be lost.

  • 1
    I often have the same problem. I have found that setting 'relativenumber' can be a great help in getting it right the first time around.
    – filbranden
    Aug 1, 2019 at 1:12
  • @filbranden Do you find it gets in the way of using absolute line numbers to say, jump to lines mentioned in error messages?
    – gandalf3
    Aug 1, 2019 at 1:13
  • 2
    Not really, since in that case I'll use a command (e.g. 346G) or the quickfix window. You can also use both 'relativenumber' and 'number' together, in which case it will show the absolute line for the current line, and relative around it. But yeah, I'd be curious to see if anyone has a better suggestion on how to handle this!
    – filbranden
    Aug 1, 2019 at 1:22

2 Answers 2

vnoremap <silent>x :<C-U>call cursor(line("'}")-1,col("'>"))<CR>`<1v``


  • vnoremap <silent>x --- remap something useless in visual mode (x is the same as d)
  • :<C-U> --- drop visual line range (also switches to normal mode)
  • call cursor(...)<CR> --- set cursor position
    • line("'}")-1 --- the last line of the current paragraph (except if it's the last paragraph in the whole file, but it's not that important, I think; use line("'}") - empty(getline(line("'}"))) if it is)
    • col("'>") --- the end column of the visual selection
  • `<1v --- restore visual mode
  • `` --- jump back to the latest jump, i.e. extend selection till that cursor() position
  • You can handle the last paragraph if you replace line("'}")-1 with line("'}") - (line("'}") == line("$") ? 0 : 1). Aug 1, 2019 at 6:12
  • @JürgenKrämer In fact, I wanted first to do line("'}") - (line("'}") != line("$")) (which is basically the same thing). But the problem is that the last line can be empty too.
    – Matt
    Aug 1, 2019 at 6:38
  • @JürgenKrämer So a "bullet-proof" version should be: line("'}") - empty(getline(line("'}")))
    – Matt
    Aug 1, 2019 at 6:42
  • Sorry, but maybe I don't understand you first comment. If the last line is empty, the last line of the last paragraph is not the last line of the buffer and subtracting 1 will work. Aug 1, 2019 at 6:43
  • @JürgenKrämer If it's empty then line("'}") == line("$"), therefore your expression yields the number of the last (empty) line.
    – Matt
    Aug 1, 2019 at 6:45

I have the same frustration, and I've come up with:

vnoremap <expr> } mode()=~'\cv' ? '}' : line("'}")-line('.')-1..'j'

This remaps the } key so that it behaves normally except when in visual-block mode. In visual-block mode it jumps to the line before the next blank line (and stays in the same column).

It is an expr map, so it evaluates to a string every time it is invoked. This string becomes the mapping.

In non-block visual modes, it evaluates to }, so no change.

In block-visual mode it evaluates to #j where # is line("'}")-line('.')-1

  • line("'}") line number for end of paragraph
  • line('.') line number for current line
  • -1 one line before

You can change the -1 to empty(getline(line("'}"))) (as per Matt's answer) to get this to work with paragraphs at the end of a file. Like this:

vnoremap <expr> } mode()=~'\cv' ? '}' : line("'}")-line('.')-empty(getline(line("'}")))..'j'

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.