'Make { and } ignore lines containing only whitespace

When you navigate by paragraph in vim using { and } it skips lines that contain nothing but whitespace though they are otherwise 'blank'.

How can I convince vim to treat "whitespace only" lines as paragraph breaks so { and } will jump to them?

vim


Solution 1:[1]

Here's a modified version that handles counts correctly:

function! ParagraphMove(delta, visual, count)
    normal m'
    normal |
    if a:visual
        normal gv
    endif

    if a:count == 0
        let limit = 1
    else
        let limit = a:count
    endif

    let i = 0
    while i < limit
        if a:delta > 0
            " first whitespace-only line following a non-whitespace character           
            let pos1 = search("\\S", "W")
            let pos2 = search("^\\s*$", "W")
            if pos1 == 0 || pos2 == 0
                let pos = search("\\%$", "W")
            endif
        elseif a:delta < 0
            " first whitespace-only line preceding a non-whitespace character           
            let pos1 = search("\\S", "bW")
            let pos2 = search("^\\s*$", "bW")
            if pos1 == 0 || pos2 == 0
                let pos = search("\\%^", "bW")
            endif
        endif
        let i += 1
    endwhile
    normal |
endfunction

nnoremap <silent> } :<C-U>call ParagraphMove( 1, 0, v:count)<CR>
onoremap <silent> } :<C-U>call ParagraphMove( 1, 0, v:count)<CR>
" vnoremap <silent> } :<C-U>call ParagraphMove( 1, 1)<CR>
nnoremap <silent> { :<C-U>call ParagraphMove(-1, 0, v:count)<CR>
onoremap <silent> { :<C-U>call ParagraphMove(-1, 0, v:count)<CR>
" vnoremap <silent> { :<C-U>call ParagraphMove(-1, 1)<CR>

Solution 2:[2]

As said before, if you run :help paragraph you will see that lines with whitespace are not treated as a boundary.

In the mean time there are two plugin projects which could help:


If you use Pathogen, simply download from one of the sites mentioned above.

If you use Vundle, put one of the following in your .vimrc:

  • Improved paragraph motion:

    Bundle 'vim-scripts/Improved-paragraph-motion'
    
  • Vim Paragraph Motion:

    Bundle 'dbakker/vim-paragraph-motion'
    

Run :BundleInstall after a reboot and the { } motions should stop on lines containing whitespace characters.

Solution 3:[3]

This is something that's bothered me for a long time. Probably the "right" solution would be to submit a patch to vim itself that would allow you to customize paragraph boundaries with a regex (like :set paragraphs, but actually useful).

In the meantime, I've made a function and a couple of mappings that almost do the right thing:

function! ParagraphMove(delta, visual)
    normal m'
    normal |
    if a:visual
        normal gv
    endif

    if a:delta > 0
        " first whitespace-only line following a non-whitespace character
        let pos1 = search("\\S", "W")
        let pos2 = search("^\\s*$", "W")
        if pos1 == 0 || pos2 == 0
            let pos = search("\\%$", "W")
        endif
    elseif a:delta < 0
        " first whitespace-only line preceding a non-whitespace character
        let pos1 = search("\\S", "bW")
        let pos2 = search("^\\s*$", "bW")
        if pos1 == 0 || pos2 == 0
            let pos = search("\\%^", "bW")
        endif
    endif
    normal |
endfunction

nnoremap <silent> } :call ParagraphMove( 1, 0)<CR>
onoremap <silent> } :call ParagraphMove( 1, 0)<CR>
" vnoremap <silent> } :call ParagraphMove( 1, 1)<CR>
nnoremap <silent> { :call ParagraphMove(-1, 0)<CR>
onoremap <silent> { :call ParagraphMove(-1, 0)<CR>
" vnoremap <silent> { :call ParagraphMove(-1, 1)<CR>

This doesn't correctly handle counts like '4}' or visual mode correctly (uncomment the vnoremap lines at your peril), but seems ok for things like not clobbering the current search pattern and not flickering. Also, 'd}', 'y}', etc. seem to work ok. If anyone has ideas for making counts work or fixing visual mode, please let me know.

Solution 4:[4]

The { and } commands move by "paragraph", and vim's documentation (see :help paragraph) says:

Note that a blank line (only containing white space) is NOT a paragraph boundary.

So the only way you can do this would be to remap { and }. Something like:

nmap { ?^\\s*$<CR>
nmap } /^\\s*$<CR>

could work, but you may want to adjust this so it doesn't alter your search history.

Solution 5:[5]

I never have a legitimate need for whitespace only lines so I solved this "problem" by adding the following to my .vimrc:

" Highlight spaces at the end of lines.
highlight link localWhitespaceError Error
au Syntax * syn match localWhitespaceError /\(\zs\%#\|\s\)\+$/ display

" Remove end of line white space.
noremap <Leader>r ma:%s/\s\+$//e<CR>`a

So then if { and } skips whitespace only lines I use my mapping to remove it and try again.

Solution 6:[6]

I finally caved and did something similar to eremite. I previously had Z mapped to {gq}, which reformats text from last blank line to the next blank line. If lines containing only spaces are not recognized as boundaries, it can destroy a lot of intricate code structures, and days of work. Undo is not an option if I've switched buffers a few times using <C-^>.

I leveraged a custom command command! Trim execute "normal \<S-m>" | % s=\s*$== | execute "normal \<C-o>z.\<C-o>" | set nohlsearch to "Trim" all trailing spaces in my file. I have expandtab enabled so I only ever have spaces, not tabs. But it's easy enough to delete all trailing space using \_s instead of \s. My new Z command is nmap Z mq:Trim<CR>`q{gq}`q. I use marker q to store my current cursor position, then use `q to return to it after the reformatting.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 keithr
Solution 2 JBert
Solution 3 Mike P
Solution 4 Laurence Gonsalves
Solution 5 eremite
Solution 6 user2153235