4

When I press zz at the end of file, the line is displayed at the center of screen by having many empty lines

zz

having empty lines beyond end of file is great, but this is too much. Is there a way to limit the number of empty lines being displayed with zz when approaching the end of file? Say I want at most 10 empty lines beyond the end of file, if zz can not center a line using no more than 10 empty lines, so be it.

Please let me know if I did not make myself clear. Thank you in advance!

3
  • 1
    I think zb will do what you want? You could remap zz to call zb if it would display empty lines and zz otherwise, but not sure if it's worth it 😅 Jun 16, 2020 at 3:31
  • @MartinTournoij Sorry I was not quite clear, I have updated my question! Thanks!
    – zyy
    Jun 16, 2020 at 4:18
  • :h map-<expr> and :h line(), plus a bit of imagination...
    – Matt
    Jun 16, 2020 at 5:25

2 Answers 2

5

If you're willing to bring a function into the mix I whipped this up...

func! ModifiedZZ()
    " modify to your liking
    let l:max_pad_lines = 10

    norm! zz
    let l:to_scr_end = winheight(0) - winline()
    let l:to_buf_end = getpos('$')[1] - getpos('.')[1]

    let l:adjustment = l:to_scr_end - l:to_buf_end - l:max_pad_lines
    if l:adjustment > 0
        exe "norm! " . l:adjustment . "\<c-y>"
    endif
endfunc

Use it with this override of zz...

nnoremap zz :call ModifiedZZ()<cr>

The script runs zz then calculates how many padding lines beyond the buffer are visible. If there are more than l:max_pad_lines then the script will adjust the buffer so that the number of pad lines is exactly l:max_pad_lines. (If line wrapping is turned on and there are wrapping lines after the cursor there may be slight discrepancies...but, hey, this is a freebie what do you want? ;)

2

This answers the OP's question by taking a somewhat different approach, but it should still be very usable. The vimscript code available below could even be modified to follow the OP's approach completely.

Below is an excerpt from my ~/.vimrc that modifies the zz command to have no empty lines displayed after the end of the buffer. This also applies to the restoring of the cursor position after opening a file, if you have already configured that the usual way, which is described in :h restore-cursor.

" Scroll the window up not to display blank lines when centering the window
" vertically, unless there are simply not enough lines in the buffer
"
" NOTE: Thanks to the way vim handles long lines, this is effectively broken
"       when the buffer contains lines whose lengths exceed the current window
"       width and the "wrap" option is enabled, which cannot be fixed without
"       performing prohibitively expensive and inaccurate calculations of the
"       window lines that would take the wrapped line fragments into account
"
function! AdjustCentered()
  let lines     = line("$")
  let winheight = winheight(0)
  let winlines  = line("w$") - line("w0") + 1
  if winlines > 0 && winlines < winheight && winlines < lines
    execute "normal! " .. (min([winheight, lines]) - winlines) .. "\<C-Y>"
  endif
endfunction

nnoremap <silent> zz zz:call AdjustCentered()<CR>

As you can see, this is broken when the buffer contains long lines. At some point in time I might even implement a patch for vim that fixes this issue.


Below is an improved version of the code that eliminates screen flickering, which used to happen quite often as a result of the function scrolling the window. For example, it happened when the window was scrolled down beyond the last line of the buffer by hand and zz was then executed manually.

I also have more vimscript code in my ~/.vimrc that ends up using the AdjustCentered() function to optionally center the search matches vertically, which also caused screen flickering quite often.

" Scroll the window up not to display blank lines when centering the window
" vertically, unless there are simply not enough lines in the buffer
"
" NOTE: Thanks to the way vim handles long lines, this is effectively broken
"       when the buffer contains lines whose lengths exceed the current window
"       width and the "wrap" option is enabled, which cannot be fixed without
"       performing prohibitively expensive and inaccurate calculations of the
"       window lines that would take the wrapped line fragments into account
"
function! AdjustCentered()
  set lazyredraw
  execute "normal! zz"
  let lines     = line("$")
  let winheight = winheight(0)
  let winlines  = line("w$") - line("w0") + 1
  if winlines > 0 && winlines < winheight && winlines < lines
    execute "normal! " .. (min([winheight, lines]) - winlines) .. "\<C-Y>"
  endif
  redraw
  set nolazyredraw
endfunction

nnoremap <silent> zz :call AdjustCentered()<CR>

To me, screen flickering of any kind is quite annoying, so I simply had to fix it. The good news is that fixing screen flickering is rather easy in vimscript functions, see this question and this answer in particular for more details.


Below is an improved version of the vimscript code that now remaps and handles a total of four variants of the window scrolling commands: z<CR>, zt, z. and zz. As a result, none of these commands produce blank lines any longer.

The code also introduces z, as a brand-new mapping that just scrolls the window vertically so no blank lines are displayed, which may still become present as a result of scrolling the window using PageUp or PageDown, for example.

" Scroll the window up not to display blank lines when scrolling the window
" vertically, unless there are simply not enough lines in the buffer
"
" NOTE: Thanks to the way vim handles long lines, this is effectively broken
"       when the buffer contains lines whose lengths exceed the current window
"       width and the "wrap" option is enabled, which cannot be fixed without
"       performing prohibitively expensive and inaccurate calculations of the
"       window lines that would take the wrapped line fragments into account
"
function! AdjustScrolled(command = v:null)
  set lazyredraw
  if a:command isnot v:null
    execute "normal! " .. a:command
  endif
  let bufflines = line("$")
  let winlines  = line("w$") - line("w0") + 1
  let winheight = winheight(0)
  if winlines > 0 && winlines < bufflines && winlines < winheight
    execute "normal! " .. (min([winheight, bufflines]) - winlines) .. "\<C-Y>"
  endif
  redraw
  set nolazyredraw
endfunction

" Adjust the existing mappings to use the new function
"
nnoremap <silent> z<CR> :call AdjustScrolled("z\<lt>CR>")<CR>
nnoremap <silent> zt    :call AdjustScrolled("zt")<CR>
nnoremap <silent> z.    :call AdjustScrolled("z.")<CR>
nnoremap <silent> zz    :call AdjustScrolled("zz")<CR>

" Add new mapping to make getting rid of blank lines easy
"
nnoremap <silent> z, :call AdjustScrolled()<CR>

I find the new z, mapping quite useful. With all this is place, scrolling becomes significantly more comfortable for those who don't like the blank lines. :)

See also this answer, which may provide some additional comfort in vim.

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.