Make { and } ignore lines containing only whitespace

Viewed 1145

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?

6 Answers

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.

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.

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.

Related