Skip to main content
7 of 7
Fix typo

EDIT: I added a second version below, which is simpler but leads to more errors for the author of the question.

I am not entirely sure if I understand your question correctly. Therefore, I restate my interpretation of your problem before proposing a potential solution below.

When pressing zz Vim centres soft wrapped lines vertically such that the middle of the hard line is at the middle of the screen, regardless of on which of the soft lines within the hard line the cursor is placed. Now, whenever you press j or k within a soft wrapped line, you move one soft line up resp. down since you have remapped them. Then, your plug-in calls zz to vertically centre the current line. (I believe, you mistakenly wrote horizontally instead of vertically in your question, right?) However, since zz does not centre with respect to the current soft line, no scrolling is happening until you enter the next hard line where zz now scrolls the window several soft lines at once. This "jump" in the scrolling is bothering you.

If this is the issue you are trying to resolve, the following function could address your needs. Additionally, you can map the function to szz. Note, that this solution does not work well with long lines that are soft wrapped around the screen boundaries.

function! SoftLineWrapAwareVerticalCenterToMidScreen ()
    " Centers the current line vertically on the window. Soft lines
    " are centered such that their middle is at the middle of the
    " window.
    execute "normal zz"

        let l:nchars = strlen(getline("."))
    let l:width = winwidth(0)
    if l:nchars <= l:width
        " There is no need to further adjust the centering
        return
    endif
    
    " The line is longer than the window width, so we need to
    " adjust the centering. We calculate the number of soft lines
    " of the current line based on the number of characters in the
    " line and the window width.
    let l:nsoftlines = nchars / l:width

    " In order to adjust the centering, first we need to calculate
    " what the soft line is to which the window was centered. 
    let l:midSoftline = l:nsoftlines / 2
    let l:midSoftline = l:midSoftline + 1

    " Now we need to calculate the soft line at which the cursor is.
    " This is the line with respect to which we want to center the 
    " window.
    let l:cursorPosV = col(".")
    let l:cursorSoftline = col(".") / l:width
    let l:cursorSoftline = l:cursorSoftline + 1

    " The difference between the soft line at which the cursor is
    " and the middle soft line is the number of soft lines we need
    " to move the window in order to center the cursor.
    let l:softlineDiff = l:midSoftline - l:cursorSoftline


    " If the difference is positive, then the cursor is above the 
    " middle of the window, so we need to move the window up.
    " Otherwise, we need to move the window down. 
    if l:softlineDiff > 0
        while l:softlineDiff > 0
            " Simulate pressing <C-y> to move the window up
            call feedkeys("\<C-y>")
            let l:softlineDiff = l:softlineDiff - 1
        endwhile
    else
        let l:softlineDiff = -l:softlineDiff
        while l:softlineDiff > 0
            " Simulate pressing <C-e> to move the window
            " down
            call feedkeys("\<C-e>")
            let l:softlineDiff = l:softlineDiff - 1
        endwhile
    endif
endfunction

map szz :call SoftLineWrapAwareVerticalCenterToMidScreen()<CR>

Following up on @TheGiwi's comment, I have came up with the following simpler solution. I believed the solution to be more stable, but it appears I was incorrect. It seems that, for @TheGiwi, the first solution works better. Like the initial solution, this one also encounters issues when long lines are soft wrapped lines around screen boundaries, resulting in errors.

function! SoftlineWrapAwareVerticalCenterToMidScreen2 ()
    " Get number of lines of the current window:
    let l:winHeight = winheight(0)

    " Get the line number of the cursor in the current window:
    let l:cursorLine = winline()

    " Middle line of the window:
    let l:midLine = l:winHeight / 2

    " Difference between the middle line of the window and the line:
    let l:lineDiff = l:midLine - l:cursorLine


    " If the difference is positive, then the cursor is above the
    " middle of the window, so we need to move the window up.
    " Otherwise, we need to move the window down.
    if l:lineDiff > 0
        while l:lineDiff > 0
            " Simulate pressing <C-y> to move the window up
            call feedkeys("\<C-y>")
            let l:lineDiff = l:lineDiff - 1
        endwhile
    else
        let l:lineDiff = -l:lineDiff
        while l:lineDiff > 0
            " Simulate pressing <C-e> to move the window down
            call feedkeys("\<C-e>")
            let l:lineDiff = l:lineDiff - 1
        endwhile
    endif
endfunction

" Added <leader> because I personally did not want to remap my `j` and `k` keys at any time. 
map <leader>j gjszz
map <leader>k gkszz
``