TL;DR use autocommands, see the end of this post.
Here's the result of the investigation I've made:
bn call the
ex_bnext function which does two things:
- Go to the desired buffer via the
- Execute any command line command passed to the
goto_buffer function apparently wrap the more generic
do_buffer function and calls it with special argument related to the action of going to a buffer (see the first argument is the action which is either
do_buffer function is pretty long, but you can see the interesting part around the end. This function calls the
set_curbuf function set the current buffer to the desired buffer and calls
enter_buffer function set/reset some options for the newly displayed buffer and calls
scroll_cursor_halfway function changes the topline value, meaning the cursor will be displayed halfway the window.
Sooo, I think it's the designed behavior.
What you can do is, when leaving a buffer, save the current window display with
winsaveview and on entering a buffer, reset the view with autocommands:
autocmd! BufWinLeave * let b:winview = winsaveview()
autocmd! BufWinEnter * if exists('b:winview') | call winrestview(b:winview) | unlet b:winview
zbdo not move the cursor, they just move the window view. On the contrary
<C-d>is moving the cursor.