I'm often using <C-u> and <C-d> to quickly navigate around in my code (I don't like using { and } since they're hard to type on my layout). One thing that annoys me is that <C-u> and <C-d> don't interact with the jump list.

I know I could fix this by something like

nnoremap <C-u> m'<C-u>
nnoremap <C-d> m'<C-d>

but that would of course extend the jump list every time, which is annoying as well. I only care about the position that I started from, not the intermediate ones.

I thought that the best way to fix this would be that <C-u> and <C-d> only extend the jump list if the previous command was not <C-u> or <C-d>. This way, it wouldn't clutter the jump list but I could still jump back to where I started from. Unfortunately, I haven't found a way yet to check what the last command I typed was.

  • 1
    If you know you are going come back you can just put down a mark, e.g. mm. However, i find that many times I was writing code in a place, then I needed to look somewhere. So I either open a new window to read/review another area or I use g; to move backwards through the change-list. Commented May 5, 2021 at 13:31

1 Answer 1


This code implements what you suggest, saving the initial position into the jump list when using a series of Ctrl+D or Ctrl+U motions.

function! SaveJump(motion)
  if exists('#SaveJump#CursorMoved')
    autocmd! SaveJump
    normal! m'
  let m = a:motion
  if v:count
    let m = v:count.m
  execute 'normal!' m

function! SetJump()
  augroup SaveJump
    autocmd CursorMoved * autocmd! SaveJump
  augroup END

nnoremap <silent> <C-u> :<C-u>call SaveJump("\<lt>C-u>")<CR>:call SetJump()<CR>
nnoremap <silent> <C-d> :<C-u>call SaveJump("\<lt>C-d>")<CR>:call SetJump()<CR>

The way this works is by creating a CursorMoved autocmd to track whether the cursor was moved in between a sequence of Ctrl+D and Ctrl+U movements.

Since using a Ctrl+D/Ctrl+U will create the autocmd, we don't need an additional flag to tell whether the last motion was a Ctrl+D/Ctrl+U, we can simply check whether the autocmd exists. If it does, we just remove it, if it doesn't, then we know we're the first of a sequence, so we set a mark with m' to get us into the jumplist.

The autocmd itself just needs to reset itself, since it's triggered for a motion other than Ctrl+D/Ctrl+U, so just dropping the autocmd will make the next Ctrl+D/Ctrl+U recognize it's the first of a sequence.

Note that the code had to be broken into two separate functions, since the first function (actually executing the motion itself) needs to complete in order for the motion to have effect. Setting the autocmd in that same function has the effect of it triggering right as the function completes, which prevents us from using it to detect repeated motions of the same kind...

This idea could be extended, for instance to check whether the cursor has been moved within the same line and not reset the motion block in that case. (For example, if you'd like to allow for horizontal motion, perhaps to use $ to inspect the end of a line under nowrap in between a sequence of Ctrl+D/Ctrl+U motions.) It shouldn't be hard to extend it to cover other similar motions as well.

  • 2
    That works perfectly, thank you so much!
    – T-Rex96
    Commented May 7, 2021 at 9:57
  • 2
    This is amazing. Thank you!
    – kohane15
    Commented Jul 1, 2022 at 5:02
  • 1
    You are a vim wizard fibranden! For anyone using neovim, this works wonders along with: vim.opt.jumpoptions = "stack"
    – Jose V
    Commented Jun 28, 2023 at 7:38

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.