Given the following setup

  • edit
a long line
  • move the cursor to line 1, column 9 (> 6 anyway)
  • move down and up again

I see that when moving to the short line, the column becomes at most 6 (5 in normal mode), but when moving back up, vi remembers that I used to be in column 9.

How is this possible, are there two cursor positions, actual & desired? g_Ctrl-g shows nothing unusual. set virtualedit? is empty.

  • The only direct question I see in your post is "How is this possible?" There's not much to say to that...it's how things work. So I added a whole answer assuming that you'd like a workaround. :) If you're looking for something else please add to your question. – B Layer Feb 27 at 6:49
  • True. I'm thinking about "messing with" cursor movement (maps/plugin) and wanted to understand things better – usretc Feb 27 at 7:33

How is this possible, are there two cursor positions, actual & desired?

Yes, see :h getcurpos()

  Get the position of the cursor.  This is like getpos('.'), but
  includes an extra "curswant" item in the list:
      [0, lnum, col, off, curswant]
  The "curswant" number is the preferred column when moving the
  cursor vertically.

Also :h cursor() says

  When there is one argument {list} this is used as a |List|
  with two, three or four item:
      [{lnum}, {col}]
      [{lnum}, {col}, {off}]
      [{lnum}, {col}, {off}, {curswant}]
  This is like the return value of |getpos()| or |getcurpos()|,
  but without the first item.

BTW. off is extra "virtual offset" that makes sense only if 'virtualedit' is not empty.

  • I always found that terminology bizarre. First, why "curswant" and not "colwant". And who is doing the wanting in "curswant"? That value could come from Vim (getcurpos()) or it could come from the user (cursor()). Call them both col or maybe currcol and col... something normal-sounding. :P Thankfully, didn't have to mention it in my answer. :) – B Layer Feb 27 at 8:42
  • Who knows, someday there will be a need for linewant when... moving across columns... or something, and then curswant can store both. Anyway, this is what I was looking for. But thanks for all the ideas about interactive usage, @BLayer. – usretc Feb 27 at 14:02

How is it possible? Because it's implemented that way. ;)

Vim help details this is at the end of the :h up-down-motions section and it says:

[j and k] put the cursor in the same column (if possible) as it was after the last command that changed the column

Two things that I take from this are

  1. There is no mention of a builtin alternative that goes straight up/down, and Vim's help is usually pretty good about noting such things. That suggests there isn't one. (I thought for a moment Up and Down arrows would behave differently but they appear to have the same effect. They're off the home row anyways.)
  2. Since it mentions "the last command that changed the column" that suggests you can precede k with something like hl and you'll remain in the column.

So if that's something you prefer you could add to your vimrc:

nnoremap j hlj
nnoremap k hlk

There's a problem, though. That won't work if the cursor is at the beginning of the line. You'll be in the second column since h can't actually move the cursor. (If you used lh instead it would break at the end of the line.)

This works better (and has a less "hacky" feel to it)...

nnoremap j :call cursor(getcurpos()[1]+1, 0)<CR>
nnoremap k :call cursor(getcurpos()[1]-1, 0)<CR>

The second parameter is column number. A value of 0 there means you want to stay in the same column.

Be forewarned, though, that this may act funky in some circumstances. For instance, if you're leaning on j or k to scroll up/down a bunch of lines (which, to be honest, means you rely on j/k way too much) then once you hit an empty line the cursor will remain in column 1 the rest of the way.

So if there are things that bother you a better idea might be to use some unmapped keys and leave the native commands as is. Use the mappings only when you're in a situation where you definitively want to apply straight up/down movement. One easy-to-remember alternative is nnoremap <leader>j ....

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.